Commit c6b0e12e authored by Jakob Gruber's avatar Jakob Gruber Committed by Commit Bot

[nojit] Refactor CallApiCallback calling convention

This is the first (and major) step towards converting CallApiCallback
and CallApiGetter stubs into builtins.

The CallApiCallbackStub was parameterized with the number of arguments
passed on the stack. This CL converts the compile-time parameter into
an explicit runtime parameter, and removes all uses of the stub
parameter.

Drive-by: The implementation is now mostly consistent across platforms.
Drive-by: Refactor the calling convention to free up two registers
(kCallData and kHolder are now passed on the stack).

Bug: v8:7777
Change-Id: I212dccc2930de89c264a13755918c9fae7842f1f
Reviewed-on: https://chromium-review.googlesource.com/c/1354887
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#58005}
parent f40638d1
......@@ -351,20 +351,32 @@ static void CallApiFunctionAndReturn(MacroAssembler* masm,
void CallApiCallbackStub::Generate(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- r4 : call_data
// -- r2 : holder
// -- r1 : api_function_address
// -- cp : context
// -- cp : kTargetContext
// -- r1 : kApiFunctionAddress
// -- r2 : kArgc
// --
// -- sp[0] : last argument
// -- ...
// -- sp[(argc - 1) * 4] : first argument
// -- sp[argc * 4] : receiver
// -- sp[(argc + 0) * 4] : receiver
// -- sp[(argc + 1) * 4] : kHolder
// -- sp[(argc + 2) * 4] : kCallData
// -----------------------------------
Register call_data = r4;
Register holder = r2;
Register api_function_address = r1;
Register argc = r2;
Register scratch = r4;
Register index = r5; // For indexing MemOperands.
DCHECK(!AreAliased(api_function_address, argc, scratch, index));
// Stack offsets (without argc).
static constexpr int kReceiverOffset = 0;
static constexpr int kHolderOffset = kReceiverOffset + 1;
static constexpr int kCallDataOffset = kHolderOffset + 1;
// Extra stack arguments are: the receiver, kHolder, kCallData.
static constexpr int kExtraStackArgumentCount = 3;
typedef FunctionCallbackArguments FCA;
......@@ -376,60 +388,88 @@ void CallApiCallbackStub::Generate(MacroAssembler* masm) {
STATIC_ASSERT(FCA::kIsolateIndex == 1);
STATIC_ASSERT(FCA::kHolderIndex == 0);
// new target
__ PushRoot(RootIndex::kUndefinedValue);
// call data
__ push(call_data);
// Set up FunctionCallbackInfo's implicit_args on the stack as follows:
//
// Target state:
// sp[0 * kPointerSize]: kHolder
// sp[1 * kPointerSize]: kIsolate
// sp[2 * kPointerSize]: undefined (kReturnValueDefaultValue)
// sp[3 * kPointerSize]: undefined (kReturnValue)
// sp[4 * kPointerSize]: kData
// sp[5 * kPointerSize]: undefined (kNewTarget)
// Reserve space on the stack.
__ sub(sp, sp, Operand(FCA::kArgsLength * kPointerSize));
// kHolder.
__ add(index, argc, Operand(FCA::kArgsLength + kHolderOffset));
__ ldr(scratch, MemOperand(sp, index, LSL, kPointerSizeLog2));
__ str(scratch, MemOperand(sp, 0 * kPointerSize));
// kIsolate.
__ Move(scratch, ExternalReference::isolate_address(masm->isolate()));
__ str(scratch, MemOperand(sp, 1 * kPointerSize));
// kReturnValueDefaultValue, kReturnValue, and kNewTarget.
__ LoadRoot(scratch, RootIndex::kUndefinedValue);
__ str(scratch, MemOperand(sp, 2 * kPointerSize));
__ str(scratch, MemOperand(sp, 3 * kPointerSize));
__ str(scratch, MemOperand(sp, 5 * kPointerSize));
Register scratch0 = call_data;
Register scratch1 = r5;
__ LoadRoot(scratch0, RootIndex::kUndefinedValue);
// return value
__ push(scratch0);
// return value default
__ push(scratch0);
// isolate
__ Move(scratch1, ExternalReference::isolate_address(masm->isolate()));
__ push(scratch1);
// holder
__ push(holder);
// kData.
__ add(index, argc, Operand(FCA::kArgsLength + kCallDataOffset));
__ ldr(scratch, MemOperand(sp, index, LSL, kPointerSizeLog2));
__ str(scratch, MemOperand(sp, 4 * kPointerSize));
// Prepare arguments.
__ mov(scratch0, sp);
// Keep a pointer to kHolder (= implicit_args) in a scratch register.
// We use it below to set up the FunctionCallbackInfo object.
__ mov(scratch, sp);
// Allocate the v8::Arguments structure in the arguments' space since
// it's not controlled by GC.
const int kApiStackSpace = 3;
static constexpr int kApiStackSpace = 4;
static constexpr bool kDontSaveDoubles = false;
FrameScope frame_scope(masm, StackFrame::MANUAL);
__ EnterExitFrame(false, kApiStackSpace);
__ EnterExitFrame(kDontSaveDoubles, kApiStackSpace);
DCHECK(api_function_address != r0 && scratch0 != r0);
// r0 = FunctionCallbackInfo&
// Arguments is after the return address.
// FunctionCallbackInfo::implicit_args_ (points at kHolder as set up above).
// Arguments are after the return address (pushed by EnterExitFrame()).
__ str(scratch, MemOperand(sp, 1 * kPointerSize));
// FunctionCallbackInfo::values_ (points at the first varargs argument passed
// on the stack).
__ add(scratch, scratch, Operand((FCA::kArgsLength - 1) * kPointerSize));
__ add(scratch, scratch, Operand(argc, LSL, kPointerSizeLog2));
__ str(scratch, MemOperand(sp, 2 * kPointerSize));
// FunctionCallbackInfo::length_.
__ str(argc, MemOperand(sp, 3 * kPointerSize));
// We also store the number of bytes to drop from the stack after returning
// from the API function here.
__ mov(scratch,
Operand((FCA::kArgsLength + kExtraStackArgumentCount) * kPointerSize));
__ add(scratch, scratch, Operand(argc, LSL, kPointerSizeLog2));
__ str(scratch, MemOperand(sp, 4 * kPointerSize));
// v8::InvocationCallback's argument.
__ add(r0, sp, Operand(1 * kPointerSize));
// FunctionCallbackInfo::implicit_args_
__ str(scratch0, MemOperand(r0, 0 * kPointerSize));
// FunctionCallbackInfo::values_
__ add(scratch1, scratch0,
Operand((FCA::kArgsLength - 1 + argc()) * kPointerSize));
__ str(scratch1, MemOperand(r0, 1 * kPointerSize));
// FunctionCallbackInfo::length_ = argc
__ mov(scratch0, Operand(argc()));
__ str(scratch0, MemOperand(r0, 2 * kPointerSize));
ExternalReference thunk_ref = ExternalReference::invoke_function_callback();
// There are two stack slots above the arguments we constructed on the stack.
// TODO(jgruber): Document what these arguments are.
static constexpr int kStackSlotsAboveFCA = 2;
MemOperand return_value_operand(
fp, (kStackSlotsAboveFCA + FCA::kReturnValueOffset) * kPointerSize);
static constexpr int kUseStackSpaceOperand = 0;
MemOperand stack_space_operand(sp, 4 * kPointerSize);
AllowExternalCallThatCantCauseGC scope(masm);
// Stores return the first js argument
int return_value_offset = 2 + FCA::kReturnValueOffset;
MemOperand return_value_operand(fp, return_value_offset * kPointerSize);
const int stack_space = argc() + FCA::kArgsLength + 1;
MemOperand* stack_space_operand = nullptr;
CallApiFunctionAndReturn(masm, api_function_address, thunk_ref, stack_space,
stack_space_operand, return_value_operand);
CallApiFunctionAndReturn(masm, api_function_address, thunk_ref,
kUseStackSpaceOperand, &stack_space_operand,
return_value_operand);
}
......@@ -490,8 +530,10 @@ void CallApiGetterStub::Generate(MacroAssembler* masm) {
// +3 is to skip prolog, return address and name handle.
MemOperand return_value_operand(
fp, (PropertyCallbackArguments::kReturnValueOffset + 3) * kPointerSize);
MemOperand* const kUseStackSpaceConstant = nullptr;
CallApiFunctionAndReturn(masm, api_function_address, thunk_ref,
kStackUnwindSpace, nullptr, return_value_operand);
kStackUnwindSpace, kUseStackSpaceConstant,
return_value_operand);
}
#undef __
......
......@@ -202,10 +202,9 @@ void ArgumentsAdaptorDescriptor::InitializePlatformSpecific(
void ApiCallbackDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
JavaScriptFrame::context_register(), // callee context
r4, // call_data
r2, // holder
r1, // api_function_address
JavaScriptFrame::context_register(), // kTargetContext
r1, // kApiFunctionAddress
r2, // kArgc
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
......
......@@ -232,8 +232,7 @@ void DirectCEntryStub::GenerateCall(MacroAssembler* masm,
// The number of register that CallApiFunctionAndReturn will need to save on
// the stack. The space for these registers need to be allocated in the
// ExitFrame before calling CallApiFunctionAndReturn.
static const int kCallApiFunctionSpillSpace = 4;
static constexpr int kCallApiFunctionSpillSpace = 4;
static int AddressOffset(ExternalReference ref0, ExternalReference ref1) {
return static_cast<int>(ref0.address() - ref1.address());
......@@ -248,7 +247,9 @@ static int AddressOffset(ExternalReference ref0, ExternalReference ref1) {
static void CallApiFunctionAndReturn(MacroAssembler* masm,
Register function_address,
ExternalReference thunk_ref,
int stack_space, int spill_offset,
int stack_space,
MemOperand* stack_space_operand,
int spill_offset,
MemOperand return_value_operand) {
ASM_LOCATION("CallApiFunctionAndReturn");
Isolate* isolate = masm->isolate();
......@@ -349,6 +350,12 @@ static void CallApiFunctionAndReturn(MacroAssembler* masm,
__ Peek(x21, (spill_offset + 2) * kXRegSize);
__ Peek(x22, (spill_offset + 3) * kXRegSize);
if (stack_space_operand != nullptr) {
CHECK_EQ(stack_space, 0);
// Load the number of stack slots to drop before LeaveExitFrame modifies sp.
__ Ldr(x19, *stack_space_operand);
}
__ LeaveExitFrame(false, x1, x5);
// Check if the function scheduled an exception.
......@@ -356,7 +363,14 @@ static void CallApiFunctionAndReturn(MacroAssembler* masm,
__ Ldr(x5, MemOperand(x5));
__ JumpIfNotRoot(x5, RootIndex::kTheHoleValue, &promote_scheduled_exception);
__ DropSlots(stack_space);
if (stack_space_operand == nullptr) {
CHECK_NE(stack_space, 0);
__ DropSlots(stack_space);
} else {
CHECK_EQ(stack_space, 0);
__ DropArguments(x19);
}
__ Ret();
// Re-throw by promoting a scheduled exception.
......@@ -377,20 +391,32 @@ static void CallApiFunctionAndReturn(MacroAssembler* masm,
void CallApiCallbackStub::Generate(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- x4 : call_data
// -- x2 : holder
// -- x1 : api_function_address
// -- cp : context
// -- cp : kTargetContext
// -- r1 : kApiFunctionAddress
// -- r2 : kArgc
// --
// -- sp[0] : last argument
// -- ...
// -- sp[(argc - 1) * 8] : first argument
// -- sp[argc * 8] : receiver
// -- sp[(argc + 0) * 8] : receiver
// -- sp[(argc + 1) * 8] : kHolder
// -- sp[(argc + 2) * 8] : kCallData
// -----------------------------------
Register call_data = x4;
Register holder = x2;
Register api_function_address = x1;
Register argc = x2;
Register scratch = x4;
Register index = x5; // For indexing MemOperands.
DCHECK(!AreAliased(api_function_address, argc, scratch, index));
// Stack offsets (without argc).
static constexpr int kReceiverOffset = 0;
static constexpr int kHolderOffset = kReceiverOffset + 1;
static constexpr int kCallDataOffset = kHolderOffset + 1;
// Extra stack arguments are: the receiver, kHolder, kCallData.
static constexpr int kExtraStackArgumentCount = 3;
typedef FunctionCallbackArguments FCA;
......@@ -402,61 +428,97 @@ void CallApiCallbackStub::Generate(MacroAssembler* masm) {
STATIC_ASSERT(FCA::kIsolateIndex == 1);
STATIC_ASSERT(FCA::kHolderIndex == 0);
Register undef = x7;
__ LoadRoot(undef, RootIndex::kUndefinedValue);
// Push new target, call data.
__ Push(undef, call_data);
Register isolate_reg = x5;
__ Mov(isolate_reg, ExternalReference::isolate_address(masm->isolate()));
// FunctionCallbackArguments:
// return value, return value default, isolate, holder.
__ Push(undef, undef, isolate_reg, holder);
// Prepare arguments.
Register args = x6;
__ Mov(args, sp);
// Set up FunctionCallbackInfo's implicit_args on the stack as follows:
//
// Target state:
// sp[0 * kPointerSize]: kHolder
// sp[1 * kPointerSize]: kIsolate
// sp[2 * kPointerSize]: undefined (kReturnValueDefaultValue)
// sp[3 * kPointerSize]: undefined (kReturnValue)
// sp[4 * kPointerSize]: kData
// sp[5 * kPointerSize]: undefined (kNewTarget)
// Reserve space on the stack.
__ Sub(sp, sp, Operand(FCA::kArgsLength * kPointerSize));
// kHolder.
__ Add(index, argc, Operand(FCA::kArgsLength + kHolderOffset));
__ Ldr(scratch, MemOperand(sp, index, LSL, kPointerSizeLog2));
__ Str(scratch, MemOperand(sp, 0 * kPointerSize));
// kIsolate.
__ Mov(scratch, ExternalReference::isolate_address(masm->isolate()));
__ Str(scratch, MemOperand(sp, 1 * kPointerSize));
// kReturnValueDefaultValue, kReturnValue, and kNewTarget.
__ LoadRoot(scratch, RootIndex::kUndefinedValue);
__ Str(scratch, MemOperand(sp, 2 * kPointerSize));
__ Str(scratch, MemOperand(sp, 3 * kPointerSize));
__ Str(scratch, MemOperand(sp, 5 * kPointerSize));
// kData.
__ Add(index, argc, Operand(FCA::kArgsLength + kCallDataOffset));
__ Ldr(scratch, MemOperand(sp, index, LSL, kPointerSizeLog2));
__ Str(scratch, MemOperand(sp, 4 * kPointerSize));
// Keep a pointer to kHolder (= implicit_args) in a scratch register.
// We use it below to set up the FunctionCallbackInfo object.
__ Mov(scratch, sp);
// Allocate the v8::Arguments structure in the arguments' space, since it's
// not controlled by GC.
const int kApiStackSpace = 3;
// Allocate space so that CallApiFunctionAndReturn can store some scratch
// registers on the stack.
const int kCallApiFunctionSpillSpace = 4;
static constexpr int kApiStackSpace = 4;
static constexpr bool kDontSaveDoubles = false;
FrameScope frame_scope(masm, StackFrame::MANUAL);
__ EnterExitFrame(false, x10, kApiStackSpace + kCallApiFunctionSpillSpace);
__ EnterExitFrame(kDontSaveDoubles, x10,
kApiStackSpace + kCallApiFunctionSpillSpace);
// FunctionCallbackInfo::implicit_args_ (points at kHolder as set up above).
// Arguments are after the return address (pushed by EnterExitFrame()).
__ Str(scratch, MemOperand(sp, 1 * kPointerSize));
// FunctionCallbackInfo::values_ (points at the first varargs argument passed
// on the stack).
__ Add(scratch, scratch, Operand((FCA::kArgsLength - 1) * kPointerSize));
__ Add(scratch, scratch, Operand(argc, LSL, kPointerSizeLog2));
__ Str(scratch, MemOperand(sp, 2 * kPointerSize));
// FunctionCallbackInfo::length_.
__ Str(argc, MemOperand(sp, 3 * kPointerSize));
// We also store the number of slots to drop from the stack after returning
// from the API function here.
// Note: Unlike on other architectures, this stores the number of slots to
// drop, not the number of bytes. arm64 must always drop a slot count that is
// a multiple of two, and related helper functions (DropArguments) expect a
// register containing the slot count.
__ Add(scratch, argc, Operand(FCA::kArgsLength + kExtraStackArgumentCount));
__ Str(scratch, MemOperand(sp, 4 * kPointerSize));
// v8::InvocationCallback's argument.
DCHECK(!AreAliased(x0, api_function_address));
// x0 = FunctionCallbackInfo&
// Arguments is after the return address.
__ SlotAddress(x0, 1);
// FunctionCallbackInfo::implicit_args_ and FunctionCallbackInfo::values_
__ Add(x10, args, Operand((FCA::kArgsLength - 1 + argc()) * kPointerSize));
__ Stp(args, x10, MemOperand(x0, 0 * kPointerSize));
// FunctionCallbackInfo::length_ = argc
__ Mov(x10, argc());
__ Str(x10, MemOperand(x0, 2 * kPointerSize));
__ add(x0, sp, Operand(1 * kPointerSize));
ExternalReference thunk_ref = ExternalReference::invoke_function_callback();
AllowExternalCallThatCantCauseGC scope(masm);
// Stores return the first js argument
int return_value_offset = 2 + FCA::kReturnValueOffset;
MemOperand return_value_operand(fp, return_value_offset * kPointerSize);
// The number of arguments might be odd, but will be padded when calling the
// stub. We do not round up stack_space to account for odd argc here, this
// will be done in CallApiFunctionAndReturn.
const int stack_space = (argc() + 1) + FCA::kArgsLength;
// The current frame needs to be aligned.
DCHECK_EQ((stack_space - (argc() + 1)) % 2, 0);
const int spill_offset = 1 + kApiStackSpace;
CallApiFunctionAndReturn(masm, api_function_address, thunk_ref, stack_space,
spill_offset, return_value_operand);
DCHECK_EQ(FCA::kArgsLength % 2, 0);
// There are two stack slots above the arguments we constructed on the stack.
// TODO(jgruber): Document what these arguments are.
static constexpr int kStackSlotsAboveFCA = 2;
MemOperand return_value_operand(
fp, (kStackSlotsAboveFCA + FCA::kReturnValueOffset) * kPointerSize);
static constexpr int kSpillOffset = 1 + kApiStackSpace;
static constexpr int kUseStackSpaceOperand = 0;
MemOperand stack_space_operand(sp, 4 * kPointerSize);
AllowExternalCallThatCantCauseGC scope(masm);
CallApiFunctionAndReturn(masm, api_function_address, thunk_ref,
kUseStackSpaceOperand, &stack_space_operand,
kSpillOffset, return_value_operand);
}
......@@ -504,10 +566,6 @@ void CallApiGetterStub::Generate(MacroAssembler* masm) {
const int kApiStackSpace = 1;
// Allocate space so that CallApiFunctionAndReturn can store some scratch
// registers on the stack.
const int kCallApiFunctionSpillSpace = 4;
FrameScope frame_scope(masm, StackFrame::MANUAL);
__ EnterExitFrame(false, x10, kApiStackSpace + kCallApiFunctionSpillSpace);
......@@ -530,9 +588,10 @@ void CallApiGetterStub::Generate(MacroAssembler* masm) {
// +3 is to skip prolog, return address and name handle.
MemOperand return_value_operand(
fp, (PropertyCallbackArguments::kReturnValueOffset + 3) * kPointerSize);
MemOperand* const kUseStackSpaceConstant = nullptr;
CallApiFunctionAndReturn(masm, api_function_address, thunk_ref,
kStackUnwindSpace, spill_offset,
return_value_operand);
kStackUnwindSpace, kUseStackSpaceConstant,
spill_offset, return_value_operand);
}
#undef __
......
......@@ -206,10 +206,9 @@ void ArgumentsAdaptorDescriptor::InitializePlatformSpecific(
void ApiCallbackDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
JavaScriptFrame::context_register(), // callee context
x4, // call_data
x2, // holder
x1, // api_function_address
JavaScriptFrame::context_register(), // kTargetContext
x1, // kApiFunctionAddress
x2, // kArgc
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
......
......@@ -2868,9 +2868,6 @@ Reduction JSCallReducer::ReduceCallApiFunction(
Handle<FunctionTemplateInfo> function_template_info(
FunctionTemplateInfo::cast(shared.object()->function_data()), isolate());
// CallApiCallbackStub expects the target in a register, so we count it out,
// and counts the receiver as an implicit argument, so we count the receiver
// out too.
if (argc > CallApiCallbackStub::kArgMax) return NoChange();
// Infer the {receiver} maps, and check if we can inline the API function
......@@ -2945,13 +2942,14 @@ Reduction JSCallReducer::ReduceCallApiFunction(
node->InsertInput(graph()->zone(), 0,
jsgraph()->HeapConstant(call_api_callback.code()));
node->ReplaceInput(1, context);
node->InsertInput(graph()->zone(), 2, jsgraph()->Constant(data));
node->InsertInput(graph()->zone(), 3, holder);
node->InsertInput(graph()->zone(), 4,
node->InsertInput(graph()->zone(), 2,
jsgraph()->ExternalConstant(function_reference));
node->ReplaceInput(5, receiver);
node->RemoveInput(6 + argc); // Remove context input.
node->ReplaceInput(7 + argc, effect); // Update effect input.
node->InsertInput(graph()->zone(), 3, jsgraph()->Constant(argc));
node->InsertInput(graph()->zone(), 4, jsgraph()->Constant(data));
node->InsertInput(graph()->zone(), 5, holder);
node->ReplaceInput(6, receiver);
node->RemoveInput(7 + argc); // Remove context input.
node->ReplaceInput(8 + argc, effect); // Update effect input.
NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
return Changed(node);
}
......
......@@ -2038,16 +2038,17 @@ Node* JSNativeContextSpecialization::InlineApiCall(
// Add CallApiCallbackStub's register argument as well.
Node* context = jsgraph()->Constant(native_context());
Node* inputs[10] = {code, context, data, holder, function_reference,
receiver};
int index = 6 + argc;
Node* inputs[11] = {
code, context, function_reference, jsgraph()->Constant(argc), data,
holder, receiver};
int index = 7 + argc;
inputs[index++] = frame_state;
inputs[index++] = *effect;
inputs[index++] = *control;
// This needs to stay here because of the edge case described in
// http://crbug.com/675648.
if (value != nullptr) {
inputs[6] = value;
inputs[7] = value;
}
return *effect = *control =
......
......@@ -294,22 +294,32 @@ static void CallApiFunctionAndReturn(MacroAssembler* masm,
void CallApiCallbackStub::Generate(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : call_data
// -- ecx : holder
// -- edx : api_function_address
// -- esi : context
// -- esi : kTargetContext
// -- edx : kApiFunctionAddress
// -- ecx : kArgc
// --
// -- esp[0] : return address
// -- esp[4] : last argument
// -- ...
// -- esp[argc * 4] : first argument
// -- esp[(argc + 1) * 4] : receiver
// -- esp[(argc + 2) * 4] : kHolder
// -- esp[(argc + 3) * 4] : kCallData
// -----------------------------------
Register call_data = eax;
Register holder = ecx;
Register api_function_address = edx;
Register return_address = edi;
Register argc = ecx;
Register scratch = eax;
DCHECK(!AreAliased(api_function_address, argc, scratch));
// Stack offsets (without argc).
static constexpr int kReceiverOffset = kPointerSize;
static constexpr int kHolderOffset = kReceiverOffset + kPointerSize;
static constexpr int kCallDataOffset = kHolderOffset + kPointerSize;
// Extra stack arguments are: the receiver, kHolder, kCallData.
static constexpr int kExtraStackArgumentCount = 3;
typedef FunctionCallbackArguments FCA;
......@@ -321,64 +331,100 @@ void CallApiCallbackStub::Generate(MacroAssembler* masm) {
STATIC_ASSERT(FCA::kIsolateIndex == 1);
STATIC_ASSERT(FCA::kHolderIndex == 0);
__ pop(return_address);
// new target
__ PushRoot(RootIndex::kUndefinedValue);
// call data
__ push(call_data);
// return value
__ PushRoot(RootIndex::kUndefinedValue);
// return value default
__ PushRoot(RootIndex::kUndefinedValue);
// isolate
__ Push(Immediate(ExternalReference::isolate_address(isolate())));
// holder
__ push(holder);
Register scratch = call_data;
__ mov(scratch, esp);
// push return address
__ push(return_address);
// API function gets reference to the v8::Arguments. If CPU profiler
// is enabled wrapper function will be called and we need to pass
// address of the callback as additional parameter, always allocate
// Set up FunctionCallbackInfo's implicit_args on the stack as follows:
//
// Current state:
// esp[0]: return address
//
// Target state:
// esp[0 * kPointerSize]: return address
// esp[1 * kPointerSize]: kHolder
// esp[2 * kPointerSize]: kIsolate
// esp[3 * kPointerSize]: undefined (kReturnValueDefaultValue)
// esp[4 * kPointerSize]: undefined (kReturnValue)
// esp[5 * kPointerSize]: kData
// esp[6 * kPointerSize]: undefined (kNewTarget)
// Reserve space on the stack.
__ sub(esp, Immediate(FCA::kArgsLength * kPointerSize));
// Return address (the old stack location is overwritten later on).
__ mov(scratch, Operand(esp, FCA::kArgsLength * kPointerSize));
__ mov(Operand(esp, 0 * kPointerSize), scratch);
// kHolder.
__ mov(scratch, Operand(esp, argc, times_pointer_size,
FCA::kArgsLength * kPointerSize + kHolderOffset));
__ mov(Operand(esp, 1 * kPointerSize), scratch);
// kIsolate.
__ Move(scratch,
Immediate(ExternalReference::isolate_address(masm->isolate())));
__ mov(Operand(esp, 2 * kPointerSize), scratch);
// kReturnValueDefaultValue, kReturnValue, and kNewTarget.
__ LoadRoot(scratch, RootIndex::kUndefinedValue);
__ mov(Operand(esp, 3 * kPointerSize), scratch);
__ mov(Operand(esp, 4 * kPointerSize), scratch);
__ mov(Operand(esp, 6 * kPointerSize), scratch);
// kData.
__ mov(scratch, Operand(esp, argc, times_pointer_size,
FCA::kArgsLength * kPointerSize + kCallDataOffset));
__ mov(Operand(esp, 5 * kPointerSize), scratch);
// Keep a pointer to kHolder (= implicit_args) in a scratch register.
// We use it below to set up the FunctionCallbackInfo object.
__ lea(scratch, Operand(esp, 1 * kPointerSize));
// The API function takes a reference to v8::Arguments. If the CPU profiler
// is enabled, a wrapper function will be called and we need to pass
// the address of the callback as an additional parameter. Always allocate
// space for it.
const int kApiArgc = 1 + 1;
static constexpr int kApiArgc = 1 + 1;
// Allocate the v8::Arguments structure in the arguments' space since
// it's not controlled by GC.
const int kApiStackSpace = 3;
static constexpr int kApiStackSpace = 4;
PrepareCallApiFunction(masm, kApiArgc + kApiStackSpace, edi);
// FunctionCallbackInfo::implicit_args_.
__ mov(ApiParameterOperand(2), scratch);
__ add(scratch, Immediate((argc() + FCA::kArgsLength - 1) * kPointerSize));
// FunctionCallbackInfo::values_.
__ mov(ApiParameterOperand(3), scratch);
// FunctionCallbackInfo::implicit_args_ (points at kHolder as set up above).
__ mov(ApiParameterOperand(kApiArgc + 0), scratch);
// FunctionCallbackInfo::values_ (points at the first varargs argument passed
// on the stack).
__ lea(scratch, Operand(scratch, argc, times_pointer_size,
(FCA::kArgsLength - 1) * kPointerSize));
__ mov(ApiParameterOperand(kApiArgc + 1), scratch);
// FunctionCallbackInfo::length_.
__ Move(ApiParameterOperand(4), Immediate(argc()));
__ mov(ApiParameterOperand(kApiArgc + 2), argc);
// We also store the number of bytes to drop from the stack after returning
// from the API function here.
__ lea(scratch,
Operand(argc, times_pointer_size,
(FCA::kArgsLength + kExtraStackArgumentCount) * kPointerSize));
__ mov(ApiParameterOperand(kApiArgc + 3), scratch);
// v8::InvocationCallback's argument.
__ lea(scratch, ApiParameterOperand(2));
__ lea(scratch, ApiParameterOperand(kApiArgc + 0));
__ mov(ApiParameterOperand(0), scratch);
ExternalReference thunk_ref = ExternalReference::invoke_function_callback();
// Stores return the first js argument
int return_value_offset = 2 + FCA::kReturnValueOffset;
Operand return_value_operand(ebp, return_value_offset * kPointerSize);
const int stack_space = argc() + FCA::kArgsLength + 1;
Operand* stack_space_operand = nullptr;
// There are two stack slots above the arguments we constructed on the stack:
// the stored ebp (pushed by EnterApiExitFrame), and the return address.
static constexpr int kStackSlotsAboveFCA = 2;
Operand return_value_operand(
ebp, (kStackSlotsAboveFCA + FCA::kReturnValueOffset) * kPointerSize);
static constexpr int kUseStackSpaceOperand = 0;
Operand stack_space_operand = ApiParameterOperand(kApiArgc + 3);
CallApiFunctionAndReturn(masm, api_function_address, thunk_ref,
ApiParameterOperand(1), stack_space,
stack_space_operand, return_value_operand);
ApiParameterOperand(1), kUseStackSpaceOperand,
&stack_space_operand, return_value_operand);
}
......@@ -449,8 +495,10 @@ void CallApiGetterStub::Generate(MacroAssembler* masm) {
// +3 is to skip prolog, return address and name handle.
Operand return_value_operand(
ebp, (PropertyCallbackArguments::kReturnValueOffset + 3) * kPointerSize);
Operand* const kUseStackSpaceConstant = nullptr;
CallApiFunctionAndReturn(masm, function_address, thunk_ref, thunk_last_arg,
kStackUnwindSpace, nullptr, return_value_operand);
kStackUnwindSpace, kUseStackSpaceConstant,
return_value_operand);
}
#undef __
......
......@@ -205,10 +205,9 @@ void ArgumentsAdaptorDescriptor::InitializePlatformSpecific(
void ApiCallbackDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
JavaScriptFrame::context_register(), // callee context
eax, // call_data
ecx, // holder
edx, // api_function_address
JavaScriptFrame::context_register(), // kTargetContext
edx, // kApiFunctionAddress
ecx, // kArgc
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
......
......@@ -273,8 +273,9 @@ void AccessorAssembler::HandleLoadAccessor(
BIND(&load);
Callable callable = CodeFactory::CallApiCallback(isolate(), 0);
exit_point->Return(CallStub(callable, nullptr, context, data,
api_holder.value(), callback, p->receiver));
TNode<IntPtrT> argc = IntPtrConstant(0);
exit_point->Return(CallStub(callable, nullptr, context, callback, argc,
data, api_holder.value(), p->receiver));
}
BIND(&runtime);
......@@ -1415,8 +1416,9 @@ void AccessorAssembler::HandleStoreICProtoHandler(
BIND(&store);
Callable callable = CodeFactory::CallApiCallback(isolate(), 1);
Return(CallStub(callable, nullptr, context, data, api_holder.value(),
callback, p->receiver, p->value));
TNode<IntPtrT> argc = IntPtrConstant(1);
Return(CallStub(callable, nullptr, context, callback, argc, data,
api_holder.value(), p->receiver, p->value));
}
BIND(&if_store_global_proxy);
......
......@@ -949,15 +949,18 @@ class CEntry1ArgvOnStackDescriptor : public CallInterfaceDescriptor {
class ApiCallbackDescriptor : public CallInterfaceDescriptor {
public:
// TODO(jgruber): This could be simplified to pass call data on the stack
// since this is what the CallApiCallbackStub anyways. This would free a
// register.
DEFINE_PARAMETERS_NO_CONTEXT(kTargetContext, kCallData, kHolder,
kApiFunctionAddress)
DEFINE_PARAMETERS_NO_CONTEXT(kTargetContext, // register argument
kApiFunctionAddress, // register argument
kArgc, // register argument
kCallData, // stack argument 1
kHolder) // stack argument 2
// receiver is implicit stack argument 3
// argv are implicit stack arguments [4, 4 + kArgc[
DEFINE_PARAMETER_TYPES(MachineType::AnyTagged(), // kTargetContext
MachineType::Pointer(), // kApiFunctionAddress
MachineType::IntPtr(), // kArgc
MachineType::AnyTagged(), // kCallData
MachineType::AnyTagged(), // kHolder
MachineType::Pointer()) // kApiFunctionAddress
MachineType::AnyTagged()) // kHolder
DECLARE_DESCRIPTOR(ApiCallbackDescriptor, CallInterfaceDescriptor)
};
......
......@@ -237,7 +237,7 @@ static void CallApiFunctionAndReturn(MacroAssembler* masm,
Register function_address,
ExternalReference thunk_ref,
int stack_space,
int32_t stack_space_offset,
MemOperand* stack_space_operand,
MemOperand return_value_operand) {
Isolate* isolate = masm->isolate();
ExternalReference next_address =
......@@ -321,15 +321,22 @@ static void CallApiFunctionAndReturn(MacroAssembler* masm,
// Leave the API exit frame.
__ bind(&leave_exit_frame);
if (stack_space_offset != kInvalidStackOffset) {
// ExitFrame contains four MIPS argument slots after DirectCEntryStub call
// so this must be accounted for.
__ lw(s0, MemOperand(sp, stack_space_offset + kCArgsSlotsSize));
} else {
if (stack_space_operand == nullptr) {
CHECK_NE(stack_space, 0);
__ li(s0, Operand(stack_space));
} else {
CHECK_EQ(stack_space, 0);
// The ExitFrame contains four MIPS argument slots after the
// DirectCEntryStub call so this must be accounted for.
// TODO(jgruber): Remove once the DirectCEntryStub is gone.
__ Drop(kCArgSlotCount);
__ lw(s0, *stack_space_operand);
}
__ LeaveExitFrame(false, s0, NO_EMIT_RETURN,
stack_space_offset != kInvalidStackOffset);
static constexpr bool kDontSaveDoubles = false;
static constexpr bool kRegisterContainsSlotCount = false;
__ LeaveExitFrame(kDontSaveDoubles, s0, NO_EMIT_RETURN,
kRegisterContainsSlotCount);
// Check if the function scheduled an exception.
__ LoadRoot(t0, RootIndex::kTheHoleValue);
......@@ -357,20 +364,32 @@ static void CallApiFunctionAndReturn(MacroAssembler* masm,
void CallApiCallbackStub::Generate(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- t0 : call_data
// -- a2 : holder
// -- a1 : api_function_address
// -- cp : context
// -- cp : kTargetContext
// -- a1 : kApiFunctionAddress
// -- a2 : kArgc
// --
// -- sp[0] : last argument
// -- ...
// -- sp[(argc - 1)* 4] : first argument
// -- sp[argc * 4] : receiver
// -- sp[(argc - 1) * 4] : first argument
// -- sp[(argc + 0) * 4] : receiver
// -- sp[(argc + 1) * 4] : kHolder
// -- sp[(argc + 2) * 4] : kCallData
// -----------------------------------
Register call_data = t0;
Register holder = a2;
Register api_function_address = a1;
Register argc = a2;
Register scratch = t0;
Register base = t1; // For addressing MemOperands on the stack.
DCHECK(!AreAliased(api_function_address, argc, scratch, base));
// Stack offsets (without argc).
static constexpr int kReceiverOffset = 0 * kPointerSize;
static constexpr int kHolderOffset = kReceiverOffset + kPointerSize;
static constexpr int kCallDataOffset = kHolderOffset + kPointerSize;
// Extra stack arguments are: the receiver, kHolder, kCallData.
static constexpr int kExtraStackArgumentCount = 3;
typedef FunctionCallbackArguments FCA;
......@@ -382,55 +401,90 @@ void CallApiCallbackStub::Generate(MacroAssembler* masm) {
STATIC_ASSERT(FCA::kIsolateIndex == 1);
STATIC_ASSERT(FCA::kHolderIndex == 0);
// new target
__ PushRoot(RootIndex::kUndefinedValue);
// Set up FunctionCallbackInfo's implicit_args on the stack as follows:
//
// Target state:
// sp[0 * kPointerSize]: kHolder
// sp[1 * kPointerSize]: kIsolate
// sp[2 * kPointerSize]: undefined (kReturnValueDefaultValue)
// sp[3 * kPointerSize]: undefined (kReturnValue)
// sp[4 * kPointerSize]: kData
// sp[5 * kPointerSize]: undefined (kNewTarget)
// Set up the base register for addressing through MemOperands. It will point
// at the receiver (located at sp + argc * kPointerSize).
__ Lsa(base, sp, argc, kPointerSizeLog2);
// Reserve space on the stack.
__ Subu(sp, sp, Operand(FCA::kArgsLength * kPointerSize));
// kHolder.
__ lw(scratch, MemOperand(base, kHolderOffset));
__ sw(scratch, MemOperand(sp, 0 * kPointerSize));
// call data.
__ Push(call_data);
// kIsolate.
__ li(scratch, ExternalReference::isolate_address(masm->isolate()));
__ sw(scratch, MemOperand(sp, 1 * kPointerSize));
Register scratch = call_data;
// kReturnValueDefaultValue, kReturnValue, and kNewTarget.
__ LoadRoot(scratch, RootIndex::kUndefinedValue);
// Push return value and default return value.
__ Push(scratch, scratch);
__ li(scratch, ExternalReference::isolate_address(masm->isolate()));
// Push isolate and holder.
__ Push(scratch, holder);
__ sw(scratch, MemOperand(sp, 2 * kPointerSize));
__ sw(scratch, MemOperand(sp, 3 * kPointerSize));
__ sw(scratch, MemOperand(sp, 5 * kPointerSize));
// kData.
__ lw(scratch, MemOperand(base, kCallDataOffset));
__ sw(scratch, MemOperand(sp, 4 * kPointerSize));
// Prepare arguments.
// Keep a pointer to kHolder (= implicit_args) in a scratch register.
// We use it below to set up the FunctionCallbackInfo object.
__ mov(scratch, sp);
// Allocate the v8::Arguments structure in the arguments' space since
// it's not controlled by GC.
const int kApiStackSpace = 3;
static constexpr int kApiStackSpace = 4;
static constexpr bool kDontSaveDoubles = false;
FrameScope frame_scope(masm, StackFrame::MANUAL);
__ EnterExitFrame(false, kApiStackSpace);
__ EnterExitFrame(kDontSaveDoubles, kApiStackSpace);
// FunctionCallbackInfo::implicit_args_ (points at kHolder as set up above).
// Arguments are after the return address (pushed by EnterExitFrame()).
__ sw(scratch, MemOperand(sp, 1 * kPointerSize));
// FunctionCallbackInfo::values_ (points at the first varargs argument passed
// on the stack).
__ Subu(scratch, base, Operand(1 * kPointerSize));
__ sw(scratch, MemOperand(sp, 2 * kPointerSize));
// FunctionCallbackInfo::length_.
__ sw(argc, MemOperand(sp, 3 * kPointerSize));
// We also store the number of bytes to drop from the stack after returning
// from the API function here.
// Note: Unlike on other architectures, this stores the number of slots to
// drop, not the number of bytes.
__ Addu(scratch, argc, Operand(FCA::kArgsLength + kExtraStackArgumentCount));
__ sw(scratch, MemOperand(sp, 4 * kPointerSize));
DCHECK(api_function_address != a0 && scratch != a0);
// a0 = FunctionCallbackInfo&
// Arguments is after the return address.
// v8::InvocationCallback's argument.
DCHECK(!AreAliased(api_function_address, scratch, a0));
__ Addu(a0, sp, Operand(1 * kPointerSize));
// FunctionCallbackInfo::implicit_args_
__ sw(scratch, MemOperand(a0, 0 * kPointerSize));
// FunctionCallbackInfo::values_
__ Addu(kScratchReg, scratch,
Operand((FCA::kArgsLength - 1 + argc()) * kPointerSize));
__ sw(kScratchReg, MemOperand(a0, 1 * kPointerSize));
// FunctionCallbackInfo::length_ = argc
__ li(kScratchReg, Operand(argc()));
__ sw(kScratchReg, MemOperand(a0, 2 * kPointerSize));
ExternalReference thunk_ref = ExternalReference::invoke_function_callback();
// There are two stack slots above the arguments we constructed on the stack.
// TODO(jgruber): Document what these arguments are.
static constexpr int kStackSlotsAboveFCA = 2;
MemOperand return_value_operand(
fp, (kStackSlotsAboveFCA + FCA::kReturnValueOffset) * kPointerSize);
static constexpr int kUseStackSpaceOperand = 0;
MemOperand stack_space_operand(sp, 4 * kPointerSize);
AllowExternalCallThatCantCauseGC scope(masm);
// Stores return the first js argument.
int return_value_offset = 2 + FCA::kReturnValueOffset;
MemOperand return_value_operand(fp, return_value_offset * kPointerSize);
const int stack_space = argc() + FCA::kArgsLength + 1;
// TODO(adamk): Why are we clobbering this immediately?
const int32_t stack_space_offset = kInvalidStackOffset;
CallApiFunctionAndReturn(masm, api_function_address, thunk_ref, stack_space,
stack_space_offset, return_value_operand);
CallApiFunctionAndReturn(masm, api_function_address, thunk_ref,
kUseStackSpaceOperand, &stack_space_operand,
return_value_operand);
}
......@@ -500,8 +554,9 @@ void CallApiGetterStub::Generate(MacroAssembler* masm) {
// +3 is to skip prolog, return address and name handle.
MemOperand return_value_operand(
fp, (PropertyCallbackArguments::kReturnValueOffset + 3) * kPointerSize);
MemOperand* const kUseStackSpaceConstant = nullptr;
CallApiFunctionAndReturn(masm, api_function_address, thunk_ref,
kStackUnwindSpace, kInvalidStackOffset,
kStackUnwindSpace, kUseStackSpaceConstant,
return_value_operand);
}
......
......@@ -1684,9 +1684,10 @@ class Instruction : public InstructionGetters<InstructionBase> {
// C/C++ argument slots size.
const int kCArgSlotCount = 4;
const int kCArgsSlotsSize = kCArgSlotCount * kInstrSize;
const int kInvalidStackOffset = -1;
// JS argument slots size.
const int kJSArgsSlotsSize = 0 * kInstrSize;
// Assembly builtins argument slots size.
const int kBArgsSlotsSize = 0 * kInstrSize;
......
......@@ -203,10 +203,9 @@ void ArgumentsAdaptorDescriptor::InitializePlatformSpecific(
void ApiCallbackDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
JavaScriptFrame::context_register(), // callee context
t0, // call_data
a2, // holder
a1, // api_function_address
JavaScriptFrame::context_register(), // kTargetContext
a1, // kApiFunctionAddress
a2, // kArgc
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
......
......@@ -240,7 +240,7 @@ static void CallApiFunctionAndReturn(MacroAssembler* masm,
Register function_address,
ExternalReference thunk_ref,
int stack_space,
int32_t stack_space_offset,
MemOperand* stack_space_operand,
MemOperand return_value_operand) {
Isolate* isolate = masm->isolate();
ExternalReference next_address =
......@@ -324,14 +324,19 @@ static void CallApiFunctionAndReturn(MacroAssembler* masm,
// Leave the API exit frame.
__ bind(&leave_exit_frame);
if (stack_space_offset != kInvalidStackOffset) {
DCHECK_EQ(kCArgsSlotsSize, 0);
__ Ld(s0, MemOperand(sp, stack_space_offset));
} else {
if (stack_space_operand == nullptr) {
CHECK_NE(stack_space, 0);
__ li(s0, Operand(stack_space));
} else {
CHECK_EQ(stack_space, 0);
STATIC_ASSERT(kCArgSlotCount == 0);
__ Ld(s0, *stack_space_operand);
}
__ LeaveExitFrame(false, s0, NO_EMIT_RETURN,
stack_space_offset != kInvalidStackOffset);
static constexpr bool kDontSaveDoubles = false;
static constexpr bool kRegisterContainsSlotCount = false;
__ LeaveExitFrame(kDontSaveDoubles, s0, NO_EMIT_RETURN,
kRegisterContainsSlotCount);
// Check if the function scheduled an exception.
__ LoadRoot(a4, RootIndex::kTheHoleValue);
......@@ -359,20 +364,32 @@ static void CallApiFunctionAndReturn(MacroAssembler* masm,
void CallApiCallbackStub::Generate(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- a4 : call_data
// -- a2 : holder
// -- a1 : api_function_address
// -- cp : context
// -- cp : kTargetContext
// -- a1 : kApiFunctionAddress
// -- a2 : kArgc
// --
// -- sp[0] : last argument
// -- ...
// -- sp[(argc - 1) * 8] : first argument
// -- sp[argc * 8] : receiver
// -- sp[(argc + 0) * 8] : receiver
// -- sp[(argc + 1) * 8] : kHolder
// -- sp[(argc + 2) * 8] : kCallData
// -----------------------------------
Register call_data = a4;
Register holder = a2;
Register api_function_address = a1;
Register argc = a2;
Register scratch = t0;
Register base = t1; // For addressing MemOperands on the stack.
DCHECK(!AreAliased(api_function_address, argc, scratch, base));
// Stack offsets (without argc).
static constexpr int kReceiverOffset = 0 * kPointerSize;
static constexpr int kHolderOffset = kReceiverOffset + kPointerSize;
static constexpr int kCallDataOffset = kHolderOffset + kPointerSize;
// Extra stack arguments are: the receiver, kHolder, kCallData.
static constexpr int kExtraStackArgumentCount = 3;
typedef FunctionCallbackArguments FCA;
......@@ -384,57 +401,94 @@ void CallApiCallbackStub::Generate(MacroAssembler* masm) {
STATIC_ASSERT(FCA::kIsolateIndex == 1);
STATIC_ASSERT(FCA::kHolderIndex == 0);
// new target
__ PushRoot(RootIndex::kUndefinedValue);
// Set up FunctionCallbackInfo's implicit_args on the stack as follows:
//
// Target state:
// sp[0 * kPointerSize]: kHolder
// sp[1 * kPointerSize]: kIsolate
// sp[2 * kPointerSize]: undefined (kReturnValueDefaultValue)
// sp[3 * kPointerSize]: undefined (kReturnValue)
// sp[4 * kPointerSize]: kData
// sp[5 * kPointerSize]: undefined (kNewTarget)
// Set up the base register for addressing through MemOperands. It will point
// at the receiver (located at sp + argc * kPointerSize).
__ Dlsa(base, sp, argc, kPointerSizeLog2);
// Reserve space on the stack.
__ Dsubu(sp, sp, Operand(FCA::kArgsLength * kPointerSize));
// kHolder.
__ Ld(scratch, MemOperand(base, kHolderOffset));
__ Sd(scratch, MemOperand(sp, 0 * kPointerSize));
// call data.
__ Push(call_data);
// kIsolate.
__ li(scratch, ExternalReference::isolate_address(masm->isolate()));
__ Sd(scratch, MemOperand(sp, 1 * kPointerSize));
Register scratch = call_data;
// kReturnValueDefaultValue, kReturnValue, and kNewTarget.
__ LoadRoot(scratch, RootIndex::kUndefinedValue);
// Push return value and default return value.
__ Push(scratch, scratch);
__ li(scratch, ExternalReference::isolate_address(masm->isolate()));
// Push isolate and holder.
__ Push(scratch, holder);
__ Sd(scratch, MemOperand(sp, 2 * kPointerSize));
__ Sd(scratch, MemOperand(sp, 3 * kPointerSize));
__ Sd(scratch, MemOperand(sp, 5 * kPointerSize));
// Prepare arguments.
// kData.
__ Ld(scratch, MemOperand(base, kCallDataOffset));
__ Sd(scratch, MemOperand(sp, 4 * kPointerSize));
// Keep a pointer to kHolder (= implicit_args) in a scratch register.
// We use it below to set up the FunctionCallbackInfo object.
__ mov(scratch, sp);
// Allocate the v8::Arguments structure in the arguments' space since
// it's not controlled by GC.
const int kApiStackSpace = 3;
static constexpr int kApiStackSpace = 4;
static constexpr bool kDontSaveDoubles = false;
FrameScope frame_scope(masm, StackFrame::MANUAL);
__ EnterExitFrame(false, kApiStackSpace);
__ EnterExitFrame(kDontSaveDoubles, kApiStackSpace);
DCHECK(api_function_address != a0 && scratch != a0);
// a0 = FunctionCallbackInfo&
// Arguments is after the return address.
__ Daddu(a0, sp, Operand(1 * kPointerSize));
// FunctionCallbackInfo::implicit_args_
__ Sd(scratch, MemOperand(a0, 0 * kPointerSize));
// FunctionCallbackInfo::values_
__ Daddu(kScratchReg, scratch,
Operand((FCA::kArgsLength - 1 + argc()) * kPointerSize));
__ Sd(kScratchReg, MemOperand(a0, 1 * kPointerSize));
// FunctionCallbackInfo::length_ = argc
// EnterExitFrame may align the sp.
// FunctionCallbackInfo::implicit_args_ (points at kHolder as set up above).
// Arguments are after the return address (pushed by EnterExitFrame()).
__ Sd(scratch, MemOperand(sp, 1 * kPointerSize));
// FunctionCallbackInfo::values_ (points at the first varargs argument passed
// on the stack).
__ Dsubu(scratch, base, Operand(1 * kPointerSize));
__ Sd(scratch, MemOperand(sp, 2 * kPointerSize));
// FunctionCallbackInfo::length_.
// Stored as int field, 32-bit integers within struct on stack always left
// justified by n64 ABI.
__ li(kScratchReg, Operand(argc()));
__ Sw(kScratchReg, MemOperand(a0, 2 * kPointerSize));
__ Sw(argc, MemOperand(sp, 3 * kPointerSize));
// We also store the number of bytes to drop from the stack after returning
// from the API function here.
// Note: Unlike on other architectures, this stores the number of slots to
// drop, not the number of bytes.
__ Daddu(scratch, argc, Operand(FCA::kArgsLength + kExtraStackArgumentCount));
__ Sd(scratch, MemOperand(sp, 4 * kPointerSize));
// v8::InvocationCallback's argument.
DCHECK(!AreAliased(api_function_address, scratch, a0));
__ Daddu(a0, sp, Operand(1 * kPointerSize));
ExternalReference thunk_ref = ExternalReference::invoke_function_callback();
// There are two stack slots above the arguments we constructed on the stack.
// TODO(jgruber): Document what these arguments are.
static constexpr int kStackSlotsAboveFCA = 2;
MemOperand return_value_operand(
fp, (kStackSlotsAboveFCA + FCA::kReturnValueOffset) * kPointerSize);
static constexpr int kUseStackSpaceOperand = 0;
MemOperand stack_space_operand(sp, 4 * kPointerSize);
AllowExternalCallThatCantCauseGC scope(masm);
// Stores return the first js argument.
int return_value_offset = 2 + FCA::kReturnValueOffset;
MemOperand return_value_operand(fp, return_value_offset * kPointerSize);
const int stack_space = argc() + FCA::kArgsLength + 1;
// TODO(adamk): Why are we clobbering this immediately?
const int32_t stack_space_offset = kInvalidStackOffset;
CallApiFunctionAndReturn(masm, api_function_address, thunk_ref, stack_space,
stack_space_offset, return_value_operand);
CallApiFunctionAndReturn(masm, api_function_address, thunk_ref,
kUseStackSpaceOperand, &stack_space_operand,
return_value_operand);
}
......@@ -505,8 +559,9 @@ void CallApiGetterStub::Generate(MacroAssembler* masm) {
// +3 is to skip prolog, return address and name handle.
MemOperand return_value_operand(
fp, (PropertyCallbackArguments::kReturnValueOffset + 3) * kPointerSize);
MemOperand* const kUseStackSpaceConstant = nullptr;
CallApiFunctionAndReturn(masm, api_function_address, thunk_ref,
kStackUnwindSpace, kInvalidStackOffset,
kStackUnwindSpace, kUseStackSpaceConstant,
return_value_operand);
}
......
......@@ -203,10 +203,9 @@ void ArgumentsAdaptorDescriptor::InitializePlatformSpecific(
void ApiCallbackDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
JavaScriptFrame::context_register(), // callee context
a4, // call_data
a2, // holder
a1, // api_function_address
JavaScriptFrame::context_register(), // kTargetContext
a1, // kApiFunctionAddress
a2, // kArgc
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
......
......@@ -177,16 +177,6 @@ static int Offset(ExternalReference ref0, ExternalReference ref1) {
return static_cast<int>(offset);
}
// Prepares stack to put arguments (aligns and so on). WIN64 calling convention
// requires to put the pointer to the return value slot into rcx (rcx must be
// preserverd until CallApiFunctionAndReturn). Clobbers rax. Allocates
// arg_stack_space * kPointerSize inside the exit frame (not GCed) accessible
// via StackSpaceOperand.
static void PrepareCallApiFunction(MacroAssembler* masm, int arg_stack_space) {
__ EnterApiExitFrame(arg_stack_space);
}
// Calls an API function. Allocates HandleScope, extracts returned value
// from handle and propagates exceptions. Clobbers r14, r15, rbx and
// caller-save registers. Restores context. On return removes
......@@ -345,24 +335,40 @@ static void CallApiFunctionAndReturn(MacroAssembler* masm,
__ jmp(&leave_exit_frame);
}
// TODO(jgruber): Instead of explicitly setting up implicit_args_ on the stack
// in CallApiCallback, we could use the calling convention to set up the stack
// correctly in the first place.
//
// TODO(jgruber): I suspect that most of CallApiCallback could be implemented
// as a C++ trampoline, vastly simplifying the assembly implementation.
void CallApiCallbackStub::Generate(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rbx : call_data
// -- rcx : holder
// -- rdx : api_function_address
// -- rsi : context
// -- rax : number of arguments if argc is a register
// -- rsi : kTargetContext
// -- rdx : kApiFunctionAddress
// -- rcx : kArgc
// --
// -- rsp[0] : return address
// -- rsp[8] : last argument
// -- ...
// -- rsp[argc * 8] : first argument
// -- rsp[(argc + 1) * 8] : receiver
// -- rsp[(argc + 2) * 8] : kHolder
// -- rsp[(argc + 3) * 8] : kCallData
// -----------------------------------
Register call_data = rbx;
Register holder = rcx;
Register api_function_address = rdx;
Register return_address = r8;
Register argc = rcx;
DCHECK(!AreAliased(api_function_address, argc, kScratchRegister));
// Stack offsets (without argc).
static constexpr int kReceiverOffset = kPointerSize;
static constexpr int kHolderOffset = kReceiverOffset + kPointerSize;
static constexpr int kCallDataOffset = kHolderOffset + kPointerSize;
// Extra stack arguments are: the receiver, kHolder, kCallData.
static constexpr int kExtraStackArgumentCount = 3;
typedef FunctionCallbackArguments FCA;
......@@ -374,52 +380,82 @@ void CallApiCallbackStub::Generate(MacroAssembler* masm) {
STATIC_ASSERT(FCA::kIsolateIndex == 1);
STATIC_ASSERT(FCA::kHolderIndex == 0);
__ PopReturnAddressTo(return_address);
// new target
__ PushRoot(RootIndex::kUndefinedValue);
// call data
__ Push(call_data);
// return value
__ PushRoot(RootIndex::kUndefinedValue);
// return value default
__ PushRoot(RootIndex::kUndefinedValue);
// isolate
Register scratch = call_data;
__ Move(scratch, ExternalReference::isolate_address(masm->isolate()));
__ Push(scratch);
// holder
__ Push(holder);
// Set up FunctionCallbackInfo's implicit_args on the stack as follows:
//
// Current state:
// rsp[0]: return address
//
// Target state:
// rsp[0 * kPointerSize]: return address
// rsp[1 * kPointerSize]: kHolder
// rsp[2 * kPointerSize]: kIsolate
// rsp[3 * kPointerSize]: undefined (kReturnValueDefaultValue)
// rsp[4 * kPointerSize]: undefined (kReturnValue)
// rsp[5 * kPointerSize]: kData
// rsp[6 * kPointerSize]: undefined (kNewTarget)
// Reserve space on the stack.
__ subp(rsp, Immediate(FCA::kArgsLength * kPointerSize));
// Return address (the old stack location is overwritten later on).
__ movp(kScratchRegister, Operand(rsp, FCA::kArgsLength * kPointerSize));
__ movp(Operand(rsp, 0 * kPointerSize), kScratchRegister);
// kHolder.
__ movp(kScratchRegister,
Operand(rsp, argc, times_pointer_size,
FCA::kArgsLength * kPointerSize + kHolderOffset));
__ movp(Operand(rsp, 1 * kPointerSize), kScratchRegister);
// kIsolate.
__ Move(kScratchRegister,
ExternalReference::isolate_address(masm->isolate()));
__ movp(Operand(rsp, 2 * kPointerSize), kScratchRegister);
// kReturnValueDefaultValue, kReturnValue, and kNewTarget.
__ LoadRoot(kScratchRegister, RootIndex::kUndefinedValue);
__ movp(Operand(rsp, 3 * kPointerSize), kScratchRegister);
__ movp(Operand(rsp, 4 * kPointerSize), kScratchRegister);
__ movp(Operand(rsp, 6 * kPointerSize), kScratchRegister);
int argc = this->argc();
// kData.
__ movp(kScratchRegister,
Operand(rsp, argc, times_pointer_size,
FCA::kArgsLength * kPointerSize + kCallDataOffset));
__ movp(Operand(rsp, 5 * kPointerSize), kScratchRegister);
__ movp(scratch, rsp);
// Push return address back on stack.
__ PushReturnAddressFrom(return_address);
// Keep a pointer to kHolder (= implicit_args) in a scratch register.
// We use it below to set up the FunctionCallbackInfo object.
Register scratch = rbx;
__ leap(scratch, Operand(rsp, 1 * kPointerSize));
// Allocate the v8::Arguments structure in the arguments' space since
// it's not controlled by GC.
const int kApiStackSpace = 3;
static constexpr int kApiStackSpace = 4;
__ EnterApiExitFrame(kApiStackSpace);
PrepareCallApiFunction(masm, kApiStackSpace);
// FunctionCallbackInfo::implicit_args_.
// FunctionCallbackInfo::implicit_args_ (points at kHolder as set up above).
__ movp(StackSpaceOperand(0), scratch);
__ addp(scratch, Immediate((argc + FCA::kArgsLength - 1) * kPointerSize));
// FunctionCallbackInfo::values_.
// FunctionCallbackInfo::values_ (points at the first varargs argument passed
// on the stack).
__ leap(scratch, Operand(scratch, argc, times_pointer_size,
(FCA::kArgsLength - 1) * kPointerSize));
__ movp(StackSpaceOperand(1), scratch);
// FunctionCallbackInfo::length_.
__ Set(StackSpaceOperand(2), argc);
__ movp(StackSpaceOperand(2), argc);
#if defined(__MINGW64__) || defined(_WIN64)
Register arguments_arg = rcx;
Register callback_arg = rdx;
#else
Register arguments_arg = rdi;
Register callback_arg = rsi;
#endif
// We also store the number of bytes to drop from the stack after returning
// from the API function here.
__ leaq(
kScratchRegister,
Operand(argc, times_pointer_size,
(FCA::kArgsLength + kExtraStackArgumentCount) * kPointerSize));
__ movp(StackSpaceOperand(3), kScratchRegister);
Register arguments_arg = arg_reg_1;
Register callback_arg = arg_reg_2;
// It's okay if api_function_address == callback_arg
// but not arguments_arg
......@@ -430,15 +466,16 @@ void CallApiCallbackStub::Generate(MacroAssembler* masm) {
ExternalReference thunk_ref = ExternalReference::invoke_function_callback();
// Accessor for FunctionCallbackInfo and first js arg.
StackArgumentsAccessor args_from_rbp(rbp, FCA::kArgsLength + 1,
ARGUMENTS_DONT_CONTAIN_RECEIVER);
Operand return_value_operand = args_from_rbp.GetArgumentOperand(
FCA::kArgsLength - FCA::kReturnValueOffset);
const int stack_space = argc + FCA::kArgsLength + 1;
Operand* stack_space_operand = nullptr;
// There are two stack slots above the arguments we constructed on the stack:
// the stored ebp (pushed by EnterApiExitFrame), and the return address.
static constexpr int kStackSlotsAboveFCA = 2;
Operand return_value_operand(
rbp, (kStackSlotsAboveFCA + FCA::kReturnValueOffset) * kPointerSize);
static constexpr int kUseStackSpaceOperand = 0;
Operand stack_space_operand = StackSpaceOperand(3);
CallApiFunctionAndReturn(masm, api_function_address, thunk_ref, callback_arg,
stack_space, stack_space_operand,
kUseStackSpaceOperand, &stack_space_operand,
return_value_operand);
}
......@@ -493,14 +530,15 @@ void CallApiGetterStub::Generate(MacroAssembler* masm) {
// Load address of v8::PropertyAccessorInfo::args_ array.
__ leap(scratch, Operand(rsp, 2 * kPointerSize));
PrepareCallApiFunction(masm, kArgStackSpace);
__ EnterApiExitFrame(kArgStackSpace);
// Create v8::PropertyCallbackInfo object on the stack and initialize
// it's args_ field.
Operand info_object = StackSpaceOperand(0);
__ movp(info_object, scratch);
__ leap(name_arg, Operand(scratch, -kPointerSize));
// The context register (rsi) has been saved in PrepareCallApiFunction and
// The context register (rsi) has been saved in EnterApiExitFrame and
// could be used to pass arguments.
__ leap(accessor_info_arg, info_object);
......@@ -518,8 +556,10 @@ void CallApiGetterStub::Generate(MacroAssembler* masm) {
// +3 is to skip prolog, return address and name handle.
Operand return_value_operand(
rbp, (PropertyCallbackArguments::kReturnValueOffset + 3) * kPointerSize);
Operand* const kUseStackSpaceConstant = nullptr;
CallApiFunctionAndReturn(masm, api_function_address, thunk_ref, getter_arg,
kStackUnwindSpace, nullptr, return_value_operand);
kStackUnwindSpace, kUseStackSpaceConstant,
return_value_operand);
}
#undef __
......
......@@ -204,10 +204,9 @@ void ArgumentsAdaptorDescriptor::InitializePlatformSpecific(
void ApiCallbackDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
JavaScriptFrame::context_register(), // callee context
rbx, // call_data
rcx, // holder
rdx, // api_function_address
JavaScriptFrame::context_register(), // kTargetContext
rdx, // kApiFunctionAddress
rcx, // kArgc
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment