| Index: src/arm64/code-stubs-arm64.cc
|
| diff --git a/src/arm64/code-stubs-arm64.cc b/src/arm64/code-stubs-arm64.cc
|
| index a42f5cd34da87677a2f8fd4d59c417c82fbb4ee5..127588267d791170d0a88f56e32374f5a94026b0 100644
|
| --- a/src/arm64/code-stubs-arm64.cc
|
| +++ b/src/arm64/code-stubs-arm64.cc
|
| @@ -1477,15 +1477,78 @@ void CEntryStub::GenerateAheadOfTime(Isolate* isolate) {
|
| }
|
|
|
|
|
| -void CEntryStub::GenerateCore(MacroAssembler* masm,
|
| - Label* throw_normal,
|
| - Label* throw_termination,
|
| - bool do_gc,
|
| - bool always_allocate) {
|
| - // x0 : Result parameter for PerformGC, if do_gc is true.
|
| +void CEntryStub::Generate(MacroAssembler* masm) {
|
| + // The Abort mechanism relies on CallRuntime, which in turn relies on
|
| + // CEntryStub, so until this stub has been generated, we have to use a
|
| + // fall-back Abort mechanism.
|
| + //
|
| + // Note that this stub must be generated before any use of Abort.
|
| + MacroAssembler::NoUseRealAbortsScope no_use_real_aborts(masm);
|
| +
|
| + ASM_LOCATION("CEntryStub::Generate entry");
|
| + ProfileEntryHookStub::MaybeCallEntryHook(masm);
|
| +
|
| + // Register parameters:
|
| + // x0: argc (including receiver, untagged)
|
| + // x1: target
|
| + //
|
| + // The stack on entry holds the arguments and the receiver, with the receiver
|
| + // at the highest address:
|
| + //
|
| + // jssp]argc-1]: receiver
|
| + // jssp[argc-2]: arg[argc-2]
|
| + // ... ...
|
| + // jssp[1]: arg[1]
|
| + // jssp[0]: arg[0]
|
| + //
|
| + // The arguments are in reverse order, so that arg[argc-2] is actually the
|
| + // first argument to the target function and arg[0] is the last.
|
| + ASSERT(jssp.Is(__ StackPointer()));
|
| + const Register& argc_input = x0;
|
| + const Register& target_input = x1;
|
| +
|
| + // Calculate argv, argc and the target address, and store them in
|
| + // callee-saved registers so we can retry the call without having to reload
|
| + // these arguments.
|
| + // TODO(jbramley): If the first call attempt succeeds in the common case (as
|
| + // it should), then we might be better off putting these parameters directly
|
| + // into their argument registers, rather than using callee-saved registers and
|
| + // preserving them on the stack.
|
| + const Register& argv = x21;
|
| + const Register& argc = x22;
|
| + const Register& target = x23;
|
| +
|
| + // Derive argv from the stack pointer so that it points to the first argument
|
| + // (arg[argc-2]), or just below the receiver in case there are no arguments.
|
| + // - Adjust for the arg[] array.
|
| + Register temp_argv = x11;
|
| + __ Add(temp_argv, jssp, Operand(x0, LSL, kPointerSizeLog2));
|
| + // - Adjust for the receiver.
|
| + __ Sub(temp_argv, temp_argv, 1 * kPointerSize);
|
| +
|
| + // Enter the exit frame. Reserve three slots to preserve x21-x23 callee-saved
|
| + // registers.
|
| + FrameScope scope(masm, StackFrame::MANUAL);
|
| + __ EnterExitFrame(save_doubles_, x10, 3);
|
| + ASSERT(csp.Is(__ StackPointer()));
|
| +
|
| + // Poke callee-saved registers into reserved space.
|
| + __ Poke(argv, 1 * kPointerSize);
|
| + __ Poke(argc, 2 * kPointerSize);
|
| + __ Poke(target, 3 * kPointerSize);
|
| +
|
| + // We normally only keep tagged values in callee-saved registers, as they
|
| + // could be pushed onto the stack by called stubs and functions, and on the
|
| + // stack they can confuse the GC. However, we're only calling C functions
|
| + // which can push arbitrary data onto the stack anyway, and so the GC won't
|
| + // examine that part of the stack.
|
| + __ Mov(argc, argc_input);
|
| + __ Mov(target, target_input);
|
| + __ Mov(argv, temp_argv);
|
| +
|
| // x21 : argv
|
| // x22 : argc
|
| - // x23 : target
|
| + // x23 : call target
|
| //
|
| // The stack (on entry) holds the arguments and the receiver, with the
|
| // receiver at the highest address:
|
| @@ -1515,44 +1578,21 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
|
| //
|
| // After an unsuccessful call, the exit frame and suchlike are left
|
| // untouched, and the stub either throws an exception by jumping to one of
|
| - // the provided throw_ labels, or it falls through. The failure details are
|
| - // passed through in x0.
|
| + // the exception_returned label.
|
| +
|
| ASSERT(csp.Is(__ StackPointer()));
|
|
|
| Isolate* isolate = masm->isolate();
|
|
|
| - const Register& argv = x21;
|
| - const Register& argc = x22;
|
| - const Register& target = x23;
|
| -
|
| - if (do_gc) {
|
| - // Call Runtime::PerformGC, passing x0 (the result parameter for
|
| - // PerformGC) and x1 (the isolate).
|
| - __ Mov(x1, ExternalReference::isolate_address(masm->isolate()));
|
| - __ CallCFunction(
|
| - ExternalReference::perform_gc_function(isolate), 2, 0);
|
| - }
|
| -
|
| - ExternalReference scope_depth =
|
| - ExternalReference::heap_always_allocate_scope_depth(isolate);
|
| - if (always_allocate) {
|
| - __ Mov(x10, Operand(scope_depth));
|
| - __ Ldr(x11, MemOperand(x10));
|
| - __ Add(x11, x11, 1);
|
| - __ Str(x11, MemOperand(x10));
|
| - }
|
| -
|
| // Prepare AAPCS64 arguments to pass to the builtin.
|
| __ Mov(x0, argc);
|
| __ Mov(x1, argv);
|
| __ Mov(x2, ExternalReference::isolate_address(isolate));
|
|
|
| - // Store the return address on the stack, in the space previously allocated
|
| - // by EnterExitFrame. The return address is queried by
|
| - // ExitFrame::GetStateForFramePointer.
|
| Label return_location;
|
| __ Adr(x12, &return_location);
|
| __ Poke(x12, 0);
|
| +
|
| if (__ emit_debug_code()) {
|
| // Verify that the slot below fp[kSPOffset]-8 points to the return location
|
| // (currently in x12).
|
| @@ -1567,27 +1607,17 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
|
| // Call the builtin.
|
| __ Blr(target);
|
| __ Bind(&return_location);
|
| - const Register& result = x0;
|
| -
|
| - if (always_allocate) {
|
| - __ Mov(x10, Operand(scope_depth));
|
| - __ Ldr(x11, MemOperand(x10));
|
| - __ Sub(x11, x11, 1);
|
| - __ Str(x11, MemOperand(x10));
|
| - }
|
|
|
| // x0 result The return code from the call.
|
| // x21 argv
|
| // x22 argc
|
| // x23 target
|
| - //
|
| - // If all of the result bits matching kFailureTagMask are '1', the result is
|
| - // a failure. Otherwise, it's an ordinary tagged object and the call was a
|
| - // success.
|
| - Label failure;
|
| - __ And(x10, result, kFailureTagMask);
|
| - __ Cmp(x10, kFailureTagMask);
|
| - __ B(&failure, eq);
|
| + const Register& result = x0;
|
| +
|
| + // Check result for exception sentinel.
|
| + Label exception_returned;
|
| + __ CompareRoot(result, Heap::kExceptionRootIndex);
|
| + __ B(eq, &exception_returned);
|
|
|
| // The call succeeded, so unwind the stack and return.
|
|
|
| @@ -1612,27 +1642,15 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
|
| // hasn't changed (except for the return address).
|
| __ SetStackPointer(csp);
|
|
|
| - __ Bind(&failure);
|
| - // The call failed, so check if we need to throw an exception, and fall
|
| - // through (to retry) otherwise.
|
| -
|
| - Label retry;
|
| - // x0 result The return code from the call, including the failure
|
| - // code and details.
|
| - // x21 argv
|
| - // x22 argc
|
| - // x23 target
|
| - // Refer to the Failure class for details of the bit layout.
|
| - STATIC_ASSERT(Failure::RETRY_AFTER_GC == 0);
|
| - __ Tst(result, kFailureTypeTagMask << kFailureTagSize);
|
| - __ B(eq, &retry); // RETRY_AFTER_GC
|
| + // Handling of exception.
|
| + __ Bind(&exception_returned);
|
|
|
| // Retrieve the pending exception.
|
| + ExternalReference pending_exception_address(
|
| + Isolate::kPendingExceptionAddress, isolate);
|
| const Register& exception = result;
|
| const Register& exception_address = x11;
|
| - __ Mov(exception_address,
|
| - Operand(ExternalReference(Isolate::kPendingExceptionAddress,
|
| - isolate)));
|
| + __ Mov(exception_address, Operand(pending_exception_address));
|
| __ Ldr(exception, MemOperand(exception_address));
|
|
|
| // Clear the pending exception.
|
| @@ -1646,118 +1664,9 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
|
|
|
| // Special handling of termination exceptions, which are uncatchable by
|
| // JavaScript code.
|
| + Label throw_termination_exception;
|
| __ Cmp(exception, Operand(isolate->factory()->termination_exception()));
|
| - __ B(eq, throw_termination);
|
| -
|
| - // Handle normal exception.
|
| - __ B(throw_normal);
|
| -
|
| - __ Bind(&retry);
|
| - // The result (x0) is passed through as the next PerformGC parameter.
|
| -}
|
| -
|
| -
|
| -void CEntryStub::Generate(MacroAssembler* masm) {
|
| - // The Abort mechanism relies on CallRuntime, which in turn relies on
|
| - // CEntryStub, so until this stub has been generated, we have to use a
|
| - // fall-back Abort mechanism.
|
| - //
|
| - // Note that this stub must be generated before any use of Abort.
|
| - MacroAssembler::NoUseRealAbortsScope no_use_real_aborts(masm);
|
| -
|
| - ASM_LOCATION("CEntryStub::Generate entry");
|
| - ProfileEntryHookStub::MaybeCallEntryHook(masm);
|
| -
|
| - // Register parameters:
|
| - // x0: argc (including receiver, untagged)
|
| - // x1: target
|
| - //
|
| - // The stack on entry holds the arguments and the receiver, with the receiver
|
| - // at the highest address:
|
| - //
|
| - // jssp]argc-1]: receiver
|
| - // jssp[argc-2]: arg[argc-2]
|
| - // ... ...
|
| - // jssp[1]: arg[1]
|
| - // jssp[0]: arg[0]
|
| - //
|
| - // The arguments are in reverse order, so that arg[argc-2] is actually the
|
| - // first argument to the target function and arg[0] is the last.
|
| - ASSERT(jssp.Is(__ StackPointer()));
|
| - const Register& argc_input = x0;
|
| - const Register& target_input = x1;
|
| -
|
| - // Calculate argv, argc and the target address, and store them in
|
| - // callee-saved registers so we can retry the call without having to reload
|
| - // these arguments.
|
| - // TODO(jbramley): If the first call attempt succeeds in the common case (as
|
| - // it should), then we might be better off putting these parameters directly
|
| - // into their argument registers, rather than using callee-saved registers and
|
| - // preserving them on the stack.
|
| - const Register& argv = x21;
|
| - const Register& argc = x22;
|
| - const Register& target = x23;
|
| -
|
| - // Derive argv from the stack pointer so that it points to the first argument
|
| - // (arg[argc-2]), or just below the receiver in case there are no arguments.
|
| - // - Adjust for the arg[] array.
|
| - Register temp_argv = x11;
|
| - __ Add(temp_argv, jssp, Operand(x0, LSL, kPointerSizeLog2));
|
| - // - Adjust for the receiver.
|
| - __ Sub(temp_argv, temp_argv, 1 * kPointerSize);
|
| -
|
| - // Enter the exit frame. Reserve three slots to preserve x21-x23 callee-saved
|
| - // registers.
|
| - FrameScope scope(masm, StackFrame::MANUAL);
|
| - __ EnterExitFrame(save_doubles_, x10, 3);
|
| - ASSERT(csp.Is(__ StackPointer()));
|
| -
|
| - // Poke callee-saved registers into reserved space.
|
| - __ Poke(argv, 1 * kPointerSize);
|
| - __ Poke(argc, 2 * kPointerSize);
|
| - __ Poke(target, 3 * kPointerSize);
|
| -
|
| - // We normally only keep tagged values in callee-saved registers, as they
|
| - // could be pushed onto the stack by called stubs and functions, and on the
|
| - // stack they can confuse the GC. However, we're only calling C functions
|
| - // which can push arbitrary data onto the stack anyway, and so the GC won't
|
| - // examine that part of the stack.
|
| - __ Mov(argc, argc_input);
|
| - __ Mov(target, target_input);
|
| - __ Mov(argv, temp_argv);
|
| -
|
| - Label throw_normal;
|
| - Label throw_termination;
|
| -
|
| - // Call the runtime function.
|
| - GenerateCore(masm,
|
| - &throw_normal,
|
| - &throw_termination,
|
| - false,
|
| - false);
|
| -
|
| - // If successful, the previous GenerateCore will have returned to the
|
| - // calling code. Otherwise, we fall through into the following.
|
| -
|
| - // Do space-specific GC and retry runtime call.
|
| - GenerateCore(masm,
|
| - &throw_normal,
|
| - &throw_termination,
|
| - true,
|
| - false);
|
| -
|
| - // Do full GC and retry runtime call one final time.
|
| - __ Mov(x0, reinterpret_cast<uint64_t>(Failure::InternalError()));
|
| - GenerateCore(masm,
|
| - &throw_normal,
|
| - &throw_termination,
|
| - true,
|
| - true);
|
| -
|
| - { FrameScope scope(masm, StackFrame::MANUAL);
|
| - __ CallCFunction(
|
| - ExternalReference::out_of_memory_function(masm->isolate()), 0);
|
| - }
|
| + __ B(eq, &throw_termination_exception);
|
|
|
| // We didn't execute a return case, so the stack frame hasn't been updated
|
| // (except for the return address slot). However, we don't need to initialize
|
| @@ -1765,24 +1674,18 @@ void CEntryStub::Generate(MacroAssembler* masm) {
|
| // unwinds the stack.
|
| __ SetStackPointer(jssp);
|
|
|
| - // Throw exceptions.
|
| - // If we throw an exception, we can end up re-entering CEntryStub before we
|
| - // pop the exit frame, so need to ensure that x21-x23 contain GC-safe values
|
| - // here.
|
| -
|
| - __ Bind(&throw_termination);
|
| - ASM_LOCATION("Throw termination");
|
| + ASM_LOCATION("Throw normal");
|
| __ Mov(argv, 0);
|
| __ Mov(argc, 0);
|
| __ Mov(target, 0);
|
| - __ ThrowUncatchable(x0, x10, x11, x12, x13);
|
| + __ Throw(x0, x10, x11, x12, x13);
|
|
|
| - __ Bind(&throw_normal);
|
| - ASM_LOCATION("Throw normal");
|
| + __ Bind(&throw_termination_exception);
|
| + ASM_LOCATION("Throw termination");
|
| __ Mov(argv, 0);
|
| __ Mov(argc, 0);
|
| __ Mov(target, 0);
|
| - __ Throw(x0, x10, x11, x12, x13);
|
| + __ ThrowUncatchable(x0, x10, x11, x12, x13);
|
| }
|
|
|
|
|
| @@ -1882,7 +1785,7 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
|
| isolate)));
|
| }
|
| __ Str(code_entry, MemOperand(x10));
|
| - __ Mov(x0, Operand(reinterpret_cast<int64_t>(Failure::Exception())));
|
| + __ LoadRoot(x0, Heap::kExceptionRootIndex);
|
| __ B(&exit);
|
|
|
| // Invoke: Link this frame into the handler chain. There's only one
|
|
|