|
| 1 | +# 《Chrome V8源码》26.Bytecode Handler,字节码的核心 |
| 2 | + |
| 3 | +# 1 摘要 |
| 4 | +本篇文章是Builtin专题的第二篇,讲解Bytecode Handler的初始化过程以及相关数据结构。Bytecode handler是采用CAS方式编写的Builtin,它实现了Bytecode的功能,每一条Bytecode对应一条Bytecode handler。本文内容组织方法:Bytecode handler介绍(章节2);Bytecode Handler初始化(章节3)。 |
| 5 | +# 2 Bytecode Handler介绍 |
| 6 | +Bytecode handler的地址保存在Dispatch_table数组中,Dispatch_table保存在Isolate中。每一条Bytecode执行完毕后通过Dispatch_table找到下一条Bytecode并完成跳转。Bytecode handler的源码在interpreter-generator.cc文件中,其中Star和Mov的源码如下: |
| 7 | +```c++ |
| 8 | +1. // Store accumulator to register <dst>. |
| 9 | +2. IGNITION_HANDLER(Star, InterpreterAssembler) { |
| 10 | +3. TNode<Object> accumulator = GetAccumulator(); |
| 11 | +4. StoreRegisterAtOperandIndex(accumulator, 0); |
| 12 | +5. Dispatch(); |
| 13 | +6. } |
| 14 | +7. // Mov <src> <dst> |
| 15 | +8. // |
| 16 | +9. // Stores the value of register <src> to register <dst>. |
| 17 | +10. IGNITION_HANDLER(Mov, InterpreterAssembler) { |
| 18 | +11. TNode<Object> src_value = LoadRegisterAtOperandIndex(0); |
| 19 | +12. StoreRegisterAtOperandIndex(src_value, 1); |
| 20 | +13. Dispatch(); |
| 21 | +14. } |
| 22 | +``` |
| 23 | +上述代码中,`Dispatch()`利用Isolate读取Dispatch_table并跳转到下一条指令。 |
| 24 | +# 3 Bytecode Handler初始化 |
| 25 | +Bytecode handler是用CodeStubAssembler编写的Builtin。第一个Isolate创建时,Bytecode handler完成初始化并存储在dispatch_table中,后续创建新的Isoalte时无需再做初始化,只需要拷贝即可。 |
| 26 | +Bytecode Handler初始化的入口源码如下: |
| 27 | +```c++ |
| 28 | +1. void SetupIsolateDelegate::SetupBuiltinsInternal(Isolate* isolate) { |
| 29 | +2. Builtins* builtins = isolate->builtins(); |
| 30 | +3. int index = 0; |
| 31 | +4. Code code; |
| 32 | +5. //...........省略............................ |
| 33 | +6. #define BUILD_BCH(Name, OperandScale, Bytecode) \ |
| 34 | +7. code = GenerateBytecodeHandler(isolate, index, OperandScale, Bytecode); \ |
| 35 | +8. AddBuiltin(builtins, index++, code); |
| 36 | +9. BUILTIN_LIST(BUILD_CPP, BUILD_TFJ, BUILD_TFC, BUILD_TFS, BUILD_TFH, BUILD_BCH, |
| 37 | +10. BUILD_ASM); |
| 38 | +11. //.........省略............................ |
| 39 | +12. } |
| 40 | +``` |
| 41 | +`BUILD_BCH`、`BUILTIN_LIST`和`GenerateBytecodeHandler()`共同完成所有Bytecode handler的创建,`GenerateBytecodeHandler()`源码如下: |
| 42 | +```c++ |
| 43 | +Code GenerateBytecodeHandler(Isolate* isolate, int builtin_index, |
| 44 | + interpreter::OperandScale operand_scale, |
| 45 | + interpreter::Bytecode bytecode) { |
| 46 | + DCHECK(interpreter::Bytecodes::BytecodeHasHandler(bytecode, operand_scale)); |
| 47 | + Handle<Code> code = interpreter::GenerateBytecodeHandler( |
| 48 | + isolate, Builtins::name(builtin_index), bytecode, operand_scale, |
| 49 | + builtin_index, BuiltinAssemblerOptions(isolate, builtin_index)); |
| 50 | + return *code; |
| 51 | +} |
| 52 | +``` |
| 53 | +上述代码中`Builtins::name(builtin_index)`获取Bytecode的名字,源码如下: |
| 54 | +```c++ |
| 55 | +const char* Builtins::name(int index) { |
| 56 | + DCHECK(IsBuiltinId(index)); |
| 57 | + return builtin_metadata[index].name; |
| 58 | +} |
| 59 | +//................分隔线....................... |
| 60 | +#define DECL_CPP(Name, ...) \ |
| 61 | + {#Name, Builtins::CPP, {FUNCTION_ADDR(Builtin_##Name)}}, |
| 62 | +#define DECL_TFJ(Name, Count, ...) {#Name, Builtins::TFJ, {Count, 0}}, |
| 63 | +#define DECL_TFC(Name, ...) {#Name, Builtins::TFC, {}}, |
| 64 | +#define DECL_TFS(Name, ...) {#Name, Builtins::TFS, {}}, |
| 65 | +#define DECL_TFH(Name, ...) {#Name, Builtins::TFH, {}}, |
| 66 | +#define DECL_BCH(Name, OperandScale, Bytecode) \ |
| 67 | + {#Name, Builtins::BCH, {Bytecode, OperandScale}}, |
| 68 | +#define DECL_ASM(Name, ...) {#Name, Builtins::ASM, {}}, |
| 69 | +const BuiltinMetadata builtin_metadata[] = {BUILTIN_LIST( |
| 70 | + DECL_CPP, DECL_TFJ, DECL_TFC, DECL_TFS, DECL_TFH, DECL_BCH, DECL_ASM)}; |
| 71 | +``` |
| 72 | +上述代码中`builtin_metadata`数组保存每一条Bytecode的名字、类型和地址指针。如图1中所示,1075号Builtin的名字是DebugBreak0Handler,类型是BCH(Bytecode handler),函数地址是data。 |
| 73 | + |
| 74 | + |
| 75 | +在`Code GenerateBytecodeHandler()`中调用`interpreter::GenerateBytecodeHandler()`,源码如下: |
| 76 | +```c++ |
| 77 | +1. Handle<Code> GenerateBytecodeHandler(Isolate* isolate, const char* debug_name, |
| 78 | +2. Bytecode bytecode, |
| 79 | +3. OperandScale operand_scale, |
| 80 | +4. int builtin_index, |
| 81 | +5. const AssemblerOptions& options) { |
| 82 | +6. Zone zone(isolate->allocator(), ZONE_NAME); |
| 83 | +7. compiler::CodeAssemblerState state( |
| 84 | +8. isolate, &zone, InterpreterDispatchDescriptor{}, Code::BYTECODE_HANDLER, |
| 85 | +9. debug_name, |
| 86 | +10. FLAG_untrusted_code_mitigations |
| 87 | +11. ? PoisoningMitigationLevel::kPoisonCriticalOnly |
| 88 | +12. : PoisoningMitigationLevel::kDontPoison, |
| 89 | +13. builtin_index); |
| 90 | +14. switch (bytecode) { |
| 91 | +15. #define CALL_GENERATOR(Name, ...) \ |
| 92 | +16. case Bytecode::k##Name: \ |
| 93 | +17. Name##Assembler::Generate(&state, operand_scale); \ |
| 94 | +18. break; |
| 95 | +19. BYTECODE_LIST(CALL_GENERATOR); |
| 96 | +20. #undef CALL_GENERATOR |
| 97 | +21. } |
| 98 | +22. Handle<Code> code = compiler::CodeAssembler::GenerateCode(&state, options); |
| 99 | +23. return code; |
| 100 | +24. } |
| 101 | +``` |
| 102 | +
|
| 103 | +上述代码中,`Bytecode bytecode`是枚举变量,源码如下: |
| 104 | +```c++ |
| 105 | +// Enumeration of interpreter bytecodes. |
| 106 | +enum class Bytecode : uint8_t { |
| 107 | +#define DECLARE_BYTECODE(Name, ...) k##Name, |
| 108 | + BYTECODE_LIST(DECLARE_BYTECODE) |
| 109 | +#undef DECLARE_BYTECODE |
| 110 | +#define COUNT_BYTECODE(x, ...) +1 |
| 111 | + // The COUNT_BYTECODE macro will turn this into kLast = -1 +1 +1... which will |
| 112 | + // evaluate to the same value as the last real bytecode. |
| 113 | + kLast = -1 BYTECODE_LIST(COUNT_BYTECODE) |
| 114 | +#undef COUNT_BYTECODE |
| 115 | +}; |
| 116 | +``` |
| 117 | +`GenerateBytecodeHandler()`的第14行代码:根据`bytecode`的类型执行对应的`case`以完成Bytecode handler的初始化。`BYTECODE_LIST`源码如下: |
| 118 | +```c++ |
| 119 | +// The list of bytecodes which are interpreted by the interpreter. |
| 120 | +// Format is V(<bytecode>, <accumulator_use>, <operands>). |
| 121 | +#define BYTECODE_LIST(V) \ |
| 122 | + /* Extended width operands */ \ |
| 123 | + V(Wide, AccumulatorUse::kNone) \ |
| 124 | + V(ExtraWide, AccumulatorUse::kNone) \ |
| 125 | + \ |
| 126 | + /* Debug Breakpoints - one for each possible size of unscaled bytecodes */ \ |
| 127 | + /* and one for each operand widening prefix bytecode */ \ |
| 128 | + V(DebugBreakWide, AccumulatorUse::kReadWrite) \ |
| 129 | + V(DebugBreakExtraWide, AccumulatorUse::kReadWrite) \ |
| 130 | + V(DebugBreak0, AccumulatorUse::kReadWrite) \ |
| 131 | + V(DebugBreak1, AccumulatorUse::kReadWrite, OperandType::kReg) \ |
| 132 | + V(DebugBreak2, AccumulatorUse::kReadWrite, OperandType::kReg, \ |
| 133 | + OperandType::kReg) \ |
| 134 | + V(DebugBreak3, AccumulatorUse::kReadWrite, OperandType::kReg, \ |
| 135 | + OperandType::kReg, OperandType::kReg) \ |
| 136 | + //省略................................... |
| 137 | +//.................................分隔线....................................... |
| 138 | +switch (bytecode) { |
| 139 | + case Bytecode::kWide: \ |
| 140 | + WideAssembler::Generate(&state, operand_scale); \ |
| 141 | + break; |
| 142 | + case Bytecode::kLdaSmi: \ |
| 143 | + LdaSmiAssembler::Generate(&state, operand_scale); \ |
| 144 | + break; |
| 145 | +//省略................................... |
| 146 | +} |
| 147 | +``` |
| 148 | +通过展开`kWide`和`KLdaSmi`可以看到各自的生成函数,图2给出了此时的调用堆栈。 |
| 149 | + |
| 150 | +下面讲解`LdaSmi`的初始化,`LdaSmi`的初始化函数由宏`IGNITION_HANDLER`和`IGNITION_HANDLER(LdaSmi, InterpreterAssembler)`共同组成,展开后的源码如下: |
| 151 | +```c++ |
| 152 | +1. class LdaSmiAssembler : public InterpreterAssembler { |
| 153 | +2. public: |
| 154 | +3. explicit LdaSmiAssembler(compiler::CodeAssemblerState* state, |
| 155 | +4. Bytecode bytecode, OperandScale scale) |
| 156 | +5. : InterpreterAssembler(state, bytecode, scale) {} |
| 157 | +6. static void Generate(compiler::CodeAssemblerState* state, |
| 158 | +7. OperandScale scale); |
| 159 | +8. private: |
| 160 | +9. void GenerateImpl(); |
| 161 | +10. DISALLOW_COPY_AND_ASSIGN(LdaSmiAssembler); |
| 162 | +11. }; |
| 163 | +12. void LdaSmiAssembler::Generate(compiler::CodeAssemblerState* state, |
| 164 | +13. OperandScale scale) { |
| 165 | +14. LdaSmiAssembler assembler(state, Bytecode::kLdaSmi, scale); |
| 166 | +15. state->SetInitialDebugInformation("LdaSmi", __FILE__, __LINE__); |
| 167 | +16. assembler.GenerateImpl(); |
| 168 | +17. } |
| 169 | +18. void LdaSmiAssembler::GenerateImpl(){ |
| 170 | +19. TNode<Smi> smi_int = BytecodeOperandImmSmi(0); |
| 171 | +20. SetAccumulator(smi_int); |
| 172 | +21. Dispatch(); |
| 173 | +22. } |
| 174 | +``` |
| 175 | +上述代码满足`case Bytecode::kLdaSmi`条件,执行第12行代码`Generate()`;在`Generate()`中调用`GenerateImpl()`(第18行代码);第19行代码生成小整数`smi_int`;第20行代码把`smi_int`存入累加寄存器。第1行代码`InterpreterAssembler`的父类`InterpreterAssembler`实现了很多Bytecode handler的基础功能,源码如下: |
| 176 | +```c++ |
| 177 | +TNode<Object> InterpreterAssembler::GetAccumulator() { |
| 178 | + DCHECK(Bytecodes::ReadsAccumulator(bytecode_)); |
| 179 | + accumulator_use_ = accumulator_use_ | AccumulatorUse::kRead; |
| 180 | + return TaggedPoisonOnSpeculation(GetAccumulatorUnchecked()); |
| 181 | +} |
| 182 | +//分隔线................................. |
| 183 | +void InterpreterAssembler::SetAccumulator(SloppyTNode<Object> value) { |
| 184 | + DCHECK(Bytecodes::WritesAccumulator(bytecode_)); |
| 185 | + accumulator_use_ = accumulator_use_ | AccumulatorUse::kWrite; |
| 186 | + accumulator_ = value; |
| 187 | +} |
| 188 | +//分隔线................................. |
| 189 | +TNode<ExternalReference> InterpreterAssembler::DispatchTablePointer() { |
| 190 | + if (Bytecodes::MakesCallAlongCriticalPath(bytecode_) && made_call_ && |
| 191 | + (dispatch_table_.value() == |
| 192 | + Parameter(InterpreterDispatchDescriptor::kDispatchTable))) { |
| 193 | + dispatch_table_ = ExternalConstant( |
| 194 | + ExternalReference::interpreter_dispatch_table_address(isolate())); |
| 195 | + } |
| 196 | + return dispatch_table_.value(); |
| 197 | +} |
| 198 | +``` |
| 199 | +上述代码中`GetAccumulator()`获取累加器的值;`SetAccumulator`设置累加器的值;`DispatchTablePointer()`获取dispatch_table_的地址;这些函数的源码在interpreter-assembler.cc文件中。 |
| 200 | +**技术总结** |
| 201 | +**(1)** 调试Bytecode handler的方法是在`Code GenerateBytecodeHandler()`中设置断点; |
| 202 | +**(2)** Bytecode在宏BYTECODE_LIST中的位置是Bytecode的编号index,index是Bytecode handler在dispatch_table_中的下标; |
| 203 | +**(3)** `InterpreterAssembler`中定义了Bytecode handler的底层功能,`CodeAssembler`中定义了更底层的原子操作。 |
| 204 | +
|
| 205 | +好了,今天到这里,下次见。 |
| 206 | +
|
| 207 | +**个人能力有限,有不足与纰漏,欢迎批评指正** |
| 208 | +**微信:qq9123013 备注:v8交流 邮箱:v8blink@outlook.com** |
| 209 | + |
| 210 | + |
| 211 | +本文由灰豆原创发布 |
| 212 | +转载出处: https://www.anquanke.com/post/id/260062 |
| 213 | +安全客 - 有思想的安全新媒体 |
0 commit comments