summaryrefslogtreecommitdiff
path: root/deps/v8/src/compiler/wasm-compiler.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/compiler/wasm-compiler.cc')
-rw-r--r--deps/v8/src/compiler/wasm-compiler.cc982
1 files changed, 686 insertions, 296 deletions
diff --git a/deps/v8/src/compiler/wasm-compiler.cc b/deps/v8/src/compiler/wasm-compiler.cc
index bc731b2bb8..a04c7b3e5d 100644
--- a/deps/v8/src/compiler/wasm-compiler.cc
+++ b/deps/v8/src/compiler/wasm-compiler.cc
@@ -13,6 +13,7 @@
#include "src/builtins/builtins.h"
#include "src/code-factory.h"
#include "src/compiler/access-builder.h"
+#include "src/compiler/code-generator.h"
#include "src/compiler/common-operator.h"
#include "src/compiler/compiler-source-position-table.h"
#include "src/compiler/diamond.h"
@@ -31,6 +32,7 @@
#include "src/factory.h"
#include "src/isolate-inl.h"
#include "src/log-inl.h"
+#include "src/trap-handler/trap-handler.h"
#include "src/wasm/function-body-decoder.h"
#include "src/wasm/wasm-limits.h"
#include "src/wasm/wasm-module.h"
@@ -82,6 +84,7 @@ WasmGraphBuilder::WasmGraphBuilder(
function_table_sizes_(zone),
cur_buffer_(def_buffer_),
cur_bufsize_(kDefaultBufferSize),
+ untrusted_code_mitigations_(FLAG_untrusted_code_mitigations),
runtime_exception_support_(exception_support),
sig_(sig),
source_position_table_(source_position_table) {
@@ -118,10 +121,6 @@ Node* WasmGraphBuilder::Terminate(Node* effect, Node* control) {
return terminate;
}
-unsigned WasmGraphBuilder::InputCount(Node* node) {
- return static_cast<unsigned>(node->InputCount());
-}
-
bool WasmGraphBuilder::IsPhiWithMerge(Node* phi, Node* merge) {
return phi && IrOpcode::IsPhiOpcode(phi->opcode()) &&
NodeProperties::GetControlInput(phi) == merge;
@@ -2363,15 +2362,21 @@ Node* WasmGraphBuilder::BuildWasmCall(wasm::FunctionSig* sig, Node** args,
Node* WasmGraphBuilder::CallDirect(uint32_t index, Node** args, Node*** rets,
wasm::WasmCodePosition position) {
DCHECK_NULL(args[0]);
-
- // Add code object as constant.
- Handle<Code> code = index < env_->function_code.size()
- ? env_->function_code[index]
- : env_->default_function_code;
-
- DCHECK(!code.is_null());
- args[0] = HeapConstant(code);
wasm::FunctionSig* sig = env_->module->functions[index].sig;
+ if (FLAG_wasm_jit_to_native) {
+ // Simply encode the index of the target.
+ Address code = reinterpret_cast<Address>(index);
+ args[0] = jsgraph()->RelocatableIntPtrConstant(
+ reinterpret_cast<intptr_t>(code), RelocInfo::WASM_CALL);
+ } else {
+ // Add code object as constant.
+ Handle<Code> code = index < env_->function_code.size()
+ ? env_->function_code[index]
+ : env_->default_function_code;
+
+ DCHECK(!code.is_null());
+ args[0] = HeapConstant(code);
+ }
return BuildWasmCall(sig, args, rets, position);
}
@@ -2415,23 +2420,31 @@ Node* WasmGraphBuilder::CallIndirect(uint32_t sig_index, Node** args,
Int32Constant(kPointerSizeLog2)),
Int32Constant(fixed_offset)),
*effect_, *control_);
- auto map = env_->signature_maps[table_index];
- Node* sig_match = graph()->NewNode(
- machine->WordEqual(), load_sig,
- jsgraph()->SmiConstant(static_cast<int>(map->FindOrInsert(sig))));
+ int32_t canonical_sig_num = env_->module->signature_ids[sig_index];
+ CHECK_GE(sig_index, 0);
+ Node* sig_match =
+ graph()->NewNode(machine->WordEqual(), load_sig,
+ jsgraph()->SmiConstant(canonical_sig_num));
TrapIfFalse(wasm::kTrapFuncSigMismatch, sig_match, position);
}
- // Load code object from the table.
- Node* load_code = graph()->NewNode(
+ // Load code object from the table. It is held by a Foreign.
+ Node* entry = graph()->NewNode(
machine->Load(MachineType::AnyTagged()), table,
graph()->NewNode(machine->Int32Add(),
graph()->NewNode(machine->Word32Shl(), key,
Int32Constant(kPointerSizeLog2)),
Uint32Constant(fixed_offset)),
*effect_, *control_);
-
- args[0] = load_code;
+ if (FLAG_wasm_jit_to_native) {
+ Node* address = graph()->NewNode(
+ machine->Load(MachineType::Pointer()), entry,
+ Int32Constant(Foreign::kForeignAddressOffset - kHeapObjectTag),
+ *effect_, *control_);
+ args[0] = address;
+ } else {
+ args[0] = entry;
+ }
return BuildWasmCall(sig, args, rets, position);
}
@@ -2765,7 +2778,7 @@ Node* WasmGraphBuilder::BuildHeapNumberValueIndexConstant() {
return jsgraph()->IntPtrConstant(HeapNumber::kValueOffset - kHeapObjectTag);
}
-void WasmGraphBuilder::BuildJSToWasmWrapper(Handle<Code> wasm_code,
+void WasmGraphBuilder::BuildJSToWasmWrapper(WasmCodeWrapper wasm_code,
Address wasm_context_address) {
const int wasm_count = static_cast<int>(sig_->parameter_count());
const int count =
@@ -2791,18 +2804,28 @@ void WasmGraphBuilder::BuildJSToWasmWrapper(Handle<Code> wasm_code,
reinterpret_cast<uintptr_t>(wasm_context_address),
RelocInfo::WASM_CONTEXT_REFERENCE);
+ Node* wasm_code_node = nullptr;
+ if (!wasm_code.IsCodeObject()) {
+ const wasm::WasmCode* code = wasm_code.GetWasmCode();
+ Address instr_start =
+ code == nullptr ? nullptr : code->instructions().start();
+ wasm_code_node = jsgraph()->RelocatableIntPtrConstant(
+ reinterpret_cast<intptr_t>(instr_start), RelocInfo::JS_TO_WASM_CALL);
+ } else {
+ wasm_code_node = HeapConstant(wasm_code.GetCode());
+ }
if (!wasm::IsJSCompatibleSignature(sig_)) {
// Throw a TypeError. Use the js_context of the calling javascript function
// (passed as a parameter), such that the generated code is js_context
// independent.
- BuildCallToRuntimeWithContextFromJS(Runtime::kWasmThrowTypeError,
- js_context, nullptr, 0);
+ BuildCallToRuntimeWithContext(Runtime::kWasmThrowTypeError, js_context,
+ nullptr, 0);
// Add a dummy call to the wasm function so that the generated wrapper
// contains a reference to the wrapped wasm function. Without this reference
// the wasm function could not be re-imported into another wasm module.
int pos = 0;
- args[pos++] = HeapConstant(wasm_code);
+ args[pos++] = wasm_code_node;
args[pos++] = wasm_context_;
args[pos++] = *effect_;
args[pos++] = *control_;
@@ -2817,7 +2840,7 @@ void WasmGraphBuilder::BuildJSToWasmWrapper(Handle<Code> wasm_code,
}
int pos = 0;
- args[pos++] = HeapConstant(wasm_code);
+ args[pos++] = wasm_code_node;
args[pos++] = wasm_context_;
// Convert JS parameters to wasm numbers.
@@ -3016,7 +3039,7 @@ bool HasInt64ParamOrReturn(wasm::FunctionSig* sig) {
}
} // namespace
-void WasmGraphBuilder::BuildWasmToWasmWrapper(Handle<Code> target,
+void WasmGraphBuilder::BuildWasmToWasmWrapper(WasmCodeWrapper wasm_code,
Address new_context_address) {
int wasm_count = static_cast<int>(sig_->parameter_count());
int count = wasm_count + 4; // wasm_code, wasm_context, effect, and control.
@@ -3029,7 +3052,15 @@ void WasmGraphBuilder::BuildWasmToWasmWrapper(Handle<Code> target,
int pos = 0;
// Add the wasm code target.
- args[pos++] = jsgraph()->HeapConstant(target);
+ if (!wasm_code.IsCodeObject()) {
+ const wasm::WasmCode* code = wasm_code.GetWasmCode();
+ Address instr_start =
+ code == nullptr ? nullptr : code->instructions().start();
+ args[pos++] = jsgraph()->RelocatableIntPtrConstant(
+ reinterpret_cast<intptr_t>(instr_start), RelocInfo::JS_TO_WASM_CALL);
+ } else {
+ args[pos++] = jsgraph()->HeapConstant(wasm_code.GetCode());
+ }
// Add the wasm_context of the other instance.
args[pos++] = jsgraph()->IntPtrConstant(
reinterpret_cast<uintptr_t>(new_context_address));
@@ -3041,16 +3072,14 @@ void WasmGraphBuilder::BuildWasmToWasmWrapper(Handle<Code> target,
args[pos++] = *effect_;
args[pos++] = *control_;
- // Call the wasm code.
+ // Tail-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);
+ 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) {
+void WasmGraphBuilder::BuildWasmInterpreterEntry(uint32_t func_index) {
int param_count = static_cast<int>(sig_->parameter_count());
// Build the start and the parameter nodes.
@@ -3095,9 +3124,8 @@ 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()->SmiConstant(func_index), // function index
+ arg_buffer, // argument buffer
};
BuildCallToRuntime(Runtime::kWasmRunInterpreter, parameters,
arraysize(parameters));
@@ -3130,12 +3158,19 @@ void WasmGraphBuilder::BuildCWasmEntry(Address wasm_context_address) {
reinterpret_cast<uintptr_t>(wasm_context_address));
// Create parameter nodes (offset by 1 for the receiver parameter).
- Node* code_obj = Param(CWasmEntryParameters::kCodeObject + 1);
+ Node* code_obj = nullptr;
+ if (FLAG_wasm_jit_to_native) {
+ Node* foreign_code_obj = Param(CWasmEntryParameters::kCodeObject + 1);
+ MachineOperatorBuilder* machine = jsgraph()->machine();
+ code_obj = graph()->NewNode(
+ machine->Load(MachineType::Pointer()), foreign_code_obj,
+ Int32Constant(Foreign::kForeignAddressOffset - kHeapObjectTag),
+ *effect_, *control_);
+ } else {
+ code_obj = Param(CWasmEntryParameters::kCodeObject + 1);
+ }
Node* arg_buffer = Param(CWasmEntryParameters::kArgumentsBuffer + 1);
- // Set the ThreadInWasm flag before we do the actual call.
- BuildModifyThreadInWasmFlag(true);
-
int wasm_arg_count = static_cast<int>(sig_->parameter_count());
int arg_count = wasm_arg_count + 4; // code, wasm_context, control, effect
Node** args = Buffer(arg_count);
@@ -3165,9 +3200,6 @@ void WasmGraphBuilder::BuildCWasmEntry(Address wasm_context_address) {
graph()->NewNode(jsgraph()->common()->Call(desc), arg_count, args);
*effect_ = call;
- // Clear the ThreadInWasmFlag
- BuildModifyThreadInWasmFlag(false);
-
// Store the return value.
DCHECK_GE(1, sig_->return_count());
if (sig_->return_count() == 1) {
@@ -3193,53 +3225,167 @@ 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_,
-// which will always contain the correct node (stored in the SsaEnv).
-Node* WasmGraphBuilder::LoadMemStart() {
+void WasmGraphBuilder::InitContextCache(WasmContextCacheNodes* context_cache) {
DCHECK_NOT_NULL(wasm_context_);
- Node* mem_buffer = graph()->NewNode(
+ DCHECK_NOT_NULL(*control_);
+ DCHECK_NOT_NULL(*effect_);
+
+ // Load the memory start.
+ Node* mem_start = graph()->NewNode(
jsgraph()->machine()->Load(MachineType::UintPtr()), wasm_context_,
jsgraph()->Int32Constant(
static_cast<int32_t>(offsetof(WasmContext, mem_start))),
*effect_, *control_);
- *effect_ = mem_buffer;
- return mem_buffer;
-}
+ *effect_ = mem_start;
+ context_cache->mem_start = mem_start;
-// 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_,
-// which will always contain the correct node (stored in the SsaEnv).
-Node* WasmGraphBuilder::LoadMemSize() {
- // Load mem_size from the memory_context location at runtime.
- DCHECK_NOT_NULL(wasm_context_);
+ // Load the memory size.
Node* mem_size = graph()->NewNode(
jsgraph()->machine()->Load(MachineType::Uint32()), wasm_context_,
jsgraph()->Int32Constant(
static_cast<int32_t>(offsetof(WasmContext, mem_size))),
*effect_, *control_);
*effect_ = mem_size;
- if (jsgraph()->machine()->Is64()) {
- mem_size = graph()->NewNode(jsgraph()->machine()->ChangeUint32ToUint64(),
- mem_size);
+ context_cache->mem_size = mem_size;
+
+ if (untrusted_code_mitigations_) {
+ // Load the memory mask.
+ Node* mem_mask = graph()->NewNode(
+ jsgraph()->machine()->Load(MachineType::Uint32()), wasm_context_,
+ jsgraph()->Int32Constant(
+ static_cast<int32_t>(offsetof(WasmContext, mem_mask))),
+ *effect_, *control_);
+ *effect_ = mem_mask;
+ context_cache->mem_mask = mem_mask;
+ } else {
+ // Explicitly set to nullptr to ensure a SEGV when we try to use it.
+ context_cache->mem_mask = nullptr;
+ }
+}
+
+void WasmGraphBuilder::PrepareContextCacheForLoop(
+ WasmContextCacheNodes* context_cache, Node* control) {
+#define INTRODUCE_PHI(field, rep) \
+ context_cache->field = Phi(rep, 1, &context_cache->field, control);
+
+ INTRODUCE_PHI(mem_start, MachineType::PointerRepresentation());
+ INTRODUCE_PHI(mem_size, MachineRepresentation::kWord32);
+ if (untrusted_code_mitigations_) {
+ INTRODUCE_PHI(mem_mask, MachineRepresentation::kWord32);
+ }
+
+#undef INTRODUCE_PHI
+}
+
+void WasmGraphBuilder::NewContextCacheMerge(WasmContextCacheNodes* to,
+ WasmContextCacheNodes* from,
+ Node* merge) {
+#define INTRODUCE_PHI(field, rep) \
+ if (to->field != from->field) { \
+ Node* vals[] = {to->field, from->field}; \
+ to->field = Phi(rep, 2, vals, merge); \
+ }
+
+ INTRODUCE_PHI(mem_start, MachineType::PointerRepresentation());
+ INTRODUCE_PHI(mem_size, MachineRepresentation::kWord32);
+ if (untrusted_code_mitigations_) {
+ INTRODUCE_PHI(mem_mask, MachineRepresentation::kWord32);
+ }
+
+#undef INTRODUCE_PHI
+}
+
+void WasmGraphBuilder::MergeContextCacheInto(WasmContextCacheNodes* to,
+ WasmContextCacheNodes* from,
+ Node* merge) {
+ to->mem_size = CreateOrMergeIntoPhi(MachineRepresentation::kWord32, merge,
+ to->mem_size, from->mem_size);
+ to->mem_start = CreateOrMergeIntoPhi(MachineType::PointerRepresentation(),
+ merge, to->mem_start, from->mem_start);
+ if (untrusted_code_mitigations_) {
+ to->mem_mask = CreateOrMergeIntoPhi(MachineRepresentation::kWord32, merge,
+ to->mem_mask, from->mem_mask);
+ }
+}
+
+Node* WasmGraphBuilder::CreateOrMergeIntoPhi(wasm::ValueType type, Node* merge,
+ Node* tnode, Node* fnode) {
+ if (IsPhiWithMerge(tnode, merge)) {
+ AppendToPhi(tnode, fnode);
+ } else if (tnode != fnode) {
+ uint32_t count = merge->InputCount();
+ Node** vals = Buffer(count);
+ for (uint32_t j = 0; j < count - 1; j++) vals[j] = tnode;
+ vals[count - 1] = fnode;
+ return Phi(type, count, vals, merge);
+ }
+ return tnode;
+}
+
+Node* WasmGraphBuilder::CreateOrMergeIntoEffectPhi(Node* merge, Node* tnode,
+ Node* fnode) {
+ if (IsPhiWithMerge(tnode, merge)) {
+ AppendToPhi(tnode, fnode);
+ } else if (tnode != fnode) {
+ uint32_t count = merge->InputCount();
+ Node** effects = Buffer(count);
+ for (uint32_t j = 0; j < count - 1; j++) {
+ effects[j] = tnode;
+ }
+ effects[count - 1] = fnode;
+ tnode = EffectPhi(count, effects, merge);
+ }
+ return tnode;
+}
+
+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);
}
- return mem_size;
}
Node* WasmGraphBuilder::MemBuffer(uint32_t offset) {
- DCHECK_NOT_NULL(*mem_start_);
- if (offset == 0) return *mem_start_;
- return graph()->NewNode(jsgraph()->machine()->IntAdd(), *mem_start_,
+ DCHECK_NOT_NULL(context_cache_);
+ Node* mem_start = context_cache_->mem_start;
+ DCHECK_NOT_NULL(mem_start);
+ if (offset == 0) return mem_start;
+ return graph()->NewNode(jsgraph()->machine()->IntAdd(), mem_start,
jsgraph()->IntPtrConstant(offset));
}
Node* WasmGraphBuilder::CurrentMemoryPages() {
// CurrentMemoryPages can not be called from asm.js.
DCHECK_EQ(wasm::kWasmOrigin, env_->module->origin());
- DCHECK_NOT_NULL(*mem_size_);
- Node* mem_size = *mem_size_;
+ DCHECK_NOT_NULL(context_cache_);
+ Node* mem_size = context_cache_->mem_size;
+ DCHECK_NOT_NULL(mem_size);
if (jsgraph()->machine()->Is64()) {
mem_size = graph()->NewNode(jsgraph()->machine()->TruncateInt64ToInt32(),
mem_size);
@@ -3296,23 +3442,6 @@ Node* WasmGraphBuilder::BuildCallToRuntimeWithContext(Runtime::FunctionId f,
Node* js_context,
Node** parameters,
int parameter_count) {
- // We're leaving Wasm code, so clear the flag.
- *control_ = BuildModifyThreadInWasmFlag(false);
- // Since the thread-in-wasm flag is clear, it is as if we are calling from JS.
- Node* call = BuildCallToRuntimeWithContextFromJS(f, js_context, parameters,
- parameter_count);
-
- // Restore the thread-in-wasm flag, since we have returned to Wasm.
- *control_ = BuildModifyThreadInWasmFlag(true);
-
- return call;
-}
-
-// This version of BuildCallToRuntime does not clear and set the thread-in-wasm
-// flag.
-Node* WasmGraphBuilder::BuildCallToRuntimeWithContextFromJS(
- Runtime::FunctionId f, Node* js_context, Node* const* parameters,
- int parameter_count) {
const Runtime::Function* fun = Runtime::FunctionForId(f);
CallDescriptor* desc = Linkage::GetRuntimeCallDescriptor(
jsgraph()->zone(), f, fun->nargs, Operator::kNoProperties,
@@ -3354,13 +3483,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;
}
@@ -3368,23 +3496,33 @@ 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;
}
-void WasmGraphBuilder::BoundsCheckMem(MachineType memtype, Node* index,
- uint32_t offset,
- wasm::WasmCodePosition position) {
- if (FLAG_wasm_no_bounds_checks) return;
- DCHECK_NOT_NULL(*mem_size_);
+Node* WasmGraphBuilder::BoundsCheckMem(MachineType memtype, Node* index,
+ uint32_t offset,
+ wasm::WasmCodePosition position,
+ EnforceBoundsCheck enforce_check) {
+ if (FLAG_wasm_no_bounds_checks) return index;
+ DCHECK_NOT_NULL(context_cache_);
+ Node* mem_size = context_cache_->mem_size;
+ DCHECK_NOT_NULL(mem_size);
+
+ auto m = jsgraph()->machine();
+ if (trap_handler::UseTrapHandler() && enforce_check == kCanOmitBoundsCheck) {
+ // Simply zero out the 32-bits on 64-bit targets and let the trap handler
+ // do its job.
+ return m->Is64() ? graph()->NewNode(m->ChangeUint32ToUint64(), index)
+ : index;
+ }
uint32_t min_size = env_->module->initial_pages * wasm::WasmModule::kPageSize;
uint32_t max_size =
@@ -3398,7 +3536,7 @@ void WasmGraphBuilder::BoundsCheckMem(MachineType memtype, Node* index,
// The access will be out of bounds, even for the largest memory.
TrapIfEq32(wasm::kTrapMemOutOfBounds, jsgraph()->Int32Constant(0), 0,
position);
- return;
+ return jsgraph()->IntPtrConstant(0);
}
uint32_t end_offset = offset + access_size;
@@ -3406,48 +3544,44 @@ void WasmGraphBuilder::BoundsCheckMem(MachineType memtype, Node* index,
// The end offset is larger than the smallest memory.
// Dynamically check the end offset against the actual memory size, which
// is not known at compile time.
- Node* cond;
- if (jsgraph()->machine()->Is32()) {
- cond = graph()->NewNode(jsgraph()->machine()->Uint32LessThanOrEqual(),
- jsgraph()->Int32Constant(end_offset), *mem_size_);
- } else {
- cond = graph()->NewNode(
- jsgraph()->machine()->Uint64LessThanOrEqual(),
- jsgraph()->Int64Constant(static_cast<int64_t>(end_offset)),
- *mem_size_);
- }
+ Node* cond =
+ graph()->NewNode(jsgraph()->machine()->Uint32LessThanOrEqual(),
+ jsgraph()->Int32Constant(end_offset), mem_size);
TrapIfFalse(wasm::kTrapMemOutOfBounds, cond, position);
} else {
// The end offset is within the bounds of the smallest memory, so only
// one check is required. Check to see if the index is also a constant.
- UintPtrMatcher m(index);
- if (m.HasValue()) {
- uint64_t index_val = m.Value();
- if ((index_val + offset + access_size) <= min_size) {
+ Uint32Matcher match(index);
+ if (match.HasValue()) {
+ uint32_t index_val = match.Value();
+ if (index_val <= min_size - end_offset) {
// The input index is a constant and everything is statically within
// bounds of the smallest possible memory.
- return;
+ return m->Is64() ? graph()->NewNode(m->ChangeUint32ToUint64(), index)
+ : index;
}
}
}
- Node* effective_size;
- if (jsgraph()->machine()->Is32()) {
- effective_size =
- graph()->NewNode(jsgraph()->machine()->Int32Sub(), *mem_size_,
- jsgraph()->Int32Constant(end_offset - 1));
- } else {
- effective_size = graph()->NewNode(
- jsgraph()->machine()->Int64Sub(), *mem_size_,
- jsgraph()->Int64Constant(static_cast<int64_t>(end_offset - 1)));
- }
-
- const Operator* less = jsgraph()->machine()->Is32()
- ? jsgraph()->machine()->Uint32LessThan()
- : jsgraph()->machine()->Uint64LessThan();
+ // Compute the effective size of the memory, which is the size of the memory
+ // minus the statically known offset, minus the byte size of the access minus
+ // one.
+ // This produces a positive number since {end_offset <= min_size <= mem_size}.
+ Node* effective_size =
+ graph()->NewNode(jsgraph()->machine()->Int32Sub(), mem_size,
+ jsgraph()->Int32Constant(end_offset - 1));
- Node* cond = graph()->NewNode(less, index, effective_size);
+ // Introduce the actual bounds check.
+ Node* cond = graph()->NewNode(m->Uint32LessThan(), index, effective_size);
TrapIfFalse(wasm::kTrapMemOutOfBounds, cond, position);
+
+ if (untrusted_code_mitigations_) {
+ // In the fallthrough case, condition the index with the memory mask.
+ Node* mem_mask = context_cache_->mem_mask;
+ DCHECK_NOT_NULL(mem_mask);
+ index = graph()->NewNode(m->Word32And(), index, mem_mask);
+ }
+ return m->Is64() ? graph()->NewNode(m->ChangeUint32ToUint64(), index) : index;
}
const Operator* WasmGraphBuilder::GetSafeLoadOperator(int offset,
@@ -3500,14 +3634,9 @@ Node* WasmGraphBuilder::LoadMem(wasm::ValueType type, MachineType memtype,
wasm::WasmCodePosition position) {
Node* load;
- if (jsgraph()->machine()->Is64()) {
- index =
- graph()->NewNode(jsgraph()->machine()->ChangeUint32ToUint64(), index);
- }
- // Wasm semantics throw on OOB. Introduce explicit bounds check.
- if (!FLAG_wasm_trap_handler || !V8_TRAP_HANDLER_SUPPORTED) {
- BoundsCheckMem(memtype, index, offset, position);
- }
+ // Wasm semantics throw on OOB. Introduce explicit bounds check and
+ // conditioning when not using the trap handler.
+ index = BoundsCheckMem(memtype, index, offset, position, kCanOmitBoundsCheck);
if (memtype.representation() == MachineRepresentation::kWord8 ||
jsgraph()->machine()->UnalignedLoadSupported(memtype.representation())) {
@@ -3521,7 +3650,7 @@ Node* WasmGraphBuilder::LoadMem(wasm::ValueType type, MachineType memtype,
}
} else {
// TODO(eholk): Support unaligned loads with trap handlers.
- DCHECK(!FLAG_wasm_trap_handler || !V8_TRAP_HANDLER_SUPPORTED);
+ DCHECK(!trap_handler::UseTrapHandler());
load = graph()->NewNode(jsgraph()->machine()->UnalignedLoad(memtype),
MemBuffer(offset), index, *effect_, *control_);
}
@@ -3559,14 +3688,7 @@ Node* WasmGraphBuilder::StoreMem(MachineType memtype, Node* index,
wasm::ValueType type) {
Node* store;
- if (jsgraph()->machine()->Is64()) {
- index =
- graph()->NewNode(jsgraph()->machine()->ChangeUint32ToUint64(), index);
- }
- // Wasm semantics throw on OOB. Introduce explicit bounds check.
- if (!FLAG_wasm_trap_handler || !V8_TRAP_HANDLER_SUPPORTED) {
- BoundsCheckMem(memtype, index, offset, position);
- }
+ index = BoundsCheckMem(memtype, index, offset, position, kCanOmitBoundsCheck);
#if defined(V8_TARGET_BIG_ENDIAN)
val = BuildChangeEndiannessStore(val, memtype, type);
@@ -3574,7 +3696,7 @@ Node* WasmGraphBuilder::StoreMem(MachineType memtype, Node* index,
if (memtype.representation() == MachineRepresentation::kWord8 ||
jsgraph()->machine()->UnalignedStoreSupported(memtype.representation())) {
- if (FLAG_wasm_trap_handler && V8_TRAP_HANDLER_SUPPORTED) {
+ if (trap_handler::UseTrapHandler()) {
store = graph()->NewNode(
jsgraph()->machine()->ProtectedStore(memtype.representation()),
MemBuffer(offset), index, val, *effect_, *control_);
@@ -3587,7 +3709,7 @@ Node* WasmGraphBuilder::StoreMem(MachineType memtype, Node* index,
}
} else {
// TODO(eholk): Support unaligned stores with trap handlers.
- DCHECK(!FLAG_wasm_trap_handler || !V8_TRAP_HANDLER_SUPPORTED);
+ DCHECK(!trap_handler::UseTrapHandler());
UnalignedStoreRepresentation rep(memtype.representation());
store =
graph()->NewNode(jsgraph()->machine()->UnalignedStore(rep),
@@ -3604,37 +3726,106 @@ Node* WasmGraphBuilder::StoreMem(MachineType memtype, Node* index,
return store;
}
+namespace {
+Node* GetAsmJsOOBValue(MachineRepresentation rep, JSGraph* jsgraph) {
+ switch (rep) {
+ case MachineRepresentation::kWord8:
+ case MachineRepresentation::kWord16:
+ case MachineRepresentation::kWord32:
+ return jsgraph->Int32Constant(0);
+ case MachineRepresentation::kWord64:
+ return jsgraph->Int64Constant(0);
+ case MachineRepresentation::kFloat32:
+ return jsgraph->Float32Constant(std::numeric_limits<float>::quiet_NaN());
+ case MachineRepresentation::kFloat64:
+ return jsgraph->Float64Constant(std::numeric_limits<double>::quiet_NaN());
+ default:
+ UNREACHABLE();
+ }
+}
+} // namespace
+
Node* WasmGraphBuilder::BuildAsmjsLoadMem(MachineType type, Node* index) {
- // TODO(turbofan): fold bounds checks for constant asm.js loads.
- // asm.js semantics use CheckedLoad (i.e. OOB reads return 0ish).
- DCHECK_NOT_NULL(*mem_size_);
- DCHECK_NOT_NULL(*mem_start_);
+ DCHECK_NOT_NULL(context_cache_);
+ Node* mem_start = context_cache_->mem_start;
+ Node* mem_size = context_cache_->mem_size;
+ DCHECK_NOT_NULL(mem_start);
+ DCHECK_NOT_NULL(mem_size);
+
+ // Asm.js semantics are defined along the lines of typed arrays, hence OOB
+ // reads return {undefined} coerced to the result type (0 for integers, NaN
+ // for float and double).
+ // Note that we check against the memory size ignoring the size of the
+ // stored value, which is conservative if misaligned. Technically, asm.js
+ // should never have misaligned accesses.
+ Diamond bounds_check(
+ graph(), jsgraph()->common(),
+ graph()->NewNode(jsgraph()->machine()->Uint32LessThan(), index, mem_size),
+ BranchHint::kTrue);
+ bounds_check.Chain(*control_);
+
+ if (untrusted_code_mitigations_) {
+ // Condition the index with the memory mask.
+ Node* mem_mask = context_cache_->mem_mask;
+ DCHECK_NOT_NULL(mem_mask);
+ index =
+ graph()->NewNode(jsgraph()->machine()->Word32And(), index, mem_mask);
+ }
+
if (jsgraph()->machine()->Is64()) {
index =
graph()->NewNode(jsgraph()->machine()->ChangeUint32ToUint64(), index);
}
- const Operator* op = jsgraph()->machine()->CheckedLoad(type);
- Node* load =
- graph()->NewNode(op, *mem_start_, index, *mem_size_, *effect_, *control_);
- *effect_ = load;
- return load;
+ Node* load = graph()->NewNode(jsgraph()->machine()->Load(type), mem_start,
+ index, *effect_, bounds_check.if_true);
+ Node* value_phi =
+ bounds_check.Phi(type.representation(), load,
+ GetAsmJsOOBValue(type.representation(), jsgraph()));
+ Node* effect_phi = graph()->NewNode(jsgraph()->common()->EffectPhi(2), load,
+ *effect_, bounds_check.merge);
+ *effect_ = effect_phi;
+ *control_ = bounds_check.merge;
+ return value_phi;
}
Node* WasmGraphBuilder::BuildAsmjsStoreMem(MachineType type, Node* index,
Node* val) {
- // TODO(turbofan): fold bounds checks for constant asm.js stores.
- // asm.js semantics use CheckedStore (i.e. ignore OOB writes).
- DCHECK_NOT_NULL(*mem_size_);
- DCHECK_NOT_NULL(*mem_start_);
+ DCHECK_NOT_NULL(context_cache_);
+ Node* mem_start = context_cache_->mem_start;
+ Node* mem_size = context_cache_->mem_size;
+ DCHECK_NOT_NULL(mem_start);
+ DCHECK_NOT_NULL(mem_size);
+
+ // Asm.js semantics are to ignore OOB writes.
+ // Note that we check against the memory size ignoring the size of the
+ // stored value, which is conservative if misaligned. Technically, asm.js
+ // should never have misaligned accesses.
+ Diamond bounds_check(
+ graph(), jsgraph()->common(),
+ graph()->NewNode(jsgraph()->machine()->Uint32LessThan(), index, mem_size),
+ BranchHint::kTrue);
+ bounds_check.Chain(*control_);
+
+ if (untrusted_code_mitigations_) {
+ // Condition the index with the memory mask.
+ Node* mem_mask = context_cache_->mem_mask;
+ DCHECK_NOT_NULL(mem_mask);
+ index =
+ graph()->NewNode(jsgraph()->machine()->Word32And(), index, mem_mask);
+ }
+
if (jsgraph()->machine()->Is64()) {
index =
graph()->NewNode(jsgraph()->machine()->ChangeUint32ToUint64(), index);
}
- const Operator* op =
- jsgraph()->machine()->CheckedStore(type.representation());
- Node* store = graph()->NewNode(op, *mem_start_, index, *mem_size_, val,
- *effect_, *control_);
- *effect_ = store;
+ const Operator* store_op = jsgraph()->machine()->Store(StoreRepresentation(
+ type.representation(), WriteBarrierKind::kNoWriteBarrier));
+ Node* store = graph()->NewNode(store_op, mem_start, index, val, *effect_,
+ bounds_check.if_true);
+ Node* effect_phi = graph()->NewNode(jsgraph()->common()->EffectPhi(2), store,
+ *effect_, bounds_check.merge);
+ *effect_ = effect_phi;
+ *control_ = bounds_check.merge;
return val;
}
@@ -4111,47 +4302,50 @@ Node* WasmGraphBuilder::AtomicOp(wasm::WasmOpcode opcode, Node* const* inputs,
// TODO(gdeepti): Add alignment validation, traps on misalignment
Node* node;
switch (opcode) {
-#define BUILD_ATOMIC_BINOP(Name, Operation, Type) \
- case wasm::kExpr##Name: { \
- BoundsCheckMem(MachineType::Type(), inputs[0], offset, position); \
- node = graph()->NewNode( \
- jsgraph()->machine()->Atomic##Operation(MachineType::Type()), \
- MemBuffer(offset), inputs[0], inputs[1], *effect_, *control_); \
- break; \
+#define BUILD_ATOMIC_BINOP(Name, Operation, Type) \
+ case wasm::kExpr##Name: { \
+ Node* index = BoundsCheckMem(MachineType::Type(), inputs[0], offset, \
+ position, kNeedsBoundsCheck); \
+ node = graph()->NewNode( \
+ jsgraph()->machine()->Atomic##Operation(MachineType::Type()), \
+ MemBuffer(offset), index, inputs[1], *effect_, *control_); \
+ break; \
}
ATOMIC_BINOP_LIST(BUILD_ATOMIC_BINOP)
#undef BUILD_ATOMIC_BINOP
-#define BUILD_ATOMIC_TERNARY_OP(Name, Operation, Type) \
- case wasm::kExpr##Name: { \
- BoundsCheckMem(MachineType::Type(), inputs[0], offset, position); \
- node = graph()->NewNode( \
- jsgraph()->machine()->Atomic##Operation(MachineType::Type()), \
- MemBuffer(offset), inputs[0], inputs[1], inputs[2], *effect_, \
- *control_); \
- break; \
+#define BUILD_ATOMIC_TERNARY_OP(Name, Operation, Type) \
+ case wasm::kExpr##Name: { \
+ Node* index = BoundsCheckMem(MachineType::Type(), inputs[0], offset, \
+ position, kNeedsBoundsCheck); \
+ node = graph()->NewNode( \
+ jsgraph()->machine()->Atomic##Operation(MachineType::Type()), \
+ MemBuffer(offset), index, inputs[1], inputs[2], *effect_, *control_); \
+ break; \
}
ATOMIC_TERNARY_LIST(BUILD_ATOMIC_TERNARY_OP)
#undef BUILD_ATOMIC_TERNARY_OP
-#define BUILD_ATOMIC_LOAD_OP(Name, Type) \
- case wasm::kExpr##Name: { \
- BoundsCheckMem(MachineType::Type(), inputs[0], offset, position); \
- node = graph()->NewNode( \
- jsgraph()->machine()->AtomicLoad(MachineType::Type()), \
- MemBuffer(offset), inputs[0], *effect_, *control_); \
- break; \
+#define BUILD_ATOMIC_LOAD_OP(Name, Type) \
+ case wasm::kExpr##Name: { \
+ Node* index = BoundsCheckMem(MachineType::Type(), inputs[0], offset, \
+ position, kNeedsBoundsCheck); \
+ node = graph()->NewNode( \
+ jsgraph()->machine()->AtomicLoad(MachineType::Type()), \
+ MemBuffer(offset), index, *effect_, *control_); \
+ break; \
}
ATOMIC_LOAD_LIST(BUILD_ATOMIC_LOAD_OP)
#undef BUILD_ATOMIC_LOAD_OP
-#define BUILD_ATOMIC_STORE_OP(Name, Type, Rep) \
- case wasm::kExpr##Name: { \
- BoundsCheckMem(MachineType::Type(), inputs[0], offset, position); \
- node = graph()->NewNode( \
- jsgraph()->machine()->AtomicStore(MachineRepresentation::Rep), \
- MemBuffer(offset), inputs[0], inputs[1], *effect_, *control_); \
- break; \
+#define BUILD_ATOMIC_STORE_OP(Name, Type, Rep) \
+ case wasm::kExpr##Name: { \
+ Node* index = BoundsCheckMem(MachineType::Type(), inputs[0], offset, \
+ position, kNeedsBoundsCheck); \
+ node = graph()->NewNode( \
+ jsgraph()->machine()->AtomicStore(MachineRepresentation::Rep), \
+ MemBuffer(offset), index, inputs[1], *effect_, *control_); \
+ break; \
}
ATOMIC_STORE_LIST(BUILD_ATOMIC_STORE_OP)
#undef BUILD_ATOMIC_STORE_OP
@@ -4196,7 +4390,7 @@ void RecordFunctionCompilation(CodeEventListener::LogEventsAndTags tag,
} // namespace
Handle<Code> CompileJSToWasmWrapper(Isolate* isolate, wasm::WasmModule* module,
- Handle<Code> wasm_code, uint32_t index,
+ WasmCodeWrapper wasm_code, uint32_t index,
Address wasm_context_address) {
const wasm::WasmFunction* func = &module->functions[index];
@@ -4206,20 +4400,25 @@ Handle<Code> CompileJSToWasmWrapper(Isolate* isolate, wasm::WasmModule* module,
Zone zone(isolate->allocator(), ZONE_NAME);
Graph graph(&zone);
CommonOperatorBuilder common(&zone);
- MachineOperatorBuilder machine(&zone);
+ MachineOperatorBuilder machine(
+ &zone, MachineType::PointerRepresentation(),
+ InstructionSelector::SupportedMachineOperatorFlags(),
+ InstructionSelector::AlignmentRequirements());
JSGraph jsgraph(isolate, &graph, &common, nullptr, nullptr, &machine);
Node* control = nullptr;
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,
+ std::vector<Address>(), // function_tables
+ std::vector<Address>(), // signature_tables
+ // TODO(mtrofin): remove these 2 lines when we don't need
+ // FLAG_wasm_jit_to_native
+ std::vector<Handle<Code>>(), // function_code
+ BUILTIN_CODE(isolate, Illegal) // default_function_code
+ };
WasmGraphBuilder builder(&env, &zone, &jsgraph,
CEntryStub(isolate, 1).GetCode(), func->sig);
@@ -4250,8 +4449,9 @@ Handle<Code> CompileJSToWasmWrapper(Isolate* isolate, wasm::WasmModule* module,
Vector<const char> func_name = CStrVector("js-to-wasm");
#endif
- CompilationInfo info(func_name, isolate, &zone, Code::JS_TO_WASM_FUNCTION);
- Handle<Code> code = Pipeline::GenerateCodeForTesting(&info, incoming, &graph);
+ CompilationInfo info(func_name, &zone, Code::JS_TO_WASM_FUNCTION);
+ Handle<Code> code =
+ Pipeline::GenerateCodeForTesting(&info, isolate, incoming, &graph);
#ifdef ENABLE_DISASSEMBLER
if (FLAG_print_opt_code && !code.is_null()) {
OFStream os(stdout);
@@ -4267,6 +4467,8 @@ Handle<Code> CompileJSToWasmWrapper(Isolate* isolate, wasm::WasmModule* module,
return code;
}
+namespace {
+
void ValidateImportWrapperReferencesImmovables(Handle<Code> wrapper) {
#if !DEBUG
return;
@@ -4283,7 +4485,7 @@ void ValidateImportWrapperReferencesImmovables(Handle<Code> wrapper) {
case RelocInfo::CODE_TARGET:
// this would be either one of the stubs or builtins, because
// we didn't link yet.
- target = reinterpret_cast<Object*>(it.rinfo()->target_address());
+ target = Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
break;
case RelocInfo::EMBEDDED_OBJECT:
target = it.rinfo()->target_object();
@@ -4294,10 +4496,19 @@ void ValidateImportWrapperReferencesImmovables(Handle<Code> wrapper) {
CHECK_NOT_NULL(target);
bool is_immovable =
target->IsSmi() || Heap::IsImmovable(HeapObject::cast(target));
- CHECK(is_immovable);
+ bool is_allowed_stub = false;
+ if (target->IsCode()) {
+ Code* code = Code::cast(target);
+ is_allowed_stub =
+ code->kind() == Code::STUB &&
+ CodeStub::MajorKeyFromKey(code->stub_key()) == CodeStub::DoubleToI;
+ }
+ CHECK(is_immovable || is_allowed_stub);
}
}
+} // namespace
+
Handle<Code> CompileWasmToJSWrapper(
Isolate* isolate, Handle<JSReceiver> target, wasm::FunctionSig* sig,
uint32_t index, wasm::ModuleOrigin origin,
@@ -4308,7 +4519,10 @@ Handle<Code> CompileWasmToJSWrapper(
Zone zone(isolate->allocator(), ZONE_NAME);
Graph graph(&zone);
CommonOperatorBuilder common(&zone);
- MachineOperatorBuilder machine(&zone);
+ MachineOperatorBuilder machine(
+ &zone, MachineType::PointerRepresentation(),
+ InstructionSelector::SupportedMachineOperatorFlags(),
+ InstructionSelector::AlignmentRequirements());
JSGraph jsgraph(isolate, &graph, &common, nullptr, nullptr, &machine);
Node* control = nullptr;
@@ -4357,9 +4571,9 @@ Handle<Code> CompileWasmToJSWrapper(
Vector<const char> func_name = CStrVector("wasm-to-js");
#endif
- CompilationInfo info(func_name, isolate, &zone, Code::WASM_TO_JS_FUNCTION);
+ CompilationInfo info(func_name, &zone, Code::WASM_TO_JS_FUNCTION);
Handle<Code> code = Pipeline::GenerateCodeForTesting(
- &info, incoming, &graph, nullptr, source_position_table);
+ &info, isolate, incoming, &graph, nullptr, source_position_table);
ValidateImportWrapperReferencesImmovables(code);
Handle<FixedArray> deopt_data =
isolate->factory()->NewFixedArray(2, TENURED);
@@ -4386,8 +4600,8 @@ Handle<Code> CompileWasmToJSWrapper(
return code;
}
-Handle<Code> CompileWasmToWasmWrapper(Isolate* isolate, Handle<Code> target,
- wasm::FunctionSig* sig, uint32_t index,
+Handle<Code> CompileWasmToWasmWrapper(Isolate* isolate, WasmCodeWrapper target,
+ wasm::FunctionSig* sig,
Address new_wasm_context_address) {
//----------------------------------------------------------------------------
// Create the Graph
@@ -4395,7 +4609,10 @@ Handle<Code> CompileWasmToWasmWrapper(Isolate* isolate, Handle<Code> target,
Zone zone(isolate->allocator(), ZONE_NAME);
Graph graph(&zone);
CommonOperatorBuilder common(&zone);
- MachineOperatorBuilder machine(&zone);
+ MachineOperatorBuilder machine(
+ &zone, MachineType::PointerRepresentation(),
+ InstructionSelector::SupportedMachineOperatorFlags(),
+ InstructionSelector::AlignmentRequirements());
JSGraph jsgraph(isolate, &graph, &common, nullptr, nullptr, &machine);
Node* control = nullptr;
@@ -4433,8 +4650,9 @@ Handle<Code> CompileWasmToWasmWrapper(Isolate* isolate, Handle<Code> target,
func_name = Vector<const char>::cast(buffer.SubVector(0, chars));
}
- CompilationInfo info(func_name, isolate, &zone, Code::WASM_FUNCTION);
- Handle<Code> code = Pipeline::GenerateCodeForTesting(&info, incoming, &graph);
+ CompilationInfo info(func_name, &zone, Code::WASM_TO_WASM_FUNCTION);
+ Handle<Code> code =
+ Pipeline::GenerateCodeForTesting(&info, isolate, incoming, &graph);
#ifdef ENABLE_DISASSEMBLER
if (FLAG_print_opt_code && !code.is_null()) {
OFStream os(stdout);
@@ -4446,7 +4664,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;
@@ -4474,7 +4692,7 @@ Handle<Code> CompileWasmInterpreterEntry(Isolate* isolate, uint32_t func_index,
CEntryStub(isolate, 1).GetCode(), sig);
builder.set_control_ptr(&control);
builder.set_effect_ptr(&effect);
- builder.BuildWasmInterpreterEntry(func_index, instance);
+ builder.BuildWasmInterpreterEntry(func_index);
Handle<Code> code = Handle<Code>::null();
{
@@ -4497,9 +4715,9 @@ Handle<Code> CompileWasmInterpreterEntry(Isolate* isolate, uint32_t func_index,
Vector<const char> func_name = CStrVector("wasm-interpreter-entry");
#endif
- CompilationInfo info(func_name, isolate, &zone,
- Code::WASM_INTERPRETER_ENTRY);
- code = Pipeline::GenerateCodeForTesting(&info, incoming, &graph, nullptr);
+ CompilationInfo info(func_name, &zone, Code::WASM_INTERPRETER_ENTRY);
+ code = Pipeline::GenerateCodeForTesting(&info, isolate, incoming, &graph,
+ nullptr);
#ifdef ENABLE_DISASSEMBLER
if (FLAG_print_opt_code && !code.is_null()) {
OFStream os(stdout);
@@ -4513,11 +4731,13 @@ Handle<Code> CompileWasmInterpreterEntry(Isolate* isolate, uint32_t func_index,
}
}
- Handle<FixedArray> deopt_data = isolate->factory()->NewFixedArray(1, TENURED);
- Handle<WeakCell> weak_instance = isolate->factory()->NewWeakCell(instance);
- deopt_data->set(0, *weak_instance);
- code->set_deoptimization_data(*deopt_data);
-
+ if (!FLAG_wasm_jit_to_native) {
+ Handle<FixedArray> deopt_data =
+ isolate->factory()->NewFixedArray(1, TENURED);
+ Handle<WeakCell> weak_instance = isolate->factory()->NewWeakCell(instance);
+ deopt_data->set(0, *weak_instance);
+ code->set_deoptimization_data(*deopt_data);
+ }
return code;
}
@@ -4526,7 +4746,10 @@ Handle<Code> CompileCWasmEntry(Isolate* isolate, wasm::FunctionSig* sig,
Zone zone(isolate->allocator(), ZONE_NAME);
Graph graph(&zone);
CommonOperatorBuilder common(&zone);
- MachineOperatorBuilder machine(&zone);
+ MachineOperatorBuilder machine(
+ &zone, MachineType::PointerRepresentation(),
+ InstructionSelector::SupportedMachineOperatorFlags(),
+ InstructionSelector::AlignmentRequirements());
JSGraph jsgraph(isolate, &graph, &common, nullptr, nullptr, &machine);
Node* control = nullptr;
@@ -4566,8 +4789,9 @@ Handle<Code> CompileCWasmEntry(Isolate* isolate, wasm::FunctionSig* sig,
debug_name[name_len] = '\0';
Vector<const char> debug_name_vec(debug_name, name_len);
- CompilationInfo info(debug_name_vec, isolate, &zone, Code::C_WASM_ENTRY);
- Handle<Code> code = Pipeline::GenerateCodeForTesting(&info, incoming, &graph);
+ CompilationInfo info(debug_name_vec, &zone, Code::C_WASM_ENTRY);
+ Handle<Code> code =
+ Pipeline::GenerateCodeForTesting(&info, isolate, incoming, &graph);
#ifdef ENABLE_DISASSEMBLER
if (FLAG_print_opt_code && !code.is_null()) {
OFStream os(stdout);
@@ -4585,7 +4809,6 @@ SourcePositionTable* WasmCompilationUnit::BuildGraphForWasmFunction(
size_t tables_size = env_->module->function_tables.size();
DCHECK_EQ(tables_size, env_->function_tables.size());
DCHECK_EQ(tables_size, env_->signature_tables.size());
- DCHECK_EQ(tables_size, env_->signature_maps.size());
}
#endif
@@ -4596,17 +4819,17 @@ SourcePositionTable* WasmCompilationUnit::BuildGraphForWasmFunction(
// Create a TF graph during decoding.
SourcePositionTable* source_position_table =
- new (jsgraph_->zone()) SourcePositionTable(jsgraph_->graph());
- WasmGraphBuilder builder(env_, jsgraph_->zone(), jsgraph_, centry_stub_,
- func_body_.sig, source_position_table,
+ new (tf_.jsgraph_->zone()) SourcePositionTable(tf_.jsgraph_->graph());
+ WasmGraphBuilder builder(env_, tf_.jsgraph_->zone(), tf_.jsgraph_,
+ centry_stub_, func_body_.sig, source_position_table,
runtime_exception_support_);
- graph_construction_result_ =
+ tf_.graph_construction_result_ =
wasm::BuildTFGraph(isolate_->allocator(), &builder, func_body_);
- if (graph_construction_result_.failed()) {
+ if (tf_.graph_construction_result_.failed()) {
if (FLAG_trace_wasm_compiler) {
OFStream os(stdout);
- os << "Compilation failed: " << graph_construction_result_.error_msg()
+ os << "Compilation failed: " << tf_.graph_construction_result_.error_msg()
<< std::endl;
}
return nullptr;
@@ -4616,7 +4839,7 @@ SourcePositionTable* WasmCompilationUnit::BuildGraphForWasmFunction(
if (builder.has_simd() &&
(!CpuFeatures::SupportsWasmSimd128() || lower_simd_)) {
- SimdScalarLowering(jsgraph_, func_body_.sig).LowerGraph();
+ SimdScalarLowering(tf_.jsgraph_, func_body_.sig).LowerGraph();
}
if (func_index_ >= FLAG_trace_wasm_ast_start &&
@@ -4647,13 +4870,21 @@ Vector<const char> GetDebugName(Zone* zone, wasm::WasmName name, int index) {
memcpy(index_name, name_vector.start(), name_len);
return Vector<const char>(index_name, name_len);
}
+
} // namespace
+// static
+WasmCompilationUnit::CompilationMode
+WasmCompilationUnit::GetDefaultCompilationMode() {
+ return FLAG_liftoff ? WasmCompilationUnit::CompilationMode::kLiftoff
+ : WasmCompilationUnit::CompilationMode::kTurbofan;
+}
+
WasmCompilationUnit::WasmCompilationUnit(
- Isolate* isolate, ModuleEnv* env, wasm::FunctionBody body,
- wasm::WasmName name, int index, Handle<Code> centry_stub,
- Counters* counters, RuntimeExceptionSupport exception_support,
- bool lower_simd)
+ Isolate* isolate, ModuleEnv* env, wasm::NativeModule* native_module,
+ wasm::FunctionBody body, wasm::WasmName name, int index,
+ Handle<Code> centry_stub, CompilationMode mode, Counters* counters,
+ RuntimeExceptionSupport exception_support, bool lower_simd)
: isolate_(isolate),
env_(env),
func_body_(body),
@@ -4662,9 +4893,38 @@ WasmCompilationUnit::WasmCompilationUnit(
centry_stub_(centry_stub),
func_index_(index),
runtime_exception_support_(exception_support),
- lower_simd_(lower_simd) {}
+ native_module_(native_module),
+ lower_simd_(lower_simd),
+ protected_instructions_(
+ new std::vector<trap_handler::ProtectedInstructionData>()),
+ mode_(mode) {
+ switch (mode_) {
+ case WasmCompilationUnit::CompilationMode::kLiftoff:
+ new (&liftoff_) LiftoffData(isolate);
+ break;
+ case WasmCompilationUnit::CompilationMode::kTurbofan:
+ new (&tf_) TurbofanData();
+ break;
+ }
+}
+
+WasmCompilationUnit::~WasmCompilationUnit() {
+ switch (mode_) {
+ case WasmCompilationUnit::CompilationMode::kLiftoff:
+ liftoff_.~LiftoffData();
+ break;
+ case WasmCompilationUnit::CompilationMode::kTurbofan:
+ tf_.~TurbofanData();
+ break;
+ }
+}
void WasmCompilationUnit::ExecuteCompilation() {
+ auto size_histogram = env_->module->is_wasm()
+ ? counters()->wasm_wasm_function_size_bytes()
+ : counters()->wasm_asm_function_size_bytes();
+ size_histogram->AddSample(
+ static_cast<int>(func_body_.end - func_body_.start));
auto timed_histogram = env_->module->is_wasm()
? counters()->wasm_compile_wasm_function_time()
: counters()->wasm_compile_asm_function_time();
@@ -4679,13 +4939,28 @@ void WasmCompilationUnit::ExecuteCompilation() {
}
}
+ switch (mode_) {
+ case WasmCompilationUnit::CompilationMode::kLiftoff:
+ if (ExecuteLiftoffCompilation()) break;
+ // Otherwise, fall back to turbofan.
+ liftoff_.~LiftoffData();
+ mode_ = WasmCompilationUnit::CompilationMode::kTurbofan;
+ new (&tf_) TurbofanData();
+ // fall-through
+ case WasmCompilationUnit::CompilationMode::kTurbofan:
+ ExecuteTurbofanCompilation();
+ break;
+ }
+}
+
+void WasmCompilationUnit::ExecuteTurbofanCompilation() {
double decode_ms = 0;
size_t node_count = 0;
// Scope for the {graph_zone}.
{
Zone graph_zone(isolate_->allocator(), ZONE_NAME);
- jsgraph_ = new (&graph_zone) JSGraph(
+ tf_.jsgraph_ = new (&graph_zone) JSGraph(
isolate_, new (&graph_zone) Graph(&graph_zone),
new (&graph_zone) CommonOperatorBuilder(&graph_zone), nullptr, nullptr,
new (&graph_zone) MachineOperatorBuilder(
@@ -4695,39 +4970,37 @@ void WasmCompilationUnit::ExecuteCompilation() {
SourcePositionTable* source_positions =
BuildGraphForWasmFunction(&decode_ms);
- if (graph_construction_result_.failed()) {
+ if (tf_.graph_construction_result_.failed()) {
ok_ = false;
return;
}
base::ElapsedTimer pipeline_timer;
if (FLAG_trace_wasm_decode_time) {
- node_count = jsgraph_->graph()->NodeCount();
+ node_count = tf_.jsgraph_->graph()->NodeCount();
pipeline_timer.Start();
}
- compilation_zone_.reset(new Zone(isolate_->allocator(), ZONE_NAME));
+ tf_.compilation_zone_.reset(new Zone(isolate_->allocator(), ZONE_NAME));
// Run the compiler pipeline to generate machine code.
CallDescriptor* descriptor =
- GetWasmCallDescriptor(compilation_zone_.get(), func_body_.sig);
- if (jsgraph_->machine()->Is32()) {
+ GetWasmCallDescriptor(tf_.compilation_zone_.get(), func_body_.sig);
+ if (tf_.jsgraph_->machine()->Is32()) {
descriptor =
- GetI32WasmCallDescriptor(compilation_zone_.get(), descriptor);
+ GetI32WasmCallDescriptor(tf_.compilation_zone_.get(), descriptor);
}
- info_.reset(new CompilationInfo(
- GetDebugName(compilation_zone_.get(), func_name_, func_index_),
- isolate_, compilation_zone_.get(), Code::WASM_FUNCTION));
- ZoneVector<trap_handler::ProtectedInstructionData> protected_instructions(
- compilation_zone_.get());
-
- job_.reset(Pipeline::NewWasmCompilationJob(
- info_.get(), jsgraph_, descriptor, source_positions,
- &protected_instructions, env_->module->origin()));
- ok_ = job_->ExecuteJob() == CompilationJob::SUCCEEDED;
+ tf_.info_.reset(new CompilationInfo(
+ GetDebugName(tf_.compilation_zone_.get(), func_name_, func_index_),
+ tf_.compilation_zone_.get(), Code::WASM_FUNCTION));
+
+ tf_.job_.reset(Pipeline::NewWasmCompilationJob(
+ tf_.info_.get(), isolate_, tf_.jsgraph_, descriptor, source_positions,
+ protected_instructions_.get(), env_->module->origin()));
+ ok_ = tf_.job_->ExecuteJob() == CompilationJob::SUCCEEDED;
// TODO(bradnelson): Improve histogram handling of size_t.
counters()->wasm_compile_function_peak_memory_bytes()->AddSample(
- static_cast<int>(jsgraph_->graph()->zone()->allocation_size()));
+ static_cast<int>(tf_.jsgraph_->graph()->zone()->allocation_size()));
if (FLAG_trace_wasm_decode_time) {
double pipeline_ms = pipeline_timer.Elapsed().InMillisecondsF();
@@ -4738,19 +5011,40 @@ void WasmCompilationUnit::ExecuteCompilation() {
node_count, pipeline_ms);
}
// The graph zone is about to get out of scope. Avoid invalid references.
- jsgraph_ = nullptr;
+ tf_.jsgraph_ = nullptr;
}
// Record the memory cost this unit places on the system until
// it is finalized.
- size_t cost = job_->AllocatedMemory();
- set_memory_cost(cost);
+ memory_cost_ = tf_.job_->AllocatedMemory();
+}
+
+// WasmCompilationUnit::ExecuteLiftoffCompilation() is defined in
+// liftoff-compiler.cc.
+
+WasmCodeWrapper WasmCompilationUnit::FinishCompilation(
+ wasm::ErrorThrower* thrower) {
+ WasmCodeWrapper ret;
+ switch (mode_) {
+ case WasmCompilationUnit::CompilationMode::kLiftoff:
+ ret = FinishLiftoffCompilation(thrower);
+ break;
+ case WasmCompilationUnit::CompilationMode::kTurbofan:
+ ret = FinishTurbofanCompilation(thrower);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ if (!ret.IsCodeObject() && ret.is_null()) {
+ thrower->RuntimeError("Error finalizing code.");
+ }
+ return ret;
}
-MaybeHandle<Code> WasmCompilationUnit::FinishCompilation(
+WasmCodeWrapper WasmCompilationUnit::FinishTurbofanCompilation(
wasm::ErrorThrower* thrower) {
if (!ok_) {
- if (graph_construction_result_.failed()) {
+ if (tf_.graph_construction_result_.failed()) {
// Add the function as another context for the exception.
EmbeddedVector<char, 128> message;
if (func_name_.start() == nullptr) {
@@ -4760,7 +5054,7 @@ MaybeHandle<Code> WasmCompilationUnit::FinishCompilation(
SNPrintF(message, "Compiling wasm function #%d:%.*s failed",
func_index_, trunc_name.length(), trunc_name.start());
}
- thrower->CompileFailed(message.start(), graph_construction_result_);
+ thrower->CompileFailed(message.start(), tf_.graph_construction_result_);
}
return {};
@@ -4769,41 +5063,137 @@ MaybeHandle<Code> WasmCompilationUnit::FinishCompilation(
if (FLAG_trace_wasm_decode_time) {
codegen_timer.Start();
}
- if (job_->FinalizeJob() != CompilationJob::SUCCEEDED) {
- return Handle<Code>::null();
- }
- Handle<Code> code = info_->code();
- DCHECK(!code.is_null());
- if (must_record_function_compilation(isolate_)) {
- wasm::TruncatedUserString<> trunc_name(func_name_);
- RecordFunctionCompilation(CodeEventListener::FUNCTION_TAG, isolate_, code,
- "wasm_function#%d:%.*s", func_index_,
- trunc_name.length(), trunc_name.start());
+ if (tf_.job_->FinalizeJob(isolate_) != CompilationJob::SUCCEEDED) {
+ return {};
}
+ if (!FLAG_wasm_jit_to_native) {
+ Handle<Code> code = tf_.info_->code();
+ DCHECK(!code.is_null());
- if (FLAG_trace_wasm_decode_time) {
- double codegen_ms = codegen_timer.Elapsed().InMillisecondsF();
- PrintF("wasm-code-generation ok: %u bytes, %0.3f ms code generation\n",
- static_cast<unsigned>(func_body_.end - func_body_.start),
- codegen_ms);
- }
+ if (FLAG_trace_wasm_decode_time) {
+ double codegen_ms = codegen_timer.Elapsed().InMillisecondsF();
+ PrintF("wasm-code-generation ok: %u bytes, %0.3f ms code generation\n",
+ static_cast<unsigned>(func_body_.end - func_body_.start),
+ codegen_ms);
+ }
- return code;
+ PackProtectedInstructions(code);
+ return WasmCodeWrapper(code);
+ } else {
+ // TODO(mtrofin): when we crystalize a design in lieu of WasmCodeDesc, that
+ // works for both wasm and non-wasm, we can simplify AddCode to just take
+ // that as a parameter.
+ const CodeDesc& desc =
+ tf_.job_->compilation_info()->wasm_code_desc()->code_desc;
+ wasm::WasmCode* code = native_module_->AddCode(
+ desc, tf_.job_->compilation_info()->wasm_code_desc()->frame_slot_count,
+ func_index_,
+ tf_.job_->compilation_info()->wasm_code_desc()->safepoint_table_offset,
+ protected_instructions_);
+ if (!code) {
+ return WasmCodeWrapper(code);
+ }
+ // TODO(mtrofin): add CodeEventListener call - see the non-native case.
+ if (FLAG_trace_wasm_decode_time) {
+ double codegen_ms = codegen_timer.Elapsed().InMillisecondsF();
+ PrintF("wasm-code-generation ok: %u bytes, %0.3f ms code generation\n",
+ static_cast<unsigned>(func_body_.end - func_body_.start),
+ codegen_ms);
+ }
+
+ Handle<ByteArray> source_positions =
+ tf_.job_->compilation_info()->wasm_code_desc()->source_positions_table;
+ MaybeHandle<HandlerTable> handler_table =
+ tf_.job_->compilation_info()->wasm_code_desc()->handler_table;
+
+ int function_index_as_int = static_cast<int>(func_index_);
+ native_module_->compiled_module()->source_positions()->set(
+ function_index_as_int, *source_positions);
+ if (!handler_table.is_null()) {
+ native_module_->compiled_module()->handler_table()->set(
+ function_index_as_int, *handler_table.ToHandleChecked());
+ }
+ // TODO(mtrofin): this should probably move up in the common caller,
+ // once liftoff has source positions. Until then, we'd need to handle
+ // undefined values, which is complicating the code.
+ LOG_CODE_EVENT(isolate_,
+ CodeLinePosInfoRecordEvent(code->instructions().start(),
+ *source_positions));
+ return WasmCodeWrapper(code);
+ }
+}
+
+// TODO(mtrofin): remove when FLAG_wasm_jit_to_native is not needed
+void WasmCompilationUnit::PackProtectedInstructions(Handle<Code> code) const {
+ if (protected_instructions_->empty()) return;
+ DCHECK_LT(protected_instructions_->size(), std::numeric_limits<int>::max());
+ const int num_instructions =
+ static_cast<int>(protected_instructions_->size());
+ Handle<FixedArray> fn_protected = isolate_->factory()->NewFixedArray(
+ num_instructions * Code::kTrapDataSize, TENURED);
+ for (int i = 0; i < num_instructions; ++i) {
+ const trap_handler::ProtectedInstructionData& instruction =
+ protected_instructions_->at(i);
+ fn_protected->set(Code::kTrapDataSize * i + Code::kTrapCodeOffset,
+ Smi::FromInt(instruction.instr_offset));
+ fn_protected->set(Code::kTrapDataSize * i + Code::kTrapLandingOffset,
+ Smi::FromInt(instruction.landing_offset));
+ }
+ code->set_protected_instructions(*fn_protected);
+}
+
+WasmCodeWrapper WasmCompilationUnit::FinishLiftoffCompilation(
+ wasm::ErrorThrower* thrower) {
+ CodeDesc desc;
+ liftoff_.asm_.GetCode(isolate_, &desc);
+ WasmCodeWrapper ret;
+ if (!FLAG_wasm_jit_to_native) {
+ Handle<Code> code;
+ code = isolate_->factory()->NewCode(desc, Code::WASM_FUNCTION, code);
+#ifdef ENABLE_DISASSEMBLER
+ if (FLAG_print_code || FLAG_print_wasm_code) {
+ // TODO(wasm): Use proper log files, here and elsewhere.
+ OFStream os(stdout);
+ os << "--- Wasm liftoff code ---\n";
+ EmbeddedVector<char, 32> func_name;
+ func_name.Truncate(SNPrintF(func_name, "wasm#%d-liftoff", func_index_));
+ code->Disassemble(func_name.start(), os);
+ os << "--- End code ---\n";
+ }
+#endif
+ if (isolate_->logger()->is_logging_code_events() ||
+ isolate_->is_profiling()) {
+ RecordFunctionCompilation(CodeEventListener::FUNCTION_TAG, isolate_, code,
+ "wasm#%d-liftoff", func_index_);
+ }
+
+ PackProtectedInstructions(code);
+ return WasmCodeWrapper(code);
+ } else {
+ // TODO(mtrofin): figure a way to raise events; also, disassembly.
+ // Consider lifting them both to FinishCompilation.
+ return WasmCodeWrapper(native_module_->AddCode(
+ desc, liftoff_.asm_.GetTotalFrameSlotCount(), func_index_,
+ liftoff_.asm_.GetSafepointTableOffset(), protected_instructions_,
+ true));
+ }
}
// static
-MaybeHandle<Code> WasmCompilationUnit::CompileWasmFunction(
- wasm::ErrorThrower* thrower, Isolate* isolate,
- const wasm::ModuleWireBytes& wire_bytes, ModuleEnv* env,
- const wasm::WasmFunction* function) {
+WasmCodeWrapper WasmCompilationUnit::CompileWasmFunction(
+ wasm::NativeModule* native_module, wasm::ErrorThrower* thrower,
+ Isolate* isolate, const wasm::ModuleWireBytes& wire_bytes, ModuleEnv* env,
+ const wasm::WasmFunction* function, CompilationMode mode) {
wasm::FunctionBody function_body{
function->sig, function->code.offset(),
wire_bytes.start() + function->code.offset(),
wire_bytes.start() + function->code.end_offset()};
- WasmCompilationUnit unit(
- isolate, env, function_body, wire_bytes.GetNameOrNull(function),
- function->func_index, CEntryStub(isolate, 1).GetCode());
+
+ WasmCompilationUnit unit(isolate, env, native_module, function_body,
+ wire_bytes.GetNameOrNull(function),
+ function->func_index,
+ CEntryStub(isolate, 1).GetCode(), mode);
unit.ExecuteCompilation();
return unit.FinishCompilation(thrower);
}