diff --git a/src/compiler/arm/code-generator-arm.cc b/src/compiler/arm/code-generator-arm.cc index d3ee44a6bc..4acc0562c3 100644 --- a/src/compiler/arm/code-generator-arm.cc +++ b/src/compiler/arm/code-generator-arm.cc @@ -1690,6 +1690,9 @@ void CodeGenerator::AssembleConstructFrame() { } } else if (descriptor->IsJSFunctionCall()) { __ Prologue(this->info()->GeneratePreagedPrologue()); + if (descriptor->PushArgumentCount()) { + __ Push(kJavaScriptCallArgCountRegister); + } } else { __ StubPrologue(info()->GetOutputStackFrameType()); } @@ -1699,7 +1702,8 @@ void CodeGenerator::AssembleConstructFrame() { } } - int shrink_slots = frame()->GetSpillSlotCount(); + int shrink_slots = + frame()->GetTotalFrameSlotCount() - descriptor->CalculateFixedFrameSize(); if (info()->is_osr()) { // TurboFan OSR-compiled functions cannot be entered directly. diff --git a/src/compiler/arm64/code-generator-arm64.cc b/src/compiler/arm64/code-generator-arm64.cc index 814652c9d7..3d30339dfe 100644 --- a/src/compiler/arm64/code-generator-arm64.cc +++ b/src/compiler/arm64/code-generator-arm64.cc @@ -1795,43 +1795,57 @@ void CodeGenerator::AssembleConstructFrame() { __ AssertCspAligned(); } + int fixed_frame_size = descriptor->CalculateFixedFrameSize(); + int shrink_slots = + frame()->GetTotalFrameSlotCount() - descriptor->CalculateFixedFrameSize(); + if (frame_access_state()->has_frame()) { + // Link the frame if (descriptor->IsJSFunctionCall()) { DCHECK(!descriptor->UseNativeStack()); __ Prologue(this->info()->GeneratePreagedPrologue()); } else { - if (descriptor->IsCFunctionCall()) { - __ Push(lr, fp); - __ Mov(fp, masm_.StackPointer()); - __ Claim(frame()->GetSpillSlotCount()); - } else { - __ StubPrologue(info()->GetOutputStackFrameType(), - frame()->GetTotalFrameSlotCount()); - } + __ Push(lr, fp); + __ Mov(fp, masm_.StackPointer()); } - if (!info()->GeneratePreagedPrologue()) { unwinding_info_writer_.MarkFrameConstructed(__ pc_offset()); } - } - int shrink_slots = frame()->GetSpillSlotCount(); + // Create OSR entry if applicable + if (info()->is_osr()) { + // TurboFan OSR-compiled functions cannot be entered directly. + __ Abort(kShouldNotDirectlyEnterOsrFunction); - if (info()->is_osr()) { - // TurboFan OSR-compiled functions cannot be entered directly. - __ Abort(kShouldNotDirectlyEnterOsrFunction); + // Unoptimized code jumps directly to this entrypoint while the + // unoptimized + // frame is still on the stack. Optimized code uses OSR values directly + // from + // the unoptimized frame. Thus, all that needs to be done is to allocate + // the + // remaining stack slots. + if (FLAG_code_comments) __ RecordComment("-- OSR entrypoint --"); + osr_pc_offset_ = __ pc_offset(); + shrink_slots -= OsrHelper(info()).UnoptimizedFrameSlots(); + } - // Unoptimized code jumps directly to this entrypoint while the unoptimized - // frame is still on the stack. Optimized code uses OSR values directly from - // the unoptimized frame. Thus, all that needs to be done is to allocate the - // remaining stack slots. - if (FLAG_code_comments) __ RecordComment("-- OSR entrypoint --"); - osr_pc_offset_ = __ pc_offset(); - shrink_slots -= OsrHelper(info()).UnoptimizedFrameSlots(); - } - - if (descriptor->IsJSFunctionCall()) { - __ Claim(shrink_slots); + // Build remainder of frame, including accounting for and filling-in + // frame-specific header information, e.g. claiming the extra slot that + // other platforms explicitly push for STUB frames and frames recording + // their argument count. + __ Claim(shrink_slots + (fixed_frame_size & 1)); + if (descriptor->PushArgumentCount()) { + __ Str(kJavaScriptCallArgCountRegister, + MemOperand(fp, OptimizedBuiltinFrameConstants::kArgCOffset)); + } + bool is_stub_frame = + !descriptor->IsJSFunctionCall() && !descriptor->IsCFunctionCall(); + if (is_stub_frame) { + UseScratchRegisterScope temps(masm()); + Register temp = temps.AcquireX(); + __ Mov(temp, Smi::FromInt(info()->GetOutputStackFrameType())); + __ Str(temp, MemOperand(fp, TypedFrameConstants::kFrameTypeOffset)); + } } // Save FP registers. diff --git a/src/compiler/code-assembler.cc b/src/compiler/code-assembler.cc index 6379d236c5..281186d729 100644 --- a/src/compiler/code-assembler.cc +++ b/src/compiler/code-assembler.cc @@ -41,8 +41,11 @@ CodeAssembler::CodeAssembler(Isolate* isolate, Zone* zone, CodeAssembler::CodeAssembler(Isolate* isolate, Zone* zone, int parameter_count, Code::Flags flags, const char* name) : CodeAssembler(isolate, zone, - Linkage::GetJSCallDescriptor(zone, false, parameter_count, - CallDescriptor::kNoFlags), + Linkage::GetJSCallDescriptor( + zone, false, parameter_count, + Code::ExtractKindFromFlags(flags) == Code::BUILTIN + ? CallDescriptor::kPushArgumentCount + : CallDescriptor::kNoFlags), flags, name) {} CodeAssembler::CodeAssembler(Isolate* isolate, Zone* zone, diff --git a/src/compiler/ia32/code-generator-ia32.cc b/src/compiler/ia32/code-generator-ia32.cc index 2e54e2a634..23ebac8524 100644 --- a/src/compiler/ia32/code-generator-ia32.cc +++ b/src/compiler/ia32/code-generator-ia32.cc @@ -1945,12 +1945,16 @@ void CodeGenerator::AssembleConstructFrame() { __ mov(ebp, esp); } else if (descriptor->IsJSFunctionCall()) { __ Prologue(this->info()->GeneratePreagedPrologue()); + if (descriptor->PushArgumentCount()) { + __ push(kJavaScriptCallArgCountRegister); + } } else { __ StubPrologue(info()->GetOutputStackFrameType()); } } - int shrink_slots = frame()->GetSpillSlotCount(); + int shrink_slots = + frame()->GetTotalFrameSlotCount() - descriptor->CalculateFixedFrameSize(); if (info()->is_osr()) { // TurboFan OSR-compiled functions cannot be entered directly. diff --git a/src/compiler/linkage.cc b/src/compiler/linkage.cc index 51c54fb806..971ea7212d 100644 --- a/src/compiler/linkage.cc +++ b/src/compiler/linkage.cc @@ -107,6 +107,23 @@ bool CallDescriptor::CanTailCall(const Node* node) const { return HasSameReturnLocationsAs(CallDescriptorOf(node->op())); } +int CallDescriptor::CalculateFixedFrameSize() const { + switch (kind_) { + case kCallJSFunction: + return PushArgumentCount() + ? OptimizedBuiltinFrameConstants::kFixedSlotCount + : StandardFrameConstants::kFixedSlotCount; + break; + case kCallAddress: + return CommonFrameConstants::kFixedSlotCountAboveFp + + CommonFrameConstants::kCPSlotCount; + break; + case kCallCodeObject: + return TypedFrameConstants::kFixedSlotCount; + } + UNREACHABLE(); + return 0; +} CallDescriptor* Linkage::ComputeIncoming(Zone* zone, CompilationInfo* info) { DCHECK(!info->IsStub()); diff --git a/src/compiler/linkage.h b/src/compiler/linkage.h index 0ecf64530e..b515aca2da 100644 --- a/src/compiler/linkage.h +++ b/src/compiler/linkage.h @@ -187,7 +187,9 @@ class V8_EXPORT_PRIVATE CallDescriptor final // Causes the code generator to initialize the root register. kInitializeRootRegister = 1u << 7, // Does not ever try to allocate space on our heap. - kNoAllocate = 1u << 8 + kNoAllocate = 1u << 8, + // Push argument count as part of function prologue. + kPushArgumentCount = 1u << 9 }; typedef base::Flags Flags; @@ -249,6 +251,7 @@ class V8_EXPORT_PRIVATE CallDescriptor final bool NeedsFrameState() const { return flags() & kNeedsFrameState; } bool SupportsTailCalls() const { return flags() & kSupportsTailCalls; } bool UseNativeStack() const { return flags() & kUseNativeStack; } + bool PushArgumentCount() const { return flags() & kPushArgumentCount; } bool InitializeRootRegister() const { return flags() & kInitializeRootRegister; } @@ -296,6 +299,8 @@ class V8_EXPORT_PRIVATE CallDescriptor final bool CanTailCall(const Node* call) const; + int CalculateFixedFrameSize() const; + private: friend class Linkage; diff --git a/src/compiler/mips/code-generator-mips.cc b/src/compiler/mips/code-generator-mips.cc index 6c31a411b6..7643b62e50 100644 --- a/src/compiler/mips/code-generator-mips.cc +++ b/src/compiler/mips/code-generator-mips.cc @@ -1922,6 +1922,9 @@ void CodeGenerator::AssembleConstructFrame() { __ mov(fp, sp); } else if (descriptor->IsJSFunctionCall()) { __ Prologue(this->info()->GeneratePreagedPrologue()); + if (descriptor->PushArgumentCount()) { + __ Push(kJavaScriptCallArgCountRegister); + } } else { __ StubPrologue(info()->GetOutputStackFrameType()); } diff --git a/src/compiler/mips64/code-generator-mips64.cc b/src/compiler/mips64/code-generator-mips64.cc index 157e0fd48a..1ce1c5428b 100644 --- a/src/compiler/mips64/code-generator-mips64.cc +++ b/src/compiler/mips64/code-generator-mips64.cc @@ -2244,7 +2244,8 @@ void CodeGenerator::AssembleConstructFrame() { } } - int shrink_slots = frame()->GetSpillSlotCount(); + int shrink_slots = + frame()->GetTotalFrameSlotCount() - descriptor->CalculateFixedFrameSize(); if (info()->is_osr()) { // TurboFan OSR-compiled functions cannot be entered directly. diff --git a/src/compiler/pipeline.cc b/src/compiler/pipeline.cc index e480520f66..7b0330cfb6 100644 --- a/src/compiler/pipeline.cc +++ b/src/compiler/pipeline.cc @@ -283,7 +283,7 @@ class PipelineData { DCHECK(frame_ == nullptr); int fixed_frame_size = 0; if (descriptor != nullptr) { - fixed_frame_size = CalculateFixedFrameSize(descriptor); + fixed_frame_size = descriptor->CalculateFixedFrameSize(); } frame_ = new (instruction_zone()) Frame(fixed_frame_size); } @@ -355,16 +355,6 @@ class PipelineData { // Source position output for --trace-turbo. std::string source_position_output_; - int CalculateFixedFrameSize(CallDescriptor* descriptor) { - if (descriptor->IsJSFunctionCall()) { - return StandardFrameConstants::kFixedSlotCount; - } - return descriptor->IsCFunctionCall() - ? (CommonFrameConstants::kFixedSlotCountAboveFp + - CommonFrameConstants::kCPSlotCount) - : TypedFrameConstants::kFixedSlotCount; - } - DISALLOW_COPY_AND_ASSIGN(PipelineData); }; diff --git a/src/compiler/x64/code-generator-x64.cc b/src/compiler/x64/code-generator-x64.cc index 8bd9e89b05..841606a4a4 100644 --- a/src/compiler/x64/code-generator-x64.cc +++ b/src/compiler/x64/code-generator-x64.cc @@ -2397,6 +2397,9 @@ void CodeGenerator::AssembleConstructFrame() { __ movq(rbp, rsp); } else if (descriptor->IsJSFunctionCall()) { __ Prologue(this->info()->GeneratePreagedPrologue()); + if (descriptor->PushArgumentCount()) { + __ pushq(kJavaScriptCallArgCountRegister); + } } else { __ StubPrologue(info()->GetOutputStackFrameType()); } @@ -2405,7 +2408,8 @@ void CodeGenerator::AssembleConstructFrame() { unwinding_info_writer_.MarkFrameConstructed(pc_base); } } - int shrink_slots = frame()->GetSpillSlotCount(); + int shrink_slots = + frame()->GetTotalFrameSlotCount() - descriptor->CalculateFixedFrameSize(); if (info()->is_osr()) { // TurboFan OSR-compiled functions cannot be entered directly. diff --git a/src/frames.cc b/src/frames.cc index b52052eee8..15adc16e23 100644 --- a/src/frames.cc +++ b/src/frames.cc @@ -1285,6 +1285,19 @@ DeoptimizationInputData* OptimizedFrame::GetDeoptimizationData( return nullptr; } +Object* OptimizedFrame::receiver() const { + Code* code = LookupCode(); + if (code->kind() == Code::BUILTIN) { + Address argc_ptr = fp() + OptimizedBuiltinFrameConstants::kArgCOffset; + intptr_t argc = *reinterpret_cast(argc_ptr); + intptr_t args_size = + (StandardFrameConstants::kFixedSlotCountAboveFp + argc) * kPointerSize; + Address receiver_ptr = fp() + args_size; + return *reinterpret_cast(receiver_ptr); + } else { + return JavaScriptFrame::receiver(); + } +} void OptimizedFrame::GetFunctions(List* functions) const { DCHECK(functions->length() == 0); diff --git a/src/frames.h b/src/frames.h index 66c36dc89d..1daa36404b 100644 --- a/src/frames.h +++ b/src/frames.h @@ -218,6 +218,48 @@ class StandardFrameConstants : public CommonFrameConstants { static const int kLastObjectOffset = kContextOffset; }; +// OptimizedBuiltinFrameConstants are used for TF-generated builtins. They +// always have a context below the saved fp/constant pool and below that the +// JSFunction of the executing function and below that an integer (not a Smi) +// containing the number of arguments passed to the builtin. +// +// slot JS frame +// +-----------------+-------------------------------- +// -n-1 | parameter 0 | ^ +// |- - - - - - - - -| | +// -n | | Caller +// ... | ... | frame slots +// -2 | parameter n-1 | (slot < 0) +// |- - - - - - - - -| | +// -1 | parameter n | v +// -----+-----------------+-------------------------------- +// 0 | return addr | ^ ^ +// |- - - - - - - - -| | | +// 1 | saved frame ptr | Fixed | +// |- - - - - - - - -| Header <-- frame ptr | +// 2 | [Constant Pool] | | | +// |- - - - - - - - -| | | +// 2+cp | Context | | if a constant pool | +// |- - - - - - - - -| | is used, cp = 1, | +// 3+cp | JSFunction | | otherwise, cp = 0 | +// |- - - - - - - - -| | | +// 4+cp | argc | v | +// +-----------------+---- | +// 5+cp | | ^ Callee +// |- - - - - - - - -| | frame slots +// ... | | Frame slots (slot >= 0) +// |- - - - - - - - -| | | +// | | v | +// -----+-----------------+----- <-- stack ptr ------------- +// +class OptimizedBuiltinFrameConstants : public StandardFrameConstants { + public: + static const int kArgCSize = kPointerSize; + static const int kArgCOffset = -3 * kPointerSize - kCPSlotSize; + static const int kFixedFrameSize = kFixedFrameSizeAboveFp - kArgCOffset; + static const int kFixedSlotCount = kFixedFrameSize / kPointerSize; +}; + // TypedFrames have a SMI type maker value below the saved FP/constant pool to // distinguish them from StandardFrames, which have a context in that position // instead. @@ -941,6 +983,8 @@ class OptimizedFrame : public JavaScriptFrame { DeoptimizationInputData* GetDeoptimizationData(int* deopt_index) const; + Object* receiver() const override; + static int StackSlotOffsetRelativeToFp(int slot_index); protected: diff --git a/test/cctest/compiler/code-assembler-tester.h b/test/cctest/compiler/code-assembler-tester.h index 1e3c58f453..c8da903ad2 100644 --- a/test/cctest/compiler/code-assembler-tester.h +++ b/test/cctest/compiler/code-assembler-tester.h @@ -35,10 +35,11 @@ class CodeAssemblerTesterImpl : private ZoneHolder, public CodeAssemblerT { scope_(isolate) {} // Test generating code for a JS function (e.g. builtins). - CodeAssemblerTesterImpl(Isolate* isolate, int parameter_count) + CodeAssemblerTesterImpl(Isolate* isolate, int parameter_count, + Code::Kind kind = Code::FUNCTION) : ZoneHolder(isolate), CodeAssemblerT(isolate, ZoneHolder::zone(), parameter_count, - Code::ComputeFlags(Code::FUNCTION), "test"), + Code::ComputeFlags(kind), "test"), scope_(isolate) {} // This constructor is intended to be used for creating code objects with diff --git a/test/cctest/test-code-stub-assembler.cc b/test/cctest/test-code-stub-assembler.cc index 8a3af41a70..94200aafdf 100644 --- a/test/cctest/test-code-stub-assembler.cc +++ b/test/cctest/test-code-stub-assembler.cc @@ -1510,7 +1510,8 @@ TEST(GotoIfException) { Isolate* isolate(CcTest::InitIsolateOnce()); const int kNumParams = 1; - CodeStubAssemblerTester m(isolate, kNumParams); + // Emulate TFJ builtin + CodeStubAssemblerTester m(isolate, kNumParams, Code::BUILTIN); Node* context = m.HeapConstant(Handle(isolate->native_context())); Node* to_string_tag = @@ -1529,9 +1530,6 @@ TEST(GotoIfException) { Handle code = m.GenerateCode(); CHECK(!code.is_null()); - // Emulate TFJ builtin - code->set_flags(Code::ComputeFlags(Code::BUILTIN)); - FunctionTester ft(code, kNumParams); Handle result = ft.Call().ToHandleChecked(); @@ -1551,7 +1549,8 @@ TEST(GotoIfExceptionMultiple) { Isolate* isolate(CcTest::InitIsolateOnce()); const int kNumParams = 4; // receiver, first, second, third - CodeStubAssemblerTester m(isolate, kNumParams); + // Emulate TFJ builtin + CodeStubAssemblerTester m(isolate, kNumParams, Code::BUILTIN); Node* context = m.HeapConstant(Handle(isolate->native_context())); Node* first_value = m.Parameter(0); @@ -1596,9 +1595,6 @@ TEST(GotoIfExceptionMultiple) { Handle code = m.GenerateCode(); CHECK(!code.is_null()); - // Emulate TFJ builtin - code->set_flags(Code::ComputeFlags(Code::BUILTIN)); - FunctionTester ft(code, kNumParams); Handle result;