| // 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/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<Object> UncheckedParameter(int index) { |
| return UncheckedCast<Object>(Parameter(index)); |
| } |
| |
| TNode<Code> LoadBuiltinFromFrame(Builtins::Name id) { |
| TNode<Object> instance = LoadInstanceFromFrame(); |
| TNode<IntPtrT> isolate_root = UncheckedCast<IntPtrT>( |
| Load(MachineType::Pointer(), instance, |
| IntPtrConstant(WasmInstanceObject::kIsolateRootOffset - |
| kHeapObjectTag))); |
| TNode<Code> target = UncheckedCast<Code>( |
| Load(MachineType::TaggedPointer(), isolate_root, |
| IntPtrConstant(IsolateData::builtin_slot_offset(id)))); |
| return target; |
| } |
| |
| TNode<Object> LoadInstanceFromFrame() { |
| return UncheckedCast<Object>( |
| LoadFromParentFrame(WasmCompiledFrameConstants::kWasmInstanceOffset)); |
| } |
| |
| TNode<Object> LoadContextFromInstance(TNode<Object> instance) { |
| return UncheckedCast<Object>( |
| Load(MachineType::AnyTagged(), instance, |
| IntPtrConstant(WasmInstanceObject::kNativeContextOffset - |
| kHeapObjectTag))); |
| } |
| |
| TNode<Code> LoadCEntryFromInstance(TNode<Object> instance) { |
| return UncheckedCast<Code>( |
| Load(MachineType::AnyTagged(), instance, |
| IntPtrConstant(WasmInstanceObject::kCEntryStubOffset - |
| kHeapObjectTag))); |
| } |
| }; |
| |
| TF_BUILTIN(WasmAllocateHeapNumber, WasmBuiltinsAssembler) { |
| TNode<Code> target = LoadBuiltinFromFrame(Builtins::kAllocateHeapNumber); |
| TailCallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant()); |
| } |
| |
| TF_BUILTIN(WasmCallJavaScript, WasmBuiltinsAssembler) { |
| TNode<Object> context = UncheckedParameter(Descriptor::kContext); |
| TNode<Object> function = UncheckedParameter(Descriptor::kFunction); |
| TNode<Object> argc = UncheckedParameter(Descriptor::kActualArgumentsCount); |
| TNode<Code> target = LoadBuiltinFromFrame(Builtins::kCall_ReceiverIsAny); |
| TailCallStub(CallTrampolineDescriptor{}, target, context, function, argc); |
| } |
| |
| TF_BUILTIN(WasmRecordWrite, WasmBuiltinsAssembler) { |
| TNode<Object> object = UncheckedParameter(Descriptor::kObject); |
| TNode<Object> slot = UncheckedParameter(Descriptor::kSlot); |
| TNode<Object> remembered = UncheckedParameter(Descriptor::kRememberedSet); |
| TNode<Object> fp_mode = UncheckedParameter(Descriptor::kFPMode); |
| TNode<Code> target = LoadBuiltinFromFrame(Builtins::kRecordWrite); |
| TailCallStub(RecordWriteDescriptor{}, target, NoContextConstant(), object, |
| slot, remembered, fp_mode); |
| } |
| |
| TF_BUILTIN(WasmToNumber, WasmBuiltinsAssembler) { |
| TNode<Object> context = UncheckedParameter(Descriptor::kContext); |
| TNode<Object> argument = UncheckedParameter(Descriptor::kArgument); |
| TNode<Code> target = LoadBuiltinFromFrame(Builtins::kToNumber); |
| TailCallStub(TypeConversionDescriptor(), target, context, argument); |
| } |
| |
| TF_BUILTIN(WasmStackGuard, WasmBuiltinsAssembler) { |
| TNode<Object> instance = LoadInstanceFromFrame(); |
| TNode<Code> centry = LoadCEntryFromInstance(instance); |
| TNode<Object> context = LoadContextFromInstance(instance); |
| TailCallRuntimeWithCEntry(Runtime::kWasmStackGuard, centry, context); |
| } |
| |
| TF_BUILTIN(WasmStackOverflow, WasmBuiltinsAssembler) { |
| TNode<Object> instance = LoadInstanceFromFrame(); |
| TNode<Code> centry = LoadCEntryFromInstance(instance); |
| TNode<Object> context = LoadContextFromInstance(instance); |
| TailCallRuntimeWithCEntry(Runtime::kThrowWasmStackOverflow, centry, context); |
| } |
| |
| TF_BUILTIN(WasmThrow, WasmBuiltinsAssembler) { |
| TNode<Object> exception = UncheckedParameter(Descriptor::kException); |
| TNode<Object> instance = LoadInstanceFromFrame(); |
| TNode<Code> centry = LoadCEntryFromInstance(instance); |
| TNode<Object> context = LoadContextFromInstance(instance); |
| TailCallRuntimeWithCEntry(Runtime::kThrow, centry, context, exception); |
| } |
| |
| TF_BUILTIN(WasmRethrow, WasmBuiltinsAssembler) { |
| TNode<Object> exception = UncheckedParameter(Descriptor::kException); |
| TNode<Object> instance = LoadInstanceFromFrame(); |
| TNode<Code> centry = LoadCEntryFromInstance(instance); |
| TNode<Object> context = LoadContextFromInstance(instance); |
| TailCallRuntimeWithCEntry(Runtime::kReThrow, centry, context, exception); |
| } |
| |
| TF_BUILTIN(WasmAtomicNotify, WasmBuiltinsAssembler) { |
| TNode<Uint32T> address = |
| UncheckedCast<Uint32T>(Parameter(Descriptor::kAddress)); |
| TNode<Uint32T> count = UncheckedCast<Uint32T>(Parameter(Descriptor::kCount)); |
| |
| TNode<Object> instance = LoadInstanceFromFrame(); |
| TNode<Code> centry = LoadCEntryFromInstance(instance); |
| |
| TNode<Code> target = LoadBuiltinFromFrame(Builtins::kAllocateHeapNumber); |
| |
| // TODO(aseemgarg): Use SMIs if possible for address and count |
| TNode<HeapNumber> address_heap = UncheckedCast<HeapNumber>( |
| CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); |
| StoreHeapNumberValue(address_heap, ChangeUint32ToFloat64(address)); |
| |
| TNode<HeapNumber> count_heap = UncheckedCast<HeapNumber>( |
| CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); |
| StoreHeapNumberValue(count_heap, ChangeUint32ToFloat64(count)); |
| |
| TNode<Smi> result_smi = UncheckedCast<Smi>(CallRuntimeWithCEntry( |
| Runtime::kWasmAtomicNotify, centry, NoContextConstant(), instance, |
| address_heap, count_heap)); |
| ReturnRaw(SmiToInt32(result_smi)); |
| } |
| |
| TF_BUILTIN(WasmI32AtomicWait, WasmBuiltinsAssembler) { |
| TNode<Uint32T> address = |
| UncheckedCast<Uint32T>(Parameter(Descriptor::kAddress)); |
| TNode<Int32T> expected_value = |
| UncheckedCast<Int32T>(Parameter(Descriptor::kExpectedValue)); |
| TNode<Float64T> timeout = |
| UncheckedCast<Float64T>(Parameter(Descriptor::kTimeout)); |
| |
| TNode<Object> instance = LoadInstanceFromFrame(); |
| TNode<Code> centry = LoadCEntryFromInstance(instance); |
| |
| TNode<Code> target = LoadBuiltinFromFrame(Builtins::kAllocateHeapNumber); |
| |
| // TODO(aseemgarg): Use SMIs if possible for address and expected_value |
| TNode<HeapNumber> address_heap = UncheckedCast<HeapNumber>( |
| CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); |
| StoreHeapNumberValue(address_heap, ChangeUint32ToFloat64(address)); |
| |
| TNode<HeapNumber> expected_value_heap = UncheckedCast<HeapNumber>( |
| CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); |
| StoreHeapNumberValue(expected_value_heap, |
| ChangeInt32ToFloat64(expected_value)); |
| |
| TNode<HeapNumber> timeout_heap = UncheckedCast<HeapNumber>( |
| CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); |
| StoreHeapNumberValue(timeout_heap, timeout); |
| |
| TNode<Smi> result_smi = UncheckedCast<Smi>(CallRuntimeWithCEntry( |
| Runtime::kWasmI32AtomicWait, centry, NoContextConstant(), instance, |
| address_heap, expected_value_heap, timeout_heap)); |
| ReturnRaw(SmiToInt32(result_smi)); |
| } |
| |
| TF_BUILTIN(WasmI64AtomicWait, WasmBuiltinsAssembler) { |
| TNode<Uint32T> address = |
| UncheckedCast<Uint32T>(Parameter(Descriptor::kAddress)); |
| TNode<Uint32T> expected_value_high = |
| UncheckedCast<Uint32T>(Parameter(Descriptor::kExpectedValueHigh)); |
| TNode<Uint32T> expected_value_low = |
| UncheckedCast<Uint32T>(Parameter(Descriptor::kExpectedValueLow)); |
| TNode<Float64T> timeout = |
| UncheckedCast<Float64T>(Parameter(Descriptor::kTimeout)); |
| |
| TNode<Object> instance = LoadInstanceFromFrame(); |
| TNode<Code> centry = LoadCEntryFromInstance(instance); |
| |
| TNode<Code> target = LoadBuiltinFromFrame(Builtins::kAllocateHeapNumber); |
| |
| // TODO(aseemgarg): Use SMIs if possible for address and expected_value |
| TNode<HeapNumber> address_heap = UncheckedCast<HeapNumber>( |
| CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); |
| StoreHeapNumberValue(address_heap, ChangeUint32ToFloat64(address)); |
| |
| TNode<HeapNumber> expected_value_high_heap = UncheckedCast<HeapNumber>( |
| CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); |
| StoreHeapNumberValue(expected_value_high_heap, |
| ChangeUint32ToFloat64(expected_value_high)); |
| |
| TNode<HeapNumber> expected_value_low_heap = UncheckedCast<HeapNumber>( |
| CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); |
| StoreHeapNumberValue(expected_value_low_heap, |
| ChangeUint32ToFloat64(expected_value_low)); |
| |
| TNode<HeapNumber> timeout_heap = UncheckedCast<HeapNumber>( |
| CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); |
| StoreHeapNumberValue(timeout_heap, timeout); |
| |
| TNode<Smi> result_smi = UncheckedCast<Smi>(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<Int32T> num_pages = |
| UncheckedCast<Int32T>(Parameter(Descriptor::kNumPages)); |
| Label num_pages_out_of_range(this, Label::kDeferred); |
| |
| TNode<BoolT> num_pages_fits_in_smi = |
| IsValidPositiveSmi(ChangeInt32ToIntPtr(num_pages)); |
| GotoIfNot(num_pages_fits_in_smi, &num_pages_out_of_range); |
| |
| TNode<Smi> num_pages_smi = SmiFromInt32(num_pages); |
| TNode<Object> instance = LoadInstanceFromFrame(); |
| TNode<Code> centry = LoadCEntryFromInstance(instance); |
| TNode<Object> context = LoadContextFromInstance(instance); |
| TNode<Smi> ret_smi = UncheckedCast<Smi>(CallRuntimeWithCEntry( |
| Runtime::kWasmMemoryGrow, centry, context, instance, num_pages_smi)); |
| TNode<Int32T> ret = SmiToInt32(ret_smi); |
| ReturnRaw(ret); |
| |
| BIND(&num_pages_out_of_range); |
| ReturnRaw(Int32Constant(-1)); |
| } |
| |
| TF_BUILTIN(WasmTableGet, WasmBuiltinsAssembler) { |
| TNode<Int32T> entry_index = |
| UncheckedCast<Int32T>(Parameter(Descriptor::kEntryIndex)); |
| TNode<Object> instance = LoadInstanceFromFrame(); |
| TNode<Code> centry = LoadCEntryFromInstance(instance); |
| TNode<Object> context = LoadContextFromInstance(instance); |
| Label entry_index_out_of_range(this, Label::kDeferred); |
| |
| TNode<BoolT> entry_index_fits_in_smi = |
| IsValidPositiveSmi(ChangeInt32ToIntPtr(entry_index)); |
| GotoIfNot(entry_index_fits_in_smi, &entry_index_out_of_range); |
| |
| TNode<Smi> entry_index_smi = SmiFromInt32(entry_index); |
| TNode<Smi> table_index_smi = |
| UncheckedCast<Smi>(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<int>(message_id))); |
| } |
| |
| TF_BUILTIN(WasmTableSet, WasmBuiltinsAssembler) { |
| TNode<Int32T> entry_index = |
| UncheckedCast<Int32T>(Parameter(Descriptor::kEntryIndex)); |
| TNode<Object> instance = LoadInstanceFromFrame(); |
| TNode<Code> centry = LoadCEntryFromInstance(instance); |
| TNode<Object> context = LoadContextFromInstance(instance); |
| Label entry_index_out_of_range(this, Label::kDeferred); |
| |
| TNode<BoolT> entry_index_fits_in_smi = |
| IsValidPositiveSmi(ChangeInt32ToIntPtr(entry_index)); |
| GotoIfNot(entry_index_fits_in_smi, &entry_index_out_of_range); |
| |
| TNode<Smi> entry_index_smi = SmiFromInt32(entry_index); |
| TNode<Smi> table_index_smi = |
| UncheckedCast<Smi>(Parameter(Descriptor::kTableIndex)); |
| TNode<Object> value = UncheckedCast<Object>(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<int>(message_id))); |
| } |
| |
| TF_BUILTIN(WasmI64ToBigInt, WasmBuiltinsAssembler) { |
| if (!Is64()) { |
| Unreachable(); |
| return; |
| } |
| |
| TNode<Code> target = LoadBuiltinFromFrame(Builtins::kI64ToBigInt); |
| TNode<IntPtrT> argument = |
| UncheckedCast<IntPtrT>(Parameter(Descriptor::kArgument)); |
| |
| TailCallStub(I64ToBigIntDescriptor(), target, NoContextConstant(), argument); |
| } |
| |
| TF_BUILTIN(WasmBigIntToI64, WasmBuiltinsAssembler) { |
| if (!Is64()) { |
| Unreachable(); |
| return; |
| } |
| |
| TNode<Object> context = |
| UncheckedCast<Object>(Parameter(Descriptor::kContext)); |
| TNode<Code> target = LoadBuiltinFromFrame(Builtins::kBigIntToI64); |
| TNode<IntPtrT> argument = |
| UncheckedCast<IntPtrT>(Parameter(Descriptor::kArgument)); |
| |
| TailCallStub(BigIntToI64Descriptor(), target, context, argument); |
| } |
| |
| #define DECLARE_ENUM(name) \ |
| TF_BUILTIN(ThrowWasm##name, WasmBuiltinsAssembler) { \ |
| TNode<Object> instance = LoadInstanceFromFrame(); \ |
| TNode<Code> centry = LoadCEntryFromInstance(instance); \ |
| TNode<Object> context = LoadContextFromInstance(instance); \ |
| MessageTemplate message_id = \ |
| wasm::WasmOpcodes::TrapReasonToMessageId(wasm::k##name); \ |
| TailCallRuntimeWithCEntry(Runtime::kThrowWasmError, centry, context, \ |
| SmiConstant(static_cast<int>(message_id))); \ |
| } |
| FOREACH_WASM_TRAPREASON(DECLARE_ENUM) |
| #undef DECLARE_ENUM |
| |
| } // namespace internal |
| } // namespace v8 |