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/common/globals.h"
10#include "src/objects/code-inl.h"
11#include "src/objects/feedback-vector.h"
12#include "src/objects/shared-function-info.h"
13
14namespace v8 {
15namespace internal {
16
17void LazyBuiltinsAssembler::GenerateTailCallToJSCode(
18    TNode<CodeT> code, TNode<JSFunction> function) {
19  auto argc = UncheckedParameter<Int32T>(Descriptor::kActualArgumentsCount);
20  auto context = Parameter<Context>(Descriptor::kContext);
21  auto new_target = Parameter<Object>(Descriptor::kNewTarget);
22  TailCallJSCode(code, context, function, new_target, argc);
23}
24
25void LazyBuiltinsAssembler::GenerateTailCallToReturnedCode(
26    Runtime::FunctionId function_id, TNode<JSFunction> function) {
27  auto context = Parameter<Context>(Descriptor::kContext);
28  TNode<CodeT> code = CAST(CallRuntime(function_id, context, function));
29  GenerateTailCallToJSCode(code, function);
30}
31
32void LazyBuiltinsAssembler::TailCallRuntimeIfStateEquals(
33    TNode<Uint32T> state, TieringState expected_state,
34    Runtime::FunctionId function_id, TNode<JSFunction> function) {
35  Label no_match(this);
36  GotoIfNot(
37      Word32Equal(state, Uint32Constant(static_cast<uint32_t>(expected_state))),
38      &no_match);
39  GenerateTailCallToReturnedCode(function_id, function);
40  BIND(&no_match);
41}
42
43void LazyBuiltinsAssembler::MaybeTailCallOptimizedCodeSlot(
44    TNode<JSFunction> function, TNode<FeedbackVector> feedback_vector) {
45  Label fallthrough(this), may_have_optimized_code(this);
46
47  TNode<Uint32T> optimization_state =
48      LoadObjectField<Uint32T>(feedback_vector, FeedbackVector::kFlagsOffset);
49
50  // Fall through if no optimization trigger or optimized code.
51  GotoIfNot(
52      IsSetWord32(
53          optimization_state,
54          FeedbackVector::kHasOptimizedCodeOrTieringStateIsAnyRequestMask),
55      &fallthrough);
56
57  GotoIfNot(IsSetWord32(optimization_state,
58                        FeedbackVector::kTieringStateIsAnyRequestMask),
59            &may_have_optimized_code);
60
61  // TODO(ishell): introduce Runtime::kHandleTieringState and check
62  // all these state values there.
63  TNode<Uint32T> state =
64      DecodeWord32<FeedbackVector::TieringStateBits>(optimization_state);
65  TailCallRuntimeIfStateEquals(state,
66                               TieringState::kRequestTurbofan_Synchronous,
67                               Runtime::kCompileTurbofan_Synchronous, function);
68  TailCallRuntimeIfStateEquals(state, TieringState::kRequestTurbofan_Concurrent,
69                               Runtime::kCompileTurbofan_Concurrent, function);
70  TailCallRuntimeIfStateEquals(state, TieringState::kRequestMaglev_Synchronous,
71                               Runtime::kCompileMaglev_Synchronous, function);
72  TailCallRuntimeIfStateEquals(state, TieringState::kRequestMaglev_Concurrent,
73                               Runtime::kCompileMaglev_Concurrent, function);
74
75  Unreachable();
76  BIND(&may_have_optimized_code);
77  {
78    Label heal_optimized_code_slot(this);
79    TNode<MaybeObject> maybe_optimized_code_entry = LoadMaybeWeakObjectField(
80        feedback_vector, FeedbackVector::kMaybeOptimizedCodeOffset);
81
82    // Optimized code slot is a weak reference to CodeT object.
83    TNode<CodeT> optimized_code = CAST(GetHeapObjectAssumeWeak(
84        maybe_optimized_code_entry, &heal_optimized_code_slot));
85
86    // Check if the optimized code is marked for deopt. If it is, call the
87    // runtime to clear it.
88    TNode<CodeDataContainer> code_data_container =
89        CodeDataContainerFromCodeT(optimized_code);
90    TNode<Int32T> code_kind_specific_flags = LoadObjectField<Int32T>(
91        code_data_container, CodeDataContainer::kKindSpecificFlagsOffset);
92    GotoIf(IsSetWord32<Code::MarkedForDeoptimizationField>(
93               code_kind_specific_flags),
94           &heal_optimized_code_slot);
95
96    // Optimized code is good, get it into the closure and link the closure into
97    // the optimized functions list, then tail call the optimized code.
98    StoreObjectField(function, JSFunction::kCodeOffset, optimized_code);
99    Comment("MaybeTailCallOptimizedCodeSlot:: GenerateTailCallToJSCode");
100    GenerateTailCallToJSCode(optimized_code, function);
101
102    // Optimized code slot contains deoptimized code, or the code is cleared
103    // and tiering state hasn't yet been updated. Evict the code, update the
104    // state and re-enter the closure's code.
105    BIND(&heal_optimized_code_slot);
106    GenerateTailCallToReturnedCode(Runtime::kHealOptimizedCodeSlot, function);
107  }
108
109  // Fall-through if the optimized code cell is clear and the tiering state is
110  // kNone.
111  BIND(&fallthrough);
112}
113
114void LazyBuiltinsAssembler::CompileLazy(TNode<JSFunction> function) {
115  // First lookup code, maybe we don't need to compile!
116  Label compile_function(this, Label::kDeferred);
117
118  // Check the code object for the SFI. If SFI's code entry points to
119  // CompileLazy, then we need to lazy compile regardless of the function or
120  // tiering state.
121  TNode<SharedFunctionInfo> shared =
122      CAST(LoadObjectField(function, JSFunction::kSharedFunctionInfoOffset));
123  TVARIABLE(Uint16T, sfi_data_type);
124  TNode<CodeT> sfi_code =
125      GetSharedFunctionInfoCode(shared, &sfi_data_type, &compile_function);
126
127  TNode<HeapObject> feedback_cell_value = LoadFeedbackCellValue(function);
128
129  // If feedback cell isn't initialized, compile function
130  GotoIf(IsUndefined(feedback_cell_value), &compile_function);
131
132  CSA_DCHECK(this, TaggedNotEqual(sfi_code, HeapConstant(BUILTIN_CODE(
133                                                isolate(), CompileLazy))));
134  StoreObjectField(function, JSFunction::kCodeOffset, sfi_code);
135
136  Label maybe_use_sfi_code(this);
137  // If there is no feedback, don't check for optimized code.
138  GotoIf(HasInstanceType(feedback_cell_value, CLOSURE_FEEDBACK_CELL_ARRAY_TYPE),
139         &maybe_use_sfi_code);
140
141  // If it isn't undefined or fixed array it must be a feedback vector.
142  CSA_DCHECK(this, IsFeedbackVector(feedback_cell_value));
143
144  // Is there a tiering state or optimized code in the feedback vector?
145  MaybeTailCallOptimizedCodeSlot(function, CAST(feedback_cell_value));
146  Goto(&maybe_use_sfi_code);
147
148  // At this point we have a candidate Code object. It's *not* a cached
149  // optimized Code object (we'd have tail-called it above). A usual case would
150  // be the InterpreterEntryTrampoline to start executing existing bytecode.
151  BIND(&maybe_use_sfi_code);
152  Label tailcall_code(this), baseline(this);
153  TVARIABLE(CodeT, code);
154
155  // Check if we have baseline code.
156  GotoIf(InstanceTypeEqual(sfi_data_type.value(), CODET_TYPE), &baseline);
157
158  code = sfi_code;
159  Goto(&tailcall_code);
160
161  BIND(&baseline);
162  // Ensure we have a feedback vector.
163  code = Select<CodeT>(
164      IsFeedbackVector(feedback_cell_value), [=]() { return sfi_code; },
165      [=]() {
166        return CAST(CallRuntime(Runtime::kInstallBaselineCode,
167                                Parameter<Context>(Descriptor::kContext),
168                                function));
169      });
170  Goto(&tailcall_code);
171
172  BIND(&tailcall_code);
173  GenerateTailCallToJSCode(code.value(), function);
174
175  BIND(&compile_function);
176  GenerateTailCallToReturnedCode(Runtime::kCompileLazy, function);
177}
178
179TF_BUILTIN(CompileLazy, LazyBuiltinsAssembler) {
180  auto function = Parameter<JSFunction>(Descriptor::kTarget);
181
182  CompileLazy(function);
183}
184
185TF_BUILTIN(CompileLazyDeoptimizedCode, LazyBuiltinsAssembler) {
186  auto function = Parameter<JSFunction>(Descriptor::kTarget);
187
188  TNode<CodeT> code = HeapConstant(BUILTIN_CODE(isolate(), CompileLazy));
189  // Set the code slot inside the JSFunction to CompileLazy.
190  StoreObjectField(function, JSFunction::kCodeOffset, code);
191  GenerateTailCallToJSCode(code, function);
192}
193
194}  // namespace internal
195}  // namespace v8
196