Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(54)

Unified Diff: src/builtins/ia32/builtins-ia32.cc

Issue 2335513004: [Interpreter] Adds stackcheck in InterpreterPushArgsAndCall/Construct builtins. (Closed)
Patch Set: Reworked ia32 PushArgs builtin and addressed comments from Ross. Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/builtins/arm64/builtins-arm64.cc ('k') | src/builtins/mips/builtins-mips.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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);
« no previous file with comments | « src/builtins/arm64/builtins-arm64.cc ('k') | src/builtins/mips/builtins-mips.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698