Chromium Code Reviews| Index: src/builtins/ia32/builtins-ia32.cc |
| diff --git a/src/builtins/ia32/builtins-ia32.cc b/src/builtins/ia32/builtins-ia32.cc |
| index 00edd35cdca8cffc4b5190f19b70b5fdd7ebe682..70f883082b785ba3503a7864704aca927b850db8 100644 |
| --- a/src/builtins/ia32/builtins-ia32.cc |
| +++ b/src/builtins/ia32/builtins-ia32.cc |
| @@ -703,6 +703,28 @@ void Builtins::Generate_InterpreterMarkBaselineOnReturn(MacroAssembler* masm) { |
| __ ret(0); |
| } |
| +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. |
| + ExternalReference real_stack_limit = |
| + ExternalReference::address_of_real_stack_limit(masm->isolate()); |
| + __ mov(scratch1, Operand::StaticVariable(real_stack_limit)); |
| + // Make scratch2 the space we have left. The stack might already be overflowed |
| + // here which will cause scratch2 to become negative. |
| + __ mov(scratch2, esp); |
| + __ sub(scratch2, scratch1); |
| + // Make scratch1 the space we need for the array when it is unrolled onto the |
| + // stack. |
| + __ mov(scratch1, num_args); |
| + __ shl(scratch1, kPointerSizeLog2); |
| + // Check if the arguments will overflow the stack. |
| + __ cmp(scratch2, scratch1); |
| + __ j(less_equal, stack_overflow); // Signed comparison. |
| +} |
| + |
| static void Generate_InterpreterPushArgs(MacroAssembler* masm, |
| Register array_limit, |
| Register start_address) { |
| @@ -732,18 +754,25 @@ void Builtins::Generate_InterpreterPushArgsAndCallImpl( |
| // they are to be pushed onto the stack. |
| // -- edi : the target to call (can be any Object). |
| // ----------------------------------- |
| + Label stack_overflow; |
| + // Compute the expected number of arguments. |
| + __ mov(ecx, eax); |
| + __ add(ecx, Immediate(1)); // Add one for receiver. |
| + |
| + // Add a stack check before pushing the arguments. We need an extra register |
| + // to perform a stack check. So push it onto the stack temporarily. This |
| + // might cause stack overflow, but it will be detected by the check. |
| + __ Push(edi); |
| + Generate_StackOverflowCheck(masm, ecx, edx, edi, &stack_overflow); |
| + __ Pop(edi); |
| // Pop return address to allow tail-call after pushing arguments. |
| __ Pop(edx); |
| // Find the address of the last argument. |
| - __ mov(ecx, eax); |
| - __ add(ecx, Immediate(1)); // Add one for receiver. |
| __ shl(ecx, kPointerSizeLog2); |
| __ neg(ecx); |
| __ add(ecx, ebx); |
| - |
| - // TODO(mythria): Add a stack check before pushing the arguments. |
| Generate_InterpreterPushArgs(masm, ecx, ebx); |
| // Call the target. |
| @@ -759,41 +788,45 @@ void Builtins::Generate_InterpreterPushArgsAndCallImpl( |
| tail_call_mode), |
| RelocInfo::CODE_TARGET); |
| } |
| + |
| + __ bind(&stack_overflow); |
| + { |
| + // Pop the temporary registers, so that return address is on top of stack. |
| + __ Pop(edi); |
| + |
| + __ TailCallRuntime(Runtime::kThrowStackOverflow); |
| + |
| + // This should be unreachable. |
| + __ int3(); |
| + } |
| } |
| namespace { |
| -// This function modified start_addr, and only reads the contents of num_args |
| -// register. scratch1 and scratch2 are used as temporary registers. Their |
| -// original values are restored after the use. |
| -void Generate_InterpreterPushArgsAndReturnAddress( |
| - MacroAssembler* masm, Register num_args, Register start_addr, |
| - Register scratch1, Register scratch2, bool receiver_in_args) { |
| - // Store scratch2, scratch1 onto the stack. We need to restore the original |
| - // values |
| - // so store scratch2, scratch1 temporarily on stack. |
| - __ Push(scratch2); |
| - __ Push(scratch1); |
| - |
| - // We have to pop return address and the two temporary registers before we |
| - // can push arguments onto the stack. we do not have any free registers so |
| - // update the stack and copy them into the correct places on the stack. |
| - // current stack =====> required stack layout |
| - // | | | scratch1 | (2) <-- esp(1) |
| - // | | | scratch2 | (3) |
| - // | | | return addr | (4) |
| - // | | | arg N | (5) |
| - // | scratch1 | <-- esp | .... | |
| - // | scratch2 | | arg 0 | |
| - // | return addr | | receiver slot | |
| +void Generate_InterpreterPushArgsUpdateStackPointer(MacroAssembler* masm, |
| + Register num_args, |
| + Register scratch1, |
| + Register scratch2, |
| + Label* stack_overflow) { |
| + // Check for stack overflow. We are not trying to catch interrupts (e.g. |
| + // debug break and preemption) here, so the "real stack limit" is checked. |
| + ExternalReference real_stack_limit = |
| + ExternalReference::address_of_real_stack_limit(masm->isolate()); |
| + __ mov(scratch1, Operand::StaticVariable(real_stack_limit)); |
| + // Make scratch2 the space we have left. The stack might already be overflowed |
| + // here which will cause scratch2 to become negative. |
| + __ mov(scratch2, esp); |
| + __ sub(scratch2, scratch1); |
| - // First increment the stack pointer to the correct location. |
| - // we need additional slots for arguments and the receiver. |
| - // Step 1 - compute the required increment to the stack. |
| + // Compute expected number of arguments. |
| __ mov(scratch1, num_args); |
| + __ add(scratch1, Immediate(1)); // Add one for receiver. |
| __ shl(scratch1, kPointerSizeLog2); |
| - __ add(scratch1, Immediate(kPointerSize)); |
| + __ cmp(scratch2, scratch1); |
| + __ j(less_equal, stack_overflow); // Signed comparison. |
| + |
| +// Update the stack pointer to the correct location. |
| #ifdef _MSC_VER |
| // TODO(mythria): Move it to macro assembler. |
| // In windows, we cannot increment the stack size by more than one page |
| @@ -813,56 +846,35 @@ void Generate_InterpreterPushArgsAndReturnAddress( |
| __ bind(&update_stack_pointer); |
| #endif |
| - // TODO(mythria): Add a stack check before updating the stack pointer. |
| - |
| - // Step 1 - Update the stack pointer. |
| __ sub(esp, scratch1); |
| +} |
| - // Step 2 move scratch1 to the correct location. Move scratch1 first otherwise |
| - // we may overwrite when num_args = 0 or 1, basically when the source and |
| - // destination overlap. We at least need one extra slot for receiver, |
| - // so no extra checks are required to avoid copy. |
| - __ mov(scratch1, |
| - Operand(esp, num_args, times_pointer_size, 1 * kPointerSize)); |
| - __ mov(Operand(esp, 0), scratch1); |
| - |
| - // Step 3 move scratch2 to the correct location |
| - __ mov(scratch1, |
| - Operand(esp, num_args, times_pointer_size, 2 * kPointerSize)); |
| - __ mov(Operand(esp, 1 * kPointerSize), scratch1); |
| - |
| - // Step 4 move return address to the correct location |
| - __ mov(scratch1, |
| - Operand(esp, num_args, times_pointer_size, 3 * kPointerSize)); |
| - __ mov(Operand(esp, 2 * kPointerSize), scratch1); |
| - |
| - // Step 5 copy arguments to correct locations. |
| - if (receiver_in_args) { |
| - __ mov(scratch1, num_args); |
| - __ add(scratch1, Immediate(1)); |
| - } else { |
| - // Slot meant for receiver contains return address. Reset it so that |
| - // we will not incorrectly interpret return address as an object. |
| - __ mov(Operand(esp, num_args, times_pointer_size, 3 * kPointerSize), |
| - Immediate(0)); |
| - __ mov(scratch1, num_args); |
| +void Generate_InterpreterPushArgsMoveReturnAddress(MacroAssembler* masm, |
| + Register num_args, |
| + Register scratch, |
| + int num_slots_to_move) { |
| + for (int i = 0; i < num_slots_to_move; i++) { |
| + __ mov(scratch, |
| + Operand(esp, num_args, times_pointer_size, (i + 1) * kPointerSize)); |
| + __ mov(Operand(esp, i * kPointerSize), scratch); |
| } |
| +} |
| +void Generate_InterpreterPushArgsCopyArguments(MacroAssembler* masm, |
| + Register start_addr, |
| + Register array_limit, |
| + Register dest_addr, |
| + Register scratch) { |
| Label loop_header, loop_check; |
| __ jmp(&loop_check); |
| __ bind(&loop_header); |
| - __ mov(scratch2, Operand(start_addr, 0)); |
| - __ mov(Operand(esp, scratch1, times_pointer_size, 2 * kPointerSize), |
| - scratch2); |
| + __ mov(scratch, Operand(start_addr, 0)); |
| + __ mov(Operand(dest_addr, 0), scratch); |
| __ sub(start_addr, Immediate(kPointerSize)); |
| - __ sub(scratch1, Immediate(1)); |
| + __ sub(dest_addr, Immediate(kPointerSize)); |
| __ bind(&loop_check); |
| - __ cmp(scratch1, Immediate(0)); |
| + __ cmp(start_addr, array_limit); |
| __ j(greater, &loop_header, Label::kNear); |
| - |
| - // Restore scratch1 and scratch2. |
| - __ Pop(scratch1); |
| - __ Pop(scratch2); |
| } |
| } // end anonymous namespace |
| @@ -879,11 +891,62 @@ void Builtins::Generate_InterpreterPushArgsAndConstructImpl( |
| // arguments should be consecutive above this, in the same order as |
| // they are to be pushed onto the stack. |
| // ----------------------------------- |
| + Label stack_overflow; |
| + int num_slots_above_ret_address = 3; |
| - // Push arguments and move return address to the top of stack. |
| - // The eax register is readonly. The ecx register will be modified. The edx |
| - // and edi registers will be modified but restored to their original values. |
| - Generate_InterpreterPushArgsAndReturnAddress(masm, eax, ecx, edx, edi, false); |
| + // We need three scratch registers. Push edi, edx and ebx onto stack. |
| + __ Push(edi); |
| + __ Push(edx); |
| + __ Push(ebx); |
| + |
| + // We have to pop return address and the two temporary registers before we |
| + // can push arguments onto the stack. |
| + // current stack =====> required stack layout |
| + // | | | scratch3 | (2) <-- esp(1) |
| + // | | | scratch2 | (2) |
| + // | | | scratch1 | (2) |
| + // | | | return addr | (2) |
| + // | scratch3 | <-- esp | arg N | (3) |
| + // | scratch2 | | .... | |
| + // | scratch1 | | arg 0 | |
| + // | return addr | | receiver slot | |
| + |
| + // Step1: Update the stack pointer to the correct location after verifying the |
| + // stack does not overflow. |
| + // The register eax is readonly, registers edi and edx are used as scratch. |
| + Generate_InterpreterPushArgsUpdateStackPointer(masm, eax, edi, edx, |
| + &stack_overflow); |
| + |
| + // Step2: Move return address and the two temporary registers to correct |
| + // locations.This function will move return address and all the values above |
| + // it by num_args + 1 locations. |
| + // The register eax is readonly, register edx is used as scratch. |
| + Generate_InterpreterPushArgsMoveReturnAddress( |
| + masm, eax, edx, num_slots_above_ret_address + 1); |
| + |
| + // Slot meant for receiver contains return address. Reset it so that |
| + // we will not incorrectly interpret return address as an object. |
| + __ mov(Operand(esp, eax, times_pointer_size, |
| + (num_slots_above_ret_address + 1) * kPointerSize), |
| + Immediate(0)); |
| + |
| + // Copy the arguments. |
| + // Find the address of the last argument. |
| + __ mov(edx, eax); |
| + __ shl(edx, kPointerSizeLog2); |
| + __ neg(edx); |
| + __ add(edx, ecx); |
| + |
| + // Find the destination address. |
| + __ lea(edi, Operand(esp, eax, times_pointer_size, |
| + (num_slots_above_ret_address)*kPointerSize)); |
|
rmcilroy
2016/09/13 10:13:56
space between binary ops
mythria
2016/09/13 11:02:32
Done.
|
| + |
| + Generate_InterpreterPushArgsCopyArguments(masm, ecx, edx, edi, ebx); |
| + |
| + // Restore ebx, edx and edi. |
| + __ Pop(ebx); |
| + __ Pop(edx); |
| + __ Pop(edi); |
| __ AssertUndefinedOrAllocationSite(ebx); |
| if (construct_type == CallableType::kJSFunction) { |
| @@ -901,6 +964,19 @@ void Builtins::Generate_InterpreterPushArgsAndConstructImpl( |
| // Call the constructor with unmodified eax, edi, edx values. |
| __ Jump(masm->isolate()->builtins()->Construct(), RelocInfo::CODE_TARGET); |
| } |
| + |
| + __ bind(&stack_overflow); |
| + { |
| + // Pop the temporary registers, so that return address is on top of stack. |
| + __ Pop(ebx); |
| + __ Pop(edx); |
| + __ Pop(edi); |
| + |
| + __ TailCallRuntime(Runtime::kThrowStackOverflow); |
| + |
| + // This should be unreachable. |
| + __ int3(); |
| + } |
| } |
| // static |
| @@ -914,17 +990,62 @@ void Builtins::Generate_InterpreterPushArgsAndConstructArray( |
| // arguments should be consecutive above this, in the same order as |
| // they are to be pushed onto the stack. |
| // ----------------------------------- |
| + Label stack_overflow; |
| + int num_slots_above_ret_address = 2; |
| + |
| + // We need three scratch registers. Push edx and ebx onto stack. The register |
| + // edi is already available. |
| + __ Push(edx); |
| + __ Push(ebx); |
| + |
| + // Step1: Update the stack pointer to the correct location after verifying the |
| + // stack does not overflow. |
| + // The register eax is readonly, registers edi and edx are used as scratch. |
| + Generate_InterpreterPushArgsUpdateStackPointer(masm, eax, edi, edx, |
| + &stack_overflow); |
| + |
| + // Step2: Move return address and the two temporary registers to correct |
| + // locations. This function will move return address and all the values above |
| + // it by num_args + 1 locations. |
| + // The register eax is readonly, register edx is used as scratch. |
| + Generate_InterpreterPushArgsMoveReturnAddress( |
| + masm, eax, edx, num_slots_above_ret_address + 1); |
| + |
| + // Copy the arguments. |
| + // Find the address of the last argument. |
| + __ mov(edx, eax); |
| + __ add(edx, Immediate(1)); |
| + __ shl(edx, kPointerSizeLog2); |
| + __ neg(edx); |
| + __ add(edx, ecx); |
| - // Push arguments and move return address to the top of stack. |
| - // The eax register is readonly. The ecx register will be modified. The edx |
| - // and edi registers will be modified but restored to their original values. |
| - Generate_InterpreterPushArgsAndReturnAddress(masm, eax, ecx, edx, ebx, true); |
| + // Find the destination address. |
| + __ lea(edi, Operand(esp, eax, times_pointer_size, |
| + (num_slots_above_ret_address + 1) * kPointerSize)); |
| + |
| + Generate_InterpreterPushArgsCopyArguments(masm, ecx, edx, edi, ebx); |
|
rmcilroy
2016/09/13 10:13:56
As discussed, could we merge these back into the h
mythria
2016/09/13 11:02:32
Done.
|
| + |
| + // Restore edx and edi. |
| + __ Pop(ebx); |
| + __ Pop(edx); |
| // Array constructor expects constructor in edi. It is same as edx here. |
| __ Move(edi, edx); |
| ArrayConstructorStub stub(masm->isolate()); |
| __ TailCallStub(&stub); |
| + |
| + __ bind(&stack_overflow); |
| + { |
| + // Pop the temporary registers, so that return address is on top of stack. |
| + __ Pop(ebx); |
| + __ Pop(edx); |
| + |
| + __ TailCallRuntime(Runtime::kThrowStackOverflow); |
| + |
| + // This should be unreachable. |
| + __ int3(); |
| + } |
| } |
| void Builtins::Generate_InterpreterEnterBytecodeDispatch(MacroAssembler* masm) { |
| @@ -2150,32 +2271,6 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) { |
| } |
| } |
| -static void ArgumentsAdaptorStackCheck(MacroAssembler* masm, |
| - Label* stack_overflow) { |
| - // ----------- S t a t e ------------- |
| - // -- eax : actual number of arguments |
| - // -- ebx : expected number of arguments |
| - // -- edx : new target (passed through to callee) |
| - // ----------------------------------- |
| - // 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. |
| - ExternalReference real_stack_limit = |
| - ExternalReference::address_of_real_stack_limit(masm->isolate()); |
| - __ mov(edi, Operand::StaticVariable(real_stack_limit)); |
| - // Make ecx the space we have left. The stack might already be overflowed |
| - // here which will cause ecx to become negative. |
| - __ mov(ecx, esp); |
| - __ sub(ecx, edi); |
| - // Make edi the space we need for the array when it is unrolled onto the |
| - // stack. |
| - __ mov(edi, ebx); |
| - __ shl(edi, kPointerSizeLog2); |
| - // Check if the arguments will overflow the stack. |
| - __ cmp(ecx, edi); |
| - __ j(less_equal, stack_overflow); // Signed comparison. |
| -} |
| - |
| static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) { |
| __ push(ebp); |
| __ mov(ebp, esp); |
| @@ -2922,7 +3017,9 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { |
| { // Enough parameters: Actual >= expected. |
| __ bind(&enough); |
| EnterArgumentsAdaptorFrame(masm); |
| - ArgumentsAdaptorStackCheck(masm, &stack_overflow); |
| + // edi is used as a scratch register. It should be restored from the frame |
| + // when needed. |
| + Generate_StackOverflowCheck(masm, ebx, ecx, edi, &stack_overflow); |
| // Copy receiver and all expected arguments. |
| const int offset = StandardFrameConstants::kCallerSPOffset; |
| @@ -2943,7 +3040,9 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { |
| { // Too few parameters: Actual < expected. |
| __ bind(&too_few); |
| EnterArgumentsAdaptorFrame(masm); |
| - ArgumentsAdaptorStackCheck(masm, &stack_overflow); |
| + // edi is used as a scratch register. It should be restored from the frame |
| + // when needed. |
| + Generate_StackOverflowCheck(masm, ebx, ecx, edi, &stack_overflow); |
| // Remember expected arguments in ecx. |
| __ mov(ecx, ebx); |