diff --git a/BUILD.gn b/BUILD.gn index 8e52daf32a..937b1a91b4 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -112,6 +112,7 @@ declare_args() { # Disable arguments adaptor frame (sets -dV8_NO_ARGUMENTS_ADAPTOR). v8_disable_arguments_adaptor = v8_current_cpu == "x86" || v8_current_cpu == "x64" || + v8_current_cpu == "mipsel" || v8_current_cpu == "mips64el" || v8_current_cpu == "arm" # Sets -dOBJECT_PRINT. diff --git a/src/builtins/mips/builtins-mips.cc b/src/builtins/mips/builtins-mips.cc index b625ddf931..cba65817a4 100644 --- a/src/builtins/mips/builtins-mips.cc +++ b/src/builtins/mips/builtins-mips.cc @@ -68,23 +68,6 @@ static void GenerateTailCallToReturnedCode(MacroAssembler* masm, namespace { -enum StackLimitKind { kInterruptStackLimit, kRealStackLimit }; - -void LoadStackLimit(MacroAssembler* masm, Register destination, - StackLimitKind kind) { - DCHECK(masm->root_array_available()); - Isolate* isolate = masm->isolate(); - ExternalReference limit = - kind == StackLimitKind::kRealStackLimit - ? ExternalReference::address_of_real_jslimit(isolate) - : ExternalReference::address_of_jslimit(isolate); - DCHECK(TurboAssembler::IsAddressableThroughRootRegister(isolate, limit)); - - intptr_t offset = - TurboAssembler::RootRegisterOffsetForExternalReference(isolate, limit); - __ Lw(destination, MemOperand(kRootRegister, static_cast(offset))); -} - void Generate_JSBuiltinsConstructStubHelper(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- a0 : number of arguments @@ -131,22 +114,6 @@ void Generate_JSBuiltinsConstructStubHelper(MacroAssembler* masm) { __ Ret(); } -static void Generate_StackOverflowCheck(MacroAssembler* masm, Register num_args, - Register scratch1, Register scratch2, - Label* stack_overflow) { - // Check the stack for overflow. We are not trying to catch - // interruptions (e.g. debug break and preemption) here, so the "real stack - // limit" is checked. - LoadStackLimit(masm, scratch1, StackLimitKind::kRealStackLimit); - // Make scratch1 the space we have left. The stack might already be overflowed - // here which will cause scratch1 to become negative. - __ subu(scratch1, sp, scratch1); - // Check if the arguments will overflow the stack. - __ sll(scratch2, num_args, kPointerSizeLog2); - // Signed comparison. - __ Branch(stack_overflow, le, scratch1, Operand(scratch2)); -} - } // namespace // The construct stub for ES5 constructor functions and ES6 class constructors. @@ -241,7 +208,7 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { __ SmiUntag(a0); Label enough_stack_space, stack_overflow; - Generate_StackOverflowCheck(masm, a0, t0, t1, &stack_overflow); + __ StackOverflowCheck(a0, t0, t1, &stack_overflow); __ Branch(&enough_stack_space); __ bind(&stack_overflow); @@ -253,6 +220,11 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { __ bind(&enough_stack_space); + // TODO(victorgomes): When the arguments adaptor is completely removed, we + // should get the formal parameter count and copy the arguments in its + // correct position (including any undefined), instead of delaying this to + // InvokeFunction. + // Copy arguments and receiver to the expression stack. __ PushArray(t2, a0, t0, t1); // We need two copies because we may have to return the original one @@ -337,7 +309,7 @@ static void Generate_CheckStackOverflow(MacroAssembler* masm, Register argc, // interruptions (e.g. debug break and preemption) here, so the "real stack // limit" is checked. Label okay; - LoadStackLimit(masm, scratch1, StackLimitKind::kRealStackLimit); + __ LoadStackLimit(scratch1, MacroAssembler::StackLimitKind::kRealStackLimit); // Make a2 the space we have left. The stack might already be overflowed // here which will cause a2 to become negative. __ Subu(scratch1, sp, scratch1); @@ -682,7 +654,8 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { // Check the stack for overflow. We are not trying to catch interruptions // (i.e. debug break and preemption) here, so check the "real stack limit". Label stack_overflow; - LoadStackLimit(masm, kScratchReg, StackLimitKind::kRealStackLimit); + __ LoadStackLimit(kScratchReg, + MacroAssembler::StackLimitKind::kRealStackLimit); __ Branch(&stack_overflow, lo, sp, Operand(kScratchReg)); // ----------- S t a t e ------------- @@ -783,20 +756,35 @@ static void ReplaceClosureCodeWithOptimizedCode(MacroAssembler* masm, OMIT_SMI_CHECK); } -static void LeaveInterpreterFrame(MacroAssembler* masm, Register scratch) { - Register args_count = scratch; +static void LeaveInterpreterFrame(MacroAssembler* masm, Register scratch1, + Register scratch2) { + Register params_size = scratch1; - // Get the arguments + receiver count. - __ lw(args_count, + // Get the size of the formal parameters + receiver (in bytes). + __ lw(params_size, MemOperand(fp, InterpreterFrameConstants::kBytecodeArrayFromFp)); - __ lw(args_count, - FieldMemOperand(args_count, BytecodeArray::kParameterSizeOffset)); + __ lw(params_size, + FieldMemOperand(params_size, BytecodeArray::kParameterSizeOffset)); + +#ifdef V8_NO_ARGUMENTS_ADAPTOR + Register actual_params_size = scratch2; + // Compute the size of the actual parameters + receiver (in bytes). + __ Lw(actual_params_size, + MemOperand(fp, StandardFrameConstants::kArgCOffset)); + __ sll(actual_params_size, actual_params_size, kPointerSizeLog2); + __ Addu(actual_params_size, actual_params_size, Operand(kSystemPointerSize)); + + // If actual is bigger than formal, then we should use it to free up the stack + // arguments. + __ slt(t2, params_size, actual_params_size); + __ movn(params_size, actual_params_size, t2); +#endif // Leave the frame (also dropping the register file). __ LeaveFrame(StackFrame::INTERPRETED); // Drop receiver + arguments. - __ Addu(sp, sp, args_count); + __ Addu(sp, sp, params_size); } // Tail-call |function_id| if |actual_marker| == |expected_marker| @@ -1067,7 +1055,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { // Do a stack check to ensure we don't go over the limit. __ Subu(t1, sp, Operand(t0)); - LoadStackLimit(masm, a2, StackLimitKind::kRealStackLimit); + __ LoadStackLimit(a2, MacroAssembler::StackLimitKind::kRealStackLimit); __ Branch(&stack_overflow, lo, t1, Operand(a2)); // If ok, push undefined as the initial value for all register file entries. @@ -1099,7 +1087,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { // Perform interrupt stack check. // TODO(solanes): Merge with the real stack limit check above. Label stack_check_interrupt, after_stack_check_interrupt; - LoadStackLimit(masm, a2, StackLimitKind::kInterruptStackLimit); + __ LoadStackLimit(a2, MacroAssembler::StackLimitKind::kInterruptStackLimit); __ Branch(&stack_check_interrupt, lo, sp, Operand(a2)); __ bind(&after_stack_check_interrupt); @@ -1141,7 +1129,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { __ bind(&do_return); // The return value is in v0. - LeaveInterpreterFrame(masm, t0); + LeaveInterpreterFrame(masm, t0, t1); __ Jump(ra); __ bind(&stack_check_interrupt); @@ -1235,7 +1223,7 @@ void Builtins::Generate_InterpreterPushArgsThenCallImpl( __ Addu(t0, a0, Operand(1)); // Add one for receiver. - Generate_StackOverflowCheck(masm, t0, t4, t1, &stack_overflow); + __ StackOverflowCheck(t0, t4, t1, &stack_overflow); if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) { // Don't copy receiver. @@ -1285,7 +1273,7 @@ void Builtins::Generate_InterpreterPushArgsThenConstructImpl( // ----------------------------------- Label stack_overflow; __ addiu(t2, a0, 1); - Generate_StackOverflowCheck(masm, t2, t1, t0, &stack_overflow); + __ StackOverflowCheck(t2, t1, t0, &stack_overflow); if (mode == InterpreterPushArgsMode::kWithFinalSpread) { // The spread argument should not be pushed. @@ -1798,7 +1786,7 @@ void Builtins::Generate_CallOrConstructVarargs(MacroAssembler* masm, // Check for stack overflow. Label stack_overflow; - Generate_StackOverflowCheck(masm, t0, kScratchReg, t1, &stack_overflow); + __ StackOverflowCheck(t0, kScratchReg, t1, &stack_overflow); // Move the arguments already in the stack, // including the receiver and the return address. @@ -1877,6 +1865,13 @@ void Builtins::Generate_CallOrConstructForwardVarargs(MacroAssembler* masm, __ bind(&new_target_constructor); } +#ifdef V8_NO_ARGUMENTS_ADAPTOR + // TODO(victorgomes): Remove this copy when all the arguments adaptor frame + // code is erased. + __ mov(t3, fp); + __ Lw(t2, MemOperand(fp, StandardFrameConstants::kArgCOffset)); +#else + // Check if we have an arguments adaptor frame below the function frame. Label arguments_adaptor, arguments_done; __ lw(t3, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); @@ -1898,13 +1893,14 @@ void Builtins::Generate_CallOrConstructForwardVarargs(MacroAssembler* masm, __ SmiUntag(t2); } __ bind(&arguments_done); +#endif Label stack_done, stack_overflow; __ Subu(t2, t2, a2); __ Branch(&stack_done, le, t2, Operand(zero_reg)); { // Check for stack overflow. - Generate_StackOverflowCheck(masm, t2, t0, t1, &stack_overflow); + __ StackOverflowCheck(t2, t0, t1, &stack_overflow); // Forward the arguments from the caller frame. // Point to the first argument to copy (skipping the receiver). @@ -2095,7 +2091,8 @@ void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm) { __ Subu(t1, sp, Operand(t1)); // Check the stack for overflow. We are not trying to catch interruptions // (i.e. debug break and preemption) here, so check the "real stack limit". - LoadStackLimit(masm, kScratchReg, StackLimitKind::kRealStackLimit); + __ LoadStackLimit(kScratchReg, + MacroAssembler::StackLimitKind::kRealStackLimit); __ Branch(&done, hs, t1, Operand(kScratchReg)); { FrameScope scope(masm, StackFrame::MANUAL); @@ -2236,7 +2233,8 @@ void Builtins::Generate_ConstructBoundFunction(MacroAssembler* masm) { __ Subu(t1, sp, Operand(t1)); // Check the stack for overflow. We are not trying to catch interruptions // (i.e. debug break and preemption) here, so check the "real stack limit". - LoadStackLimit(masm, kScratchReg, StackLimitKind::kRealStackLimit); + __ LoadStackLimit(kScratchReg, + MacroAssembler::StackLimitKind::kRealStackLimit); __ Branch(&done, hs, t1, Operand(kScratchReg)); { FrameScope scope(masm, StackFrame::MANUAL); @@ -2356,7 +2354,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { // a3: new target (passed through to callee) __ bind(&enough); EnterArgumentsAdaptorFrame(masm); - Generate_StackOverflowCheck(masm, a2, t1, kScratchReg, &stack_overflow); + __ StackOverflowCheck(a2, t1, kScratchReg, &stack_overflow); // Calculate copy start address into a0 and copy end address into t1. __ Lsa(a0, fp, a2, kPointerSizeLog2); @@ -2386,7 +2384,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { { // Too few parameters: Actual < expected. __ bind(&too_few); EnterArgumentsAdaptorFrame(masm); - Generate_StackOverflowCheck(masm, a2, t1, kScratchReg, &stack_overflow); + __ StackOverflowCheck(a2, t1, kScratchReg, &stack_overflow); // Fill the remaining expected arguments with undefined. __ LoadRoot(t0, RootIndex::kUndefinedValue); diff --git a/src/builtins/mips64/builtins-mips64.cc b/src/builtins/mips64/builtins-mips64.cc index 10f8109931..1027ec35e5 100644 --- a/src/builtins/mips64/builtins-mips64.cc +++ b/src/builtins/mips64/builtins-mips64.cc @@ -67,24 +67,6 @@ static void GenerateTailCallToReturnedCode(MacroAssembler* masm, namespace { -enum StackLimitKind { kInterruptStackLimit, kRealStackLimit }; - -void LoadStackLimit(MacroAssembler* masm, Register destination, - StackLimitKind kind) { - DCHECK(masm->root_array_available()); - Isolate* isolate = masm->isolate(); - ExternalReference limit = - kind == StackLimitKind::kRealStackLimit - ? ExternalReference::address_of_real_jslimit(isolate) - : ExternalReference::address_of_jslimit(isolate); - DCHECK(TurboAssembler::IsAddressableThroughRootRegister(isolate, limit)); - - intptr_t offset = - TurboAssembler::RootRegisterOffsetForExternalReference(isolate, limit); - CHECK(is_int32(offset)); - __ Ld(destination, MemOperand(kRootRegister, static_cast(offset))); -} - void Generate_JSBuiltinsConstructStubHelper(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- a0 : number of arguments @@ -133,22 +115,6 @@ void Generate_JSBuiltinsConstructStubHelper(MacroAssembler* masm) { __ Ret(); } -static void Generate_StackOverflowCheck(MacroAssembler* masm, Register num_args, - Register scratch1, Register scratch2, - Label* stack_overflow) { - // Check the stack for overflow. We are not trying to catch - // interruptions (e.g. debug break and preemption) here, so the "real stack - // limit" is checked. - LoadStackLimit(masm, scratch1, StackLimitKind::kRealStackLimit); - // Make scratch1 the space we have left. The stack might already be overflowed - // here which will cause scratch1 to become negative. - __ dsubu(scratch1, sp, scratch1); - // Check if the arguments will overflow the stack. - __ dsll(scratch2, num_args, kPointerSizeLog2); - // Signed comparison. - __ Branch(stack_overflow, le, scratch1, Operand(scratch2)); -} - } // namespace // The construct stub for ES5 constructor functions and ES6 class constructors. @@ -243,7 +209,7 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { __ SmiUntag(a0); Label enough_stack_space, stack_overflow; - Generate_StackOverflowCheck(masm, a0, t0, t1, &stack_overflow); + __ StackOverflowCheck(a0, t0, t1, &stack_overflow); __ Branch(&enough_stack_space); __ bind(&stack_overflow); @@ -255,6 +221,11 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { __ bind(&enough_stack_space); + // TODO(victorgomes): When the arguments adaptor is completely removed, we + // should get the formal parameter count and copy the arguments in its + // correct position (including any undefined), instead of delaying this to + // InvokeFunction. + // Copy arguments and receiver to the expression stack. __ PushArray(t2, a0, t0, t1); // We need two copies because we may have to return the original one @@ -378,7 +349,8 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { // Check the stack for overflow. We are not trying to catch interruptions // (i.e. debug break and preemption) here, so check the "real stack limit". Label stack_overflow; - LoadStackLimit(masm, kScratchReg, StackLimitKind::kRealStackLimit); + __ LoadStackLimit(kScratchReg, + MacroAssembler::StackLimitKind::kRealStackLimit); __ Branch(&stack_overflow, lo, sp, Operand(kScratchReg)); // ----------- S t a t e ------------- @@ -481,7 +453,7 @@ static void Generate_CheckStackOverflow(MacroAssembler* masm, Register argc, // interruptions (e.g. debug break and preemption) here, so the "real stack // limit" is checked. Label okay; - LoadStackLimit(masm, scratch1, StackLimitKind::kRealStackLimit); + __ LoadStackLimit(scratch1, MacroAssembler::StackLimitKind::kRealStackLimit); // Make a2 the space we have left. The stack might already be overflowed // here which will cause r2 to become negative. __ dsubu(scratch1, sp, scratch1); @@ -801,19 +773,35 @@ static void ReplaceClosureCodeWithOptimizedCode(MacroAssembler* masm, OMIT_SMI_CHECK); } -static void LeaveInterpreterFrame(MacroAssembler* masm, Register scratch) { - Register args_count = scratch; +static void LeaveInterpreterFrame(MacroAssembler* masm, Register scratch1, + Register scratch2) { + Register params_size = scratch1; - // Get the arguments + receiver count. - __ Ld(args_count, + // Get the size of the formal parameters + receiver (in bytes). + __ Ld(params_size, MemOperand(fp, InterpreterFrameConstants::kBytecodeArrayFromFp)); - __ Lw(t0, FieldMemOperand(args_count, BytecodeArray::kParameterSizeOffset)); + __ Lw(params_size, + FieldMemOperand(params_size, BytecodeArray::kParameterSizeOffset)); + +#ifdef V8_NO_ARGUMENTS_ADAPTOR + Register actual_params_size = scratch2; + // Compute the size of the actual parameters + receiver (in bytes). + __ Ld(actual_params_size, + MemOperand(fp, StandardFrameConstants::kArgCOffset)); + __ dsll(actual_params_size, actual_params_size, kPointerSizeLog2); + __ Daddu(actual_params_size, actual_params_size, Operand(kSystemPointerSize)); + + // If actual is bigger than formal, then we should use it to free up the stack + // arguments. + __ slt(t2, params_size, actual_params_size); + __ movn(params_size, actual_params_size, t2); +#endif // Leave the frame (also dropping the register file). __ LeaveFrame(StackFrame::INTERPRETED); // Drop receiver + arguments. - __ Daddu(sp, sp, args_count); + __ Daddu(sp, sp, params_size); } // Tail-call |function_id| if |actual_marker| == |expected_marker| @@ -1085,7 +1073,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { // Do a stack check to ensure we don't go over the limit. __ Dsubu(a5, sp, Operand(a4)); - LoadStackLimit(masm, a2, StackLimitKind::kRealStackLimit); + __ LoadStackLimit(a2, MacroAssembler::StackLimitKind::kRealStackLimit); __ Branch(&stack_overflow, lo, a5, Operand(a2)); // If ok, push undefined as the initial value for all register file entries. @@ -1117,7 +1105,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { // Perform interrupt stack check. // TODO(solanes): Merge with the real stack limit check above. Label stack_check_interrupt, after_stack_check_interrupt; - LoadStackLimit(masm, a5, StackLimitKind::kInterruptStackLimit); + __ LoadStackLimit(a5, MacroAssembler::StackLimitKind::kInterruptStackLimit); __ Branch(&stack_check_interrupt, lo, sp, Operand(a5)); __ bind(&after_stack_check_interrupt); @@ -1160,7 +1148,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { __ bind(&do_return); // The return value is in v0. - LeaveInterpreterFrame(masm, t0); + LeaveInterpreterFrame(masm, t0, t1); __ Jump(ra); __ bind(&stack_check_interrupt); @@ -1254,7 +1242,7 @@ void Builtins::Generate_InterpreterPushArgsThenCallImpl( __ Daddu(a3, a0, Operand(1)); // Add one for receiver. - Generate_StackOverflowCheck(masm, a3, a4, t0, &stack_overflow); + __ StackOverflowCheck(a3, a4, t0, &stack_overflow); if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) { // Don't copy receiver. @@ -1304,7 +1292,7 @@ void Builtins::Generate_InterpreterPushArgsThenConstructImpl( // ----------------------------------- Label stack_overflow; __ daddiu(a6, a0, 1); - Generate_StackOverflowCheck(masm, a6, a5, t0, &stack_overflow); + __ StackOverflowCheck(a6, a5, t0, &stack_overflow); if (mode == InterpreterPushArgsMode::kWithFinalSpread) { // The spread argument should not be pushed. @@ -1859,7 +1847,7 @@ void Builtins::Generate_CallOrConstructVarargs(MacroAssembler* masm, // Check for stack overflow. Label stack_overflow; - Generate_StackOverflowCheck(masm, len, kScratchReg, a5, &stack_overflow); + __ StackOverflowCheck(len, kScratchReg, a5, &stack_overflow); // Move the arguments already in the stack, // including the receiver and the return address. @@ -1943,6 +1931,13 @@ void Builtins::Generate_CallOrConstructForwardVarargs(MacroAssembler* masm, __ bind(&new_target_constructor); } +#ifdef V8_NO_ARGUMENTS_ADAPTOR + // TODO(victorgomes): Remove this copy when all the arguments adaptor frame + // code is erased. + __ mov(a6, fp); + __ Ld(a7, MemOperand(fp, StandardFrameConstants::kArgCOffset)); +#else + // Check if we have an arguments adaptor frame below the function frame. Label arguments_adaptor, arguments_done; __ Ld(a6, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); @@ -1964,13 +1959,14 @@ void Builtins::Generate_CallOrConstructForwardVarargs(MacroAssembler* masm, MemOperand(a6, ArgumentsAdaptorFrameConstants::kLengthOffset)); } __ bind(&arguments_done); +#endif Label stack_done, stack_overflow; __ Subu(a7, a7, a2); __ Branch(&stack_done, le, a7, Operand(zero_reg)); { // Check for stack overflow. - Generate_StackOverflowCheck(masm, a7, a4, a5, &stack_overflow); + __ StackOverflowCheck(a7, a4, a5, &stack_overflow); // Forward the arguments from the caller frame. @@ -2161,7 +2157,8 @@ void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm) { __ Dsubu(t0, sp, Operand(a5)); // Check the stack for overflow. We are not trying to catch interruptions // (i.e. debug break and preemption) here, so check the "real stack limit". - LoadStackLimit(masm, kScratchReg, StackLimitKind::kRealStackLimit); + __ LoadStackLimit(kScratchReg, + MacroAssembler::StackLimitKind::kRealStackLimit); __ Branch(&done, hs, t0, Operand(kScratchReg)); { FrameScope scope(masm, StackFrame::MANUAL); @@ -2300,7 +2297,8 @@ void Builtins::Generate_ConstructBoundFunction(MacroAssembler* masm) { __ Dsubu(t0, sp, Operand(a5)); // Check the stack for overflow. We are not trying to catch interruptions // (i.e. debug break and preemption) here, so check the "real stack limit". - LoadStackLimit(masm, kScratchReg, StackLimitKind::kRealStackLimit); + __ LoadStackLimit(kScratchReg, + MacroAssembler::StackLimitKind::kRealStackLimit); __ Branch(&done, hs, t0, Operand(kScratchReg)); { FrameScope scope(masm, StackFrame::MANUAL); @@ -2421,7 +2419,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { // a3: new target (passed through to callee) __ bind(&enough); EnterArgumentsAdaptorFrame(masm); - Generate_StackOverflowCheck(masm, a2, a5, kScratchReg, &stack_overflow); + __ StackOverflowCheck(a2, a5, kScratchReg, &stack_overflow); // Calculate copy start address into a0 and copy end address into a4. __ dsll(a0, a2, kPointerSizeLog2); @@ -2453,7 +2451,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { { // Too few parameters: Actual < expected. __ bind(&too_few); EnterArgumentsAdaptorFrame(masm); - Generate_StackOverflowCheck(masm, a2, a5, kScratchReg, &stack_overflow); + __ StackOverflowCheck(a2, a5, kScratchReg, &stack_overflow); // Fill the remaining expected arguments with undefined. __ LoadRoot(t0, RootIndex::kUndefinedValue); diff --git a/src/codegen/mips/macro-assembler-mips.cc b/src/codegen/mips/macro-assembler-mips.cc index 9a852878c3..8f53a1aaf0 100644 --- a/src/codegen/mips/macro-assembler-mips.cc +++ b/src/codegen/mips/macro-assembler-mips.cc @@ -4387,23 +4387,106 @@ void TurboAssembler::PrepareForTailCall(Register callee_args_count, mov(sp, dst_reg); } +void MacroAssembler::LoadStackLimit(Register destination, StackLimitKind kind) { + DCHECK(root_array_available()); + Isolate* isolate = this->isolate(); + ExternalReference limit = + kind == StackLimitKind::kRealStackLimit + ? ExternalReference::address_of_real_jslimit(isolate) + : ExternalReference::address_of_jslimit(isolate); + DCHECK(TurboAssembler::IsAddressableThroughRootRegister(isolate, limit)); + + intptr_t offset = + TurboAssembler::RootRegisterOffsetForExternalReference(isolate, limit); + CHECK(is_int32(offset)); + Lw(destination, MemOperand(kRootRegister, static_cast(offset))); +} + +void MacroAssembler::StackOverflowCheck(Register num_args, Register scratch1, + Register scratch2, + Label* stack_overflow) { + // Check the stack for overflow. We are not trying to catch + // interruptions (e.g. debug break and preemption) here, so the "real stack + // limit" is checked. + + LoadStackLimit(scratch1, StackLimitKind::kRealStackLimit); + // Make scratch1 the space we have left. The stack might already be overflowed + // here which will cause scratch1 to become negative. + subu(scratch1, sp, scratch1); + // Check if the arguments will overflow the stack. + sll(scratch2, num_args, kPointerSizeLog2); + // Signed comparison. + Branch(stack_overflow, le, scratch1, Operand(scratch2)); +} + void MacroAssembler::InvokePrologue(Register expected_parameter_count, Register actual_parameter_count, Label* done, InvokeFlag flag) { Label regular_invoke; - // Check whether the expected and actual arguments count match. The - // registers are set up according to contract with - // ArgumentsAdaptorTrampoline: // a0: actual arguments count // a1: function (passed through to callee) // a2: expected arguments count - // The code below is made a lot easier because the calling code already sets - // up actual and expected registers according to the contract. DCHECK_EQ(actual_parameter_count, a0); DCHECK_EQ(expected_parameter_count, a2); +#ifdef V8_NO_ARGUMENTS_ADAPTOR + // If the expected parameter count is equal to the adaptor sentinel, no need + // to push undefined value as arguments. + Branch(®ular_invoke, eq, expected_parameter_count, + Operand(kDontAdaptArgumentsSentinel)); + + // If overapplication or if the actual argument count is equal to the + // formal parameter count, no need to push extra undefined values. + Subu(expected_parameter_count, expected_parameter_count, + actual_parameter_count); + Branch(®ular_invoke, le, expected_parameter_count, Operand(zero_reg)); + + Label stack_overflow; + StackOverflowCheck(expected_parameter_count, t0, t1, &stack_overflow); + // Underapplication. Move the arguments already in the stack, including the + // receiver and the return address. + { + Label copy; + Register src = t3, dest = t4; + mov(src, sp); + sll(t0, expected_parameter_count, kSystemPointerSizeLog2); + Subu(sp, sp, Operand(t0)); + // Update stack pointer. + mov(dest, sp); + mov(t0, a0); + bind(©); + Lw(t1, MemOperand(src, 0)); + Sw(t1, MemOperand(dest, 0)); + Subu(t0, t0, Operand(1)); + Addu(src, src, Operand(kSystemPointerSize)); + Addu(dest, dest, Operand(kSystemPointerSize)); + Branch(©, ge, t0, Operand(zero_reg)); + } + + // Fill remaining expected arguments with undefined values. + LoadRoot(t0, RootIndex::kUndefinedValue); + { + Label loop; + bind(&loop); + Sw(t0, MemOperand(t4, 0)); + Subu(expected_parameter_count, expected_parameter_count, Operand(1)); + Addu(t4, t4, Operand(kSystemPointerSize)); + Branch(&loop, gt, expected_parameter_count, Operand(zero_reg)); + } + b(®ular_invoke); + nop(); + + bind(&stack_overflow); + { + FrameScope frame(this, StackFrame::MANUAL); + CallRuntime(Runtime::kThrowStackOverflow); + break_(0xCC); + } +#else + // Check whether the expected and actual arguments count match. The registers + // are set up according to contract with ArgumentsAdaptorTrampoline: Branch(®ular_invoke, eq, expected_parameter_count, Operand(actual_parameter_count)); @@ -4414,7 +4497,7 @@ void MacroAssembler::InvokePrologue(Register expected_parameter_count, } else { Jump(adaptor, RelocInfo::CODE_TARGET); } - +#endif bind(®ular_invoke); } diff --git a/src/codegen/mips/macro-assembler-mips.h b/src/codegen/mips/macro-assembler-mips.h index e2c60cd9b7..d91a4a7bb8 100644 --- a/src/codegen/mips/macro-assembler-mips.h +++ b/src/codegen/mips/macro-assembler-mips.h @@ -1096,6 +1096,14 @@ class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler { Register scratch2); // ------------------------------------------------------------------------- + // Stack limit utilities + + enum StackLimitKind { kInterruptStackLimit, kRealStackLimit }; + void LoadStackLimit(Register destination, StackLimitKind kind); + void StackOverflowCheck(Register num_args, Register scratch1, + Register scratch2, Label* stack_overflow); + + // --------------------------------------------------------------------------- // Smi utilities. void SmiTag(Register reg) { Addu(reg, reg, reg); } diff --git a/src/codegen/mips64/macro-assembler-mips64.cc b/src/codegen/mips64/macro-assembler-mips64.cc index cc7ba31d02..b5744e1caa 100644 --- a/src/codegen/mips64/macro-assembler-mips64.cc +++ b/src/codegen/mips64/macro-assembler-mips64.cc @@ -4732,23 +4732,107 @@ void TurboAssembler::PrepareForTailCall(Register callee_args_count, mov(sp, dst_reg); } +void MacroAssembler::LoadStackLimit(Register destination, StackLimitKind kind) { + DCHECK(root_array_available()); + Isolate* isolate = this->isolate(); + ExternalReference limit = + kind == StackLimitKind::kRealStackLimit + ? ExternalReference::address_of_real_jslimit(isolate) + : ExternalReference::address_of_jslimit(isolate); + DCHECK(TurboAssembler::IsAddressableThroughRootRegister(isolate, limit)); + + intptr_t offset = + TurboAssembler::RootRegisterOffsetForExternalReference(isolate, limit); + CHECK(is_int32(offset)); + Ld(destination, MemOperand(kRootRegister, static_cast(offset))); +} + +void MacroAssembler::StackOverflowCheck(Register num_args, Register scratch1, + Register scratch2, + Label* stack_overflow) { + // Check the stack for overflow. We are not trying to catch + // interruptions (e.g. debug break and preemption) here, so the "real stack + // limit" is checked. + + LoadStackLimit(scratch1, StackLimitKind::kRealStackLimit); + // Make scratch1 the space we have left. The stack might already be overflowed + // here which will cause scratch1 to become negative. + dsubu(scratch1, sp, scratch1); + // Check if the arguments will overflow the stack. + dsll(scratch2, num_args, kPointerSizeLog2); + // Signed comparison. + Branch(stack_overflow, le, scratch1, Operand(scratch2)); +} + void MacroAssembler::InvokePrologue(Register expected_parameter_count, Register actual_parameter_count, Label* done, InvokeFlag flag) { Label regular_invoke; - // Check whether the expected and actual arguments count match. The registers - // are set up according to contract with ArgumentsAdaptorTrampoline: // a0: actual arguments count // a1: function (passed through to callee) // a2: expected arguments count - // The code below is made a lot easier because the calling code already sets - // up actual and expected registers according to the contract. - DCHECK_EQ(actual_parameter_count, a0); DCHECK_EQ(expected_parameter_count, a2); +#ifdef V8_NO_ARGUMENTS_ADAPTOR + // If the expected parameter count is equal to the adaptor sentinel, no need + // to push undefined value as arguments. + Branch(®ular_invoke, eq, expected_parameter_count, + Operand(kDontAdaptArgumentsSentinel)); + + // If overapplication or if the actual argument count is equal to the + // formal parameter count, no need to push extra undefined values. + Dsubu(expected_parameter_count, expected_parameter_count, + actual_parameter_count); + Branch(®ular_invoke, le, expected_parameter_count, Operand(zero_reg)); + + Label stack_overflow; + StackOverflowCheck(expected_parameter_count, t0, t1, &stack_overflow); + // Underapplication. Move the arguments already in the stack, including the + // receiver and the return address. + { + Label copy; + Register src = a6, dest = a7; + mov(src, sp); + dsll(t0, expected_parameter_count, kSystemPointerSizeLog2); + Dsubu(sp, sp, Operand(t0)); + // Update stack pointer. + mov(dest, sp); + mov(t0, a0); + bind(©); + Ld(t1, MemOperand(src, 0)); + Sd(t1, MemOperand(dest, 0)); + Dsubu(t0, t0, Operand(1)); + Daddu(src, src, Operand(kSystemPointerSize)); + Daddu(dest, dest, Operand(kSystemPointerSize)); + Branch(©, ge, t0, Operand(zero_reg)); + } + + // Fill remaining expected arguments with undefined values. + LoadRoot(t0, RootIndex::kUndefinedValue); + { + Label loop; + bind(&loop); + Sd(t0, MemOperand(a7, 0)); + Dsubu(expected_parameter_count, expected_parameter_count, Operand(1)); + Daddu(a7, a7, Operand(kSystemPointerSize)); + Branch(&loop, gt, expected_parameter_count, Operand(zero_reg)); + } + b(®ular_invoke); + nop(); + + bind(&stack_overflow); + { + FrameScope frame(this, StackFrame::MANUAL); + CallRuntime(Runtime::kThrowStackOverflow); + break_(0xCC); + } +#else + // Check whether the expected and actual arguments count match. The registers + // are set up according to contract with ArgumentsAdaptorTrampoline: + Branch(®ular_invoke, eq, expected_parameter_count, Operand(actual_parameter_count)); @@ -4759,7 +4843,7 @@ void MacroAssembler::InvokePrologue(Register expected_parameter_count, } else { Jump(adaptor, RelocInfo::CODE_TARGET); } - +#endif bind(®ular_invoke); } diff --git a/src/codegen/mips64/macro-assembler-mips64.h b/src/codegen/mips64/macro-assembler-mips64.h index febfbf5f75..a0d5e59bf0 100644 --- a/src/codegen/mips64/macro-assembler-mips64.h +++ b/src/codegen/mips64/macro-assembler-mips64.h @@ -1140,6 +1140,14 @@ class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler { Register scratch2); // ------------------------------------------------------------------------- + // Stack limit utilities + + enum StackLimitKind { kInterruptStackLimit, kRealStackLimit }; + void LoadStackLimit(Register destination, StackLimitKind kind); + void StackOverflowCheck(Register num_args, Register scratch1, + Register scratch2, Label* stack_overflow); + + // --------------------------------------------------------------------------- // Smi utilities. void SmiTag(Register dst, Register src) { diff --git a/src/compiler/backend/mips/code-generator-mips.cc b/src/compiler/backend/mips/code-generator-mips.cc index 76acbd959f..dd7154e5c5 100644 --- a/src/compiler/backend/mips/code-generator-mips.cc +++ b/src/compiler/backend/mips/code-generator-mips.cc @@ -4031,9 +4031,8 @@ void CodeGenerator::AssembleConstructFrame() { } } -void CodeGenerator::AssembleReturn(InstructionOperand* pop) { +void CodeGenerator::AssembleReturn(InstructionOperand* additional_pop_count) { auto call_descriptor = linkage()->GetIncomingDescriptor(); - int pop_count = static_cast(call_descriptor->StackParameterCount()); const int returns = frame()->GetReturnSlotCount(); if (returns != 0) { @@ -4053,36 +4052,76 @@ void CodeGenerator::AssembleReturn(InstructionOperand* pop) { } MipsOperandConverter g(this, nullptr); + const int parameter_count = + static_cast(call_descriptor->StackParameterCount()); + + // {aditional_pop_count} is only greater than zero if {parameter_count = 0}. + // Check RawMachineAssembler::PopAndReturn. + if (parameter_count != 0) { + if (additional_pop_count->IsImmediate()) { + DCHECK_EQ(g.ToConstant(additional_pop_count).ToInt32(), 0); + } else if (__ emit_debug_code()) { + __ Assert(eq, AbortReason::kUnexpectedAdditionalPopValue, + g.ToRegister(additional_pop_count), + Operand(static_cast(0))); + } + } +#ifdef V8_NO_ARGUMENTS_ADAPTOR + // Functions with JS linkage have at least one parameter (the receiver). + // If {parameter_count} == 0, it means it is a builtin with + // kDontAdaptArgumentsSentinel, which takes care of JS arguments popping + // itself. + const bool drop_jsargs = frame_access_state()->has_frame() && + call_descriptor->IsJSFunctionCall() && + parameter_count != 0; +#else + const bool drop_jsargs = false; +#endif + if (call_descriptor->IsCFunctionCall()) { AssembleDeconstructFrame(); } else if (frame_access_state()->has_frame()) { // Canonicalize JSFunction return sites for now unless they have an variable // number of stack slot pops. - if (pop->IsImmediate() && g.ToConstant(pop).ToInt32() == 0) { + if (additional_pop_count->IsImmediate() && + g.ToConstant(additional_pop_count).ToInt32() == 0) { if (return_label_.is_bound()) { __ Branch(&return_label_); return; } else { __ bind(&return_label_); - AssembleDeconstructFrame(); } - } else { - AssembleDeconstructFrame(); } + if (drop_jsargs) { + // Get the actual argument count + __ Lw(t0, MemOperand(fp, StandardFrameConstants::kArgCOffset)); + } + AssembleDeconstructFrame(); } - if (pop->IsImmediate()) { - DCHECK_EQ(Constant::kInt32, g.ToConstant(pop).type()); - pop_count += g.ToConstant(pop).ToInt32(); + + if (drop_jsargs) { + // We must pop all arguments from the stack (including the receiver). This + // number of arguments is given by max(1 + argc_reg, parameter_count). + __ Addu(t0, t0, Operand(1)); // Also pop the receiver. + if (parameter_count > 1) { + Label max_number; + __ Branch(&max_number, ge, t0, Operand(parameter_count)); + __ li(t0, parameter_count); + __ bind(&max_number); + } + __ sll(t0, t0, kSystemPointerSizeLog2); + __ Addu(sp, sp, t0); + } else if (additional_pop_count->IsImmediate()) { + DCHECK_EQ(Constant::kInt32, g.ToConstant(additional_pop_count).type()); + int additional_count = g.ToConstant(additional_pop_count).ToInt32(); + __ Drop(parameter_count + additional_count); } else { - Register pop_reg = g.ToRegister(pop); + Register pop_reg = g.ToRegister(additional_pop_count); + __ Drop(parameter_count); __ sll(pop_reg, pop_reg, kSystemPointerSizeLog2); - __ Addu(sp, sp, Operand(pop_reg)); - } - if (pop_count != 0) { - __ DropAndRet(pop_count); - } else { - __ Ret(); + __ Addu(sp, sp, pop_reg); } + __ Ret(); } void CodeGenerator::FinishCode() {} diff --git a/src/compiler/backend/mips64/code-generator-mips64.cc b/src/compiler/backend/mips64/code-generator-mips64.cc index 2091ae5e11..a9974c47be 100644 --- a/src/compiler/backend/mips64/code-generator-mips64.cc +++ b/src/compiler/backend/mips64/code-generator-mips64.cc @@ -4314,7 +4314,7 @@ void CodeGenerator::AssembleConstructFrame() { } } -void CodeGenerator::AssembleReturn(InstructionOperand* pop) { +void CodeGenerator::AssembleReturn(InstructionOperand* additional_pop_count) { auto call_descriptor = linkage()->GetIncomingDescriptor(); const int returns = frame()->GetReturnSlotCount(); @@ -4335,36 +4335,76 @@ void CodeGenerator::AssembleReturn(InstructionOperand* pop) { } MipsOperandConverter g(this, nullptr); + + const int parameter_count = + static_cast(call_descriptor->StackParameterCount()); + + // {aditional_pop_count} is only greater than zero if {parameter_count = 0}. + // Check RawMachineAssembler::PopAndReturn. + if (parameter_count != 0) { + if (additional_pop_count->IsImmediate()) { + DCHECK_EQ(g.ToConstant(additional_pop_count).ToInt32(), 0); + } else if (__ emit_debug_code()) { + __ Assert(eq, AbortReason::kUnexpectedAdditionalPopValue, + g.ToRegister(additional_pop_count), + Operand(static_cast(0))); + } + } +#ifdef V8_NO_ARGUMENTS_ADAPTOR + // Functions with JS linkage have at least one parameter (the receiver). + // If {parameter_count} == 0, it means it is a builtin with + // kDontAdaptArgumentsSentinel, which takes care of JS arguments popping + // itself. + const bool drop_jsargs = frame_access_state()->has_frame() && + call_descriptor->IsJSFunctionCall() && + parameter_count != 0; +#else + const bool drop_jsargs = false; +#endif + if (call_descriptor->IsCFunctionCall()) { AssembleDeconstructFrame(); } else if (frame_access_state()->has_frame()) { // Canonicalize JSFunction return sites for now unless they have an variable // number of stack slot pops. - if (pop->IsImmediate() && g.ToConstant(pop).ToInt32() == 0) { + if (additional_pop_count->IsImmediate() && + g.ToConstant(additional_pop_count).ToInt32() == 0) { if (return_label_.is_bound()) { __ Branch(&return_label_); return; } else { __ bind(&return_label_); - AssembleDeconstructFrame(); } - } else { - AssembleDeconstructFrame(); } + if (drop_jsargs) { + // Get the actual argument count + __ Ld(t0, MemOperand(fp, StandardFrameConstants::kArgCOffset)); + } + AssembleDeconstructFrame(); } - int pop_count = static_cast(call_descriptor->StackParameterCount()); - if (pop->IsImmediate()) { - pop_count += g.ToConstant(pop).ToInt32(); + if (drop_jsargs) { + // We must pop all arguments from the stack (including the receiver). This + // number of arguments is given by max(1 + argc_reg, parameter_count). + __ Daddu(t0, t0, Operand(1)); // Also pop the receiver. + if (parameter_count > 1) { + Label max_number; + __ Branch(&max_number, ge, t0, Operand(parameter_count)); + __ li(t0, parameter_count); + __ bind(&max_number); + } + __ dsll(t0, t0, kSystemPointerSizeLog2); + __ Daddu(sp, sp, t0); + } else if (additional_pop_count->IsImmediate()) { + DCHECK_EQ(Constant::kInt32, g.ToConstant(additional_pop_count).type()); + int additional_count = g.ToConstant(additional_pop_count).ToInt32(); + __ Drop(parameter_count + additional_count); } else { - Register pop_reg = g.ToRegister(pop); + Register pop_reg = g.ToRegister(additional_pop_count); + __ Drop(parameter_count); __ dsll(pop_reg, pop_reg, kSystemPointerSizeLog2); __ Daddu(sp, sp, pop_reg); } - if (pop_count != 0) { - __ DropAndRet(pop_count); - } else { - __ Ret(); - } + __ Ret(); } void CodeGenerator::FinishCode() {}