blob: 26b6dd70548484782d03bb1c9c767cc4e975c96a [file] [log] [blame]
// Copyright 2022 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/compiler/wasm-graph-assembler.h"
#include "src/common/globals.h"
#include "src/compiler/access-builder.h"
#include "src/compiler/diamond.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/wasm-compiler-definitions.h"
#include "src/objects/string.h"
#include "src/wasm/object-access.h"
#include "src/wasm/wasm-objects.h"
namespace v8::internal::compiler {
// static
CallDescriptor* GetBuiltinCallDescriptor(Builtin name, Zone* zone,
StubCallMode stub_mode,
bool needs_frame_state,
Operator::Properties properties) {
CallInterfaceDescriptor interface_descriptor =
Builtins::CallInterfaceDescriptorFor(name);
return Linkage::GetStubCallDescriptor(
zone, // zone
interface_descriptor, // descriptor
interface_descriptor.GetStackParameterCount(), // stack parameter count
needs_frame_state ? CallDescriptor::kNeedsFrameState
: CallDescriptor::kNoFlags, // flags
properties, // properties
stub_mode); // stub call mode
}
// static
ObjectAccess ObjectAccessForGCStores(wasm::ValueType type) {
return ObjectAccess(
MachineType::TypeForRepresentation(type.machine_representation(),
!type.is_packed()),
type.is_reference() ? kFullWriteBarrier : kNoWriteBarrier);
}
// Sets {true_node} and {false_node} to their corresponding Branch outputs.
// Returns the Branch node. Does not change control().
Node* WasmGraphAssembler::Branch(Node* cond, Node** true_node,
Node** false_node, BranchHint hint) {
DCHECK_NOT_NULL(cond);
Node* branch =
graph()->NewNode(mcgraph()->common()->Branch(hint), cond, control());
*true_node = graph()->NewNode(mcgraph()->common()->IfTrue(), branch);
*false_node = graph()->NewNode(mcgraph()->common()->IfFalse(), branch);
return branch;
}
Node* WasmGraphAssembler::BuildTruncateIntPtrToInt32(Node* value) {
return mcgraph()->machine()->Is64() ? TruncateInt64ToInt32(value) : value;
}
Node* WasmGraphAssembler::BuildChangeInt32ToIntPtr(Node* value) {
return mcgraph()->machine()->Is64() ? ChangeInt32ToInt64(value) : value;
}
Node* WasmGraphAssembler::BuildChangeIntPtrToInt64(Node* value) {
return mcgraph()->machine()->Is32() ? ChangeInt32ToInt64(value) : value;
}
Node* WasmGraphAssembler::BuildChangeUint32ToUintPtr(Node* node) {
if (mcgraph()->machine()->Is32()) return node;
// Fold instances of ChangeUint32ToUint64(IntConstant) directly.
Uint32Matcher matcher(node);
if (matcher.HasResolvedValue()) {
uintptr_t value = matcher.ResolvedValue();
return mcgraph()->IntPtrConstant(base::bit_cast<intptr_t>(value));
}
return ChangeUint32ToUint64(node);
}
Node* WasmGraphAssembler::BuildSmiShiftBitsConstant() {
return IntPtrConstant(kSmiShiftSize + kSmiTagSize);
}
Node* WasmGraphAssembler::BuildSmiShiftBitsConstant32() {
return Int32Constant(kSmiShiftSize + kSmiTagSize);
}
Node* WasmGraphAssembler::BuildChangeInt32ToSmi(Node* value) {
// With pointer compression, only the lower 32 bits are used.
return COMPRESS_POINTERS_BOOL ? BitcastWord32ToWord64(Word32Shl(
value, BuildSmiShiftBitsConstant32()))
: WordShl(BuildChangeInt32ToIntPtr(value),
BuildSmiShiftBitsConstant());
}
Node* WasmGraphAssembler::BuildChangeUint31ToSmi(Node* value) {
return COMPRESS_POINTERS_BOOL
? Word32Shl(value, BuildSmiShiftBitsConstant32())
: WordShl(BuildChangeUint32ToUintPtr(value),
BuildSmiShiftBitsConstant());
}
Node* WasmGraphAssembler::BuildChangeSmiToInt32(Node* value) {
return COMPRESS_POINTERS_BOOL
? Word32Sar(value, BuildSmiShiftBitsConstant32())
: BuildTruncateIntPtrToInt32(
WordSar(value, BuildSmiShiftBitsConstant()));
}
Node* WasmGraphAssembler::BuildConvertUint32ToSmiWithSaturation(
Node* value, uint32_t maxval) {
DCHECK(Smi::IsValid(maxval));
Node* max = mcgraph()->Uint32Constant(maxval);
Node* check = Uint32LessThanOrEqual(value, max);
Node* valsmi = BuildChangeUint31ToSmi(value);
Node* maxsmi = NumberConstant(maxval);
Diamond d(graph(), mcgraph()->common(), check, BranchHint::kTrue);
d.Chain(control());
return d.Phi(MachineRepresentation::kTagged, valsmi, maxsmi);
}
Node* WasmGraphAssembler::BuildChangeSmiToIntPtr(Node* value) {
return COMPRESS_POINTERS_BOOL ? BuildChangeInt32ToIntPtr(Word32Sar(
value, BuildSmiShiftBitsConstant32()))
: WordSar(value, BuildSmiShiftBitsConstant());
}
// Helper functions for dealing with HeapObjects.
// Rule of thumb: if access to a given field in an object is required in
// at least two places, put a helper function here.
Node* WasmGraphAssembler::Allocate(int size) {
return Allocate(Int32Constant(size));
}
Node* WasmGraphAssembler::Allocate(Node* size) {
return AddNode(graph()->NewNode(
simplified_.AllocateRaw(Type::Any(), AllocationType::kYoung), size,
effect(), control()));
}
Node* WasmGraphAssembler::LoadFromObject(MachineType type, Node* base,
Node* offset) {
return AddNode(graph()->NewNode(
simplified_.LoadFromObject(ObjectAccess(type, kNoWriteBarrier)), base,
offset, effect(), control()));
}
Node* WasmGraphAssembler::LoadImmutableFromObject(MachineType type, Node* base,
Node* offset) {
return AddNode(graph()->NewNode(
simplified_.LoadImmutableFromObject(ObjectAccess(type, kNoWriteBarrier)),
base, offset, effect(), control()));
}
Node* WasmGraphAssembler::LoadImmutable(LoadRepresentation rep, Node* base,
Node* offset) {
return AddNode(
graph()->NewNode(mcgraph()->machine()->LoadImmutable(rep), base, offset));
}
Node* WasmGraphAssembler::StoreToObject(ObjectAccess access, Node* base,
Node* offset, Node* value) {
return AddNode(graph()->NewNode(simplified_.StoreToObject(access), base,
offset, value, effect(), control()));
}
Node* WasmGraphAssembler::InitializeImmutableInObject(ObjectAccess access,
Node* base, Node* offset,
Node* value) {
return AddNode(
graph()->NewNode(simplified_.InitializeImmutableInObject(access), base,
offset, value, effect(), control()));
}
Node* WasmGraphAssembler::BuildDecodeSandboxedExternalPointer(
Node* handle, ExternalPointerTag tag, Node* isolate_root) {
#if V8_ENABLE_SANDBOX
Node* index = Word32Shr(handle, Int32Constant(kExternalPointerIndexShift));
Node* offset = ChangeUint32ToUint64(
Word32Shl(index, Int32Constant(kExternalPointerTableEntrySizeLog2)));
Node* table;
if (IsSharedExternalPointerType(tag)) {
Node* table_address =
Load(MachineType::Pointer(), isolate_root,
IsolateData::shared_external_pointer_table_offset());
table = Load(MachineType::Pointer(), table_address,
Internals::kExternalPointerTableBasePointerOffset);
} else {
table = Load(MachineType::Pointer(), isolate_root,
IsolateData::external_pointer_table_offset() +
Internals::kExternalPointerTableBasePointerOffset);
}
Node* decoded_ptr = Load(MachineType::Pointer(), table, offset);
return WordAnd(decoded_ptr, IntPtrConstant(~tag));
#else
UNREACHABLE();
#endif // V8_ENABLE_SANDBOX
}
Node* WasmGraphAssembler::BuildDecodeTrustedPointer(Node* handle,
IndirectPointerTag tag) {
#if V8_ENABLE_SANDBOX
Node* index = Word32Shr(handle, Int32Constant(kTrustedPointerHandleShift));
Node* offset = ChangeUint32ToUint64(
Word32Shl(index, Int32Constant(kTrustedPointerTableEntrySizeLog2)));
Node* table = Load(MachineType::Pointer(), LoadRootRegister(),
IsolateData::trusted_pointer_table_offset() +
Internals::kTrustedPointerTableBasePointerOffset);
Node* decoded_ptr = Load(MachineType::Pointer(), table, offset);
// Mask out the tag.
// TODO(saelo): Enable this once we tag pointers in the trusted table.
// decoded_ptr = WordAnd(decoded_ptr, IntPtrConstant(~tag));
// Always set the tagged bit, used as a marking bit in that table.
decoded_ptr = WordOr(decoded_ptr, IntPtrConstant(kHeapObjectTag));
return decoded_ptr;
#else
UNREACHABLE();
#endif // V8_ENABLE_SANDBOX
}
Node* WasmGraphAssembler::BuildLoadExternalPointerFromObject(
Node* object, int field_offset, ExternalPointerTag tag,
Node* isolate_root) {
#ifdef V8_ENABLE_SANDBOX
DCHECK_NE(tag, kExternalPointerNullTag);
Node* handle = LoadFromObject(MachineType::Uint32(), object,
wasm::ObjectAccess::ToTagged(field_offset));
return BuildDecodeSandboxedExternalPointer(handle, tag, isolate_root);
#else
return LoadFromObject(MachineType::Pointer(), object,
wasm::ObjectAccess::ToTagged(field_offset));
#endif // V8_ENABLE_SANDBOX
}
Node* WasmGraphAssembler::IsSmi(Node* object) {
if (COMPRESS_POINTERS_BOOL) {
return Word32Equal(Word32And(object, Int32Constant(kSmiTagMask)),
Int32Constant(kSmiTag));
} else {
return WordEqual(WordAnd(object, IntPtrConstant(kSmiTagMask)),
IntPtrConstant(kSmiTag));
}
}
// Maps and their contents.
Node* WasmGraphAssembler::LoadMap(Node* object) {
Node* map_word =
LoadImmutableFromObject(MachineType::TaggedPointer(), object,
HeapObject::kMapOffset - kHeapObjectTag);
#ifdef V8_MAP_PACKING
return UnpackMapWord(map_word);
#else
return map_word;
#endif
}
void WasmGraphAssembler::StoreMap(Node* heap_object, Node* map) {
ObjectAccess access(MachineType::TaggedPointer(), kMapWriteBarrier);
#ifdef V8_MAP_PACKING
map = PackMapWord(TNode<Map>::UncheckedCast(map));
#endif
InitializeImmutableInObject(access, heap_object,
HeapObject::kMapOffset - kHeapObjectTag, map);
}
Node* WasmGraphAssembler::LoadInstanceType(Node* map) {
return LoadImmutableFromObject(
MachineType::Uint16(), map,
wasm::ObjectAccess::ToTagged(Map::kInstanceTypeOffset));
}
Node* WasmGraphAssembler::LoadWasmTypeInfo(Node* map) {
int offset = Map::kConstructorOrBackPointerOrNativeContextOffset;
return LoadImmutableFromObject(MachineType::TaggedPointer(), map,
wasm::ObjectAccess::ToTagged(offset));
}
// FixedArrays.
Node* WasmGraphAssembler::LoadFixedArrayLengthAsSmi(Node* fixed_array) {
return LoadImmutableFromObject(
MachineType::TaggedSigned(), fixed_array,
wasm::ObjectAccess::ToTagged(FixedArray::kLengthOffset));
}
Node* WasmGraphAssembler::LoadFixedArrayElement(Node* fixed_array,
Node* index_intptr,
MachineType type) {
DCHECK(IsSubtype(type.representation(), MachineRepresentation::kTagged));
Node* offset = IntAdd(
IntMul(index_intptr, IntPtrConstant(kTaggedSize)),
IntPtrConstant(wasm::ObjectAccess::ToTagged(FixedArray::kHeaderSize)));
return LoadFromObject(type, fixed_array, offset);
}
Node* WasmGraphAssembler::LoadWeakArrayListElement(Node* fixed_array,
Node* index_intptr,
MachineType type) {
Node* offset = IntAdd(
IntMul(index_intptr, IntPtrConstant(kTaggedSize)),
IntPtrConstant(wasm::ObjectAccess::ToTagged(WeakArrayList::kHeaderSize)));
return LoadFromObject(type, fixed_array, offset);
}
Node* WasmGraphAssembler::LoadImmutableFixedArrayElement(Node* fixed_array,
Node* index_intptr,
MachineType type) {
Node* offset = IntAdd(
IntMul(index_intptr, IntPtrConstant(kTaggedSize)),
IntPtrConstant(wasm::ObjectAccess::ToTagged(FixedArray::kHeaderSize)));
return LoadImmutableFromObject(type, fixed_array, offset);
}
Node* WasmGraphAssembler::LoadFixedArrayElement(Node* array, int index,
MachineType type) {
return LoadFromObject(
type, array, wasm::ObjectAccess::ElementOffsetInTaggedFixedArray(index));
}
Node* WasmGraphAssembler::LoadByteArrayElement(Node* byte_array,
Node* index_intptr,
MachineType type) {
int element_size = ElementSizeInBytes(type.representation());
Node* offset = IntAdd(
IntMul(index_intptr, IntPtrConstant(element_size)),
IntPtrConstant(wasm::ObjectAccess::ToTagged(ByteArray::kHeaderSize)));
return LoadFromObject(type, byte_array, offset);
}
Node* WasmGraphAssembler::LoadExternalPointerArrayElement(
Node* array, Node* index_intptr, ExternalPointerTag tag,
Node* isolate_root) {
Node* offset = IntAdd(
IntMul(index_intptr, IntPtrConstant(kExternalPointerSlotSize)),
IntPtrConstant(
wasm::ObjectAccess::ToTagged(ExternalPointerArray::kHeaderSize)));
#ifdef V8_ENABLE_SANDBOX
Node* handle = LoadFromObject(MachineType::Uint32(), array, offset);
return BuildDecodeSandboxedExternalPointer(handle, tag, isolate_root);
#else
return LoadFromObject(MachineType::Pointer(), array, offset);
#endif
}
Node* WasmGraphAssembler::LoadImmutableTrustedPointerFromObject(
Node* object, int field_offset, IndirectPointerTag tag) {
Node* offset = IntPtrConstant(field_offset);
#ifdef V8_ENABLE_SANDBOX
Node* handle = LoadImmutableFromObject(MachineType::Uint32(), object, offset);
return BuildDecodeTrustedPointer(handle, tag);
#else
return LoadImmutableFromObject(MachineType::TaggedPointer(), object, offset);
#endif
}
Node* WasmGraphAssembler::StoreFixedArrayElement(Node* array, int index,
Node* value,
ObjectAccess access) {
return StoreToObject(
access, array, wasm::ObjectAccess::ElementOffsetInTaggedFixedArray(index),
value);
}
// Functions, SharedFunctionInfos, FunctionData.
Node* WasmGraphAssembler::LoadSharedFunctionInfo(Node* js_function) {
return LoadFromObject(
MachineType::TaggedPointer(), js_function,
wasm::ObjectAccess::SharedFunctionInfoOffsetInTaggedJSFunction());
}
Node* WasmGraphAssembler::LoadContextFromJSFunction(Node* js_function) {
return LoadFromObject(MachineType::TaggedPointer(), js_function,
wasm::ObjectAccess::ContextOffsetInTaggedJSFunction());
}
Node* WasmGraphAssembler::LoadFunctionDataFromJSFunction(Node* js_function) {
Node* shared = LoadSharedFunctionInfo(js_function);
return LoadFromObject(
MachineType::TaggedPointer(), shared,
wasm::ObjectAccess::ToTagged(SharedFunctionInfo::kFunctionDataOffset));
}
Node* WasmGraphAssembler::LoadExportedFunctionIndexAsSmi(
Node* exported_function_data) {
return LoadImmutableFromObject(
MachineType::TaggedSigned(), exported_function_data,
wasm::ObjectAccess::ToTagged(
WasmExportedFunctionData::kFunctionIndexOffset));
}
Node* WasmGraphAssembler::LoadExportedFunctionInstance(
Node* exported_function_data) {
return LoadImmutableFromObject(
MachineType::TaggedPointer(), exported_function_data,
wasm::ObjectAccess::ToTagged(WasmExportedFunctionData::kInstanceOffset));
}
// JavaScript objects.
Node* WasmGraphAssembler::LoadJSArrayElements(Node* js_array) {
return LoadFromObject(
MachineType::AnyTagged(), js_array,
wasm::ObjectAccess::ToTagged(JSObject::kElementsOffset));
}
// WasmGC objects.
Node* WasmGraphAssembler::FieldOffset(const wasm::StructType* type,
uint32_t field_index) {
return IntPtrConstant(wasm::ObjectAccess::ToTagged(
WasmStruct::kHeaderSize + type->field_offset(field_index)));
}
Node* WasmGraphAssembler::WasmArrayElementOffset(Node* index,
wasm::ValueType element_type) {
Node* index_intptr =
mcgraph()->machine()->Is64() ? ChangeInt32ToInt64(index) : index;
return IntAdd(
IntPtrConstant(wasm::ObjectAccess::ToTagged(WasmArray::kHeaderSize)),
IntMul(index_intptr, IntPtrConstant(element_type.value_kind_size())));
}
Node* WasmGraphAssembler::IsDataRefMap(Node* map) {
Node* instance_type = LoadInstanceType(map);
// We're going to test a range of WasmObject instance types with a single
// unsigned comparison.
Node* comparison_value =
Int32Sub(instance_type, Int32Constant(FIRST_WASM_OBJECT_TYPE));
return Uint32LessThanOrEqual(
comparison_value,
Int32Constant(LAST_WASM_OBJECT_TYPE - FIRST_WASM_OBJECT_TYPE));
}
Node* WasmGraphAssembler::WasmTypeCheck(Node* object, Node* rtt,
WasmTypeCheckConfig config) {
return AddNode(graph()->NewNode(simplified_.WasmTypeCheck(config), object,
rtt, effect(), control()));
}
Node* WasmGraphAssembler::WasmTypeCheckAbstract(Node* object,
WasmTypeCheckConfig config) {
return AddNode(graph()->NewNode(simplified_.WasmTypeCheckAbstract(config),
object, effect(), control()));
}
Node* WasmGraphAssembler::WasmTypeCast(Node* object, Node* rtt,
WasmTypeCheckConfig config) {
return AddNode(graph()->NewNode(simplified_.WasmTypeCast(config), object, rtt,
effect(), control()));
}
Node* WasmGraphAssembler::WasmTypeCastAbstract(Node* object,
WasmTypeCheckConfig config) {
return AddNode(graph()->NewNode(simplified_.WasmTypeCastAbstract(config),
object, effect(), control()));
}
Node* WasmGraphAssembler::Null(wasm::ValueType type) {
return AddNode(graph()->NewNode(simplified_.Null(type)));
}
Node* WasmGraphAssembler::IsNull(Node* object, wasm::ValueType type) {
return AddNode(graph()->NewNode(simplified_.IsNull(type), object, control()));
}
Node* WasmGraphAssembler::IsNotNull(Node* object, wasm::ValueType type) {
return AddNode(
graph()->NewNode(simplified_.IsNotNull(type), object, control()));
}
Node* WasmGraphAssembler::AssertNotNull(Node* object, wasm::ValueType type,
TrapId trap_id) {
return AddNode(graph()->NewNode(simplified_.AssertNotNull(type, trap_id),
object, effect(), control()));
}
Node* WasmGraphAssembler::WasmAnyConvertExtern(Node* object) {
return AddNode(graph()->NewNode(simplified_.WasmAnyConvertExtern(), object,
effect(), control()));
}
Node* WasmGraphAssembler::WasmExternConvertAny(Node* object) {
return AddNode(graph()->NewNode(simplified_.WasmExternConvertAny(), object,
effect(), control()));
}
Node* WasmGraphAssembler::StructGet(Node* object, const wasm::StructType* type,
int field_index, bool is_signed,
CheckForNull null_check) {
return AddNode(graph()->NewNode(
simplified_.WasmStructGet(type, field_index, is_signed, null_check),
object, effect(), control()));
}
void WasmGraphAssembler::StructSet(Node* object, Node* value,
const wasm::StructType* type,
int field_index, CheckForNull null_check) {
AddNode(
graph()->NewNode(simplified_.WasmStructSet(type, field_index, null_check),
object, value, effect(), control()));
}
Node* WasmGraphAssembler::ArrayGet(Node* array, Node* index,
const wasm::ArrayType* type,
bool is_signed) {
return AddNode(graph()->NewNode(simplified_.WasmArrayGet(type, is_signed),
array, index, effect(), control()));
}
void WasmGraphAssembler::ArraySet(Node* array, Node* index, Node* value,
const wasm::ArrayType* type) {
AddNode(graph()->NewNode(simplified_.WasmArraySet(type), array, index, value,
effect(), control()));
}
Node* WasmGraphAssembler::ArrayLength(Node* array, CheckForNull null_check) {
return AddNode(graph()->NewNode(simplified_.WasmArrayLength(null_check),
array, effect(), control()));
}
void WasmGraphAssembler::ArrayInitializeLength(Node* array, Node* length) {
AddNode(graph()->NewNode(simplified_.WasmArrayInitializeLength(), array,
length, effect(), control()));
}
Node* WasmGraphAssembler::LoadStringLength(Node* string) {
return LoadImmutableFromObject(
MachineType::Int32(), string,
wasm::ObjectAccess::ToTagged(AccessBuilder::ForStringLength().offset));
}
Node* WasmGraphAssembler::StringAsWtf16(Node* string) {
return AddNode(graph()->NewNode(simplified_.StringAsWtf16(), string, effect(),
control()));
}
Node* WasmGraphAssembler::StringPrepareForGetCodeunit(Node* string) {
return AddNode(graph()->NewNode(simplified_.StringPrepareForGetCodeunit(),
string, effect(), control()));
}
Node* WasmGraphAssembler::LoadTrustedDataFromInstanceObject(
Node* instance_object) {
return LoadImmutableTrustedPointerFromObject(
instance_object,
wasm::ObjectAccess::ToTagged(WasmInstanceObject::kTrustedDataOffset),
kWasmTrustedInstanceDataIndirectPointerTag);
}
// Generic HeapObject helpers.
Node* WasmGraphAssembler::HasInstanceType(Node* heap_object,
InstanceType type) {
Node* map = LoadMap(heap_object);
Node* instance_type = LoadInstanceType(map);
return Word32Equal(instance_type, Int32Constant(type));
}
} // namespace v8::internal::compiler