// Copyright 2017 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/builtins/builtins-utils-gen.h" #include "src/codegen/code-stub-assembler.h" #include "src/codegen/interface-descriptors.h" #include "src/objects/objects-inl.h" #include "src/wasm/wasm-objects.h" #include "src/wasm/wasm-opcodes.h" namespace v8 { namespace internal { class WasmBuiltinsAssembler : public CodeStubAssembler { public: explicit WasmBuiltinsAssembler(compiler::CodeAssemblerState* state) : CodeStubAssembler(state) {} protected: TNode LoadInstanceFromFrame() { return CAST( LoadFromParentFrame(WasmCompiledFrameConstants::kWasmInstanceOffset)); } TNode LoadContextFromInstance(TNode instance) { return CAST(Load(MachineType::AnyTagged(), instance, IntPtrConstant(WasmInstanceObject::kNativeContextOffset - kHeapObjectTag))); } TNode SmiFromUint32WithSaturation(TNode value, uint32_t max) { DCHECK_LE(max, static_cast(Smi::kMaxValue)); TNode capped_value = SelectConstant( Uint32LessThan(value, Uint32Constant(max)), value, Uint32Constant(max)); return SmiFromUint32(capped_value); } }; TF_BUILTIN(WasmStackGuard, WasmBuiltinsAssembler) { TNode instance = LoadInstanceFromFrame(); TNode context = LoadContextFromInstance(instance); TailCallRuntime(Runtime::kWasmStackGuard, context); } TF_BUILTIN(WasmStackOverflow, WasmBuiltinsAssembler) { TNode instance = LoadInstanceFromFrame(); TNode context = LoadContextFromInstance(instance); TailCallRuntime(Runtime::kThrowWasmStackOverflow, context); } TF_BUILTIN(WasmThrow, WasmBuiltinsAssembler) { TNode exception = CAST(Parameter(Descriptor::kException)); TNode instance = LoadInstanceFromFrame(); TNode context = LoadContextFromInstance(instance); TailCallRuntime(Runtime::kThrow, context, exception); } TF_BUILTIN(WasmRethrow, WasmBuiltinsAssembler) { TNode exception = CAST(Parameter(Descriptor::kException)); TNode instance = LoadInstanceFromFrame(); TNode context = LoadContextFromInstance(instance); Label nullref(this, Label::kDeferred); GotoIf(TaggedEqual(NullConstant(), exception), &nullref); TailCallRuntime(Runtime::kReThrow, context, exception); BIND(&nullref); MessageTemplate message_id = MessageTemplate::kWasmTrapRethrowNullRef; TailCallRuntime(Runtime::kThrowWasmError, context, SmiConstant(static_cast(message_id))); } TF_BUILTIN(WasmTraceMemory, WasmBuiltinsAssembler) { TNode info = CAST(Parameter(Descriptor::kMemoryTracingInfo)); TNode instance = LoadInstanceFromFrame(); TNode context = LoadContextFromInstance(instance); TailCallRuntime(Runtime::kWasmTraceMemory, context, info); } TF_BUILTIN(WasmAtomicNotify, WasmBuiltinsAssembler) { TNode address = UncheckedCast(Parameter(Descriptor::kAddress)); TNode count = UncheckedCast(Parameter(Descriptor::kCount)); TNode instance = LoadInstanceFromFrame(); TNode address_number = ChangeUint32ToTagged(address); TNode count_number = ChangeUint32ToTagged(count); TNode context = LoadContextFromInstance(instance); TNode result_smi = CAST(CallRuntime(Runtime::kWasmAtomicNotify, context, instance, address_number, count_number)); Return(Unsigned(SmiToInt32(result_smi))); } TF_BUILTIN(WasmI32AtomicWait32, WasmBuiltinsAssembler) { if (!Is32()) { Unreachable(); return; } TNode address = UncheckedCast(Parameter(Descriptor::kAddress)); TNode address_number = ChangeUint32ToTagged(address); TNode expected_value = UncheckedCast(Parameter(Descriptor::kExpectedValue)); TNode expected_value_number = ChangeInt32ToTagged(expected_value); TNode timeout_low = UncheckedCast(Parameter(Descriptor::kTimeoutLow)); TNode timeout_high = UncheckedCast(Parameter(Descriptor::kTimeoutHigh)); TNode timeout = BigIntFromInt32Pair(timeout_low, timeout_high); TNode instance = LoadInstanceFromFrame(); TNode context = LoadContextFromInstance(instance); TNode result_smi = CAST(CallRuntime(Runtime::kWasmI32AtomicWait, context, instance, address_number, expected_value_number, timeout)); Return(Unsigned(SmiToInt32(result_smi))); } TF_BUILTIN(WasmI32AtomicWait64, WasmBuiltinsAssembler) { if (!Is64()) { Unreachable(); return; } TNode address = UncheckedCast(Parameter(Descriptor::kAddress)); TNode address_number = ChangeUint32ToTagged(address); TNode expected_value = UncheckedCast(Parameter(Descriptor::kExpectedValue)); TNode expected_value_number = ChangeInt32ToTagged(expected_value); TNode timeout_raw = UncheckedCast(Parameter(Descriptor::kTimeout)); TNode timeout = BigIntFromInt64(timeout_raw); TNode instance = LoadInstanceFromFrame(); TNode context = LoadContextFromInstance(instance); TNode result_smi = CAST(CallRuntime(Runtime::kWasmI32AtomicWait, context, instance, address_number, expected_value_number, timeout)); Return(Unsigned(SmiToInt32(result_smi))); } TF_BUILTIN(WasmI64AtomicWait32, WasmBuiltinsAssembler) { if (!Is32()) { Unreachable(); return; } TNode address = UncheckedCast(Parameter(Descriptor::kAddress)); TNode address_number = ChangeUint32ToTagged(address); TNode expected_value_low = UncheckedCast(Parameter(Descriptor::kExpectedValueLow)); TNode expected_value_high = UncheckedCast(Parameter(Descriptor::kExpectedValueHigh)); TNode expected_value = BigIntFromInt32Pair(expected_value_low, expected_value_high); TNode timeout_low = UncheckedCast(Parameter(Descriptor::kTimeoutLow)); TNode timeout_high = UncheckedCast(Parameter(Descriptor::kTimeoutHigh)); TNode timeout = BigIntFromInt32Pair(timeout_low, timeout_high); TNode instance = LoadInstanceFromFrame(); TNode context = LoadContextFromInstance(instance); TNode result_smi = CAST(CallRuntime(Runtime::kWasmI64AtomicWait, context, instance, address_number, expected_value, timeout)); Return(Unsigned(SmiToInt32(result_smi))); } TF_BUILTIN(WasmI64AtomicWait64, WasmBuiltinsAssembler) { if (!Is64()) { Unreachable(); return; } TNode address = UncheckedCast(Parameter(Descriptor::kAddress)); TNode address_number = ChangeUint32ToTagged(address); TNode expected_value_raw = UncheckedCast(Parameter(Descriptor::kExpectedValue)); TNode expected_value = BigIntFromInt64(expected_value_raw); TNode timeout_raw = UncheckedCast(Parameter(Descriptor::kTimeout)); TNode timeout = BigIntFromInt64(timeout_raw); TNode instance = LoadInstanceFromFrame(); TNode context = LoadContextFromInstance(instance); TNode result_smi = CAST(CallRuntime(Runtime::kWasmI64AtomicWait, context, instance, address_number, expected_value, timeout)); Return(Unsigned(SmiToInt32(result_smi))); } TF_BUILTIN(WasmMemoryGrow, WasmBuiltinsAssembler) { TNode num_pages = UncheckedCast(Parameter(Descriptor::kNumPages)); Label num_pages_out_of_range(this, Label::kDeferred); TNode num_pages_fits_in_smi = IsValidPositiveSmi(ChangeInt32ToIntPtr(num_pages)); GotoIfNot(num_pages_fits_in_smi, &num_pages_out_of_range); TNode num_pages_smi = SmiFromInt32(num_pages); TNode instance = LoadInstanceFromFrame(); TNode context = LoadContextFromInstance(instance); TNode ret_smi = CAST( CallRuntime(Runtime::kWasmMemoryGrow, context, instance, num_pages_smi)); Return(SmiToInt32(ret_smi)); BIND(&num_pages_out_of_range); Return(Int32Constant(-1)); } TF_BUILTIN(WasmTableInit, WasmBuiltinsAssembler) { TNode dst_raw = UncheckedCast(Parameter(Descriptor::kDestination)); // We cap {dst}, {src}, and {size} by {wasm::kV8MaxWasmTableSize + 1} to make // sure that the values fit into a Smi. STATIC_ASSERT(static_cast(Smi::kMaxValue) >= wasm::kV8MaxWasmTableSize + 1); constexpr uint32_t kCap = static_cast(wasm::kV8MaxWasmTableSize + 1); TNode dst = SmiFromUint32WithSaturation(dst_raw, kCap); TNode src_raw = UncheckedCast(Parameter(Descriptor::kSource)); TNode src = SmiFromUint32WithSaturation(src_raw, kCap); TNode size_raw = UncheckedCast(Parameter(Descriptor::kSize)); TNode size = SmiFromUint32WithSaturation(size_raw, kCap); TNode table_index = UncheckedCast(Parameter(Descriptor::kTableIndex)); TNode segment_index = UncheckedCast(Parameter(Descriptor::kSegmentIndex)); TNode instance = LoadInstanceFromFrame(); TNode context = LoadContextFromInstance(instance); TailCallRuntime(Runtime::kWasmTableInit, context, instance, table_index, segment_index, dst, src, size); } TF_BUILTIN(WasmTableCopy, WasmBuiltinsAssembler) { // We cap {dst}, {src}, and {size} by {wasm::kV8MaxWasmTableSize + 1} to make // sure that the values fit into a Smi. STATIC_ASSERT(static_cast(Smi::kMaxValue) >= wasm::kV8MaxWasmTableSize + 1); constexpr uint32_t kCap = static_cast(wasm::kV8MaxWasmTableSize + 1); TNode dst_raw = UncheckedCast(Parameter(Descriptor::kDestination)); TNode dst = SmiFromUint32WithSaturation(dst_raw, kCap); TNode src_raw = UncheckedCast(Parameter(Descriptor::kSource)); TNode src = SmiFromUint32WithSaturation(src_raw, kCap); TNode size_raw = UncheckedCast(Parameter(Descriptor::kSize)); TNode size = SmiFromUint32WithSaturation(size_raw, kCap); TNode dst_table = UncheckedCast(Parameter(Descriptor::kDestinationTable)); TNode src_table = UncheckedCast(Parameter(Descriptor::kSourceTable)); TNode instance = LoadInstanceFromFrame(); TNode context = LoadContextFromInstance(instance); TailCallRuntime(Runtime::kWasmTableCopy, context, instance, dst_table, src_table, dst, src, size); } TF_BUILTIN(WasmTableGet, WasmBuiltinsAssembler) { TNode entry_index = UncheckedCast(Parameter(Descriptor::kEntryIndex)); TNode instance = LoadInstanceFromFrame(); TNode context = LoadContextFromInstance(instance); Label entry_index_out_of_range(this, Label::kDeferred); TNode entry_index_fits_in_smi = IsValidPositiveSmi(ChangeInt32ToIntPtr(entry_index)); GotoIfNot(entry_index_fits_in_smi, &entry_index_out_of_range); TNode entry_index_smi = SmiFromInt32(entry_index); TNode table_index_smi = CAST(Parameter(Descriptor::kTableIndex)); TailCallRuntime(Runtime::kWasmFunctionTableGet, context, instance, table_index_smi, entry_index_smi); BIND(&entry_index_out_of_range); MessageTemplate message_id = wasm::WasmOpcodes::TrapReasonToMessageId(wasm::kTrapTableOutOfBounds); TailCallRuntime(Runtime::kThrowWasmError, context, SmiConstant(static_cast(message_id))); } TF_BUILTIN(WasmTableSet, WasmBuiltinsAssembler) { TNode entry_index = UncheckedCast(Parameter(Descriptor::kEntryIndex)); TNode instance = LoadInstanceFromFrame(); TNode context = LoadContextFromInstance(instance); Label entry_index_out_of_range(this, Label::kDeferred); TNode entry_index_fits_in_smi = IsValidPositiveSmi(ChangeInt32ToIntPtr(entry_index)); GotoIfNot(entry_index_fits_in_smi, &entry_index_out_of_range); TNode entry_index_smi = SmiFromInt32(entry_index); TNode table_index_smi = CAST(Parameter(Descriptor::kTableIndex)); TNode value = CAST(Parameter(Descriptor::kValue)); TailCallRuntime(Runtime::kWasmFunctionTableSet, context, instance, table_index_smi, entry_index_smi, value); BIND(&entry_index_out_of_range); MessageTemplate message_id = wasm::WasmOpcodes::TrapReasonToMessageId(wasm::kTrapTableOutOfBounds); TailCallRuntime(Runtime::kThrowWasmError, context, SmiConstant(static_cast(message_id))); } #define DECLARE_THROW_RUNTIME_FN(name) \ TF_BUILTIN(ThrowWasm##name, WasmBuiltinsAssembler) { \ TNode instance = LoadInstanceFromFrame(); \ TNode context = LoadContextFromInstance(instance); \ MessageTemplate message_id = \ wasm::WasmOpcodes::TrapReasonToMessageId(wasm::k##name); \ TailCallRuntime(Runtime::kThrowWasmError, context, \ SmiConstant(static_cast(message_id))); \ } FOREACH_WASM_TRAPREASON(DECLARE_THROW_RUNTIME_FN) #undef DECLARE_THROW_RUNTIME_FN } // namespace internal } // namespace v8