2

《Chrome V8源码》31.Ignition到底做了什么?(二)

 2 years ago
source link: https://zhuanlan.zhihu.com/p/443455606
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

《Chrome V8源码》31.Ignition到底做了什么?(二)

V8粉丝一枚,正在努力做一名V8源码的朗读者。

本篇文章是Builtin专题的第六篇,讲解Ignition中的Builtin::kInterpreterEntryTrampoline源码。包括InterpreterEntryTrampoline、Runtime_InterpreterTraceBytecodeEntry和Runtime_InterpreterTraceBytecodeExit源码。

2 InterpreterEntryTrampoline

提示: 本文使用的V8版本是7.9.10,CPU:x64,http://Builtins-x64.cc,样例代码参见上一篇。
InterpreterEntryTrampoline源码如下:

1.  void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
2.  Register closure = rdi;
3.  Register feedback_vector = rbx;
4.  __ LoadTaggedPointerField(
5.      rax, FieldOperand(closure, JSFunction::kSharedFunctionInfoOffset));
6.  __ LoadTaggedPointerField(
7.      kInterpreterBytecodeArrayRegister,
8.      FieldOperand(rax, SharedFunctionInfo::kFunctionDataOffset));
9.  GetSharedFunctionInfoBytecode(masm, kInterpreterBytecodeArrayRegister,
10.                                  kScratchRegister);
11.    Label compile_lazy;
12.    __ CmpObjectType(kInterpreterBytecodeArrayRegister, BYTECODE_ARRAY_TYPE, rax);
13.    __ j(not_equal, &compile_lazy);
14.    __ bind(&push_stack_frame);
15.    FrameScope frame_scope(masm, StackFrame::MANUAL);
16.    __ pushq(rbp);  // Caller's frame pointer.
17.    __ movq(rbp, rsp);
18.    __ Push(rsi);  // Callee's context.
19.    __ Push(rdi);  // Callee's JS function.
20.    __ movw(FieldOperand(kInterpreterBytecodeArrayRegister,
21.                         BytecodeArray::kOsrNestingLevelOffset),
22.            Immediate(0));
23.    __ movq(kInterpreterBytecodeOffsetRegister,
24.            Immediate(BytecodeArray::kHeaderSize - kHeapObjectTag));
25.    __ Push(kInterpreterBytecodeArrayRegister);
26.    __ SmiTag(rcx, kInterpreterBytecodeOffsetRegister);
27.    __ Push(rcx);
28.    {
29.      //Allocate the local and temporary register file on the stack.
30.      //省略...............
31.    }
32.    __ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kUndefinedValue);
33.    Label do_dispatch;
34.    __ bind(&do_dispatch);
35.    __ Move(
36.        kInterpreterDispatchTableRegister,
37.        ExternalReference::interpreter_dispatch_table_address(masm->isolate()));
38.    __ movzxbq(r11, Operand(kInterpreterBytecodeArrayRegister,
39.                            kInterpreterBytecodeOffsetRegister, times_1, 0));
40.    __ movq(kJavaScriptCallCodeStartRegister,
41.            Operand(kInterpreterDispatchTableRegister, r11,
42.                    times_system_pointer_size, 0));
43.    __ call(kJavaScriptCallCodeStartRegister);
44.    masm->isolate()->heap()->SetInterpreterEntryReturnPCOffset(masm->pc_offset());
45.    __ movq(kInterpreterBytecodeArrayRegister,
46.            Operand(rbp, InterpreterFrameConstants::kBytecodeArrayFromFp));
47.    __ movq(kInterpreterBytecodeOffsetRegister,
48.            Operand(rbp, InterpreterFrameConstants::kBytecodeOffsetFromFp));
49.    __ SmiUntag(kInterpreterBytecodeOffsetRegister,
50.                kInterpreterBytecodeOffsetRegister);
51.    Label do_return;
52.    __ movzxbq(rbx, Operand(kInterpreterBytecodeArrayRegister,
53.                            kInterpreterBytecodeOffsetRegister, times_1, 0));
54.    AdvanceBytecodeOffsetOrReturn(masm, kInterpreterBytecodeArrayRegister,
55.                                  kInterpreterBytecodeOffsetRegister, rbx, rcx,
56.                                  &do_return);
57.    __ jmp(&do_dispatch);
58.    __ bind(&do_return);
59.    LeaveInterpreterFrame(masm, rbx, rcx);
60.    __ ret(0);
61.    __ bind(&compile_lazy);
62.    GenerateTailCallToReturnedCode(masm, Runtime::kCompileLazy);
63.     __ int3(); 
64.   }

