| // Copyright 2018 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/builtins/builtins-lazy-gen.h" |
| |
| #include "src/builtins/builtins-utils-gen.h" |
| #include "src/builtins/builtins.h" |
| #include "src/common/globals.h" |
| #include "src/objects/feedback-vector.h" |
| #include "src/objects/shared-function-info.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| void LazyBuiltinsAssembler::GenerateTailCallToJSCode( |
| TNode<Code> code, TNode<JSFunction> function) { |
| TNode<Int32T> argc = |
| UncheckedCast<Int32T>(Parameter(Descriptor::kActualArgumentsCount)); |
| TNode<Context> context = CAST(Parameter(Descriptor::kContext)); |
| TNode<Object> new_target = CAST(Parameter(Descriptor::kNewTarget)); |
| |
| TailCallJSCode(code, context, function, new_target, argc); |
| } |
| |
| void LazyBuiltinsAssembler::GenerateTailCallToReturnedCode( |
| Runtime::FunctionId function_id, TNode<JSFunction> function) { |
| TNode<Context> context = CAST(Parameter(Descriptor::kContext)); |
| TNode<Code> code = CAST(CallRuntime(function_id, context, function)); |
| GenerateTailCallToJSCode(code, function); |
| } |
| |
| void LazyBuiltinsAssembler::TailCallRuntimeIfMarkerEquals( |
| TNode<Smi> marker, OptimizationMarker expected_marker, |
| Runtime::FunctionId function_id, TNode<JSFunction> function) { |
| Label no_match(this); |
| GotoIfNot(SmiEqual(marker, SmiConstant(expected_marker)), &no_match); |
| GenerateTailCallToReturnedCode(function_id, function); |
| BIND(&no_match); |
| } |
| |
| void LazyBuiltinsAssembler::MaybeTailCallOptimizedCodeSlot( |
| TNode<JSFunction> function, TNode<FeedbackVector> feedback_vector) { |
| Label fallthrough(this); |
| |
| TNode<MaybeObject> maybe_optimized_code_entry = LoadMaybeWeakObjectField( |
| feedback_vector, FeedbackVector::kOptimizedCodeWeakOrSmiOffset); |
| |
| // Check if the code entry is a Smi. If yes, we interpret it as an |
| // optimisation marker. Otherwise, interpret it as a weak reference to a code |
| // object. |
| Label optimized_code_slot_is_smi(this), optimized_code_slot_is_weak_ref(this); |
| Branch(TaggedIsSmi(maybe_optimized_code_entry), &optimized_code_slot_is_smi, |
| &optimized_code_slot_is_weak_ref); |
| |
| BIND(&optimized_code_slot_is_smi); |
| { |
| // Optimized code slot is a Smi optimization marker. |
| TNode<Smi> marker = CAST(maybe_optimized_code_entry); |
| |
| // Fall through if no optimization trigger. |
| GotoIf(SmiEqual(marker, SmiConstant(OptimizationMarker::kNone)), |
| &fallthrough); |
| |
| // TODO(ishell): introduce Runtime::kHandleOptimizationMarker and check |
| // all these marker values there. |
| TailCallRuntimeIfMarkerEquals(marker, |
| OptimizationMarker::kLogFirstExecution, |
| Runtime::kFunctionFirstExecution, function); |
| TailCallRuntimeIfMarkerEquals(marker, OptimizationMarker::kCompileOptimized, |
| Runtime::kCompileOptimized_NotConcurrent, |
| function); |
| TailCallRuntimeIfMarkerEquals( |
| marker, OptimizationMarker::kCompileOptimizedConcurrent, |
| Runtime::kCompileOptimized_Concurrent, function); |
| |
| // Otherwise, the marker is InOptimizationQueue, so fall through hoping |
| // that an interrupt will eventually update the slot with optimized code. |
| CSA_ASSERT(this, |
| SmiEqual(marker, |
| SmiConstant(OptimizationMarker::kInOptimizationQueue))); |
| Goto(&fallthrough); |
| } |
| |
| BIND(&optimized_code_slot_is_weak_ref); |
| { |
| // Optimized code slot is a weak reference. |
| TNode<Code> optimized_code = |
| CAST(GetHeapObjectAssumeWeak(maybe_optimized_code_entry, &fallthrough)); |
| |
| // Check if the optimized code is marked for deopt. If it is, call the |
| // runtime to clear it. |
| Label found_deoptimized_code(this); |
| TNode<CodeDataContainer> code_data_container = |
| CAST(LoadObjectField(optimized_code, Code::kCodeDataContainerOffset)); |
| |
| TNode<Int32T> code_kind_specific_flags = LoadObjectField<Int32T>( |
| code_data_container, CodeDataContainer::kKindSpecificFlagsOffset); |
| GotoIf(IsSetWord32<Code::MarkedForDeoptimizationField>( |
| code_kind_specific_flags), |
| &found_deoptimized_code); |
| |
| // Optimized code is good, get it into the closure and link the closure into |
| // the optimized functions list, then tail call the optimized code. |
| StoreObjectField(function, JSFunction::kCodeOffset, optimized_code); |
| GenerateTailCallToJSCode(optimized_code, function); |
| |
| // Optimized code slot contains deoptimized code, evict it and re-enter the |
| // closure's code. |
| BIND(&found_deoptimized_code); |
| GenerateTailCallToReturnedCode(Runtime::kEvictOptimizedCodeSlot, function); |
| } |
| |
| // Fall-through if the optimized code cell is clear and there is no |
| // optimization marker. |
| BIND(&fallthrough); |
| } |
| |
| void LazyBuiltinsAssembler::CompileLazy(TNode<JSFunction> function) { |
| // First lookup code, maybe we don't need to compile! |
| Label compile_function(this, Label::kDeferred); |
| |
| // Check the code object for the SFI. If SFI's code entry points to |
| // CompileLazy, then we need to lazy compile regardless of the function or |
| // feedback vector marker. |
| TNode<SharedFunctionInfo> shared = |
| CAST(LoadObjectField(function, JSFunction::kSharedFunctionInfoOffset)); |
| TNode<Code> sfi_code = GetSharedFunctionInfoCode(shared, &compile_function); |
| |
| TNode<HeapObject> feedback_cell_value = LoadFeedbackCellValue(function); |
| |
| // If feedback cell isn't initialized, compile function |
| GotoIf(IsUndefined(feedback_cell_value), &compile_function); |
| |
| Label use_sfi_code(this); |
| // If there is no feedback, don't check for optimized code. |
| GotoIf(HasInstanceType(feedback_cell_value, CLOSURE_FEEDBACK_CELL_ARRAY_TYPE), |
| &use_sfi_code); |
| |
| // If it isn't undefined or fixed array it must be a feedback vector. |
| CSA_ASSERT(this, IsFeedbackVector(feedback_cell_value)); |
| |
| // Is there an optimization marker or optimized code in the feedback vector? |
| MaybeTailCallOptimizedCodeSlot(function, CAST(feedback_cell_value)); |
| Goto(&use_sfi_code); |
| |
| BIND(&use_sfi_code); |
| // If not, install the SFI's code entry and jump to that. |
| CSA_ASSERT(this, TaggedNotEqual(sfi_code, HeapConstant(BUILTIN_CODE( |
| isolate(), CompileLazy)))); |
| StoreObjectField(function, JSFunction::kCodeOffset, sfi_code); |
| GenerateTailCallToJSCode(sfi_code, function); |
| |
| BIND(&compile_function); |
| GenerateTailCallToReturnedCode(Runtime::kCompileLazy, function); |
| } |
| |
| TF_BUILTIN(CompileLazy, LazyBuiltinsAssembler) { |
| TNode<JSFunction> function = CAST(Parameter(Descriptor::kTarget)); |
| |
| CompileLazy(function); |
| } |
| |
| TF_BUILTIN(CompileLazyDeoptimizedCode, LazyBuiltinsAssembler) { |
| TNode<JSFunction> function = CAST(Parameter(Descriptor::kTarget)); |
| |
| // Set the code slot inside the JSFunction to CompileLazy. |
| TNode<Code> code = HeapConstant(BUILTIN_CODE(isolate(), CompileLazy)); |
| StoreObjectField(function, JSFunction::kCodeOffset, code); |
| GenerateTailCallToJSCode(code, function); |
| } |
| |
| } // namespace internal |
| } // namespace v8 |