Skip to content

Commit 6e34ddd

Browse files
authored
Add files via upload
1 parent 2b9c6d1 commit 6e34ddd

File tree

28 files changed

+2447
-0
lines changed

28 files changed

+2447
-0
lines changed

chap43/f1.png

101 KB
Loading

chap43/readme.md

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
# 《Chrome V8 源码》43. Turbofan 源码分析
2+
![avatar](../v8.png)
3+
# 1 介绍
4+
接上一篇文章继续说,本文讲解 Turbofan 的工作流程、梳理 PrepareJob、ExecuteJob 和 FinalizeJob 的主要功能以及重要数据结构。
5+
# 2 Turbofan 工作流程
6+
前文提到,Turbofan 分为 NotConcurrent 和 Concurrent 两种工作方式,它们的区别是 NotConcurrent 立即启动优化工作,而 Concurrent 把工作放进同步分发队列。
7+
Concurrent 方式由 GetOptimizedCodeLater() 函数负责,其源码如下:
8+
```c++
9+
1. bool GetOptimizedCodeLater(OptimizedCompilationJob* job, Isolate* isolate) {
10+
2. OptimizedCompilationInfo* compilation_info = job->compilation_info();
11+
3. if (!isolate->optimizing_compile_dispatcher()->IsQueueAvailable()) {
12+
4. if (FLAG_trace_concurrent_recompilation) {
13+
5. //省略................
14+
6. }
15+
7. return false;
16+
8. }
17+
9. if (isolate->heap()->HighMemoryPressure()) {
18+
10. if (FLAG_trace_concurrent_recompilation) {
19+
11. //省略................
20+
12. }
21+
13. return false;
22+
14. }
23+
15. TimerEventScope<TimerEventRecompileSynchronous> timer(isolate);
24+
16. RuntimeCallTimerScope runtimeTimer(
25+
17. isolate, RuntimeCallCounterId::kOptimizeConcurrentPrepare);
26+
18. TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
27+
19. "V8.OptimizeConcurrentPrepare");
28+
20. if (job->PrepareJob(isolate) != CompilationJob::SUCCEEDED) return false;
29+
21. isolate->optimizing_compile_dispatcher()->QueueForOptimization(job);
30+
22. if (FLAG_trace_concurrent_recompilation) {
31+
23. PrintF(" ** Queued ");
32+
24. compilation_info->closure()->ShortPrint();
33+
25. PrintF(" for concurrent optimization.\n");
34+
26. }
35+
27. return true;
36+
28. }
37+
```
38+
上述代码中,第 4-14 行检测工作队列和内存是否满足要求,不满足则停止优化编译。停止优化编译不影响当前 JavaScript 程序的运行,因为 JavaScript 程序正在被解释执行。
39+
第 15-20 行统计 V8 运行信息,与优化编译的功能无关;
40+
第 21 行把优化编译工作 job 添加到工作队列中,并返回结果 true。
41+
NotConcurrent 方式由 GetOptimizedCodeNow() 函数负责,其源码如下:
42+
```c++
43+
1. bool GetOptimizedCodeNow(OptimizedCompilationJob* job, Isolate* isolate) {
44+
2. TimerEventScope<TimerEventRecompileSynchronous> timer(isolate);
45+
3. RuntimeCallTimerScope runtimeTimer(
46+
4. isolate, RuntimeCallCounterId::kOptimizeNonConcurrent);
47+
5. OptimizedCompilationInfo* compilation_info = job->compilation_info();
48+
6. TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
49+
7. "V8.OptimizeNonConcurrent");
50+
8. if (job->PrepareJob(isolate) != CompilationJob::SUCCEEDED ||
51+
9. job->ExecuteJob(isolate->counters()->runtime_call_stats()) !=
52+
10. CompilationJob::SUCCEEDED ||
53+
11. job->FinalizeJob(isolate) != CompilationJob::SUCCEEDED) {
54+
12. if (FLAG_trace_opt) {
55+
13. CodeTracer::Scope scope(isolate->GetCodeTracer());
56+
14. PrintF(scope.file(), "[aborted optimizing ");
57+
15. compilation_info->closure()->ShortPrint(scope.file());
58+
16. PrintF(scope.file(), " because: %s]\n",
59+
17. GetBailoutReason(compilation_info->bailout_reason()));
60+
18. }
61+
19. return false;
62+
20. }
63+
21. // Success!
64+
22. job->RecordCompilationStats(OptimizedCompilationJob::kSynchronous, isolate);
65+
23. DCHECK(!isolate->has_pending_exception());
66+
24. InsertCodeIntoOptimizedCodeCache(compilation_info);
67+
25. job->RecordFunctionCompilation(CodeEventListener::LAZY_COMPILE_TAG, isolate);
68+
26. return true;
69+
27. }
70+
```
71+
上述代码中, 第 2-7 行统计 V8 运行信息,与优化编译的功能无关;
72+
第 8-9 行完成优化编译的所有工作,这些工作由 PrepareJob、ExecuteJob 以及 FinalizeJob 三个函数负责;
73+
第 10-25 行更新编译状态等信息并返回 true。 优化编译同步进行,也就意味着暂停解释执行并等待优化编译的结果。
74+
# 3 准备 PrepareJob
75+
源码如下:
76+
```c++
77+
1. CompilationJob::Status OptimizedCompilationJob::PrepareJob(Isolate* isolate) {
78+
2. DCHECK_EQ(ThreadId::Current(), isolate->thread_id());
79+
3. DisallowJavascriptExecution no_js(isolate);
80+
4. if (FLAG_trace_opt && compilation_info()->IsOptimizing()) {
81+
5. //省略..............
82+
6. }
83+
7. // Delegate to the underlying implementation.
84+
8. DCHECK_EQ(state(), State::kReadyToPrepare);
85+
9. ScopedTimer t(&time_taken_to_prepare_);
86+
10. return UpdateState(PrepareJobImpl(isolate), State::kReadyToExecute);
87+
11. }
88+
```
89+
上述代码中,第 2-3 行做状态检测、第 4-6 行设置打印出输信息;第 10 行 UpdateState 更新状态信息,PrepareJobImpl 完成初始化工作,其源码如下:
90+
```c++
91+
1. PipelineCompilationJob::Status PipelineCompilationJob::PrepareJobImpl(
92+
2. Isolate* isolate) {
93+
3. PipelineJobScope scope(&data_, isolate->counters()->runtime_call_stats());
94+
4. if (compilation_info()->bytecode_array()->length() >
95+
5. FLAG_max_optimized_bytecode_size) {
96+
6. return AbortOptimization(BailoutReason::kFunctionTooBig);
97+
7. }
98+
8. if (!FLAG_always_opt) {
99+
9. compilation_info()->MarkAsBailoutOnUninitialized();
100+
10. }
101+
11. if (FLAG_turbo_loop_peeling) {
102+
12. compilation_info()->MarkAsLoopPeelingEnabled();
103+
13. }
104+
14. if (FLAG_turbo_inlining) {
105+
15. compilation_info()->MarkAsInliningEnabled();
106+
16. }
107+
17. PoisoningMitigationLevel load_poisoning =
108+
18. PoisoningMitigationLevel::kDontPoison;
109+
19. if (FLAG_untrusted_code_mitigations) {
110+
20. load_poisoning = PoisoningMitigationLevel::kPoisonCriticalOnly;
111+
21. }
112+
22. compilation_info()->SetPoisoningMitigationLevel(load_poisoning);
113+
23. if (FLAG_turbo_allocation_folding) {
114+
24. compilation_info()->MarkAsAllocationFoldingEnabled();
115+
25. }
116+
26. if (compilation_info()->closure()->raw_feedback_cell().map() ==
117+
27. ReadOnlyRoots(isolate).one_closure_cell_map() &&
118+
28. !compilation_info()->is_osr()) {
119+
29. compilation_info()->MarkAsFunctionContextSpecializing();
120+
30. data_.ChooseSpecializationContext();
121+
31. }
122+
32. if (compilation_info()->is_source_positions_enabled()) {
123+
33. SharedFunctionInfo::EnsureSourcePositionsAvailable(
124+
34. isolate, compilation_info()->shared_info());
125+
35. }
126+
36. data_.set_start_source_position(
127+
37. compilation_info()->shared_info()->StartPosition());
128+
38. linkage_ = new (compilation_info()->zone()) Linkage(
129+
39. Linkage::ComputeIncoming(compilation_info()->zone(), compilation_info()));
130+
40. if (compilation_info()->is_osr()) data_.InitializeOsrHelper();
131+
41. Deoptimizer::EnsureCodeForDeoptimizationEntries(isolate);
132+
42. pipeline_.Serialize();
133+
43. if (!data_.broker()->is_concurrent_inlining()) {
134+
44. if (!pipeline_.CreateGraph()) {
135+
45. CHECK(!isolate->has_pending_exception());
136+
46. return AbortOptimization(BailoutReason::kGraphBuildingFailed);
137+
47. }
138+
48. }
139+
49. return SUCCEEDED;
140+
50. }
141+
```
142+
上述代码中,第 4-7 行检查 BytecodeArray 的长度是否超过最大长度限制;
143+
第 8-10 行检查 always_optimization 使能标记,它的作用是 always try to optimize functions;
144+
第 11-25 行检测 loop_peeling、inling、allocation_folding 使能标记,详细说明参见 flag-definitions.h 文件;
145+
第 26-37 行设置 context、OSR、源码信息;
146+
第 38 行创建编译需要的 link 信息;
147+
第 44 行创建 V8.TFGraph,这之后不再需要 `T<Node>`了;
148+
# 4 编译 ExecuteJob
149+
ExecuteJob() 中调用 ExecuteJobImpl() 来完成优化编译的主体工作,其源码如下:
150+
```c++
151+
1. PipelineCompilationJob::Status PipelineCompilationJob::ExecuteJobImpl(
152+
2. RuntimeCallStats* stats) {
153+
3. PipelineJobScope scope(&data_, stats);
154+
4. if (data_.broker()->is_concurrent_inlining()) {
155+
5. //省略.....
156+
6. }
157+
7. bool success;
158+
8. if (FLAG_turboprop) {
159+
9. success = pipeline_.OptimizeGraphForMidTier(linkage_);
160+
10. } else {
161+
11. success = pipeline_.OptimizeGraph(linkage_);
162+
12. }
163+
13. if (!success) return FAILED;
164+
14. pipeline_.AssembleCode(linkage_);
165+
15. return SUCCEEDED;
166+
```
167+
上述代码的核心功能就两个,一个基于图的优化功能(OptimizeGraphForMidTier 和 OptimizeGraph),另一个汇编生成器(AssembleCode)。优化功能的源码如下:
168+
```c++
169+
bool PipelineImpl::OptimizeGraphForMidTier(Linkage* linkage) {
170+
Run<TyperPhase>(data->CreateTyper());
171+
RunPrintAndVerify(TyperPhase::phase_name());
172+
Run<TypedLoweringPhase>();
173+
RunPrintAndVerify(TypedLoweringPhase::phase_name());
174+
Run<LoopExitEliminationPhase>();
175+
//省略..............
176+
}
177+
//分隔线....................
178+
bool PipelineImpl::OptimizeGraph(Linkage* linkage) {
179+
PipelineData* data = this->data_;
180+
data->BeginPhaseKind("V8.TFLowering");
181+
Run<TyperPhase>(data->CreateTyper());
182+
RunPrintAndVerify(TyperPhase::phase_name());
183+
Run<TypedLoweringPhase>();
184+
RunPrintAndVerify(TypedLoweringPhase::phase_name());
185+
//省略..............
186+
}
187+
```
188+
上述代码中,每一个 Run 方法代表过了一种优化技术,每种优化技术的实现都有对应的数据结构,本文不做讲解。
189+
汇编生成器(AssembleCode)的源码如下:
190+
```c++
191+
1. void PipelineImpl::AssembleCode(Linkage* linkage,
192+
2. std::unique_ptr<AssemblerBuffer> buffer) {
193+
3. PipelineData* data = this->data_;
194+
4. data->BeginPhaseKind("V8.TFCodeGeneration");
195+
5. data->InitializeCodeGenerator(linkage, std::move(buffer));
196+
6. Run<AssembleCodePhase>();
197+
7. //省略.....
198+
8. }
199+
9. //分隔.................
200+
10. CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
201+
11. Instruction* instr) {
202+
12. switch (arch_opcode) {
203+
13. case kArchCallCodeObject: {
204+
14. if (HasImmediateInput(instr, 0)) {
205+
15. //省略.......................
206+
16. } else {
207+
17. Register reg = i.InputRegister(0);
208+
18. DCHECK_IMPLIES(
209+
19. HasCallDescriptorFlag(instr, CallDescriptor::kFixedTargetRegister),
210+
20. reg == kJavaScriptCallCodeStartRegister);
211+
21. __ LoadCodeObjectEntry(reg, reg);
212+
22. if (HasCallDescriptorFlag(instr, CallDescriptor::kRetpoline)) {
213+
23. __ RetpolineCall(reg);
214+
24. } else {
215+
25. __ call(reg);
216+
26. } }
217+
27. RecordCallPosition(instr);
218+
28. frame_access_state()->ClearSPDelta();
219+
29. break;
220+
30. }
221+
31. case kArchCallBuiltinPointer: {
222+
32. //省略.......................
223+
33. break;
224+
34. }}
225+
35. }
226+
```
227+
上述第 5 行代码初始 CodeGenerator,第 6 行代码 Run() 方法最终会调用第 10 行 AssembleArchInstruction() 方法以完成汇编码的生成。第 12-34 行代码采用 switch-case 为每条操作码(OPCODE)编写不同的汇编码生成规则。每条操作码对应一个 case,这个 case 描绘了把操作码转换为汇编码的规则。图 1 给出了 AssembleArchInstruction 的调用堆栈。
228+
![avatar](f1.png)
229+
V8 中 OPCODE 分为两类,一类是体系结构通用的操作码(COMMON_ARCH_OPCODE_LIST),另一类是体系结构专用的操作码(TARGET_ARCH_OPCODE_LIST),具体参见宏模板。
230+
# 5 收尾 FinalizeJob
231+
收尾工作由 FinalizeJobImpl() 负责,源码如下:
232+
```c++
233+
1. PipelineCompilationJob::Status PipelineCompilationJob::FinalizeJobImpl(
234+
2. Isolate* isolate) {
235+
3. //省略.................
236+
4. MaybeHandle<Code> maybe_code = pipeline_.FinalizeCode();
237+
5. Handle<Code> code;
238+
6. if (!maybe_code.ToHandle(&code)) {
239+
7. //省略.................
240+
8. }
241+
9. if (!pipeline_.CommitDependencies(code)) {
242+
10. //省略.................
243+
11. }
244+
12. compilation_info()->SetCode(code);
245+
13. compilation_info()->native_context().AddOptimizedCode(*code);
246+
14. RegisterWeakObjectsInOptimizedCode(code, isolate);
247+
15. return SUCCEEDED;
248+
16. }
249+
```
250+
上述第 4 行代码接收优化编译的结果;第 6-8 行代码优化编译失败并返回 false;第 9-11 行代码重试优化编译;第 12 行代码将优化编译结果存储进 Cache,下次再优化该 SharedFunction 时将直接使用 Cache 结果。
251+
**技术总结**
252+
**(1)** --Trace-XXX 用于打印编译状态和结果,参见 d8 --help 或 flag-definitions.h;
253+
**(2)** 优化编译的使能标记的定义在 flag-definitions.h 中;
254+
**(3)** On-Stack Replacement(OSR)是一种运行时替换函数的栈帧的方法。
255+
# 新文章介绍
256+
**《Chrome V8 Bug》** 系列文章即将上线。
257+
《Chrome V8 Bug》系列文章的目的是解释漏洞的产生原因,并向你展示这些漏洞如何影响 V8 的正确性。其他的漏洞文章大多从安全研究的角度分析,讲述如何设计与使用 PoC。而本系列文章是从源码研究的角度来写的,分析 PoC 在 V8 中的执行细节,讲解为什么 Poc 要这样设计。当然,学习 Poc 的设计与使用,是 V8 安全研究的很好的出发点,所以,对于希望深入学习 V8 源码和 PoC 原理的人来说,本系列文章也是很有价值的介绍性读物。
258+
本系列文章主要讲解 https://bugs.chromium.org/p/v8/issues 的内容,每篇文章讲解一个 issue。如果你有想学习的 issue 也可以告诉我,我会优先分析讲解。
259+
260+
好了,今天到这里,下次见。
261+
**个人能力有限,有不足与纰漏,欢迎批评指正**
262+
**微信:qq9123013 备注:v8交流 邮箱:[email protected]**
263+

chap44/f1.png

266 KB
Loading

0 commit comments

Comments
 (0)