Commit c4959e25 authored by Clemens Backes's avatar Clemens Backes Committed by V8 LUCI CQ

Revert "Port Generic JS-Wasm Wrapper for arm64"

This reverts commit 5a318a23.

Reason for revert: Fails on Mac arm64: https://ci.chromium.org/ui/p/v8/builders/ci/V8%20Mac%20-%20arm64%20-%20debug/8211/overview

Original change's description:
> Port Generic JS-Wasm Wrapper for arm64
>
> Bug: v8:10701
> Change-Id: I2014f8994c74379663998e2560d1d51b98a4a9a6
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3811834
> Reviewed-by: Jakob Linke <jgruber@chromium.org>
> Commit-Queue: Ilya Rezvov <irezvov@chromium.org>
> Reviewed-by: Thibaud Michaud <thibaudm@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#82915}

Bug: v8:10701
Change-Id: I9d5f19fedb82e2be64bd313f8cf5821fb0d8c795
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3869145
Commit-Queue: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Auto-Submit: Clemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/main@{#82917}
parent f2faee85
......@@ -28,9 +28,6 @@
#if V8_ENABLE_WEBASSEMBLY
#include "src/wasm/wasm-linkage.h"
#include "src/wasm/wasm-objects.h"
#include "src/wasm/object-access.h"
#include "src/wasm/stacks.h"
#include "src/wasm/wasm-constants.h"
#endif // V8_ENABLE_WEBASSEMBLY
#if defined(V8_OS_WIN)
......@@ -3032,1005 +3029,9 @@ void Builtins::Generate_WasmDebugBreak(MacroAssembler* masm) {
__ Ret();
}
namespace {
// Helper functions for the GenericJSToWasmWrapper.
void PrepareForBuiltinCall(MacroAssembler* masm, MemOperand GCScanSlotPlace,
const int GCScanSlotCount, Register current_param,
Register param_limit,
Register current_int_param_slot,
Register current_float_param_slot,
Register valuetypes_array_ptr,
Register wasm_instance, Register function_data,
Register alignment_reg) {
UseScratchRegisterScope temps(masm);
Register GCScanCount = temps.AcquireX();
// Pushes and puts the values in order onto the stack before builtin calls for
// the GenericJSToWasmWrapper.
__ Mov(GCScanCount, GCScanSlotCount);
__ Str(GCScanCount, GCScanSlotPlace);
__ Stp(current_param, param_limit,
MemOperand(sp, -2 * kSystemPointerSize, PreIndex));
__ Stp(current_int_param_slot, current_float_param_slot,
MemOperand(sp, -2 * kSystemPointerSize, PreIndex));
__ Stp(valuetypes_array_ptr, alignment_reg,
MemOperand(sp, -2 * kSystemPointerSize, PreIndex));
__ Stp(wasm_instance, function_data,
MemOperand(sp, -2 * kSystemPointerSize, PreIndex));
// We had to prepare the parameters for the Call: we have to put the context
// into kContextRegister.
__ LoadAnyTaggedField(
kContextRegister, // cp(x27)
MemOperand(wasm_instance, wasm::ObjectAccess::ToTagged(
WasmInstanceObject::kNativeContextOffset)));
}
void RestoreAfterBuiltinCall(MacroAssembler* masm, Register function_data,
Register wasm_instance,
Register valuetypes_array_ptr,
Register current_float_param_slot,
Register current_int_param_slot,
Register param_limit, Register current_param,
Register alignment_reg) {
// Pop and load values from the stack in order into the registers after
// builtin calls for the GenericJSToWasmWrapper.
__ Ldp(wasm_instance, function_data,
MemOperand(sp, 2 * kSystemPointerSize, PostIndex));
__ Ldp(valuetypes_array_ptr, alignment_reg,
MemOperand(sp, 2 * kSystemPointerSize, PostIndex));
__ Ldp(current_int_param_slot, current_float_param_slot,
MemOperand(sp, 2 * kSystemPointerSize, PostIndex));
__ Ldp(current_param, param_limit,
MemOperand(sp, 2 * kSystemPointerSize, PostIndex));
}
void LoadFunctionDataAndWasmInstance(MacroAssembler* masm,
Register function_data,
Register wasm_instance) {
Register closure = function_data;
__ LoadAnyTaggedField(
function_data,
MemOperand(
closure,
wasm::ObjectAccess::SharedFunctionInfoOffsetInTaggedJSFunction()));
__ LoadAnyTaggedField(
function_data,
FieldMemOperand(function_data,
SharedFunctionInfo::kFunctionDataOffset));
__ LoadAnyTaggedField(
wasm_instance,
FieldMemOperand(function_data,
WasmExportedFunctionData::kInstanceOffset));
}
void LoadValueTypesArray(MacroAssembler* masm, Register function_data,
Register valuetypes_array_ptr, Register return_count,
Register param_count) {
Register signature = valuetypes_array_ptr;
__ LoadExternalPointerField(
signature,
FieldMemOperand(function_data, WasmExportedFunctionData::kSigOffset),
kWasmExportedFunctionDataSignatureTag);
__ Ldr(return_count,
MemOperand(signature, wasm::FunctionSig::kReturnCountOffset));
__ Ldr(param_count,
MemOperand(signature, wasm::FunctionSig::kParameterCountOffset));
valuetypes_array_ptr = signature;
__ Ldr(valuetypes_array_ptr,
MemOperand(signature, wasm::FunctionSig::kRepsOffset));
}
class RegisterAllocator {
public:
explicit RegisterAllocator(const CPURegList& registers)
: initial_(registers),
available_(registers) {}
void Ask(Register* reg) {
DCHECK_EQ(*reg, no_reg);
DCHECK(!available_.IsEmpty());
*reg = available_.PopLowestIndex().X();
allocated_registers_.push_back(reg);
}
void Pinned(const Register& requested, Register* reg) {
DCHECK(available_.IncludesAliasOf(requested));
*reg = requested;
Reserve(requested);
allocated_registers_.push_back(reg);
}
void Reserve(const Register& reg) {
CPURegList list(reg);
available_.Remove(list);
}
void Reserve(const CPURegList& list) {
available_.Remove(list);
}
void ResetExcept(const Register& reg1 = NoReg,
const Register& reg2 = NoReg,
const Register& reg3 = NoReg,
const Register& reg4 = NoReg,
const Register& reg5 = NoReg,
const Register& reg6 = NoReg) {
available_ = initial_;
if (reg1 != NoReg) {
available_.Remove(reg1, reg2, reg3, reg4);
}
if (reg5 != NoReg) {
available_.Remove(reg5, reg6);
}
auto it = allocated_registers_.begin();
while (it != allocated_registers_.end()) {
if (available_.IncludesAliasOf(**it)) {
**it = no_reg;
allocated_registers_.erase(it);
} else {
it++;
}
}
}
static RegisterAllocator WithAllocatableGeneralRegisters() {
CPURegList list(kXRegSizeInBits, RegList());
const RegisterConfiguration* config(RegisterConfiguration::Default());
list.set_bits(config->allocatable_general_codes_mask());
return RegisterAllocator(list);
}
private:
std::vector<Register*> allocated_registers_;
const CPURegList initial_;
CPURegList available_;
};
#define DEFINE_REG(Name) \
Register Name = no_reg; \
regs.Ask(&Name);
#define DEFINE_REG_W(Name) \
DEFINE_REG(Name); \
Name = Name.W();
#define ASSIGN_REG(Name) \
regs.Ask(&Name);
#define ASSIGN_REG_W(Name) \
ASSIGN_REG(Name); \
Name = Name.W();
#define DEFINE_PINNED(Name, Reg) \
Register Name = no_reg; \
regs.Pinned(Reg, &Name);
} // namespace
void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) {
auto regs = RegisterAllocator::WithAllocatableGeneralRegisters();
// Set up the stackframe.
__ EnterFrame(StackFrame::JS_TO_WASM);
// -------------------------------------------
// Compute offsets and prepare for GC.
// -------------------------------------------
constexpr int kGCScanSlotCountOffset =
BuiltinWasmWrapperConstants::kGCScanSlotCountOffset;
// The number of parameters passed to this function.
constexpr int kInParamCountOffset =
BuiltinWasmWrapperConstants::kInParamCountOffset;
// The number of parameters according to the signature.
constexpr int kParamCountOffset =
BuiltinWasmWrapperConstants::kParamCountOffset;
constexpr int kSuspenderOffset =
BuiltinWasmWrapperConstants::kSuspenderOffset;
constexpr int kReturnCountOffset = kSuspenderOffset - kSystemPointerSize;
constexpr int kValueTypesArrayStartOffset =
kReturnCountOffset - kSystemPointerSize;
// The number of reference parameters.
// It is used as a boolean flag to check if one of the parameters is
// a reference.
// If so, we iterate over the parameters two times, first for all value types
// and then for all references. During second iteration we store the actual
// reference params count.
constexpr int kRefParamsCountOffset =
kValueTypesArrayStartOffset - kSystemPointerSize;
// We set and use this slot only when moving parameters into the parameter
// registers (so no GC scan is needed).
constexpr int kFunctionDataOffset =
kRefParamsCountOffset - kSystemPointerSize;
constexpr int kLastSpillOffset = kFunctionDataOffset;
constexpr int kNumSpillSlots =
(-TypedFrameConstants::kFixedFrameSizeFromFp - kLastSpillOffset) >>
kSystemPointerSizeLog2;
__ Sub(sp, sp, Immediate(kNumSpillSlots * kSystemPointerSize));
// Put the in_parameter count on the stack, we only need it at the very end
// when we pop the parameters off the stack.
__ Sub(kJavaScriptCallArgCountRegister, kJavaScriptCallArgCountRegister, 1);
__ Str(kJavaScriptCallArgCountRegister, MemOperand(fp, kInParamCountOffset));
Label compile_wrapper, compile_wrapper_done;
// Load function data and check wrapper budget.
DEFINE_PINNED(function_data, kJSFunctionRegister);
DEFINE_PINNED(wasm_instance, kWasmInstanceRegister);
LoadFunctionDataAndWasmInstance(masm, function_data, wasm_instance);
// -------------------------------------------
// Decrement the budget of the generic wrapper in function data.
// -------------------------------------------
DEFINE_REG(scratch);
MemOperand budget_loc = FieldMemOperand(
function_data,
WasmExportedFunctionData::kWrapperBudgetOffset);
__ SmiUntag(scratch, budget_loc);
__ Subs(scratch, scratch, 1);
__ SmiTag(scratch);
__ Str(scratch, budget_loc);
// -------------------------------------------
// Check if the budget of the generic wrapper reached 0 (zero).
// -------------------------------------------
// Instead of a specific comparison, we can directly use the flags set
// from the previous addition.
__ B(&compile_wrapper, le);
__ bind(&compile_wrapper_done);
regs.ResetExcept(function_data, wasm_instance);
Label prepare_for_wasm_call;
// Load a signature and store on stack.
// Param should be x0 for calling Runtime in the conversion loop.
DEFINE_PINNED(param, x0);
DEFINE_REG(valuetypes_array_ptr);
DEFINE_REG(return_count);
DEFINE_REG(param_count);
// -------------------------------------------
// Load values from the signature.
// -------------------------------------------
LoadValueTypesArray(masm, function_data, valuetypes_array_ptr,
return_count, param_count);
// Initialize the {RefParamsCount} slot with 0.
__ Str(xzr, MemOperand(fp, kRefParamsCountOffset));
// -------------------------------------------
// Store signature-related values to the stack.
// -------------------------------------------
// We store values on the stack to restore them after function calls.
// We cannot push values onto the stack right before the wasm call.
// The Wasm function expects the parameters, that didn't fit into
// the registers, on the top of the stack.
__ Str(param_count, MemOperand(fp, kParamCountOffset));
__ Str(return_count, MemOperand(fp, kReturnCountOffset));
__ Str(valuetypes_array_ptr, MemOperand(fp, kValueTypesArrayStartOffset));
// -------------------------------------------
// Parameter handling.
// -------------------------------------------
__ Cmp(param_count, 0);
// IF we have 0 params: jump through parameter handling.
__ B(&prepare_for_wasm_call, eq);
// -------------------------------------------
// Create 2 sections for integer and float params.
// -------------------------------------------
// We will create 2 sections on the stack for the evaluated parameters:
// Integer and Float section, both with parameter count size. We will place
// the parameters into these sections depending on their valuetype. This
// way we can easily fill the general purpose and floating point parameter
// registers and place the remaining parameters onto the stack in proper
// order for the Wasm function. These remaining params are the final stack
// parameters for the call to WebAssembly. Example of the stack layout
// after processing 2 int and 1 float parameters when param_count is 4.
// +-----------------+
// | fp |
// |-----------------|-------------------------------
// | | Slots we defined
// | Saved values | when setting up
// | | the stack
// | |
// +-Integer section-+--- <--- start_int_section ----
// | 1st int param |
// |- - - - - - - - -|
// | 2nd int param |
// |- - - - - - - - -| <----- current_int_param_slot
// | | (points to the stackslot
// |- - - - - - - - -| where the next int param should be placed)
// | |
// +--Float section--+--- <--- start_float_section --
// | 1st float param |
// |- - - - - - - - -| <---- current_float_param_slot
// | | (points to the stackslot
// |- - - - - - - - -| where the next float param should be placed)
// | |
// |- - - - - - - - -|
// | |
// +---Final stack---+------------------------------
// +-parameters for--+------------------------------
// +-the Wasm call---+------------------------------
// | . . . |
// For Integer section.
DEFINE_REG(current_int_param_slot);
// Set the current_int_param_slot to point to the start of the section.
__ Sub(current_int_param_slot, sp, kSystemPointerSize);
DEFINE_REG(current_float_param_slot);
// Set the current_float_param_slot to point to the start of the section.
__ Sub(current_float_param_slot, current_int_param_slot,
Operand(param_count, LSL, kSystemPointerSizeLog2));
// Claim space for int and float params at once,
// to be sure sp is aligned by kSystemPointerSize << 1 = 16.
__ Sub(sp, sp, Operand(param_count, LSL, kSystemPointerSizeLog2 + 1));
// -------------------------------------------
// Set up for the param evaluation loop.
// -------------------------------------------
// We will loop through the params starting with the 1st param.
// The order of processing the params is important. We have to evaluate
// them in an increasing order.
// +-----------------+---------------
// | param n |
// |- - - - - - - - -|
// | param n-1 | Caller
// | ... | frame slots
// | param 1 |
// |- - - - - - - - -|
// | receiver |
// +-----------------+---------------
// | return addr |
// FP->|- - - - - - - - -|
// | fp | Spill slots
// |- - - - - - - - -|
//
// [current_param] gives us the parameter we are processing.
// We iterate through half-open interval <1st param, [fp + param_limit]).
DEFINE_REG(param_ptr);
constexpr int kReceiverOnStackSize = kSystemPointerSize;
__ Add(param_ptr, fp,
kFPOnStackSize + kPCOnStackSize + kReceiverOnStackSize);
DEFINE_REG(param_limit);
__ Add(param_limit, param_ptr,
Operand(param_count, LSL, kSystemPointerSizeLog2));
// We have to check the types of the params. The ValueType array contains
// first the return then the param types.
// Set the ValueType array pointer to point to the first parameter.
constexpr int kValueTypeSize = sizeof(wasm::ValueType);
static_assert(kValueTypeSize == 4);
const int32_t kValueTypeSizeLog2 = log2(kValueTypeSize);
__ Add(valuetypes_array_ptr, valuetypes_array_ptr,
Operand(return_count, LSL, kValueTypeSizeLog2));
DEFINE_REG_W(valuetype);
// -------------------------------------------
// Param evaluation loop.
// -------------------------------------------
Label loop_through_params;
__ bind(&loop_through_params);
__ Ldr(param, MemOperand(param_ptr, kSystemPointerSize, PostIndex));
__ Ldr(valuetype, MemOperand(valuetypes_array_ptr,
wasm::ValueType::bit_field_offset()));
// -------------------------------------------
// Param conversion.
// -------------------------------------------
// If param is a Smi we can easily convert it. Otherwise we'll call
// a builtin for conversion.
Label convert_param, param_conversion_done;
__ cmp(valuetype, Immediate(wasm::kWasmI32.raw_bit_field()));
__ B(&convert_param, ne);
__ JumpIfNotSmi(param, &convert_param);
// Change the paramfrom Smi to int32.
__ SmiUntag(param);
// Place the param into the proper slot in Integer section.
__ Str(param,
MemOperand(current_int_param_slot, -kSystemPointerSize, PostIndex));
__ jmp(&param_conversion_done);
// -------------------------------------------
// Param conversion builtins.
// -------------------------------------------
__ bind(&convert_param);
// The order of pushes is important. We want the heap objects,
// that should be scanned by GC, to be on the top of the stack.
// We have to set the indicating value for the GC to the number of values
// on the top of the stack that have to be scanned before calling
// the builtin function.
// The builtin expects the parameter to be in register param = x0.
constexpr int kBuiltinCallGCScanSlotCount = 2;
PrepareForBuiltinCall(masm, MemOperand(fp, kGCScanSlotCountOffset),
kBuiltinCallGCScanSlotCount, param_ptr, param_limit,
current_int_param_slot, current_float_param_slot,
valuetypes_array_ptr, wasm_instance, function_data,
fp); // We need even amount registers to be saved.
// Here we save fp as gap register.
Label param_kWasmI32_not_smi;
Label param_kWasmI64;
Label param_kWasmF32;
Label param_kWasmF64;
__ cmp(valuetype, Immediate(wasm::kWasmI32.raw_bit_field()));
__ B(&param_kWasmI32_not_smi, eq);
__ cmp(valuetype, Immediate(wasm::kWasmI64.raw_bit_field()));
__ B(&param_kWasmI64, eq);
__ cmp(valuetype, Immediate(wasm::kWasmF32.raw_bit_field()));
__ B(&param_kWasmF32, eq);
__ cmp(valuetype, Immediate(wasm::kWasmF64.raw_bit_field()));
__ B(&param_kWasmF64, eq);
// The parameter is a reference.
// We do not copy the references to the int section yet.
// Instead we will later loop over all parameters again to handle reference
// parameters. The reason is that later value type parameters may trigger a
// GC, and we cannot keep reference parameters alive then. Instead we leave
// reference parameters at their initial place on the stack and only copy
// them once no GC can happen anymore.
// As an optimization we set a flag here that indicates that we have seen a
// reference so far. If there was no reference parameter, we would not
// iterate over the parameters for a second time.
// Use param_limit as a scratch reg,
// it is going to be restored in next call anyway.
__ Mov(param_limit, Immediate(1));
__ Str(param_limit, MemOperand(fp, kRefParamsCountOffset));
RestoreAfterBuiltinCall(masm, function_data, wasm_instance,
valuetypes_array_ptr, current_float_param_slot,
current_int_param_slot, param_limit, param_ptr,
fp);
__ jmp(&param_conversion_done);
__ bind(&param_kWasmI32_not_smi);
__ Call(BUILTIN_CODE(masm->isolate(), WasmTaggedNonSmiToInt32),
RelocInfo::CODE_TARGET);
// Param is the result of the builtin.
RestoreAfterBuiltinCall(masm, function_data, wasm_instance,
valuetypes_array_ptr, current_float_param_slot,
current_int_param_slot, param_limit, param_ptr,
fp);
__ Str(param,
MemOperand(current_int_param_slot, -kSystemPointerSize, PostIndex));
__ jmp(&param_conversion_done);
__ bind(&param_kWasmI64);
__ Call(BUILTIN_CODE(masm->isolate(), BigIntToI64), RelocInfo::CODE_TARGET);
RestoreAfterBuiltinCall(masm, function_data, wasm_instance,
valuetypes_array_ptr, current_float_param_slot,
current_int_param_slot, param_limit, param_ptr,
fp);
__ Str(param,
MemOperand(current_int_param_slot, -kSystemPointerSize, PostIndex));
__ jmp(&param_conversion_done);
__ bind(&param_kWasmF32);
__ Call(BUILTIN_CODE(masm->isolate(), WasmTaggedToFloat64),
RelocInfo::CODE_TARGET);
RestoreAfterBuiltinCall(masm, function_data, wasm_instance,
valuetypes_array_ptr, current_float_param_slot,
current_int_param_slot, param_limit, param_ptr,
fp);
// Truncate float64 to float32.
__ Fcvt(s1, kFPReturnRegister0);
__ Str(s1, MemOperand(current_float_param_slot, -kSystemPointerSize,
PostIndex));
__ jmp(&param_conversion_done);
__ bind(&param_kWasmF64);
__ Call(BUILTIN_CODE(masm->isolate(), WasmTaggedToFloat64),
RelocInfo::CODE_TARGET);
RestoreAfterBuiltinCall(masm, function_data, wasm_instance,
valuetypes_array_ptr, current_float_param_slot,
current_int_param_slot, param_limit, param_ptr,
fp);
__ Str(kFPReturnRegister0,
MemOperand(current_float_param_slot, -kSystemPointerSize,
PostIndex));
__ jmp(&param_conversion_done);
// -------------------------------------------
// Param conversion done.
// -------------------------------------------
__ bind(&param_conversion_done);
__ Add(valuetypes_array_ptr, valuetypes_array_ptr, kValueTypeSize);
__ cmp(param_ptr, param_limit);
__ B(&loop_through_params, ne);
// -------------------------------------------
// Second loop to handle references.
// -------------------------------------------
// In this loop we iterate over all parameters for a second time and copy
// all reference parameters at the end of the integer parameters section.
Label ref_params_done;
// We check if we have seen a reference in the first parameter loop.
__ Ldr(param_count, MemOperand(fp, kParamCountOffset));
DEFINE_REG(ref_param_count);
__ Ldr(ref_param_count, MemOperand(fp, kRefParamsCountOffset));
__ cmp(ref_param_count, 0);
__ B(&ref_params_done, eq);
__ Mov(ref_param_count, 0);
// We re-calculate the beginning of the value-types array and the beginning
// of the parameters ({valuetypes_array_ptr} and {current_param}).
__ Ldr(valuetypes_array_ptr, MemOperand(fp, kValueTypesArrayStartOffset));
__ Ldr(return_count, MemOperand(fp, kReturnCountOffset));
__ Add(valuetypes_array_ptr, valuetypes_array_ptr,
Operand(return_count, LSL, kValueTypeSizeLog2));
__ Add(param_ptr, fp,
kFPOnStackSize + kPCOnStackSize + kReceiverOnStackSize);
__ Add(param_limit, param_ptr,
Operand(param_count, LSL, kSystemPointerSizeLog2));
Label ref_loop_through_params;
Label ref_loop_end;
// Start of the loop.
__ bind(&ref_loop_through_params);
// Load the current parameter with type.
__ Ldr(param, MemOperand(param_ptr, kSystemPointerSize, PostIndex));
__ Ldr(valuetype,
MemOperand(valuetypes_array_ptr,
wasm::ValueType::bit_field_offset()));
// Extract the ValueKind of the type, to check for kRef and kRefNull.
__ And(valuetype, valuetype, Immediate(wasm::kWasmValueKindBitsMask));
Label move_ref_to_slot;
__ cmp(valuetype, Immediate(wasm::ValueKind::kRefNull));
__ B(&move_ref_to_slot, eq);
__ cmp(valuetype, Immediate(wasm::ValueKind::kRef));
__ B(&move_ref_to_slot, eq);
__ jmp(&ref_loop_end);
// Place the param into the proper slot in Integer section.
__ bind(&move_ref_to_slot);
__ Add(ref_param_count, ref_param_count, Immediate(1));
__ Str(param,
MemOperand(current_int_param_slot, -kSystemPointerSize, PostIndex));
// Move to the next parameter.
__ bind(&ref_loop_end);
__ Add(valuetypes_array_ptr, valuetypes_array_ptr, kValueTypeSize);
// Check if we finished all parameters.
__ cmp(param_ptr, param_limit);
__ B(&ref_loop_through_params, ne);
__ Str(ref_param_count, MemOperand(fp, kRefParamsCountOffset));
__ bind(&ref_params_done);
// There is no potential GC calls after this point,
// so storing it in the spill to reuse register.
__ Str(function_data, MemOperand(fp, kFunctionDataOffset));
regs.ResetExcept(valuetypes_array_ptr, param_count, current_int_param_slot,
current_float_param_slot, wasm_instance);
// -------------------------------------------
// Allocate space on the stack for Wasm params.
// -------------------------------------------
// We have to pre-allocate stack param space before iterating them,
// because ARM64 requires SP to be aligned by 16. To comply we have
// to insert a 8 bytes gap in a case of odd amount of parameters and
// fill the slots skipping this gap. We cannot place the gap slot
// at the end, because Wasm function is expecting params from the bottom
// border of a caller frame without any gaps.
// There is one gap slot after the last spill slot.
// It is there because kNumSpillSlots + StackMarker == 9*8 bytes,
// but SP should be aligned by 16.
constexpr int kGapSlotSize = kSystemPointerSize;
constexpr int kIntegerSectionStartOffset =
kLastSpillOffset - kGapSlotSize - kSystemPointerSize;
DEFINE_REG(start_int_section);
__ Add(start_int_section, fp, kIntegerSectionStartOffset);
DEFINE_REG(start_float_section);
__ Sub(start_float_section, start_int_section,
Operand(param_count, LSL, kSystemPointerSizeLog2));
// Substract params passed in registers.
// There are 6 general purpose and 8 fp registers for parameters,
// but kIntegerSectionStartOffset is already shifted by kSystemPointerSize,
// so we should substruct (n - 1) slots.
__ Sub(start_int_section, start_int_section, 5 * kSystemPointerSize);
__ Sub(start_float_section, start_float_section, 7 * kSystemPointerSize);
// We want the current_param_slot (insertion) pointers to point at the last
// param of the section instead of the next free slot.
__ Add(current_int_param_slot, current_int_param_slot,
Immediate(kSystemPointerSize));
__ Add(current_float_param_slot, current_float_param_slot,
Immediate(kSystemPointerSize));
DEFINE_REG(args_pointer);
Label has_ints, has_floats;
// How much space int params require on stack(in bytes)?
__ Subs(args_pointer, start_int_section, current_int_param_slot);
__ B(&has_ints, gt);
// Clamp negative value to 0.
__ Mov(args_pointer, 0);
__ bind(&has_ints);
ASSIGN_REG(scratch);
// How much space float params require on stack(in bytes)?
__ Subs(scratch, start_float_section, current_float_param_slot);
__ B(&has_floats, gt);
// Clamp negative value to 0.
__ Mov(scratch, 0);
__ bind(&has_floats);
// Sum int and float stack space requirements.
__ Add(args_pointer, args_pointer, scratch);
// Round up stack space to 16 divisor.
__ Add(scratch, args_pointer, 0xF);
__ Bic(scratch, scratch, 0xF);
// Reserve space for params on stack.
__ Sub(sp, sp, scratch);
// Setup args pointer after possible gap.
// args_pointer contains num_of_stack_arguments * kSystemPointerSize.
__ Add(args_pointer, sp, args_pointer);
// Setup args_pointer to first stack param slot.
__ Sub(args_pointer, args_pointer, kSystemPointerSize);
// -------------------------------------------
// Final stack parameters loop.
// -------------------------------------------
// The parameters that didn't fit into the registers should be placed on
// the top of the stack contiguously. The interval of parameters between
// the start_section and the current_param_slot pointers define
// the remaining parameters of the section.
// We can iterate through the valuetypes array to decide from which section
// we need to push the parameter onto the top of the stack. By iterating in
// a reversed order we can easily pick the last parameter of the proper
// section. The parameter of the section is pushed on the top of the stack
// only if the interval of remaining params is not empty. This way we
// ensure that only params that didn't fit into param registers are
// pushed again.
Label loop_through_valuetypes;
Label loop_place_ref_params;
ASSIGN_REG(ref_param_count);
__ Ldr(ref_param_count, MemOperand(fp, kRefParamsCountOffset));
__ bind(&loop_place_ref_params);
__ cmp(ref_param_count, Immediate(0));
__ B(&loop_through_valuetypes, eq);
__ Cmp(start_int_section, current_int_param_slot);
// if no int or ref param remains, directly iterate valuetypes
__ B(&loop_through_valuetypes, le);
ASSIGN_REG(param);
__ Ldr(param,
MemOperand(current_int_param_slot, kSystemPointerSize, PostIndex));
__ Str(param, MemOperand(args_pointer, -kSystemPointerSize, PostIndex));
__ Sub(ref_param_count, ref_param_count, Immediate(1));
__ jmp(&loop_place_ref_params);
__ bind(&loop_through_valuetypes);
// We iterated through the valuetypes array, we are one field over the end
// in the beginning. Also, we have to decrement it in each iteration.
__ Sub(valuetypes_array_ptr, valuetypes_array_ptr, kValueTypeSize);
// Check if there are still remaining integer params.
Label continue_loop;
__ cmp(start_int_section, current_int_param_slot);
// If there are remaining integer params.
__ B(&continue_loop, gt);
// Check if there are still remaining float params.
__ cmp(start_float_section, current_float_param_slot);
// If there aren't any params remaining.
Label params_done;
__ B(&params_done, le);
__ bind(&continue_loop);
ASSIGN_REG_W(valuetype);
__ Ldr(valuetype, MemOperand(valuetypes_array_ptr,
wasm::ValueType::bit_field_offset()));
Label place_integer_param;
Label place_float_param;
__ cmp(valuetype, Immediate(wasm::kWasmI32.raw_bit_field()));
__ B(&place_integer_param, eq);
__ cmp(valuetype, Immediate(wasm::kWasmI64.raw_bit_field()));
__ B(&place_integer_param, eq);
__ cmp(valuetype, Immediate(wasm::kWasmF32.raw_bit_field()));
__ B(&place_float_param, eq);
__ cmp(valuetype, Immediate(wasm::kWasmF64.raw_bit_field()));
__ B(&place_float_param, eq);
// ref params have already been pushed, so go through directly
__ jmp(&loop_through_valuetypes);
// All other types are reference types. We can just fall through to place
// them in the integer section.
__ bind(&place_integer_param);
__ cmp(start_int_section, current_int_param_slot);
// If there aren't any integer params remaining, just floats, then go to
// the next valuetype.
__ B(&loop_through_valuetypes, le);
// Copy the param from the integer section to the actual parameter area.
__ Ldr(param,
MemOperand(current_int_param_slot, kSystemPointerSize, PostIndex));
__ Str(param, MemOperand(args_pointer, -kSystemPointerSize, PostIndex));
__ jmp(&loop_through_valuetypes);
__ bind(&place_float_param);
__ cmp(start_float_section, current_float_param_slot);
// If there aren't any float params remaining, just integers, then go to
// the next valuetype.
__ B(&loop_through_valuetypes, le);
// Copy the param from the float section to the actual parameter area.
__ Ldr(param,
MemOperand(current_float_param_slot, kSystemPointerSize, PostIndex));
__ Str(param, MemOperand(args_pointer, -kSystemPointerSize, PostIndex));
__ jmp(&loop_through_valuetypes);
__ bind(&params_done);
regs.ResetExcept(wasm_instance, param_count);
// -------------------------------------------
// Move the parameters into the proper param registers.
// -------------------------------------------
// Exclude param registers from the register registry.
regs.Reserve(CPURegList(x0, x2, x3, x4, x5, x6));
DEFINE_PINNED(function_entry, x1);
ASSIGN_REG(start_int_section);
__ Add(start_int_section, fp, kIntegerSectionStartOffset);
ASSIGN_REG(start_float_section);
__ Sub(start_float_section, start_int_section,
Operand(param_count, LSL, kSystemPointerSizeLog2));
// Arm64 simulator checks access below SP, so allocate some
// extra space to make it happy during filling registers,
// when we have less slots than param registers.
__ Sub(sp, sp, 8 * kSystemPointerSize);
// Fill the FP param registers.
__ Ldr(d0, MemOperand(start_float_section, 0));
__ Ldr(d1, MemOperand(start_float_section, -kSystemPointerSize));
__ Ldr(d2, MemOperand(start_float_section, -2 * kSystemPointerSize));
__ Ldr(d3, MemOperand(start_float_section, -3 * kSystemPointerSize));
__ Ldr(d4, MemOperand(start_float_section, -4 * kSystemPointerSize));
__ Ldr(d5, MemOperand(start_float_section, -5 * kSystemPointerSize));
__ Ldr(d6, MemOperand(start_float_section, -6 * kSystemPointerSize));
__ Ldr(d7, MemOperand(start_float_section, -7 * kSystemPointerSize));
// Fill the GP param registers.
__ Ldr(x0, MemOperand(start_int_section, 0));
__ Ldr(x2, MemOperand(start_int_section, -kSystemPointerSize));
__ Ldr(x3, MemOperand(start_int_section, -2 * kSystemPointerSize));
__ Ldr(x4, MemOperand(start_int_section, -3 * kSystemPointerSize));
__ Ldr(x5, MemOperand(start_int_section, -4 * kSystemPointerSize));
__ Ldr(x6, MemOperand(start_int_section, -5 * kSystemPointerSize));
// Restore SP to previous state.
__ Add(sp, sp, 8 * kSystemPointerSize);
// If we jump through 0 params shortcut, then function_data is live in x1.
// In regular flow we need to repopulate it from the spill slot.
DCHECK_EQ(function_data, no_reg);
function_data = function_entry;
__ Ldr(function_data, MemOperand(fp, kFunctionDataOffset));
__ bind(&prepare_for_wasm_call);
// -------------------------------------------
// Prepare for the Wasm call.
// -------------------------------------------
// Set thread_in_wasm_flag.
DEFINE_REG(thread_in_wasm_flag_addr);
__ Ldr(
thread_in_wasm_flag_addr,
MemOperand(kRootRegister,
Isolate::thread_in_wasm_flag_address_offset()));
ASSIGN_REG(scratch);
__ Mov(scratch, 1);
__ Str(scratch, MemOperand(thread_in_wasm_flag_addr, 0));
__ LoadAnyTaggedField(
function_entry,
FieldMemOperand(function_data,
WasmExportedFunctionData::kInternalOffset));
function_data = no_reg;
__ LoadExternalPointerField(
function_entry,
FieldMemOperand(function_entry,
WasmInternalFunction::kCallTargetOffset),
kWasmInternalFunctionCallTargetTag);
// We set the indicating value for the GC to the proper one for Wasm call.
__ Str(xzr, MemOperand(fp, kGCScanSlotCountOffset));
// -------------------------------------------
// Call the Wasm function.
// -------------------------------------------
__ Call(function_entry);
// -------------------------------------------
// Resetting after the Wasm call.
// -------------------------------------------
// Restore rsp to free the reserved stack slots for the sections.
__ Add(sp, fp, kLastSpillOffset - kSystemPointerSize);
// Unset thread_in_wasm_flag.
__ Ldr(
thread_in_wasm_flag_addr,
MemOperand(kRootRegister,
Isolate::thread_in_wasm_flag_address_offset()));
__ Str(xzr, MemOperand(thread_in_wasm_flag_addr, 0));
regs.ResetExcept();
// -------------------------------------------
// Return handling.
// -------------------------------------------
DEFINE_PINNED(return_reg, kReturnRegister0); // x0
ASSIGN_REG(return_count);
__ Ldr(return_count, MemOperand(fp, kReturnCountOffset));
// If we have 1 return value, then jump to conversion.
__ cmp(return_count, 1);
Label convert_return;
__ B(&convert_return, eq);
// Otherwise load undefined.
__ LoadRoot(return_reg, RootIndex::kUndefinedValue);
Label return_done;
__ bind(&return_done);
ASSIGN_REG(param_count);
__ Ldr(param_count, MemOperand(fp, kParamCountOffset));
// Calculate the number of parameters we have to pop off the stack. This
// number is max(in_param_count, param_count).
DEFINE_REG(in_param_count);
__ Ldr(in_param_count, MemOperand(fp, kInParamCountOffset));
__ cmp(param_count, in_param_count);
__ csel(param_count, in_param_count, param_count, lt);
// -------------------------------------------
// Deconstrunct the stack frame.
// -------------------------------------------
__ LeaveFrame(StackFrame::JS_TO_WASM);
// We have to remove the caller frame slots:
// - JS arguments
// - the receiver
// and transfer the control to the return address (the return address is
// expected to be on the top of the stack).
// We cannot use just the ret instruction for this, because we cannot pass
// the number of slots to remove in a Register as an argument.
__ DropArguments(param_count, TurboAssembler::kCountExcludesReceiver);
__ Ret(lr);
// -------------------------------------------
// Return conversions.
// -------------------------------------------
__ bind(&convert_return);
// We have to make sure that the kGCScanSlotCount is set correctly when we
// call the builtins for conversion. For these builtins it's the same as
// for the Wasm call, that is, kGCScanSlotCount = 0, so we don't have to
// reset it. We don't need the JS context for these builtin calls.
ASSIGN_REG(valuetypes_array_ptr);
__ Ldr(valuetypes_array_ptr, MemOperand(fp, kValueTypesArrayStartOffset));
// The first valuetype of the array is the return's valuetype.
ASSIGN_REG_W(valuetype);
__ Ldr(valuetype,
MemOperand(valuetypes_array_ptr,
wasm::ValueType::bit_field_offset()));
Label return_kWasmI32;
Label return_kWasmI64;
Label return_kWasmF32;
Label return_kWasmF64;
Label return_kWasmFuncRef;
__ cmp(valuetype, Immediate(wasm::kWasmI32.raw_bit_field()));
__ B(&return_kWasmI32, eq);
__ cmp(valuetype, Immediate(wasm::kWasmI64.raw_bit_field()));
__ B(&return_kWasmI64, eq);
__ cmp(valuetype, Immediate(wasm::kWasmF32.raw_bit_field()));
__ B(&return_kWasmF32, eq);
__ cmp(valuetype, Immediate(wasm::kWasmF64.raw_bit_field()));
__ B(&return_kWasmF64, eq);
// kWasmFuncRef is not representable as a cmp immediate operand.
ASSIGN_REG_W(scratch);
__ Mov(scratch, Immediate(wasm::kWasmFuncRef.raw_bit_field()));
__ cmp(valuetype, scratch);
__ B(&return_kWasmFuncRef, eq);
// All types that are not SIMD are reference types.
__ cmp(valuetype, Immediate(wasm::kWasmS128.raw_bit_field()));
// References can be passed to JavaScript as is.
__ B(&return_done, ne);
__ bind(&return_kWasmI32);
Label to_heapnumber;
// If pointer compression is disabled, we can convert the return to a smi.
if (SmiValuesAre32Bits()) {
__ SmiTag(return_reg);
} else {
__ Mov(scratch, return_reg.W());
// Double the return value to test if it can be a Smi.
__ Adds(scratch, scratch, return_reg.W());
// If there was overflow, convert the return value to a HeapNumber.
__ B(&to_heapnumber, vs);
// If there was no overflow, we can convert to Smi.
__ SmiTag(return_reg);
}
__ jmp(&return_done);
// Handle the conversion of the I32 return value to HeapNumber when it
// cannot be a smi.
__ bind(&to_heapnumber);
__ Call(BUILTIN_CODE(masm->isolate(), WasmInt32ToHeapNumber),
RelocInfo::CODE_TARGET);
__ jmp(&return_done);
__ bind(&return_kWasmI64);
__ Call(BUILTIN_CODE(masm->isolate(), I64ToBigInt),
RelocInfo::CODE_TARGET);
__ jmp(&return_done);
__ bind(&return_kWasmF32);
__ Call(BUILTIN_CODE(masm->isolate(), WasmFloat32ToNumber),
RelocInfo::CODE_TARGET);
__ jmp(&return_done);
__ bind(&return_kWasmF64);
__ Call(BUILTIN_CODE(masm->isolate(), WasmFloat64ToNumber),
RelocInfo::CODE_TARGET);
__ jmp(&return_done);
__ bind(&return_kWasmFuncRef);
__ Call(BUILTIN_CODE(masm->isolate(), WasmFuncRefToJS),
RelocInfo::CODE_TARGET);
__ jmp(&return_done);
regs.ResetExcept();
// --------------------------------------------------------------------------
// Deferred code.
// --------------------------------------------------------------------------
// -------------------------------------------
// Kick off compilation.
// -------------------------------------------
__ bind(&compile_wrapper);
{
// Enable GC.
MemOperand GCScanSlotPlace = MemOperand(fp, kGCScanSlotCountOffset);
ASSIGN_REG(scratch);
__ Mov(scratch, 4);
__ Str(scratch, GCScanSlotPlace);
// These register are live and pinned to the same values
// at the place of jumping to this deffered code.
DEFINE_PINNED(function_data, kJSFunctionRegister);
DEFINE_PINNED(wasm_instance, kWasmInstanceRegister);
__ Stp(wasm_instance, function_data,
MemOperand(sp, -2 * kSystemPointerSize, PreIndex));
// Push the arguments for the runtime call.
__ Push(wasm_instance, function_data);
// Set up context.
__ Move(kContextRegister, Smi::zero());
// Call the runtime function that kicks off compilation.
__ CallRuntime(Runtime::kWasmCompileWrapper, 2);
__ Ldp(wasm_instance, function_data,
MemOperand(sp, 2 * kSystemPointerSize, PostIndex));
__ jmp(&compile_wrapper_done);
}
// TODO(v8:10701): Implement for this platform.
__ Trap();
}
void Builtins::Generate_WasmReturnPromiseOnSuspend(MacroAssembler* masm) {
......
......@@ -2842,35 +2842,59 @@ void TurboAssembler::Prologue() {
void TurboAssembler::EnterFrame(StackFrame::Type type) {
UseScratchRegisterScope temps(this);
if (StackFrame::IsJavaScript(type)) {
// Just push a minimal "machine frame", saving the frame pointer and return
// address, without any markers.
Push<TurboAssembler::kSignLR>(lr, fp);
Mov(fp, sp);
// sp[1] : lr
// sp[0] : fp
} else {
if (type == StackFrame::INTERNAL
#if V8_ENABLE_WEBASSEMBLY
|| type == StackFrame::WASM_DEBUG_BREAK
#endif // V8_ENABLE_WEBASSEMBLY
) {
Register type_reg = temps.AcquireX();
Mov(type_reg, StackFrame::TypeToMarker(type));
Register fourth_reg = no_reg;
if (type == StackFrame::CONSTRUCT) {
fourth_reg = cp;
Push<TurboAssembler::kSignLR>(lr, fp, type_reg, padreg);
const int kFrameSize =
TypedFrameConstants::kFixedFrameSizeFromFp + kSystemPointerSize;
Add(fp, sp, kFrameSize);
// sp[3] : lr
// sp[2] : fp
// sp[1] : type
// sp[0] : for alignment
#if V8_ENABLE_WEBASSEMBLY
} else if (type == StackFrame::WASM ||
type == StackFrame::WASM_COMPILE_LAZY ||
type == StackFrame::WASM_EXIT) {
fourth_reg = kWasmInstanceRegister;
Register type_reg = temps.AcquireX();
Mov(type_reg, StackFrame::TypeToMarker(type));
Push<TurboAssembler::kSignLR>(lr, fp);
Mov(fp, sp);
Push(type_reg, kWasmInstanceRegister);
// sp[3] : lr
// sp[2] : fp
// sp[1] : type
// sp[0] : wasm instance
#endif // V8_ENABLE_WEBASSEMBLY
} else {
fourth_reg = padreg;
}
Push<TurboAssembler::kSignLR>(lr, fp, type_reg, fourth_reg);
static constexpr int kSPToFPDelta = 2 * kSystemPointerSize;
Add(fp, sp, kSPToFPDelta);
} else if (type == StackFrame::CONSTRUCT) {
Register type_reg = temps.AcquireX();
Mov(type_reg, StackFrame::TypeToMarker(type));
// Users of this frame type push a context pointer after the type field,
// so do it here to keep the stack pointer aligned.
Push<TurboAssembler::kSignLR>(lr, fp, type_reg, cp);
// The context pointer isn't part of the fixed frame, so add an extra slot
// to account for it.
Add(fp, sp,
TypedFrameConstants::kFixedFrameSizeFromFp + kSystemPointerSize);
// sp[3] : lr
// sp[2] : fp
// sp[1] : type
// sp[0] : cp | wasm instance | for alignment
// sp[0] : cp
} else {
DCHECK(StackFrame::IsJavaScript(type));
// Just push a minimal "machine frame", saving the frame pointer and return
// address, without any markers.
Push<TurboAssembler::kSignLR>(lr, fp);
Mov(fp, sp);
// sp[1] : lr
// sp[0] : fp
}
}
......
......@@ -959,10 +959,6 @@ DEFINE_VALUE_IMPLICATION(optimize_for_size, max_semi_space_size, size_t{1})
DEFINE_BOOL(wasm_generic_wrapper, true,
"allow use of the generic js-to-wasm wrapper instead of "
"per-signature wrappers")
DEFINE_BOOL(enable_wasm_arm64_generic_wrapper, false,
"allow use of the generic js-to-wasm wrapper instead of "
"per-signature wrappers on arm64")
DEFINE_WEAK_IMPLICATION(future, enable_wasm_arm64_generic_wrapper)
DEFINE_BOOL(expose_wasm, true, "expose wasm interface to JavaScript")
DEFINE_INT(wasm_num_compilation_tasks, 128,
"maximum number of parallel compilation tasks for wasm")
......
......@@ -169,12 +169,7 @@ void WasmCompilationUnit::CompileWasmFunction(Isolate* isolate,
namespace {
bool UseGenericWrapper(const FunctionSig* sig) {
#if V8_TARGET_ARCH_ARM64
if (!v8_flags.enable_wasm_arm64_generic_wrapper) {
return false;
}
#endif
#if (V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_ARM64)
#if V8_TARGET_ARCH_X64
if (sig->returns().size() > 1) {
return false;
}
......
......@@ -17,7 +17,7 @@ namespace test_run_wasm_wrappers {
using testing::CompileAndInstantiateForTesting;
#if (V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_ARM64)
#ifdef V8_TARGET_ARCH_X64
namespace {
Handle<WasmInstanceObject> CompileModule(Zone* zone, Isolate* isolate,
WasmModuleBuilder* builder) {
......@@ -69,9 +69,6 @@ TEST(WrapperBudget) {
// This test assumes use of the generic wrapper.
FlagScope<bool> use_wasm_generic_wrapper(&v8_flags.wasm_generic_wrapper,
true);
FlagScope<bool> use_enable_wasm_arm64_generic_wrapper(
&v8_flags.enable_wasm_arm64_generic_wrapper,
true);
// Initialize the environment and create a module builder.
AccountingAllocator allocator;
......@@ -119,9 +116,6 @@ TEST(WrapperReplacement) {
// This test assumes use of the generic wrapper.
FlagScope<bool> use_wasm_generic_wrapper(&v8_flags.wasm_generic_wrapper,
true);
FlagScope<bool> use_enable_wasm_arm64_generic_wrapper(
&v8_flags.enable_wasm_arm64_generic_wrapper,
true);
// Initialize the environment and create a module builder.
AccountingAllocator allocator;
......@@ -191,9 +185,6 @@ TEST(EagerWrapperReplacement) {
// This test assumes use of the generic wrapper.
FlagScope<bool> use_wasm_generic_wrapper(&v8_flags.wasm_generic_wrapper,
true);
FlagScope<bool> use_enable_wasm_arm64_generic_wrapper(
&v8_flags.enable_wasm_arm64_generic_wrapper,
true);
// Initialize the environment and create a module builder.
AccountingAllocator allocator;
......@@ -299,9 +290,6 @@ TEST(WrapperReplacement_IndirectExport) {
// This test assumes use of the generic wrapper.
FlagScope<bool> use_wasm_generic_wrapper(&v8_flags.wasm_generic_wrapper,
true);
FlagScope<bool> use_enable_wasm_arm64_generic_wrapper(
&v8_flags.enable_wasm_arm64_generic_wrapper,
true);
// Initialize the environment and create a module builder.
AccountingAllocator allocator;
......
......@@ -3,7 +3,6 @@
// found in the LICENSE file.
// Flags: --wasm-generic-wrapper --expose-gc --allow-natives-syntax
// Flags: --enable-wasm-arm64-generic-wrapper
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
......
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