上述代码中,第4行代码:从JSFunction中取出SharedFunction并存储到rax中;第6行代码:从SharedFunctionInfo获取kFunctionDataOffset的数据并存储到kInterpreterBytecodeArrayRegister中;第9行代码:加载Bytecodearray到kInterpreterBytecodeArrayRegister。
细节说明:
(1) FieldOperand(x,y)方法中x是基址,y是偏移量,该方法用于返回x+y的位置的数据;
(2) 因为SharedFunction::kFunctionDataOffset可能存储Bytecodearray或Builtin,所以执行完第6行代码后需要用第9行代码判断kInterpreterBytecodeArrayRegister中的数据是否是Bytecodearray。
上述第10-13行代码:判断kInterpreterBytecodeArrayRegister的值是Bytecodarray还是Builtins::kCompileLazy,根据判断结果跳转到相应的Label;第15-19行代码存储caller的栈帧并把callee的信息压入堆栈。第20-27行代码获取Bytecodearray中第一条Bytecode的偏移量并压入堆栈。BytecodeArray类继承自FixedArrayBase,FixedArrayBase又继承自HeapObject,所以获取第一条Bytecode时需要使用刚刚获取的偏移量。
上述第32行初始化kInterpreterAccumulatorRegister;第35行代码加载dispatch到kInterpreterDispatchTableRegister;第38-40行代码加载第一条Bytecode到kJavaScriptCallCodeStartRegister;第43行代码开始执行Bytecode。所有Bytecode都执行完成后会跳转到第44行代码以设置返回地址。
两种情况下会执行上述第45-63行代码,(1)当全部Bytecode执行完后,Bytecode的结尾会调用Dispatch(),所以只有全部执行完时才会返回;(2)在Bytecode执行过程中调用了其它Builtin,因为调用其它Builtin要重新构建堆栈,所以还要用InterpreterEntryTrampoline。
至此,InterpreterEntryTrampoline分析完毕。

3 Register

InterpreterEntryTrampoline中使用了很多Register,列表如下:

constexpr Register kReturnRegister0 = rax;
constexpr Register kReturnRegister1 = rdx;
constexpr Register kReturnRegister2 = r8;
constexpr Register kJSFunctionRegister = rdi;
constexpr Register kContextRegister = rsi;
constexpr Register kAllocateSizeRegister = rdx;
constexpr Register kSpeculationPoisonRegister = r12;
constexpr Register kInterpreterAccumulatorRegister = rax;
constexpr Register kInterpreterBytecodeOffsetRegister = r9;
constexpr Register kInterpreterBytecodeArrayRegister = r14;
constexpr Register kInterpreterDispatchTableRegister = r15;
//省略.................

InterpreterEntryTrampoline中常用到的寄存器是rax、rdi、rdx和r15,其中被多次提及的r15负责Bytecode的调度。我在汇编中调试Byteocde时,r15寄存器常被用作“入口标记”,即看到r15就说明一条Bytecode开始了,再次看到r15就说明这条Bytecode结束了。

4 InterpreterTraceBytecodeEntry和InterpreterTraceBytecodeExit

这两个方法用于跟踪Bytecode的解释过程,InterpreterTraceBytecodeEntry可以查看寄存器状态;Bytecode执行后调用InterpreterTraceBytecodeExit。源码如下:

1.  RUNTIME_FUNCTION(Runtime_InterpreterTraceBytecodeEntry) {
2.    if (!FLAG_trace_ignition) {
3.      return ReadOnlyRoots(isolate).undefined_value();
4.    }
5.    SealHandleScope shs(isolate);
6.    DCHECK_EQ(3, args.length());
7.    CONVERT_ARG_HANDLE_CHECKED(BytecodeArray, bytecode_array, 0);
8.    CONVERT_SMI_ARG_CHECKED(bytecode_offset, 1);
9.    CONVERT_ARG_HANDLE_CHECKED(Object, accumulator, 2);
10.    int offset = bytecode_offset - BytecodeArray::kHeaderSize + kHeapObjectTag;
11.    interpreter::BytecodeArrayIterator bytecode_iterator(bytecode_array);
12.    AdvanceToOffsetForTracing(bytecode_iterator, offset);
13.    if (offset == bytecode_iterator.current_offset()) {
14.      StdoutStream os;
15.      // Print bytecode.
16.      const uint8_t* base_address = reinterpret_cast<const uint8_t*>(
17.          bytecode_array->GetFirstBytecodeAddress());
18.      const uint8_t* bytecode_address = base_address + offset;
19.      os << " -> " << static_cast<const void*>(bytecode_address) << " @ "
20.         << std::setw(4) << offset << " : ";
21.      interpreter::BytecodeDecoder::Decode(os, bytecode_address,
22.                                           bytecode_array->parameter_count());
23.      os << std::endl;
24.      // Print all input registers and accumulator.
25.      PrintRegisters(isolate, os, true, bytecode_iterator, accumulator);
26.      os << std::flush;
27.    }
28.    return ReadOnlyRoots(isolate).undefined_value();
29.  }
30.  //分隔线...............................
31.  RUNTIME_FUNCTION(Runtime_InterpreterTraceBytecodeExit) {
32.    if (!FLAG_trace_ignition) {
33.      return ReadOnlyRoots(isolate).undefined_value();
34.    }
35.    SealHandleScope shs(isolate);
36.    DCHECK_EQ(3, args.length());
37.    CONVERT_ARG_HANDLE_CHECKED(BytecodeArray, bytecode_array, 0);
38.    CONVERT_SMI_ARG_CHECKED(bytecode_offset, 1);
39.    CONVERT_ARG_HANDLE_CHECKED(Object, accumulator, 2);
40.    int offset = bytecode_offset - BytecodeArray::kHeaderSize + kHeapObjectTag;
41.    interpreter::BytecodeArrayIterator bytecode_iterator(bytecode_array);
42.    AdvanceToOffsetForTracing(bytecode_iterator, offset);
43.    if (bytecode_iterator.current_operand_scale() ==
44.            interpreter::OperandScale::kSingle ||
45.        offset > bytecode_iterator.current_offset()) {
46.      StdoutStream os;
47.      // Print all output registers and accumulator.
48.      PrintRegisters(isolate, os, false, bytecode_iterator, accumulator);
49.      os << std::flush;
50.    }
51.    return ReadOnlyRoots(isolate).undefined_value();
52.  }

