盒子
盒子
文章目录
  1. 0x01 前言
  2. 0x02 正文
  3. InliningPhase:
  4. InliningPhase:
  5. EarlyGraphTrimmingPhase:
  6. TyperPhase:
  7. TypedLoweringPhase:
    1. DeadCodeElimination
    2. JSCreateLowering:

v8-JIT部分源码分析

0x01 前言

吃灰很久了…是之前实习的时候分析的,后续因为某种原因没有继续搞浏览器了,所以就一直没管。现在让我看我也看不太懂了…所以就索性发出来好了,能帮助到别人就更好了,帮不到就当一个古老随笔了。如果有什么疑问的话,也别问我,因为我也不知道。

0x02 正文

测试代码:

1
2
3
4
5
6
7
8
9
10
function opt(){
var a = [1,2,3,4];
return a[0];
}

for(var i=0;i < 100000;i++){
opt();
}

opt();

程序执行流程:

总体流程在PipelineImpl::OptimizeGraph,分了好几个阶段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
bool PipelineImpl::OptimizeGraph(Linkage* linkage) {
PipelineData* data = this->data_;

data->BeginPhaseKind("V8.TFLowering");

// Type the graph and keep the Typer running such that new nodes get
// automatically typed when they are created.
Run<TyperPhase>(data->CreateTyper()); //typer阶段
RunPrintAndVerify(TyperPhase::phase_name());
Run<TypedLoweringPhase>(); //typerlowering
RunPrintAndVerify(TypedLoweringPhase::phase_name());

if (data->info()->is_loop_peeling_enabled()) {
Run<LoopPeelingPhase>(); //loopeeling
RunPrintAndVerify(LoopPeelingPhase::phase_name(), true);
} else {
Run<LoopExitEliminationPhase>(); //LoopExitElimination
RunPrintAndVerify(LoopExitEliminationPhase::phase_name(), true);
}

if (FLAG_turbo_load_elimination) {
Run<LoadEliminationPhase>(); //LoadElimination
RunPrintAndVerify(LoadEliminationPhase::phase_name());
}
data->DeleteTyper();

if (FLAG_turbo_escape) {
Run<EscapeAnalysisPhase>(); //EscapeAnalysis
if (data->compilation_failed()) {
info()->AbortOptimization(
BailoutReason::kCyclicObjectStateDetectedInEscapeAnalysis);
data->EndPhaseKind();
return false;
}
RunPrintAndVerify(EscapeAnalysisPhase::phase_name());
}

if (FLAG_assert_types) {
Run<TypeAssertionsPhase>(); //TypeAssertions
RunPrintAndVerify(TypeAssertionsPhase::phase_name());
}

// Perform simplified lowering. This has to run w/o the Typer decorator,
// because we cannot compute meaningful types anyways, and the computed types
// might even conflict with the representation/truncation logic.
Run<SimplifiedLoweringPhase>(); //SimplifiedLowering
RunPrintAndVerify(SimplifiedLoweringPhase::phase_name(), true);

// From now on it is invalid to look at types on the nodes, because the types
// on the nodes might not make sense after representation selection due to the
// way we handle truncations; if we'd want to look at types afterwards we'd
// essentially need to re-type (large portions of) the graph.

// In order to catch bugs related to type access after this point, we now
// remove the types from the nodes (currently only in Debug builds).
#ifdef DEBUG
Run<UntyperPhase>();
RunPrintAndVerify(UntyperPhase::phase_name(), true);
#endif
//......
}

../../src/runtime/runtime-compiler.cc:228

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
RUNTIME_FUNCTION(Runtime_CompileForOnStackReplacement) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);

// Only reachable when OST is enabled.
CHECK(FLAG_use_osr);

// Determine frame triggering OSR request.
JavaScriptFrameIterator it(isolate);
JavaScriptFrame* frame = it.frame();
DCHECK_EQ(frame->function(), *function);
DCHECK(frame->is_interpreted());

// Determine the entry point for which this OSR request has been fired and
// also disarm all back edges in the calling code to stop new requests.
BailoutId ast_id = DetermineEntryAndDisarmOSRForInterpreter(frame);
DCHECK(!ast_id.IsNone());

MaybeHandle<Code> maybe_result;
if (IsSuitableForOnStackReplacement(isolate, function)) {
if (FLAG_trace_osr) {
PrintF("[OSR - Compiling: ");
function->PrintName();
PrintF(" at AST id %d]\n", ast_id.ToInt());
}
maybe_result = Compiler::GetOptimizedCodeForOSR(function, ast_id, frame); //important
}

../../src/codegen/compiler.cc:2280

1
2
3
4
5
6
7
8
MaybeHandle<Code> Compiler::GetOptimizedCodeForOSR(Handle<JSFunction> function,
BailoutId osr_offset,
JavaScriptFrame* osr_frame) {
DCHECK(!osr_offset.IsNone());
DCHECK_NOT_NULL(osr_frame);
return GetOptimizedCode(function, ConcurrencyMode::kNotConcurrent, osr_offset, //important
osr_frame);
}

../../src/codegen/compiler.cc:899

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
MaybeHandle<Code> GetOptimizedCode(Handle<JSFunction> function,
ConcurrencyMode mode,
BailoutId osr_offset = BailoutId::None(),
JavaScriptFrame* osr_frame = nullptr) {
Isolate* isolate = function->GetIsolate();
Handle<SharedFunctionInfo> shared(function->shared(), isolate);

// Make sure we clear the optimization marker on the function so that we
// don't try to re-optimize.
if (function->HasOptimizationMarker()) {
function->ClearOptimizationMarker();
}

if (shared->optimization_disabled() &&
shared->disable_optimization_reason() == BailoutReason::kNeverOptimize) {
return MaybeHandle<Code>();
}

if (isolate->debug()->needs_check_on_function_call()) {
// Do not optimize when debugger needs to hook into every call.
return MaybeHandle<Code>();
}

// If code was pending optimization for testing, delete remove the entry
// from the table that was preventing the bytecode from being flushed
if (V8_UNLIKELY(FLAG_testing_d8_test_runner)) {
PendingOptimizationTable::FunctionWasOptimized(isolate, function);
}

Handle<Code> cached_code;
if (GetCodeFromOptimizedCodeCache(function, osr_offset)
.ToHandle(&cached_code)) {
if (FLAG_trace_opt) {
PrintF("[found optimized code for ");
function->ShortPrint();
if (!osr_offset.IsNone()) {
PrintF(" at OSR AST id %d", osr_offset.ToInt());
}
PrintF("]\n");
}
return cached_code;
}

// Reset profiler ticks, function is no longer considered hot.
DCHECK(shared->is_compiled());
function->feedback_vector().set_profiler_ticks(0);

VMState<COMPILER> state(isolate);
TimerEventScope<TimerEventOptimizeCode> optimize_code_timer(isolate);
RuntimeCallTimerScope runtimeTimer(isolate,
RuntimeCallCounterId::kOptimizeCode);
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"), "V8.OptimizeCode");

DCHECK(!isolate->has_pending_exception());
PostponeInterruptsScope postpone(isolate);
bool has_script = shared->script().IsScript();
// BUG(5946): This DCHECK is necessary to make certain that we won't
// tolerate the lack of a script without bytecode.
DCHECK_IMPLIES(!has_script, shared->HasBytecodeArray());
std::unique_ptr<OptimizedCompilationJob> job(
compiler::Pipeline::NewCompilationJob(isolate, function, has_script));
OptimizedCompilationInfo* compilation_info = job->compilation_info();

compilation_info->SetOptimizingForOsr(osr_offset, osr_frame);

// Do not use TurboFan if we need to be able to set break points.
if (compilation_info->shared_info()->HasBreakInfo()) {
compilation_info->AbortOptimization(BailoutReason::kFunctionBeingDebugged);
return MaybeHandle<Code>();
}

// Do not use TurboFan if optimization is disabled or function doesn't pass
// turbo_filter.
if (!FLAG_opt || !shared->PassesFilter(FLAG_turbo_filter)) {
compilation_info->AbortOptimization(BailoutReason::kOptimizationDisabled);
return MaybeHandle<Code>();
}

// In case of concurrent recompilation, all handles below this point will be
// allocated in a deferred handle scope that is detached and handed off to
// the background thread when we return.
base::Optional<CompilationHandleScope> compilation;
if (mode == ConcurrencyMode::kConcurrent) {
compilation.emplace(isolate, compilation_info);
}

// All handles below will be canonicalized.
CanonicalHandleScope canonical(isolate);

// Reopen handles in the new CompilationHandleScope.
compilation_info->ReopenHandlesInNewHandleScope(isolate);

if (mode == ConcurrencyMode::kConcurrent) {
if (GetOptimizedCodeLater(job.get(), isolate)) {
job.release(); // The background recompile job owns this now.

// Set the optimization marker and return a code object which checks it.
function->SetOptimizationMarker(OptimizationMarker::kInOptimizationQueue);
DCHECK(function->IsInterpreted() ||
(!function->is_compiled() && function->shared().IsInterpreted()));
DCHECK(function->shared().HasBytecodeArray());
return BUILTIN_CODE(isolate, InterpreterEntryTrampoline);
}
} else {
if (GetOptimizedCodeNow(job.get(), isolate)) //important
return compilation_info->code();
}

../../src/codegen/compiler.cc:739

1
2
3
4
5
6
7
8
9
10
11
12
13
14
bool GetOptimizedCodeNow(OptimizedCompilationJob* job, Isolate* isolate) {
TimerEventScope<TimerEventRecompileSynchronous> timer(isolate);
RuntimeCallTimerScope runtimeTimer(
isolate, RuntimeCallCounterId::kRecompileSynchronous);
OptimizedCompilationInfo* compilation_info = job->compilation_info();
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
"V8.RecompileSynchronous");

if (job->PrepareJob(isolate) != CompilationJob::SUCCEEDED ||
job->ExecuteJob() != CompilationJob::SUCCEEDED || //important
job->FinalizeJob(isolate) != CompilationJob::SUCCEEDED) {
if (FLAG_trace_opt) {
PrintF("[aborted optimizing ");
compilation_info->closure()->ShortPrint();

../../src/codegen/compiler.cc:227

1
2
3
4
5
6
7
CompilationJob::Status OptimizedCompilationJob::ExecuteJob() {
DisallowHeapAccess no_heap_access;
// Delegate to the underlying implementation.
DCHECK_EQ(state(), State::kReadyToExecute);
ScopedTimer t(&time_taken_to_execute_);
return UpdateState(ExecuteJobImpl(), State::kReadyToFinalize); //important
}

../../src/compiler/pipeline.cc:1092

1
2
3
4
5
6
7
8
9
PipelineCompilationJob::Status PipelineCompilationJob::ExecuteJobImpl() {
TRACE_EVENT_WITH_FLOW1(
TRACE_DISABLED_BY_DEFAULT("v8.compile"), "v8.optimizingCompile.execute",
this, TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "function",
compilation_info()->shared_info()->TraceIDRef());
if (!pipeline_.OptimizeGraph(linkage_)) return FAILED; //important
pipeline_.AssembleCode(linkage_);
return SUCCEEDED;
}

../../src/compiler/pipeline.cc:2388

之后往下的就是到我断点的CheckBounds处了,暂且先不讨论。

../../src/codegen/compiler.cc:739大致就能看出点事来。比如说断点时执行的是ExecuteJob,之前还有个PrepareJob。大概查看一下PrepareJob的引用就能知道该函数执行的是编译优化的前期准备工作。而ExecuteJob则是编译优化的执行工作。

先从准备处开始下手。

首先是该函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
PipelineCompilationJob::Status PipelineCompilationJob::PrepareJobImpl(
Isolate* isolate) {
TRACE_EVENT_WITH_FLOW1(
TRACE_DISABLED_BY_DEFAULT("v8.compile"), "v8.optimizingCompile.prepare",
this, TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "function",
compilation_info()->shared_info()->TraceIDRef());
if (compilation_info()->bytecode_array()->length() >
FLAG_max_optimized_bytecode_size) {
return AbortOptimization(BailoutReason::kFunctionTooBig);
}

if (!FLAG_always_opt) {
compilation_info()->MarkAsBailoutOnUninitialized();
}
if (FLAG_turbo_loop_peeling) {
compilation_info()->MarkAsLoopPeelingEnabled();
}
if (FLAG_turbo_inlining) {
compilation_info()->MarkAsInliningEnabled();
}
if (FLAG_inline_accessors) {
compilation_info()->MarkAsAccessorInliningEnabled();
}

// This is the bottleneck for computing and setting poisoning level in the
// optimizing compiler.
PoisoningMitigationLevel load_poisoning =
PoisoningMitigationLevel::kDontPoison;
if (FLAG_untrusted_code_mitigations) {
// For full mitigations, this can be changed to
// PoisoningMitigationLevel::kPoisonAll.
load_poisoning = PoisoningMitigationLevel::kPoisonCriticalOnly;
}
compilation_info()->SetPoisoningMitigationLevel(load_poisoning);

if (FLAG_turbo_allocation_folding) {
compilation_info()->MarkAsAllocationFoldingEnabled();
}

// Determine whether to specialize the code for the function's context.
// We can't do this in the case of OSR, because we want to cache the
// generated code on the native context keyed on SharedFunctionInfo.
// TODO(mythria): Check if it is better to key the OSR cache on JSFunction and
// allow context specialization for OSR code.
if (compilation_info()->closure()->raw_feedback_cell().map() ==
ReadOnlyRoots(isolate).one_closure_cell_map() &&
!compilation_info()->is_osr()) {
compilation_info()->MarkAsFunctionContextSpecializing();
data_.ChooseSpecializationContext();
}

if (compilation_info()->is_source_positions_enabled()) {
SharedFunctionInfo::EnsureSourcePositionsAvailable(
isolate, compilation_info()->shared_info());
}

data_.set_start_source_position(
compilation_info()->shared_info()->StartPosition());

linkage_ = new (compilation_info()->zone()) Linkage(
Linkage::ComputeIncoming(compilation_info()->zone(), compilation_info()));

if (compilation_info()->is_osr()) data_.InitializeOsrHelper();

// Make sure that we have generated the deopt entries code. This is in order
// to avoid triggering the generation of deopt entries later during code
// assembly.
Deoptimizer::EnsureCodeForDeoptimizationEntries(isolate);

pipeline_.Serialize();

if (!FLAG_concurrent_inlining) {
if (!pipeline_.CreateGraph()) {
CHECK(!isolate->has_pending_exception());
return AbortOptimization(BailoutReason::kGraphBuildingFailed);
}
}

return SUCCEEDED;
}

检查输入中的编译优化信息,如有,则在标志中标记并加入。

而后生成IR图表:pipeline_.CreateGraph()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
bool PipelineImpl::CreateGraph() {
PipelineData* data = this->data_;

data->BeginPhaseKind("V8.TFGraphCreation");

Run<GraphBuilderPhase>();
RunPrintAndVerify(GraphBuilderPhase::phase_name(), true);

// Perform function context specialization and inlining (if enabled).
Run<InliningPhase>();
RunPrintAndVerify(InliningPhase::phase_name(), true);

// Remove dead->live edges from the graph.
Run<EarlyGraphTrimmingPhase>();
RunPrintAndVerify(EarlyGraphTrimmingPhase::phase_name(), true);

// Determine the Typer operation flags.
{
SharedFunctionInfoRef shared_info(data->broker(), info()->shared_info());
if (is_sloppy(shared_info.language_mode()) &&
shared_info.IsUserJavaScript()) {
// Sloppy mode functions always have an Object for this.
data->AddTyperFlag(Typer::kThisIsReceiver);
}
if (IsClassConstructor(shared_info.kind())) {
// Class constructors cannot be [[Call]]ed.
data->AddTyperFlag(Typer::kNewTargetIsReceiver);
}
}

// Run the type-sensitive lowerings and optimizations on the graph.
{
if (!FLAG_concurrent_inlining) {
Run<HeapBrokerInitializationPhase>();
Run<CopyMetadataForConcurrentCompilePhase>();
data->broker()->StopSerializing();
}
}

data->EndPhaseKind();

return true;
}

从第六行起始就能知道该函数是JIT Lowering中的各个小阶段的执行函数:

先是GraphBuilderPhase、然后InliningPhase、EarlyGraphTrimmingPhase、HeapBrokerInitializationPhase、CopyMetadataForConcurrentCompilePhase。

先从GraphBuilderPhase说起。

我给其中的一些加了注释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct GraphBuilderPhase {
static const char* phase_name() { return "V8.TFBytecodeGraphBuilder"; }

void Run(PipelineData* data, Zone* temp_zone) {
BytecodeGraphBuilderFlags flags;

//检查是否有kAnalyzeEnvironmentLiveness标志

if (data->info()->is_analyze_environment_liveness()) {
flags |= BytecodeGraphBuilderFlag::kAnalyzeEnvironmentLiveness;
}

//检查是否有kBailoutOnUninitialized标志

if (data->info()->is_bailout_on_uninitialized()) {
flags |= BytecodeGraphBuilderFlag::kBailoutOnUninitialized;
}

JSFunctionRef closure(data->broker(), data->info()->closure());

跟进JSFunctionRef closure这句:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
ObjectRef::ObjectRef(JSHeapBroker* broker, Handle<Object> object,
bool check_type)
: broker_(broker) {
switch (broker->mode()) { //(JSHeapBroker::kDisabled)
case JSHeapBroker::kSerialized:
data_ = broker->GetData(object);
break;
case JSHeapBroker::kSerializing:
data_ = broker->GetOrCreateData(object);
break;
case JSHeapBroker::kDisabled: {
RefsMap::Entry* entry =
broker->refs_->LookupOrInsert(object.address(), broker->zone());
ObjectData** storage = &(entry->value);
if (*storage == nullptr) {
AllowHandleDereference handle_dereference;
entry->value = new (broker->zone())
ObjectData(broker, storage, object,
object->IsSmi() ? kSmi : kUnserializedHeapObject);
}
data_ = *storage;
break;
}
case JSHeapBroker::kRetired:
UNREACHABLE();
}
if (!data_) { // TODO(mslekova): Remove once we're on the background thread.
AllowHandleDereference handle_dereference;
object->Print();
}
CHECK_WITH_MSG(data_ != nullptr, "Object is not known to the heap broker");
}
**/

整个结构的继承性:
ObjectRef:
HeapObjectRef:
JSReceiverRef:
JSObjectRef:
JSFunctionRef:

接上源码:

1
2
3
4
5
6
7
8
9
10
11
12
CallFrequency frequency(1.0f);                      //设定调用频率
BuildGraphFromBytecode(
data->broker(), temp_zone, closure.shared(), closure.feedback_vector(),
data->info()->osr_offset(), data->jsgraph(), frequency,
data->source_positions(), SourcePosition::kNotInlined, flags,
&data->info()->tick_counter());
}
};



//最后的bytecode --> ../../src/compiler/bytecode-graph-builder.cc:4094
1
builder.CreateGraph();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void BytecodeGraphBuilder::CreateGraph() {
DisallowHeapAccessIf disallow_heap_access(FLAG_concurrent_inlining);
SourcePositionTable::Scope pos_scope(source_positions_, start_position_);

// Set up the basic structure of the graph. Outputs for {Start} are the formal
// parameters (including the receiver) plus new target, number of arguments,
// context and closure.
int actual_parameter_count = bytecode_array().parameter_count() + 4; //定义参数个数
graph()->SetStart(graph()->NewNode(common()->Start(actual_parameter_count))); //新建一个start
//结点并设置Start
//common结点统一在common-operator.cc中

Environment env(this, bytecode_array().register_count(),
bytecode_array().parameter_count(),
bytecode_array().incoming_new_target_or_generator_register(),
graph()->start());
set_environment(&env); //初始化环境,设置接收者、参数、注册者

VisitBytecodes();

// Finish the basic structure of the graph.
DCHECK_NE(0u, exit_controls_.size());
int const input_count = static_cast<int>(exit_controls_.size());
Node** const inputs = &exit_controls_.front();
Node* end = graph()->NewNode(common()->End(input_count), input_count, inputs);
graph()->SetEnd(end);
}

上面的BytecodeGraphBuilder::CreateGraph()VisitBytecoeds中:

AdvanceToOsrEntryAndPeelLoops();
该函数先剥离osr,优化n个循环而后逐字节码迭代。从osr loop循环开始,一直循环到最外圈的loop_0,但是期间不为任何父loop创建跳转节点,但是还原字节码,记录jumploop偏移量并且构建与osrloop对应的新节点,期间还要注意合并环境以及异常处理程序。(只保留最外层loop和osrloop之间相对应的节点)
迭代遍历字节码,并检测是否有shot bytecode
当不在并发内联并且有shot bytecode时,用shot bytecode优化函数特性去设置计数器的使用。

VisitBytecodes之后,设置IR图的end节点(exit_controls_控制退出函数),结束基本结构。

InliningPhase:

继续看,接下来是InliningPhase,部分注释写在里面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
struct InliningPhase {
static const char* phase_name() { return "V8.TFInlining"; }

void Run(PipelineData* data, Zone* temp_zone) {

//定义优化编译信息,例如(kInliningEnabled、kBailoutOnUninitialized)这种标记flag
OptimizedCompilationInfo* info = data->info();
//创建完整节点图reduce实例(Replace、ReduceTop、ReplaceWithValue、pop、push等)
GraphReducer graph_reducer(temp_zone, data->graph(), &info->tick_counter(),
data->jsgraph()->Dead());
//创建dead节点消除实例(end、node、loopormerge、loopexit、BranchOrSwitch等)
DeadCodeElimination dead_code_elimination(&graph_reducer, data->graph(),
data->common(), temp_zone);
//创建冗余检查点消除实例(checkpoint)
CheckpointElimination checkpoint_elimination(&graph_reducer);
//创建公共操作符reduce实例(branch、merge、phi、return、swith等)
CommonOperatorReducer common_reducer(&graph_reducer, data->graph(),
data->broker(), data->common(),
data->machine(), temp_zone);
//创建对JSConstruct和JSCall节点的reduce实例(ReduceObjectIs、ReduceObjectCreate等)
JSCallReducer call_reducer(&graph_reducer, data->jsgraph(), data->broker(),
data->info()->is_bailout_on_uninitialized()
? JSCallReducer::kBailoutOnUninitialized
: JSCallReducer::kNoFlags,
data->dependencies());
//创建Load、StoreContext节点的reduce实例(ReduceParameter、JSLoadContext、JSStoreContext、SimplifyJSStoreContext、SimplifyJSStoreContext)
JSContextSpecialization context_specialization(
&graph_reducer, data->jsgraph(), data->broker(),
data->specialization_context(),
data->info()->is_function_context_specializing()
? data->info()->closure()
: MaybeHandle<JSFunction>());
//设置JSNativeContextSpecialization->flag为kNoFlags
JSNativeContextSpecialization::Flags flags =
JSNativeContextSpecialization::kNoFlags;
//根据输入info标识添加相应的flag标识
if (data->info()->is_accessor_inlining_enabled()) {
flags |= JSNativeContextSpecialization::kAccessorInliningEnabled;
}
if (data->info()->is_bailout_on_uninitialized()) {
flags |= JSNativeContextSpecialization::kBailoutOnUninitialized;
}

// Passing the OptimizedCompilationInfo's shared zone here as
// JSNativeContextSpecialization allocates out-of-heap objects
// that need to live until code generation.

//创建Load、StoreGlobal节点的reduce实例(LoadGlobal、StoreGlobal、LoadNamed、StoreNamed、ReduceJSAdd、ReduceJSAsyncFunctionEnter等)
JSNativeContextSpecialization native_context_specialization(
&graph_reducer, data->jsgraph(), data->broker(), flags,
data->dependencies(), temp_zone, info->zone());
//具体不清楚该函数功能是什么,貌似是关于多态内联的
JSInliningHeuristic inlining(&graph_reducer,
data->info()->is_inlining_enabled()
? JSInliningHeuristic::kGeneralInlining
: JSInliningHeuristic::kRestrictedInlining,
temp_zone, data->info(), data->jsgraph(),
data->broker(), data->source_positions());
//创建JS级别运行调用的lowers(ReduceDeoptimizeNow、ReduceIsSmi、ReduceToString)
JSIntrinsicLowering intrinsic_lowering(&graph_reducer, data->jsgraph(),
data->broker());
//添加以上各个reduce实例进IR图
AddReducer(data, &graph_reducer, &dead_code_elimination);
AddReducer(data, &graph_reducer, &checkpoint_elimination);
AddReducer(data, &graph_reducer, &common_reducer);
AddReducer(data, &graph_reducer, &native_context_specialization);
AddReducer(data, &graph_reducer, &context_specialization);
AddReducer(data, &graph_reducer, &intrinsic_lowering);
AddReducer(data, &graph_reducer, &call_reducer);
AddReducer(data, &graph_reducer, &inlining);
//执行reduceGraph操作
graph_reducer.ReduceGraph();
}
};

AddReducer会将对应的reducer加入到reducers_中,reducers是vector.

1
2
3
void GraphReducer::AddReducer(Reducer* reducer) {
reducers_.push_back(reducer);
}

最后执行graph_reducer.ReduceGraph()

ReducerGraph:

1
void GraphReducer::ReduceGraph() { ReduceNode(graph()->end()); }

该函数以graph的end节点开始深度优先搜索。

整个函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void GraphReducer::ReduceNode(Node* node) {
DCHECK(stack_.empty());
DCHECK(revisit_.empty());
Push(node);//将node push 到 stack_中
for (;;) {
if (!stack_.empty()) { //stack_不为空
// Process the node on the top of the stack, potentially pushing more or
// popping the node off the stack.
ReduceTop();
} else if (!revisit_.empty()) {//revisit_不为空
// If the stack becomes empty, revisit any nodes in the revisit queue.
Node* const node = revisit_.front();
revisit_.pop();
if (state_.Get(node) == State::kRevisit) {
// state can change while in queue.
Push(node);
}
} else {
// Run all finalizers.
for (Reducer* const reducer : reducers_) reducer->Finalize();

// Check if we have new nodes to revisit.
if (revisit_.empty()) break;
}
}//for (;;)
DCHECK(revisit_.empty());
DCHECK(stack_.empty());
}

优先搜索时维护两个栈:revisit_和stack_。revisit_存放再次访问的节点。

ReduceTop()是深度优先搜索的函数,将stack栈搜索完成之后清空,然后往revisit栈中搜索。

ReduceTop:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
void GraphReducer::ReduceTop() {
NodeState& entry = stack_.top();
Node* node = entry.node;//获取stack顶部的node
DCHECK_EQ(State::kOnStack, state_.Get(node));

if (node->IsDead()) return Pop(); // Node was killed while on stack.

Node::Inputs node_inputs = node->inputs();//以该node的input_location和input_count初始化Node::Inputs

// Recurse on an input if necessary.
int start = entry.input_index < node_inputs.count() ? entry.input_index : 0;
for (int i = start; i < node_inputs.count(); ++i) { //遍历该节点的input节点
Node* input = node_inputs[i];
if (input != node && Recurse(input)) { //如果该input节点没有访问过,入栈,然后返回
entry.input_index = i + 1; // 压入栈中后就不会再遍历
return;
}
}
for (int i = 0; i < start; ++i) {
Node* input = node_inputs[i];
if (input != node && Recurse(input)) {
entry.input_index = i + 1;
return;
}
}

// Remember the max node id before reduction.
NodeId const max_id = static_cast<NodeId>(graph()->NodeCount() - 1);

Reduction reduction = Reduce(node);//遍历所有的reducer节点reduce

// If there was no reduction, pop {node} and continue.
// 没有对该节点reduce,则pop
if (!reduction.Changed()) return Pop();

// Check if the reduction is an in-place update of the {node}.
Node* const replacement = reduction.replacement();
if (replacement == node) { //replacement == node 代表该node被更新
// In-place update of {node}, may need to recurse on an input.
Node::Inputs node_inputs = node->inputs();
for (int i = 0; i < node_inputs.count(); ++i) {
Node* input = node_inputs[i];
if (input != node && Recurse(input)) {
entry.input_index = i + 1;
return;
}
}
}

// After reducing the node, pop it off the stack.
Pop();

// Check if we have a new replacement.
if (replacement != node) {
Replace(node, replacement, max_id);
} else {
// Revisit all uses of the node.
// 重新访问该节点的use节点,入栈
for (Node* const user : node->uses()) {
// Don't revisit this node if it refers to itself.
if (user != node) Revisit(user);
}
}
}

InliningPhase:

仔细分析一下该阶段reduce所做的一些操作。

前面分析过了InliningPhase的大结构体的内容,现在看看ReduceGraph。

graph_reducer.ReduceGraph();

1
void GraphReducer::ReduceGraph() { ReduceNode(graph()->end()); }

继续看ReduceNode,参数为图表的end节点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
push标记:kOnStack
pop标记:kVisited

void GraphReducer::ReduceNode(Node* node) {
DCHECK(stack_.empty());
DCHECK(revisit_.empty());
Push(node); //压入stack栈中
for (;;) {
if (!stack_.empty()) {
//如果stack栈中不为空,则进入ReduceTop函数中
// Process the node on the top of the stack, potentially pushing more or
// popping the node off the stack.
ReduceTop();
} else if (!revisit_.empty()) {
//如果revisit栈中不为空,则pop掉栈顶节点检查是否为kRevisit标记,是则标记为kOnStack然后压入stack栈中
// If the stack becomes empty, revisit any nodes in the revisit queue.
Node* const node = revisit_.front();
revisit_.pop();
if (state_.Get(node) == State::kRevisit) {
// state can change while in queue.
Push(node);
}
} else {
//如果stack、revisit栈都空,则执行所有reduce的析构函数,并检查revisit栈是否为空,是则结束循环
// Run all finalizers.
for (Reducer* const reducer : reducers_) reducer->Finalize();

// Check if we have new nodes to revisit.
if (revisit_.empty()) break;
}
}
DCHECK(revisit_.empty());
DCHECK(stack_.empty());
}

再看ReduceTop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
void GraphReducer::ReduceTop() {
NodeState& entry = stack_.top();

// struct NodeState {
// Node* node;
// int input_index; //input节点索引下标
// };

Node* node = entry.node;
DCHECK_EQ(State::kOnStack, state_.Get(node));

//如果无input节点
if (node->IsDead()) return Pop(); // Node was killed while on stack.

//所有该node上的input节点
Node::Inputs node_inputs = node->inputs();

// Recurse on an input if necessary.
//下面两个循环其实可以结合在一起,用的就是把每个input节点都遍历一遍,先遍历input_index后面部分,再遍历前面
//(分开来的话感觉是作用于input_index赋值的?)
int start = entry.input_index < node_inputs.count() ? entry.input_index : 0;
for (int i = start; i < node_inputs.count(); ++i) {
Node* input = node_inputs[i];

//如果该input节点不为该node节点,且>kRevisit标记,return false
if (input != node && Recurse(input)) {
//input_index加一
entry.input_index = i + 1;
return;
}
}
for (int i = 0; i < start; ++i) {
Node* input = node_inputs[i];
if (input != node && Recurse(input)) {
entry.input_index = i + 1;
return;
}
}

// Remember the max node id before reduction.
NodeId const max_id = static_cast<NodeId>(graph()->NodeCount() - 1);

// All inputs should be visited or on stack. Apply reductions to node.
Reduction reduction = Reduce(node);

// If there was no reduction, pop {node} and continue.
//如果该node在reduce中没有改变,则标记kVisited后pop stack栈
if (!reduction.Changed()) return Pop();

// Check if the reduction is an in-place update of the {node}.
Node* const replacement = reduction.replacement();
//如果发生了节点状态更新,则需要重新遍历每个inputs节点查看是否有kRevisited标记并push到stack栈上
if (replacement == node) {
// In-place update of {node}, may need to recurse on an input.
Node::Inputs node_inputs = node->inputs();
for (int i = 0; i < node_inputs.count(); ++i) {
Node* input = node_inputs[i];
if (input != node && Recurse(input)) {
entry.input_index = i + 1;
return;
}
}
}

// After reducing the node, pop it off the stack.
//标记kVisited并pop stack栈
Pop();

// Check if we have a new replacement.
//如果该node直接被替换了,则进入Replace
if (replacement != node) {
Replace(node, replacement, max_id);
} else {
//否则该node的use节点如果不指向自己且都为kVisited标记,则标记为kRevisit并push到revisit栈中
// Revisit all uses of the node.
for (Node* const user : node->uses()) {
// Don't revisit this node if it refers to itself.
if (user != node) Revisit(user);
}
}
}

最后看Reduce

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
Reduce阶段在该节点上按序执行之前添加的各个reduce(dead_code_elimination、checkpoint_elimination)

Reduction GraphReducer::Reduce(Node* const node) {
auto skip = reducers_.end();
for (auto i = reducers_.begin(); i != reducers_.end();) {
if (i != skip) {
tick_counter_->DoTick();
Reduction reduction = (*i)->Reduce(node);
if (!reduction.Changed()) { //如果该node在该reduce下没有任何change,则进行下一个reduce
// No change from this reducer.
} else if (reduction.replacement() == node) {

//如果node发生了状态更新(非替换),则重新遍历除该reduce之外的所有reduce
//同时skip变成该reduce
// {replacement} == {node} represents an in-place reduction. Rerun
// all the other reducers for this node, as now there may be more
// opportunities for reduction.
if (FLAG_trace_turbo_reduction) {
StdoutStream{} << "- In-place update of " << *node << " by reducer "
<< (*i)->reducer_name() << std::endl;
}
skip = i;
i = reducers_.begin();
continue;
} else {
//如果node被替换了,则直接返回该reduce
// {node} was replaced by another node.
if (FLAG_trace_turbo_reduction) {
StdoutStream{} << "- Replacement of " << *node << " with "
<< *(reduction.replacement()) << " by reducer "
<< (*i)->reducer_name() << std::endl;
}
return reduction;
}
}
++i;
}
//如果skip为最后一个reduce则node没有发生过改变
if (skip == reducers_.end()) {
// No change from any reducer.
return Reducer::NoChange();
}
//如果不是则至少发生了一次状态更新(即一个reduce发生作用)
// At least one reducer did some in-place reduction.
return Reducer::Changed(node);
}

结合PPT来看看,整个reduce维护了两个栈,一个stack一个revisit。采用的是深度优先遍历,从end节点开始。遍历n6的input节点,随后n6进入reduce。

11D7EBAA-064E-4A4C-87C2-E33695440086

如果node的inputs节点在栈中,即存在递归调用。n6的input节点n3已经在栈中,为kOnStack标记,则:

1
2
3
4
5
bool GraphReducer::Recurse(Node* node) {
if (state_.Get(node) > State::kRevisit) return false;
Push(node);
return true;
}

返回false,不压入stack栈中。

60DC1A9C-7607-4CCE-847C-C9BD1EEEBD6F

n6被reduce。

B3DEC9EE-31C1-47BE-BE8D-E59A774F0F6A

当一个node成功reduce之后,会遍历该node的use节点(即把该node作为input节点的node)。此时n3的use节点为n6。标记为kRevisit并push到revisit栈中。而后又会被压入stack栈中。

DEC70B02-F924-4BD3-8D09-191C1B1C9F32

n6重新需要reduce。

73CE78E6-8CC5-4B78-8646-F51B372148B4

EarlyGraphTrimmingPhase:

1
2
3
4
5
6
7
8
9
10
11
struct EarlyGraphTrimmingPhase {
static const char* phase_name() { return "V8.TFEarlyTrimming"; }
void Run(PipelineData* data, Zone* temp_zone) {
//调整graph中的不可达节点,即删除dead node的实例
GraphTrimmer trimmer(temp_zone, data->graph());
//创建节点向量实例
NodeVector roots(temp_zone);
data->jsgraph()->GetCachedNodes(&roots);
trimmer.TrimGraph(roots.begin(), roots.end());
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void TrimGraph(ForwardIterator begin, ForwardIterator end) {
//遍历所有节点,若节点不是死节点,则标记为live
while (begin != end) {
Node* const node = *begin++;
if (!node->IsDead()) MarkAsLive(node);
}
TrimGraph();
}

V8_INLINE void MarkAsLive(Node* const node) {
DCHECK(!node->IsDead());
if (!IsLive(node)) {
is_live_.Set(node, true);
live_.push_back(node);
}
}

TrimGraph:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void GraphTrimmer::TrimGraph() {
// Mark end node as live.
//标记end节点Live
MarkAsLive(graph()->end());
//遍历所有Live标记的节点,把其所有input节点也标记为Live
// Compute transitive closure of live nodes.
for (size_t i = 0; i < live_.size(); ++i) {
Node* const live = live_[i];
for (Node* const input : live->inputs()) MarkAsLive(input);
}
//
// Remove dead->live edges.
//遍历Live标记的节点
for (Node* const live : live_) {
DCHECK(IsLive(live));
//遍历这些Live节点中的use节点,如果不为Live,则更新为dead节点
for (Edge edge : live->use_edges()) {
Node* const user = edge.from();
if (!IsLive(user)) {
if (FLAG_trace_turbo_trimming) {
StdoutStream{} << "DeadLink: " << *user << "(" << edge.index()
<< ") -> " << *live << std::endl;
}
edge.UpdateTo(nullptr);
}
}
}
}

CreateGraph:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
bool PipelineImpl::CreateGraph() {
PipelineData* data = this->data_;

data->BeginPhaseKind("V8.TFGraphCreation");

Run<GraphBuilderPhase>();
RunPrintAndVerify(GraphBuilderPhase::phase_name(), true);

// Perform function context specialization and inlining (if enabled).
Run<InliningPhase>();
RunPrintAndVerify(InliningPhase::phase_name(), true);

// Remove dead->live edges from the graph.
Run<EarlyGraphTrimmingPhase>();
RunPrintAndVerify(EarlyGraphTrimmingPhase::phase_name(), true);

//确定typer阶段的flag标志
// Determine the Typer operation flags.
{
SharedFunctionInfoRef shared_info(data->broker(), info()->shared_info());
//如果是sloppy模式则添加kThisIsReceiver标识(表示this参数是一个Object)
if (is_sloppy(shared_info.language_mode()) &&
shared_info.IsUserJavaScript()) {
// Sloppy mode functions always have an Object for this.
data->AddTyperFlag(Typer::kThisIsReceiver);
}
//(表示参数new.target是一个Object)
if (IsClassConstructor(shared_info.kind())) {
// Class constructors cannot be [[Call]]ed.
data->AddTyperFlag(Typer::kNewTargetIsReceiver);
}
}

//类型敏感的简化和优化(一般不会执行)
// Run the type-sensitive lowerings and optimizations on the graph.
{
if (!FLAG_concurrent_inlining) {
//初始化一些标准对象
Run<HeapBrokerInitializationPhase>();
//调用JSHeapCopyReducer
Run<CopyMetadataForConcurrentCompilePhase>();
//标记已序列化过(kSerialized)
data->broker()->StopSerializing();
}
}

//阶段结束
data->EndPhaseKind();

return true;
}

HeapBrokerInitializationPhase:

1
2
3
4
5
6
7
struct HeapBrokerInitializationPhase {
static const char* phase_name() { return "V8.TFHeapBrokerInitialization"; }

void Run(PipelineData* data, Zone* temp_zone) {
data->broker()->InitializeAndStartSerializing(data->native_context());
}
};

OptimizeGraph:(优化块)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
PipelineCompilationJob::Status PipelineCompilationJob::ExecuteJobImpl() {
if (FLAG_concurrent_inlining) {
if (!pipeline_.CreateGraph()) {
return AbortOptimization(BailoutReason::kGraphBuildingFailed);
}
}

bool success;
if (FLAG_turboprop) {
//启用中间层编译器
success = pipeline_.OptimizeGraphForMidTier(linkage_);
} else {
success = pipeline_.OptimizeGraph(linkage_);
}
if (!success) return FAILED;

pipeline_.AssembleCode(linkage_);
return SUCCEEDED;
}

TyperPhase:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct TyperPhase {
static const char* phase_name() { return "V8.TFTyper"; }

void Run(PipelineData* data, Zone* temp_zone, Typer* typer) {
NodeVector roots(temp_zone);
data->jsgraph()->GetCachedNodes(&roots);

// Make sure we always type True and False. Needed for escape analysis.
//节点向量加入True False常数(为逃逸分析阶段做准备)
roots.push_back(data->jsgraph()->TrueConstant());
roots.push_back(data->jsgraph()->FalseConstant());

//创建循环变量优化实例
LoopVariableOptimizer induction_vars(data->jsgraph()->graph(),
data->common(), temp_zone);
if (FLAG_turbo_loop_variable) induction_vars.Run();
typer->Run(roots, &induction_vars);
}
};

LoopVariableOptimizer::Run:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
void LoopVariableOptimizer::Run() {
ZoneQueue<Node*> queue(zone());
//start节点压入队列
queue.push(graph()->start());
设置mark_min_和mark_max_(mark_max_,mark_max_+2
NodeMarker<bool> queued(graph(), 2);
while (!queue.empty()) {
Node* node = queue.front();
queue.pop();
//设置node节点的mark_属性(mark_=mark_min_+false)
queued.Set(node, false);

DCHECK(!reduced_.Get(node));
//自start节点起遍如果遍历到了GetControlInput,则搜索该node的use节点
bool all_inputs_visited = true;
int inputs_end = (node->opcode() == IrOpcode::kLoop)
? kFirstBackedge
: node->op()->ControlInputCount();
for (int i = 0; i < inputs_end; i++) {
if (!reduced_.Get(NodeProperties::GetControlInput(node, i))) {
all_inputs_visited = false;
break;
}
}
//下一轮搜索
if (!all_inputs_visited) continue;

VisitNode(node);
reduced_.Set(node, true);

// Queue control outputs.
for (Edge edge : node->use_edges()) {
if (NodeProperties::IsControlEdge(edge) &&
edge.from()->op()->ControlOutputCount() > 0) {
Node* use = edge.from();
if (use->opcode() == IrOpcode::kLoop &&
edge.index() != kAssumedLoopEntryIndex) {
VisitBackedge(node, use);
} else if (!queued.Get(use)) {
queue.push(use);
queued.Set(use, true);
}
}
}
}
}

TyperPhase:

接着看TyperPhase,在Typer::Run阶段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void Typer::Run(const NodeVector& roots,
LoopVariableOptimizer* induction_vars) {
if (induction_vars != nullptr) {
induction_vars->ChangeToInductionVariablePhis();
}
//创建visitor的reduce实例,为后面的ReduceGraph作准备
Visitor visitor(this, induction_vars);
GraphReducer graph_reducer(zone(), graph(), tick_counter_);
graph_reducer.AddReducer(&visitor);
//遍历节点向量,执行reduce node
for (Node* const root : roots) graph_reducer.ReduceNode(root);
//从end节点开始深度优先reduce,采用的reduce方法为visitor
graph_reducer.ReduceGraph();

if (induction_vars != nullptr) {
induction_vars->ChangeToPhisAndInsertGuards();
}
}

​ 接着是其中的visitor的reduce内容,主要就是所有节点对比opcode然后返回type类型并执行一些基本的reduce:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
class Typer::Visitor : public Reducer {
public:
explicit Visitor(Typer* typer, LoopVariableOptimizer* induction_vars)
: typer_(typer),
induction_vars_(induction_vars),
weakened_nodes_(typer->zone()),
remembered_types_(typer->zone()) {}

const char* reducer_name() const override { return "Typer"; }

Reduction Reduce(Node* node) override {
if (node->op()->ValueOutputCount() == 0) return NoChange();
//以下这些宏都是opcode,根据case对比各个宏里面的opcode
//其中例如SIMPLIFIED_COMPARE_BINOP_LIST中的定义如下:
// #define SIMPLIFIED_COMPARE_BINOP_LIST(V) \
// V(NumberEqual) \
// V(NumberLessThan) \
// V(NumberLessThanOrEqual) \
// V(SpeculativeNumberEqual) \
// V(SpeculativeNumberLessThan) \
// V(SpeculativeNumberLessThanOrEqual) \
// V(ReferenceEqual) \
// V(SameValue) \
// V(SameValueNumbersOnly) \
// V(NumberSameValue) \
// V(StringEqual) \
// V(StringLessThan) \
// V(StringLessThanOrEqual)

typer.cc里再看看NumberEqual函数:
Type Typer::Visitor::TypeNumberEqual(Node* node) {
return TypeBinaryOp(node, NumberEqualTyper);
}
查看调用之后就会发现是JS_SIMPLE_BINOP_LIST中调用的。
--------------------------
switch (node->opcode()) {
#define DECLARE_CASE(x) \
//该case中对应的都是加上Typer后缀的opcode,例如JSHasInPrototypeChain:
//Type Typer::Visitor::JSHasInPrototypeChainTyper(Type lhs, Type rhs, Typer* t) {
// return Type::Boolean();
//}等等
case IrOpcode::k##x: \
return UpdateType(node, TypeBinaryOp(node, x##Typer));
JS_SIMPLE_BINOP_LIST(DECLARE_CASE)
#undef DECLARE_CASE

#define DECLARE_CASE(x) \
//该case中对应的是加上Type前缀的opcode,例如EffectPhi:
//Type Typer::Visitor::TypeEffectPhi(Node* node) { UNREACHABLE(); }
//等等
case IrOpcode::k##x: \
return UpdateType(node, Type##x(node));
DECLARE_CASE(Start)
DECLARE_CASE(IfException)
// VALUE_OP_LIST without JS_SIMPLE_BINOP_LIST:
COMMON_OP_LIST(DECLARE_CASE)
SIMPLIFIED_COMPARE_BINOP_LIST(DECLARE_CASE)
SIMPLIFIED_OTHER_OP_LIST(DECLARE_CASE)
JS_SIMPLE_UNOP_LIST(DECLARE_CASE)
JS_OBJECT_OP_LIST(DECLARE_CASE)
JS_CONTEXT_OP_LIST(DECLARE_CASE)
JS_OTHER_OP_LIST(DECLARE_CASE)
#undef DECLARE_CASE

#define DECLARE_CASE(x) \
//该case对应的是含有两个操作数的opcode,例如SIMPLIFIED_NUMBER_BINOP_LIST中的:
//#define SIMPLIFIED_NUMBER_BINOP_LIST(V) \
// V(NumberAdd) \
// V(NumberSubtract) \
// V(NumberMultiply) \
// V(NumberDivide) \
// V(NumberModulus) \
// V(NumberBitwiseOr) \
// V(NumberBitwiseXor) \
// V(NumberBitwiseAnd) \
// V(NumberShiftLeft) \
// V(NumberShiftRight) \
// V(NumberShiftRightLogical) \
// V(NumberAtan2) \
// V(NumberImul) \
// V(NumberMax) \
// V(NumberMin) \
// V(NumberPow)
//以上的opcode都是二元操作数
case IrOpcode::k##x: \
return UpdateType(node, TypeBinaryOp(node, x));
SIMPLIFIED_NUMBER_BINOP_LIST(DECLARE_CASE)
SIMPLIFIED_BIGINT_BINOP_LIST(DECLARE_CASE)
SIMPLIFIED_SPECULATIVE_NUMBER_BINOP_LIST(DECLARE_CASE)
SIMPLIFIED_SPECULATIVE_BIGINT_BINOP_LIST(DECLARE_CASE)
#undef DECLARE_CASE

#define DECLARE_CASE(x) \
//该case对应的是含有一个操作数的opcode,例如SIMPLIFIED_NUMBER_UNOP_LIST:
//#define SIMPLIFIED_NUMBER_UNOP_LIST(V) \
// V(NumberAbs) \
// V(NumberAcos) \
// V(NumberAcosh) \
// V(NumberAsin) \
// V(NumberAsinh) \
// V(NumberAtan) \
// V(NumberAtanh) \
// V(NumberCbrt) \
// V(NumberCeil) \
// V(NumberClz32) \
// V(NumberCos) \
// V(NumberCosh) \
// V(NumberExp) \
// V(NumberExpm1) \
// V(NumberFloor) \
// V(NumberFround) \
// V(NumberLog) \
// V(NumberLog1p) \
// .........
//以上opcode都是单元操作数
//不过此处的opcode一般都被JSCall所引用
case IrOpcode::k##x: \
return UpdateType(node, TypeUnaryOp(node, x));
SIMPLIFIED_NUMBER_UNOP_LIST(DECLARE_CASE)
SIMPLIFIED_BIGINT_UNOP_LIST(DECLARE_CASE)
SIMPLIFIED_SPECULATIVE_NUMBER_UNOP_LIST(DECLARE_CASE)
SIMPLIFIED_SPECULATIVE_BIGINT_UNOP_LIST(DECLARE_CASE)
#undef DECLARE_CASE

//该case对应的是没有type类型的opcode,loop、switch、IfTrue这些都是不包含type的
#define DECLARE_CASE(x) case IrOpcode::k##x:
DECLARE_CASE(Loop)
DECLARE_CASE(Branch)
DECLARE_CASE(IfTrue)
DECLARE_CASE(IfFalse)
DECLARE_CASE(IfSuccess)
DECLARE_CASE(Switch)
DECLARE_CASE(IfValue)
DECLARE_CASE(IfDefault)
DECLARE_CASE(Merge)
DECLARE_CASE(Deoptimize)
DECLARE_CASE(DeoptimizeIf)
DECLARE_CASE(DeoptimizeUnless)
DECLARE_CASE(TrapIf)
DECLARE_CASE(TrapUnless)
DECLARE_CASE(Return)
DECLARE_CASE(TailCall)
DECLARE_CASE(Terminate)
DECLARE_CASE(OsrNormalEntry)
DECLARE_CASE(OsrLoopEntry)
DECLARE_CASE(Throw)
DECLARE_CASE(End)
SIMPLIFIED_CHANGE_OP_LIST(DECLARE_CASE)
SIMPLIFIED_CHECKED_OP_LIST(DECLARE_CASE)
MACHINE_SIMD_OP_LIST(DECLARE_CASE)
MACHINE_OP_LIST(DECLARE_CASE)
#undef DECLARE_CASE
break;
}
return NoChange();
}

TyperPhase就差不多结束了,之前没审计的时候以为该阶段就只是给node节点加上type类型,现在知道了还是有少部分reduce的。不过基本涵盖了所有的opcode,具体的一些返回type类型和reduce函数就不细看了…实在是太多。

后面是TypedLoweringPhase。

TypedLoweringPhase:

先看结构体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
struct TypedLoweringPhase {
static const char* phase_name() { return "V8.TFTypedLowering"; }

void Run(PipelineData* data, Zone* temp_zone) {
//创建IR Reduce实例
GraphReducer graph_reducer(temp_zone, data->graph(),
&data->info()->tick_counter(),
data->jsgraph()->Dead());
DeadCodeElimination dead_code_elimination(&graph_reducer, data->graph(),
data->common(), temp_zone);
JSCreateLowering create_lowering(&graph_reducer, data->dependencies(),
data->jsgraph(), data->broker(),
temp_zone);
JSTypedLowering typed_lowering(&graph_reducer, data->jsgraph(),
data->broker(), temp_zone);
ConstantFoldingReducer constant_folding_reducer(
&graph_reducer, data->jsgraph(), data->broker());
TypedOptimization typed_optimization(&graph_reducer, data->dependencies(),
data->jsgraph(), data->broker());
SimplifiedOperatorReducer simple_reducer(&graph_reducer, data->jsgraph(),
data->broker());
CheckpointElimination checkpoint_elimination(&graph_reducer);
CommonOperatorReducer common_reducer(&graph_reducer, data->graph(),
data->broker(), data->common(),
data->machine(), temp_zone);
AddReducer(data, &graph_reducer, &dead_code_elimination);
AddReducer(data, &graph_reducer, &create_lowering);
AddReducer(data, &graph_reducer, &constant_folding_reducer);
AddReducer(data, &graph_reducer, &typed_lowering);
AddReducer(data, &graph_reducer, &typed_optimization);
AddReducer(data, &graph_reducer, &simple_reducer);
AddReducer(data, &graph_reducer, &checkpoint_elimination);
AddReducer(data, &graph_reducer, &common_reducer);
//该函数之前已经分析过了,深度优先
graph_reducer.ReduceGraph();
}
};

由于之前审过Builder阶段,还是很类似的,所以一目了然,主要看这些的reduce就可以了。

DeadCodeElimination

(作用于end、loop、merge等节点)(主要作用是消除dead节点)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Reduction DeadCodeElimination::Reduce(Node* node) {
DisallowHeapAccess no_heap_access;
switch (node->opcode()) {
case IrOpcode::kEnd:
return ReduceEnd(node);
case IrOpcode::kLoop:
case IrOpcode::kMerge:
return ReduceLoopOrMerge(node);
case IrOpcode::kLoopExit:
return ReduceLoopExit(node);
case IrOpcode::kUnreachable:
case IrOpcode::kIfException:
return ReduceUnreachableOrIfException(node);
case IrOpcode::kPhi:
return ReducePhi(node);
case IrOpcode::kEffectPhi:
return ReduceEffectPhi(node);
case IrOpcode::kDeoptimize:
case IrOpcode::kReturn:
case IrOpcode::kTerminate:
case IrOpcode::kTailCall:
return ReduceDeoptimizeOrReturnOrTerminateOrTailCall(node);
case IrOpcode::kThrow:
return PropagateDeadControl(node);
case IrOpcode::kBranch:
case IrOpcode::kSwitch:
return ReduceBranchOrSwitch(node);
default:
return ReduceNode(node);
}
UNREACHABLE();
}

对应opcode执行reduce,不过后面审各个的reduce的时候,审到第三个就发现是大同小异的,执行流程其实基本都差不多,就是遍历所有node输去检查有没有dead节点再消除的过程。所以这里就审了前面几个部分。

ReduceEnd:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Reduction DeadCodeElimination::ReduceEnd(Node* node) {
DCHECK_EQ(IrOpcode::kEnd, node->opcode());
//得到所有inputs节点
Node::Inputs inputs = node->inputs();
DCHECK_LE(1, inputs.count());
int live_input_count = 0;
//循环遍历inputs节点,记录活节点(非dead节点,live_input_count)
for (int i = 0; i < inputs.count(); ++i) {
Node* const input = inputs[i];
// Skip dead inputs.
if (input->opcode() == IrOpcode::kDead) continue;
// Compact live inputs.
//紧凑非dead节点,往前移位(index前移)
if (i != live_input_count) node->ReplaceInput(live_input_count, input);
++live_input_count;
}
//如果全是dead节点,则用dead节点代替end的inputs节点
if (live_input_count == 0) {
return Replace(dead());
} else if (live_input_count < inputs.count()) {
//如果只有部分dead节点,则重新end的input_count,返回Changed
node->TrimInputCount(live_input_count);
NodeProperties::ChangeOp(node, common()->End(live_input_count));
return Changed(node);
}
//无dead节点,返回NoChange
DCHECK_EQ(inputs.count(), live_input_count);
return NoChange();
}

ReduceLoopOrMerge:(作用于loop和merge)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
Reduction DeadCodeElimination::ReduceLoopOrMerge(Node* node) {
DCHECK(IrOpcode::IsMergeOpcode(node->opcode()));
Node::Inputs inputs = node->inputs();
DCHECK_LE(1, inputs.count());
// Count the number of live inputs to {node} and compact them on the fly, also
// compacting the inputs of the associated {Phi} and {EffectPhi} uses at the
// same time. We consider {Loop}s dead even if only the first control input
// is dead.
int live_input_count = 0;
//是否为loop节点,且第一个input节点是否为dead节点
//后续跟上面函数一样,紧凑非dead节点
if (node->opcode() != IrOpcode::kLoop ||
node->InputAt(0)->opcode() != IrOpcode::kDead) {
for (int i = 0; i < inputs.count(); ++i) {
Node* const input = inputs[i];
// Skip dead inputs.
if (input->opcode() == IrOpcode::kDead) continue;
// Compact live inputs.
if (live_input_count != i) {
node->ReplaceInput(live_input_count, input);
//还遍历use节点为Phi、EffectPhi的节点,如果是则把use节点的input节点也紧凑一遍
for (Node* const use : node->uses()) {
if (NodeProperties::IsPhi(use)) {
DCHECK_EQ(inputs.count() + 1, use->InputCount());
use->ReplaceInput(live_input_count, use->InputAt(i));
}
}
}
++live_input_count;
}
}
if (live_input_count == 0) {
return Replace(dead());
} else if (live_input_count == 1) {
//如果只有一个input节点为非dead节点
NodeVector loop_exits(zone_);
// Due to compaction above, the live input is at offset 0.
//遍历use节点并把Phi的use节点替换为index为0的input节点
for (Node* const use : node->uses()) {
if (NodeProperties::IsPhi(use)) {
Replace(use, use->InputAt(0));
} else if (use->opcode() == IrOpcode::kLoopExit &&
use->InputAt(1) == node) {
//如果不为Phi而是loopexit并且index为1的input节点就是node
//则将该use节点压入loopexits空间
// Remember the loop exits so that we can mark their loop input dead.
// This has to be done after the use list iteration so that we do
// not mutate the use list while it is being iterated.
loop_exits.push_back(use);
} else if (use->opcode() == IrOpcode::kTerminate) {
//如果为Terminate,则dead节点代替use节点
DCHECK_EQ(IrOpcode::kLoop, node->opcode());
Replace(use, dead());
}
}
//遍历loopexits空间,loopexits中的节点(use)将index为1的input节点替换为dead,重复visit每个节点
for (Node* loop_exit : loop_exits) {
loop_exit->ReplaceInput(1, dead());
Revisit(loop_exit);
}
//将node用index为0的input节点代替
return Replace(node->InputAt(0));
}
DCHECK_LE(2, live_input_count);
DCHECK_LE(live_input_count, inputs.count());
// Trim input count for the {Merge} or {Loop} node.
//如果input中肯定有dead节点
if (live_input_count < inputs.count()) {
// Trim input counts for all phi uses and revisit them.
//遍历该node的use节点,如果为Phi,则将node节点紧凑
for (Node* const use : node->uses()) {
if (NodeProperties::IsPhi(use)) {
use->ReplaceInput(live_input_count, node);
TrimMergeOrPhi(use, live_input_count);
Revisit(use);
}
}
//修整node节点的live_input个数
TrimMergeOrPhi(node, live_input_count);
return Changed(node);
}
return NoChange();
}

ReduceLoopExit:

1
2
3
4
5
6
7
8
9
10
Reduction DeadCodeElimination::ReduceLoopExit(Node* node) {
Node* control = NodeProperties::GetControlInput(node, 0);
Node* loop = NodeProperties::GetControlInput(node, 1);
//如果control和loop节点有一个是dead,则遍历node的use节点,如果为LoopExitValue、LoopExitEffect,则用use节点//的input(index为0)节点代替use,最终用control节点代替node
if (control->opcode() == IrOpcode::kDead ||
loop->opcode() == IrOpcode::kDead) {
return RemoveLoopExit(node);
}
return NoChange();
}

ReduceUnreachableOrIfException:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Reduction DeadCodeElimination::ReduceUnreachableOrIfException(Node* node) {
DCHECK(node->opcode() == IrOpcode::kUnreachable ||
node->opcode() == IrOpcode::kIfException);
Reduction reduction = PropagateDeadControl(node);
//如果node的control节点为dead则返回replace(control)
if (reduction.Changed()) return reduction;
Node* effect = NodeProperties::GetEffectInput(node, 0);
//如果node的effect节点为dead或unreachable则返回replace(effect)
if (effect->opcode() == IrOpcode::kDead) {
return Replace(effect);
}
if (effect->opcode() == IrOpcode::kUnreachable) {
return Replace(effect);
}
return NoChange();
}

ReducePhi:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
Reduction DeadCodeElimination::ReducePhi(Node* node) {
DCHECK_EQ(IrOpcode::kPhi, node->opcode());
//跟上面函数一样为dead返回replace
Reduction reduction = PropagateDeadControl(node);
if (reduction.Changed()) return reduction;
//机器码表示(操作码),获取该node的Phi表示操作码
//enum class MachineRepresentation : uint8_t {
// kNone,
// kBit,
// kWord8,
// kWord16,
// kWord32,
// kWord64,
// kTaggedSigned,
// kTaggedPointer,
// kTagged,
// kCompressedPointer,
// kCompressed,
// // FP representations must be last, and in order of increasing size.
// kFloat32,
// kFloat64,
// kSimd128,
// kFirstFPRepresentation = kFloat32,
// kLastRepresentation = kSimd128
//};
MachineRepresentation rep = PhiRepresentationOf(node->op());
//如果为None类型或者字节没有type类型,则返回deadvalue
if (rep == MachineRepresentation::kNone ||
NodeProperties::GetTypeOrAny(node).IsNone()) {
return Replace(DeadValue(node, rep));
}
int input_count = node->op()->ValueInputCount();
//循环遍历node的input节点
for (int i = 0; i < input_count; ++i) {
Node* input = NodeProperties::GetValueInput(node, i);
//如果input的opcode为deadvalue且deadvalue表示返回的机器码和Phi表示返回的机器码不同
//则用deadvalue替换node节点上的index为i的input节点
if (input->opcode() == IrOpcode::kDeadValue &&
DeadValueRepresentationOf(input->op()) != rep) {
NodeProperties::ReplaceValueInput(node, DeadValue(input, rep), i);
}
}
return NoChange();
}

后面的匆匆瞥几眼就过了。。

再看后面的大reduce:

JSCreateLowering:

对应的case有(kJSCreate、kJSCreateArguments、kJSCreateArray、kJSCreateArrayIterator、kJSCreateAsyncFunctionObject、kJSCreateBoundFunction、kJSCreateClosure、kJSCreateCollectionIterator、kJSCreateIterResultObject、kJSCreateStringIterator、kJSCreateKeyValueArray…太多了,不过看名字就能知道是作用在各种create opcode里面的)

重点关注这几个(kJSCreate、kJSCreateArguments、kJSCreateArray、kJSCreateBoundFunction、kJSCreateObject)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
Reduction JSCreateLowering::Reduce(Node* node) {
DisallowHeapAccess disallow_heap_access;
switch (node->opcode()) {
case IrOpcode::kJSCreate:
return ReduceJSCreate(node);
case IrOpcode::kJSCreateArguments:
return ReduceJSCreateArguments(node);
case IrOpcode::kJSCreateArray:
return ReduceJSCreateArray(node);
case IrOpcode::kJSCreateArrayIterator:
return ReduceJSCreateArrayIterator(node);
case IrOpcode::kJSCreateAsyncFunctionObject:
return ReduceJSCreateAsyncFunctionObject(node);
case IrOpcode::kJSCreateBoundFunction:
return ReduceJSCreateBoundFunction(node);
case IrOpcode::kJSCreateClosure:
return ReduceJSCreateClosure(node);
case IrOpcode::kJSCreateCollectionIterator:
return ReduceJSCreateCollectionIterator(node);
case IrOpcode::kJSCreateIterResultObject:
return ReduceJSCreateIterResultObject(node);
case IrOpcode::kJSCreateStringIterator:
return ReduceJSCreateStringIterator(node);
case IrOpcode::kJSCreateKeyValueArray:
return ReduceJSCreateKeyValueArray(node);
case IrOpcode::kJSCreatePromise:
return ReduceJSCreatePromise(node);
case IrOpcode::kJSCreateLiteralArray:
case IrOpcode::kJSCreateLiteralObject:
return ReduceJSCreateLiteralArrayOrObject(node);
case IrOpcode::kJSCreateLiteralRegExp:
return ReduceJSCreateLiteralRegExp(node);
case IrOpcode::kJSGetTemplateObject:
return ReduceJSGetTemplateObject(node);
case IrOpcode::kJSCreateEmptyLiteralArray:
return ReduceJSCreateEmptyLiteralArray(node);
case IrOpcode::kJSCreateEmptyLiteralObject:
return ReduceJSCreateEmptyLiteralObject(node);
case IrOpcode::kJSCreateFunctionContext:
return ReduceJSCreateFunctionContext(node);
case IrOpcode::kJSCreateWithContext:
return ReduceJSCreateWithContext(node);
case IrOpcode::kJSCreateCatchContext:
return ReduceJSCreateCatchContext(node);
case IrOpcode::kJSCreateBlockContext:
return ReduceJSCreateBlockContext(node);
case IrOpcode::kJSCreateGeneratorObject:
return ReduceJSCreateGeneratorObject(node);
case IrOpcode::kJSCreateObject:
return ReduceJSCreateObject(node);
default:
break;
}
return NoChange();
}

放到后面再写详细的吧…

完。

支持一下
扫一扫,支持v1nke
  • 微信扫一扫
  • 支付宝扫一扫