Commit c02f5e3a authored by Ben L. Titzer's avatar Ben L. Titzer Committed by Commit Bot

[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: 's avatarMircea Trofin <mtrofin@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Reviewed-by: 's avatarBill Budge <bbudge@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48581}
parent cf9d3d52
......@@ -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: ";
......
This diff is collapsed.
......@@ -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);
......
This diff is collapsed.
......@@ -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));
FOREACH_TYPE(LOAD_SET_GLOBAL_TEST_BODY)
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());
#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());
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment