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, ...@@ -351,20 +351,32 @@ static void CallApiFunctionAndReturn(MacroAssembler* masm,
void CallApiCallbackStub::Generate(MacroAssembler* masm) { void CallApiCallbackStub::Generate(MacroAssembler* masm) {
// ----------- S t a t e ------------- // ----------- S t a t e -------------
// -- r4 : call_data // -- cp : kTargetContext
// -- r2 : holder // -- r1 : kApiFunctionAddress
// -- r1 : api_function_address // -- r2 : kArgc
// -- cp : context
// -- // --
// -- sp[0] : last argument // -- sp[0] : last argument
// -- ... // -- ...
// -- sp[(argc - 1) * 4] : first 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 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; typedef FunctionCallbackArguments FCA;
...@@ -376,60 +388,88 @@ void CallApiCallbackStub::Generate(MacroAssembler* masm) { ...@@ -376,60 +388,88 @@ void CallApiCallbackStub::Generate(MacroAssembler* masm) {
STATIC_ASSERT(FCA::kIsolateIndex == 1); STATIC_ASSERT(FCA::kIsolateIndex == 1);
STATIC_ASSERT(FCA::kHolderIndex == 0); STATIC_ASSERT(FCA::kHolderIndex == 0);
// new target // Set up FunctionCallbackInfo's implicit_args on the stack as follows:
__ PushRoot(RootIndex::kUndefinedValue); //
// Target state:
// call data // sp[0 * kPointerSize]: kHolder
__ push(call_data); // 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; // kData.
Register scratch1 = r5; __ add(index, argc, Operand(FCA::kArgsLength + kCallDataOffset));
__ LoadRoot(scratch0, RootIndex::kUndefinedValue); __ ldr(scratch, MemOperand(sp, index, LSL, kPointerSizeLog2));
// return value __ str(scratch, MemOperand(sp, 4 * kPointerSize));
__ push(scratch0);
// return value default
__ push(scratch0);
// isolate
__ Move(scratch1, ExternalReference::isolate_address(masm->isolate()));
__ push(scratch1);
// holder
__ push(holder);
// Prepare arguments. // Keep a pointer to kHolder (= implicit_args) in a scratch register.
__ mov(scratch0, sp); // We use it below to set up the FunctionCallbackInfo object.
__ mov(scratch, sp);
// Allocate the v8::Arguments structure in the arguments' space since // Allocate the v8::Arguments structure in the arguments' space since
// it's not controlled by GC. // 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); FrameScope frame_scope(masm, StackFrame::MANUAL);
__ EnterExitFrame(false, kApiStackSpace); __ EnterExitFrame(kDontSaveDoubles, kApiStackSpace);
DCHECK(api_function_address != r0 && scratch0 != r0); // FunctionCallbackInfo::implicit_args_ (points at kHolder as set up above).
// r0 = FunctionCallbackInfo& // Arguments are after the return address (pushed by EnterExitFrame()).
// Arguments is after the return address. __ 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)); __ 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(); 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); AllowExternalCallThatCantCauseGC scope(masm);
// Stores return the first js argument CallApiFunctionAndReturn(masm, api_function_address, thunk_ref,
int return_value_offset = 2 + FCA::kReturnValueOffset; kUseStackSpaceOperand, &stack_space_operand,
MemOperand return_value_operand(fp, return_value_offset * kPointerSize); return_value_operand);
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);
} }
...@@ -490,8 +530,10 @@ void CallApiGetterStub::Generate(MacroAssembler* masm) { ...@@ -490,8 +530,10 @@ void CallApiGetterStub::Generate(MacroAssembler* masm) {
// +3 is to skip prolog, return address and name handle. // +3 is to skip prolog, return address and name handle.
MemOperand return_value_operand( MemOperand return_value_operand(
fp, (PropertyCallbackArguments::kReturnValueOffset + 3) * kPointerSize); fp, (PropertyCallbackArguments::kReturnValueOffset + 3) * kPointerSize);
MemOperand* const kUseStackSpaceConstant = nullptr;
CallApiFunctionAndReturn(masm, api_function_address, thunk_ref, CallApiFunctionAndReturn(masm, api_function_address, thunk_ref,
kStackUnwindSpace, nullptr, return_value_operand); kStackUnwindSpace, kUseStackSpaceConstant,
return_value_operand);
} }
#undef __ #undef __
......
...@@ -202,10 +202,9 @@ void ArgumentsAdaptorDescriptor::InitializePlatformSpecific( ...@@ -202,10 +202,9 @@ void ArgumentsAdaptorDescriptor::InitializePlatformSpecific(
void ApiCallbackDescriptor::InitializePlatformSpecific( void ApiCallbackDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) { CallInterfaceDescriptorData* data) {
Register registers[] = { Register registers[] = {
JavaScriptFrame::context_register(), // callee context JavaScriptFrame::context_register(), // kTargetContext
r4, // call_data r1, // kApiFunctionAddress
r2, // holder r2, // kArgc
r1, // api_function_address
}; };
data->InitializePlatformSpecific(arraysize(registers), registers); data->InitializePlatformSpecific(arraysize(registers), registers);
} }
......
This diff is collapsed.
...@@ -206,10 +206,9 @@ void ArgumentsAdaptorDescriptor::InitializePlatformSpecific( ...@@ -206,10 +206,9 @@ void ArgumentsAdaptorDescriptor::InitializePlatformSpecific(
void ApiCallbackDescriptor::InitializePlatformSpecific( void ApiCallbackDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) { CallInterfaceDescriptorData* data) {
Register registers[] = { Register registers[] = {
JavaScriptFrame::context_register(), // callee context JavaScriptFrame::context_register(), // kTargetContext
x4, // call_data x1, // kApiFunctionAddress
x2, // holder x2, // kArgc
x1, // api_function_address
}; };
data->InitializePlatformSpecific(arraysize(registers), registers); data->InitializePlatformSpecific(arraysize(registers), registers);
} }
......
...@@ -2868,9 +2868,6 @@ Reduction JSCallReducer::ReduceCallApiFunction( ...@@ -2868,9 +2868,6 @@ Reduction JSCallReducer::ReduceCallApiFunction(
Handle<FunctionTemplateInfo> function_template_info( Handle<FunctionTemplateInfo> function_template_info(
FunctionTemplateInfo::cast(shared.object()->function_data()), isolate()); 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(); if (argc > CallApiCallbackStub::kArgMax) return NoChange();
// Infer the {receiver} maps, and check if we can inline the API function // Infer the {receiver} maps, and check if we can inline the API function
...@@ -2945,13 +2942,14 @@ Reduction JSCallReducer::ReduceCallApiFunction( ...@@ -2945,13 +2942,14 @@ Reduction JSCallReducer::ReduceCallApiFunction(
node->InsertInput(graph()->zone(), 0, node->InsertInput(graph()->zone(), 0,
jsgraph()->HeapConstant(call_api_callback.code())); jsgraph()->HeapConstant(call_api_callback.code()));
node->ReplaceInput(1, context); node->ReplaceInput(1, context);
node->InsertInput(graph()->zone(), 2, jsgraph()->Constant(data)); node->InsertInput(graph()->zone(), 2,
node->InsertInput(graph()->zone(), 3, holder);
node->InsertInput(graph()->zone(), 4,
jsgraph()->ExternalConstant(function_reference)); jsgraph()->ExternalConstant(function_reference));
node->ReplaceInput(5, receiver); node->InsertInput(graph()->zone(), 3, jsgraph()->Constant(argc));
node->RemoveInput(6 + argc); // Remove context input. node->InsertInput(graph()->zone(), 4, jsgraph()->Constant(data));
node->ReplaceInput(7 + argc, effect); // Update effect input. 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)); NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
return Changed(node); return Changed(node);
} }
......
...@@ -2038,16 +2038,17 @@ Node* JSNativeContextSpecialization::InlineApiCall( ...@@ -2038,16 +2038,17 @@ Node* JSNativeContextSpecialization::InlineApiCall(
// Add CallApiCallbackStub's register argument as well. // Add CallApiCallbackStub's register argument as well.
Node* context = jsgraph()->Constant(native_context()); Node* context = jsgraph()->Constant(native_context());
Node* inputs[10] = {code, context, data, holder, function_reference, Node* inputs[11] = {
receiver}; code, context, function_reference, jsgraph()->Constant(argc), data,
int index = 6 + argc; holder, receiver};
int index = 7 + argc;
inputs[index++] = frame_state; inputs[index++] = frame_state;
inputs[index++] = *effect; inputs[index++] = *effect;
inputs[index++] = *control; inputs[index++] = *control;
// This needs to stay here because of the edge case described in // This needs to stay here because of the edge case described in
// http://crbug.com/675648. // http://crbug.com/675648.
if (value != nullptr) { if (value != nullptr) {
inputs[6] = value; inputs[7] = value;
} }
return *effect = *control = return *effect = *control =
......
...@@ -294,22 +294,32 @@ static void CallApiFunctionAndReturn(MacroAssembler* masm, ...@@ -294,22 +294,32 @@ static void CallApiFunctionAndReturn(MacroAssembler* masm,
void CallApiCallbackStub::Generate(MacroAssembler* masm) { void CallApiCallbackStub::Generate(MacroAssembler* masm) {
// ----------- S t a t e ------------- // ----------- S t a t e -------------
// -- eax : call_data // -- esi : kTargetContext
// -- ecx : holder // -- edx : kApiFunctionAddress
// -- edx : api_function_address // -- ecx : kArgc
// -- esi : context
// -- // --
// -- esp[0] : return address // -- esp[0] : return address
// -- esp[4] : last argument // -- esp[4] : last argument
// -- ... // -- ...
// -- esp[argc * 4] : first argument // -- esp[argc * 4] : first argument
// -- esp[(argc + 1) * 4] : receiver // -- 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 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; typedef FunctionCallbackArguments FCA;
...@@ -321,64 +331,100 @@ void CallApiCallbackStub::Generate(MacroAssembler* masm) { ...@@ -321,64 +331,100 @@ void CallApiCallbackStub::Generate(MacroAssembler* masm) {
STATIC_ASSERT(FCA::kIsolateIndex == 1); STATIC_ASSERT(FCA::kIsolateIndex == 1);
STATIC_ASSERT(FCA::kHolderIndex == 0); STATIC_ASSERT(FCA::kHolderIndex == 0);
__ pop(return_address); // Set up FunctionCallbackInfo's implicit_args on the stack as follows:
//
// new target // Current state:
__ PushRoot(RootIndex::kUndefinedValue); // esp[0]: return address
//
// call data // Target state:
__ push(call_data); // esp[0 * kPointerSize]: return address
// esp[1 * kPointerSize]: kHolder
// return value // esp[2 * kPointerSize]: kIsolate
__ PushRoot(RootIndex::kUndefinedValue); // esp[3 * kPointerSize]: undefined (kReturnValueDefaultValue)
// return value default // esp[4 * kPointerSize]: undefined (kReturnValue)
__ PushRoot(RootIndex::kUndefinedValue); // esp[5 * kPointerSize]: kData
// isolate // esp[6 * kPointerSize]: undefined (kNewTarget)
__ Push(Immediate(ExternalReference::isolate_address(isolate())));
// holder // Reserve space on the stack.
__ push(holder); __ sub(esp, Immediate(FCA::kArgsLength * kPointerSize));
Register scratch = call_data; // Return address (the old stack location is overwritten later on).
__ mov(scratch, Operand(esp, FCA::kArgsLength * kPointerSize));
__ mov(scratch, esp); __ mov(Operand(esp, 0 * kPointerSize), scratch);
// push return address // kHolder.
__ push(return_address); __ mov(scratch, Operand(esp, argc, times_pointer_size,
FCA::kArgsLength * kPointerSize + kHolderOffset));
// API function gets reference to the v8::Arguments. If CPU profiler __ mov(Operand(esp, 1 * kPointerSize), scratch);
// is enabled wrapper function will be called and we need to pass
// address of the callback as additional parameter, always allocate // 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. // space for it.
const int kApiArgc = 1 + 1; static constexpr int kApiArgc = 1 + 1;
// Allocate the v8::Arguments structure in the arguments' space since // Allocate the v8::Arguments structure in the arguments' space since
// it's not controlled by GC. // it's not controlled by GC.
const int kApiStackSpace = 3; static constexpr int kApiStackSpace = 4;
PrepareCallApiFunction(masm, kApiArgc + kApiStackSpace, edi); PrepareCallApiFunction(masm, kApiArgc + kApiStackSpace, edi);
// FunctionCallbackInfo::implicit_args_. // FunctionCallbackInfo::implicit_args_ (points at kHolder as set up above).
__ mov(ApiParameterOperand(2), scratch); __ mov(ApiParameterOperand(kApiArgc + 0), scratch);
__ add(scratch, Immediate((argc() + FCA::kArgsLength - 1) * kPointerSize));
// FunctionCallbackInfo::values_. // FunctionCallbackInfo::values_ (points at the first varargs argument passed
__ mov(ApiParameterOperand(3), scratch); // on the stack).
__ lea(scratch, Operand(scratch, argc, times_pointer_size,
(FCA::kArgsLength - 1) * kPointerSize));
__ mov(ApiParameterOperand(kApiArgc + 1), scratch);
// FunctionCallbackInfo::length_. // 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. // v8::InvocationCallback's argument.
__ lea(scratch, ApiParameterOperand(2)); __ lea(scratch, ApiParameterOperand(kApiArgc + 0));
__ mov(ApiParameterOperand(0), scratch); __ mov(ApiParameterOperand(0), scratch);
ExternalReference thunk_ref = ExternalReference::invoke_function_callback(); ExternalReference thunk_ref = ExternalReference::invoke_function_callback();
// Stores return the first js argument // There are two stack slots above the arguments we constructed on the stack:
int return_value_offset = 2 + FCA::kReturnValueOffset; // the stored ebp (pushed by EnterApiExitFrame), and the return address.
Operand return_value_operand(ebp, return_value_offset * kPointerSize); static constexpr int kStackSlotsAboveFCA = 2;
const int stack_space = argc() + FCA::kArgsLength + 1; Operand return_value_operand(
Operand* stack_space_operand = nullptr; ebp, (kStackSlotsAboveFCA + FCA::kReturnValueOffset) * kPointerSize);
static constexpr int kUseStackSpaceOperand = 0;
Operand stack_space_operand = ApiParameterOperand(kApiArgc + 3);
CallApiFunctionAndReturn(masm, api_function_address, thunk_ref, CallApiFunctionAndReturn(masm, api_function_address, thunk_ref,
ApiParameterOperand(1), stack_space, ApiParameterOperand(1), kUseStackSpaceOperand,
stack_space_operand, return_value_operand); &stack_space_operand, return_value_operand);
} }
...@@ -449,8 +495,10 @@ void CallApiGetterStub::Generate(MacroAssembler* masm) { ...@@ -449,8 +495,10 @@ void CallApiGetterStub::Generate(MacroAssembler* masm) {
// +3 is to skip prolog, return address and name handle. // +3 is to skip prolog, return address and name handle.
Operand return_value_operand( Operand return_value_operand(
ebp, (PropertyCallbackArguments::kReturnValueOffset + 3) * kPointerSize); ebp, (PropertyCallbackArguments::kReturnValueOffset + 3) * kPointerSize);
Operand* const kUseStackSpaceConstant = nullptr;
CallApiFunctionAndReturn(masm, function_address, thunk_ref, thunk_last_arg, CallApiFunctionAndReturn(masm, function_address, thunk_ref, thunk_last_arg,
kStackUnwindSpace, nullptr, return_value_operand); kStackUnwindSpace, kUseStackSpaceConstant,
return_value_operand);
} }
#undef __ #undef __
......
...@@ -205,10 +205,9 @@ void ArgumentsAdaptorDescriptor::InitializePlatformSpecific( ...@@ -205,10 +205,9 @@ void ArgumentsAdaptorDescriptor::InitializePlatformSpecific(
void ApiCallbackDescriptor::InitializePlatformSpecific( void ApiCallbackDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) { CallInterfaceDescriptorData* data) {
Register registers[] = { Register registers[] = {
JavaScriptFrame::context_register(), // callee context JavaScriptFrame::context_register(), // kTargetContext
eax, // call_data edx, // kApiFunctionAddress
ecx, // holder ecx, // kArgc
edx, // api_function_address
}; };
data->InitializePlatformSpecific(arraysize(registers), registers); data->InitializePlatformSpecific(arraysize(registers), registers);
} }
......
...@@ -273,8 +273,9 @@ void AccessorAssembler::HandleLoadAccessor( ...@@ -273,8 +273,9 @@ void AccessorAssembler::HandleLoadAccessor(
BIND(&load); BIND(&load);
Callable callable = CodeFactory::CallApiCallback(isolate(), 0); Callable callable = CodeFactory::CallApiCallback(isolate(), 0);
exit_point->Return(CallStub(callable, nullptr, context, data, TNode<IntPtrT> argc = IntPtrConstant(0);
api_holder.value(), callback, p->receiver)); exit_point->Return(CallStub(callable, nullptr, context, callback, argc,
data, api_holder.value(), p->receiver));
} }
BIND(&runtime); BIND(&runtime);
...@@ -1415,8 +1416,9 @@ void AccessorAssembler::HandleStoreICProtoHandler( ...@@ -1415,8 +1416,9 @@ void AccessorAssembler::HandleStoreICProtoHandler(
BIND(&store); BIND(&store);
Callable callable = CodeFactory::CallApiCallback(isolate(), 1); Callable callable = CodeFactory::CallApiCallback(isolate(), 1);
Return(CallStub(callable, nullptr, context, data, api_holder.value(), TNode<IntPtrT> argc = IntPtrConstant(1);
callback, p->receiver, p->value)); Return(CallStub(callable, nullptr, context, callback, argc, data,
api_holder.value(), p->receiver, p->value));
} }
BIND(&if_store_global_proxy); BIND(&if_store_global_proxy);
......
...@@ -949,15 +949,18 @@ class CEntry1ArgvOnStackDescriptor : public CallInterfaceDescriptor { ...@@ -949,15 +949,18 @@ class CEntry1ArgvOnStackDescriptor : public CallInterfaceDescriptor {
class ApiCallbackDescriptor : public CallInterfaceDescriptor { class ApiCallbackDescriptor : public CallInterfaceDescriptor {
public: public:
// TODO(jgruber): This could be simplified to pass call data on the stack DEFINE_PARAMETERS_NO_CONTEXT(kTargetContext, // register argument
// since this is what the CallApiCallbackStub anyways. This would free a kApiFunctionAddress, // register argument
// register. kArgc, // register argument
DEFINE_PARAMETERS_NO_CONTEXT(kTargetContext, kCallData, kHolder, kCallData, // stack argument 1
kApiFunctionAddress) kHolder) // stack argument 2
// receiver is implicit stack argument 3
// argv are implicit stack arguments [4, 4 + kArgc[
DEFINE_PARAMETER_TYPES(MachineType::AnyTagged(), // kTargetContext DEFINE_PARAMETER_TYPES(MachineType::AnyTagged(), // kTargetContext
MachineType::Pointer(), // kApiFunctionAddress
MachineType::IntPtr(), // kArgc
MachineType::AnyTagged(), // kCallData MachineType::AnyTagged(), // kCallData
MachineType::AnyTagged(), // kHolder MachineType::AnyTagged()) // kHolder
MachineType::Pointer()) // kApiFunctionAddress
DECLARE_DESCRIPTOR(ApiCallbackDescriptor, CallInterfaceDescriptor) DECLARE_DESCRIPTOR(ApiCallbackDescriptor, CallInterfaceDescriptor)
}; };
......
...@@ -237,7 +237,7 @@ static void CallApiFunctionAndReturn(MacroAssembler* masm, ...@@ -237,7 +237,7 @@ static void CallApiFunctionAndReturn(MacroAssembler* masm,
Register function_address, Register function_address,
ExternalReference thunk_ref, ExternalReference thunk_ref,
int stack_space, int stack_space,
int32_t stack_space_offset, MemOperand* stack_space_operand,
MemOperand return_value_operand) { MemOperand return_value_operand) {
Isolate* isolate = masm->isolate(); Isolate* isolate = masm->isolate();
ExternalReference next_address = ExternalReference next_address =
...@@ -321,15 +321,22 @@ static void CallApiFunctionAndReturn(MacroAssembler* masm, ...@@ -321,15 +321,22 @@ static void CallApiFunctionAndReturn(MacroAssembler* masm,
// Leave the API exit frame. // Leave the API exit frame.
__ bind(&leave_exit_frame); __ bind(&leave_exit_frame);
if (stack_space_offset != kInvalidStackOffset) { if (stack_space_operand == nullptr) {
// ExitFrame contains four MIPS argument slots after DirectCEntryStub call CHECK_NE(stack_space, 0);
// so this must be accounted for.
__ lw(s0, MemOperand(sp, stack_space_offset + kCArgsSlotsSize));
} else {
__ li(s0, Operand(stack_space)); __ 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. // Check if the function scheduled an exception.
__ LoadRoot(t0, RootIndex::kTheHoleValue); __ LoadRoot(t0, RootIndex::kTheHoleValue);
...@@ -357,20 +364,32 @@ static void CallApiFunctionAndReturn(MacroAssembler* masm, ...@@ -357,20 +364,32 @@ static void CallApiFunctionAndReturn(MacroAssembler* masm,
void CallApiCallbackStub::Generate(MacroAssembler* masm) { void CallApiCallbackStub::Generate(MacroAssembler* masm) {
// ----------- S t a t e ------------- // ----------- S t a t e -------------
// -- t0 : call_data // -- cp : kTargetContext
// -- a2 : holder // -- a1 : kApiFunctionAddress
// -- a1 : api_function_address // -- a2 : kArgc
// -- cp : context
// -- // --
// -- sp[0] : last argument // -- sp[0] : last argument
// -- ... // -- ...
// -- sp[(argc - 1)* 4] : first 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 = t0;
Register holder = a2;
Register api_function_address = a1; 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; typedef FunctionCallbackArguments FCA;
...@@ -382,55 +401,90 @@ void CallApiCallbackStub::Generate(MacroAssembler* masm) { ...@@ -382,55 +401,90 @@ void CallApiCallbackStub::Generate(MacroAssembler* masm) {
STATIC_ASSERT(FCA::kIsolateIndex == 1); STATIC_ASSERT(FCA::kIsolateIndex == 1);
STATIC_ASSERT(FCA::kHolderIndex == 0); STATIC_ASSERT(FCA::kHolderIndex == 0);
// new target // Set up FunctionCallbackInfo's implicit_args on the stack as follows:
__ PushRoot(RootIndex::kUndefinedValue); //
// 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. // kIsolate.
__ Push(call_data); __ 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); __ LoadRoot(scratch, RootIndex::kUndefinedValue);
// Push return value and default return value. __ sw(scratch, MemOperand(sp, 2 * kPointerSize));
__ Push(scratch, scratch); __ sw(scratch, MemOperand(sp, 3 * kPointerSize));
__ li(scratch, ExternalReference::isolate_address(masm->isolate())); __ sw(scratch, MemOperand(sp, 5 * kPointerSize));
// Push isolate and holder.
__ Push(scratch, holder); // 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); __ mov(scratch, sp);
// Allocate the v8::Arguments structure in the arguments' space since // Allocate the v8::Arguments structure in the arguments' space since
// it's not controlled by GC. // 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); 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); // v8::InvocationCallback's argument.
// a0 = FunctionCallbackInfo& DCHECK(!AreAliased(api_function_address, scratch, a0));
// Arguments is after the return address.
__ Addu(a0, sp, Operand(1 * kPointerSize)); __ 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(); 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); AllowExternalCallThatCantCauseGC scope(masm);
// Stores return the first js argument. CallApiFunctionAndReturn(masm, api_function_address, thunk_ref,
int return_value_offset = 2 + FCA::kReturnValueOffset; kUseStackSpaceOperand, &stack_space_operand,
MemOperand return_value_operand(fp, return_value_offset * kPointerSize); return_value_operand);
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);
} }
...@@ -500,8 +554,9 @@ void CallApiGetterStub::Generate(MacroAssembler* masm) { ...@@ -500,8 +554,9 @@ void CallApiGetterStub::Generate(MacroAssembler* masm) {
// +3 is to skip prolog, return address and name handle. // +3 is to skip prolog, return address and name handle.
MemOperand return_value_operand( MemOperand return_value_operand(
fp, (PropertyCallbackArguments::kReturnValueOffset + 3) * kPointerSize); fp, (PropertyCallbackArguments::kReturnValueOffset + 3) * kPointerSize);
MemOperand* const kUseStackSpaceConstant = nullptr;
CallApiFunctionAndReturn(masm, api_function_address, thunk_ref, CallApiFunctionAndReturn(masm, api_function_address, thunk_ref,
kStackUnwindSpace, kInvalidStackOffset, kStackUnwindSpace, kUseStackSpaceConstant,
return_value_operand); return_value_operand);
} }
......
...@@ -1684,9 +1684,10 @@ class Instruction : public InstructionGetters<InstructionBase> { ...@@ -1684,9 +1684,10 @@ class Instruction : public InstructionGetters<InstructionBase> {
// C/C++ argument slots size. // C/C++ argument slots size.
const int kCArgSlotCount = 4; const int kCArgSlotCount = 4;
const int kCArgsSlotsSize = kCArgSlotCount * kInstrSize; const int kCArgsSlotsSize = kCArgSlotCount * kInstrSize;
const int kInvalidStackOffset = -1;
// JS argument slots size. // JS argument slots size.
const int kJSArgsSlotsSize = 0 * kInstrSize; const int kJSArgsSlotsSize = 0 * kInstrSize;
// Assembly builtins argument slots size. // Assembly builtins argument slots size.
const int kBArgsSlotsSize = 0 * kInstrSize; const int kBArgsSlotsSize = 0 * kInstrSize;
......
...@@ -203,10 +203,9 @@ void ArgumentsAdaptorDescriptor::InitializePlatformSpecific( ...@@ -203,10 +203,9 @@ void ArgumentsAdaptorDescriptor::InitializePlatformSpecific(
void ApiCallbackDescriptor::InitializePlatformSpecific( void ApiCallbackDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) { CallInterfaceDescriptorData* data) {
Register registers[] = { Register registers[] = {
JavaScriptFrame::context_register(), // callee context JavaScriptFrame::context_register(), // kTargetContext
t0, // call_data a1, // kApiFunctionAddress
a2, // holder a2, // kArgc
a1, // api_function_address
}; };
data->InitializePlatformSpecific(arraysize(registers), registers); data->InitializePlatformSpecific(arraysize(registers), registers);
} }
......
...@@ -240,7 +240,7 @@ static void CallApiFunctionAndReturn(MacroAssembler* masm, ...@@ -240,7 +240,7 @@ static void CallApiFunctionAndReturn(MacroAssembler* masm,
Register function_address, Register function_address,
ExternalReference thunk_ref, ExternalReference thunk_ref,
int stack_space, int stack_space,
int32_t stack_space_offset, MemOperand* stack_space_operand,
MemOperand return_value_operand) { MemOperand return_value_operand) {
Isolate* isolate = masm->isolate(); Isolate* isolate = masm->isolate();
ExternalReference next_address = ExternalReference next_address =
...@@ -324,14 +324,19 @@ static void CallApiFunctionAndReturn(MacroAssembler* masm, ...@@ -324,14 +324,19 @@ static void CallApiFunctionAndReturn(MacroAssembler* masm,
// Leave the API exit frame. // Leave the API exit frame.
__ bind(&leave_exit_frame); __ bind(&leave_exit_frame);
if (stack_space_offset != kInvalidStackOffset) { if (stack_space_operand == nullptr) {
DCHECK_EQ(kCArgsSlotsSize, 0); CHECK_NE(stack_space, 0);
__ Ld(s0, MemOperand(sp, stack_space_offset));
} else {
__ li(s0, Operand(stack_space)); __ 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. // Check if the function scheduled an exception.
__ LoadRoot(a4, RootIndex::kTheHoleValue); __ LoadRoot(a4, RootIndex::kTheHoleValue);
...@@ -359,20 +364,32 @@ static void CallApiFunctionAndReturn(MacroAssembler* masm, ...@@ -359,20 +364,32 @@ static void CallApiFunctionAndReturn(MacroAssembler* masm,
void CallApiCallbackStub::Generate(MacroAssembler* masm) { void CallApiCallbackStub::Generate(MacroAssembler* masm) {
// ----------- S t a t e ------------- // ----------- S t a t e -------------
// -- a4 : call_data // -- cp : kTargetContext
// -- a2 : holder // -- a1 : kApiFunctionAddress
// -- a1 : api_function_address // -- a2 : kArgc
// -- cp : context
// -- // --
// -- sp[0] : last argument // -- sp[0] : last argument
// -- ... // -- ...
// -- sp[(argc - 1) * 8] : first 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 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; typedef FunctionCallbackArguments FCA;
...@@ -384,57 +401,94 @@ void CallApiCallbackStub::Generate(MacroAssembler* masm) { ...@@ -384,57 +401,94 @@ void CallApiCallbackStub::Generate(MacroAssembler* masm) {
STATIC_ASSERT(FCA::kIsolateIndex == 1); STATIC_ASSERT(FCA::kIsolateIndex == 1);
STATIC_ASSERT(FCA::kHolderIndex == 0); STATIC_ASSERT(FCA::kHolderIndex == 0);
// new target // Set up FunctionCallbackInfo's implicit_args on the stack as follows:
__ PushRoot(RootIndex::kUndefinedValue); //
// 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. // kIsolate.
__ Push(call_data); __ 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); __ LoadRoot(scratch, RootIndex::kUndefinedValue);
// Push return value and default return value. __ Sd(scratch, MemOperand(sp, 2 * kPointerSize));
__ Push(scratch, scratch); __ Sd(scratch, MemOperand(sp, 3 * kPointerSize));
__ li(scratch, ExternalReference::isolate_address(masm->isolate())); __ Sd(scratch, MemOperand(sp, 5 * kPointerSize));
// Push isolate and holder.
__ Push(scratch, holder);
// 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); __ mov(scratch, sp);
// Allocate the v8::Arguments structure in the arguments' space since // Allocate the v8::Arguments structure in the arguments' space since
// it's not controlled by GC. // 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); FrameScope frame_scope(masm, StackFrame::MANUAL);
__ EnterExitFrame(false, kApiStackSpace); __ EnterExitFrame(kDontSaveDoubles, kApiStackSpace);
DCHECK(api_function_address != a0 && scratch != a0); // EnterExitFrame may align the sp.
// a0 = FunctionCallbackInfo&
// Arguments is after the return address. // FunctionCallbackInfo::implicit_args_ (points at kHolder as set up above).
__ Daddu(a0, sp, Operand(1 * kPointerSize)); // Arguments are after the return address (pushed by EnterExitFrame()).
// FunctionCallbackInfo::implicit_args_ __ Sd(scratch, MemOperand(sp, 1 * kPointerSize));
__ Sd(scratch, MemOperand(a0, 0 * kPointerSize));
// FunctionCallbackInfo::values_ // FunctionCallbackInfo::values_ (points at the first varargs argument passed
__ Daddu(kScratchReg, scratch, // on the stack).
Operand((FCA::kArgsLength - 1 + argc()) * kPointerSize)); __ Dsubu(scratch, base, Operand(1 * kPointerSize));
__ Sd(kScratchReg, MemOperand(a0, 1 * kPointerSize)); __ Sd(scratch, MemOperand(sp, 2 * kPointerSize));
// FunctionCallbackInfo::length_ = argc
// FunctionCallbackInfo::length_.
// Stored as int field, 32-bit integers within struct on stack always left // Stored as int field, 32-bit integers within struct on stack always left
// justified by n64 ABI. // justified by n64 ABI.
__ li(kScratchReg, Operand(argc())); __ Sw(argc, MemOperand(sp, 3 * kPointerSize));
__ Sw(kScratchReg, MemOperand(a0, 2 * 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(); 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); AllowExternalCallThatCantCauseGC scope(masm);
// Stores return the first js argument. CallApiFunctionAndReturn(masm, api_function_address, thunk_ref,
int return_value_offset = 2 + FCA::kReturnValueOffset; kUseStackSpaceOperand, &stack_space_operand,
MemOperand return_value_operand(fp, return_value_offset * kPointerSize); return_value_operand);
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);
} }
...@@ -505,8 +559,9 @@ void CallApiGetterStub::Generate(MacroAssembler* masm) { ...@@ -505,8 +559,9 @@ void CallApiGetterStub::Generate(MacroAssembler* masm) {
// +3 is to skip prolog, return address and name handle. // +3 is to skip prolog, return address and name handle.
MemOperand return_value_operand( MemOperand return_value_operand(
fp, (PropertyCallbackArguments::kReturnValueOffset + 3) * kPointerSize); fp, (PropertyCallbackArguments::kReturnValueOffset + 3) * kPointerSize);
MemOperand* const kUseStackSpaceConstant = nullptr;
CallApiFunctionAndReturn(masm, api_function_address, thunk_ref, CallApiFunctionAndReturn(masm, api_function_address, thunk_ref,
kStackUnwindSpace, kInvalidStackOffset, kStackUnwindSpace, kUseStackSpaceConstant,
return_value_operand); return_value_operand);
} }
......
...@@ -203,10 +203,9 @@ void ArgumentsAdaptorDescriptor::InitializePlatformSpecific( ...@@ -203,10 +203,9 @@ void ArgumentsAdaptorDescriptor::InitializePlatformSpecific(
void ApiCallbackDescriptor::InitializePlatformSpecific( void ApiCallbackDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) { CallInterfaceDescriptorData* data) {
Register registers[] = { Register registers[] = {
JavaScriptFrame::context_register(), // callee context JavaScriptFrame::context_register(), // kTargetContext
a4, // call_data a1, // kApiFunctionAddress
a2, // holder a2, // kArgc
a1, // api_function_address
}; };
data->InitializePlatformSpecific(arraysize(registers), registers); data->InitializePlatformSpecific(arraysize(registers), registers);
} }
......
...@@ -177,16 +177,6 @@ static int Offset(ExternalReference ref0, ExternalReference ref1) { ...@@ -177,16 +177,6 @@ static int Offset(ExternalReference ref0, ExternalReference ref1) {
return static_cast<int>(offset); 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 // Calls an API function. Allocates HandleScope, extracts returned value
// from handle and propagates exceptions. Clobbers r14, r15, rbx and // from handle and propagates exceptions. Clobbers r14, r15, rbx and
// caller-save registers. Restores context. On return removes // caller-save registers. Restores context. On return removes
...@@ -345,24 +335,40 @@ static void CallApiFunctionAndReturn(MacroAssembler* masm, ...@@ -345,24 +335,40 @@ static void CallApiFunctionAndReturn(MacroAssembler* masm,
__ jmp(&leave_exit_frame); __ 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) { void CallApiCallbackStub::Generate(MacroAssembler* masm) {
// ----------- S t a t e ------------- // ----------- S t a t e -------------
// -- rbx : call_data // -- rsi : kTargetContext
// -- rcx : holder // -- rdx : kApiFunctionAddress
// -- rdx : api_function_address // -- rcx : kArgc
// -- rsi : context // --
// -- rax : number of arguments if argc is a register
// -- rsp[0] : return address // -- rsp[0] : return address
// -- rsp[8] : last argument // -- rsp[8] : last argument
// -- ... // -- ...
// -- rsp[argc * 8] : first argument // -- rsp[argc * 8] : first argument
// -- rsp[(argc + 1) * 8] : receiver // -- 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 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; typedef FunctionCallbackArguments FCA;
...@@ -374,52 +380,82 @@ void CallApiCallbackStub::Generate(MacroAssembler* masm) { ...@@ -374,52 +380,82 @@ void CallApiCallbackStub::Generate(MacroAssembler* masm) {
STATIC_ASSERT(FCA::kIsolateIndex == 1); STATIC_ASSERT(FCA::kIsolateIndex == 1);
STATIC_ASSERT(FCA::kHolderIndex == 0); STATIC_ASSERT(FCA::kHolderIndex == 0);
__ PopReturnAddressTo(return_address); // Set up FunctionCallbackInfo's implicit_args on the stack as follows:
//
// new target // Current state:
__ PushRoot(RootIndex::kUndefinedValue); // rsp[0]: return address
//
// call data // Target state:
__ Push(call_data); // rsp[0 * kPointerSize]: return address
// rsp[1 * kPointerSize]: kHolder
// return value // rsp[2 * kPointerSize]: kIsolate
__ PushRoot(RootIndex::kUndefinedValue); // rsp[3 * kPointerSize]: undefined (kReturnValueDefaultValue)
// return value default // rsp[4 * kPointerSize]: undefined (kReturnValue)
__ PushRoot(RootIndex::kUndefinedValue); // rsp[5 * kPointerSize]: kData
// isolate // rsp[6 * kPointerSize]: undefined (kNewTarget)
Register scratch = call_data;
__ Move(scratch, ExternalReference::isolate_address(masm->isolate())); // Reserve space on the stack.
__ Push(scratch); __ subp(rsp, Immediate(FCA::kArgsLength * kPointerSize));
// holder
__ Push(holder); // 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); // Keep a pointer to kHolder (= implicit_args) in a scratch register.
// Push return address back on stack. // We use it below to set up the FunctionCallbackInfo object.
__ PushReturnAddressFrom(return_address); Register scratch = rbx;
__ leap(scratch, Operand(rsp, 1 * kPointerSize));
// Allocate the v8::Arguments structure in the arguments' space since // Allocate the v8::Arguments structure in the arguments' space since
// it's not controlled by GC. // it's not controlled by GC.
const int kApiStackSpace = 3; static constexpr int kApiStackSpace = 4;
__ EnterApiExitFrame(kApiStackSpace);
PrepareCallApiFunction(masm, kApiStackSpace); // FunctionCallbackInfo::implicit_args_ (points at kHolder as set up above).
// FunctionCallbackInfo::implicit_args_.
__ movp(StackSpaceOperand(0), scratch); __ 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); __ movp(StackSpaceOperand(1), scratch);
// FunctionCallbackInfo::length_. // FunctionCallbackInfo::length_.
__ Set(StackSpaceOperand(2), argc); __ movp(StackSpaceOperand(2), argc);
#if defined(__MINGW64__) || defined(_WIN64) // We also store the number of bytes to drop from the stack after returning
Register arguments_arg = rcx; // from the API function here.
Register callback_arg = rdx; __ leaq(
#else kScratchRegister,
Register arguments_arg = rdi; Operand(argc, times_pointer_size,
Register callback_arg = rsi; (FCA::kArgsLength + kExtraStackArgumentCount) * kPointerSize));
#endif __ 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 // It's okay if api_function_address == callback_arg
// but not arguments_arg // but not arguments_arg
...@@ -430,15 +466,16 @@ void CallApiCallbackStub::Generate(MacroAssembler* masm) { ...@@ -430,15 +466,16 @@ void CallApiCallbackStub::Generate(MacroAssembler* masm) {
ExternalReference thunk_ref = ExternalReference::invoke_function_callback(); ExternalReference thunk_ref = ExternalReference::invoke_function_callback();
// Accessor for FunctionCallbackInfo and first js arg. // There are two stack slots above the arguments we constructed on the stack:
StackArgumentsAccessor args_from_rbp(rbp, FCA::kArgsLength + 1, // the stored ebp (pushed by EnterApiExitFrame), and the return address.
ARGUMENTS_DONT_CONTAIN_RECEIVER); static constexpr int kStackSlotsAboveFCA = 2;
Operand return_value_operand = args_from_rbp.GetArgumentOperand( Operand return_value_operand(
FCA::kArgsLength - FCA::kReturnValueOffset); rbp, (kStackSlotsAboveFCA + FCA::kReturnValueOffset) * kPointerSize);
const int stack_space = argc + FCA::kArgsLength + 1;
Operand* stack_space_operand = nullptr; static constexpr int kUseStackSpaceOperand = 0;
Operand stack_space_operand = StackSpaceOperand(3);
CallApiFunctionAndReturn(masm, api_function_address, thunk_ref, callback_arg, CallApiFunctionAndReturn(masm, api_function_address, thunk_ref, callback_arg,
stack_space, stack_space_operand, kUseStackSpaceOperand, &stack_space_operand,
return_value_operand); return_value_operand);
} }
...@@ -493,14 +530,15 @@ void CallApiGetterStub::Generate(MacroAssembler* masm) { ...@@ -493,14 +530,15 @@ void CallApiGetterStub::Generate(MacroAssembler* masm) {
// Load address of v8::PropertyAccessorInfo::args_ array. // Load address of v8::PropertyAccessorInfo::args_ array.
__ leap(scratch, Operand(rsp, 2 * kPointerSize)); __ leap(scratch, Operand(rsp, 2 * kPointerSize));
PrepareCallApiFunction(masm, kArgStackSpace); __ EnterApiExitFrame(kArgStackSpace);
// Create v8::PropertyCallbackInfo object on the stack and initialize // Create v8::PropertyCallbackInfo object on the stack and initialize
// it's args_ field. // it's args_ field.
Operand info_object = StackSpaceOperand(0); Operand info_object = StackSpaceOperand(0);
__ movp(info_object, scratch); __ movp(info_object, scratch);
__ leap(name_arg, Operand(scratch, -kPointerSize)); __ 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. // could be used to pass arguments.
__ leap(accessor_info_arg, info_object); __ leap(accessor_info_arg, info_object);
...@@ -518,8 +556,10 @@ void CallApiGetterStub::Generate(MacroAssembler* masm) { ...@@ -518,8 +556,10 @@ void CallApiGetterStub::Generate(MacroAssembler* masm) {
// +3 is to skip prolog, return address and name handle. // +3 is to skip prolog, return address and name handle.
Operand return_value_operand( Operand return_value_operand(
rbp, (PropertyCallbackArguments::kReturnValueOffset + 3) * kPointerSize); rbp, (PropertyCallbackArguments::kReturnValueOffset + 3) * kPointerSize);
Operand* const kUseStackSpaceConstant = nullptr;
CallApiFunctionAndReturn(masm, api_function_address, thunk_ref, getter_arg, CallApiFunctionAndReturn(masm, api_function_address, thunk_ref, getter_arg,
kStackUnwindSpace, nullptr, return_value_operand); kStackUnwindSpace, kUseStackSpaceConstant,
return_value_operand);
} }
#undef __ #undef __
......
...@@ -204,10 +204,9 @@ void ArgumentsAdaptorDescriptor::InitializePlatformSpecific( ...@@ -204,10 +204,9 @@ void ArgumentsAdaptorDescriptor::InitializePlatformSpecific(
void ApiCallbackDescriptor::InitializePlatformSpecific( void ApiCallbackDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) { CallInterfaceDescriptorData* data) {
Register registers[] = { Register registers[] = {
JavaScriptFrame::context_register(), // callee context JavaScriptFrame::context_register(), // kTargetContext
rbx, // call_data rdx, // kApiFunctionAddress
rcx, // holder rcx, // kArgc
rdx, // api_function_address
}; };
data->InitializePlatformSpecific(arraysize(registers), registers); 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