[wasm] Store the globals_start in WasmContext.
This CL removes the code specialization for WASM functions that access globals. Previously, we were embedding the start address of the globals memory (globals_start) as a constant in the code, which required patching for every instance. We now put this base in to the WasmContext, which is available as a parameter to every WasmFunction. R=ahaas@chromium.org, CC=mtrofin@chromium.org Bug: Change-Id: I04bb739e898cc5a3b7dd081cc166483022d113fd Reviewed-on: https://chromium-review.googlesource.com/712595 Commit-Queue: Ben Titzer <titzer@chromium.org> Reviewed-by: Mircea Trofin <mtrofin@chromium.org> Reviewed-by: Andreas Haas <ahaas@chromium.org> Reviewed-by: Bill Budge <bbudge@chromium.org> Cr-Commit-Position: refs/heads/master@{#48581}
This commit is contained in:
parent
cf9d3d52eb
commit
c02f5e3ab3
@ -317,22 +317,6 @@ Address RelocInfo::global_handle() const {
|
||||
return embedded_address();
|
||||
}
|
||||
|
||||
void RelocInfo::update_wasm_global_reference(
|
||||
Isolate* isolate, Address old_base, Address new_base,
|
||||
ICacheFlushMode icache_flush_mode) {
|
||||
DCHECK(IsWasmGlobalReference(rmode_));
|
||||
Address updated_reference;
|
||||
DCHECK_LE(old_base, wasm_global_reference());
|
||||
updated_reference = new_base + (wasm_global_reference() - old_base);
|
||||
DCHECK_LE(new_base, updated_reference);
|
||||
set_embedded_address(isolate, updated_reference, icache_flush_mode);
|
||||
}
|
||||
|
||||
Address RelocInfo::wasm_global_reference() const {
|
||||
DCHECK(IsWasmGlobalReference(rmode_));
|
||||
return embedded_address();
|
||||
}
|
||||
|
||||
uint32_t RelocInfo::wasm_function_table_size_reference() const {
|
||||
DCHECK(IsWasmFunctionTableSizeReference(rmode_));
|
||||
return embedded_size();
|
||||
@ -642,8 +626,6 @@ const char* RelocInfo::RelocModeName(RelocInfo::Mode rmode) {
|
||||
return "veneer pool";
|
||||
case WASM_CONTEXT_REFERENCE:
|
||||
return "wasm context reference";
|
||||
case WASM_GLOBAL_REFERENCE:
|
||||
return "wasm global value reference";
|
||||
case WASM_FUNCTION_TABLE_SIZE_REFERENCE:
|
||||
return "wasm function table size reference";
|
||||
case WASM_PROTECTED_INSTRUCTION_LANDING:
|
||||
@ -729,7 +711,6 @@ void RelocInfo::Verify(Isolate* isolate) {
|
||||
case CONST_POOL:
|
||||
case VENEER_POOL:
|
||||
case WASM_CONTEXT_REFERENCE:
|
||||
case WASM_GLOBAL_REFERENCE:
|
||||
case WASM_FUNCTION_TABLE_SIZE_REFERENCE:
|
||||
case WASM_GLOBAL_HANDLE:
|
||||
case WASM_PROTECTED_INSTRUCTION_LANDING:
|
||||
|
@ -364,7 +364,6 @@ class RelocInfo {
|
||||
// wasm code. Everything after WASM_CONTEXT_REFERENCE (inclusive) is not
|
||||
// GC'ed.
|
||||
WASM_CONTEXT_REFERENCE,
|
||||
WASM_GLOBAL_REFERENCE,
|
||||
WASM_FUNCTION_TABLE_SIZE_REFERENCE,
|
||||
WASM_PROTECTED_INSTRUCTION_LANDING,
|
||||
WASM_GLOBAL_HANDLE,
|
||||
@ -460,9 +459,6 @@ class RelocInfo {
|
||||
static inline bool IsWasmContextReference(Mode mode) {
|
||||
return mode == WASM_CONTEXT_REFERENCE;
|
||||
}
|
||||
static inline bool IsWasmGlobalReference(Mode mode) {
|
||||
return mode == WASM_GLOBAL_REFERENCE;
|
||||
}
|
||||
static inline bool IsWasmFunctionTableSizeReference(Mode mode) {
|
||||
return mode == WASM_FUNCTION_TABLE_SIZE_REFERENCE;
|
||||
}
|
||||
@ -473,8 +469,7 @@ class RelocInfo {
|
||||
return IsWasmFunctionTableSizeReference(mode);
|
||||
}
|
||||
static inline bool IsWasmPtrReference(Mode mode) {
|
||||
return mode == WASM_CONTEXT_REFERENCE || mode == WASM_GLOBAL_REFERENCE ||
|
||||
mode == WASM_GLOBAL_HANDLE;
|
||||
return mode == WASM_CONTEXT_REFERENCE || mode == WASM_GLOBAL_HANDLE;
|
||||
}
|
||||
static inline bool IsWasmProtectedLanding(Mode mode) {
|
||||
return mode == WASM_PROTECTED_INSTRUCTION_LANDING;
|
||||
@ -506,17 +501,12 @@ class RelocInfo {
|
||||
bool IsInConstantPool();
|
||||
|
||||
Address wasm_context_reference() const;
|
||||
Address wasm_global_reference() const;
|
||||
uint32_t wasm_function_table_size_reference() const;
|
||||
uint32_t wasm_memory_size_reference() const;
|
||||
Address global_handle() const;
|
||||
|
||||
void set_wasm_context_reference(
|
||||
Isolate* isolate, Address address,
|
||||
ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
|
||||
void update_wasm_global_reference(
|
||||
Isolate* isolate, Address old_base, Address new_base,
|
||||
ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
|
||||
void update_wasm_function_table_size_reference(
|
||||
Isolate* isolate, uint32_t old_base, uint32_t new_base,
|
||||
ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
|
||||
|
@ -297,17 +297,28 @@ void Int64Lowering::LowerNode(Node* node) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IrOpcode::kTailCall: {
|
||||
CallDescriptor* descriptor =
|
||||
const_cast<CallDescriptor*>(CallDescriptorOf(node->op()));
|
||||
if (DefaultLowering(node) ||
|
||||
(descriptor->ReturnCount() == 1 &&
|
||||
descriptor->GetReturnType(0) == MachineType::Int64())) {
|
||||
// Tail calls do not have return values, so adjusting the call
|
||||
// descriptor is enough.
|
||||
auto new_descriptor = GetI32WasmCallDescriptor(zone(), descriptor);
|
||||
NodeProperties::ChangeOp(node, common()->TailCall(new_descriptor));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IrOpcode::kCall: {
|
||||
// TODO(turbofan): Make wasm code const-correct wrt. CallDescriptor.
|
||||
CallDescriptor* descriptor =
|
||||
const_cast<CallDescriptor*>(CallDescriptorOf(node->op()));
|
||||
if (DefaultLowering(node) ||
|
||||
(descriptor->ReturnCount() == 1 &&
|
||||
descriptor->GetReturnType(0) == MachineType::Int64())) {
|
||||
// We have to adjust the call descriptor.
|
||||
const Operator* op =
|
||||
common()->Call(GetI32WasmCallDescriptor(zone(), descriptor));
|
||||
NodeProperties::ChangeOp(node, op);
|
||||
NodeProperties::ChangeOp(
|
||||
node, common()->Call(GetI32WasmCallDescriptor(zone(), descriptor)));
|
||||
}
|
||||
if (descriptor->ReturnCount() == 1 &&
|
||||
descriptor->GetReturnType(0) == MachineType::Int64()) {
|
||||
|
@ -3041,16 +3041,15 @@ void WasmGraphBuilder::BuildWasmToWasmWrapper(Handle<Code> target,
|
||||
args[pos++] = *effect_;
|
||||
args[pos++] = *control_;
|
||||
|
||||
// Call the wasm code.
|
||||
CallDescriptor* desc = GetWasmCallDescriptor(jsgraph()->zone(), sig_);
|
||||
Node* call = graph()->NewNode(jsgraph()->common()->Call(desc), count, args);
|
||||
*effect_ = call;
|
||||
Node* retval = sig_->return_count() == 0 ? jsgraph()->Int32Constant(0) : call;
|
||||
Return(retval);
|
||||
// Tail-call the wasm code.
|
||||
CallDescriptor* desc = GetWasmCallDescriptor(jsgraph()->zone(), sig_, true);
|
||||
Node* tail_call =
|
||||
graph()->NewNode(jsgraph()->common()->TailCall(desc), count, args);
|
||||
MergeControlToEnd(jsgraph(), tail_call);
|
||||
}
|
||||
|
||||
void WasmGraphBuilder::BuildWasmInterpreterEntry(
|
||||
uint32_t function_index, Handle<WasmInstanceObject> instance) {
|
||||
uint32_t func_index, Handle<WasmInstanceObject> instance) {
|
||||
int param_count = static_cast<int>(sig_->parameter_count());
|
||||
|
||||
// Build the start and the parameter nodes.
|
||||
@ -3095,9 +3094,9 @@ void WasmGraphBuilder::BuildWasmInterpreterEntry(
|
||||
// like a Smi (lowest bit not set). In the runtime function however, don't
|
||||
// call Smi::value on it, but just cast it to a byte pointer.
|
||||
Node* parameters[] = {
|
||||
jsgraph()->HeapConstant(instance), // wasm instance
|
||||
jsgraph()->SmiConstant(function_index), // function index
|
||||
arg_buffer, // argument buffer
|
||||
jsgraph()->HeapConstant(instance), // wasm instance
|
||||
jsgraph()->SmiConstant(func_index), // function index
|
||||
arg_buffer, // argument buffer
|
||||
};
|
||||
BuildCallToRuntime(Runtime::kWasmRunInterpreter, parameters,
|
||||
arraysize(parameters));
|
||||
@ -3194,8 +3193,8 @@ void WasmGraphBuilder::BuildCWasmEntry(Address wasm_context_address) {
|
||||
}
|
||||
|
||||
// This function is used by WasmFullDecoder to create a node that loads the
|
||||
// mem_start variable from the WasmContext. It should not be used directly by
|
||||
// the WasmGraphBuilder. The WasmGraphBuilder should directly use mem_start_,
|
||||
// {mem_start} variable from the WasmContext. It should not be used directly by
|
||||
// the WasmGraphBuilder. The WasmGraphBuilder should directly use {mem_start_},
|
||||
// which will always contain the correct node (stored in the SsaEnv).
|
||||
Node* WasmGraphBuilder::LoadMemStart() {
|
||||
DCHECK_NOT_NULL(wasm_context_);
|
||||
@ -3209,11 +3208,11 @@ Node* WasmGraphBuilder::LoadMemStart() {
|
||||
}
|
||||
|
||||
// This function is used by WasmFullDecoder to create a node that loads the
|
||||
// mem_size variable from the WasmContext. It should not be used directly by
|
||||
// the WasmGraphBuilder. The WasmGraphBuilder should directly use mem_size_,
|
||||
// {mem_size} variable from the WasmContext. It should not be used directly by
|
||||
// the WasmGraphBuilder. The WasmGraphBuilder should directly use {mem_size_},
|
||||
// which will always contain the correct node (stored in the SsaEnv).
|
||||
Node* WasmGraphBuilder::LoadMemSize() {
|
||||
// Load mem_size from the memory_context location at runtime.
|
||||
// Load mem_size from the WasmContext at runtime.
|
||||
DCHECK_NOT_NULL(wasm_context_);
|
||||
Node* mem_size = graph()->NewNode(
|
||||
jsgraph()->machine()->Load(MachineType::Uint32()), wasm_context_,
|
||||
@ -3224,6 +3223,39 @@ Node* WasmGraphBuilder::LoadMemSize() {
|
||||
return mem_size;
|
||||
}
|
||||
|
||||
void WasmGraphBuilder::GetGlobalBaseAndOffset(MachineType mem_type,
|
||||
uint32_t offset, Node** base_node,
|
||||
Node** offset_node) {
|
||||
DCHECK_NOT_NULL(wasm_context_);
|
||||
if (globals_start_ == nullptr) {
|
||||
// Load globals_start from the WasmContext at runtime.
|
||||
// TODO(wasm): we currently generate only one load of the {globals_start}
|
||||
// start per graph, which means it can be placed anywhere by the scheduler.
|
||||
// This is legal because the globals_start should never change.
|
||||
// However, in some cases (e.g. if the WasmContext is already in a
|
||||
// register), it is slightly more efficient to reload this value from the
|
||||
// WasmContext. Since this depends on register allocation, it is not
|
||||
// possible to express in the graph, and would essentially constitute a
|
||||
// "mem2reg" optimization in TurboFan.
|
||||
globals_start_ = graph()->NewNode(
|
||||
jsgraph()->machine()->Load(MachineType::UintPtr()), wasm_context_,
|
||||
jsgraph()->Int32Constant(
|
||||
static_cast<int32_t>(offsetof(WasmContext, globals_start))),
|
||||
graph()->start(), graph()->start());
|
||||
}
|
||||
*base_node = globals_start_;
|
||||
*offset_node = jsgraph()->Int32Constant(offset);
|
||||
|
||||
if (mem_type == MachineType::Simd128() && offset != 0) {
|
||||
// TODO(titzer,bbudge): code generation for SIMD memory offsets is broken.
|
||||
*base_node =
|
||||
graph()->NewNode(kPointerSize == 4 ? jsgraph()->machine()->Int32Add()
|
||||
: jsgraph()->machine()->Int64Add(),
|
||||
*base_node, *offset_node);
|
||||
*offset_node = jsgraph()->Int32Constant(0);
|
||||
}
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::MemBuffer(uint32_t offset) {
|
||||
DCHECK_NOT_NULL(*mem_start_);
|
||||
if (offset == 0) return *mem_start_;
|
||||
@ -3345,13 +3377,12 @@ Node* WasmGraphBuilder::BuildCallToRuntime(Runtime::FunctionId f,
|
||||
Node* WasmGraphBuilder::GetGlobal(uint32_t index) {
|
||||
MachineType mem_type =
|
||||
wasm::WasmOpcodes::MachineTypeFor(env_->module->globals[index].type);
|
||||
uintptr_t global_addr =
|
||||
env_->globals_start + env_->module->globals[index].offset;
|
||||
Node* addr = jsgraph()->RelocatableIntPtrConstant(
|
||||
global_addr, RelocInfo::WASM_GLOBAL_REFERENCE);
|
||||
const Operator* op = jsgraph()->machine()->Load(mem_type);
|
||||
Node* node = graph()->NewNode(op, addr, jsgraph()->Int32Constant(0), *effect_,
|
||||
*control_);
|
||||
Node* base = nullptr;
|
||||
Node* offset = nullptr;
|
||||
GetGlobalBaseAndOffset(mem_type, env_->module->globals[index].offset, &base,
|
||||
&offset);
|
||||
Node* node = graph()->NewNode(jsgraph()->machine()->Load(mem_type), base,
|
||||
offset, *effect_, *control_);
|
||||
*effect_ = node;
|
||||
return node;
|
||||
}
|
||||
@ -3359,14 +3390,13 @@ Node* WasmGraphBuilder::GetGlobal(uint32_t index) {
|
||||
Node* WasmGraphBuilder::SetGlobal(uint32_t index, Node* val) {
|
||||
MachineType mem_type =
|
||||
wasm::WasmOpcodes::MachineTypeFor(env_->module->globals[index].type);
|
||||
uintptr_t global_addr =
|
||||
env_->globals_start + env_->module->globals[index].offset;
|
||||
Node* addr = jsgraph()->RelocatableIntPtrConstant(
|
||||
global_addr, RelocInfo::WASM_GLOBAL_REFERENCE);
|
||||
Node* base = nullptr;
|
||||
Node* offset = nullptr;
|
||||
GetGlobalBaseAndOffset(mem_type, env_->module->globals[index].offset, &base,
|
||||
&offset);
|
||||
const Operator* op = jsgraph()->machine()->Store(
|
||||
StoreRepresentation(mem_type.representation(), kNoWriteBarrier));
|
||||
Node* node = graph()->NewNode(op, addr, jsgraph()->Int32Constant(0), val,
|
||||
*effect_, *control_);
|
||||
Node* node = graph()->NewNode(op, base, offset, val, *effect_, *control_);
|
||||
*effect_ = node;
|
||||
return node;
|
||||
}
|
||||
@ -4172,13 +4202,14 @@ Handle<Code> CompileJSToWasmWrapper(Isolate* isolate, wasm::WasmModule* module,
|
||||
Node* effect = nullptr;
|
||||
|
||||
// TODO(titzer): compile JS to WASM wrappers without a {ModuleEnv}.
|
||||
ModuleEnv env = {module,
|
||||
std::vector<Address>(), // function_tables
|
||||
std::vector<Address>(), // signature_tables
|
||||
std::vector<wasm::SignatureMap*>(), // signature_maps
|
||||
std::vector<Handle<Code>>(), // function_code
|
||||
BUILTIN_CODE(isolate, Illegal), // default_function_code
|
||||
0};
|
||||
ModuleEnv env = {
|
||||
module, // module itself
|
||||
std::vector<Address>(), // function_tables
|
||||
std::vector<Address>(), // signature_tables
|
||||
std::vector<wasm::SignatureMap*>(), // signature_maps
|
||||
std::vector<Handle<Code>>(), // function_code
|
||||
BUILTIN_CODE(isolate, Illegal) // default_function_code
|
||||
};
|
||||
|
||||
WasmGraphBuilder builder(&env, &zone, &jsgraph,
|
||||
CEntryStub(isolate, 1).GetCode(), func->sig);
|
||||
@ -4353,7 +4384,8 @@ Handle<Code> CompileWasmToJSWrapper(
|
||||
}
|
||||
|
||||
Handle<Code> CompileWasmToWasmWrapper(Isolate* isolate, Handle<Code> target,
|
||||
wasm::FunctionSig* sig, uint32_t index,
|
||||
wasm::FunctionSig* sig,
|
||||
uint32_t func_index,
|
||||
Address new_wasm_context_address) {
|
||||
//----------------------------------------------------------------------------
|
||||
// Create the Graph
|
||||
@ -4412,7 +4444,7 @@ Handle<Code> CompileWasmToWasmWrapper(Isolate* isolate, Handle<Code> target,
|
||||
}
|
||||
if (isolate->logger()->is_logging_code_events() || isolate->is_profiling()) {
|
||||
RecordFunctionCompilation(CodeEventListener::FUNCTION_TAG, isolate, code,
|
||||
"wasm-to-wasm#%d", index);
|
||||
"wasm-to-wasm");
|
||||
}
|
||||
|
||||
return code;
|
||||
|
@ -45,8 +45,7 @@ namespace compiler {
|
||||
// which the compiled code should be specialized, including which code to call
|
||||
// for direct calls {function_code}, which tables to use for indirect calls
|
||||
// {function_tables}, memory start address and size {mem_start, mem_size},
|
||||
// globals start address {globals_start}, as well as signature maps
|
||||
// {signature_maps} and the module itself {module}.
|
||||
// as well as signature maps {signature_maps} and the module itself {module}.
|
||||
// ModuleEnvs are shareable across multiple compilations.
|
||||
struct ModuleEnv {
|
||||
// A pointer to the decoded module's static representation.
|
||||
@ -70,8 +69,6 @@ struct ModuleEnv {
|
||||
const std::vector<Handle<Code>> function_code;
|
||||
// If the default code is not a null handle, always use it for direct calls.
|
||||
const Handle<Code> default_function_code;
|
||||
// Address of the start of the globals region.
|
||||
const uintptr_t globals_start;
|
||||
};
|
||||
|
||||
enum RuntimeExceptionSupport : bool {
|
||||
@ -151,7 +148,8 @@ Handle<Code> CompileJSToWasmWrapper(Isolate* isolate, wasm::WasmModule* module,
|
||||
// Wraps a wasm function, producing a code object that can be called from other
|
||||
// wasm instances (the WasmContext address must be changed).
|
||||
Handle<Code> CompileWasmToWasmWrapper(Isolate* isolate, Handle<Code> target,
|
||||
wasm::FunctionSig* sig, uint32_t index,
|
||||
wasm::FunctionSig* sig,
|
||||
uint32_t func_index,
|
||||
Address new_wasm_context_address);
|
||||
|
||||
// Compiles a stub that redirects a call to a wasm function to the wasm
|
||||
@ -323,6 +321,8 @@ class WasmGraphBuilder {
|
||||
|
||||
Node* LoadMemSize();
|
||||
Node* LoadMemStart();
|
||||
void GetGlobalBaseAndOffset(MachineType mem_type, uint32_t offset,
|
||||
Node** base_node, Node** offset_node);
|
||||
|
||||
void set_mem_size(Node** mem_size) { this->mem_size_ = mem_size; }
|
||||
|
||||
@ -373,6 +373,7 @@ class WasmGraphBuilder {
|
||||
Node** effect_ = nullptr;
|
||||
Node** mem_size_ = nullptr;
|
||||
Node** mem_start_ = nullptr;
|
||||
Node* globals_start_ = nullptr;
|
||||
Node** cur_buffer_;
|
||||
size_t cur_bufsize_;
|
||||
Node* def_buffer_[kDefaultBufferSize];
|
||||
@ -538,8 +539,8 @@ class WasmGraphBuilder {
|
||||
// call descriptors. This is used by the Int64Lowering::LowerNode method.
|
||||
constexpr int kWasmContextParameterIndex = 0;
|
||||
|
||||
V8_EXPORT_PRIVATE CallDescriptor* GetWasmCallDescriptor(Zone* zone,
|
||||
wasm::FunctionSig* sig);
|
||||
V8_EXPORT_PRIVATE CallDescriptor* GetWasmCallDescriptor(
|
||||
Zone* zone, wasm::FunctionSig* sig, bool supports_tails_calls = false);
|
||||
V8_EXPORT_PRIVATE CallDescriptor* GetI32WasmCallDescriptor(
|
||||
Zone* zone, CallDescriptor* descriptor);
|
||||
V8_EXPORT_PRIVATE CallDescriptor* GetI32WasmCallDescriptorForSimd(
|
||||
|
@ -221,7 +221,8 @@ static constexpr Allocator parameter_registers(kGPParamRegisters,
|
||||
} // namespace
|
||||
|
||||
// General code uses the above configuration data.
|
||||
CallDescriptor* GetWasmCallDescriptor(Zone* zone, wasm::FunctionSig* fsig) {
|
||||
CallDescriptor* GetWasmCallDescriptor(Zone* zone, wasm::FunctionSig* fsig,
|
||||
bool supports_tail_calls) {
|
||||
// The '+ 1' here is to accomodate the wasm_context as first parameter.
|
||||
LocationSignature::Builder locations(zone, fsig->return_count(),
|
||||
fsig->parameter_count() + 1);
|
||||
@ -254,6 +255,8 @@ CallDescriptor* GetWasmCallDescriptor(Zone* zone, wasm::FunctionSig* fsig) {
|
||||
MachineType target_type = MachineType::AnyTagged();
|
||||
LinkageLocation target_loc = LinkageLocation::ForAnyRegister(target_type);
|
||||
|
||||
CallDescriptor::Flags flags = CallDescriptor::kUseNativeStack;
|
||||
if (supports_tail_calls) flags |= CallDescriptor::kSupportsTailCalls;
|
||||
return new (zone) CallDescriptor( // --
|
||||
CallDescriptor::kCallCodeObject, // kind
|
||||
target_type, // target MachineType
|
||||
@ -263,7 +266,7 @@ CallDescriptor* GetWasmCallDescriptor(Zone* zone, wasm::FunctionSig* fsig) {
|
||||
compiler::Operator::kNoProperties, // properties
|
||||
kCalleeSaveRegisters, // callee-saved registers
|
||||
kCalleeSaveFPRegisters, // callee-saved fp regs
|
||||
CallDescriptor::kUseNativeStack, // flags
|
||||
flags, // flags
|
||||
"wasm-call");
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "src/ostreams.h"
|
||||
#include "src/regexp/jsregexp.h"
|
||||
#include "src/transitions-inl.h"
|
||||
#include "src/wasm/wasm-objects-inl.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -1087,6 +1088,14 @@ void JSFunction::JSFunctionPrint(std::ostream& os) { // NOLINT
|
||||
os << "\n - bytecode = " << shared()->bytecode_array();
|
||||
}
|
||||
}
|
||||
if (WasmExportedFunction::IsWasmExportedFunction(this)) {
|
||||
WasmExportedFunction* function = WasmExportedFunction::cast(this);
|
||||
os << "\n - WASM instance "
|
||||
<< reinterpret_cast<void*>(function->instance());
|
||||
os << "\n context "
|
||||
<< reinterpret_cast<void*>(function->instance()->wasm_context()->get());
|
||||
os << "\n - WASM function index " << function->function_index();
|
||||
}
|
||||
shared()->PrintSourceCode(os);
|
||||
JSObjectPrintBody(os, this);
|
||||
os << "\n - feedback vector: ";
|
||||
|
@ -714,16 +714,12 @@ compiler::ModuleEnv CreateModuleEnvFromCompiledModule(
|
||||
|
||||
std::vector<Handle<Code>> empty_code;
|
||||
|
||||
compiler::ModuleEnv result = {
|
||||
module, // --
|
||||
function_tables, // --
|
||||
signature_tables, // --
|
||||
signature_maps, // --
|
||||
empty_code, // --
|
||||
BUILTIN_CODE(isolate, WasmCompileLazy), // --
|
||||
reinterpret_cast<uintptr_t>( // --
|
||||
compiled_module->GetGlobalsStartOrNull()) // --
|
||||
};
|
||||
compiler::ModuleEnv result = {module, // --
|
||||
function_tables, // --
|
||||
signature_tables, // --
|
||||
signature_maps, // --
|
||||
empty_code, // --
|
||||
BUILTIN_CODE(isolate, WasmCompileLazy)};
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -1345,47 +1341,54 @@ bool in_bounds(uint32_t offset, uint32_t size, uint32_t upper) {
|
||||
using WasmInstanceMap =
|
||||
IdentityMap<Handle<WasmInstanceObject>, FreeStoreAllocationPolicy>;
|
||||
|
||||
Handle<Code> MakeWasmToWasmWrapper(
|
||||
Isolate* isolate, Handle<WasmExportedFunction> imported_function,
|
||||
FunctionSig* expected_sig, FunctionSig** sig,
|
||||
WasmInstanceMap* imported_instances, Handle<WasmInstanceObject> instance) {
|
||||
// TODO(wasm): cache WASM-to-WASM wrappers by signature and clone+patch.
|
||||
Handle<WasmInstanceObject> imported_instance(imported_function->instance(),
|
||||
isolate);
|
||||
imported_instances->Set(imported_instance, imported_instance);
|
||||
Handle<Code> wasm_code = imported_function->GetWasmCode();
|
||||
WasmContext* new_wasm_context = imported_instance->wasm_context()->get();
|
||||
Address new_wasm_context_address =
|
||||
reinterpret_cast<Address>(new_wasm_context);
|
||||
*sig = imported_instance->module()
|
||||
->functions[imported_function->function_index()]
|
||||
.sig;
|
||||
if (expected_sig && !expected_sig->Equals(*sig)) return Handle<Code>::null();
|
||||
|
||||
Handle<Code> wrapper_code = compiler::CompileWasmToWasmWrapper(
|
||||
isolate, wasm_code, *sig, imported_function->function_index(),
|
||||
new_wasm_context_address);
|
||||
// Set the deoptimization data for the WasmToWasm wrapper. This is
|
||||
// needed by the interpreter to find the imported instance for
|
||||
// a cross-instance call.
|
||||
Factory* factory = isolate->factory();
|
||||
Handle<WeakCell> weak_link = factory->NewWeakCell(imported_instance);
|
||||
Handle<FixedArray> deopt_data = factory->NewFixedArray(2, TENURED);
|
||||
deopt_data->set(0, *weak_link);
|
||||
auto function_index = Smi::FromInt(imported_function->function_index());
|
||||
deopt_data->set(1, function_index);
|
||||
wrapper_code->set_deoptimization_data(*deopt_data);
|
||||
return wrapper_code;
|
||||
}
|
||||
|
||||
Handle<Code> UnwrapExportOrCompileImportWrapper(
|
||||
Isolate* isolate, int index, FunctionSig* sig, Handle<JSReceiver> target,
|
||||
ModuleOrigin origin, WasmInstanceMap* imported_instances,
|
||||
Handle<FixedArray> js_imports_table, Handle<WasmInstanceObject> instance) {
|
||||
WasmFunction* other_func = GetWasmFunctionForExport(isolate, target);
|
||||
if (other_func) {
|
||||
if (!sig->Equals(other_func->sig)) return Handle<Code>::null();
|
||||
// Signature matched. Unwrap the import wrapper and return the raw wasm
|
||||
// function code.
|
||||
// Remember the wasm instance of the import. We have to keep it alive.
|
||||
Handle<WasmInstanceObject> imported_instance(
|
||||
Handle<WasmExportedFunction>::cast(target)->instance(), isolate);
|
||||
imported_instances->Set(imported_instance, imported_instance);
|
||||
Handle<Code> wasm_code =
|
||||
UnwrapExportWrapper(Handle<JSFunction>::cast(target));
|
||||
// Create a WasmToWasm wrapper to replace the current wasm context with
|
||||
// the imported_instance one, in order to access the right memory.
|
||||
// If the imported instance does not have memory, avoid the wrapper.
|
||||
// TODO(wasm): Avoid the wrapper also if instance memory and imported
|
||||
// instance share the same memory object.
|
||||
bool needs_wasm_to_wasm_wrapper = imported_instance->has_memory_object();
|
||||
if (!needs_wasm_to_wasm_wrapper) return wasm_code;
|
||||
Address new_wasm_context =
|
||||
reinterpret_cast<Address>(imported_instance->wasm_context());
|
||||
Handle<Code> wrapper_code = compiler::CompileWasmToWasmWrapper(
|
||||
isolate, wasm_code, sig, index, new_wasm_context);
|
||||
// Set the deoptimization data for the WasmToWasm wrapper.
|
||||
// TODO(wasm): Remove the deoptimization data when we will use tail calls
|
||||
// for WasmToWasm wrappers.
|
||||
Factory* factory = isolate->factory();
|
||||
Handle<WeakCell> weak_link = factory->NewWeakCell(instance);
|
||||
Handle<FixedArray> deopt_data = factory->NewFixedArray(2, TENURED);
|
||||
deopt_data->set(0, *weak_link);
|
||||
deopt_data->set(1, Smi::FromInt(index));
|
||||
wrapper_code->set_deoptimization_data(*deopt_data);
|
||||
return wrapper_code;
|
||||
Isolate* isolate, FunctionSig* sig, Handle<JSReceiver> target,
|
||||
uint32_t import_index, ModuleOrigin origin,
|
||||
WasmInstanceMap* imported_instances, Handle<FixedArray> js_imports_table,
|
||||
Handle<WasmInstanceObject> instance) {
|
||||
if (WasmExportedFunction::IsWasmExportedFunction(*target)) {
|
||||
FunctionSig* unused = nullptr;
|
||||
return MakeWasmToWasmWrapper(isolate,
|
||||
Handle<WasmExportedFunction>::cast(target),
|
||||
sig, &unused, imported_instances, instance);
|
||||
}
|
||||
// No wasm function or being debugged. Compile a new wrapper for the new
|
||||
// signature.
|
||||
return compiler::CompileWasmToJSWrapper(isolate, target, sig, index, origin,
|
||||
js_imports_table);
|
||||
return compiler::CompileWasmToJSWrapper(isolate, target, sig, import_index,
|
||||
origin, js_imports_table);
|
||||
}
|
||||
|
||||
double MonotonicallyIncreasingTimeInMs() {
|
||||
@ -1428,8 +1431,7 @@ std::unique_ptr<compiler::ModuleEnv> CreateDefaultModuleEnv(
|
||||
signature_tables, // --
|
||||
signature_maps, // --
|
||||
empty_code, // --
|
||||
illegal_builtin, // --
|
||||
0 // --
|
||||
illegal_builtin // --
|
||||
};
|
||||
return std::unique_ptr<compiler::ModuleEnv>(new compiler::ModuleEnv(result));
|
||||
}
|
||||
@ -1752,17 +1754,8 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
|
||||
thrower_->RangeError("Out of memory: wasm globals");
|
||||
return {};
|
||||
}
|
||||
Address old_globals_start = compiled_module_->GetGlobalsStartOrNull();
|
||||
Address new_globals_start =
|
||||
static_cast<Address>(global_buffer->backing_store());
|
||||
code_specialization.RelocateGlobals(old_globals_start, new_globals_start);
|
||||
// The address of the backing buffer for the golbals is in native memory
|
||||
// and, thus, not moving. We need it saved for
|
||||
// serialization/deserialization purposes - so that the other end
|
||||
// understands how to relocate the references. We still need to save the
|
||||
// JSArrayBuffer on the instance, to keep it all alive.
|
||||
WasmCompiledModule::SetGlobalsStartAddressFrom(factory, compiled_module_,
|
||||
global_buffer);
|
||||
instance->wasm_context()->get()->globals_start =
|
||||
reinterpret_cast<byte*>(global_buffer->backing_store());
|
||||
instance->set_globals_buffer(*global_buffer);
|
||||
}
|
||||
|
||||
@ -1861,26 +1854,25 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Create a memory object to have a WasmContext.
|
||||
// Create a memory object if there is not already one.
|
||||
//--------------------------------------------------------------------------
|
||||
if (module_->has_memory) {
|
||||
if (!instance->has_memory_object()) {
|
||||
Handle<WasmMemoryObject> memory_object = WasmMemoryObject::New(
|
||||
isolate_,
|
||||
instance->has_memory_buffer() ? handle(instance->memory_buffer())
|
||||
: Handle<JSArrayBuffer>::null(),
|
||||
module_->maximum_pages != 0 ? module_->maximum_pages : -1);
|
||||
instance->set_memory_object(*memory_object);
|
||||
}
|
||||
|
||||
code_specialization.RelocateWasmContextReferences(
|
||||
reinterpret_cast<Address>(instance->wasm_context()));
|
||||
// Store the wasm_context address in the JSToWasmWrapperCache so that it can
|
||||
// be used to compile JSToWasmWrappers.
|
||||
js_to_wasm_cache_.SetContextAddress(
|
||||
reinterpret_cast<Address>(instance->wasm_context()));
|
||||
if (module_->has_memory && !instance->has_memory_object()) {
|
||||
Handle<WasmMemoryObject> memory_object = WasmMemoryObject::New(
|
||||
isolate_,
|
||||
instance->has_memory_buffer() ? handle(instance->memory_buffer())
|
||||
: Handle<JSArrayBuffer>::null(),
|
||||
module_->maximum_pages != 0 ? module_->maximum_pages : -1);
|
||||
instance->set_memory_object(*memory_object);
|
||||
}
|
||||
|
||||
// Set the WasmContext address in wrappers.
|
||||
// TODO(wasm): the wasm context should only appear as a constant in wrappers;
|
||||
// this code specialization is applied to the whole instance.
|
||||
WasmContext* wasm_context = instance->wasm_context()->get();
|
||||
Address wasm_context_address = reinterpret_cast<Address>(wasm_context);
|
||||
code_specialization.RelocateWasmContextReferences(wasm_context_address);
|
||||
js_to_wasm_cache_.SetContextAddress(wasm_context_address);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Set up the runtime support for the new instance.
|
||||
//--------------------------------------------------------------------------
|
||||
@ -2146,8 +2138,9 @@ void InstanceBuilder::LoadDataSegments(Address mem_addr, size_t mem_size) {
|
||||
void InstanceBuilder::WriteGlobalValue(WasmGlobal& global,
|
||||
Handle<Object> value) {
|
||||
double num = value->Number();
|
||||
TRACE("init [globals+%u] = %lf, type = %s\n", global.offset, num,
|
||||
WasmOpcodes::TypeName(global.type));
|
||||
TRACE("init [globals_start=%p + %u] = %lf, type = %s\n",
|
||||
reinterpret_cast<void*>(raw_buffer_ptr(globals_, 0)), global.offset,
|
||||
num, WasmOpcodes::TypeName(global.type));
|
||||
switch (global.type) {
|
||||
case kWasmI32:
|
||||
*GetRawGlobalPtr<int32_t>(global) = static_cast<int32_t>(num);
|
||||
@ -2256,8 +2249,8 @@ int InstanceBuilder::ProcessImports(Handle<FixedArray> code_table,
|
||||
}
|
||||
|
||||
Handle<Code> import_code = UnwrapExportOrCompileImportWrapper(
|
||||
isolate_, index, module_->functions[import.index].sig,
|
||||
Handle<JSReceiver>::cast(value), module_->origin(),
|
||||
isolate_, module_->functions[import.index].sig,
|
||||
Handle<JSReceiver>::cast(value), index, module_->origin(),
|
||||
&imported_wasm_instances, js_imports_table, instance);
|
||||
if (import_code.is_null()) {
|
||||
ReportLinkError("imported function does not match the expected type",
|
||||
@ -2323,16 +2316,19 @@ int InstanceBuilder::ProcessImports(Handle<FixedArray> code_table,
|
||||
for (int i = 0; i < table_size; ++i) {
|
||||
Handle<Object> val(table_instance.js_wrappers->get(i), isolate_);
|
||||
if (!val->IsJSFunction()) continue;
|
||||
WasmFunction* function = GetWasmFunctionForExport(isolate_, val);
|
||||
if (function == nullptr) {
|
||||
if (!WasmExportedFunction::IsWasmExportedFunction(*val)) {
|
||||
thrower_->LinkError("table import %d[%d] is not a wasm function",
|
||||
index, i);
|
||||
return -1;
|
||||
}
|
||||
int sig_index = table.map.FindOrInsert(function->sig);
|
||||
auto target = Handle<WasmExportedFunction>::cast(val);
|
||||
FunctionSig* sig = nullptr;
|
||||
Handle<Code> code =
|
||||
MakeWasmToWasmWrapper(isolate_, target, nullptr, &sig,
|
||||
&imported_wasm_instances, instance);
|
||||
int sig_index = table.map.FindOrInsert(sig);
|
||||
table_instance.signature_table->set(i, Smi::FromInt(sig_index));
|
||||
table_instance.function_table->set(
|
||||
i, *UnwrapExportWrapper(Handle<JSFunction>::cast(val)));
|
||||
table_instance.function_table->set(i, *code);
|
||||
}
|
||||
|
||||
num_imported_tables++;
|
||||
|
@ -79,12 +79,6 @@ void CodeSpecialization::RelocateWasmContextReferences(Address new_context) {
|
||||
new_wasm_context_address = new_context;
|
||||
}
|
||||
|
||||
void CodeSpecialization::RelocateGlobals(Address old_start, Address new_start) {
|
||||
DCHECK(old_globals_start == 0 && new_globals_start == 0);
|
||||
old_globals_start = old_start;
|
||||
new_globals_start = new_start;
|
||||
}
|
||||
|
||||
void CodeSpecialization::PatchTableSize(uint32_t old_size, uint32_t new_size) {
|
||||
DCHECK(old_function_table_size == 0 && new_function_table_size == 0);
|
||||
old_function_table_size = old_size;
|
||||
@ -178,7 +172,6 @@ bool CodeSpecialization::ApplyToWasmCode(Code* code,
|
||||
DisallowHeapAllocation no_gc;
|
||||
DCHECK_EQ(Code::WASM_FUNCTION, code->kind());
|
||||
|
||||
bool reloc_globals = old_globals_start || new_globals_start;
|
||||
bool patch_table_size = old_function_table_size || new_function_table_size;
|
||||
bool reloc_direct_calls = !relocate_direct_calls_instance.is_null();
|
||||
bool reloc_pointers = pointers_to_relocate.size() > 0;
|
||||
@ -187,7 +180,6 @@ bool CodeSpecialization::ApplyToWasmCode(Code* code,
|
||||
auto add_mode = [&reloc_mode](bool cond, RelocInfo::Mode mode) {
|
||||
if (cond) reloc_mode |= RelocInfo::ModeMask(mode);
|
||||
};
|
||||
add_mode(reloc_globals, RelocInfo::WASM_GLOBAL_REFERENCE);
|
||||
add_mode(patch_table_size, RelocInfo::WASM_FUNCTION_TABLE_SIZE_REFERENCE);
|
||||
add_mode(reloc_direct_calls, RelocInfo::CODE_TARGET);
|
||||
add_mode(reloc_pointers, RelocInfo::WASM_GLOBAL_HANDLE);
|
||||
@ -198,13 +190,6 @@ bool CodeSpecialization::ApplyToWasmCode(Code* code,
|
||||
for (RelocIterator it(code, reloc_mode); !it.done(); it.next()) {
|
||||
RelocInfo::Mode mode = it.rinfo()->rmode();
|
||||
switch (mode) {
|
||||
case RelocInfo::WASM_GLOBAL_REFERENCE:
|
||||
DCHECK(reloc_globals);
|
||||
it.rinfo()->update_wasm_global_reference(
|
||||
code->GetIsolate(), old_globals_start, new_globals_start,
|
||||
icache_flush_mode);
|
||||
changed = true;
|
||||
break;
|
||||
case RelocInfo::CODE_TARGET: {
|
||||
DCHECK(reloc_direct_calls);
|
||||
// Skip everything which is not a wasm call (stack checks, traps, ...).
|
||||
|
@ -30,8 +30,6 @@ class CodeSpecialization {
|
||||
|
||||
// Update WasmContext references.
|
||||
void RelocateWasmContextReferences(Address new_context);
|
||||
// Update references to global variables.
|
||||
void RelocateGlobals(Address old_start, Address new_start);
|
||||
// Update function table size.
|
||||
// TODO(wasm): Prepare this for more than one indirect function table.
|
||||
void PatchTableSize(uint32_t old_size, uint32_t new_size);
|
||||
@ -50,9 +48,6 @@ class CodeSpecialization {
|
||||
private:
|
||||
Address new_wasm_context_address = 0;
|
||||
|
||||
Address old_globals_start = 0;
|
||||
Address new_globals_start = 0;
|
||||
|
||||
uint32_t old_function_table_size = 0;
|
||||
uint32_t new_function_table_size = 0;
|
||||
|
||||
|
@ -138,25 +138,17 @@ class InterpreterHandle {
|
||||
|
||||
static uint32_t GetMemSize(WasmDebugInfo* debug_info) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
return debug_info->wasm_instance()->has_memory_object()
|
||||
? debug_info->wasm_instance()->wasm_context()->mem_size
|
||||
: 0;
|
||||
return debug_info->wasm_instance()->wasm_context()->get()->mem_size;
|
||||
}
|
||||
|
||||
static byte* GetMemStart(WasmDebugInfo* debug_info) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
return debug_info->wasm_instance()->has_memory_object()
|
||||
? debug_info->wasm_instance()->wasm_context()->mem_start
|
||||
: nullptr;
|
||||
return debug_info->wasm_instance()->wasm_context()->get()->mem_start;
|
||||
}
|
||||
|
||||
static byte* GetGlobalsStart(WasmDebugInfo* debug_info) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
WasmCompiledModule* compiled_module =
|
||||
debug_info->wasm_instance()->compiled_module();
|
||||
return reinterpret_cast<byte*>(compiled_module->has_globals_start()
|
||||
? compiled_module->globals_start()
|
||||
: 0);
|
||||
return debug_info->wasm_instance()->wasm_context()->get()->globals_start;
|
||||
}
|
||||
|
||||
public:
|
||||
|
@ -628,8 +628,8 @@ inline int32_t ExecuteGrowMemory(uint32_t delta_pages,
|
||||
// Ensure the effects of GrowMemory have been observed by the interpreter.
|
||||
// See {UpdateMemory}. In all cases, we are in agreement with the runtime
|
||||
// object's view.
|
||||
DCHECK_EQ(mem_info->mem_size, instance->wasm_context()->mem_size);
|
||||
DCHECK_EQ(mem_info->mem_start, instance->wasm_context()->mem_start);
|
||||
DCHECK_EQ(mem_info->mem_size, instance->wasm_context()->get()->mem_size);
|
||||
DCHECK_EQ(mem_info->mem_start, instance->wasm_context()->get()->mem_start);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -714,6 +714,7 @@ void WebAssemblyTableSet(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
|
||||
// Parameter 1.
|
||||
i::Handle<i::Object> value = Utils::OpenHandle(*args[1]);
|
||||
// TODO(titzer): use WasmExportedFunction::IsWasmExportedFunction() here.
|
||||
if (!value->IsNull(i_isolate) &&
|
||||
(!value->IsJSFunction() ||
|
||||
i::Handle<i::JSFunction>::cast(value)->code()->kind() !=
|
||||
|
@ -128,31 +128,6 @@ WasmFunction* GetWasmFunctionForExport(Isolate* isolate,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Handle<Code> UnwrapExportWrapper(Handle<JSFunction> export_wrapper) {
|
||||
Handle<Code> export_wrapper_code = handle(export_wrapper->code());
|
||||
DCHECK_EQ(export_wrapper_code->kind(), Code::JS_TO_WASM_FUNCTION);
|
||||
int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET);
|
||||
for (RelocIterator it(*export_wrapper_code, mask);; it.next()) {
|
||||
DCHECK(!it.done());
|
||||
Code* target = Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
|
||||
if (target->kind() != Code::WASM_FUNCTION &&
|
||||
target->kind() != Code::WASM_TO_JS_FUNCTION &&
|
||||
target->kind() != Code::WASM_INTERPRETER_ENTRY)
|
||||
continue;
|
||||
// There should only be this one call to wasm code.
|
||||
#ifdef DEBUG
|
||||
for (it.next(); !it.done(); it.next()) {
|
||||
Code* code = Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
|
||||
DCHECK(code->kind() != Code::WASM_FUNCTION &&
|
||||
code->kind() != Code::WASM_TO_JS_FUNCTION &&
|
||||
code->kind() != Code::WASM_INTERPRETER_ENTRY);
|
||||
}
|
||||
#endif
|
||||
return handle(target);
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void UpdateDispatchTables(Isolate* isolate, Handle<FixedArray> dispatch_tables,
|
||||
int index, WasmFunction* function,
|
||||
Handle<Code> code) {
|
||||
|
@ -279,11 +279,9 @@ Handle<FixedArray> DecodeLocalNames(Isolate*, Handle<WasmCompiledModule>);
|
||||
// to the wrapped wasm function; in all other cases, return nullptr.
|
||||
// The returned pointer is owned by the wasm instance target belongs to. The
|
||||
// result is alive as long as the instance exists.
|
||||
// TODO(titzer): move this to WasmExportedFunction.
|
||||
WasmFunction* GetWasmFunctionForExport(Isolate* isolate, Handle<Object> target);
|
||||
|
||||
// {export_wrapper} is known to be an export.
|
||||
Handle<Code> UnwrapExportWrapper(Handle<JSFunction> export_wrapper);
|
||||
|
||||
void UpdateDispatchTables(Isolate* isolate, Handle<FixedArray> dispatch_tables,
|
||||
int index, WasmFunction* function, Handle<Code> code);
|
||||
|
||||
|
@ -39,10 +39,10 @@ ACCESSORS(WasmMemoryObject, array_buffer, JSArrayBuffer, kArrayBufferOffset)
|
||||
SMI_ACCESSORS(WasmMemoryObject, maximum_pages, kMaximumPagesOffset)
|
||||
OPTIONAL_ACCESSORS(WasmMemoryObject, instances, WeakFixedArray,
|
||||
kInstancesOffset)
|
||||
ACCESSORS(WasmMemoryObject, wasm_context, Managed<WasmContext>,
|
||||
kWasmContextOffset)
|
||||
|
||||
// WasmInstanceObject
|
||||
ACCESSORS(WasmInstanceObject, wasm_context, Managed<WasmContext>,
|
||||
kWasmContextOffset)
|
||||
ACCESSORS(WasmInstanceObject, compiled_module, WasmCompiledModule,
|
||||
kCompiledModuleOffset)
|
||||
ACCESSORS(WasmInstanceObject, exports_object, JSObject, kExportsObjectOffset)
|
||||
@ -151,29 +151,6 @@ FORWARD_SHARED(bool, is_asm_js)
|
||||
return handle(TYPE::cast(weak_##NAME()->value())); \
|
||||
}
|
||||
|
||||
#define WCM_LARGE_NUMBER(TYPE, NAME) \
|
||||
TYPE WasmCompiledModule::NAME() const { \
|
||||
Object* value = get(kID_##NAME); \
|
||||
DCHECK(value->IsMutableHeapNumber()); \
|
||||
return static_cast<TYPE>(HeapNumber::cast(value)->value()); \
|
||||
} \
|
||||
\
|
||||
void WasmCompiledModule::set_##NAME(TYPE value) { \
|
||||
Object* number = get(kID_##NAME); \
|
||||
DCHECK(number->IsMutableHeapNumber()); \
|
||||
HeapNumber::cast(number)->set_value(static_cast<double>(value)); \
|
||||
} \
|
||||
\
|
||||
void WasmCompiledModule::recreate_##NAME(Handle<WasmCompiledModule> obj, \
|
||||
Factory* factory, TYPE init_val) { \
|
||||
Handle<HeapNumber> number = factory->NewHeapNumber( \
|
||||
static_cast<double>(init_val), MutableMode::MUTABLE, TENURED); \
|
||||
obj->set(kID_##NAME, *number); \
|
||||
} \
|
||||
bool WasmCompiledModule::has_##NAME() const { \
|
||||
return get(kID_##NAME)->IsMutableHeapNumber(); \
|
||||
}
|
||||
|
||||
#define DEFINITION(KIND, TYPE, NAME) WCM_##KIND(TYPE, NAME)
|
||||
WCM_PROPERTY_TABLE(DEFINITION)
|
||||
#undef DECLARATION
|
||||
@ -192,11 +169,6 @@ bool WasmTableObject::has_maximum_length() {
|
||||
|
||||
bool WasmMemoryObject::has_maximum_pages() { return maximum_pages() >= 0; }
|
||||
|
||||
Address WasmCompiledModule::GetGlobalsStartOrNull() const {
|
||||
return has_globals_start() ? reinterpret_cast<Address>(globals_start())
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
void WasmCompiledModule::ReplaceCodeTableForTesting(
|
||||
Handle<FixedArray> testing_table) {
|
||||
set_code_table(testing_table);
|
||||
|
@ -298,12 +298,19 @@ void WasmTableObject::Set(Isolate* isolate, Handle<WasmTableObject> table,
|
||||
Handle<Object> value = isolate->factory()->null_value();
|
||||
|
||||
if (!function.is_null()) {
|
||||
auto exported_function = Handle<WasmExportedFunction>::cast(function);
|
||||
wasm_function = wasm::GetWasmFunctionForExport(isolate, function);
|
||||
// The verification that {function} is an export was done
|
||||
// by the caller.
|
||||
DCHECK_NOT_NULL(wasm_function);
|
||||
code = wasm::UnwrapExportWrapper(function);
|
||||
value = Handle<Object>::cast(function);
|
||||
value = function;
|
||||
// TODO(titzer): Make JSToWasm wrappers just call the WASM to WASM wrapper,
|
||||
// and then we can just reuse the WASM to WASM wrapper.
|
||||
Address new_context_address = reinterpret_cast<Address>(
|
||||
exported_function->instance()->wasm_context()->get());
|
||||
code = compiler::CompileWasmToWasmWrapper(
|
||||
isolate, exported_function->GetWasmCode(), wasm_function->sig,
|
||||
exported_function->function_index(), new_context_address);
|
||||
}
|
||||
|
||||
UpdateDispatchTables(isolate, dispatch_tables, index, wasm_function, code);
|
||||
@ -364,15 +371,18 @@ void SetInstanceMemory(Isolate* isolate, Handle<WasmInstanceObject> instance,
|
||||
if (instance->has_debug_info()) {
|
||||
instance->debug_info()->UpdateMemory(*buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateWasmContext(WasmContext* wasm_context,
|
||||
Handle<JSArrayBuffer> buffer) {
|
||||
uint32_t new_mem_size = buffer->byte_length()->Number();
|
||||
Address new_mem_start = static_cast<Address>(buffer->backing_store());
|
||||
DCHECK_NOT_NULL(new_mem_start);
|
||||
wasm_context->mem_start = new_mem_start;
|
||||
wasm_context->mem_size = new_mem_size;
|
||||
auto wasm_context = instance->wasm_context()->get();
|
||||
wasm_context->mem_start = reinterpret_cast<byte*>(buffer->backing_store());
|
||||
wasm_context->mem_size = buffer->byte_length()->Number();
|
||||
#if DEBUG
|
||||
// To flush out bugs earlier, in DEBUG mode, check that all pages of the
|
||||
// memory are accessible by reading and writing one byte on each page.
|
||||
for (uint32_t offset = 0; offset < wasm_context->mem_size;
|
||||
offset += WasmModule::kPageSize) {
|
||||
byte val = wasm_context->mem_start[offset];
|
||||
wasm_context->mem_start[offset] = val;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@ -384,21 +394,19 @@ Handle<WasmMemoryObject> WasmMemoryObject::New(Isolate* isolate,
|
||||
isolate->native_context()->wasm_memory_constructor());
|
||||
auto memory_obj = Handle<WasmMemoryObject>::cast(
|
||||
isolate->factory()->NewJSObject(memory_ctor, TENURED));
|
||||
auto wasm_context = Managed<WasmContext>::Allocate(isolate);
|
||||
|
||||
if (buffer.is_null()) {
|
||||
const bool enable_guard_regions = trap_handler::UseTrapHandler();
|
||||
// If no buffer was provided, create a 0-length one.
|
||||
buffer = wasm::SetupArrayBuffer(isolate, nullptr, 0, nullptr, 0, false,
|
||||
enable_guard_regions);
|
||||
wasm_context->get()->mem_size = 0;
|
||||
wasm_context->get()->mem_start = nullptr;
|
||||
trap_handler::UseTrapHandler());
|
||||
} else {
|
||||
CHECK(buffer->byte_length()->ToUint32(&wasm_context->get()->mem_size));
|
||||
wasm_context->get()->mem_start =
|
||||
static_cast<Address>(buffer->backing_store());
|
||||
// Paranoid check that the buffer size makes sense.
|
||||
uint32_t mem_size = 0;
|
||||
CHECK(buffer->byte_length()->ToUint32(&mem_size));
|
||||
}
|
||||
memory_obj->set_array_buffer(*buffer);
|
||||
memory_obj->set_maximum_pages(maximum);
|
||||
memory_obj->set_wasm_context(*wasm_context);
|
||||
|
||||
return memory_obj;
|
||||
}
|
||||
|
||||
@ -418,6 +426,8 @@ void WasmMemoryObject::AddInstance(Isolate* isolate,
|
||||
Handle<WeakFixedArray> new_instances =
|
||||
WeakFixedArray::Add(old_instances, instance);
|
||||
memory->set_instances(*new_instances);
|
||||
Handle<JSArrayBuffer> buffer(memory->array_buffer(), isolate);
|
||||
SetInstanceMemory(isolate, instance, buffer);
|
||||
}
|
||||
|
||||
void WasmMemoryObject::RemoveInstance(Isolate* isolate,
|
||||
@ -461,29 +471,19 @@ int32_t WasmMemoryObject::Grow(Isolate* isolate,
|
||||
Handle<JSArrayBuffer> old_buffer(memory_object->array_buffer());
|
||||
uint32_t old_size = 0;
|
||||
CHECK(old_buffer->byte_length()->ToUint32(&old_size));
|
||||
DCHECK_EQ(0, old_size % WasmModule::kPageSize);
|
||||
Handle<JSArrayBuffer> new_buffer;
|
||||
// Return current size if grow by 0.
|
||||
if (pages == 0) {
|
||||
DCHECK_EQ(0, old_size % WasmModule::kPageSize);
|
||||
return old_size / WasmModule::kPageSize;
|
||||
}
|
||||
if (pages == 0) return old_size / WasmModule::kPageSize;
|
||||
|
||||
uint32_t maximum_pages;
|
||||
uint32_t maximum_pages = FLAG_wasm_max_mem_pages;
|
||||
if (memory_object->has_maximum_pages()) {
|
||||
maximum_pages = Min(FLAG_wasm_max_mem_pages,
|
||||
static_cast<uint32_t>(memory_object->maximum_pages()));
|
||||
} else {
|
||||
maximum_pages = FLAG_wasm_max_mem_pages;
|
||||
}
|
||||
new_buffer = GrowMemoryBuffer(isolate, old_buffer, pages, maximum_pages);
|
||||
if (new_buffer.is_null()) return -1;
|
||||
|
||||
// Verify that the values we will change are actually the ones we expect.
|
||||
DCHECK_EQ(memory_object->wasm_context()->get()->mem_size, old_size);
|
||||
DCHECK_EQ(memory_object->wasm_context()->get()->mem_start,
|
||||
static_cast<Address>(old_buffer->backing_store()));
|
||||
UpdateWasmContext(memory_object->wasm_context()->get(), new_buffer);
|
||||
|
||||
if (memory_object->has_instances()) {
|
||||
Handle<WeakFixedArray> instances(memory_object->instances(), isolate);
|
||||
for (int i = 0; i < instances->Length(); i++) {
|
||||
@ -495,7 +495,6 @@ int32_t WasmMemoryObject::Grow(Isolate* isolate,
|
||||
}
|
||||
}
|
||||
memory_object->set_array_buffer(*new_buffer);
|
||||
DCHECK_EQ(0, old_size % WasmModule::kPageSize);
|
||||
return old_size / WasmModule::kPageSize;
|
||||
}
|
||||
|
||||
@ -503,11 +502,6 @@ WasmModuleObject* WasmInstanceObject::module_object() {
|
||||
return *compiled_module()->wasm_module();
|
||||
}
|
||||
|
||||
WasmContext* WasmInstanceObject::wasm_context() {
|
||||
DCHECK(has_memory_object());
|
||||
return memory_object()->wasm_context()->get();
|
||||
}
|
||||
|
||||
WasmModule* WasmInstanceObject::module() { return compiled_module()->module(); }
|
||||
|
||||
Handle<WasmDebugInfo> WasmInstanceObject::GetOrCreateDebugInfo(
|
||||
@ -528,6 +522,12 @@ Handle<WasmInstanceObject> WasmInstanceObject::New(
|
||||
Handle<WasmInstanceObject> instance(
|
||||
reinterpret_cast<WasmInstanceObject*>(*instance_object), isolate);
|
||||
|
||||
auto wasm_context = Managed<WasmContext>::Allocate(isolate);
|
||||
wasm_context->get()->mem_start = nullptr;
|
||||
wasm_context->get()->mem_size = 0;
|
||||
wasm_context->get()->globals_start = nullptr;
|
||||
instance->set_wasm_context(*wasm_context);
|
||||
|
||||
instance->set_compiled_module(*compiled_module);
|
||||
return instance;
|
||||
}
|
||||
@ -681,6 +681,32 @@ Handle<WasmExportedFunction> WasmExportedFunction::New(
|
||||
return Handle<WasmExportedFunction>::cast(js_function);
|
||||
}
|
||||
|
||||
Handle<Code> WasmExportedFunction::GetWasmCode() {
|
||||
DisallowHeapAllocation no_gc;
|
||||
Handle<Code> export_wrapper_code = handle(this->code());
|
||||
DCHECK_EQ(export_wrapper_code->kind(), Code::JS_TO_WASM_FUNCTION);
|
||||
int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET);
|
||||
for (RelocIterator it(*export_wrapper_code, mask);; it.next()) {
|
||||
DCHECK(!it.done());
|
||||
Code* target = Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
|
||||
if (target->kind() != Code::WASM_FUNCTION &&
|
||||
target->kind() != Code::WASM_TO_JS_FUNCTION &&
|
||||
target->kind() != Code::WASM_INTERPRETER_ENTRY)
|
||||
continue;
|
||||
// There should only be this one call to wasm code.
|
||||
#ifdef DEBUG
|
||||
for (it.next(); !it.done(); it.next()) {
|
||||
Code* code = Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
|
||||
DCHECK(code->kind() != Code::WASM_FUNCTION &&
|
||||
code->kind() != Code::WASM_TO_JS_FUNCTION &&
|
||||
code->kind() != Code::WASM_INTERPRETER_ENTRY);
|
||||
}
|
||||
#endif
|
||||
return handle(target);
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
bool WasmSharedModuleData::IsWasmSharedModuleData(Object* object) {
|
||||
if (!object->IsFixedArray()) return false;
|
||||
FixedArray* arr = FixedArray::cast(object);
|
||||
@ -975,10 +1001,6 @@ Handle<WasmCompiledModule> WasmCompiledModule::Clone(
|
||||
ret->reset_weak_next_instance();
|
||||
ret->reset_weak_prev_instance();
|
||||
ret->reset_weak_exported_functions();
|
||||
if (ret->has_globals_start()) {
|
||||
WasmCompiledModule::recreate_globals_start(ret, isolate->factory(),
|
||||
ret->globals_start());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1015,13 +1037,6 @@ void WasmCompiledModule::Reset(Isolate* isolate,
|
||||
Zone specialization_zone(isolate->allocator(), ZONE_NAME);
|
||||
wasm::CodeSpecialization code_specialization(isolate, &specialization_zone);
|
||||
|
||||
if (compiled_module->has_globals_start()) {
|
||||
Address globals_start =
|
||||
reinterpret_cast<Address>(compiled_module->globals_start());
|
||||
code_specialization.RelocateGlobals(globals_start, nullptr);
|
||||
compiled_module->set_globals_start(0);
|
||||
}
|
||||
|
||||
// Reset function tables.
|
||||
if (compiled_module->has_function_tables()) {
|
||||
FixedArray* function_tables = compiled_module->ptr_to_function_tables();
|
||||
@ -1084,19 +1099,6 @@ void WasmCompiledModule::InitId() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void WasmCompiledModule::SetGlobalsStartAddressFrom(
|
||||
Factory* factory, Handle<WasmCompiledModule> compiled_module,
|
||||
Handle<JSArrayBuffer> buffer) {
|
||||
DCHECK(!buffer.is_null());
|
||||
size_t start_address = reinterpret_cast<size_t>(buffer->backing_store());
|
||||
if (!compiled_module->has_globals_start()) {
|
||||
WasmCompiledModule::recreate_globals_start(compiled_module, factory,
|
||||
start_address);
|
||||
} else {
|
||||
compiled_module->set_globals_start(start_address);
|
||||
}
|
||||
}
|
||||
|
||||
MaybeHandle<String> WasmCompiledModule::ExtractUtf8StringFromModuleBytes(
|
||||
Isolate* isolate, Handle<WasmCompiledModule> compiled_module,
|
||||
wasm::WireBytesRef ref) {
|
||||
@ -1127,7 +1129,6 @@ bool WasmCompiledModule::IsWasmCompiledModule(Object* obj) {
|
||||
if (!obj->IsFixedArray()) return false;
|
||||
FixedArray* arr = FixedArray::cast(obj);
|
||||
if (arr->length() != PropertyIndices::Count) return false;
|
||||
Isolate* isolate = arr->GetIsolate();
|
||||
#define WCM_CHECK_TYPE(NAME, TYPE_CHECK) \
|
||||
do { \
|
||||
Object* obj = arr->get(kID_##NAME); \
|
||||
@ -1150,9 +1151,6 @@ bool WasmCompiledModule::IsWasmCompiledModule(Object* obj) {
|
||||
#define WCM_CHECK(KIND, TYPE, NAME) WCM_CHECK_##KIND(TYPE, NAME)
|
||||
#define WCM_CHECK_SMALL_CONST_NUMBER(TYPE, NAME) \
|
||||
WCM_CHECK_TYPE(NAME, obj->IsSmi())
|
||||
#define WCM_CHECK_LARGE_NUMBER(TYPE, NAME) \
|
||||
WCM_CHECK_TYPE(NAME, obj->IsUndefined(isolate) || obj->IsMutableHeapNumber())
|
||||
WCM_PROPERTY_TABLE(WCM_CHECK)
|
||||
#undef WCM_CHECK_TYPE
|
||||
#undef WCM_CHECK_OBJECT
|
||||
#undef WCM_CHECK_CONST_OBJECT
|
||||
@ -1161,7 +1159,6 @@ bool WasmCompiledModule::IsWasmCompiledModule(Object* obj) {
|
||||
#undef WCM_CHECK_SMALL_NUMBER
|
||||
#undef WCM_CHECK
|
||||
#undef WCM_CHECK_SMALL_CONST_NUMBER
|
||||
#undef WCM_CHECK_LARGE_NUMBER
|
||||
|
||||
// All checks passed.
|
||||
return true;
|
||||
|
@ -61,6 +61,7 @@ class WasmInstanceObject;
|
||||
struct WasmContext {
|
||||
byte* mem_start;
|
||||
uint32_t mem_size;
|
||||
byte* globals_start;
|
||||
};
|
||||
|
||||
// Representation of a WebAssembly.Module JavaScript-level object.
|
||||
@ -171,6 +172,7 @@ class WasmInstanceObject : public JSObject {
|
||||
public:
|
||||
DECL_CAST(WasmInstanceObject)
|
||||
|
||||
DECL_ACCESSORS(wasm_context, Managed<WasmContext>)
|
||||
DECL_ACCESSORS(compiled_module, WasmCompiledModule)
|
||||
DECL_ACCESSORS(exports_object, JSObject)
|
||||
DECL_OPTIONAL_ACCESSORS(memory_object, WasmMemoryObject)
|
||||
@ -185,6 +187,7 @@ class WasmInstanceObject : public JSObject {
|
||||
DECL_ACCESSORS(js_imports_table, FixedArray)
|
||||
|
||||
enum { // --
|
||||
kWasmContextIndex,
|
||||
kCompiledModuleIndex,
|
||||
kExportsObjectIndex,
|
||||
kMemoryObjectIndex,
|
||||
@ -199,6 +202,7 @@ class WasmInstanceObject : public JSObject {
|
||||
};
|
||||
|
||||
DEF_SIZE(JSObject)
|
||||
DEF_OFFSET(WasmContext)
|
||||
DEF_OFFSET(CompiledModule)
|
||||
DEF_OFFSET(ExportsObject)
|
||||
DEF_OFFSET(MemoryObject)
|
||||
@ -211,7 +215,6 @@ class WasmInstanceObject : public JSObject {
|
||||
DEF_OFFSET(JsImportsTable)
|
||||
|
||||
WasmModuleObject* module_object();
|
||||
WasmContext* wasm_context();
|
||||
V8_EXPORT_PRIVATE wasm::WasmModule* module();
|
||||
|
||||
// Get the debug info associated with the given wasm object.
|
||||
@ -321,10 +324,7 @@ class WasmSharedModuleData : public FixedArray {
|
||||
// used as memory of a particular WebAssembly.Instance object. This
|
||||
// information are then used at runtime to access memory / verify bounds
|
||||
// check limits.
|
||||
// - bounds check limits, computed at compile time, relative to the
|
||||
// size of the memory.
|
||||
// - the objects representing the function tables and signature tables
|
||||
// - raw pointer to the globals buffer.
|
||||
//
|
||||
// Even without instantiating, we need values for all of these parameters.
|
||||
// We need to track these values to be able to create new instances and
|
||||
@ -332,11 +332,6 @@ class WasmSharedModuleData : public FixedArray {
|
||||
// The design decisions for how we track these values is not too immediate,
|
||||
// and it deserves a summary. The "tricky" ones are: memory, globals, and
|
||||
// the tables (signature and functions).
|
||||
// The first 2 (memory & globals) are embedded as raw pointers to native
|
||||
// buffers. All we need to track them is the start addresses and, in the
|
||||
// case of memory, the size. We model all of them as HeapNumbers, because
|
||||
// we need to store size_t values (for addresses), and potentially full
|
||||
// 32 bit unsigned values for the size. Smis are 31 bits.
|
||||
// For tables, we need to hold a reference to the JS Heap object, because
|
||||
// we embed them as objects, and they may move.
|
||||
class WasmCompiledModule : public FixedArray {
|
||||
@ -386,14 +381,6 @@ class WasmCompiledModule : public FixedArray {
|
||||
public: \
|
||||
inline Handle<TYPE> NAME() const;
|
||||
|
||||
#define WCM_LARGE_NUMBER(TYPE, NAME) \
|
||||
public: \
|
||||
inline TYPE NAME() const; \
|
||||
inline void set_##NAME(TYPE value); \
|
||||
inline static void recreate_##NAME(Handle<WasmCompiledModule> obj, \
|
||||
Factory* factory, TYPE init_val); \
|
||||
inline bool has_##NAME() const;
|
||||
|
||||
// Add values here if they are required for creating new instances or
|
||||
// for deserialization, and if they are serializable.
|
||||
// By default, instance values go to WasmInstanceObject, however, if
|
||||
@ -409,7 +396,6 @@ class WasmCompiledModule : public FixedArray {
|
||||
MACRO(OBJECT, FixedArray, signature_tables) \
|
||||
MACRO(CONST_OBJECT, FixedArray, empty_function_tables) \
|
||||
MACRO(CONST_OBJECT, FixedArray, empty_signature_tables) \
|
||||
MACRO(LARGE_NUMBER, size_t, globals_start) \
|
||||
MACRO(SMALL_CONST_NUMBER, uint32_t, initial_pages) \
|
||||
MACRO(WEAK_LINK, WasmCompiledModule, next_instance) \
|
||||
MACRO(WEAK_LINK, WasmCompiledModule, prev_instance) \
|
||||
@ -447,14 +433,8 @@ class WasmCompiledModule : public FixedArray {
|
||||
Handle<WasmCompiledModule> module);
|
||||
static void Reset(Isolate* isolate, WasmCompiledModule* module);
|
||||
|
||||
inline Address GetGlobalsStartOrNull() const;
|
||||
|
||||
uint32_t default_mem_size() const;
|
||||
|
||||
static void SetGlobalsStartAddressFrom(
|
||||
Factory* factory, Handle<WasmCompiledModule> compiled_module,
|
||||
Handle<JSArrayBuffer> buffer);
|
||||
|
||||
#define DECLARATION(KIND, TYPE, NAME) WCM_##KIND(TYPE, NAME)
|
||||
WCM_PROPERTY_TABLE(DECLARATION)
|
||||
#undef DECLARATION
|
||||
|
@ -48,7 +48,7 @@ static void RunLoadStoreRelocation(MachineType rep) {
|
||||
CType new_buffer[kNumElems];
|
||||
byte* raw = reinterpret_cast<byte*>(buffer);
|
||||
byte* new_raw = reinterpret_cast<byte*>(new_buffer);
|
||||
WasmContext wasm_context = {raw, sizeof(buffer)};
|
||||
WasmContext wasm_context = {raw, sizeof(buffer), nullptr};
|
||||
for (size_t i = 0; i < sizeof(buffer); i++) {
|
||||
raw[i] = static_cast<byte>((i + sizeof(CType)) ^ 0xAA);
|
||||
new_raw[i] = static_cast<byte>((i + sizeof(CType)) ^ 0xAA);
|
||||
@ -99,7 +99,7 @@ static void RunLoadStoreRelocationOffset(MachineType rep) {
|
||||
int32_t y = kNumElems - x - 1;
|
||||
// initialize the buffer with raw data.
|
||||
byte* raw = reinterpret_cast<byte*>(buffer);
|
||||
wasm_context = {raw, sizeof(buffer)};
|
||||
wasm_context = {raw, sizeof(buffer), nullptr};
|
||||
for (size_t i = 0; i < sizeof(buffer); i++) {
|
||||
raw[i] = static_cast<byte>((i + sizeof(buffer)) ^ 0xAA);
|
||||
}
|
||||
@ -152,7 +152,7 @@ TEST(RunLoadStoreRelocationOffset) {
|
||||
TEST(Uint32LessThanMemoryRelocation) {
|
||||
RawMachineAssemblerTester<uint32_t> m;
|
||||
RawMachineLabel within_bounds, out_of_bounds;
|
||||
WasmContext wasm_context = {reinterpret_cast<Address>(1234), 0x200};
|
||||
WasmContext wasm_context = {reinterpret_cast<Address>(1234), 0x200, nullptr};
|
||||
Node* index = m.Int32Constant(0x200);
|
||||
Node* wasm_context_node =
|
||||
m.RelocatableIntPtrConstant(reinterpret_cast<uintptr_t>(&wasm_context),
|
||||
|
@ -17,54 +17,46 @@ namespace internal {
|
||||
namespace wasm {
|
||||
namespace test_run_wasm_relocation {
|
||||
|
||||
#define FOREACH_TYPE(TEST_BODY) \
|
||||
TEST_BODY(int32_t, WASM_I32_ADD) \
|
||||
TEST_BODY(int64_t, WASM_I64_ADD) \
|
||||
TEST_BODY(float, WASM_F32_ADD) \
|
||||
TEST_BODY(double, WASM_F64_ADD)
|
||||
TEST(RunPatchWasmContext) {
|
||||
WasmRunner<uint32_t, uint32_t> r(kExecuteCompiled);
|
||||
Isolate* isolate = CcTest::i_isolate();
|
||||
|
||||
#define LOAD_SET_GLOBAL_TEST_BODY(C_TYPE, ADD) \
|
||||
WASM_EXEC_TEST(WasmRelocateGlobal_##C_TYPE) { \
|
||||
WasmRunner<C_TYPE, C_TYPE> r(execution_mode); \
|
||||
Isolate* isolate = CcTest::i_isolate(); \
|
||||
\
|
||||
r.builder().AddGlobal<C_TYPE>(); \
|
||||
r.builder().AddGlobal<C_TYPE>(); \
|
||||
\
|
||||
/* global = global + p0 */ \
|
||||
BUILD(r, WASM_SET_GLOBAL(1, ADD(WASM_GET_GLOBAL(0), WASM_GET_LOCAL(0))), \
|
||||
WASM_GET_GLOBAL(0)); \
|
||||
CHECK_EQ(1, r.builder().CodeTableLength()); \
|
||||
\
|
||||
int filter = 1 << RelocInfo::WASM_GLOBAL_REFERENCE; \
|
||||
\
|
||||
Handle<Code> code = r.builder().GetFunctionCode(0); \
|
||||
\
|
||||
Address old_start = r.builder().globals_start(); \
|
||||
Address new_start = old_start + 1; \
|
||||
\
|
||||
Address old_addresses[4]; \
|
||||
uint32_t address_index = 0U; \
|
||||
for (RelocIterator it(*code, filter); !it.done(); it.next()) { \
|
||||
old_addresses[address_index] = it.rinfo()->wasm_global_reference(); \
|
||||
it.rinfo()->update_wasm_global_reference(isolate, old_start, new_start); \
|
||||
++address_index; \
|
||||
} \
|
||||
CHECK_LE(address_index, 4U); \
|
||||
\
|
||||
address_index = 0U; \
|
||||
for (RelocIterator it(*code, filter); !it.done(); it.next()) { \
|
||||
CHECK_EQ(old_addresses[address_index] + 1, \
|
||||
it.rinfo()->wasm_global_reference()); \
|
||||
++address_index; \
|
||||
} \
|
||||
CHECK_LE(address_index, 4U); \
|
||||
r.builder().AddGlobal<uint32_t>();
|
||||
r.builder().AddGlobal<uint32_t>();
|
||||
|
||||
BUILD(r, WASM_SET_GLOBAL(0, WASM_GET_LOCAL(0)), WASM_GET_GLOBAL(0));
|
||||
CHECK_EQ(1, r.builder().CodeTableLength());
|
||||
|
||||
// Run with the old global data.
|
||||
CHECK_EQ(113, r.Call(113));
|
||||
|
||||
WasmContext* old_wasm_context =
|
||||
r.builder().instance_object()->wasm_context()->get();
|
||||
Address old_wasm_context_address =
|
||||
reinterpret_cast<Address>(old_wasm_context);
|
||||
|
||||
uint32_t new_global_data[3] = {0, 0, 0};
|
||||
WasmContext new_wasm_context = {0, 0,
|
||||
reinterpret_cast<byte*>(new_global_data)};
|
||||
|
||||
// Patch in a new WasmContext that points to the new global data.
|
||||
int filter = 1 << RelocInfo::WASM_CONTEXT_REFERENCE;
|
||||
bool patched = false;
|
||||
Handle<Code> code = r.GetWrapperCode();
|
||||
for (RelocIterator it(*code, filter); !it.done(); it.next()) {
|
||||
CHECK_EQ(old_wasm_context_address, it.rinfo()->wasm_context_reference());
|
||||
it.rinfo()->set_wasm_context_reference(
|
||||
isolate, reinterpret_cast<Address>(&new_wasm_context));
|
||||
patched = true;
|
||||
}
|
||||
CHECK(patched);
|
||||
Assembler::FlushICache(isolate, code->instruction_start(),
|
||||
code->instruction_size());
|
||||
|
||||
FOREACH_TYPE(LOAD_SET_GLOBAL_TEST_BODY)
|
||||
|
||||
#undef FOREACH_TYPE
|
||||
#undef LOAD_SET_GLOBAL_TEST_BODY
|
||||
// Run with the new global data.
|
||||
CHECK_EQ(115, r.Call(115));
|
||||
CHECK_EQ(115, new_global_data[0]);
|
||||
}
|
||||
|
||||
} // namespace test_run_wasm_relocation
|
||||
} // namespace wasm
|
||||
|
@ -2187,22 +2187,27 @@ const T& GetScalar(T* v, int lane) {
|
||||
|
||||
WASM_SIMD_TEST(SimdI32x4GetGlobal) {
|
||||
WasmRunner<int32_t, int32_t> r(execution_mode);
|
||||
// Pad the globals with a few unused slots to get a non-zero offset.
|
||||
r.builder().AddGlobal<int32_t>(kWasmI32); // purposefully unused
|
||||
r.builder().AddGlobal<int32_t>(kWasmI32); // purposefully unused
|
||||
r.builder().AddGlobal<int32_t>(kWasmI32); // purposefully unused
|
||||
r.builder().AddGlobal<int32_t>(kWasmI32); // purposefully unused
|
||||
int32_t* global = r.builder().AddGlobal<int32_t>(kWasmS128);
|
||||
SetVectorByLanes(global, {{0, 1, 2, 3}});
|
||||
r.AllocateLocal(kWasmI32);
|
||||
BUILD(
|
||||
r, WASM_SET_LOCAL(1, WASM_I32V(1)),
|
||||
WASM_IF(WASM_I32_NE(WASM_I32V(0),
|
||||
WASM_SIMD_I32x4_EXTRACT_LANE(0, WASM_GET_GLOBAL(0))),
|
||||
WASM_SIMD_I32x4_EXTRACT_LANE(0, WASM_GET_GLOBAL(4))),
|
||||
WASM_SET_LOCAL(1, WASM_I32V(0))),
|
||||
WASM_IF(WASM_I32_NE(WASM_I32V(1),
|
||||
WASM_SIMD_I32x4_EXTRACT_LANE(1, WASM_GET_GLOBAL(0))),
|
||||
WASM_SIMD_I32x4_EXTRACT_LANE(1, WASM_GET_GLOBAL(4))),
|
||||
WASM_SET_LOCAL(1, WASM_I32V(0))),
|
||||
WASM_IF(WASM_I32_NE(WASM_I32V(2),
|
||||
WASM_SIMD_I32x4_EXTRACT_LANE(2, WASM_GET_GLOBAL(0))),
|
||||
WASM_SIMD_I32x4_EXTRACT_LANE(2, WASM_GET_GLOBAL(4))),
|
||||
WASM_SET_LOCAL(1, WASM_I32V(0))),
|
||||
WASM_IF(WASM_I32_NE(WASM_I32V(3),
|
||||
WASM_SIMD_I32x4_EXTRACT_LANE(3, WASM_GET_GLOBAL(0))),
|
||||
WASM_SIMD_I32x4_EXTRACT_LANE(3, WASM_GET_GLOBAL(4))),
|
||||
WASM_SET_LOCAL(1, WASM_I32V(0))),
|
||||
WASM_GET_LOCAL(1));
|
||||
CHECK_EQ(1, r.Call(0));
|
||||
@ -2210,13 +2215,18 @@ WASM_SIMD_TEST(SimdI32x4GetGlobal) {
|
||||
|
||||
WASM_SIMD_TEST(SimdI32x4SetGlobal) {
|
||||
WasmRunner<int32_t, int32_t> r(execution_mode);
|
||||
// Pad the globals with a few unused slots to get a non-zero offset.
|
||||
r.builder().AddGlobal<int32_t>(kWasmI32); // purposefully unused
|
||||
r.builder().AddGlobal<int32_t>(kWasmI32); // purposefully unused
|
||||
r.builder().AddGlobal<int32_t>(kWasmI32); // purposefully unused
|
||||
r.builder().AddGlobal<int32_t>(kWasmI32); // purposefully unused
|
||||
int32_t* global = r.builder().AddGlobal<int32_t>(kWasmS128);
|
||||
BUILD(r, WASM_SET_GLOBAL(0, WASM_SIMD_I32x4_SPLAT(WASM_I32V(23))),
|
||||
WASM_SET_GLOBAL(0, WASM_SIMD_I32x4_REPLACE_LANE(1, WASM_GET_GLOBAL(0),
|
||||
BUILD(r, WASM_SET_GLOBAL(4, WASM_SIMD_I32x4_SPLAT(WASM_I32V(23))),
|
||||
WASM_SET_GLOBAL(4, WASM_SIMD_I32x4_REPLACE_LANE(1, WASM_GET_GLOBAL(4),
|
||||
WASM_I32V(34))),
|
||||
WASM_SET_GLOBAL(0, WASM_SIMD_I32x4_REPLACE_LANE(2, WASM_GET_GLOBAL(0),
|
||||
WASM_SET_GLOBAL(4, WASM_SIMD_I32x4_REPLACE_LANE(2, WASM_GET_GLOBAL(4),
|
||||
WASM_I32V(45))),
|
||||
WASM_SET_GLOBAL(0, WASM_SIMD_I32x4_REPLACE_LANE(3, WASM_GET_GLOBAL(0),
|
||||
WASM_SET_GLOBAL(4, WASM_SIMD_I32x4_REPLACE_LANE(3, WASM_GET_GLOBAL(4),
|
||||
WASM_I32V(56))),
|
||||
WASM_I32V(1));
|
||||
CHECK_EQ(1, r.Call(0));
|
||||
|
@ -65,8 +65,8 @@ byte* TestingModuleBuilder::AddMemory(uint32_t size) {
|
||||
// TODO(wasm): Delete the following two lines when test-run-wasm will use a
|
||||
// multiple of kPageSize as memory size. At the moment, the effect of these
|
||||
// two lines is used to shrink the memory for testing purposes.
|
||||
instance_object_->wasm_context()->mem_start = mem_start_;
|
||||
instance_object_->wasm_context()->mem_size = mem_size_;
|
||||
instance_object_->wasm_context()->get()->mem_start = mem_start_;
|
||||
instance_object_->wasm_context()->get()->mem_size = mem_size_;
|
||||
return mem_start_;
|
||||
}
|
||||
|
||||
@ -197,15 +197,8 @@ compiler::ModuleEnv TestingModuleBuilder::CreateModuleEnv() {
|
||||
auto& function_table = test_module_.function_tables[i];
|
||||
signature_maps.push_back(&function_table.map);
|
||||
}
|
||||
return {
|
||||
&test_module_,
|
||||
function_tables_,
|
||||
signature_tables_,
|
||||
signature_maps,
|
||||
function_code_,
|
||||
Handle<Code>::null(),
|
||||
reinterpret_cast<uintptr_t>(globals_data_),
|
||||
};
|
||||
return {&test_module_, function_tables_, signature_tables_,
|
||||
signature_maps, function_code_, Handle<Code>::null()};
|
||||
}
|
||||
|
||||
const WasmGlobal* TestingModuleBuilder::AddGlobal(ValueType type) {
|
||||
@ -237,17 +230,13 @@ Handle<WasmInstanceObject> TestingModuleBuilder::InitInstanceObject() {
|
||||
Handle<WasmCompiledModule> compiled_module = WasmCompiledModule::New(
|
||||
isolate_, shared_module_data, code_table, export_wrappers,
|
||||
function_tables_, signature_tables_);
|
||||
// This method is called when we initialize TestEnvironment. We don't
|
||||
// have a memory yet, so we won't create it here. We'll update the
|
||||
// interpreter when we get a memory. We do have globals, though.
|
||||
WasmCompiledModule::recreate_globals_start(
|
||||
compiled_module, isolate_->factory(),
|
||||
reinterpret_cast<size_t>(globals_data_));
|
||||
Handle<FixedArray> weak_exported = isolate_->factory()->NewFixedArray(0);
|
||||
compiled_module->set_weak_exported_functions(weak_exported);
|
||||
DCHECK(WasmCompiledModule::IsWasmCompiledModule(*compiled_module));
|
||||
script->set_wasm_compiled_module(*compiled_module);
|
||||
return WasmInstanceObject::New(isolate_, compiled_module);
|
||||
auto instance = WasmInstanceObject::New(isolate_, compiled_module);
|
||||
instance->wasm_context()->get()->globals_start = globals_data_;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void TestBuildingGraph(
|
||||
|
@ -216,7 +216,7 @@ class TestingModuleBuilder {
|
||||
std::vector<Handle<Code>> function_code_;
|
||||
std::vector<GlobalHandleAddress> function_tables_;
|
||||
std::vector<GlobalHandleAddress> signature_tables_;
|
||||
V8_ALIGNED(8) byte globals_data_[kMaxGlobalsSize];
|
||||
V8_ALIGNED(16) byte globals_data_[kMaxGlobalsSize];
|
||||
WasmInterpreter* interpreter_;
|
||||
Handle<WasmInstanceObject> instance_object_;
|
||||
compiler::RuntimeExceptionSupport runtime_exception_support_;
|
||||
@ -260,9 +260,13 @@ class WasmFunctionWrapper : private compiler::GraphAndBuilders {
|
||||
: common()->Int64Constant(static_cast<int64_t>(value));
|
||||
}
|
||||
|
||||
void SetContextAddress(Address value) {
|
||||
compiler::NodeProperties::ChangeOp(
|
||||
context_address_, IntPtrConstant(reinterpret_cast<uintptr_t>(value)));
|
||||
void SetContextAddress(uintptr_t value) {
|
||||
auto rmode = RelocInfo::WASM_CONTEXT_REFERENCE;
|
||||
auto op = kPointerSize == 8 ? common()->RelocatableInt64Constant(
|
||||
static_cast<int64_t>(value), rmode)
|
||||
: common()->RelocatableInt32Constant(
|
||||
static_cast<int32_t>(value), rmode);
|
||||
compiler::NodeProperties::ChangeOp(context_address_, op);
|
||||
}
|
||||
|
||||
Handle<Code> GetWrapperCode();
|
||||
@ -428,13 +432,12 @@ class WasmRunner : public WasmRunnerBase {
|
||||
set_trap_callback_for_testing(trap_callback);
|
||||
|
||||
wrapper_.SetInnerCode(builder_.GetFunctionCode(0));
|
||||
if (builder().instance_object()->has_memory_object()) {
|
||||
wrapper_.SetContextAddress(reinterpret_cast<Address>(
|
||||
builder().instance_object()->wasm_context()));
|
||||
}
|
||||
WasmContext* wasm_context =
|
||||
builder().instance_object()->wasm_context()->get();
|
||||
wrapper_.SetContextAddress(reinterpret_cast<uintptr_t>(wasm_context));
|
||||
Handle<Code> wrapper_code = wrapper_.GetWrapperCode();
|
||||
compiler::CodeRunner<int32_t> runner(CcTest::InitIsolateOnce(),
|
||||
wrapper_.GetWrapperCode(),
|
||||
wrapper_.signature());
|
||||
wrapper_code, wrapper_.signature());
|
||||
int32_t result = runner.Call(static_cast<void*>(&p)...,
|
||||
static_cast<void*>(&return_value));
|
||||
CHECK_EQ(WASM_WRAPPER_RETURN_VALUE, result);
|
||||
@ -463,6 +466,8 @@ class WasmRunner : public WasmRunnerBase {
|
||||
return ReturnType{0};
|
||||
}
|
||||
}
|
||||
|
||||
Handle<Code> GetWrapperCode() { return wrapper_.GetWrapperCode(); }
|
||||
};
|
||||
|
||||
// A macro to define tests that run in different engine configurations.
|
||||
|
@ -7,11 +7,11 @@
|
||||
load("test/mjsunit/wasm/wasm-constants.js");
|
||||
load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
|
||||
function testCallFFI(ffi) {
|
||||
function instantiateWithFFI(ffi) {
|
||||
var builder = new WasmModuleBuilder();
|
||||
|
||||
var sig_index = kSig_i_dd;
|
||||
builder.addImport("", "fun", sig_index);
|
||||
builder.addImport("mod", "fun", sig_index);
|
||||
builder.addFunction("main", sig_index)
|
||||
.addBody([
|
||||
kExprGetLocal, 0, // --
|
||||
@ -20,45 +20,75 @@ function testCallFFI(ffi) {
|
||||
]) // --
|
||||
.exportFunc();
|
||||
|
||||
var module = builder.instantiate(ffi);
|
||||
return builder.instantiate(ffi);
|
||||
}
|
||||
|
||||
// everything is good.
|
||||
(function() {
|
||||
var ffi = {"": {fun: function(a, b) { print(a, b); }}}
|
||||
testCallFFI(ffi);
|
||||
var ffi = {"mod": {fun: function(a, b) { print(a, b); }}}
|
||||
instantiateWithFFI(ffi);
|
||||
})();
|
||||
|
||||
|
||||
// FFI object should be an object.
|
||||
assertThrows(function() {
|
||||
var ffi = 0;
|
||||
testCallFFI(ffi);
|
||||
instantiateWithFFI(ffi);
|
||||
});
|
||||
|
||||
|
||||
// FFI object should have a "mod" property.
|
||||
assertThrows(function() {
|
||||
instantiateWithFFI({});
|
||||
});
|
||||
|
||||
|
||||
// FFI object should have a "fun" property.
|
||||
assertThrows(function() {
|
||||
var ffi = new Object();
|
||||
testCallFFI(ffi);
|
||||
instantiateWithFFI({mod: {}});
|
||||
});
|
||||
|
||||
|
||||
// "fun" should be a JS function.
|
||||
assertThrows(function() {
|
||||
var ffi = new Object();
|
||||
ffi.fun = new Object();
|
||||
testCallFFI(ffi);
|
||||
instantiateWithFFI({mod: {fun: new Object()}});
|
||||
});
|
||||
|
||||
|
||||
// "fun" should be a JS function.
|
||||
assertThrows(function() {
|
||||
var ffi = new Object();
|
||||
ffi.fun = 0;
|
||||
testCallFFI(ffi);
|
||||
instantiateWithFFI({mod: {fun: 0}});
|
||||
});
|
||||
|
||||
// "fun" should have signature "i_dd"
|
||||
assertThrows(function () {
|
||||
var builder = new WasmModuleBuilder();
|
||||
|
||||
var sig_index = kSig_i_dd;
|
||||
builder.addFunction("exp", kSig_i_i)
|
||||
.addBody([
|
||||
kExprGetLocal, 0,
|
||||
]) // --
|
||||
.exportFunc();
|
||||
|
||||
var exported = builder.instantiate().exports.exp;
|
||||
instantiateWithFFI({mod: {fun: exported}});
|
||||
});
|
||||
|
||||
// "fun" matches signature "i_dd"
|
||||
(function () {
|
||||
var builder = new WasmModuleBuilder();
|
||||
|
||||
builder.addFunction("exp", kSig_i_dd)
|
||||
.addBody([
|
||||
kExprI32Const, 33,
|
||||
]) // --
|
||||
.exportFunc();
|
||||
|
||||
var exported = builder.instantiate().exports.exp;
|
||||
var instance = instantiateWithFFI({mod: {fun: exported}});
|
||||
assertEquals(33, instance.exports.main());
|
||||
})();
|
||||
|
||||
(function I64InSignatureThrows() {
|
||||
var builder = new WasmModuleBuilder();
|
||||
|
@ -7,6 +7,48 @@
|
||||
load("test/mjsunit/wasm/wasm-constants.js");
|
||||
load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
|
||||
(function TestMultipleInstances() {
|
||||
print("TestMultipleInstances");
|
||||
|
||||
var builder = new WasmModuleBuilder();
|
||||
|
||||
let g = builder.addGlobal(kWasmI32, true);
|
||||
let sig_index = builder.addType(kSig_i_v);
|
||||
builder.addFunction("get", sig_index)
|
||||
.addBody([
|
||||
kExprGetGlobal, g.index])
|
||||
.exportAs("get");
|
||||
builder.addFunction("set", kSig_v_i)
|
||||
.addBody([
|
||||
kExprGetLocal, 0,
|
||||
kExprSetGlobal, g.index])
|
||||
.exportAs("set");
|
||||
|
||||
let module = new WebAssembly.Module(builder.toBuffer());
|
||||
|
||||
let a = new WebAssembly.Instance(module);
|
||||
let b = new WebAssembly.Instance(module);
|
||||
|
||||
assertEquals(0, a.exports.get());
|
||||
assertEquals(0, b.exports.get());
|
||||
|
||||
a.exports.set(1);
|
||||
|
||||
assertEquals(1, a.exports.get());
|
||||
assertEquals(0, b.exports.get());
|
||||
|
||||
b.exports.set(6);
|
||||
|
||||
assertEquals(1, a.exports.get());
|
||||
assertEquals(6, b.exports.get());
|
||||
|
||||
a.exports.set(7);
|
||||
|
||||
assertEquals(7, a.exports.get());
|
||||
assertEquals(6, b.exports.get());
|
||||
|
||||
})();
|
||||
|
||||
function TestImported(type, val, expected) {
|
||||
print("TestImported " + type + "(" + val +")" + " = " + expected);
|
||||
var builder = new WasmModuleBuilder();
|
||||
@ -26,6 +68,29 @@ TestImported(kWasmF32, 87234.87238, Math.fround(87234.87238));
|
||||
TestImported(kWasmF64, 77777.88888, 77777.88888);
|
||||
|
||||
|
||||
(function TestImportedMultipleInstances() {
|
||||
print("TestImportedMultipleInstances");
|
||||
|
||||
var builder = new WasmModuleBuilder();
|
||||
|
||||
let g = builder.addImportedGlobal("mod", "g", kWasmI32);
|
||||
let sig_index = builder.addType(kSig_i_v);
|
||||
builder.addFunction("main", sig_index)
|
||||
.addBody([
|
||||
kExprGetGlobal, g])
|
||||
.exportAs("main");
|
||||
|
||||
let module = new WebAssembly.Module(builder.toBuffer());
|
||||
|
||||
print(" i 100...");
|
||||
let i100 = new WebAssembly.Instance(module, {mod: {g: 100}});
|
||||
assertEquals(100, i100.exports.main());
|
||||
|
||||
print(" i 300...");
|
||||
let i300 = new WebAssembly.Instance(module, {mod: {g: 300}});
|
||||
assertEquals(300, i300.exports.main());
|
||||
})();
|
||||
|
||||
function TestExported(type, val, expected) {
|
||||
print("TestExported " + type + "(" + val +")" + " = " + expected);
|
||||
var builder = new WasmModuleBuilder();
|
||||
@ -96,3 +161,54 @@ function TestGlobalIndexSpace(type, val) {
|
||||
TestGlobalIndexSpace(kWasmI32, 123);
|
||||
TestGlobalIndexSpace(kWasmF32, 54321.125);
|
||||
TestGlobalIndexSpace(kWasmF64, 12345.678);
|
||||
|
||||
(function TestAccessesInBranch() {
|
||||
print("TestAccessesInBranches");
|
||||
|
||||
var builder = new WasmModuleBuilder();
|
||||
|
||||
let g = builder.addGlobal(kWasmI32, true);
|
||||
let h = builder.addGlobal(kWasmI32, true);
|
||||
let sig_index = builder.addType(kSig_i_i);
|
||||
builder.addFunction("get", sig_index)
|
||||
.addBody([
|
||||
kExprGetLocal, 0,
|
||||
kExprIf, kWasmI32,
|
||||
kExprGetGlobal, g.index,
|
||||
kExprElse,
|
||||
kExprGetGlobal, h.index,
|
||||
kExprEnd])
|
||||
.exportAs("get");
|
||||
builder.addFunction("set", kSig_v_ii)
|
||||
.addBody([
|
||||
kExprGetLocal, 0,
|
||||
kExprIf, kWasmStmt,
|
||||
kExprGetLocal, 1,
|
||||
kExprSetGlobal, g.index,
|
||||
kExprElse,
|
||||
kExprGetLocal, 1,
|
||||
kExprSetGlobal, h.index,
|
||||
kExprEnd])
|
||||
.exportAs("set");
|
||||
|
||||
let module = new WebAssembly.Module(builder.toBuffer());
|
||||
|
||||
let a = new WebAssembly.Instance(module);
|
||||
let get = a.exports.get;
|
||||
let set = a.exports.set;
|
||||
|
||||
assertEquals(0, get(0));
|
||||
assertEquals(0, get(1));
|
||||
set(0, 1);
|
||||
assertEquals(1, get(0));
|
||||
assertEquals(0, get(1));
|
||||
|
||||
set(0, 7);
|
||||
assertEquals(7, get(0));
|
||||
assertEquals(0, get(1));
|
||||
|
||||
set(1, 9);
|
||||
assertEquals(7, get(0));
|
||||
assertEquals(9, get(1));
|
||||
|
||||
})();
|
||||
|
@ -38,6 +38,111 @@ function generateBuilder(add_memory, import_sig) {
|
||||
return builder;
|
||||
}
|
||||
|
||||
function assertMemoryIndependence(load_a, store_a, load_b, store_b) {
|
||||
|
||||
assertEquals(0, load_a(0));
|
||||
assertEquals(0, load_b(0));
|
||||
assertEquals(0, load_a(4));
|
||||
assertEquals(0, load_b(4));
|
||||
|
||||
store_a(0, 101);
|
||||
assertEquals(101, load_a(0));
|
||||
assertEquals(0, load_b(0));
|
||||
assertEquals(0, load_a(4));
|
||||
assertEquals(0, load_b(4));
|
||||
|
||||
store_a(4, 102);
|
||||
assertEquals(101, load_a(0));
|
||||
assertEquals(0, load_b(0));
|
||||
assertEquals(102, load_a(4));
|
||||
assertEquals(0, load_b(4));
|
||||
|
||||
store_b(0, 103);
|
||||
assertEquals(101, load_a(0));
|
||||
assertEquals(103, load_b(0));
|
||||
assertEquals(102, load_a(4));
|
||||
assertEquals(0, load_b(4));
|
||||
|
||||
store_b(4, 107);
|
||||
assertEquals(101, load_a(0));
|
||||
assertEquals(103, load_b(0));
|
||||
assertEquals(102, load_a(4));
|
||||
assertEquals(107, load_b(4));
|
||||
|
||||
store_a(0, 0);
|
||||
store_a(4, 0);
|
||||
store_b(0, 0);
|
||||
store_b(4, 0);
|
||||
}
|
||||
|
||||
// A simple test for memory-independence between modules.
|
||||
(function SimpleMemoryIndependenceTest() {
|
||||
print("SimpleMemoryIndependenceTest");
|
||||
let kPages = 1;
|
||||
let builder = new WasmModuleBuilder();
|
||||
|
||||
builder.addMemory(kPages, kPages, true);
|
||||
builder.addFunction("store", kSig_v_ii)
|
||||
.addBody([
|
||||
kExprGetLocal, 0, // --
|
||||
kExprGetLocal, 1, // --
|
||||
kExprI32StoreMem, 0, 0, // --
|
||||
]) // --
|
||||
.exportFunc();
|
||||
builder.addFunction("load", kSig_i_i)
|
||||
.addBody([
|
||||
kExprGetLocal, 0, // --
|
||||
kExprI32LoadMem, 0, 0, // --
|
||||
]) // --
|
||||
.exportFunc();
|
||||
|
||||
var a = builder.instantiate();
|
||||
|
||||
// The {b} instance forwards all {store} calls to the imported function.
|
||||
builder = new WasmModuleBuilder();
|
||||
builder.addImport("mod", "store", kSig_v_ii);
|
||||
builder.addMemory(kPages, kPages, true);
|
||||
builder.addFunction("store", kSig_v_ii)
|
||||
.addBody([
|
||||
kExprGetLocal, 0, // --
|
||||
kExprGetLocal, 1, // --
|
||||
kExprCallFunction, 0, // --
|
||||
]) // --
|
||||
.exportFunc();
|
||||
builder.addFunction("load", kSig_i_i)
|
||||
.addBody([
|
||||
kExprGetLocal, 0, // --
|
||||
kExprI32LoadMem, 0, 0, // --
|
||||
]) // --
|
||||
.exportFunc();
|
||||
|
||||
var b = builder.instantiate({mod: {store: a.exports.store}});
|
||||
|
||||
assertEquals(0, a.exports.load(0));
|
||||
assertEquals(0, b.exports.load(0));
|
||||
assertEquals(0, a.exports.load(4));
|
||||
assertEquals(0, b.exports.load(4));
|
||||
|
||||
a.exports.store(0, 101);
|
||||
assertEquals(101, a.exports.load(0));
|
||||
assertEquals(0, b.exports.load(0));
|
||||
assertEquals(0, a.exports.load(4));
|
||||
assertEquals(0, b.exports.load(4));
|
||||
|
||||
a.exports.store(4, 102);
|
||||
assertEquals(101, a.exports.load(0));
|
||||
assertEquals(0, b.exports.load(0));
|
||||
assertEquals(102, a.exports.load(4));
|
||||
assertEquals(0, b.exports.load(4));
|
||||
|
||||
b.exports.store(4, 107); // should forward to {a}.
|
||||
assertEquals(101, a.exports.load(0));
|
||||
assertEquals(0, b.exports.load(0));
|
||||
assertEquals(107, a.exports.load(4));
|
||||
assertEquals(0, b.exports.load(4));
|
||||
|
||||
})();
|
||||
|
||||
// This test verifies that when a Wasm module without memory invokes a function
|
||||
// imported from another module that has memory, the second module reads its own
|
||||
// memory and returns the expected value.
|
||||
@ -147,3 +252,87 @@ function generateBuilder(add_memory, import_sig) {
|
||||
assertEquals(first_value, first_instance.exports.load(index));
|
||||
assertEquals(second_value, second_instance.exports.load(index));
|
||||
})();
|
||||
|
||||
// A test for memory-independence between modules when calling through
|
||||
// imported tables.
|
||||
(function CallThroughTableMemoryIndependenceTest() {
|
||||
print("CallThroughTableIndependenceTest");
|
||||
let kTableSize = 2;
|
||||
let kPages = 1;
|
||||
let builder = new WasmModuleBuilder();
|
||||
|
||||
builder.addMemory(kPages, kPages, true);
|
||||
builder.addFunction("store", kSig_v_ii)
|
||||
.addBody([
|
||||
kExprGetLocal, 0, // --
|
||||
kExprGetLocal, 1, // --
|
||||
kExprI32StoreMem, 0, 0, // --
|
||||
]) // --
|
||||
.exportFunc();
|
||||
builder.addFunction("load", kSig_i_i)
|
||||
.addBody([
|
||||
kExprGetLocal, 0, // --
|
||||
kExprI32LoadMem, 0, 0, // --
|
||||
]) // --
|
||||
.exportFunc();
|
||||
|
||||
{
|
||||
// Create two instances.
|
||||
let module = builder.toModule();
|
||||
var a = new WebAssembly.Instance(module);
|
||||
var b = new WebAssembly.Instance(module);
|
||||
// Check that the memories are initially independent.
|
||||
assertMemoryIndependence(a.exports.load, a.exports.store,
|
||||
b.exports.load, b.exports.store);
|
||||
}
|
||||
|
||||
let table = new WebAssembly.Table({element: "anyfunc",
|
||||
initial: kTableSize,
|
||||
maximum: kTableSize});
|
||||
|
||||
table.set(0, a.exports.store);
|
||||
table.set(1, b.exports.store);
|
||||
// Check that calling (from JS) through the table maintains independence.
|
||||
assertMemoryIndependence(a.exports.load, table.get(0),
|
||||
b.exports.load, table.get(1));
|
||||
|
||||
table.set(1, a.exports.store);
|
||||
table.set(0, b.exports.store);
|
||||
// Check that calling (from JS) through the table maintains independence,
|
||||
// even after reorganizing the table.
|
||||
assertMemoryIndependence(a.exports.load, table.get(1),
|
||||
b.exports.load, table.get(0));
|
||||
|
||||
// Check that calling (from WASM) through the table maintains independence.
|
||||
builder = new WasmModuleBuilder();
|
||||
builder.addImportedTable("m", "table", kTableSize, kTableSize);
|
||||
var sig_index = builder.addType(kSig_v_ii);
|
||||
builder.addFunction("store", kSig_v_iii)
|
||||
.addBody([
|
||||
kExprGetLocal, 1,
|
||||
kExprGetLocal, 2,
|
||||
kExprGetLocal, 0,
|
||||
kExprCallIndirect, sig_index, kTableZero,
|
||||
]).exportFunc();
|
||||
|
||||
let c = builder.instantiate({m: {table: table}});
|
||||
|
||||
let a_index = 1;
|
||||
let b_index = 0;
|
||||
let store_a = (index, val) => c.exports.store(a_index, index, val)
|
||||
let store_b = (index, val) => c.exports.store(b_index, index, val);
|
||||
|
||||
assertMemoryIndependence(a.exports.load, store_a,
|
||||
b.exports.load, store_b);
|
||||
|
||||
// Flip the order in the table and do it again.
|
||||
table.set(0, a.exports.store);
|
||||
table.set(1, b.exports.store);
|
||||
|
||||
a_index = 0;
|
||||
b_index = 1;
|
||||
|
||||
assertMemoryIndependence(a.exports.load, store_a,
|
||||
b.exports.load, store_b);
|
||||
|
||||
})();
|
||||
|
@ -299,3 +299,28 @@ assertThrows(function TestWasmWrapperNoElisionTypeMismatch() {
|
||||
assertEquals(the_export(2, -2), 0);
|
||||
assertEquals(%CheckWasmWrapperElision(the_export, expect_no_elison), true);
|
||||
});
|
||||
|
||||
|
||||
(function TestSimpleI64Ret() {
|
||||
var builder = new WasmModuleBuilder();
|
||||
builder.addFunction("exp", kSig_l_v)
|
||||
.addBody([
|
||||
kExprI64Const, 23
|
||||
])
|
||||
.exportFunc();
|
||||
var exported = builder.instantiate().exports.exp;
|
||||
|
||||
var builder = new WasmModuleBuilder();
|
||||
builder.addImport("imp", "func", kSig_l_v);
|
||||
builder.addFunction("main", kSig_i_v)
|
||||
.addBody([
|
||||
kExprCallFunction, 0,
|
||||
kExprI32ConvertI64
|
||||
])
|
||||
.exportFunc();
|
||||
|
||||
var instance = builder.instantiate({imp: {func: exported}});
|
||||
|
||||
assertEquals(23, instance.exports.main());
|
||||
|
||||
})();
|
||||
|
Loading…
Reference in New Issue
Block a user