// 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 UncheckedParameter(int index) { return UncheckedCast(Parameter(index)); } TNode LoadBuiltinFromFrame(Builtins::Name id) { TNode instance = LoadInstanceFromFrame(); TNode isolate_root = UncheckedCast( Load(MachineType::Pointer(), instance, IntPtrConstant(WasmInstanceObject::kIsolateRootOffset - kHeapObjectTag))); TNode target = UncheckedCast( Load(MachineType::TaggedPointer(), isolate_root, IntPtrConstant(IsolateData::builtin_slot_offset(id)))); return target; } TNode LoadInstanceFromFrame() { return UncheckedCast( LoadFromParentFrame(WasmCompiledFrameConstants::kWasmInstanceOffset)); } TNode LoadContextFromInstance(TNode instance) { return UncheckedCast( Load(MachineType::AnyTagged(), instance, IntPtrConstant(WasmInstanceObject::kNativeContextOffset - kHeapObjectTag))); } TNode LoadCEntryFromInstance(TNode instance) { TNode isolate_root = UncheckedCast( Load(MachineType::Pointer(), instance, IntPtrConstant(WasmInstanceObject::kIsolateRootOffset - kHeapObjectTag))); auto centry_id = Builtins::kCEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit; TNode target = UncheckedCast( Load(MachineType::TaggedPointer(), isolate_root, IntPtrConstant(IsolateData::builtin_slot_offset(centry_id)))); return target; } }; TF_BUILTIN(WasmAllocateHeapNumber, WasmBuiltinsAssembler) { TNode target = LoadBuiltinFromFrame(Builtins::kAllocateHeapNumber); TailCallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant()); } TF_BUILTIN(WasmRecordWrite, WasmBuiltinsAssembler) { TNode object = UncheckedParameter(Descriptor::kObject); TNode slot = UncheckedParameter(Descriptor::kSlot); TNode remembered = UncheckedParameter(Descriptor::kRememberedSet); TNode fp_mode = UncheckedParameter(Descriptor::kFPMode); TNode target = LoadBuiltinFromFrame(Builtins::kRecordWrite); TailCallStub(RecordWriteDescriptor{}, target, NoContextConstant(), object, slot, remembered, fp_mode); } TF_BUILTIN(WasmToNumber, WasmBuiltinsAssembler) { TNode context = UncheckedParameter(Descriptor::kContext); TNode argument = UncheckedParameter(Descriptor::kArgument); TNode target = LoadBuiltinFromFrame(Builtins::kToNumber); TailCallStub(TypeConversionDescriptor(), target, context, argument); } TF_BUILTIN(WasmStackGuard, WasmBuiltinsAssembler) { TNode instance = LoadInstanceFromFrame(); TNode centry = LoadCEntryFromInstance(instance); TNode context = LoadContextFromInstance(instance); TailCallRuntimeWithCEntry(Runtime::kWasmStackGuard, centry, context); } TF_BUILTIN(WasmStackOverflow, WasmBuiltinsAssembler) { TNode instance = LoadInstanceFromFrame(); TNode centry = LoadCEntryFromInstance(instance); TNode context = LoadContextFromInstance(instance); TailCallRuntimeWithCEntry(Runtime::kThrowWasmStackOverflow, centry, context); } TF_BUILTIN(WasmThrow, WasmBuiltinsAssembler) { TNode exception = UncheckedParameter(Descriptor::kException); TNode instance = LoadInstanceFromFrame(); TNode centry = LoadCEntryFromInstance(instance); TNode context = LoadContextFromInstance(instance); TailCallRuntimeWithCEntry(Runtime::kThrow, centry, context, exception); } TF_BUILTIN(WasmRethrow, WasmBuiltinsAssembler) { TNode exception = UncheckedParameter(Descriptor::kException); TNode instance = LoadInstanceFromFrame(); TNode centry = LoadCEntryFromInstance(instance); TNode context = LoadContextFromInstance(instance); TailCallRuntimeWithCEntry(Runtime::kReThrow, centry, context, exception); } TF_BUILTIN(WasmAtomicNotify, WasmBuiltinsAssembler) { TNode address = UncheckedCast(Parameter(Descriptor::kAddress)); TNode count = UncheckedCast(Parameter(Descriptor::kCount)); TNode instance = LoadInstanceFromFrame(); TNode centry = LoadCEntryFromInstance(instance); TNode target = LoadBuiltinFromFrame(Builtins::kAllocateHeapNumber); // TODO(aseemgarg): Use SMIs if possible for address and count TNode address_heap = UncheckedCast( CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); StoreHeapNumberValue(address_heap, ChangeUint32ToFloat64(address)); TNode count_heap = UncheckedCast( CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); StoreHeapNumberValue(count_heap, ChangeUint32ToFloat64(count)); TNode result_smi = UncheckedCast(CallRuntimeWithCEntry( Runtime::kWasmAtomicNotify, centry, NoContextConstant(), instance, address_heap, count_heap)); ReturnRaw(SmiToInt32(result_smi)); } TF_BUILTIN(WasmI32AtomicWait, WasmBuiltinsAssembler) { TNode address = UncheckedCast(Parameter(Descriptor::kAddress)); TNode expected_value = UncheckedCast(Parameter(Descriptor::kExpectedValue)); TNode timeout = UncheckedCast(Parameter(Descriptor::kTimeout)); TNode instance = LoadInstanceFromFrame(); TNode centry = LoadCEntryFromInstance(instance); TNode target = LoadBuiltinFromFrame(Builtins::kAllocateHeapNumber); // TODO(aseemgarg): Use SMIs if possible for address and expected_value TNode address_heap = UncheckedCast( CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); StoreHeapNumberValue(address_heap, ChangeUint32ToFloat64(address)); TNode expected_value_heap = UncheckedCast( CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); StoreHeapNumberValue(expected_value_heap, ChangeInt32ToFloat64(expected_value)); TNode timeout_heap = UncheckedCast( CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); StoreHeapNumberValue(timeout_heap, timeout); TNode result_smi = UncheckedCast(CallRuntimeWithCEntry( Runtime::kWasmI32AtomicWait, centry, NoContextConstant(), instance, address_heap, expected_value_heap, timeout_heap)); ReturnRaw(SmiToInt32(result_smi)); } TF_BUILTIN(WasmI64AtomicWait, WasmBuiltinsAssembler) { TNode address = UncheckedCast(Parameter(Descriptor::kAddress)); TNode expected_value_high = UncheckedCast(Parameter(Descriptor::kExpectedValueHigh)); TNode expected_value_low = UncheckedCast(Parameter(Descriptor::kExpectedValueLow)); TNode timeout = UncheckedCast(Parameter(Descriptor::kTimeout)); TNode instance = LoadInstanceFromFrame(); TNode centry = LoadCEntryFromInstance(instance); TNode target = LoadBuiltinFromFrame(Builtins::kAllocateHeapNumber); // TODO(aseemgarg): Use SMIs if possible for address and expected_value TNode address_heap = UncheckedCast( CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); StoreHeapNumberValue(address_heap, ChangeUint32ToFloat64(address)); TNode expected_value_high_heap = UncheckedCast( CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); StoreHeapNumberValue(expected_value_high_heap, ChangeUint32ToFloat64(expected_value_high)); TNode expected_value_low_heap = UncheckedCast( CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); StoreHeapNumberValue(expected_value_low_heap, ChangeUint32ToFloat64(expected_value_low)); TNode timeout_heap = UncheckedCast( CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); StoreHeapNumberValue(timeout_heap, timeout); TNode result_smi = UncheckedCast(CallRuntimeWithCEntry( Runtime::kWasmI64AtomicWait, centry, NoContextConstant(), instance, address_heap, expected_value_high_heap, expected_value_low_heap, timeout_heap)); ReturnRaw(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 centry = LoadCEntryFromInstance(instance); TNode context = LoadContextFromInstance(instance); TNode ret_smi = UncheckedCast(CallRuntimeWithCEntry( Runtime::kWasmMemoryGrow, centry, context, instance, num_pages_smi)); TNode ret = SmiToInt32(ret_smi); ReturnRaw(ret); BIND(&num_pages_out_of_range); ReturnRaw(Int32Constant(-1)); } TF_BUILTIN(WasmTableGet, WasmBuiltinsAssembler) { TNode entry_index = UncheckedCast(Parameter(Descriptor::kEntryIndex)); TNode instance = LoadInstanceFromFrame(); TNode centry = LoadCEntryFromInstance(instance); 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 = UncheckedCast(Parameter(Descriptor::kTableIndex)); TailCallRuntimeWithCEntry(Runtime::kWasmFunctionTableGet, centry, context, instance, table_index_smi, entry_index_smi); BIND(&entry_index_out_of_range); MessageTemplate message_id = wasm::WasmOpcodes::TrapReasonToMessageId(wasm::kTrapTableOutOfBounds); TailCallRuntimeWithCEntry(Runtime::kThrowWasmError, centry, context, SmiConstant(static_cast(message_id))); } TF_BUILTIN(WasmTableSet, WasmBuiltinsAssembler) { TNode entry_index = UncheckedCast(Parameter(Descriptor::kEntryIndex)); TNode instance = LoadInstanceFromFrame(); TNode centry = LoadCEntryFromInstance(instance); 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 = UncheckedCast(Parameter(Descriptor::kTableIndex)); TNode value = UncheckedCast(Parameter(Descriptor::kValue)); TailCallRuntimeWithCEntry(Runtime::kWasmFunctionTableSet, centry, context, instance, table_index_smi, entry_index_smi, value); BIND(&entry_index_out_of_range); MessageTemplate message_id = wasm::WasmOpcodes::TrapReasonToMessageId(wasm::kTrapTableOutOfBounds); TailCallRuntimeWithCEntry(Runtime::kThrowWasmError, centry, context, SmiConstant(static_cast(message_id))); } TF_BUILTIN(WasmI64ToBigInt, WasmBuiltinsAssembler) { if (!Is64()) { Unreachable(); return; } TNode target = LoadBuiltinFromFrame(Builtins::kI64ToBigInt); TNode argument = UncheckedCast(Parameter(Descriptor::kArgument)); TailCallStub(I64ToBigIntDescriptor(), target, NoContextConstant(), argument); } TF_BUILTIN(WasmI32PairToBigInt, WasmBuiltinsAssembler) { if (!Is32()) { Unreachable(); return; } TNode target = LoadBuiltinFromFrame(Builtins::kI32PairToBigInt); TNode low = UncheckedCast(Parameter(Descriptor::kLow)); TNode high = UncheckedCast(Parameter(Descriptor::kHigh)); TailCallStub(I32PairToBigIntDescriptor(), target, NoContextConstant(), low, high); } TF_BUILTIN(WasmBigIntToI64, WasmBuiltinsAssembler) { if (!Is64()) { Unreachable(); return; } TNode context = UncheckedCast(Parameter(Descriptor::kContext)); TNode target = LoadBuiltinFromFrame(Builtins::kBigIntToI64); TNode argument = UncheckedCast(Parameter(Descriptor::kArgument)); TailCallStub(BigIntToI64Descriptor(), target, context, argument); } TF_BUILTIN(WasmBigIntToI32Pair, WasmBuiltinsAssembler) { if (!Is32()) { Unreachable(); return; } TNode context = UncheckedCast(Parameter(Descriptor::kContext)); TNode target = LoadBuiltinFromFrame(Builtins::kBigIntToI32Pair); TNode argument = UncheckedCast(Parameter(Descriptor::kArgument)); TailCallStub(BigIntToI32PairDescriptor(), target, context, argument); } #define DECLARE_ENUM(name) \ TF_BUILTIN(ThrowWasm##name, WasmBuiltinsAssembler) { \ TNode instance = LoadInstanceFromFrame(); \ TNode centry = LoadCEntryFromInstance(instance); \ TNode context = LoadContextFromInstance(instance); \ MessageTemplate message_id = \ wasm::WasmOpcodes::TrapReasonToMessageId(wasm::k##name); \ TailCallRuntimeWithCEntry(Runtime::kThrowWasmError, centry, context, \ SmiConstant(static_cast(message_id))); \ } FOREACH_WASM_TRAPREASON(DECLARE_ENUM) #undef DECLARE_ENUM } // namespace internal } // namespace v8