[wasm] Use a tuple as the instance for JS imports

This CL refactors the implementation of WASM->JS import wrappers in order
to make the wrapper code shareable. Instead of specializing to the import
index, we use a tuple as the object ref in the both the import and indirect
tables. The tuple allows the wrapper code to load both the calling
instance and the target callable, rather than relying on code specialization.

This requires some tricky codegen machinery, because WASM call descriptors
expect an instance argument in a given register, yet the wrappers receive
a tuple, the code generator must generate a prologue that loads the
instance (and the callable), since it is not possible to express this at
the graph level.

R=mstarzinger@chromium.org
CC=clemensh@chromium.org

Change-Id: Id67e307f7f5089e776f5439a53b5aee4b76934b6
Reviewed-on: https://chromium-review.googlesource.com/c/1268237
Commit-Queue: Ben Titzer <titzer@chromium.org>
Reviewed-by: Michael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56520}
This commit is contained in:
Ben L. Titzer 2018-10-10 11:40:02 +02:00 committed by Commit Bot
parent ce9198e492
commit a2b3480611
30 changed files with 445 additions and 318 deletions

View File

@ -3024,6 +3024,16 @@ void CodeGenerator::AssembleConstructFrame() {
__ StubPrologue(info()->GetOutputStackFrameType());
if (call_descriptor->IsWasmFunctionCall()) {
__ Push(kWasmInstanceRegister);
} else if (call_descriptor->IsWasmImportWrapper()) {
// WASM import wrappers are passed a tuple in the place of the instance.
// Unpack the tuple into the instance and the target callable.
// This must be done here in the codegen because it cannot be expressed
// properly in the graph.
__ ldr(kJSFunctionRegister,
FieldMemOperand(kWasmInstanceRegister, Tuple2::kValue2Offset));
__ ldr(kWasmInstanceRegister,
FieldMemOperand(kWasmInstanceRegister, Tuple2::kValue1Offset));
__ Push(kWasmInstanceRegister);
}
}

View File

@ -2494,6 +2494,20 @@ void CodeGenerator::AssembleConstructFrame() {
__ Str(kWasmInstanceRegister,
MemOperand(fp, WasmCompiledFrameConstants::kWasmInstanceOffset));
} break;
case CallDescriptor::kCallWasmImportWrapper: {
UseScratchRegisterScope temps(tasm());
__ ldr(kJSFunctionRegister,
FieldMemOperand(kWasmInstanceRegister, Tuple2::kValue2Offset));
__ ldr(kWasmInstanceRegister,
FieldMemOperand(kWasmInstanceRegister, Tuple2::kValue1Offset));
__ Claim(shrink_slots + 2); // Claim extra slots for marker + instance.
Register scratch = temps.AcquireX();
__ Mov(scratch,
StackFrame::TypeToMarker(info()->GetOutputStackFrameType()));
__ Str(scratch, MemOperand(fp, TypedFrameConstants::kFrameTypeOffset));
__ Str(kWasmInstanceRegister,
MemOperand(fp, WasmCompiledFrameConstants::kWasmInstanceOffset));
} break;
case CallDescriptor::kCallAddress:
__ Claim(shrink_slots);
break;

View File

@ -4261,6 +4261,18 @@ void CodeGenerator::AssembleConstructFrame() {
__ StubPrologue(info()->GetOutputStackFrameType());
if (call_descriptor->IsWasmFunctionCall()) {
__ push(kWasmInstanceRegister);
} else if (call_descriptor->IsWasmImportWrapper()) {
// WASM import wrappers are passed a tuple in the place of the instance.
// Unpack the tuple into the instance and the target callable.
// This must be done here in the codegen because it cannot be expressed
// properly in the graph.
__ mov(kJSFunctionRegister,
Operand(kWasmInstanceRegister,
Tuple2::kValue2Offset - kHeapObjectTag));
__ mov(kWasmInstanceRegister,
Operand(kWasmInstanceRegister,
Tuple2::kValue1Offset - kHeapObjectTag));
__ push(kWasmInstanceRegister);
}
}
}

View File

@ -911,6 +911,7 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer,
: g.UseRegister(callee));
break;
case CallDescriptor::kCallWasmFunction:
case CallDescriptor::kCallWasmImportWrapper:
buffer->instruction_args.push_back(
(call_address_immediate &&
(callee->opcode() == IrOpcode::kRelocatableInt64Constant ||
@ -2609,6 +2610,7 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
opcode = kArchCallJSFunction | MiscField::encode(flags);
break;
case CallDescriptor::kCallWasmFunction:
case CallDescriptor::kCallWasmImportWrapper:
opcode = kArchCallWasmFunction | MiscField::encode(flags);
break;
}

View File

@ -38,7 +38,10 @@ std::ostream& operator<<(std::ostream& os, const CallDescriptor::Kind& k) {
os << "Addr";
break;
case CallDescriptor::kCallWasmFunction:
os << "Wasm";
os << "WasmFunction";
break;
case CallDescriptor::kCallWasmImportWrapper:
os << "WasmImportWrapper";
break;
}
return os;
@ -129,6 +132,7 @@ int CallDescriptor::CalculateFixedFrameSize() const {
case kCallCodeObject:
return TypedFrameConstants::kFixedSlotCount;
case kCallWasmFunction:
case kCallWasmImportWrapper:
return WasmCompiledFrameConstants::kFixedSlotCount;
}
UNREACHABLE();

View File

@ -169,10 +169,11 @@ class V8_EXPORT_PRIVATE CallDescriptor final
public:
// Describes the kind of this call, which determines the target.
enum Kind {
kCallCodeObject, // target is a Code object
kCallJSFunction, // target is a JSFunction object
kCallAddress, // target is a machine pointer
kCallWasmFunction // target is a wasm function
kCallCodeObject, // target is a Code object
kCallJSFunction, // target is a JSFunction object
kCallAddress, // target is a machine pointer
kCallWasmFunction, // target is a wasm function
kCallWasmImportWrapper // target is a wasm import wrapper
};
enum Flag {
@ -227,6 +228,9 @@ class V8_EXPORT_PRIVATE CallDescriptor final
// Returns {true} if this descriptor is a call to a WebAssembly function.
bool IsWasmFunctionCall() const { return kind_ == kCallWasmFunction; }
// Returns {true} if this descriptor is a call to a WebAssembly function.
bool IsWasmImportWrapper() const { return kind_ == kCallWasmImportWrapper; }
bool RequiresFrameAsIncoming() const {
return IsCFunctionCall() || IsJSFunctionCall() || IsWasmFunctionCall();
}

View File

@ -3299,6 +3299,16 @@ void CodeGenerator::AssembleConstructFrame() {
__ StubPrologue(info()->GetOutputStackFrameType());
if (call_descriptor->IsWasmFunctionCall()) {
__ Push(kWasmInstanceRegister);
} else if (call_descriptor->IsWasmImportWrapper()) {
// WASM import wrappers are passed a tuple in the place of the instance.
// Unpack the tuple into the instance and the target callable.
// This must be done here in the codegen because it cannot be expressed
// properly in the graph.
__ lw(kJSFunctionRegister,
FieldMemOperand(kWasmInstanceRegister, Tuple2::kValue2Offset));
__ lw(kWasmInstanceRegister,
FieldMemOperand(kWasmInstanceRegister, Tuple2::kValue1Offset));
__ Push(kWasmInstanceRegister);
}
}
}

View File

@ -3543,6 +3543,16 @@ void CodeGenerator::AssembleConstructFrame() {
__ StubPrologue(info()->GetOutputStackFrameType());
if (call_descriptor->IsWasmFunctionCall()) {
__ Push(kWasmInstanceRegister);
} else if (call_descriptor->IsWasmImportWrapper()) {
// WASM import wrappers are passed a tuple in the place of the instance.
// Unpack the tuple into the instance and the target callable.
// This must be done here in the codegen because it cannot be expressed
// properly in the graph.
__ ld(kJSFunctionRegister,
FieldMemOperand(kWasmInstanceRegister, Tuple2::kValue2Offset));
__ ld(kWasmInstanceRegister,
FieldMemOperand(kWasmInstanceRegister, Tuple2::kValue1Offset));
__ Push(kWasmInstanceRegister);
}
}
}

View File

@ -2396,6 +2396,16 @@ void CodeGenerator::AssembleConstructFrame() {
__ StubPrologue(type);
if (call_descriptor->IsWasmFunctionCall()) {
__ Push(kWasmInstanceRegister);
} else if (call_descriptor->IsWasmImportWrapper()) {
// WASM import wrappers are passed a tuple in the place of the instance.
// Unpack the tuple into the instance and the target callable.
// This must be done here in the codegen because it cannot be expressed
// properly in the graph.
__ LoadP(kJSFunctionRegister,
FieldMemOperand(kWasmInstanceRegister, Tuple2::kValue2Offset));
__ LoadP(kWasmInstanceRegister,
FieldMemOperand(kWasmInstanceRegister, Tuple2::kValue1Offset));
__ Push(kWasmInstanceRegister);
}
}
}

View File

@ -3581,6 +3581,8 @@ void CodeGenerator::AssembleConstructFrame() {
__ StubPrologue(type);
if (call_descriptor->IsWasmFunctionCall()) {
__ Push(kWasmInstanceRegister);
} else if (call_descriptor->IsWasmImportWrapper()) {
UNIMPLEMENTED(); // TODO(s390): wasm import wrapper prologue
}
}
}

View File

@ -2596,10 +2596,10 @@ Node* WasmGraphBuilder::BuildImportCall(wasm::FunctionSig* sig, Node** args,
Node*** rets,
wasm::WasmCodePosition position,
int func_index) {
// Load the instance from the imported_instances array at a known offset.
Node* imported_instances = LOAD_INSTANCE_FIELD(ImportedFunctionInstances,
MachineType::TaggedPointer());
Node* instance_node = LOAD_FIXED_ARRAY_SLOT(imported_instances, func_index);
// Load the imported function refs array from the instance.
Node* imported_function_refs =
LOAD_INSTANCE_FIELD(ImportedFunctionRefs, MachineType::TaggedPointer());
Node* ref_node = LOAD_FIXED_ARRAY_SLOT(imported_function_refs, func_index);
// Load the target from the imported_targets array at a known offset.
Node* imported_targets =
@ -2609,7 +2609,7 @@ Node* WasmGraphBuilder::BuildImportCall(wasm::FunctionSig* sig, Node** args,
mcgraph()->Int32Constant(func_index * kPointerSize), Effect(),
Control()));
args[0] = target_node;
return BuildWasmCall(sig, args, rets, position, instance_node,
return BuildWasmCall(sig, args, rets, position, ref_node,
untrusted_code_mitigations_ ? kRetpoline : kNoRetpoline);
}
@ -2617,18 +2617,18 @@ Node* WasmGraphBuilder::BuildImportCall(wasm::FunctionSig* sig, Node** args,
Node*** rets,
wasm::WasmCodePosition position,
Node* func_index) {
// Load the instance from the imported_instances array.
Node* imported_instances = LOAD_INSTANCE_FIELD(ImportedFunctionInstances,
MachineType::TaggedPointer());
// Load the imported function refs array from the instance.
Node* imported_function_refs =
LOAD_INSTANCE_FIELD(ImportedFunctionRefs, MachineType::TaggedPointer());
// Access fixed array at {header_size - tag + func_index * kPointerSize}.
Node* imported_instances_data = graph()->NewNode(
mcgraph()->machine()->IntAdd(), imported_instances,
mcgraph()->machine()->IntAdd(), imported_function_refs,
mcgraph()->IntPtrConstant(
wasm::ObjectAccess::ElementOffsetInTaggedFixedArray(0)));
Node* func_index_times_pointersize = graph()->NewNode(
mcgraph()->machine()->IntMul(), Uint32ToUintptr(func_index),
mcgraph()->Int32Constant(kPointerSize));
Node* instance_node = SetEffect(
Node* ref_node = SetEffect(
graph()->NewNode(mcgraph()->machine()->Load(MachineType::TaggedPointer()),
imported_instances_data, func_index_times_pointersize,
Effect(), Control()));
@ -2641,7 +2641,7 @@ Node* WasmGraphBuilder::BuildImportCall(wasm::FunctionSig* sig, Node** args,
mcgraph()->machine()->Load(MachineType::Pointer()), imported_targets,
func_index_times_pointersize, Effect(), Control()));
args[0] = target_node;
return BuildWasmCall(sig, args, rets, position, instance_node,
return BuildWasmCall(sig, args, rets, position, ref_node,
untrusted_code_mitigations_ ? kRetpoline : kNoRetpoline);
}
@ -2713,7 +2713,7 @@ Node* WasmGraphBuilder::CallIndirect(uint32_t sig_index, Node** args,
Node* ift_targets =
LOAD_INSTANCE_FIELD(IndirectFunctionTableTargets, MachineType::Pointer());
Node* ift_instances = LOAD_INSTANCE_FIELD(IndirectFunctionTableInstances,
Node* ift_instances = LOAD_INSTANCE_FIELD(IndirectFunctionTableRefs,
MachineType::TaggedPointer());
scaled_key = graph()->NewNode(machine->Word32Shl(), key,
@ -4502,13 +4502,12 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
Return(jsval);
}
bool BuildWasmImportCallWrapper(WasmImportCallKind kind, int func_index) {
bool BuildWasmImportCallWrapper(WasmImportCallKind kind) {
int wasm_count = static_cast<int>(sig_->parameter_count());
// Build the start and the parameter nodes.
SetEffect(SetControl(Start(wasm_count + 3)));
SetEffect(SetControl(Start(wasm_count + 4)));
// Create the instance_node from the passed parameter.
instance_node_.set(Param(wasm::kWasmInstanceParameterIndex));
Node* native_context =
@ -4526,9 +4525,9 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
return false;
}
Node* callables_node = LOAD_INSTANCE_FIELD(ImportedFunctionCallables,
MachineType::TaggedPointer());
Node* callable_node = LOAD_FIXED_ARRAY_SLOT(callables_node, func_index);
// The callable is passed as the last parameter, after WASM arguments.
Node* callable_node = Param(wasm_count + 1);
Node* undefined_node =
LOAD_INSTANCE_FIELD(UndefinedValue, MachineType::TaggedPointer());
@ -4765,16 +4764,17 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
// Create parameter nodes (offset by 1 for the receiver parameter).
Node* code_entry = Param(CWasmEntryParameters::kCodeEntry + 1);
Node* instance_node = Param(CWasmEntryParameters::kWasmInstance + 1);
Node* object_ref_node = Param(CWasmEntryParameters::kObjectRef + 1);
Node* arg_buffer = Param(CWasmEntryParameters::kArgumentsBuffer + 1);
int wasm_arg_count = static_cast<int>(sig_->parameter_count());
int arg_count = wasm_arg_count + 4; // code, instance_node, control, effect
int arg_count =
wasm_arg_count + 4; // code, object_ref_node, control, effect
Node** args = Buffer(arg_count);
int pos = 0;
args[pos++] = code_entry;
args[pos++] = instance_node;
args[pos++] = object_ref_node;
int offset = 0;
for (wasm::ValueType type : sig_->parameters()) {
@ -4953,8 +4953,7 @@ WasmImportCallKind GetWasmImportCallKind(Handle<JSReceiver> target,
MaybeHandle<Code> CompileWasmImportCallWrapper(
Isolate* isolate, WasmImportCallKind kind, wasm::FunctionSig* sig,
uint32_t index, wasm::ModuleOrigin origin,
wasm::UseTrapHandler use_trap_handler) {
wasm::ModuleOrigin origin, wasm::UseTrapHandler use_trap_handler) {
DCHECK_NE(WasmImportCallKind::kLinkError, kind);
DCHECK_NE(WasmImportCallKind::kWasmToWasm, kind);
@ -4987,18 +4986,19 @@ MaybeHandle<Code> CompileWasmImportCallWrapper(
StubCallMode::kCallWasmRuntimeStub);
builder.set_control_ptr(&control);
builder.set_effect_ptr(&effect);
builder.BuildWasmImportCallWrapper(kind, index);
builder.BuildWasmImportCallWrapper(kind);
EmbeddedVector<char, 32> func_name;
func_name.Truncate(SNPrintF(func_name, "wasm-to-js#%d", index));
const char* func_name = "wasm-to-js";
// Schedule and compile to machine code.
CallDescriptor* incoming = GetWasmCallDescriptor(&zone, sig);
CallDescriptor* incoming =
GetWasmCallDescriptor(&zone, sig, WasmGraphBuilder::kNoRetpoline,
WasmGraphBuilder::kExtraCallableParam);
if (machine.Is32()) {
incoming = GetI32WasmCallDescriptor(&zone, incoming);
}
MaybeHandle<Code> maybe_code = Pipeline::GenerateCodeForWasmStub(
isolate, incoming, &graph, Code::WASM_TO_JS_FUNCTION, func_name.start(),
isolate, incoming, &graph, Code::WASM_TO_JS_FUNCTION, func_name,
AssemblerOptions::Default(isolate), source_position_table);
Handle<Code> code;
if (!maybe_code.ToHandle(&code)) {
@ -5008,13 +5008,13 @@ MaybeHandle<Code> CompileWasmImportCallWrapper(
if (FLAG_print_opt_code) {
CodeTracer::Scope tracing_scope(isolate->GetCodeTracer());
OFStream os(tracing_scope.file());
code->Disassemble(func_name.start(), os);
code->Disassemble(func_name, os);
}
#endif
if (must_record_function_compilation(isolate)) {
RecordFunctionCompilation(CodeEventListener::STUB_TAG, isolate, code,
"%.*s", func_name.length(), func_name.start());
RecordFunctionCompilation(CodeEventListener::STUB_TAG, isolate, code, "%s",
func_name);
}
return code;
@ -5347,10 +5347,13 @@ class LinkageLocationAllocator {
// General code uses the above configuration data.
CallDescriptor* GetWasmCallDescriptor(
Zone* zone, wasm::FunctionSig* fsig,
WasmGraphBuilder::UseRetpoline use_retpoline) {
// The '+ 1' here is to accomodate the instance object as first parameter.
WasmGraphBuilder::UseRetpoline use_retpoline,
WasmGraphBuilder::ExtraCallableParam extra_callable_param) {
// The extra here is to accomodate the instance object as first parameter
// and, in the case of an import wrapper, the additional callable.
int extra_params = extra_callable_param ? 2 : 1;
LocationSignature::Builder locations(zone, fsig->return_count(),
fsig->parameter_count() + 1);
fsig->parameter_count() + extra_params);
// Add register and/or stack parameter(s).
LinkageLocationAllocator params(wasm::kGpParamRegisters,
@ -5367,6 +5370,13 @@ CallDescriptor* GetWasmCallDescriptor(
locations.AddParam(l);
}
// Import call wrappers have an additional (implicit) parameter, the callable.
// For consistency with JS, we use the JSFunction register.
if (extra_callable_param) {
locations.AddParam(LinkageLocation::ForRegister(
kJSFunctionRegister.code(), MachineType::TaggedPointer()));
}
// Add return location(s).
LinkageLocationAllocator rets(wasm::kGpReturnRegisters,
wasm::kFpReturnRegisters);
@ -5391,7 +5401,9 @@ CallDescriptor* GetWasmCallDescriptor(
MachineType target_type = MachineType::Pointer();
LinkageLocation target_loc = LinkageLocation::ForAnyRegister(target_type);
CallDescriptor::Kind kind = CallDescriptor::kCallWasmFunction;
CallDescriptor::Kind kind = extra_callable_param
? CallDescriptor::kCallWasmImportWrapper
: CallDescriptor::kCallWasmFunction;
CallDescriptor::Flags flags =
use_retpoline ? CallDescriptor::kRetpoline : CallDescriptor::kNoFlags;

View File

@ -88,7 +88,6 @@ WasmImportCallKind GetWasmImportCallKind(Handle<JSReceiver> callable,
// Compiles an import call wrapper, which allows WASM to call imports.
MaybeHandle<Code> CompileWasmImportCallWrapper(Isolate*, WasmImportCallKind,
wasm::FunctionSig*,
uint32_t index,
wasm::ModuleOrigin,
wasm::UseTrapHandler);
@ -105,7 +104,7 @@ MaybeHandle<Code> CompileWasmInterpreterEntry(Isolate*, uint32_t func_index,
enum CWasmEntryParameters {
kCodeEntry,
kWasmInstance,
kObjectRef,
kArgumentsBuffer,
// marker:
kNumParameters
@ -130,11 +129,18 @@ struct WasmInstanceCacheNodes {
// the wasm decoder from the internal details of TurboFan.
class WasmGraphBuilder {
public:
enum EnforceBoundsCheck : bool {
enum EnforceBoundsCheck : bool { // --
kNeedsBoundsCheck = true,
kCanOmitBoundsCheck = false
};
enum UseRetpoline : bool { kRetpoline = true, kNoRetpoline = false };
enum UseRetpoline : bool { // --
kRetpoline = true,
kNoRetpoline = false
};
enum ExtraCallableParam : bool { // --
kExtraCallableParam = true,
kNoExtraCallableParam = false
};
WasmGraphBuilder(wasm::ModuleEnv* env, Zone* zone, MachineGraph* mcgraph,
wasm::FunctionSig* sig,
@ -490,7 +496,9 @@ class WasmGraphBuilder {
V8_EXPORT_PRIVATE CallDescriptor* GetWasmCallDescriptor(
Zone* zone, wasm::FunctionSig* signature,
WasmGraphBuilder::UseRetpoline use_retpoline =
WasmGraphBuilder::kNoRetpoline);
WasmGraphBuilder::kNoRetpoline,
WasmGraphBuilder::ExtraCallableParam callable_param =
WasmGraphBuilder::kNoExtraCallableParam);
V8_EXPORT_PRIVATE CallDescriptor* GetI32WasmCallDescriptor(
Zone* zone, CallDescriptor* call_descriptor);

View File

@ -3250,6 +3250,18 @@ void CodeGenerator::AssembleConstructFrame() {
__ StubPrologue(info()->GetOutputStackFrameType());
if (call_descriptor->IsWasmFunctionCall()) {
__ pushq(kWasmInstanceRegister);
} else if (call_descriptor->IsWasmImportWrapper()) {
// WASM import wrappers are passed a tuple in the place of the instance.
// Unpack the tuple into the instance and the target callable.
// This must be done here in the codegen because it cannot be expressed
// properly in the graph.
__ movq(kJSFunctionRegister,
Operand(kWasmInstanceRegister,
Tuple2::kValue2Offset - kHeapObjectTag));
__ movq(kWasmInstanceRegister,
Operand(kWasmInstanceRegister,
Tuple2::kValue1Offset - kHeapObjectTag));
__ pushq(kWasmInstanceRegister);
}
}

View File

@ -39,7 +39,7 @@ class ExitFrameConstants : public TypedFrameConstants {
class WasmCompileLazyFrameConstants : public TypedFrameConstants {
public:
static constexpr int kNumberOfSavedGpParamRegs = 4;
static constexpr int kNumberOfSavedGpParamRegs = 3;
static constexpr int kNumberOfSavedFpParamRegs = 7;
// FP-relative.

View File

@ -39,7 +39,7 @@ class ExitFrameConstants : public TypedFrameConstants {
class WasmCompileLazyFrameConstants : public TypedFrameConstants {
public:
static constexpr int kNumberOfSavedGpParamRegs = 8;
static constexpr int kNumberOfSavedGpParamRegs = 7;
static constexpr int kNumberOfSavedFpParamRegs = 7;
// FP-relative.

View File

@ -1764,13 +1764,10 @@ void WasmInstanceObject::WasmInstanceObjectPrint(std::ostream& os) { // NOLINT
if (has_table_object()) {
os << "\n - table_object: " << Brief(table_object());
}
os << "\n - imported_function_instances: "
<< Brief(imported_function_instances());
os << "\n - imported_function_callables: "
<< Brief(imported_function_callables());
if (has_indirect_function_table_instances()) {
os << "\n - indirect_function_table_instances: "
<< Brief(indirect_function_table_instances());
os << "\n - imported_function_refs: " << Brief(imported_function_refs());
if (has_indirect_function_table_refs()) {
os << "\n - indirect_function_table_refs: "
<< Brief(indirect_function_table_refs());
}
if (has_managed_native_allocations()) {
os << "\n - managed_native_allocations: "

View File

@ -35,7 +35,7 @@ class ExitFrameConstants : public TypedFrameConstants {
class WasmCompileLazyFrameConstants : public TypedFrameConstants {
public:
static constexpr int kNumberOfSavedGpParamRegs = 8;
static constexpr int kNumberOfSavedGpParamRegs = 7;
static constexpr int kNumberOfSavedFpParamRegs = 8;
// FP-relative.

View File

@ -35,7 +35,7 @@ class ExitFrameConstants : public TypedFrameConstants {
class WasmCompileLazyFrameConstants : public TypedFrameConstants {
public:
static constexpr int kNumberOfSavedGpParamRegs = 5;
static constexpr int kNumberOfSavedGpParamRegs = 4;
#ifdef V8_TARGET_ARCH_S390X
static constexpr int kNumberOfSavedFpParamRegs = 4;
#else

View File

@ -26,7 +26,7 @@ constexpr Register kNoParamRegister = edi;
#elif V8_TARGET_ARCH_X64
constexpr RegList kLiftoffAssemblerGpCacheRegs =
Register::ListOf<rax, rcx, rdx, rbx, rsi, rdi>();
Register::ListOf<rax, rcx, rdx, rbx, rsi, rdi, r9>();
constexpr RegList kLiftoffAssemblerFpCacheRegs =
DoubleRegister::ListOf<xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7>();

View File

@ -1635,15 +1635,15 @@ class LiftoffCompiler {
__ Load(target, imported_targets.gp(), no_reg,
imm.index * sizeof(Address), kPointerLoadType, pinned);
LiftoffRegister imported_instances = tmp;
LOAD_INSTANCE_FIELD(imported_instances, ImportedFunctionInstances,
LiftoffRegister imported_function_refs = tmp;
LOAD_INSTANCE_FIELD(imported_function_refs, ImportedFunctionRefs,
kPointerSize);
LiftoffRegister target_instance = tmp;
__ Load(target_instance, imported_instances.gp(), no_reg,
LiftoffRegister imported_function_ref = tmp;
__ Load(imported_function_ref, imported_function_refs.gp(), no_reg,
ObjectAccess::ElementOffsetInTaggedFixedArray(imm.index),
kPointerLoadType, pinned);
LiftoffRegister* explicit_instance = &target_instance;
LiftoffRegister* explicit_instance = &imported_function_ref;
Register target_reg = target.gp();
__ PrepareCall(imm.sig, call_descriptor, &target_reg, explicit_instance);
source_position_table_builder_.AddPosition(
@ -1771,7 +1771,7 @@ class LiftoffCompiler {
__ Load(scratch, table.gp(), index.gp(), 0, kPointerLoadType, pinned);
// Load the instance from {instance->ift_instances[key]}
LOAD_INSTANCE_FIELD(table, IndirectFunctionTableInstances, kPointerSize);
LOAD_INSTANCE_FIELD(table, IndirectFunctionTableRefs, kPointerSize);
__ Load(tmp_const, table.gp(), index.gp(),
ObjectAccess::ElementOffsetInTaggedFixedArray(0), kPointerLoadType,
pinned);

View File

@ -1511,22 +1511,21 @@ int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) {
// The import reference is the instance object itself.
Address imported_target = imported_function->GetWasmCallTarget();
ImportedFunctionEntry entry(instance, func_index);
entry.set_wasm_to_wasm(*imported_instance, imported_target);
entry.SetWasmToWasm(*imported_instance, imported_target);
break;
}
default: {
// The imported function is a callable.
Handle<Code> wrapper_code =
compiler::CompileWasmImportCallWrapper(
isolate_, kind, expected_sig, func_index, module_->origin,
use_trap_handler())
.ToHandleChecked();
Handle<Code> wrapper_code = compiler::CompileWasmImportCallWrapper(
isolate_, kind, expected_sig,
module_->origin, use_trap_handler())
.ToHandleChecked();
RecordStats(*wrapper_code, isolate_->counters());
WasmCode* wasm_code =
native_module->AddImportWrapper(wrapper_code, func_index);
ImportedFunctionEntry entry(instance, func_index);
entry.set_wasm_to_js(*js_receiver, wasm_code);
entry.SetWasmToJs(isolate_, js_receiver, wasm_code);
break;
}
}
@ -1593,19 +1592,16 @@ int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) {
index, i);
return -1;
}
auto target_func = Handle<WasmExportedFunction>::cast(val);
Handle<WasmInstanceObject> target_instance =
handle(target_func->instance(), isolate_);
// Look up the signature's canonical id. If there is no canonical
// id, then the signature does not appear at all in this module,
// so putting {-1} in the table will cause checks to always fail.
auto target = Handle<WasmExportedFunction>::cast(val);
Handle<WasmInstanceObject> imported_instance =
handle(target->instance(), isolate_);
Address exported_call_target = target->GetWasmCallTarget();
FunctionSig* sig = imported_instance->module()
->functions[target->function_index()]
.sig;
FunctionSig* sig = target_func->sig();
IndirectFunctionTableEntry(instance, i)
.set(module_->signature_map.Find(*sig), *imported_instance,
exported_call_target);
.Set(module_->signature_map.Find(*sig), target_instance,
target_func->function_index());
}
num_imported_tables++;
break;
@ -2102,20 +2098,8 @@ void InstanceBuilder::LoadTableSegments(Handle<WasmInstanceObject> instance) {
// Update the local dispatch table first.
uint32_t sig_id = module_->signature_ids[function->sig_index];
Handle<WasmInstanceObject> target_instance = instance;
Address call_target;
const bool is_import = func_index < module_->num_imported_functions;
if (is_import) {
// For imported calls, take target instance and address from the
// import table.
ImportedFunctionEntry entry(instance, func_index);
target_instance = handle(entry.instance(), isolate_);
call_target = entry.target();
} else {
call_target = native_module->GetCallTargetForFunction(func_index);
}
IndirectFunctionTableEntry(instance, table_index)
.set(sig_id, *target_instance, call_target);
.Set(sig_id, instance, func_index);
if (!table_instance.table_object.is_null()) {
// Update the table object's other dispatch tables.
@ -2147,7 +2131,7 @@ void InstanceBuilder::LoadTableSegments(Handle<WasmInstanceObject> instance) {
// we have not yet added the dispatch table we are currently building.
WasmTableObject::UpdateDispatchTables(
isolate_, table_instance.table_object, table_index, function->sig,
target_instance, call_target);
instance, func_index);
}
}
}

View File

@ -425,7 +425,7 @@ WasmCode* NativeModule::AddOwnedCode(
}
WasmCode* NativeModule::AddImportWrapper(Handle<Code> code, uint32_t index) {
// TODO(wasm): Adding instance-specific wasm-to-js wrappers as owned code to
// TODO(wasm): Adding instance-specific import wrappers as owned code to
// this NativeModule is a memory leak until the whole NativeModule dies.
WasmCode* ret = AddAnonymousCode(code, WasmCode::kWasmToJsWrapper);
DCHECK_LT(index, module_->num_imported_functions);

View File

@ -230,8 +230,8 @@ class V8_EXPORT_PRIVATE NativeModule final {
OwnedVector<const byte> reloc_info,
OwnedVector<const byte> source_position_table, WasmCode::Tier tier);
// Add an import wrapper for wasm-to-JS transitions. This method copies over
// JS-allocated code, because we compile wrappers using a different pipeline.
// Add an import wrapper, e.g. for calls to JS functions. This method copies
// heap-allocated code because we compile wrappers using a different pipeline.
WasmCode* AddImportWrapper(Handle<Code> code, uint32_t index);
// Add an interpreter entry. For the same reason as AddImportWrapper, we

View File

@ -2710,9 +2710,10 @@ class ThreadImpl {
return {ExternalCallResult::EXTERNAL_RETURNED};
}
ExternalCallResult CallExternalWasmFunction(
Isolate* isolate, Handle<WasmInstanceObject> instance,
const WasmCode* code, FunctionSig* sig) {
ExternalCallResult CallExternalWasmFunction(Isolate* isolate,
Handle<Object> object_ref,
const WasmCode* code,
FunctionSig* sig) {
if (code->kind() == WasmCode::kWasmToJsWrapper &&
!IsJSCompatibleSignature(sig)) {
isolate->Throw(*isolate->factory()->NewTypeError(
@ -2781,7 +2782,7 @@ class ThreadImpl {
"code below needs adaption");
Handle<Object> args[compiler::CWasmEntryParameters::kNumParameters];
args[compiler::CWasmEntryParameters::kCodeEntry] = code_entry_obj;
args[compiler::CWasmEntryParameters::kWasmInstance] = instance;
args[compiler::CWasmEntryParameters::kObjectRef] = object_ref;
args[compiler::CWasmEntryParameters::kArgumentsBuffer] = arg_buffer_obj;
Handle<Object> receiver = isolate->factory()->undefined_value();
@ -2844,19 +2845,18 @@ class ThreadImpl {
}
ExternalCallResult CallImportedFunction(uint32_t function_index) {
DCHECK_GT(module()->num_imported_functions, function_index);
// Use a new HandleScope to avoid leaking / accumulating handles in the
// outer scope.
Isolate* isolate = instance_object_->GetIsolate();
HandleScope handle_scope(isolate);
DCHECK_GT(module()->num_imported_functions, function_index);
Handle<WasmInstanceObject> instance;
ImportedFunctionEntry entry(instance_object_, function_index);
instance = handle(entry.instance(), isolate);
Handle<Object> object_ref(entry.object_ref(), isolate);
WasmCode* code =
GetTargetCode(isolate->wasm_engine()->code_manager(), entry.target());
FunctionSig* sig = codemap()->module()->functions[function_index].sig;
return CallExternalWasmFunction(isolate, instance, code, sig);
FunctionSig* sig = module()->functions[function_index].sig;
return CallExternalWasmFunction(isolate, object_ref, code, sig);
}
ExternalCallResult CallIndirectFunction(uint32_t table_index,
@ -2900,28 +2900,20 @@ class ThreadImpl {
return {ExternalCallResult::SIGNATURE_MISMATCH};
}
Handle<WasmInstanceObject> instance = handle(entry.instance(), isolate);
HandleScope scope(isolate);
FunctionSig* signature = module()->signatures[sig_index];
Handle<Object> object_ref = handle(entry.object_ref(), isolate);
WasmCode* code =
GetTargetCode(isolate->wasm_engine()->code_manager(), entry.target());
// Call either an internal or external WASM function.
HandleScope scope(isolate);
FunctionSig* signature = module()->signatures[sig_index];
if (code->kind() == WasmCode::kFunction) {
if (!instance_object_.is_identical_to(instance)) {
// Cross instance call.
return CallExternalWasmFunction(isolate, instance, code, signature);
}
return {ExternalCallResult::INTERNAL, codemap()->GetCode(code->index())};
if (!object_ref->IsWasmInstanceObject() || /* call to an import */
!instance_object_.is_identical_to(object_ref) /* cross-instance */) {
return CallExternalWasmFunction(isolate, object_ref, code, signature);
}
// Call to external function.
if (code->kind() == WasmCode::kInterpreterEntry ||
code->kind() == WasmCode::kWasmToJsWrapper) {
return CallExternalWasmFunction(isolate, instance, code, signature);
}
return {ExternalCallResult::INVALID_FUNC};
DCHECK(code->kind() == WasmCode::kInterpreterEntry ||
code->kind() == WasmCode::kFunction);
return {ExternalCallResult::INTERNAL, codemap()->GetCode(code->index())};
}
inline Activation current_activation() {

View File

@ -14,6 +14,10 @@ namespace v8 {
namespace internal {
namespace wasm {
// TODO(wasm): optimize calling conventions to be both closer to C++ (to
// reduce adapter costs for fast WASM <-> C++ calls) and to be more efficient
// in general.
#if V8_TARGET_ARCH_IA32
// ===========================================================================
// == ia32 ===================================================================
@ -28,7 +32,7 @@ constexpr DoubleRegister kFpReturnRegisters[] = {xmm1, xmm2};
// ===========================================================================
// == x64 ====================================================================
// ===========================================================================
constexpr Register kGpParamRegisters[] = {rsi, rax, rdx, rcx, rbx, rdi};
constexpr Register kGpParamRegisters[] = {rsi, rax, rdx, rcx, rbx, r9};
constexpr Register kGpReturnRegisters[] = {rax, rdx};
constexpr DoubleRegister kFpParamRegisters[] = {xmm1, xmm2, xmm3,
xmm4, xmm5, xmm6};
@ -38,7 +42,7 @@ constexpr DoubleRegister kFpReturnRegisters[] = {xmm1, xmm2};
// ===========================================================================
// == arm ====================================================================
// ===========================================================================
constexpr Register kGpParamRegisters[] = {r3, r0, r1, r2};
constexpr Register kGpParamRegisters[] = {r3, r0, r2, r6};
constexpr Register kGpReturnRegisters[] = {r0, r1};
// ARM d-registers must be in ascending order for correct allocation.
constexpr DoubleRegister kFpParamRegisters[] = {d0, d1, d2, d3, d4, d5, d6, d7};
@ -48,7 +52,7 @@ constexpr DoubleRegister kFpReturnRegisters[] = {d0, d1};
// ===========================================================================
// == arm64 ====================================================================
// ===========================================================================
constexpr Register kGpParamRegisters[] = {x7, x0, x1, x2, x3, x4, x5, x6};
constexpr Register kGpParamRegisters[] = {x7, x0, x2, x3, x4, x5, x6};
constexpr Register kGpReturnRegisters[] = {x0, x1};
constexpr DoubleRegister kFpParamRegisters[] = {d0, d1, d2, d3, d4, d5, d6, d7};
constexpr DoubleRegister kFpReturnRegisters[] = {d0, d1};
@ -57,7 +61,7 @@ constexpr DoubleRegister kFpReturnRegisters[] = {d0, d1};
// ===========================================================================
// == mips ===================================================================
// ===========================================================================
constexpr Register kGpParamRegisters[] = {a0, a1, a2, a3};
constexpr Register kGpParamRegisters[] = {a0, a2, a3};
constexpr Register kGpReturnRegisters[] = {v0, v1};
constexpr DoubleRegister kFpParamRegisters[] = {f2, f4, f6, f8, f10, f12, f14};
constexpr DoubleRegister kFpReturnRegisters[] = {f2, f4};
@ -66,7 +70,7 @@ constexpr DoubleRegister kFpReturnRegisters[] = {f2, f4};
// ===========================================================================
// == mips64 =================================================================
// ===========================================================================
constexpr Register kGpParamRegisters[] = {a0, a1, a2, a3, a4, a5, a6, a7};
constexpr Register kGpParamRegisters[] = {a0, a2, a3, a4, a5, a6, a7};
constexpr Register kGpReturnRegisters[] = {v0, v1};
constexpr DoubleRegister kFpParamRegisters[] = {f2, f4, f6, f8, f10, f12, f14};
constexpr DoubleRegister kFpReturnRegisters[] = {f2, f4};
@ -75,7 +79,7 @@ constexpr DoubleRegister kFpReturnRegisters[] = {f2, f4};
// ===========================================================================
// == ppc & ppc64 ============================================================
// ===========================================================================
constexpr Register kGpParamRegisters[] = {r10, r3, r4, r5, r6, r7, r8, r9};
constexpr Register kGpParamRegisters[] = {r10, r3, r5, r6, r7, r8, r9};
constexpr Register kGpReturnRegisters[] = {r3, r4};
constexpr DoubleRegister kFpParamRegisters[] = {d1, d2, d3, d4, d5, d6, d7, d8};
constexpr DoubleRegister kFpReturnRegisters[] = {d1, d2};
@ -84,7 +88,7 @@ constexpr DoubleRegister kFpReturnRegisters[] = {d1, d2};
// ===========================================================================
// == s390x ==================================================================
// ===========================================================================
constexpr Register kGpParamRegisters[] = {r6, r2, r3, r4, r5};
constexpr Register kGpParamRegisters[] = {r6, r2, r4, r5};
constexpr Register kGpReturnRegisters[] = {r2, r3};
constexpr DoubleRegister kFpParamRegisters[] = {d0, d2, d4, d6};
constexpr DoubleRegister kFpReturnRegisters[] = {d0, d2, d4, d6};
@ -93,7 +97,7 @@ constexpr DoubleRegister kFpReturnRegisters[] = {d0, d2, d4, d6};
// ===========================================================================
// == s390 ===================================================================
// ===========================================================================
constexpr Register kGpParamRegisters[] = {r6, r2, r3, r4, r5};
constexpr Register kGpParamRegisters[] = {r6, r2, r4, r5};
constexpr Register kGpReturnRegisters[] = {r2, r3};
constexpr DoubleRegister kFpParamRegisters[] = {d0, d2};
constexpr DoubleRegister kFpReturnRegisters[] = {d0, d2};

View File

@ -177,12 +177,10 @@ OPTIONAL_ACCESSORS(WasmInstanceObject, debug_info, WasmDebugInfo,
kDebugInfoOffset)
OPTIONAL_ACCESSORS(WasmInstanceObject, table_object, WasmTableObject,
kTableObjectOffset)
ACCESSORS(WasmInstanceObject, imported_function_instances, FixedArray,
kImportedFunctionInstancesOffset)
ACCESSORS(WasmInstanceObject, imported_function_callables, FixedArray,
kImportedFunctionCallablesOffset)
OPTIONAL_ACCESSORS(WasmInstanceObject, indirect_function_table_instances,
FixedArray, kIndirectFunctionTableInstancesOffset)
ACCESSORS(WasmInstanceObject, imported_function_refs, FixedArray,
kImportedFunctionRefsOffset)
OPTIONAL_ACCESSORS(WasmInstanceObject, indirect_function_table_refs, FixedArray,
kIndirectFunctionTableRefsOffset)
OPTIONAL_ACCESSORS(WasmInstanceObject, managed_native_allocations, Foreign,
kManagedNativeAllocationsOffset)
OPTIONAL_ACCESSORS(WasmInstanceObject, exceptions_table, FixedArray,

View File

@ -89,7 +89,7 @@ class WasmInstanceNativeAllocations {
uint32_t old_size = instance->indirect_function_table_size();
void* new_sig_ids = nullptr;
void* new_targets = nullptr;
Handle<FixedArray> new_instances;
Handle<FixedArray> new_refs;
if (indirect_function_table_sig_ids_) {
// Reallocate the old storage.
new_sig_ids = realloc(indirect_function_table_sig_ids_,
@ -97,16 +97,14 @@ class WasmInstanceNativeAllocations {
new_targets =
realloc(indirect_function_table_targets_, new_size * sizeof(Address));
Handle<FixedArray> old(instance->indirect_function_table_instances(),
isolate);
new_instances = isolate->factory()->CopyFixedArrayAndGrow(
Handle<FixedArray> old(instance->indirect_function_table_refs(), isolate);
new_refs = isolate->factory()->CopyFixedArrayAndGrow(
old, static_cast<int>(new_size - old_size));
} else {
// Allocate new storage.
new_sig_ids = malloc(new_size * sizeof(uint32_t));
new_targets = malloc(new_size * sizeof(Address));
new_instances =
isolate->factory()->NewFixedArray(static_cast<int>(new_size));
new_refs = isolate->factory()->NewFixedArray(static_cast<int>(new_size));
}
// Initialize new entries.
instance->set_indirect_function_table_size(new_size);
@ -115,7 +113,7 @@ class WasmInstanceNativeAllocations {
SET(instance, indirect_function_table_targets,
reinterpret_cast<Address*>(new_targets));
instance->set_indirect_function_table_instances(*new_instances);
instance->set_indirect_function_table_refs(*new_refs);
for (uint32_t j = old_size; j < new_size; j++) {
IndirectFunctionTableEntry(instance, static_cast<int>(j)).clear();
}
@ -828,23 +826,22 @@ void WasmTableObject::Set(Isolate* isolate, Handle<WasmTableObject> table,
// TODO(titzer): Change this to MaybeHandle<WasmExportedFunction>
DCHECK(WasmExportedFunction::IsWasmExportedFunction(*function));
auto exported_function = Handle<WasmExportedFunction>::cast(function);
Handle<WasmInstanceObject> other_instance(exported_function->instance(),
isolate);
Handle<WasmInstanceObject> target_instance(exported_function->instance(),
isolate);
int func_index = exported_function->function_index();
auto* wasm_function = &other_instance->module()->functions[func_index];
auto* wasm_function = &target_instance->module()->functions[func_index];
DCHECK_NOT_NULL(wasm_function);
DCHECK_NOT_NULL(wasm_function->sig);
Address call_target = exported_function->GetWasmCallTarget();
UpdateDispatchTables(isolate, table, table_index, wasm_function->sig,
handle(exported_function->instance(), isolate),
call_target);
func_index);
array->set(table_index, *function);
}
void WasmTableObject::UpdateDispatchTables(
Isolate* isolate, Handle<WasmTableObject> table, int table_index,
wasm::FunctionSig* sig, Handle<WasmInstanceObject> from_instance,
Address call_target) {
wasm::FunctionSig* sig, Handle<WasmInstanceObject> target_instance,
int target_func_index) {
// We simply need to update the IFTs for each instance that imports
// this table.
Handle<FixedArray> dispatch_tables(table->dispatch_tables(), isolate);
@ -852,15 +849,15 @@ void WasmTableObject::UpdateDispatchTables(
for (int i = 0; i < dispatch_tables->length();
i += kDispatchTableNumElements) {
Handle<WasmInstanceObject> to_instance(
Handle<WasmInstanceObject> instance(
WasmInstanceObject::cast(
dispatch_tables->get(i + kDispatchTableInstanceOffset)),
isolate);
// Note that {SignatureMap::Find} may return {-1} if the signature is
// not found; it will simply never match any check.
auto sig_id = to_instance->module()->signature_map.Find(*sig);
IndirectFunctionTableEntry(to_instance, table_index)
.set(sig_id, *from_instance, call_target);
auto sig_id = instance->module()->signature_map.Find(*sig);
IndirectFunctionTableEntry(instance, table_index)
.Set(sig_id, target_instance, target_func_index);
}
}
@ -1125,23 +1122,41 @@ MaybeHandle<WasmGlobalObject> WasmGlobalObject::New(
void IndirectFunctionTableEntry::clear() {
instance_->indirect_function_table_sig_ids()[index_] = -1;
instance_->indirect_function_table_targets()[index_] = 0;
instance_->indirect_function_table_instances()->set(
instance_->indirect_function_table_refs()->set(
index_, ReadOnlyRoots(instance_->GetIsolate()).undefined_value());
}
void IndirectFunctionTableEntry::set(int sig_id, WasmInstanceObject* instance,
Address call_target) {
TRACE_IFT("IFT entry %p[%d] = {sig_id=%d, instance=%p, target=%" PRIuPTR
"}\n",
*instance_, index_, sig_id, instance, call_target);
void IndirectFunctionTableEntry::Set(int sig_id,
Handle<WasmInstanceObject> target_instance,
int target_func_index) {
TRACE_IFT(
"IFT entry %p[%d] = {sig_id=%d, target_instance=%p, "
"target_func_index=%d}\n",
*instance_, index_, sig_id, *target_instance, target_func_index);
Object* ref = nullptr;
Address call_target = 0;
if (target_func_index <
static_cast<int>(target_instance->module()->num_imported_functions)) {
// The function in the target instance was imported. Use its imports table,
// which contains a tuple needed by the import wrapper.
ImportedFunctionEntry entry(target_instance, target_func_index);
ref = entry.object_ref();
call_target = entry.target();
} else {
// The function in the target instance was not imported.
ref = *target_instance;
call_target = target_instance->GetCallTarget(target_func_index);
}
// Set the signature id, the target, and the receiver ref.
instance_->indirect_function_table_sig_ids()[index_] = sig_id;
instance_->indirect_function_table_targets()[index_] = call_target;
instance_->indirect_function_table_instances()->set(index_, instance);
instance_->indirect_function_table_refs()->set(index_, ref);
}
WasmInstanceObject* IndirectFunctionTableEntry::instance() {
return WasmInstanceObject::cast(
instance_->indirect_function_table_instances()->get(index_));
Object* IndirectFunctionTableEntry::object_ref() {
return instance_->indirect_function_table_refs()->get(index_);
}
int IndirectFunctionTableEntry::sig_id() {
@ -1152,45 +1167,50 @@ Address IndirectFunctionTableEntry::target() {
return instance_->indirect_function_table_targets()[index_];
}
void ImportedFunctionEntry::set_wasm_to_js(
JSReceiver* callable, const wasm::WasmCode* wasm_to_js_wrapper) {
void ImportedFunctionEntry::SetWasmToJs(
Isolate* isolate, Handle<JSReceiver> callable,
const wasm::WasmCode* wasm_to_js_wrapper) {
TRACE_IFT("Import callable %p[%d] = {callable=%p, target=%p}\n", *instance_,
index_, callable, wasm_to_js_wrapper->instructions().start());
index_, *callable, wasm_to_js_wrapper->instructions().start());
DCHECK_EQ(wasm::WasmCode::kWasmToJsWrapper, wasm_to_js_wrapper->kind());
instance_->imported_function_instances()->set(index_, *instance_);
instance_->imported_function_callables()->set(index_, callable);
Handle<Tuple2> tuple =
isolate->factory()->NewTuple2(instance_, callable, TENURED);
instance_->imported_function_refs()->set(index_, *tuple);
instance_->imported_function_targets()[index_] =
wasm_to_js_wrapper->instruction_start();
}
void ImportedFunctionEntry::set_wasm_to_wasm(WasmInstanceObject* instance,
Address call_target) {
void ImportedFunctionEntry::SetWasmToWasm(WasmInstanceObject* instance,
Address call_target) {
TRACE_IFT("Import WASM %p[%d] = {instance=%p, target=%" PRIuPTR "}\n",
*instance_, index_, instance, call_target);
instance_->imported_function_instances()->set(index_, instance);
instance_->imported_function_callables()->set(
index_, instance_->GetReadOnlyRoots().undefined_value());
instance_->imported_function_refs()->set(index_, instance);
instance_->imported_function_targets()[index_] = call_target;
}
WasmInstanceObject* ImportedFunctionEntry::instance() {
return WasmInstanceObject::cast(
instance_->imported_function_instances()->get(index_));
// The imported reference entry is either a target instance or a tuple
// of this instance and the target callable.
Object* value = instance_->imported_function_refs()->get(index_);
if (value->IsWasmInstanceObject()) {
return WasmInstanceObject::cast(value);
}
Tuple2* tuple = Tuple2::cast(value);
return WasmInstanceObject::cast(tuple->value1());
}
JSReceiver* ImportedFunctionEntry::callable() {
return JSReceiver::cast(
instance_->imported_function_callables()->get(index_));
return JSReceiver::cast(Tuple2::cast(object_ref())->value2());
}
Object* ImportedFunctionEntry::object_ref() {
return instance_->imported_function_refs()->get(index_);
}
Address ImportedFunctionEntry::target() {
return instance_->imported_function_targets()[index_];
}
bool ImportedFunctionEntry::is_js_receiver_entry() {
return instance_->imported_function_callables()->get(index_)->IsJSReceiver();
}
bool WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(
Handle<WasmInstanceObject> instance, uint32_t minimum_size) {
uint32_t old_size = instance->indirect_function_table_size();
@ -1259,13 +1279,9 @@ Handle<WasmInstanceObject> WasmInstanceObject::New(
num_imported_mutable_globals);
instance->set_managed_native_allocations(*native_allocations);
Handle<FixedArray> imported_function_instances =
Handle<FixedArray> imported_function_refs =
isolate->factory()->NewFixedArray(num_imported_functions);
instance->set_imported_function_instances(*imported_function_instances);
Handle<FixedArray> imported_function_callables =
isolate->factory()->NewFixedArray(num_imported_functions);
instance->set_imported_function_callables(*imported_function_callables);
instance->set_imported_function_refs(*imported_function_refs);
Handle<Code> centry_stub = CodeFactory::CEntry(isolate);
instance->set_centry_stub(*centry_stub);
@ -1413,6 +1429,10 @@ Address WasmExportedFunction::GetWasmCallTarget() {
return instance()->GetCallTarget(function_index());
}
wasm::FunctionSig* WasmExportedFunction::sig() {
return instance()->module()->functions[function_index()].sig;
}
#undef TRACE
#undef TRACE_IFT
} // namespace internal

View File

@ -46,19 +46,22 @@ class Managed;
V8_INLINE bool has_##name(); \
DECL_ACCESSORS(name, type)
// An entry in an indirect function table (IFT).
// Each entry in the IFT has the following fields:
// - instance = target instance
// - sig_id = signature id of function
// - target = entrypoint to wasm code for the function, or wasm-to-js wrapper
// A helper for an entry in an indirect function table (IFT).
// The underlying storage in the instance is used by generated code to
// call functions indirectly at runtime.
// Each entry has the following fields:
// - object = target instance, if a WASM function, tuple if imported
// - sig_id = signature id of function
// - target = entrypoint to WASM code or import wrapper code
class IndirectFunctionTableEntry {
public:
inline IndirectFunctionTableEntry(Handle<WasmInstanceObject>, int index);
void clear();
void set(int sig_id, WasmInstanceObject* instance, Address call_target);
void Set(int sig_id, Handle<WasmInstanceObject> target_instance,
int target_func_index);
WasmInstanceObject* instance();
Object* object_ref();
int sig_id();
Address target();
@ -67,32 +70,31 @@ class IndirectFunctionTableEntry {
int const index_;
};
// An entry for an imported function.
// (note this is not called a "table" since it is not dynamically indexed).
// The imported function entries are used to call imported functions.
// For each imported function there is an entry which is either:
// - an imported JSReceiver, which has fields
// - instance = importing instance
// - receiver = JSReceiver, either a JS function or other callable
// - target = pointer to wasm-to-js wrapper code entrypoint
// - an imported wasm function from another instance, which has fields
// - instance = target instance
// - target = entrypoint for the function
// A helper for an entry for an imported function, indexed statically.
// The underlying storage in the instance is used by generated code to
// call imported functions at runtime.
// Each entry is either:
// - WASM to JS, which has fields
// - object = a Tuple2 of the importing instance and the callable
// - target = entrypoint to import wrapper code
// - WASM to WASM, which has fields
// - object = target instance
// - target = entrypoint for the function
class ImportedFunctionEntry {
public:
inline ImportedFunctionEntry(Handle<WasmInstanceObject>, int index);
// Initialize this entry as a {JSReceiver} call.
void set_wasm_to_js(JSReceiver* callable,
const wasm::WasmCode* wasm_to_js_wrapper);
// Initialize this entry as a WASM to JS call. This accepts the isolate as a
// parameter, since it must allocate a tuple.
void SetWasmToJs(Isolate*, Handle<JSReceiver> callable,
const wasm::WasmCode* wasm_to_js_wrapper);
// Initialize this entry as a WASM to WASM call.
void set_wasm_to_wasm(WasmInstanceObject* target_instance,
Address call_target);
void SetWasmToWasm(WasmInstanceObject* target_instance, Address call_target);
WasmInstanceObject* instance();
JSReceiver* callable();
Object* object_ref();
Address target();
bool is_js_receiver_entry();
private:
Handle<WasmInstanceObject> const instance_;
@ -271,8 +273,8 @@ class WasmTableObject : public JSObject {
static void UpdateDispatchTables(Isolate* isolate,
Handle<WasmTableObject> table,
int table_index, wasm::FunctionSig* sig,
Handle<WasmInstanceObject> from_instance,
Address call_target);
Handle<WasmInstanceObject> target_instance,
int target_func_index);
static void ClearDispatchTables(Isolate* isolate,
Handle<WasmTableObject> table, int index);
@ -383,9 +385,8 @@ class WasmInstanceObject : public JSObject {
DECL_OPTIONAL_ACCESSORS(imported_mutable_globals_buffers, FixedArray)
DECL_OPTIONAL_ACCESSORS(debug_info, WasmDebugInfo)
DECL_OPTIONAL_ACCESSORS(table_object, WasmTableObject)
DECL_ACCESSORS(imported_function_instances, FixedArray)
DECL_ACCESSORS(imported_function_callables, FixedArray)
DECL_OPTIONAL_ACCESSORS(indirect_function_table_instances, FixedArray)
DECL_ACCESSORS(imported_function_refs, FixedArray)
DECL_OPTIONAL_ACCESSORS(indirect_function_table_refs, FixedArray)
DECL_OPTIONAL_ACCESSORS(managed_native_allocations, Foreign)
DECL_OPTIONAL_ACCESSORS(exceptions_table, FixedArray)
DECL_ACCESSORS(undefined_value, Oddball)
@ -419,9 +420,8 @@ class WasmInstanceObject : public JSObject {
V(kImportedMutableGlobalsBuffersOffset, kPointerSize) \
V(kDebugInfoOffset, kPointerSize) \
V(kTableObjectOffset, kPointerSize) \
V(kImportedFunctionInstancesOffset, kPointerSize) \
V(kImportedFunctionCallablesOffset, kPointerSize) \
V(kIndirectFunctionTableInstancesOffset, kPointerSize) \
V(kImportedFunctionRefsOffset, kPointerSize) \
V(kIndirectFunctionTableRefsOffset, kPointerSize) \
V(kManagedNativeAllocationsOffset, kPointerSize) \
V(kExceptionsTableOffset, kPointerSize) \
V(kUndefinedValueOffset, kPointerSize) \
@ -512,6 +512,8 @@ class WasmExportedFunction : public JSFunction {
Handle<Code> export_wrapper);
Address GetWasmCallTarget();
wasm::FunctionSig* sig();
};
// Information for a WasmExportedFunction which is referenced as the function

View File

@ -39,20 +39,19 @@ TestingModuleBuilder::TestingModuleBuilder(
instance_object_ = InitInstanceObject();
if (maybe_import) {
// Manually compile a wasm to JS wrapper and insert it into the instance.
// Manually compile an import wrapper and insert it into the instance.
CodeSpaceMemoryModificationScope modification_scope(isolate_->heap());
auto kind = compiler::GetWasmImportCallKind(maybe_import->js_function,
maybe_import->sig);
MaybeHandle<Code> code = compiler::CompileWasmImportCallWrapper(
isolate_, kind, maybe_import->sig, maybe_import_index,
test_module_->origin,
isolate_, kind, maybe_import->sig, test_module_->origin,
trap_handler::IsTrapHandlerEnabled() ? kUseTrapHandler
: kNoTrapHandler);
auto wasm_to_js_wrapper = native_module_->AddImportWrapper(
auto import_wrapper = native_module_->AddImportWrapper(
code.ToHandleChecked(), maybe_import_index);
ImportedFunctionEntry(instance_object_, maybe_import_index)
.set_wasm_to_js(*maybe_import->js_function, wasm_to_js_wrapper);
.SetWasmToJs(isolate_, maybe_import->js_function, import_wrapper);
}
if (tier == ExecutionTier::kInterpreter) {
@ -170,9 +169,8 @@ void TestingModuleBuilder::PopulateIndirectFunctionTable() {
for (int j = 0; j < table_size; j++) {
WasmFunction& function = test_module_->functions[table.values[j]];
int sig_id = test_module_->signature_map.Find(*function.sig);
auto target =
native_module_->GetCallTargetForFunction(function.func_index);
IndirectFunctionTableEntry(instance, j).set(sig_id, *instance, target);
IndirectFunctionTableEntry(instance, j)
.Set(sig_id, instance, function.func_index);
}
}
}

View File

@ -7,83 +7,102 @@
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
var module = (function () {
var builder = new WasmModuleBuilder();
(function Test1() {
print("Test1...");
var module = (function () {
var builder = new WasmModuleBuilder();
var sig_index = builder.addType(kSig_i_ii);
builder.addImport("q", "add", sig_index);
builder.addFunction("add", sig_index)
.addBody([
kExprGetLocal, 0, kExprGetLocal, 1, kExprCallFunction, 0
]);
builder.addFunction("sub", sig_index)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprI32Sub, // --
]);
builder.addFunction("main", kSig_i_iii)
.addBody([
kExprGetLocal, 1,
kExprGetLocal, 2,
kExprGetLocal, 0,
kExprCallIndirect, sig_index, kTableZero
])
.exportFunc()
builder.appendToTable([1, 2, 3]);
var sig_index = builder.addType(kSig_i_ii);
builder.addImport("q", "add", sig_index);
var f = builder.addFunction("add", sig_index)
.addBody([
kExprGetLocal, 0, kExprGetLocal, 1, kExprCallFunction, 0
]);
print("internal add index = " + f.index);
builder.addFunction("sub", sig_index)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprI32Sub, // --
]);
builder.addFunction("main", kSig_i_iii)
.addBody([
kExprGetLocal, 1,
kExprGetLocal, 2,
kExprGetLocal, 0,
kExprCallIndirect, sig_index, kTableZero
])
.exportFunc()
builder.appendToTable([1, 2, 3]);
return builder.instantiate({q: {add: function(a, b) { return a + b | 0; }}});
return builder.instantiate({q: {add: function(a, b) {
print(" --extadd");
return a + b | 0;
}}});
})();
// Check the module exists.
assertFalse(module === undefined);
assertFalse(module === null);
assertFalse(module === 0);
assertEquals("object", typeof module.exports);
assertEquals("function", typeof module.exports.main);
print(" --x1--");
assertEquals(19, module.exports.main(0, 12, 7));
print(" --y1--");
assertEquals(5, module.exports.main(1, 12, 7));
print(" --z1--");
assertTraps(kTrapFuncSigMismatch, () => module.exports.main(2, 12, 33));
print(" --w1--");
assertTraps(kTrapFuncInvalid, () => module.exports.main(3, 12, 33));
})();
// Check the module exists.
assertFalse(module === undefined);
assertFalse(module === null);
assertFalse(module === 0);
assertEquals("object", typeof module.exports);
assertEquals("function", typeof module.exports.main);
(function Test2() {
print("Test2...");
var module = (function () {
var builder = new WasmModuleBuilder();
assertEquals(5, module.exports.main(1, 12, 7));
assertEquals(19, module.exports.main(0, 12, 7));
var sig_i_ii = builder.addType(kSig_i_ii);
var sig_i_i = builder.addType(kSig_i_i);
var mul = builder.addImport("q", "mul", sig_i_ii);
var add = builder.addFunction("add", sig_i_ii)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprI32Add // --
]);
var popcnt = builder.addFunction("popcnt", sig_i_i)
.addBody([
kExprGetLocal, 0, // --
kExprI32Popcnt // --
]);
var main = builder.addFunction("main", kSig_i_iii)
.addBody([
kExprGetLocal, 1,
kExprGetLocal, 2,
kExprGetLocal, 0,
kExprCallIndirect, sig_i_ii, kTableZero
])
.exportFunc();
builder.appendToTable([mul, add.index, popcnt.index, main.index]);
assertTraps(kTrapFuncSigMismatch, "module.exports.main(2, 12, 33)");
assertTraps(kTrapFuncInvalid, "module.exports.main(3, 12, 33)");
return builder.instantiate({q: {mul: function(a, b) { return a * b | 0; }}});
})();
module = (function () {
var builder = new WasmModuleBuilder();
var sig_i_ii = builder.addType(kSig_i_ii);
var sig_i_i = builder.addType(kSig_i_i);
var mul = builder.addImport("q", "mul", sig_i_ii);
var add = builder.addFunction("add", sig_i_ii)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprI32Add // --
]);
var popcnt = builder.addFunction("popcnt", sig_i_i)
.addBody([
kExprGetLocal, 0, // --
kExprI32Popcnt // --
]);
var main = builder.addFunction("main", kSig_i_iii)
.addBody([
kExprGetLocal, 1,
kExprGetLocal, 2,
kExprGetLocal, 0,
kExprCallIndirect, sig_i_ii, kTableZero
])
.exportFunc();
builder.appendToTable([mul, add.index, popcnt.index, main.index]);
return builder.instantiate({q: {mul: function(a, b) { return a * b | 0; }}});
print(" --x2--");
assertEquals(-6, module.exports.main(0, -2, 3));
print(" --y2--");
assertEquals(99, module.exports.main(1, 22, 77));
print(" --z2--");
assertTraps(kTrapFuncSigMismatch, () => module.exports.main(2, 12, 33));
print(" --q2--");
assertTraps(kTrapFuncSigMismatch, () => module.exports.main(3, 12, 33));
print(" --t2--");
assertTraps(kTrapFuncInvalid, () => module.exports.main(4, 12, 33));
})();
assertEquals(-6, module.exports.main(0, -2, 3));
assertEquals(99, module.exports.main(1, 22, 77));
assertTraps(kTrapFuncSigMismatch, "module.exports.main(2, 12, 33)");
assertTraps(kTrapFuncSigMismatch, "module.exports.main(3, 12, 33)");
assertTraps(kTrapFuncInvalid, "module.exports.main(4, 12, 33)");
function AddFunctions(builder) {
var mul = builder.addFunction("mul", kSig_i_ii)
@ -108,31 +127,34 @@ function AddFunctions(builder) {
}
module = (function () {
var builder = new WasmModuleBuilder();
(function Test3() {
print("Test3...");
var module = (function () {
var builder = new WasmModuleBuilder();
var f = AddFunctions(builder);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprI32Const, 33, // --
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprCallIndirect, 0, kTableZero]) // --
.exportAs("main");
var f = AddFunctions(builder);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprI32Const, 33, // --
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprCallIndirect, 0, kTableZero]) // --
.exportAs("main");
builder.appendToTable([f.mul.index, f.add.index, f.sub.index]);
builder.appendToTable([f.mul.index, f.add.index, f.sub.index]);
return builder.instantiate();
return builder.instantiate();
})();
assertEquals(33, module.exports.main(1, 0));
assertEquals(66, module.exports.main(2, 0));
assertEquals(34, module.exports.main(1, 1));
assertEquals(35, module.exports.main(2, 1));
assertEquals(32, module.exports.main(1, 2));
assertEquals(31, module.exports.main(2, 2));
assertTraps(kTrapFuncInvalid, () => module.exports.main(12, 3));
})();
assertEquals(33, module.exports.main(1, 0));
assertEquals(66, module.exports.main(2, 0));
assertEquals(34, module.exports.main(1, 1));
assertEquals(35, module.exports.main(2, 1));
assertEquals(32, module.exports.main(1, 2));
assertEquals(31, module.exports.main(2, 2));
assertTraps(kTrapFuncInvalid, "module.exports.main(12, 3)");
(function ConstBaseTest() {
print("ConstBaseTest...");
function instanceWithTable(base, length) {
@ -166,7 +188,7 @@ assertTraps(kTrapFuncInvalid, "module.exports.main(12, 3)");
assertEquals(31, main(2, i + 1));
assertEquals(33, main(1, i + 2));
assertEquals(66, main(2, i + 2));
assertTraps(kTrapFuncInvalid, "main(12, 10)");
assertTraps(kTrapFuncInvalid, () => main(12, 10));
}
})();
@ -203,6 +225,6 @@ assertTraps(kTrapFuncInvalid, "module.exports.main(12, 3)");
assertEquals(35, main(2, i + 1));
assertEquals(32, main(1, i + 2));
assertEquals(31, main(2, i + 2));
assertTraps(kTrapFuncInvalid, "main(12, 10)");
assertTraps(kTrapFuncInvalid, () => main(12, 10));
}
})();