4

《Chrome V8 源码》43. Turbofan 源码分析

 2 years ago
source link: https://zhuanlan.zhihu.com/p/458446689
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.
neoserver,ios ssh client

《Chrome V8 源码》43. Turbofan 源码分析

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

接上一篇文章继续说,本文讲解 Turbofan 的工作流程、梳理 PrepareJob、ExecuteJob 和 FinalizeJob 的主要功能以及重要数据结构。

2 Turbofan 工作流程

前文提到,Turbofan 分为 NotConcurrent 和 Concurrent 两种工作方式,它们的区别是 NotConcurrent 立即启动优化工作,而 Concurrent 把工作放进同步分发队列。
Concurrent 方式由 GetOptimizedCodeLater() 函数负责,其源码如下:

1.  bool GetOptimizedCodeLater(OptimizedCompilationJob* job, Isolate* isolate) {
2.    OptimizedCompilationInfo* compilation_info = job->compilation_info();
3.    if (!isolate->optimizing_compile_dispatcher()->IsQueueAvailable()) {
4.      if (FLAG_trace_concurrent_recompilation) {
5.  //省略................
6.      }
7.      return false;
8.    }
9.    if (isolate->heap()->HighMemoryPressure()) {
10.      if (FLAG_trace_concurrent_recompilation) {
11.  //省略................
12.      }
13.      return false;
14.    }
15.    TimerEventScope<TimerEventRecompileSynchronous> timer(isolate);
16.    RuntimeCallTimerScope runtimeTimer(
17.        isolate, RuntimeCallCounterId::kOptimizeConcurrentPrepare);
18.    TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
19.                 "V8.OptimizeConcurrentPrepare");
20.    if (job->PrepareJob(isolate) != CompilationJob::SUCCEEDED) return false;
21.    isolate->optimizing_compile_dispatcher()->QueueForOptimization(job);
22.    if (FLAG_trace_concurrent_recompilation) {
23.      PrintF("  ** Queued ");
24.      compilation_info->closure()->ShortPrint();
25.      PrintF(" for concurrent optimization.\n");
26.    }
27.    return true;
28.  }

上述代码中,第 4-14 行检测工作队列和内存是否满足要求,不满足则停止优化编译。停止优化编译不影响当前 JavaScript 程序的运行,因为 JavaScript 程序正在被解释执行。
第 15-20 行统计 V8 运行信息,与优化编译的功能无关;
第 21 行把优化编译工作 job 添加到工作队列中,并返回结果 true。
NotConcurrent 方式由 GetOptimizedCodeNow() 函数负责,其源码如下:

1.  bool GetOptimizedCodeNow(OptimizedCompilationJob* job, Isolate* isolate) {
2.    TimerEventScope<TimerEventRecompileSynchronous> timer(isolate);
3.    RuntimeCallTimerScope runtimeTimer(
4.        isolate, RuntimeCallCounterId::kOptimizeNonConcurrent);
5.    OptimizedCompilationInfo* compilation_info = job->compilation_info();
6.    TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
7.                 "V8.OptimizeNonConcurrent");
8.    if (job->PrepareJob(isolate) != CompilationJob::SUCCEEDED ||
9.        job->ExecuteJob(isolate->counters()->runtime_call_stats()) !=
10.            CompilationJob::SUCCEEDED ||
11.        job->FinalizeJob(isolate) != CompilationJob::SUCCEEDED) {
12.      if (FLAG_trace_opt) {
13.        CodeTracer::Scope scope(isolate->GetCodeTracer());
14.        PrintF(scope.file(), "[aborted optimizing ");
15.        compilation_info->closure()->ShortPrint(scope.file());
16.        PrintF(scope.file(), " because: %s]\n",
17.               GetBailoutReason(compilation_info->bailout_reason()));
18.      }
19.      return false;
20.    }
21.    // Success!
22.    job->RecordCompilationStats(OptimizedCompilationJob::kSynchronous, isolate);
23.    DCHECK(!isolate->has_pending_exception());
24.    InsertCodeIntoOptimizedCodeCache(compilation_info);
25.    job->RecordFunctionCompilation(CodeEventListener::LAZY_COMPILE_TAG, isolate);
26.    return true;
27.  }

上述代码中, 第 2-7 行统计 V8 运行信息,与优化编译的功能无关;
第 8-9 行完成优化编译的所有工作,这些工作由 PrepareJob、ExecuteJob 以及 FinalizeJob 三个函数负责;
第 10-25 行更新编译状态等信息并返回 true。 优化编译同步进行,也就意味着暂停解释执行并等待优化编译的结果。

3 准备 PrepareJob

源码如下:

1.  CompilationJob::Status OptimizedCompilationJob::PrepareJob(Isolate* isolate) {
2.    DCHECK_EQ(ThreadId::Current(), isolate->thread_id());
3.    DisallowJavascriptExecution no_js(isolate);
4.    if (FLAG_trace_opt && compilation_info()->IsOptimizing()) {
5.  //省略..............
6.    }
7.    // Delegate to the underlying implementation.
8.    DCHECK_EQ(state(), State::kReadyToPrepare);
9.    ScopedTimer t(&time_taken_to_prepare_);
10.    return UpdateState(PrepareJobImpl(isolate), State::kReadyToExecute);
11.  }

上述代码中,第 2-3 行做状态检测、第 4-6 行设置打印出输信息;第 10 行 UpdateState 更新状态信息,PrepareJobImpl 完成初始化工作,其源码如下:

1.  PipelineCompilationJob::Status PipelineCompilationJob::PrepareJobImpl(
2.      Isolate* isolate) {
3.    PipelineJobScope scope(&data_, isolate->counters()->runtime_call_stats());
4.    if (compilation_info()->bytecode_array()->length() >
5.        FLAG_max_optimized_bytecode_size) {
6.      return AbortOptimization(BailoutReason::kFunctionTooBig);
7.    }
8.    if (!FLAG_always_opt) {
9.      compilation_info()->MarkAsBailoutOnUninitialized();
10.    }
11.    if (FLAG_turbo_loop_peeling) {
12.      compilation_info()->MarkAsLoopPeelingEnabled();
13.    }
14.    if (FLAG_turbo_inlining) {
15.      compilation_info()->MarkAsInliningEnabled();
16.    }
17.    PoisoningMitigationLevel load_poisoning =
18.        PoisoningMitigationLevel::kDontPoison;
19.    if (FLAG_untrusted_code_mitigations) {
20.      load_poisoning = PoisoningMitigationLevel::kPoisonCriticalOnly;
21.    }
22.    compilation_info()->SetPoisoningMitigationLevel(load_poisoning);
23.    if (FLAG_turbo_allocation_folding) {
24.      compilation_info()->MarkAsAllocationFoldingEnabled();
25.    }
26.    if (compilation_info()->closure()->raw_feedback_cell().map() ==
27.            ReadOnlyRoots(isolate).one_closure_cell_map() &&
28.        !compilation_info()->is_osr()) {
29.      compilation_info()->MarkAsFunctionContextSpecializing();
30.      data_.ChooseSpecializationContext();
31.    }
32.    if (compilation_info()->is_source_positions_enabled()) {
33.      SharedFunctionInfo::EnsureSourcePositionsAvailable(
34.          isolate, compilation_info()->shared_info());
35.    }
36.    data_.set_start_source_position(
37.        compilation_info()->shared_info()->StartPosition());
38.    linkage_ = new (compilation_info()->zone()) Linkage(
39.        Linkage::ComputeIncoming(compilation_info()->zone(), compilation_info()));
40.    if (compilation_info()->is_osr()) data_.InitializeOsrHelper();
41.    Deoptimizer::EnsureCodeForDeoptimizationEntries(isolate);
42.    pipeline_.Serialize();
43.    if (!data_.broker()->is_concurrent_inlining()) {
44.      if (!pipeline_.CreateGraph()) {
45.        CHECK(!isolate->has_pending_exception());
46.        return AbortOptimization(BailoutReason::kGraphBuildingFailed);
47.      }
48.    }
49.    return SUCCEEDED;
50.  }

上述代码中,第 4-7 行检查 BytecodeArray 的长度是否超过最大长度限制;
第 8-10 行检查 always_optimization 使能标记,它的作用是 always try to optimize functions;
第 11-25 行检测 loop_peeling、inling、allocation_folding 使能标记,详细说明参见 flag-definitions.h 文件;
第 26-37 行设置 context、OSR、源码信息;
第 38 行创建编译需要的 link 信息;
第 44 行创建 V8.TFGraph,这之后不再需要 T<Node>了;

4 编译 ExecuteJob

ExecuteJob() 中调用 ExecuteJobImpl() 来完成优化编译的主体工作,其源码如下:

1.  PipelineCompilationJob::Status PipelineCompilationJob::ExecuteJobImpl(
2.    RuntimeCallStats* stats) {
3.  PipelineJobScope scope(&data_, stats);
4.  if (data_.broker()->is_concurrent_inlining()) {
5.  //省略.....
6.  }
7.  bool success;
8.   if (FLAG_turboprop) {
9.     success = pipeline_.OptimizeGraphForMidTier(linkage_);
10.   } else {
11.     success = pipeline_.OptimizeGraph(linkage_);
12.   }
13.   if (!success) return FAILED;
14.   pipeline_.AssembleCode(linkage_);
15.   return SUCCEEDED;

上述代码的核心功能就两个,一个基于图的优化功能(OptimizeGraphForMidTier 和 OptimizeGraph),另一个汇编生成器(AssembleCode)。优化功能的源码如下:

bool PipelineImpl::OptimizeGraphForMidTier(Linkage* linkage) {
 Run<TyperPhase>(data->CreateTyper());
 RunPrintAndVerify(TyperPhase::phase_name());
 Run<TypedLoweringPhase>();
 RunPrintAndVerify(TypedLoweringPhase::phase_name());
 Run<LoopExitEliminationPhase>();
 //省略..............
}
//分隔线....................
bool PipelineImpl::OptimizeGraph(Linkage* linkage) {
  PipelineData* data = this->data_;
  data->BeginPhaseKind("V8.TFLowering");
  Run<TyperPhase>(data->CreateTyper());
  RunPrintAndVerify(TyperPhase::phase_name());
  Run<TypedLoweringPhase>();
  RunPrintAndVerify(TypedLoweringPhase::phase_name());
  //省略..............
}

上述代码中,每一个 Run 方法代表过了一种优化技术,每种优化技术的实现都有对应的数据结构,本文不做讲解。
汇编生成器(AssembleCode)的源码如下:

1.  void PipelineImpl::AssembleCode(Linkage* linkage,
2.                                  std::unique_ptr<AssemblerBuffer> buffer) {
3.    PipelineData* data = this->data_;
4.    data->BeginPhaseKind("V8.TFCodeGeneration");
5.    data->InitializeCodeGenerator(linkage, std::move(buffer));
6.    Run<AssembleCodePhase>();
7.  //省略.....
8.  }
9.  //分隔.................
10.  CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
11.      Instruction* instr) {
12.    switch (arch_opcode) {
13.      case kArchCallCodeObject: {
14.        if (HasImmediateInput(instr, 0)) {
15.  //省略.......................
16.        } else {
17.          Register reg = i.InputRegister(0);
18.          DCHECK_IMPLIES(
19.              HasCallDescriptorFlag(instr, CallDescriptor::kFixedTargetRegister),
20.              reg == kJavaScriptCallCodeStartRegister);
21.          __ LoadCodeObjectEntry(reg, reg);
22.          if (HasCallDescriptorFlag(instr, CallDescriptor::kRetpoline)) {
23.            __ RetpolineCall(reg);
24.          } else {
25.            __ call(reg);
26.          }  }
27.        RecordCallPosition(instr);
28.        frame_access_state()->ClearSPDelta();
29.        break;
30.      }
31.      case kArchCallBuiltinPointer: {
32.  //省略.......................
33.        break;
34.      }}
35.  }

上述第 5 行代码初始 CodeGenerator,第 6 行代码 Run() 方法最终会调用第 10 行 AssembleArchInstruction() 方法以完成汇编码的生成。第 12-34 行代码采用 switch-case 为每条操作码(OPCODE)编写不同的汇编码生成规则。每条操作码对应一个 case,这个 case 描绘了把操作码转换为汇编码的规则。图 1 给出了 AssembleArchInstruction 的调用堆栈。

V8 中 OPCODE 分为两类,一类是体系结构通用的操作码(COMMON_ARCH_OPCODE_LIST),另一类是体系结构专用的操作码(TARGET_ARCH_OPCODE_LIST),具体参见宏模板。

5 收尾 FinalizeJob

收尾工作由 FinalizeJobImpl() 负责,源码如下:

1.  PipelineCompilationJob::Status PipelineCompilationJob::FinalizeJobImpl(
2.      Isolate* isolate) {
3.  //省略.................
4.    MaybeHandle<Code> maybe_code = pipeline_.FinalizeCode();
5.    Handle<Code> code;
6.    if (!maybe_code.ToHandle(&code)) {
7.  //省略.................
8.    }
9.    if (!pipeline_.CommitDependencies(code)) {
10.  //省略.................
11.    }
12.    compilation_info()->SetCode(code);
13.    compilation_info()->native_context().AddOptimizedCode(*code);
14.    RegisterWeakObjectsInOptimizedCode(code, isolate);
15.    return SUCCEEDED;
16.  }

上述第 4 行代码接收优化编译的结果;第 6-8 行代码优化编译失败并返回 false;第 9-11 行代码重试优化编译;第 12 行代码将优化编译结果存储进 Cache,下次再优化该 SharedFunction 时将直接使用 Cache 结果。
技术总结
(1) —Trace-XXX 用于打印编译状态和结果,参见 d8 —help 或 flag-definitions.h;
(2) 优化编译的使能标记的定义在 flag-definitions.h 中;
(3) On-Stack Replacement(OSR)是一种运行时替换函数的栈帧的方法。

好了,今天到这里,下次见。

个人能力有限,有不足与纰漏,欢迎批评指正

微信:qq9123013 备注:v8交流 邮箱:[email protected]

本文由灰豆原创发布

转载注明出处:https://www.anquanke.com/post/id/264846

安全客 - 有思想的安全新媒体


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK