diff options
Diffstat (limited to 'deps/v8/src/compiler/wasm-compiler.cc')
-rw-r--r-- | deps/v8/src/compiler/wasm-compiler.cc | 1187 |
1 files changed, 684 insertions, 503 deletions
diff --git a/deps/v8/src/compiler/wasm-compiler.cc b/deps/v8/src/compiler/wasm-compiler.cc index 91dde088f6..d6b7113b27 100644 --- a/deps/v8/src/compiler/wasm-compiler.cc +++ b/deps/v8/src/compiler/wasm-compiler.cc @@ -16,6 +16,7 @@ #include "src/codegen/code-factory.h" #include "src/codegen/compiler.h" #include "src/codegen/interface-descriptors.h" +#include "src/codegen/machine-type.h" #include "src/codegen/optimized-compilation-info.h" #include "src/compiler/backend/code-generator.h" #include "src/compiler/backend/instruction-selector.h" @@ -297,9 +298,9 @@ Node* WasmGraphBuilder::RefFunc(uint32_t function_index) { Node* call_target = mcgraph()->RelocatableIntPtrConstant( wasm::WasmCode::kWasmRefFunc, RelocInfo::WASM_STUB_CALL); - return SetEffectControl( - graph()->NewNode(mcgraph()->common()->Call(call_descriptor), call_target, - Uint32Constant(function_index), effect(), control())); + return SetEffectControl(graph()->NewNode( + mcgraph()->common()->Call(call_descriptor), call_target, + mcgraph()->Uint32Constant(function_index), effect(), control())); } Node* WasmGraphBuilder::RefAsNonNull(Node* arg, @@ -321,10 +322,6 @@ Node* WasmGraphBuilder::BuildLoadIsolateRoot() { return LOAD_INSTANCE_FIELD(IsolateRoot, MachineType::Pointer()); } -Node* WasmGraphBuilder::Uint32Constant(uint32_t value) { - return mcgraph()->Uint32Constant(value); -} - Node* WasmGraphBuilder::Int32Constant(int32_t value) { return mcgraph()->Int32Constant(value); } @@ -333,10 +330,6 @@ Node* WasmGraphBuilder::Int64Constant(int64_t value) { return mcgraph()->Int64Constant(value); } -Node* WasmGraphBuilder::IntPtrConstant(intptr_t value) { - return mcgraph()->IntPtrConstant(value); -} - void WasmGraphBuilder::StackCheck(wasm::WasmCodePosition position) { DCHECK_NOT_NULL(env_); // Wrappers don't get stack checks. if (!FLAG_wasm_stack_checks || !env_->runtime_exception_support) { @@ -1062,7 +1055,7 @@ Node* WasmGraphBuilder::TrapIfEq32(wasm::TrapReason reason, Node* node, int32_t val, wasm::WasmCodePosition position) { Int32Matcher m(node); - if (m.HasValue() && !m.Is(val)) return graph()->start(); + if (m.HasResolvedValue() && !m.Is(val)) return graph()->start(); if (val == 0) { return TrapIfFalse(reason, node, position); } else { @@ -1084,7 +1077,7 @@ Node* WasmGraphBuilder::TrapIfEq64(wasm::TrapReason reason, Node* node, int64_t val, wasm::WasmCodePosition position) { Int64Matcher m(node); - if (m.HasValue() && !m.Is(val)) return graph()->start(); + if (m.HasResolvedValue() && !m.Is(val)) return graph()->start(); return TrapIfTrue(reason, graph()->NewNode(mcgraph()->machine()->Word64Equal(), node, mcgraph()->Int64Constant(val)), @@ -1144,9 +1137,10 @@ Node* WasmGraphBuilder::MaskShiftCount32(Node* node) { if (!mcgraph()->machine()->Word32ShiftIsSafe()) { // Shifts by constants are so common we pattern-match them here. Int32Matcher match(node); - if (match.HasValue()) { - int32_t masked = (match.Value() & kMask32); - if (match.Value() != masked) node = mcgraph()->Int32Constant(masked); + if (match.HasResolvedValue()) { + int32_t masked = (match.ResolvedValue() & kMask32); + if (match.ResolvedValue() != masked) + node = mcgraph()->Int32Constant(masked); } else { node = graph()->NewNode(mcgraph()->machine()->Word32And(), node, mcgraph()->Int32Constant(kMask32)); @@ -1160,9 +1154,10 @@ Node* WasmGraphBuilder::MaskShiftCount64(Node* node) { if (!mcgraph()->machine()->Word32ShiftIsSafe()) { // Shifts by constants are so common we pattern-match them here. Int64Matcher match(node); - if (match.HasValue()) { - int64_t masked = (match.Value() & kMask64); - if (match.Value() != masked) node = mcgraph()->Int64Constant(masked); + if (match.HasResolvedValue()) { + int64_t masked = (match.ResolvedValue() & kMask64); + if (match.ResolvedValue() != masked) + node = mcgraph()->Int64Constant(masked); } else { node = graph()->NewNode(mcgraph()->machine()->Word64And(), node, mcgraph()->Int64Constant(kMask64)); @@ -2084,7 +2079,7 @@ Node* WasmGraphBuilder::Throw(uint32_t exception_index, uint32_t encoded_size = WasmExceptionPackage::GetEncodedSize(exception); Node* create_parameters[] = { LoadExceptionTagFromTable(exception_index), - BuildChangeUint31ToSmi(Uint32Constant(encoded_size))}; + BuildChangeUint31ToSmi(mcgraph()->Uint32Constant(encoded_size))}; Node* except_obj = BuildCallToRuntime(Runtime::kWasmThrowCreate, create_parameters, arraysize(create_parameters)); @@ -2357,10 +2352,10 @@ Node* WasmGraphBuilder::BuildI32AsmjsDivS(Node* left, Node* right) { MachineOperatorBuilder* m = mcgraph()->machine(); Int32Matcher mr(right); - if (mr.HasValue()) { - if (mr.Value() == 0) { + if (mr.HasResolvedValue()) { + if (mr.ResolvedValue() == 0) { return mcgraph()->Int32Constant(0); - } else if (mr.Value() == -1) { + } else if (mr.ResolvedValue() == -1) { // The result is the negation of the left input. return graph()->NewNode(m->Int32Sub(), mcgraph()->Int32Constant(0), left); } @@ -2400,8 +2395,8 @@ Node* WasmGraphBuilder::BuildI32AsmjsRemS(Node* left, Node* right) { Node* const zero = mcgraph()->Int32Constant(0); Int32Matcher mr(right); - if (mr.HasValue()) { - if (mr.Value() == 0 || mr.Value() == -1) { + if (mr.HasResolvedValue()) { + if (mr.ResolvedValue() == 0 || mr.ResolvedValue() == -1) { return zero; } return graph()->NewNode(m->Int32Mod(), left, right, control()); @@ -2672,7 +2667,7 @@ Node* WasmGraphBuilder::BuildWasmCall(const wasm::FunctionSig* sig, wasm::WasmCodePosition position, Node* instance_node, UseRetpoline use_retpoline) { - auto call_descriptor = + CallDescriptor* call_descriptor = GetWasmCallDescriptor(mcgraph()->zone(), sig, use_retpoline); const Operator* op = mcgraph()->common()->Call(call_descriptor); Node* call = BuildCallNode(sig, args, position, instance_node, op); @@ -2699,7 +2694,7 @@ Node* WasmGraphBuilder::BuildWasmReturnCall(const wasm::FunctionSig* sig, wasm::WasmCodePosition position, Node* instance_node, UseRetpoline use_retpoline) { - auto call_descriptor = + CallDescriptor* call_descriptor = GetWasmCallDescriptor(mcgraph()->zone(), sig, use_retpoline); const Operator* op = mcgraph()->common()->TailCall(call_descriptor); Node* call = BuildCallNode(sig, args, position, instance_node, op); @@ -2878,7 +2873,7 @@ Node* WasmGraphBuilder::BuildIndirectCall(uint32_t table_index, // Bounds check against the table size. Node* in_bounds = graph()->NewNode(machine->Uint32LessThan(), key, ift_size); - TrapIfFalse(wasm::kTrapFuncInvalid, in_bounds, position); + TrapIfFalse(wasm::kTrapTableOutOfBounds, in_bounds, position); // Mask the key to prevent SSCA. if (untrusted_code_mitigations_) { @@ -2896,20 +2891,27 @@ Node* WasmGraphBuilder::BuildIndirectCall(uint32_t table_index, Node* int32_scaled_key = Uint32ToUintptr( graph()->NewNode(machine->Word32Shl(), key, Int32Constant(2))); + Node* loaded_sig = SetEffect( + graph()->NewNode(machine->Load(MachineType::Int32()), ift_sig_ids, + int32_scaled_key, effect(), control())); // Check that the dynamic type of the function is a subtype of its static // (table) type. Currently, the only subtyping between function types is // $t <: funcref for all $t: function_type. // TODO(7748): Expand this with function subtyping. - if (env_->module->tables[table_index].type == wasm::kWasmFuncRef) { - int32_t expected_sig_id = env_->module->signature_ids[sig_index]; - - Node* loaded_sig = SetEffect( - graph()->NewNode(machine->Load(MachineType::Int32()), ift_sig_ids, - int32_scaled_key, effect(), control())); - Node* sig_match = graph()->NewNode(machine->WordEqual(), loaded_sig, + const bool needs_typechecking = + env_->module->tables[table_index].type == wasm::kWasmFuncRef; + if (needs_typechecking) { + int32_t expected_sig_id = env_->module->canonicalized_type_ids[sig_index]; + Node* sig_match = graph()->NewNode(machine->Word32Equal(), loaded_sig, Int32Constant(expected_sig_id)); - TrapIfFalse(wasm::kTrapFuncSigMismatch, sig_match, position); + } else { + // We still have to check that the entry is initialized. + // TODO(9495): Skip this check for non-nullable tables when they are + // allowed. + Node* function_is_null = + graph()->NewNode(machine->Word32Equal(), loaded_sig, Int32Constant(-1)); + TrapIfTrue(wasm::kTrapNullDereference, function_is_null, position); } Node* tagged_scaled_key; @@ -2953,10 +2955,9 @@ Node* WasmGraphBuilder::BuildIndirectCall(uint32_t table_index, } } -Node* WasmGraphBuilder::BuildLoadFunctionDataFromExportedFunction( - Node* closure) { +Node* WasmGraphBuilder::BuildLoadFunctionDataFromJSFunction(Node* js_function) { Node* shared = gasm_->Load( - MachineType::AnyTagged(), closure, + MachineType::AnyTagged(), js_function, wasm::ObjectAccess::SharedFunctionInfoOffsetInTaggedJSFunction()); return gasm_->Load(MachineType::AnyTagged(), shared, SharedFunctionInfo::kFunctionDataOffset - kHeapObjectTag); @@ -3001,7 +3002,7 @@ Node* WasmGraphBuilder::BuildCallRef(uint32_t sig_index, Vector<Node*> args, const wasm::FunctionSig* sig = env_->module->signature(sig_index); - Node* function_data = BuildLoadFunctionDataFromExportedFunction(args[0]); + Node* function_data = BuildLoadFunctionDataFromJSFunction(args[0]); Node* is_js_function = HasInstanceType(gasm_.get(), function_data, WASM_JS_FUNCTION_DATA_TYPE); @@ -3078,13 +3079,30 @@ Node* WasmGraphBuilder::BuildCallRef(uint32_t sig_index, Vector<Node*> args, } { - // Call to a WasmJSFunction. - // The call target is the wasm-to-js wrapper code. + // Call to a WasmJSFunction. The call target is + // function_data->wasm_to_js_wrapper_code()->instruction_start(). + // The instance_node is the pair + // (current WasmInstanceObject, function_data->callable()). gasm_->Bind(&js_label); - // TODO(9495): Implement when the interaction with the type reflection - // proposal is clear. - TrapIfTrue(wasm::kTrapWasmJSFunction, gasm_->Int32Constant(1), position); - gasm_->Goto(&end_label, args[0], RefNull() /* Dummy value */); + + Node* wrapper_code = + gasm_->Load(MachineType::TaggedPointer(), function_data, + wasm::ObjectAccess::ToTagged( + WasmJSFunctionData::kWasmToJsWrapperCodeOffset)); + Node* call_target = gasm_->IntAdd( + wrapper_code, + gasm_->IntPtrConstant(wasm::ObjectAccess::ToTagged(Code::kHeaderSize))); + + Node* callable = gasm_->Load( + MachineType::TaggedPointer(), function_data, + wasm::ObjectAccess::ToTagged(WasmJSFunctionData::kCallableOffset)); + // TODO(manoskouk): Find an elegant way to avoid allocating this pair for + // every call. + Node* function_instance_node = CALL_BUILTIN( + WasmAllocatePair, instance_node_.get(), callable, + LOAD_INSTANCE_FIELD(NativeContext, MachineType::TaggedPointer())); + + gasm_->Goto(&end_label, call_target, function_instance_node); } gasm_->Bind(&end_label); @@ -3158,9 +3176,9 @@ Node* WasmGraphBuilder::BuildI32Rol(Node* left, Node* right) { // Implement Rol by Ror since TurboFan does not have Rol opcode. // TODO(weiliang): support Word32Rol opcode in TurboFan. Int32Matcher m(right); - if (m.HasValue()) { + if (m.HasResolvedValue()) { return Binop(wasm::kExprI32Ror, left, - mcgraph()->Int32Constant(32 - (m.Value() & 0x1F))); + mcgraph()->Int32Constant(32 - (m.ResolvedValue() & 0x1F))); } else { return Binop(wasm::kExprI32Ror, left, Binop(wasm::kExprI32Sub, mcgraph()->Int32Constant(32), right)); @@ -3172,8 +3190,8 @@ Node* WasmGraphBuilder::BuildI64Rol(Node* left, Node* right) { // TODO(weiliang): support Word64Rol opcode in TurboFan. Int64Matcher m(right); Node* inv_right = - m.HasValue() - ? mcgraph()->Int64Constant(64 - (m.Value() & 0x3F)) + m.HasResolvedValue() + ? mcgraph()->Int64Constant(64 - (m.ResolvedValue() & 0x3F)) : Binop(wasm::kExprI64Sub, mcgraph()->Int64Constant(64), right); return Binop(wasm::kExprI64Ror, left, inv_right); } @@ -3183,59 +3201,44 @@ Node* WasmGraphBuilder::Invert(Node* node) { } Node* WasmGraphBuilder::BuildTruncateIntPtrToInt32(Node* value) { - if (mcgraph()->machine()->Is64()) { - value = - graph()->NewNode(mcgraph()->machine()->TruncateInt64ToInt32(), value); - } - return value; + return mcgraph()->machine()->Is64() ? gasm_->TruncateInt64ToInt32(value) + : value; } Node* WasmGraphBuilder::BuildChangeInt32ToIntPtr(Node* value) { - if (mcgraph()->machine()->Is64()) { - value = graph()->NewNode(mcgraph()->machine()->ChangeInt32ToInt64(), value); - } - return value; + return mcgraph()->machine()->Is64() ? gasm_->ChangeInt32ToInt64(value) + : value; } Node* WasmGraphBuilder::BuildChangeInt32ToSmi(Node* value) { // With pointer compression, only the lower 32 bits are used. - if (COMPRESS_POINTERS_BOOL) { - return graph()->NewNode(mcgraph()->machine()->Word32Shl(), value, - BuildSmiShiftBitsConstant32()); - } - value = BuildChangeInt32ToIntPtr(value); - return graph()->NewNode(mcgraph()->machine()->WordShl(), value, - BuildSmiShiftBitsConstant()); + return COMPRESS_POINTERS_BOOL + ? gasm_->Word32Shl(value, BuildSmiShiftBitsConstant32()) + : gasm_->WordShl(BuildChangeInt32ToIntPtr(value), + BuildSmiShiftBitsConstant()); } Node* WasmGraphBuilder::BuildChangeUint31ToSmi(Node* value) { - if (COMPRESS_POINTERS_BOOL) { - return graph()->NewNode(mcgraph()->machine()->Word32Shl(), value, - BuildSmiShiftBitsConstant32()); - } - return graph()->NewNode(mcgraph()->machine()->WordShl(), - Uint32ToUintptr(value), BuildSmiShiftBitsConstant()); + return COMPRESS_POINTERS_BOOL + ? gasm_->Word32Shl(value, BuildSmiShiftBitsConstant32()) + : graph()->NewNode(mcgraph()->machine()->WordShl(), + Uint32ToUintptr(value), + BuildSmiShiftBitsConstant()); } Node* WasmGraphBuilder::BuildSmiShiftBitsConstant() { - return mcgraph()->IntPtrConstant(kSmiShiftSize + kSmiTagSize); + return gasm_->IntPtrConstant(kSmiShiftSize + kSmiTagSize); } Node* WasmGraphBuilder::BuildSmiShiftBitsConstant32() { - return mcgraph()->Int32Constant(kSmiShiftSize + kSmiTagSize); + return gasm_->Int32Constant(kSmiShiftSize + kSmiTagSize); } Node* WasmGraphBuilder::BuildChangeSmiToInt32(Node* value) { - if (COMPRESS_POINTERS_BOOL) { - value = - graph()->NewNode(mcgraph()->machine()->TruncateInt64ToInt32(), value); - value = graph()->NewNode(mcgraph()->machine()->Word32Sar(), value, - BuildSmiShiftBitsConstant32()); - } else { - value = BuildChangeSmiToIntPtr(value); - value = BuildTruncateIntPtrToInt32(value); - } - return value; + return COMPRESS_POINTERS_BOOL + ? gasm_->Word32Sar(gasm_->TruncateInt64ToInt32(value), + BuildSmiShiftBitsConstant32()) + : BuildTruncateIntPtrToInt32(BuildChangeSmiToIntPtr(value)); } Node* WasmGraphBuilder::BuildChangeSmiToIntPtr(Node* value) { @@ -3250,7 +3253,7 @@ Node* WasmGraphBuilder::BuildChangeSmiToIntPtr(Node* value) { Node* WasmGraphBuilder::BuildConvertUint32ToSmiWithSaturation(Node* value, uint32_t maxval) { DCHECK(Smi::IsValid(maxval)); - Node* max = Uint32Constant(maxval); + Node* max = mcgraph()->Uint32Constant(maxval); Node* check = graph()->NewNode(mcgraph()->machine()->Uint32LessThanOrEqual(), value, max); Node* valsmi = BuildChangeUint31ToSmi(value); @@ -3469,13 +3472,12 @@ void WasmGraphBuilder::GetBaseAndOffsetForImportedMutableExternRefGlobal( wasm::ObjectAccess::ElementOffsetInTaggedFixedArray(0))); } -Node* WasmGraphBuilder::MemBuffer(uint32_t offset) { +Node* WasmGraphBuilder::MemBuffer(uintptr_t offset) { DCHECK_NOT_NULL(instance_cache_); Node* mem_start = instance_cache_->mem_start; DCHECK_NOT_NULL(mem_start); if (offset == 0) return mem_start; - return graph()->NewNode(mcgraph()->machine()->IntAdd(), mem_start, - mcgraph()->IntPtrConstant(offset)); + return gasm_->IntAdd(mem_start, gasm_->UintPtrConstant(offset)); } Node* WasmGraphBuilder::CurrentMemoryPages() { @@ -3612,7 +3614,7 @@ Node* WasmGraphBuilder::TableGet(uint32_t table_index, Node* index, return SetEffectControl(graph()->NewNode( mcgraph()->common()->Call(call_descriptor), call_target, - IntPtrConstant(table_index), index, effect(), control())); + mcgraph()->IntPtrConstant(table_index), index, effect(), control())); } Node* WasmGraphBuilder::TableSet(uint32_t table_index, Node* index, Node* val, @@ -3624,13 +3626,12 @@ Node* WasmGraphBuilder::TableSet(uint32_t table_index, Node* index, Node* val, Node* call_target = mcgraph()->RelocatableIntPtrConstant( wasm::WasmCode::kWasmTableSet, RelocInfo::WASM_STUB_CALL); - return SetEffectControl(graph()->NewNode( - mcgraph()->common()->Call(call_descriptor), call_target, - IntPtrConstant(table_index), index, val, effect(), control())); + return gasm_->Call(call_descriptor, call_target, + gasm_->IntPtrConstant(table_index), index, val); } Node* WasmGraphBuilder::CheckBoundsAndAlignment( - uint8_t access_size, Node* index, uint32_t offset, + int8_t access_size, Node* index, uint64_t offset, wasm::WasmCodePosition position) { // Atomic operations need bounds checks until the backend can emit protected // loads. @@ -3639,11 +3640,13 @@ Node* WasmGraphBuilder::CheckBoundsAndAlignment( const uintptr_t align_mask = access_size - 1; + // {offset} is validated to be within uintptr_t range in {BoundsCheckMem}. + uintptr_t capped_offset = static_cast<uintptr_t>(offset); // Don't emit an alignment check if the index is a constant. // TODO(wasm): a constant match is also done above in {BoundsCheckMem}. UintPtrMatcher match(index); - if (match.HasValue()) { - uintptr_t effective_offset = match.Value() + offset; + if (match.HasResolvedValue()) { + uintptr_t effective_offset = match.ResolvedValue() + capped_offset; if ((effective_offset & align_mask) != 0) { // statically known to be unaligned; trap. TrapIfEq32(wasm::kTrapUnalignedAccess, Int32Constant(0), 0, position); @@ -3654,15 +3657,12 @@ Node* WasmGraphBuilder::CheckBoundsAndAlignment( // Unlike regular memory accesses, atomic memory accesses should trap if // the effective offset is misaligned. // TODO(wasm): this addition is redundant with one inserted by {MemBuffer}. - Node* effective_offset = graph()->NewNode(mcgraph()->machine()->IntAdd(), - MemBuffer(offset), index); + Node* effective_offset = gasm_->IntAdd(MemBuffer(capped_offset), index); - Node* cond = graph()->NewNode(mcgraph()->machine()->WordAnd(), - effective_offset, IntPtrConstant(align_mask)); + Node* cond = + gasm_->WordAnd(effective_offset, gasm_->IntPtrConstant(align_mask)); TrapIfFalse(wasm::kTrapUnalignedAccess, - graph()->NewNode(mcgraph()->machine()->Word32Equal(), cond, - mcgraph()->Int32Constant(0)), - position); + gasm_->Word32Equal(cond, gasm_->Int32Constant(0)), position); return index; } @@ -3688,7 +3688,7 @@ Node* WasmGraphBuilder::BoundsCheckMem(uint8_t access_size, Node* index, env_->max_memory_size)) { // The access will be out of bounds, even for the largest memory. TrapIfEq32(wasm::kTrapMemOutOfBounds, Int32Constant(0), 0, position); - return mcgraph()->UintPtrConstant(0); + return gasm_->UintPtrConstant(0); } uintptr_t end_offset = offset + access_size - 1u; Node* end_offset_node = mcgraph_->UintPtrConstant(end_offset); @@ -3702,19 +3702,18 @@ Node* WasmGraphBuilder::BoundsCheckMem(uint8_t access_size, Node* index, // - computing {effective_size} as {mem_size - end_offset} and // - checking that {index < effective_size}. - auto m = mcgraph()->machine(); Node* mem_size = instance_cache_->mem_size; if (end_offset >= env_->min_memory_size) { // The end offset is larger than the smallest memory. // Dynamically check the end offset against the dynamic memory size. - Node* cond = graph()->NewNode(m->UintLessThan(), end_offset_node, mem_size); + Node* cond = gasm_->UintLessThan(end_offset_node, mem_size); TrapIfFalse(wasm::kTrapMemOutOfBounds, cond, position); } else { // The end offset is smaller than the smallest memory, so only one check is // required. Check to see if the index is also a constant. UintPtrMatcher match(index); - if (match.HasValue()) { - uintptr_t index_val = match.Value(); + if (match.HasResolvedValue()) { + uintptr_t index_val = match.ResolvedValue(); if (index_val < env_->min_memory_size - end_offset) { // The input index is a constant and everything is statically within // bounds of the smallest possible memory. @@ -3724,18 +3723,17 @@ Node* WasmGraphBuilder::BoundsCheckMem(uint8_t access_size, Node* index, } // This produces a positive number, since {end_offset < min_size <= mem_size}. - Node* effective_size = - graph()->NewNode(m->IntSub(), mem_size, end_offset_node); + Node* effective_size = gasm_->IntSub(mem_size, end_offset_node); // Introduce the actual bounds check. - Node* cond = graph()->NewNode(m->UintLessThan(), index, effective_size); + Node* cond = gasm_->UintLessThan(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 = instance_cache_->mem_mask; DCHECK_NOT_NULL(mem_mask); - index = graph()->NewNode(m->WordAnd(), index, mem_mask); + index = gasm_->WordAnd(index, mem_mask); } return index; } @@ -3828,20 +3826,20 @@ Node* WasmGraphBuilder::TraceFunctionExit(Vector<Node*> vals, Node* WasmGraphBuilder::TraceMemoryOperation(bool is_store, MachineRepresentation rep, - Node* index, uint32_t offset, + Node* index, uintptr_t offset, wasm::WasmCodePosition position) { int kAlign = 4; // Ensure that the LSB is 0, such that this looks like a Smi. TNode<RawPtrT> info = gasm_->StackSlot(sizeof(wasm::MemoryTracingInfo), kAlign); - Node* address = gasm_->Int32Add(Int32Constant(offset), index); - auto store = [&](int offset, MachineRepresentation rep, Node* data) { + Node* effective_offset = gasm_->IntAdd(gasm_->UintPtrConstant(offset), index); + auto store = [&](int field_offset, MachineRepresentation rep, Node* data) { gasm_->Store(StoreRepresentation(rep, kNoWriteBarrier), info, - gasm_->Int32Constant(offset), data); + gasm_->Int32Constant(field_offset), data); }; - // Store address, is_store, and mem_rep. - store(offsetof(wasm::MemoryTracingInfo, address), - MachineRepresentation::kWord32, address); + // Store effective_offset, is_store, and mem_rep. + store(offsetof(wasm::MemoryTracingInfo, offset), + MachineType::PointerRepresentation(), effective_offset); store(offsetof(wasm::MemoryTracingInfo, is_store), MachineRepresentation::kWord8, mcgraph()->Int32Constant(is_store ? 1 : 0)); @@ -3862,55 +3860,56 @@ LoadTransformation GetLoadTransformation( switch (transform) { case wasm::LoadTransformationKind::kSplat: { if (memtype == MachineType::Int8()) { - return LoadTransformation::kS8x16LoadSplat; + return LoadTransformation::kS128Load8Splat; } else if (memtype == MachineType::Int16()) { - return LoadTransformation::kS16x8LoadSplat; + return LoadTransformation::kS128Load16Splat; } else if (memtype == MachineType::Int32()) { - return LoadTransformation::kS32x4LoadSplat; + return LoadTransformation::kS128Load32Splat; } else if (memtype == MachineType::Int64()) { - return LoadTransformation::kS64x2LoadSplat; + return LoadTransformation::kS128Load64Splat; } break; } case wasm::LoadTransformationKind::kExtend: { if (memtype == MachineType::Int8()) { - return LoadTransformation::kI16x8Load8x8S; + return LoadTransformation::kS128Load8x8S; } else if (memtype == MachineType::Uint8()) { - return LoadTransformation::kI16x8Load8x8U; + return LoadTransformation::kS128Load8x8U; } else if (memtype == MachineType::Int16()) { - return LoadTransformation::kI32x4Load16x4S; + return LoadTransformation::kS128Load16x4S; } else if (memtype == MachineType::Uint16()) { - return LoadTransformation::kI32x4Load16x4U; + return LoadTransformation::kS128Load16x4U; } else if (memtype == MachineType::Int32()) { - return LoadTransformation::kI64x2Load32x2S; + return LoadTransformation::kS128Load32x2S; } else if (memtype == MachineType::Uint32()) { - return LoadTransformation::kI64x2Load32x2U; + return LoadTransformation::kS128Load32x2U; } break; } case wasm::LoadTransformationKind::kZeroExtend: { if (memtype == MachineType::Int32()) { - return LoadTransformation::kS128LoadMem32Zero; + return LoadTransformation::kS128Load32Zero; } else if (memtype == MachineType::Int64()) { - return LoadTransformation::kS128LoadMem64Zero; + return LoadTransformation::kS128Load64Zero; } + break; } } UNREACHABLE(); } -LoadKind GetLoadKind(MachineGraph* mcgraph, MachineType memtype, - bool use_trap_handler) { +MemoryAccessKind GetMemoryAccessKind(MachineGraph* mcgraph, MachineType memtype, + bool use_trap_handler) { if (memtype.representation() == MachineRepresentation::kWord8 || mcgraph->machine()->UnalignedLoadSupported(memtype.representation())) { if (use_trap_handler) { - return LoadKind::kProtected; + return MemoryAccessKind::kProtected; } - return LoadKind::kNormal; + return MemoryAccessKind::kNormal; } // TODO(eholk): Support unaligned loads with trap handlers. DCHECK(!use_trap_handler); - return LoadKind::kUnaligned; + return MemoryAccessKind::kUnaligned; } } // namespace @@ -3920,7 +3919,7 @@ LoadKind GetLoadKind(MachineGraph* mcgraph, MachineType memtype, #if defined(V8_TARGET_BIG_ENDIAN) || defined(V8_TARGET_ARCH_S390_LE_SIM) Node* WasmGraphBuilder::LoadTransformBigEndian( wasm::ValueType type, MachineType memtype, - wasm::LoadTransformationKind transform, Node* index, uint32_t offset, + wasm::LoadTransformationKind transform, Node* index, uint64_t offset, uint32_t alignment, wasm::WasmCodePosition position) { #define LOAD_EXTEND(num_lanes, bytes_per_load, replace_lane) \ result = graph()->NewNode(mcgraph()->machine()->S128Zero()); \ @@ -3946,41 +3945,55 @@ Node* WasmGraphBuilder::LoadTransformBigEndian( LoadTransformation transformation = GetLoadTransformation(memtype, transform); switch (transformation) { - case LoadTransformation::kS8x16LoadSplat: { + case LoadTransformation::kS128Load8Splat: { result = LoadMem(type, memtype, index, offset, alignment, position); result = graph()->NewNode(mcgraph()->machine()->I8x16Splat(), result); break; } - case LoadTransformation::kI16x8Load8x8S: - case LoadTransformation::kI16x8Load8x8U: { + case LoadTransformation::kS128Load8x8S: + case LoadTransformation::kS128Load8x8U: { LOAD_EXTEND(8, 1, I16x8ReplaceLane) break; } - case LoadTransformation::kS16x8LoadSplat: { + case LoadTransformation::kS128Load16Splat: { result = LoadMem(type, memtype, index, offset, alignment, position); result = graph()->NewNode(mcgraph()->machine()->I16x8Splat(), result); break; } - case LoadTransformation::kI32x4Load16x4S: - case LoadTransformation::kI32x4Load16x4U: { + case LoadTransformation::kS128Load16x4S: + case LoadTransformation::kS128Load16x4U: { LOAD_EXTEND(4, 2, I32x4ReplaceLane) break; } - case LoadTransformation::kS32x4LoadSplat: { + case LoadTransformation::kS128Load32Splat: { result = LoadMem(type, memtype, index, offset, alignment, position); result = graph()->NewNode(mcgraph()->machine()->I32x4Splat(), result); break; } - case LoadTransformation::kI64x2Load32x2S: - case LoadTransformation::kI64x2Load32x2U: { + case LoadTransformation::kS128Load32x2S: + case LoadTransformation::kS128Load32x2U: { LOAD_EXTEND(2, 4, I64x2ReplaceLane) break; } - case LoadTransformation::kS64x2LoadSplat: { + case LoadTransformation::kS128Load64Splat: { result = LoadMem(type, memtype, index, offset, alignment, position); result = graph()->NewNode(mcgraph()->machine()->I64x2Splat(), result); break; } + case LoadTransformation::kS128Load32Zero: { + result = graph()->NewNode(mcgraph()->machine()->S128Zero()); + result = graph()->NewNode( + mcgraph()->machine()->I32x4ReplaceLane(0), result, + LoadMem(type, memtype, index, offset, alignment, position)); + break; + } + case LoadTransformation::kS128Load64Zero: { + result = graph()->NewNode(mcgraph()->machine()->S128Zero()); + result = graph()->NewNode( + mcgraph()->machine()->I64x2ReplaceLane(0), result, + LoadMem(type, memtype, index, offset, alignment, position)); + break; + } default: UNREACHABLE(); } @@ -3990,14 +4003,44 @@ Node* WasmGraphBuilder::LoadTransformBigEndian( } #endif +Node* WasmGraphBuilder::LoadLane(MachineType memtype, Node* value, Node* index, + uint32_t offset, uint8_t laneidx, + wasm::WasmCodePosition position) { + has_simd_ = true; + Node* load; + uint8_t access_size = memtype.MemSize(); + index = + BoundsCheckMem(access_size, index, offset, position, kCanOmitBoundsCheck); + + MemoryAccessKind load_kind = + GetMemoryAccessKind(mcgraph(), memtype, use_trap_handler()); + + load = SetEffect(graph()->NewNode( + mcgraph()->machine()->LoadLane(load_kind, memtype, laneidx), + MemBuffer(offset), index, value, effect(), control())); + + if (load_kind == MemoryAccessKind::kProtected) { + SetSourcePosition(load, position); + } + + if (FLAG_trace_wasm_memory) { + TraceMemoryOperation(false, memtype.representation(), index, offset, + position); + } + + return load; +} + Node* WasmGraphBuilder::LoadTransform(wasm::ValueType type, MachineType memtype, wasm::LoadTransformationKind transform, - Node* index, uint32_t offset, + Node* index, uint64_t offset, uint32_t alignment, wasm::WasmCodePosition position) { has_simd_ = true; Node* load; + // {offset} is validated to be within uintptr_t range in {BoundsCheckMem}. + uintptr_t capped_offset = static_cast<uintptr_t>(offset); #if defined(V8_TARGET_BIG_ENDIAN) || defined(V8_TARGET_ARCH_S390_LE_SIM) // LoadTransform cannot efficiently be executed on BE machines as a @@ -4005,7 +4048,7 @@ Node* WasmGraphBuilder::LoadTransform(wasm::ValueType type, MachineType memtype, // therefore we divide them into separate "load" and "operation" nodes. load = LoadTransformBigEndian(type, memtype, transform, index, offset, alignment, position); - USE(GetLoadKind); + USE(GetMemoryAccessKind); #else // Wasm semantics throw on OOB. Introduce explicit bounds check and // conditioning when not using the trap handler. @@ -4018,26 +4061,27 @@ Node* WasmGraphBuilder::LoadTransform(wasm::ValueType type, MachineType memtype, BoundsCheckMem(access_size, index, offset, position, kCanOmitBoundsCheck); LoadTransformation transformation = GetLoadTransformation(memtype, transform); - LoadKind load_kind = GetLoadKind(mcgraph(), memtype, use_trap_handler()); + MemoryAccessKind load_kind = + GetMemoryAccessKind(mcgraph(), memtype, use_trap_handler()); load = SetEffect(graph()->NewNode( mcgraph()->machine()->LoadTransform(load_kind, transformation), - MemBuffer(offset), index, effect(), control())); + MemBuffer(capped_offset), index, effect(), control())); - if (load_kind == LoadKind::kProtected) { + if (load_kind == MemoryAccessKind::kProtected) { SetSourcePosition(load, position); } #endif if (FLAG_trace_wasm_memory) { - TraceMemoryOperation(false, memtype.representation(), index, offset, + TraceMemoryOperation(false, memtype.representation(), index, capped_offset, position); } return load; } Node* WasmGraphBuilder::LoadMem(wasm::ValueType type, MachineType memtype, - Node* index, uint32_t offset, + Node* index, uint64_t offset, uint32_t alignment, wasm::WasmCodePosition position) { Node* load; @@ -4051,25 +4095,22 @@ Node* WasmGraphBuilder::LoadMem(wasm::ValueType type, MachineType memtype, index = BoundsCheckMem(memtype.MemSize(), index, offset, position, kCanOmitBoundsCheck); + // {offset} is validated to be within uintptr_t range in {BoundsCheckMem}. + uintptr_t capped_offset = static_cast<uintptr_t>(offset); if (memtype.representation() == MachineRepresentation::kWord8 || mcgraph()->machine()->UnalignedLoadSupported(memtype.representation())) { if (use_trap_handler()) { - load = graph()->NewNode(mcgraph()->machine()->ProtectedLoad(memtype), - MemBuffer(offset), index, effect(), control()); + load = gasm_->ProtectedLoad(memtype, MemBuffer(capped_offset), index); SetSourcePosition(load, position); } else { - load = graph()->NewNode(mcgraph()->machine()->Load(memtype), - MemBuffer(offset), index, effect(), control()); + load = gasm_->Load(memtype, MemBuffer(capped_offset), index); } } else { // TODO(eholk): Support unaligned loads with trap handlers. DCHECK(!use_trap_handler()); - load = graph()->NewNode(mcgraph()->machine()->UnalignedLoad(memtype), - MemBuffer(offset), index, effect(), control()); + load = gasm_->LoadUnaligned(memtype, MemBuffer(capped_offset), index); } - SetEffect(load); - #if defined(V8_TARGET_BIG_ENDIAN) load = BuildChangeEndiannessLoad(load, memtype, type); #endif @@ -4077,26 +4118,53 @@ Node* WasmGraphBuilder::LoadMem(wasm::ValueType type, MachineType memtype, if (type == wasm::kWasmI64 && ElementSizeInBytes(memtype.representation()) < 8) { // TODO(titzer): TF zeroes the upper bits of 64-bit loads for subword sizes. - if (memtype.IsSigned()) { - // sign extend - load = graph()->NewNode(mcgraph()->machine()->ChangeInt32ToInt64(), load); - } else { - // zero extend - load = - graph()->NewNode(mcgraph()->machine()->ChangeUint32ToUint64(), load); - } + load = memtype.IsSigned() + ? gasm_->ChangeInt32ToInt64(load) // sign extend + : gasm_->ChangeUint32ToUint64(load); // zero extend } if (FLAG_trace_wasm_memory) { - TraceMemoryOperation(false, memtype.representation(), index, offset, + TraceMemoryOperation(false, memtype.representation(), index, capped_offset, position); } return load; } +Node* WasmGraphBuilder::StoreLane(MachineRepresentation mem_rep, Node* index, + uint32_t offset, uint32_t alignment, + Node* val, uint8_t laneidx, + wasm::WasmCodePosition position, + wasm::ValueType type) { + Node* store; + has_simd_ = true; + index = BoundsCheckMem(i::ElementSizeInBytes(mem_rep), index, offset, + position, kCanOmitBoundsCheck); + + MachineType memtype = MachineType(mem_rep, MachineSemantic::kNone); + MemoryAccessKind load_kind = + GetMemoryAccessKind(mcgraph(), memtype, use_trap_handler()); + + // {offset} is validated to be within uintptr_t range in {BoundsCheckMem}. + uintptr_t capped_offset = static_cast<uintptr_t>(offset); + + store = SetEffect(graph()->NewNode( + mcgraph()->machine()->StoreLane(load_kind, mem_rep, laneidx), + MemBuffer(capped_offset), index, val, effect(), control())); + + if (load_kind == MemoryAccessKind::kProtected) { + SetSourcePosition(store, position); + } + + if (FLAG_trace_wasm_memory) { + TraceMemoryOperation(true, mem_rep, index, capped_offset, position); + } + + return store; +} + Node* WasmGraphBuilder::StoreMem(MachineRepresentation mem_rep, Node* index, - uint32_t offset, uint32_t alignment, Node* val, + uint64_t offset, uint32_t alignment, Node* val, wasm::WasmCodePosition position, wasm::ValueType type) { Node* store; @@ -4112,32 +4180,27 @@ Node* WasmGraphBuilder::StoreMem(MachineRepresentation mem_rep, Node* index, val = BuildChangeEndiannessStore(val, mem_rep, type); #endif + // {offset} is validated to be within uintptr_t range in {BoundsCheckMem}. + uintptr_t capped_offset = static_cast<uintptr_t>(offset); if (mem_rep == MachineRepresentation::kWord8 || mcgraph()->machine()->UnalignedStoreSupported(mem_rep)) { if (use_trap_handler()) { store = - graph()->NewNode(mcgraph()->machine()->ProtectedStore(mem_rep), - MemBuffer(offset), index, val, effect(), control()); + gasm_->ProtectedStore(mem_rep, MemBuffer(capped_offset), index, val); SetSourcePosition(store, position); } else { - StoreRepresentation rep(mem_rep, kNoWriteBarrier); - store = - graph()->NewNode(mcgraph()->machine()->Store(rep), MemBuffer(offset), - index, val, effect(), control()); + store = gasm_->Store(StoreRepresentation{mem_rep, kNoWriteBarrier}, + MemBuffer(capped_offset), index, val); } } else { // TODO(eholk): Support unaligned stores with trap handlers. DCHECK(!use_trap_handler()); UnalignedStoreRepresentation rep(mem_rep); - store = - graph()->NewNode(mcgraph()->machine()->UnalignedStore(rep), - MemBuffer(offset), index, val, effect(), control()); + store = gasm_->StoreUnaligned(rep, MemBuffer(capped_offset), index, val); } - SetEffect(store); - if (FLAG_trace_wasm_memory) { - TraceMemoryOperation(true, mem_rep, index, offset, position); + TraceMemoryOperation(true, mem_rep, index, capped_offset, position); } return store; @@ -4200,8 +4263,8 @@ Node* WasmGraphBuilder::Uint32ToUintptr(Node* node) { if (mcgraph()->machine()->Is32()) return node; // Fold instances of ChangeUint32ToUint64(IntConstant) directly. Uint32Matcher matcher(node); - if (matcher.HasValue()) { - uintptr_t value = matcher.Value(); + if (matcher.HasResolvedValue()) { + uintptr_t value = matcher.ResolvedValue(); return mcgraph()->IntPtrConstant(bit_cast<intptr_t>(value)); } return graph()->NewNode(mcgraph()->machine()->ChangeUint32ToUint64(), node); @@ -4573,6 +4636,20 @@ Node* WasmGraphBuilder::SimdOp(wasm::WasmOpcode opcode, Node* const* inputs) { return graph()->NewNode(mcgraph()->machine()->I64x2Splat(), inputs[0]); case wasm::kExprI64x2Neg: return graph()->NewNode(mcgraph()->machine()->I64x2Neg(), inputs[0]); + case wasm::kExprI64x2SConvertI32x4Low: + return graph()->NewNode(mcgraph()->machine()->I64x2SConvertI32x4Low(), + inputs[0]); + case wasm::kExprI64x2SConvertI32x4High: + return graph()->NewNode(mcgraph()->machine()->I64x2SConvertI32x4High(), + inputs[0]); + case wasm::kExprI64x2UConvertI32x4Low: + return graph()->NewNode(mcgraph()->machine()->I64x2UConvertI32x4Low(), + inputs[0]); + case wasm::kExprI64x2UConvertI32x4High: + return graph()->NewNode(mcgraph()->machine()->I64x2UConvertI32x4High(), + inputs[0]); + case wasm::kExprI64x2BitMask: + return graph()->NewNode(mcgraph()->machine()->I64x2BitMask(), inputs[0]); case wasm::kExprI64x2Shl: return graph()->NewNode(mcgraph()->machine()->I64x2Shl(), inputs[0], inputs[1]); @@ -4588,51 +4665,27 @@ Node* WasmGraphBuilder::SimdOp(wasm::WasmOpcode opcode, Node* const* inputs) { case wasm::kExprI64x2Mul: return graph()->NewNode(mcgraph()->machine()->I64x2Mul(), inputs[0], inputs[1]); - case wasm::kExprI64x2MinS: - return graph()->NewNode(mcgraph()->machine()->I64x2MinS(), inputs[0], - inputs[1]); - case wasm::kExprI64x2MaxS: - return graph()->NewNode(mcgraph()->machine()->I64x2MaxS(), inputs[0], - inputs[1]); case wasm::kExprI64x2Eq: return graph()->NewNode(mcgraph()->machine()->I64x2Eq(), inputs[0], inputs[1]); - case wasm::kExprI64x2Ne: - return graph()->NewNode(mcgraph()->machine()->I64x2Ne(), inputs[0], - inputs[1]); - case wasm::kExprI64x2LtS: - return graph()->NewNode(mcgraph()->machine()->I64x2GtS(), inputs[1], - inputs[0]); - case wasm::kExprI64x2LeS: - return graph()->NewNode(mcgraph()->machine()->I64x2GeS(), inputs[1], - inputs[0]); - case wasm::kExprI64x2GtS: - return graph()->NewNode(mcgraph()->machine()->I64x2GtS(), inputs[0], - inputs[1]); - case wasm::kExprI64x2GeS: - return graph()->NewNode(mcgraph()->machine()->I64x2GeS(), inputs[0], - inputs[1]); case wasm::kExprI64x2ShrU: return graph()->NewNode(mcgraph()->machine()->I64x2ShrU(), inputs[0], inputs[1]); - case wasm::kExprI64x2MinU: - return graph()->NewNode(mcgraph()->machine()->I64x2MinU(), inputs[0], - inputs[1]); - case wasm::kExprI64x2MaxU: - return graph()->NewNode(mcgraph()->machine()->I64x2MaxU(), inputs[0], - inputs[1]); - case wasm::kExprI64x2LtU: - return graph()->NewNode(mcgraph()->machine()->I64x2GtU(), inputs[1], - inputs[0]); - case wasm::kExprI64x2LeU: - return graph()->NewNode(mcgraph()->machine()->I64x2GeU(), inputs[1], - inputs[0]); - case wasm::kExprI64x2GtU: - return graph()->NewNode(mcgraph()->machine()->I64x2GtU(), inputs[0], - inputs[1]); - case wasm::kExprI64x2GeU: - return graph()->NewNode(mcgraph()->machine()->I64x2GeU(), inputs[0], - inputs[1]); + case wasm::kExprI64x2ExtMulLowI32x4S: + return graph()->NewNode(mcgraph()->machine()->I64x2ExtMulLowI32x4S(), + inputs[0], inputs[1]); + case wasm::kExprI64x2ExtMulHighI32x4S: + return graph()->NewNode(mcgraph()->machine()->I64x2ExtMulHighI32x4S(), + inputs[0], inputs[1]); + case wasm::kExprI64x2ExtMulLowI32x4U: + return graph()->NewNode(mcgraph()->machine()->I64x2ExtMulLowI32x4U(), + inputs[0], inputs[1]); + case wasm::kExprI64x2ExtMulHighI32x4U: + return graph()->NewNode(mcgraph()->machine()->I64x2ExtMulHighI32x4U(), + inputs[0], inputs[1]); + case wasm::kExprI64x2SignSelect: + return graph()->NewNode(mcgraph()->machine()->I64x2SignSelect(), + inputs[0], inputs[1], inputs[2]); case wasm::kExprI32x4Splat: return graph()->NewNode(mcgraph()->machine()->I32x4Splat(), inputs[0]); case wasm::kExprI32x4SConvertF32x4: @@ -4725,6 +4778,27 @@ Node* WasmGraphBuilder::SimdOp(wasm::WasmOpcode opcode, Node* const* inputs) { case wasm::kExprI32x4DotI16x8S: return graph()->NewNode(mcgraph()->machine()->I32x4DotI16x8S(), inputs[0], inputs[1]); + case wasm::kExprI32x4ExtMulLowI16x8S: + return graph()->NewNode(mcgraph()->machine()->I32x4ExtMulLowI16x8S(), + inputs[0], inputs[1]); + case wasm::kExprI32x4ExtMulHighI16x8S: + return graph()->NewNode(mcgraph()->machine()->I32x4ExtMulHighI16x8S(), + inputs[0], inputs[1]); + case wasm::kExprI32x4ExtMulLowI16x8U: + return graph()->NewNode(mcgraph()->machine()->I32x4ExtMulLowI16x8U(), + inputs[0], inputs[1]); + case wasm::kExprI32x4ExtMulHighI16x8U: + return graph()->NewNode(mcgraph()->machine()->I32x4ExtMulHighI16x8U(), + inputs[0], inputs[1]); + case wasm::kExprI32x4SignSelect: + return graph()->NewNode(mcgraph()->machine()->I32x4SignSelect(), + inputs[0], inputs[1], inputs[2]); + case wasm::kExprI32x4ExtAddPairwiseI16x8S: + return graph()->NewNode(mcgraph()->machine()->I32x4ExtAddPairwiseI16x8S(), + inputs[0]); + case wasm::kExprI32x4ExtAddPairwiseI16x8U: + return graph()->NewNode(mcgraph()->machine()->I32x4ExtAddPairwiseI16x8U(), + inputs[0]); case wasm::kExprI16x8Splat: return graph()->NewNode(mcgraph()->machine()->I16x8Splat(), inputs[0]); case wasm::kExprI16x8SConvertI8x16Low: @@ -4747,18 +4821,18 @@ Node* WasmGraphBuilder::SimdOp(wasm::WasmOpcode opcode, Node* const* inputs) { case wasm::kExprI16x8Add: return graph()->NewNode(mcgraph()->machine()->I16x8Add(), inputs[0], inputs[1]); - case wasm::kExprI16x8AddSaturateS: - return graph()->NewNode(mcgraph()->machine()->I16x8AddSaturateS(), - inputs[0], inputs[1]); + case wasm::kExprI16x8AddSatS: + return graph()->NewNode(mcgraph()->machine()->I16x8AddSatS(), inputs[0], + inputs[1]); case wasm::kExprI16x8AddHoriz: return graph()->NewNode(mcgraph()->machine()->I16x8AddHoriz(), inputs[0], inputs[1]); case wasm::kExprI16x8Sub: return graph()->NewNode(mcgraph()->machine()->I16x8Sub(), inputs[0], inputs[1]); - case wasm::kExprI16x8SubSaturateS: - return graph()->NewNode(mcgraph()->machine()->I16x8SubSaturateS(), - inputs[0], inputs[1]); + case wasm::kExprI16x8SubSatS: + return graph()->NewNode(mcgraph()->machine()->I16x8SubSatS(), inputs[0], + inputs[1]); case wasm::kExprI16x8Mul: return graph()->NewNode(mcgraph()->machine()->I16x8Mul(), inputs[0], inputs[1]); @@ -4798,12 +4872,12 @@ Node* WasmGraphBuilder::SimdOp(wasm::WasmOpcode opcode, Node* const* inputs) { case wasm::kExprI16x8ShrU: return graph()->NewNode(mcgraph()->machine()->I16x8ShrU(), inputs[0], inputs[1]); - case wasm::kExprI16x8AddSaturateU: - return graph()->NewNode(mcgraph()->machine()->I16x8AddSaturateU(), - inputs[0], inputs[1]); - case wasm::kExprI16x8SubSaturateU: - return graph()->NewNode(mcgraph()->machine()->I16x8SubSaturateU(), - inputs[0], inputs[1]); + case wasm::kExprI16x8AddSatU: + return graph()->NewNode(mcgraph()->machine()->I16x8AddSatU(), inputs[0], + inputs[1]); + case wasm::kExprI16x8SubSatU: + return graph()->NewNode(mcgraph()->machine()->I16x8SubSatU(), inputs[0], + inputs[1]); case wasm::kExprI16x8MinU: return graph()->NewNode(mcgraph()->machine()->I16x8MinU(), inputs[0], inputs[1]); @@ -4825,10 +4899,34 @@ Node* WasmGraphBuilder::SimdOp(wasm::WasmOpcode opcode, Node* const* inputs) { case wasm::kExprI16x8RoundingAverageU: return graph()->NewNode(mcgraph()->machine()->I16x8RoundingAverageU(), inputs[0], inputs[1]); + case wasm::kExprI16x8Q15MulRSatS: + return graph()->NewNode(mcgraph()->machine()->I16x8Q15MulRSatS(), + inputs[0], inputs[1]); case wasm::kExprI16x8Abs: return graph()->NewNode(mcgraph()->machine()->I16x8Abs(), inputs[0]); case wasm::kExprI16x8BitMask: return graph()->NewNode(mcgraph()->machine()->I16x8BitMask(), inputs[0]); + case wasm::kExprI16x8ExtMulLowI8x16S: + return graph()->NewNode(mcgraph()->machine()->I16x8ExtMulLowI8x16S(), + inputs[0], inputs[1]); + case wasm::kExprI16x8ExtMulHighI8x16S: + return graph()->NewNode(mcgraph()->machine()->I16x8ExtMulHighI8x16S(), + inputs[0], inputs[1]); + case wasm::kExprI16x8ExtMulLowI8x16U: + return graph()->NewNode(mcgraph()->machine()->I16x8ExtMulLowI8x16U(), + inputs[0], inputs[1]); + case wasm::kExprI16x8ExtMulHighI8x16U: + return graph()->NewNode(mcgraph()->machine()->I16x8ExtMulHighI8x16U(), + inputs[0], inputs[1]); + case wasm::kExprI16x8SignSelect: + return graph()->NewNode(mcgraph()->machine()->I16x8SignSelect(), + inputs[0], inputs[1], inputs[2]); + case wasm::kExprI16x8ExtAddPairwiseI8x16S: + return graph()->NewNode(mcgraph()->machine()->I16x8ExtAddPairwiseI8x16S(), + inputs[0]); + case wasm::kExprI16x8ExtAddPairwiseI8x16U: + return graph()->NewNode(mcgraph()->machine()->I16x8ExtAddPairwiseI8x16U(), + inputs[0]); case wasm::kExprI8x16Splat: return graph()->NewNode(mcgraph()->machine()->I8x16Splat(), inputs[0]); case wasm::kExprI8x16Neg: @@ -4845,15 +4943,15 @@ Node* WasmGraphBuilder::SimdOp(wasm::WasmOpcode opcode, Node* const* inputs) { case wasm::kExprI8x16Add: return graph()->NewNode(mcgraph()->machine()->I8x16Add(), inputs[0], inputs[1]); - case wasm::kExprI8x16AddSaturateS: - return graph()->NewNode(mcgraph()->machine()->I8x16AddSaturateS(), - inputs[0], inputs[1]); + case wasm::kExprI8x16AddSatS: + return graph()->NewNode(mcgraph()->machine()->I8x16AddSatS(), inputs[0], + inputs[1]); case wasm::kExprI8x16Sub: return graph()->NewNode(mcgraph()->machine()->I8x16Sub(), inputs[0], inputs[1]); - case wasm::kExprI8x16SubSaturateS: - return graph()->NewNode(mcgraph()->machine()->I8x16SubSaturateS(), - inputs[0], inputs[1]); + case wasm::kExprI8x16SubSatS: + return graph()->NewNode(mcgraph()->machine()->I8x16SubSatS(), inputs[0], + inputs[1]); case wasm::kExprI8x16Mul: return graph()->NewNode(mcgraph()->machine()->I8x16Mul(), inputs[0], inputs[1]); @@ -4887,12 +4985,12 @@ Node* WasmGraphBuilder::SimdOp(wasm::WasmOpcode opcode, Node* const* inputs) { case wasm::kExprI8x16UConvertI16x8: return graph()->NewNode(mcgraph()->machine()->I8x16UConvertI16x8(), inputs[0], inputs[1]); - case wasm::kExprI8x16AddSaturateU: - return graph()->NewNode(mcgraph()->machine()->I8x16AddSaturateU(), - inputs[0], inputs[1]); - case wasm::kExprI8x16SubSaturateU: - return graph()->NewNode(mcgraph()->machine()->I8x16SubSaturateU(), - inputs[0], inputs[1]); + case wasm::kExprI8x16AddSatU: + return graph()->NewNode(mcgraph()->machine()->I8x16AddSatU(), inputs[0], + inputs[1]); + case wasm::kExprI8x16SubSatU: + return graph()->NewNode(mcgraph()->machine()->I8x16SubSatU(), inputs[0], + inputs[1]); case wasm::kExprI8x16MinU: return graph()->NewNode(mcgraph()->machine()->I8x16MinU(), inputs[0], inputs[1]); @@ -4914,10 +5012,15 @@ Node* WasmGraphBuilder::SimdOp(wasm::WasmOpcode opcode, Node* const* inputs) { case wasm::kExprI8x16RoundingAverageU: return graph()->NewNode(mcgraph()->machine()->I8x16RoundingAverageU(), inputs[0], inputs[1]); + case wasm::kExprI8x16Popcnt: + return graph()->NewNode(mcgraph()->machine()->I8x16Popcnt(), inputs[0]); case wasm::kExprI8x16Abs: return graph()->NewNode(mcgraph()->machine()->I8x16Abs(), inputs[0]); case wasm::kExprI8x16BitMask: return graph()->NewNode(mcgraph()->machine()->I8x16BitMask(), inputs[0]); + case wasm::kExprI8x16SignSelect: + return graph()->NewNode(mcgraph()->machine()->I8x16SignSelect(), + inputs[0], inputs[1], inputs[2]); case wasm::kExprS128And: return graph()->NewNode(mcgraph()->machine()->S128And(), inputs[0], inputs[1]); @@ -4935,10 +5038,6 @@ Node* WasmGraphBuilder::SimdOp(wasm::WasmOpcode opcode, Node* const* inputs) { case wasm::kExprS128AndNot: return graph()->NewNode(mcgraph()->machine()->S128AndNot(), inputs[0], inputs[1]); - case wasm::kExprV64x2AnyTrue: - return graph()->NewNode(mcgraph()->machine()->V64x2AnyTrue(), inputs[0]); - case wasm::kExprV64x2AllTrue: - return graph()->NewNode(mcgraph()->machine()->V64x2AllTrue(), inputs[0]); case wasm::kExprV32x4AnyTrue: return graph()->NewNode(mcgraph()->machine()->V32x4AnyTrue(), inputs[0]); case wasm::kExprV32x4AllTrue: @@ -5017,158 +5116,176 @@ Node* WasmGraphBuilder::Simd8x16ShuffleOp(const uint8_t shuffle[16], inputs[0], inputs[1]); } -#define ATOMIC_BINOP_LIST(V) \ - V(I32AtomicAdd, Add, Uint32, Word32) \ - V(I64AtomicAdd, Add, Uint64, Word64) \ - V(I32AtomicAdd8U, Add, Uint8, Word32) \ - V(I32AtomicAdd16U, Add, Uint16, Word32) \ - V(I64AtomicAdd8U, Add, Uint8, Word64) \ - V(I64AtomicAdd16U, Add, Uint16, Word64) \ - V(I64AtomicAdd32U, Add, Uint32, Word64) \ - V(I32AtomicSub, Sub, Uint32, Word32) \ - V(I64AtomicSub, Sub, Uint64, Word64) \ - V(I32AtomicSub8U, Sub, Uint8, Word32) \ - V(I32AtomicSub16U, Sub, Uint16, Word32) \ - V(I64AtomicSub8U, Sub, Uint8, Word64) \ - V(I64AtomicSub16U, Sub, Uint16, Word64) \ - V(I64AtomicSub32U, Sub, Uint32, Word64) \ - V(I32AtomicAnd, And, Uint32, Word32) \ - V(I64AtomicAnd, And, Uint64, Word64) \ - V(I32AtomicAnd8U, And, Uint8, Word32) \ - V(I64AtomicAnd16U, And, Uint16, Word64) \ - V(I32AtomicAnd16U, And, Uint16, Word32) \ - V(I64AtomicAnd8U, And, Uint8, Word64) \ - V(I64AtomicAnd32U, And, Uint32, Word64) \ - V(I32AtomicOr, Or, Uint32, Word32) \ - V(I64AtomicOr, Or, Uint64, Word64) \ - V(I32AtomicOr8U, Or, Uint8, Word32) \ - V(I32AtomicOr16U, Or, Uint16, Word32) \ - V(I64AtomicOr8U, Or, Uint8, Word64) \ - V(I64AtomicOr16U, Or, Uint16, Word64) \ - V(I64AtomicOr32U, Or, Uint32, Word64) \ - V(I32AtomicXor, Xor, Uint32, Word32) \ - V(I64AtomicXor, Xor, Uint64, Word64) \ - V(I32AtomicXor8U, Xor, Uint8, Word32) \ - V(I32AtomicXor16U, Xor, Uint16, Word32) \ - V(I64AtomicXor8U, Xor, Uint8, Word64) \ - V(I64AtomicXor16U, Xor, Uint16, Word64) \ - V(I64AtomicXor32U, Xor, Uint32, Word64) \ - V(I32AtomicExchange, Exchange, Uint32, Word32) \ - V(I64AtomicExchange, Exchange, Uint64, Word64) \ - V(I32AtomicExchange8U, Exchange, Uint8, Word32) \ - V(I32AtomicExchange16U, Exchange, Uint16, Word32) \ - V(I64AtomicExchange8U, Exchange, Uint8, Word64) \ - V(I64AtomicExchange16U, Exchange, Uint16, Word64) \ - V(I64AtomicExchange32U, Exchange, Uint32, Word64) - -#define ATOMIC_CMP_EXCHG_LIST(V) \ - V(I32AtomicCompareExchange, Uint32, Word32) \ - V(I64AtomicCompareExchange, Uint64, Word64) \ - V(I32AtomicCompareExchange8U, Uint8, Word32) \ - V(I32AtomicCompareExchange16U, Uint16, Word32) \ - V(I64AtomicCompareExchange8U, Uint8, Word64) \ - V(I64AtomicCompareExchange16U, Uint16, Word64) \ - V(I64AtomicCompareExchange32U, Uint32, Word64) - -#define ATOMIC_LOAD_LIST(V) \ - V(I32AtomicLoad, Uint32, Word32) \ - V(I64AtomicLoad, Uint64, Word64) \ - V(I32AtomicLoad8U, Uint8, Word32) \ - V(I32AtomicLoad16U, Uint16, Word32) \ - V(I64AtomicLoad8U, Uint8, Word64) \ - V(I64AtomicLoad16U, Uint16, Word64) \ - V(I64AtomicLoad32U, Uint32, Word64) - -#define ATOMIC_STORE_LIST(V) \ - V(I32AtomicStore, Uint32, kWord32, Word32) \ - V(I64AtomicStore, Uint64, kWord64, Word64) \ - V(I32AtomicStore8U, Uint8, kWord8, Word32) \ - V(I32AtomicStore16U, Uint16, kWord16, Word32) \ - V(I64AtomicStore8U, Uint8, kWord8, Word64) \ - V(I64AtomicStore16U, Uint16, kWord16, Word64) \ - V(I64AtomicStore32U, Uint32, kWord32, Word64) - Node* WasmGraphBuilder::AtomicOp(wasm::WasmOpcode opcode, Node* const* inputs, - uint32_t alignment, uint32_t offset, + uint32_t alignment, uint64_t offset, wasm::WasmCodePosition position) { - Node* node; + struct AtomicOpInfo { + enum Type : int8_t { + kNoInput = 0, + kOneInput = 1, + kTwoInputs = 2, + kSpecial + }; + + using OperatorByType = + const Operator* (MachineOperatorBuilder::*)(MachineType); + using OperatorByRep = + const Operator* (MachineOperatorBuilder::*)(MachineRepresentation); + + const Type type; + const MachineType machine_type; + const OperatorByType operator_by_type = nullptr; + const OperatorByRep operator_by_rep = nullptr; + + constexpr AtomicOpInfo(Type t, MachineType m, OperatorByType o) + : type(t), machine_type(m), operator_by_type(o) {} + constexpr AtomicOpInfo(Type t, MachineType m, OperatorByRep o) + : type(t), machine_type(m), operator_by_rep(o) {} + + // Constexpr, hence just a table lookup in most compilers. + static constexpr AtomicOpInfo Get(wasm::WasmOpcode opcode) { + switch (opcode) { +#define CASE(Name, Type, MachType, Op) \ + case wasm::kExpr##Name: \ + return {Type, MachineType::MachType(), &MachineOperatorBuilder::Op}; + + // Binops. + CASE(I32AtomicAdd, kOneInput, Uint32, Word32AtomicAdd) + CASE(I64AtomicAdd, kOneInput, Uint64, Word64AtomicAdd) + CASE(I32AtomicAdd8U, kOneInput, Uint8, Word32AtomicAdd) + CASE(I32AtomicAdd16U, kOneInput, Uint16, Word32AtomicAdd) + CASE(I64AtomicAdd8U, kOneInput, Uint8, Word64AtomicAdd) + CASE(I64AtomicAdd16U, kOneInput, Uint16, Word64AtomicAdd) + CASE(I64AtomicAdd32U, kOneInput, Uint32, Word64AtomicAdd) + CASE(I32AtomicSub, kOneInput, Uint32, Word32AtomicSub) + CASE(I64AtomicSub, kOneInput, Uint64, Word64AtomicSub) + CASE(I32AtomicSub8U, kOneInput, Uint8, Word32AtomicSub) + CASE(I32AtomicSub16U, kOneInput, Uint16, Word32AtomicSub) + CASE(I64AtomicSub8U, kOneInput, Uint8, Word64AtomicSub) + CASE(I64AtomicSub16U, kOneInput, Uint16, Word64AtomicSub) + CASE(I64AtomicSub32U, kOneInput, Uint32, Word64AtomicSub) + CASE(I32AtomicAnd, kOneInput, Uint32, Word32AtomicAnd) + CASE(I64AtomicAnd, kOneInput, Uint64, Word64AtomicAnd) + CASE(I32AtomicAnd8U, kOneInput, Uint8, Word32AtomicAnd) + CASE(I32AtomicAnd16U, kOneInput, Uint16, Word32AtomicAnd) + CASE(I64AtomicAnd8U, kOneInput, Uint8, Word64AtomicAnd) + CASE(I64AtomicAnd16U, kOneInput, Uint16, Word64AtomicAnd) + CASE(I64AtomicAnd32U, kOneInput, Uint32, Word64AtomicAnd) + CASE(I32AtomicOr, kOneInput, Uint32, Word32AtomicOr) + CASE(I64AtomicOr, kOneInput, Uint64, Word64AtomicOr) + CASE(I32AtomicOr8U, kOneInput, Uint8, Word32AtomicOr) + CASE(I32AtomicOr16U, kOneInput, Uint16, Word32AtomicOr) + CASE(I64AtomicOr8U, kOneInput, Uint8, Word64AtomicOr) + CASE(I64AtomicOr16U, kOneInput, Uint16, Word64AtomicOr) + CASE(I64AtomicOr32U, kOneInput, Uint32, Word64AtomicOr) + CASE(I32AtomicXor, kOneInput, Uint32, Word32AtomicXor) + CASE(I64AtomicXor, kOneInput, Uint64, Word64AtomicXor) + CASE(I32AtomicXor8U, kOneInput, Uint8, Word32AtomicXor) + CASE(I32AtomicXor16U, kOneInput, Uint16, Word32AtomicXor) + CASE(I64AtomicXor8U, kOneInput, Uint8, Word64AtomicXor) + CASE(I64AtomicXor16U, kOneInput, Uint16, Word64AtomicXor) + CASE(I64AtomicXor32U, kOneInput, Uint32, Word64AtomicXor) + CASE(I32AtomicExchange, kOneInput, Uint32, Word32AtomicExchange) + CASE(I64AtomicExchange, kOneInput, Uint64, Word64AtomicExchange) + CASE(I32AtomicExchange8U, kOneInput, Uint8, Word32AtomicExchange) + CASE(I32AtomicExchange16U, kOneInput, Uint16, Word32AtomicExchange) + CASE(I64AtomicExchange8U, kOneInput, Uint8, Word64AtomicExchange) + CASE(I64AtomicExchange16U, kOneInput, Uint16, Word64AtomicExchange) + CASE(I64AtomicExchange32U, kOneInput, Uint32, Word64AtomicExchange) + + // Compare-exchange. + CASE(I32AtomicCompareExchange, kTwoInputs, Uint32, + Word32AtomicCompareExchange) + CASE(I64AtomicCompareExchange, kTwoInputs, Uint64, + Word64AtomicCompareExchange) + CASE(I32AtomicCompareExchange8U, kTwoInputs, Uint8, + Word32AtomicCompareExchange) + CASE(I32AtomicCompareExchange16U, kTwoInputs, Uint16, + Word32AtomicCompareExchange) + CASE(I64AtomicCompareExchange8U, kTwoInputs, Uint8, + Word64AtomicCompareExchange) + CASE(I64AtomicCompareExchange16U, kTwoInputs, Uint16, + Word64AtomicCompareExchange) + CASE(I64AtomicCompareExchange32U, kTwoInputs, Uint32, + Word64AtomicCompareExchange) + + // Load. + CASE(I32AtomicLoad, kNoInput, Uint32, Word32AtomicLoad) + CASE(I64AtomicLoad, kNoInput, Uint64, Word64AtomicLoad) + CASE(I32AtomicLoad8U, kNoInput, Uint8, Word32AtomicLoad) + CASE(I32AtomicLoad16U, kNoInput, Uint16, Word32AtomicLoad) + CASE(I64AtomicLoad8U, kNoInput, Uint8, Word64AtomicLoad) + CASE(I64AtomicLoad16U, kNoInput, Uint16, Word64AtomicLoad) + CASE(I64AtomicLoad32U, kNoInput, Uint32, Word64AtomicLoad) + + // Store. + CASE(I32AtomicStore, kOneInput, Uint32, Word32AtomicStore) + CASE(I64AtomicStore, kOneInput, Uint64, Word64AtomicStore) + CASE(I32AtomicStore8U, kOneInput, Uint8, Word32AtomicStore) + CASE(I32AtomicStore16U, kOneInput, Uint16, Word32AtomicStore) + CASE(I64AtomicStore8U, kOneInput, Uint8, Word64AtomicStore) + CASE(I64AtomicStore16U, kOneInput, Uint16, Word64AtomicStore) + CASE(I64AtomicStore32U, kOneInput, Uint32, Word64AtomicStore) + +#undef CASE + + case wasm::kExprAtomicNotify: + return {kSpecial, MachineType::Int32(), OperatorByType{nullptr}}; + case wasm::kExprI32AtomicWait: + return {kSpecial, MachineType::Int32(), OperatorByType{nullptr}}; + case wasm::kExprI64AtomicWait: + return {kSpecial, MachineType::Int64(), OperatorByType{nullptr}}; + default: +#if V8_HAS_CXX14_CONSTEXPR + UNREACHABLE(); +#else + // Return something for older GCC. + return {kSpecial, MachineType::Int64(), OperatorByType{nullptr}}; +#endif + } + } + }; + + AtomicOpInfo info = AtomicOpInfo::Get(opcode); + + Node* index = CheckBoundsAndAlignment(info.machine_type.MemSize(), inputs[0], + offset, position); + + // {offset} is validated to be within uintptr_t range in {BoundsCheckMem}. + uintptr_t capped_offset = static_cast<uintptr_t>(offset); + if (info.type != AtomicOpInfo::kSpecial) { + const Operator* op = + info.operator_by_type + ? (mcgraph()->machine()->*info.operator_by_type)(info.machine_type) + : (mcgraph()->machine()->*info.operator_by_rep)( + info.machine_type.representation()); + + Node* input_nodes[6] = {MemBuffer(capped_offset), index}; + int num_actual_inputs = info.type; + std::copy_n(inputs + 1, num_actual_inputs, input_nodes + 2); + input_nodes[num_actual_inputs + 2] = effect(); + input_nodes[num_actual_inputs + 3] = control(); + return gasm_->AddNode( + graph()->NewNode(op, num_actual_inputs + 4, input_nodes)); + } + + // After we've bounds-checked, compute the effective offset. + Node* effective_offset = + gasm_->IntAdd(gasm_->UintPtrConstant(capped_offset), index); + switch (opcode) { -#define BUILD_ATOMIC_BINOP(Name, Operation, Type, Prefix) \ - case wasm::kExpr##Name: { \ - Node* index = CheckBoundsAndAlignment(MachineType::Type().MemSize(), \ - inputs[0], offset, position); \ - node = graph()->NewNode( \ - mcgraph()->machine()->Prefix##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_CMP_EXCHG(Name, Type, Prefix) \ - case wasm::kExpr##Name: { \ - Node* index = CheckBoundsAndAlignment(MachineType::Type().MemSize(), \ - inputs[0], offset, position); \ - node = graph()->NewNode( \ - mcgraph()->machine()->Prefix##AtomicCompareExchange( \ - MachineType::Type()), \ - MemBuffer(offset), index, inputs[1], inputs[2], effect(), control()); \ - break; \ - } - ATOMIC_CMP_EXCHG_LIST(BUILD_ATOMIC_CMP_EXCHG) -#undef BUILD_ATOMIC_CMP_EXCHG - -#define BUILD_ATOMIC_LOAD_OP(Name, Type, Prefix) \ - case wasm::kExpr##Name: { \ - Node* index = CheckBoundsAndAlignment(MachineType::Type().MemSize(), \ - inputs[0], offset, position); \ - node = graph()->NewNode( \ - mcgraph()->machine()->Prefix##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, Prefix) \ - case wasm::kExpr##Name: { \ - Node* index = CheckBoundsAndAlignment(MachineType::Type().MemSize(), \ - inputs[0], offset, position); \ - node = graph()->NewNode( \ - mcgraph()->machine()->Prefix##AtomicStore(MachineRepresentation::Rep), \ - MemBuffer(offset), index, inputs[1], effect(), control()); \ - break; \ - } - ATOMIC_STORE_LIST(BUILD_ATOMIC_STORE_OP) -#undef BUILD_ATOMIC_STORE_OP case wasm::kExprAtomicNotify: { - Node* index = CheckBoundsAndAlignment(MachineType::Uint32().MemSize(), - inputs[0], offset, position); - // Now that we've bounds-checked, compute the effective address. - Node* address = graph()->NewNode(mcgraph()->machine()->Int32Add(), - Uint32Constant(offset), index); - WasmAtomicNotifyDescriptor interface_descriptor; - auto call_descriptor = Linkage::GetStubCallDescriptor( - mcgraph()->zone(), interface_descriptor, - interface_descriptor.GetStackParameterCount(), - CallDescriptor::kNoFlags, Operator::kNoProperties, - StubCallMode::kCallWasmRuntimeStub); + auto* call_descriptor = + GetBuiltinCallDescriptor<WasmAtomicNotifyDescriptor>( + this, StubCallMode::kCallWasmRuntimeStub); Node* call_target = mcgraph()->RelocatableIntPtrConstant( wasm::WasmCode::kWasmAtomicNotify, RelocInfo::WASM_STUB_CALL); - node = graph()->NewNode(mcgraph()->common()->Call(call_descriptor), - call_target, address, inputs[1], effect(), - control()); - break; + return gasm_->Call(call_descriptor, call_target, effective_offset, + inputs[1]); } case wasm::kExprI32AtomicWait: { - Node* index = CheckBoundsAndAlignment(MachineType::Uint32().MemSize(), - inputs[0], offset, position); - // Now that we've bounds-checked, compute the effective address. - Node* address = graph()->NewNode(mcgraph()->machine()->Int32Add(), - Uint32Constant(offset), index); - - auto call_descriptor = GetI32AtomicWaitCallDescriptor(); + auto* call_descriptor = GetI32AtomicWaitCallDescriptor(); intptr_t target = mcgraph()->machine()->Is64() ? wasm::WasmCode::kWasmI32AtomicWait64 @@ -5176,20 +5293,12 @@ Node* WasmGraphBuilder::AtomicOp(wasm::WasmOpcode opcode, Node* const* inputs, Node* call_target = mcgraph()->RelocatableIntPtrConstant( target, RelocInfo::WASM_STUB_CALL); - node = graph()->NewNode(mcgraph()->common()->Call(call_descriptor), - call_target, address, inputs[1], inputs[2], - effect(), control()); - break; + return gasm_->Call(call_descriptor, call_target, effective_offset, + inputs[1], inputs[2]); } case wasm::kExprI64AtomicWait: { - Node* index = CheckBoundsAndAlignment(MachineType::Uint64().MemSize(), - inputs[0], offset, position); - // Now that we've bounds-checked, compute the effective address. - Node* address = graph()->NewNode(mcgraph()->machine()->Int32Add(), - Uint32Constant(offset), index); - - CallDescriptor* call_descriptor = GetI64AtomicWaitCallDescriptor(); + auto* call_descriptor = GetI64AtomicWaitCallDescriptor(); intptr_t target = mcgraph()->machine()->Is64() ? wasm::WasmCode::kWasmI64AtomicWait64 @@ -5197,16 +5306,13 @@ Node* WasmGraphBuilder::AtomicOp(wasm::WasmOpcode opcode, Node* const* inputs, Node* call_target = mcgraph()->RelocatableIntPtrConstant( target, RelocInfo::WASM_STUB_CALL); - node = graph()->NewNode(mcgraph()->common()->Call(call_descriptor), - call_target, address, inputs[1], inputs[2], - effect(), control()); - break; + return gasm_->Call(call_descriptor, call_target, effective_offset, + inputs[1], inputs[2]); } default: FATAL_UNSUPPORTED_OPCODE(opcode); } - return SetEffect(node); } Node* WasmGraphBuilder::AtomicFence() { @@ -5214,11 +5320,6 @@ Node* WasmGraphBuilder::AtomicFence() { effect(), control())); } -#undef ATOMIC_BINOP_LIST -#undef ATOMIC_CMP_EXCHG_LIST -#undef ATOMIC_LOAD_LIST -#undef ATOMIC_STORE_LIST - Node* WasmGraphBuilder::MemoryInit(uint32_t data_segment_index, Node* dst, Node* src, Node* size, wasm::WasmCodePosition position) { @@ -6157,9 +6258,9 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder { wasm::ValueType type) { // Make sure ValueType fits in a Smi. STATIC_ASSERT(wasm::ValueType::kLastUsedBit + 1 <= kSmiValueSize); - Node* inputs[] = { - instance_node_.get(), input, - IntPtrConstant(IntToSmi(static_cast<int>(type.raw_bit_field())))}; + Node* inputs[] = {instance_node_.get(), input, + mcgraph()->IntPtrConstant( + IntToSmi(static_cast<int>(type.raw_bit_field())))}; Node* check = BuildChangeSmiToInt32(SetEffect(BuildCallToRuntimeWithContext( Runtime::kWasmIsValidRefValue, js_context, inputs, 3))); @@ -6169,7 +6270,7 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder { SetControl(type_check.if_false); Node* old_effect = effect(); - BuildCallToRuntimeWithContext(Runtime::kWasmThrowTypeError, js_context, + BuildCallToRuntimeWithContext(Runtime::kWasmThrowJSTypeError, js_context, nullptr, 0); SetEffectControl(type_check.EffectPhi(old_effect, effect()), @@ -6241,14 +6342,38 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder { BuildChangeSmiToInt32(input)); } + Node* HeapNumberToFloat64(Node* input) { + return gasm_->Load(MachineType::Float64(), input, + wasm::ObjectAccess::ToTagged(HeapNumber::kValueOffset)); + } + Node* FromJSFast(Node* input, wasm::ValueType type) { switch (type.kind()) { case wasm::ValueType::kI32: return BuildChangeSmiToInt32(input); - case wasm::ValueType::kF32: - return SmiToFloat32(input); - case wasm::ValueType::kF64: - return SmiToFloat64(input); + case wasm::ValueType::kF32: { + auto done = gasm_->MakeLabel(MachineRepresentation::kFloat32); + auto heap_number = gasm_->MakeLabel(); + gasm_->GotoIfNot(IsSmi(input), &heap_number); + gasm_->Goto(&done, SmiToFloat32(input)); + gasm_->Bind(&heap_number); + Node* value = + graph()->NewNode(mcgraph()->machine()->TruncateFloat64ToFloat32(), + HeapNumberToFloat64(input)); + gasm_->Goto(&done, value); + gasm_->Bind(&done); + return done.PhiAt(0); + } + case wasm::ValueType::kF64: { + auto done = gasm_->MakeLabel(MachineRepresentation::kFloat64); + auto heap_number = gasm_->MakeLabel(); + gasm_->GotoIfNot(IsSmi(input), &heap_number); + gasm_->Goto(&done, SmiToFloat64(input)); + gasm_->Bind(&heap_number); + gasm_->Goto(&done, HeapNumberToFloat64(input)); + gasm_->Bind(&done); + return done.PhiAt(0); + } case wasm::ValueType::kRef: case wasm::ValueType::kOptRef: case wasm::ValueType::kI64: @@ -6313,7 +6438,7 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder { Node* BuildMultiReturnFixedArrayFromIterable(const wasm::FunctionSig* sig, Node* iterable, Node* context) { Node* length = BuildChangeUint31ToSmi( - Uint32Constant(static_cast<uint32_t>(sig->return_count()))); + mcgraph()->Uint32Constant(static_cast<uint32_t>(sig->return_count()))); return CALL_BUILTIN(IterableToFixedArrayForWasm, iterable, length, context); } @@ -6420,12 +6545,30 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder { gasm_->Int32Constant(0)); } - Node* CanTransformFast(Node* input, wasm::ValueType type) { + void CanTransformFast( + Node* input, wasm::ValueType type, + v8::internal::compiler::GraphAssemblerLabel<0>* slow_path) { switch (type.kind()) { - case wasm::ValueType::kI32: - case wasm::ValueType::kF64: + case wasm::ValueType::kI32: { + gasm_->GotoIfNot(IsSmi(input), slow_path); + return; + } case wasm::ValueType::kF32: - return IsSmi(input); + case wasm::ValueType::kF64: { + auto done = gasm_->MakeLabel(); + gasm_->GotoIf(IsSmi(input), &done); + Node* map = + gasm_->Load(MachineType::TaggedPointer(), input, + wasm::ObjectAccess::ToTagged(HeapObject::kMapOffset)); + Node* heap_number_map = LOAD_FULL_POINTER( + BuildLoadIsolateRoot(), + IsolateData::root_slot_offset(RootIndex::kHeapNumberMap)); + Node* is_heap_number = gasm_->WordEqual(heap_number_map, map); + gasm_->GotoIf(is_heap_number, &done); + gasm_->Goto(slow_path); + gasm_->Bind(&done); + return; + } case wasm::ValueType::kRef: case wasm::ValueType::kOptRef: case wasm::ValueType::kI64: @@ -6460,7 +6603,7 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder { // an actual reference to an instance or a placeholder reference, // called {WasmExportedFunction} via the {WasmExportedFunctionData} // structure. - Node* function_data = BuildLoadFunctionDataFromExportedFunction(js_closure); + Node* function_data = BuildLoadFunctionDataFromJSFunction(js_closure); instance_node_.set( BuildLoadInstanceFromExportedFunctionData(function_data)); @@ -6468,7 +6611,7 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder { // 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. - BuildCallToRuntimeWithContext(Runtime::kWasmThrowTypeError, js_context, + BuildCallToRuntimeWithContext(Runtime::kWasmThrowJSTypeError, js_context, nullptr, 0); TerminateThrow(effect(), control()); return; @@ -6494,8 +6637,7 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder { // fast is encountered, skip checking the rest and fall back to the slow // path. for (int i = 0; i < wasm_count; ++i) { - gasm_->GotoIfNot(CanTransformFast(params[i + 1], sig_->GetParam(i)), - &slow_path); + CanTransformFast(params[i + 1], sig_->GetParam(i), &slow_path); } // Convert JS parameters to wasm numbers using the fast transformation // and build the call. @@ -6557,7 +6699,7 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder { global_proxy); } - bool BuildWasmImportCallWrapper(WasmImportCallKind kind, int expected_arity) { + bool BuildWasmToJSWrapper(WasmImportCallKind kind, int expected_arity) { int wasm_count = static_cast<int>(sig_->parameter_count()); // Build the start and the parameter nodes. @@ -6572,7 +6714,7 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder { // ======================================================================= // === Runtime TypeError ================================================= // ======================================================================= - BuildCallToRuntimeWithContext(Runtime::kWasmThrowTypeError, + BuildCallToRuntimeWithContext(Runtime::kWasmThrowJSTypeError, native_context, nullptr, 0); TerminateThrow(effect(), control()); return false; @@ -6621,8 +6763,45 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder { args.begin()); break; } +#ifdef V8_NO_ARGUMENTS_ADAPTOR // ======================================================================= - // === JS Functions with arguments adapter =============================== + // === JS Functions with mismatching arity =============================== + // ======================================================================= + case WasmImportCallKind::kJSFunctionArityMismatch: { + int pushed_count = std::max(expected_arity, wasm_count); + base::SmallVector<Node*, 16> args(pushed_count + 7); + int pos = 0; + + args[pos++] = callable_node; // target callable. + // Determine receiver at runtime. + args[pos++] = + BuildReceiverNode(callable_node, native_context, undefined_node); + + // Convert wasm numbers to JS values. + pos = AddArgumentNodes(VectorOf(args), pos, wasm_count, sig_); + for (int i = wasm_count; i < expected_arity; ++i) { + args[pos++] = undefined_node; + } + args[pos++] = undefined_node; // new target + args[pos++] = mcgraph()->Int32Constant(wasm_count); // argument count + + Node* function_context = + gasm_->Load(MachineType::TaggedPointer(), callable_node, + wasm::ObjectAccess::ContextOffsetInTaggedJSFunction()); + args[pos++] = function_context; + args[pos++] = effect(); + args[pos++] = control(); + DCHECK_EQ(pos, args.size()); + + auto call_descriptor = Linkage::GetJSCallDescriptor( + graph()->zone(), false, pushed_count + 1, CallDescriptor::kNoFlags); + call = graph()->NewNode(mcgraph()->common()->Call(call_descriptor), pos, + args.begin()); + break; + } +#else + // ======================================================================= + // === JS Functions with mismatching arity =============================== // ======================================================================= case WasmImportCallKind::kJSFunctionArityMismatch: { base::SmallVector<Node*, 16> args(wasm_count + 9); @@ -6630,9 +6809,8 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder { Node* function_context = gasm_->Load(MachineType::TaggedPointer(), callable_node, wasm::ObjectAccess::ContextOffsetInTaggedJSFunction()); - args[pos++] = mcgraph()->RelocatableIntPtrConstant( - wasm::WasmCode::kArgumentsAdaptorTrampoline, - RelocInfo::WASM_STUB_CALL); + args[pos++] = + GetBuiltinPointerTarget(Builtins::kArgumentsAdaptorTrampoline); args[pos++] = callable_node; // target callable args[pos++] = undefined_node; // new target args[pos++] = mcgraph()->Int32Constant(wasm_count); // argument count @@ -6657,7 +6835,7 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder { auto call_descriptor = Linkage::GetStubCallDescriptor( mcgraph()->zone(), ArgumentsAdaptorDescriptor{}, 1 + wasm_count, CallDescriptor::kNoFlags, Operator::kNoProperties, - StubCallMode::kCallWasmRuntimeStub); + StubCallMode::kCallBuiltinPointer); // Convert wasm numbers to JS values. pos = AddArgumentNodes(VectorOf(args), pos, wasm_count, sig_); @@ -6670,47 +6848,7 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder { args.begin()); break; } - // ======================================================================= - // === JS Functions without arguments adapter ============================ - // ======================================================================= - case WasmImportCallKind::kJSFunctionArityMismatchSkipAdaptor: { - base::SmallVector<Node*, 16> args(expected_arity + 7); - int pos = 0; - Node* function_context = - gasm_->Load(MachineType::TaggedPointer(), callable_node, - wasm::ObjectAccess::ContextOffsetInTaggedJSFunction()); - args[pos++] = callable_node; // target callable. - - // Determine receiver at runtime. - args[pos++] = - BuildReceiverNode(callable_node, native_context, undefined_node); - - auto call_descriptor = Linkage::GetJSCallDescriptor( - graph()->zone(), false, expected_arity + 1, - CallDescriptor::kNoFlags); - - // Convert wasm numbers to JS values. - if (expected_arity <= wasm_count) { - pos = AddArgumentNodes(VectorOf(args), pos, expected_arity, sig_); - } else { - pos = AddArgumentNodes(VectorOf(args), pos, wasm_count, sig_); - for (int i = wasm_count; i < expected_arity; ++i) { - args[pos++] = undefined_node; - } - } - - args[pos++] = undefined_node; // new target - args[pos++] = - mcgraph()->Int32Constant(expected_arity); // argument count - args[pos++] = function_context; - args[pos++] = effect(); - args[pos++] = control(); - - DCHECK_EQ(pos, args.size()); - call = graph()->NewNode(mcgraph()->common()->Call(call_descriptor), pos, - args.begin()); - break; - } +#endif // ======================================================================= // === General case of unknown callable ================================== // ======================================================================= @@ -6837,11 +6975,11 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder { BuildModifyThreadInWasmFlag(true); - Node* exception_branch = - graph()->NewNode(mcgraph()->common()->Branch(BranchHint::kTrue), - graph()->NewNode(mcgraph()->machine()->WordEqual(), - return_value, IntPtrConstant(0)), - control()); + Node* exception_branch = graph()->NewNode( + mcgraph()->common()->Branch(BranchHint::kTrue), + graph()->NewNode(mcgraph()->machine()->WordEqual(), return_value, + mcgraph()->IntPtrConstant(0)), + control()); SetControl( graph()->NewNode(mcgraph()->common()->IfFalse(), exception_branch)); WasmThrowDescriptor interface_descriptor; @@ -6897,7 +7035,7 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder { // Throw a TypeError if the signature is incompatible with JavaScript. if (!wasm::IsJSCompatibleSignature(sig_, module_, enabled_features_)) { - BuildCallToRuntimeWithContext(Runtime::kWasmThrowTypeError, context, + BuildCallToRuntimeWithContext(Runtime::kWasmThrowJSTypeError, context, nullptr, 0); TerminateThrow(effect(), control()); return; @@ -7114,17 +7252,17 @@ std::pair<WasmImportCallKind, Handle<JSReceiver>> ResolveWasmImportCall( const wasm::WasmFeatures& enabled_features) { if (WasmExportedFunction::IsWasmExportedFunction(*callable)) { auto imported_function = Handle<WasmExportedFunction>::cast(callable); - auto func_index = imported_function->function_index(); - auto module = imported_function->instance().module(); - const wasm::FunctionSig* imported_sig = module->functions[func_index].sig; - if (*imported_sig != *expected_sig) { + if (!imported_function->MatchesSignature(module, expected_sig)) { return std::make_pair(WasmImportCallKind::kLinkError, callable); } - if (static_cast<uint32_t>(func_index) >= module->num_imported_functions) { + uint32_t func_index = + static_cast<uint32_t>(imported_function->function_index()); + if (func_index >= + imported_function->instance().module()->num_imported_functions) { return std::make_pair(WasmImportCallKind::kWasmToWasm, callable); } Isolate* isolate = callable->GetIsolate(); - // Resolve the short-cut to the underlying callable and continue. + // Resolve the shortcut to the underlying callable and continue. Handle<WasmInstanceObject> instance(imported_function->instance(), isolate); ImportedFunctionEntry entry(instance, func_index); callable = handle(entry.callable(), isolate); @@ -7224,14 +7362,6 @@ std::pair<WasmImportCallKind, Handle<JSReceiver>> ResolveWasmImportCall( Compiler::Compile(function, Compiler::CLEAR_EXCEPTION, &is_compiled_scope); } -#ifndef V8_REVERSE_JSARGS - // This optimization is disabled when the arguments are reversed. It will be - // subsumed when the argumens adaptor frame is removed. - if (shared->is_safe_to_skip_arguments_adaptor()) { - return std::make_pair( - WasmImportCallKind::kJSFunctionArityMismatchSkipAdaptor, callable); - } -#endif return std::make_pair(WasmImportCallKind::kJSFunctionArityMismatch, callable); @@ -7378,7 +7508,7 @@ wasm::WasmCompilationResult CompileWasmImportCallWrapper( WasmWrapperGraphBuilder builder( &zone, mcgraph, sig, env->module, source_position_table, StubCallMode::kCallWasmRuntimeStub, env->enabled_features); - builder.BuildWasmImportCallWrapper(kind, expected_arity); + builder.BuildWasmToJSWrapper(kind, expected_arity); // Build a name in the form "wasm-to-js-<kind>-<signature>". constexpr size_t kMaxNameLen = 128; @@ -7455,6 +7585,57 @@ wasm::WasmCode* CompileWasmCapiCallWrapper(wasm::WasmEngine* wasm_engine, return native_module->PublishCode(std::move(wasm_code)); } +MaybeHandle<Code> CompileWasmToJSWrapper(Isolate* isolate, + const wasm::FunctionSig* sig, + WasmImportCallKind kind, + int expected_arity) { + std::unique_ptr<Zone> zone = std::make_unique<Zone>( + isolate->allocator(), ZONE_NAME, kCompressGraphZone); + + // Create the Graph + Graph* graph = zone->New<Graph>(zone.get()); + CommonOperatorBuilder* common = zone->New<CommonOperatorBuilder>(zone.get()); + MachineOperatorBuilder* machine = zone->New<MachineOperatorBuilder>( + zone.get(), MachineType::PointerRepresentation(), + InstructionSelector::SupportedMachineOperatorFlags(), + InstructionSelector::AlignmentRequirements()); + MachineGraph* mcgraph = zone->New<MachineGraph>(graph, common, machine); + + WasmWrapperGraphBuilder builder(zone.get(), mcgraph, sig, nullptr, nullptr, + StubCallMode::kCallWasmRuntimeStub, + wasm::WasmFeatures::FromIsolate(isolate)); + builder.BuildWasmToJSWrapper(kind, expected_arity); + + // Build a name in the form "wasm-to-js-<kind>-<signature>". + constexpr size_t kMaxNameLen = 128; + constexpr size_t kNamePrefixLen = 11; + auto name_buffer = std::unique_ptr<char[]>(new char[kMaxNameLen]); + memcpy(name_buffer.get(), "wasm-to-js:", kNamePrefixLen); + PrintSignature(VectorOf(name_buffer.get(), kMaxNameLen) + kNamePrefixLen, + sig); + + // Generate the call descriptor. + CallDescriptor* incoming = + GetWasmCallDescriptor(zone.get(), sig, WasmGraphBuilder::kNoRetpoline, + WasmCallKind::kWasmImportWrapper); + + // Run the compilation job synchronously. + std::unique_ptr<OptimizedCompilationJob> job( + Pipeline::NewWasmHeapStubCompilationJob( + isolate, isolate->wasm_engine(), incoming, std::move(zone), graph, + CodeKind::WASM_TO_JS_FUNCTION, std::move(name_buffer), + AssemblerOptions::Default(isolate))); + + // Compile the wrapper + if (job->ExecuteJob(isolate->counters()->runtime_call_stats()) == + CompilationJob::FAILED || + job->FinalizeJob(isolate) == CompilationJob::FAILED) { + return Handle<Code>(); + } + Handle<Code> code = job->compilation_info()->code(); + return code; +} + MaybeHandle<Code> CompileJSToJSWrapper(Isolate* isolate, const wasm::FunctionSig* sig, const wasm::WasmModule* module) { @@ -7547,7 +7728,7 @@ Handle<Code> CompileCWasmEntry(Isolate* isolate, const wasm::FunctionSig* sig, CodeKind::C_WASM_ENTRY, std::move(name_buffer), AssemblerOptions::Default(isolate))); - CHECK_NE(job->ExecuteJob(isolate->counters()->runtime_call_stats()), + CHECK_NE(job->ExecuteJob(isolate->counters()->runtime_call_stats(), nullptr), CompilationJob::FAILED); CHECK_NE(job->FinalizeJob(isolate), CompilationJob::FAILED); |