1 // Copyright 2018 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/builtins/builtins-lazy-gen.h"
6
7 #include "src/builtins/builtins-utils-gen.h"
8 #include "src/builtins/builtins.h"
9 #include "src/feedback-vector.h"
10 #include "src/globals.h"
11 #include "src/objects/shared-function-info.h"
12
13 namespace v8 {
14 namespace internal {
15
GenerateTailCallToJSCode(TNode<Code> code,TNode<JSFunction> function)16 void LazyBuiltinsAssembler::GenerateTailCallToJSCode(
17 TNode<Code> code, TNode<JSFunction> function) {
18 TNode<Int32T> argc =
19 UncheckedCast<Int32T>(Parameter(Descriptor::kActualArgumentsCount));
20 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
21 TNode<Object> new_target = CAST(Parameter(Descriptor::kNewTarget));
22
23 TailCallJSCode(code, context, function, new_target, argc);
24 }
25
GenerateTailCallToReturnedCode(Runtime::FunctionId function_id,TNode<JSFunction> function)26 void LazyBuiltinsAssembler::GenerateTailCallToReturnedCode(
27 Runtime::FunctionId function_id, TNode<JSFunction> function) {
28 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
29 TNode<Code> code = CAST(CallRuntime(function_id, context, function));
30 GenerateTailCallToJSCode(code, function);
31 }
32
TailCallRuntimeIfMarkerEquals(TNode<Smi> marker,OptimizationMarker expected_marker,Runtime::FunctionId function_id,TNode<JSFunction> function)33 void LazyBuiltinsAssembler::TailCallRuntimeIfMarkerEquals(
34 TNode<Smi> marker, OptimizationMarker expected_marker,
35 Runtime::FunctionId function_id, TNode<JSFunction> function) {
36 Label no_match(this);
37 GotoIfNot(SmiEqual(marker, SmiConstant(expected_marker)), &no_match);
38 GenerateTailCallToReturnedCode(function_id, function);
39 BIND(&no_match);
40 }
41
MaybeTailCallOptimizedCodeSlot(TNode<JSFunction> function,TNode<FeedbackVector> feedback_vector)42 void LazyBuiltinsAssembler::MaybeTailCallOptimizedCodeSlot(
43 TNode<JSFunction> function, TNode<FeedbackVector> feedback_vector) {
44 Label fallthrough(this);
45
46 TNode<MaybeObject> maybe_optimized_code_entry = LoadMaybeWeakObjectField(
47 feedback_vector, FeedbackVector::kOptimizedCodeOffset);
48
49 // Check if the code entry is a Smi. If yes, we interpret it as an
50 // optimisation marker. Otherwise, interpret it as a weak reference to a code
51 // object.
52 Label optimized_code_slot_is_smi(this), optimized_code_slot_is_weak_ref(this);
53 Branch(TaggedIsSmi(maybe_optimized_code_entry), &optimized_code_slot_is_smi,
54 &optimized_code_slot_is_weak_ref);
55
56 BIND(&optimized_code_slot_is_smi);
57 {
58 // Optimized code slot is a Smi optimization marker.
59 TNode<Smi> marker = CAST(maybe_optimized_code_entry);
60
61 // Fall through if no optimization trigger.
62 GotoIf(SmiEqual(marker, SmiConstant(OptimizationMarker::kNone)),
63 &fallthrough);
64
65 // TODO(ishell): introduce Runtime::kHandleOptimizationMarker and check
66 // all these marker values there.
67 TailCallRuntimeIfMarkerEquals(marker,
68 OptimizationMarker::kLogFirstExecution,
69 Runtime::kFunctionFirstExecution, function);
70 TailCallRuntimeIfMarkerEquals(marker, OptimizationMarker::kCompileOptimized,
71 Runtime::kCompileOptimized_NotConcurrent,
72 function);
73 TailCallRuntimeIfMarkerEquals(
74 marker, OptimizationMarker::kCompileOptimizedConcurrent,
75 Runtime::kCompileOptimized_Concurrent, function);
76
77 // Otherwise, the marker is InOptimizationQueue, so fall through hoping
78 // that an interrupt will eventually update the slot with optimized code.
79 CSA_ASSERT(this,
80 SmiEqual(marker,
81 SmiConstant(OptimizationMarker::kInOptimizationQueue)));
82 Goto(&fallthrough);
83 }
84
85 BIND(&optimized_code_slot_is_weak_ref);
86 {
87 // Optimized code slot is a weak reference.
88 TNode<Code> optimized_code =
89 CAST(ToWeakHeapObject(maybe_optimized_code_entry, &fallthrough));
90
91 // Check if the optimized code is marked for deopt. If it is, call the
92 // runtime to clear it.
93 Label found_deoptimized_code(this);
94 TNode<CodeDataContainer> code_data_container =
95 CAST(LoadObjectField(optimized_code, Code::kCodeDataContainerOffset));
96
97 TNode<Int32T> code_kind_specific_flags = LoadObjectField<Int32T>(
98 code_data_container, CodeDataContainer::kKindSpecificFlagsOffset);
99 GotoIf(IsSetWord32<Code::MarkedForDeoptimizationField>(
100 code_kind_specific_flags),
101 &found_deoptimized_code);
102
103 // Optimized code is good, get it into the closure and link the closure into
104 // the optimized functions list, then tail call the optimized code.
105 StoreObjectField(function, JSFunction::kCodeOffset, optimized_code);
106 GenerateTailCallToJSCode(optimized_code, function);
107
108 // Optimized code slot contains deoptimized code, evict it and re-enter the
109 // closure's code.
110 BIND(&found_deoptimized_code);
111 GenerateTailCallToReturnedCode(Runtime::kEvictOptimizedCodeSlot, function);
112 }
113
114 // Fall-through if the optimized code cell is clear and there is no
115 // optimization marker.
116 BIND(&fallthrough);
117 }
118
CompileLazy(TNode<JSFunction> function)119 void LazyBuiltinsAssembler::CompileLazy(TNode<JSFunction> function) {
120 // First lookup code, maybe we don't need to compile!
121 Label compile_function(this, Label::kDeferred);
122
123 // Compile function if we don't have a valid feedback vector.
124 TNode<FeedbackVector> feedback_vector =
125 LoadFeedbackVector(function, &compile_function);
126
127 // Is there an optimization marker or optimized code in the feedback vector?
128 MaybeTailCallOptimizedCodeSlot(function, feedback_vector);
129
130 // We found no optimized code. Infer the code object needed for the SFI.
131 TNode<SharedFunctionInfo> shared =
132 CAST(LoadObjectField(function, JSFunction::kSharedFunctionInfoOffset));
133 // If code entry points to anything other than CompileLazy, install that,
134 // otherwise call runtime to compile the function.
135 TNode<Code> code = GetSharedFunctionInfoCode(shared, &compile_function);
136
137 CSA_ASSERT(
138 this,
139 WordNotEqual(code, HeapConstant(BUILTIN_CODE(isolate(), CompileLazy))));
140
141 // Install the SFI's code entry.
142 StoreObjectField(function, JSFunction::kCodeOffset, code);
143 GenerateTailCallToJSCode(code, function);
144
145 BIND(&compile_function);
146 { GenerateTailCallToReturnedCode(Runtime::kCompileLazy, function); }
147 }
148
TF_BUILTIN(CompileLazy,LazyBuiltinsAssembler)149 TF_BUILTIN(CompileLazy, LazyBuiltinsAssembler) {
150 TNode<JSFunction> function = CAST(Parameter(Descriptor::kTarget));
151
152 CompileLazy(function);
153 }
154
TF_BUILTIN(CompileLazyDeoptimizedCode,LazyBuiltinsAssembler)155 TF_BUILTIN(CompileLazyDeoptimizedCode, LazyBuiltinsAssembler) {
156 TNode<JSFunction> function = CAST(Parameter(Descriptor::kTarget));
157
158 // Set the code slot inside the JSFunction to CompileLazy.
159 TNode<Code> code = HeapConstant(BUILTIN_CODE(isolate(), CompileLazy));
160 StoreObjectField(function, JSFunction::kCodeOffset, code);
161 GenerateTailCallToJSCode(code, function);
162 }
163
164 // Lazy deserialization design doc: http://goo.gl/dxkYDZ.
TF_BUILTIN(DeserializeLazy,LazyBuiltinsAssembler)165 TF_BUILTIN(DeserializeLazy, LazyBuiltinsAssembler) {
166 Label deserialize_in_runtime(this, Label::kDeferred);
167
168 TNode<JSFunction> function = CAST(Parameter(Descriptor::kTarget));
169
170 // Load the builtin id for lazy deserialization from SharedFunctionInfo.
171 TNode<SharedFunctionInfo> shared =
172 CAST(LoadObjectField(function, JSFunction::kSharedFunctionInfoOffset));
173
174 TNode<Smi> sfi_data =
175 CAST(LoadObjectField(shared, SharedFunctionInfo::kFunctionDataOffset));
176
177 // The builtin may already have been deserialized. If that is the case, it is
178 // stored in the builtins table, and we can copy to correct code object to
179 // both the shared function info and function without calling into runtime.
180 //
181 // Otherwise, we need to call into runtime to deserialize.
182
183 TNode<Code> code = LoadBuiltin(sfi_data);
184
185 // Check if the loaded code object has already been deserialized. This is
186 // the case iff it does not equal DeserializeLazy.
187 GotoIf(
188 WordEqual(code, HeapConstant(BUILTIN_CODE(isolate(), DeserializeLazy))),
189 &deserialize_in_runtime);
190
191 // If we've reached this spot, the target builtin has been deserialized and
192 // we simply need to copy it over to the target function.
193 StoreObjectField(function, JSFunction::kCodeOffset, code);
194
195 // All copying is done. Jump to the deserialized code object.
196 GenerateTailCallToJSCode(code, function);
197
198 BIND(&deserialize_in_runtime);
199 { GenerateTailCallToReturnedCode(Runtime::kDeserializeLazy, function); }
200 }
201
202 } // namespace internal
203 } // namespace v8
204