Bytecode执行前后会分别调用上述两个方法,但需要把FLAG_trace_ignition(第2行代码)的值设置为True,其声明在flags-definitions.h中,具体位置是DEFINE_BOOL(trace_ignition, false,"trace the bytecodes executed by the ignition interpreter")。第21行代码输出Bytecode到终端,阅读BytecodeDecoder::Decode()源码可以看明白Bytecode和operand的编码方式,这有助于理解dispatch和JS调用堆栈。
下面给出PrintRegisters源码:

1.  void PrintRegisters(Isolate* isolate, std::ostream& os, bool is_input,
2.                      interpreter::BytecodeArrayIterator&
3.                          bytecode_iterator,  // NOLINT(runtime/references)
4.                      Handle<Object> accumulator) {
5.    interpreter::Bytecode bytecode = bytecode_iterator.current_bytecode();
6.    // Print accumulator.
7.    if ((is_input && interpreter::Bytecodes::ReadsAccumulator(bytecode)) ||
8.        (!is_input && interpreter::Bytecodes::WritesAccumulator(bytecode))) {
9.      os << "      [ " << kAccumulator << kArrowDirection;
10.      accumulator->ShortPrint();
11.      os << " ]" << std::endl;
12.    }
13.    // Print the registers.
14.    JavaScriptFrameIterator frame_iterator(isolate);
15.    InterpretedFrame* frame =
16.        reinterpret_cast<InterpretedFrame*>(frame_iterator.frame());
17.    int operand_count = interpreter::Bytecodes::NumberOfOperands(bytecode);
18.    for (int operand_index = 0; operand_index < operand_count; operand_index++) {
19.      interpreter::OperandType operand_type =
20.          interpreter::Bytecodes::GetOperandType(bytecode, operand_index);
21.      bool should_print =
22.          is_input
23.              ? interpreter::Bytecodes::IsRegisterInputOperandType(operand_type)
24.              : interpreter::Bytecodes::IsRegisterOutputOperandType(operand_type);
25.      if (should_print) {
26.        interpreter::Register first_reg =
27.            bytecode_iterator.GetRegisterOperand(operand_index);
28.        int range = bytecode_iterator.GetRegisterOperandRange(operand_index);
29.        for (int reg_index = first_reg.index();
30.             reg_index < first_reg.index() + range; reg_index++) {
31.          Object reg_object = frame->ReadInterpreterRegister(reg_index);
32.          os << "      [ " << std::setw(kRegFieldWidth)
33.             << interpreter::Register(reg_index).ToString(
34.                    bytecode_iterator.bytecode_array()->parameter_count())
35.             << kArrowDirection;
36.          reg_object.ShortPrint(os);
37.          os << " ]" << std::endl;
38.        }
39.      }
40.    }
41.  }

上述第14-17行代码计算操作数的数量。第20-37行代码输出寄存器的值。通过阅读PrintRegisters()方法,我们可以学到三个有用的知识点:
(1)读取寄存器的方法;
(2)V8中打印数据的方法;
(3)InterpretedFrame的数据结构。
这三点可以帮助我们更好地了解Bytecode的执行过程。提示V8中功能全面的打印方法是logger。

技术总结
(1) SharedFunction::kFunctionDataOffset位置存储的内容可能是Bytecodearray也可能是Builtins::kCompileLazy;
(2) BytecodeDecoder::Decode()PrintRegisters()很重要,可以帮助我们理解Bytecode的执行过程。
好了,今天到这里,下次见。

个人能力有限,有不足与纰漏,欢迎批评指正
微信:qq9123013 备注:v8交流 邮箱:[email protected]


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK