[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:
Ben L. Titzer 2017-10-16 10:49:45 +02:00 committed by Commit Bot
parent cf9d3d52eb
commit c02f5e3ab3
27 changed files with 721 additions and 447 deletions

View File

@ -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:

View File

@ -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);

View File

@ -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()) {

View File

@ -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;

View File

@ -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(

View File

@ -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");
}

View File

@ -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: ";

View File

@ -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++;

View File

@ -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, ...).

View File

@ -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;

View File

@ -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:

View File

@ -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;
}

View File

@ -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() !=

View File

@ -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) {

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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),

View File

@ -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

View File

@ -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));

View File

@ -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(

View File

@ -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.

View File

@ -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();

View File

@ -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));
})();

View File

@ -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);
})();

View File

@ -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());
})();