%PDF- %PDF-
Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/maglev/ |
Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/maglev/maglev-ir.h |
// Copyright 2021 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. #ifndef V8_MAGLEV_MAGLEV_IR_H_ #define V8_MAGLEV_MAGLEV_IR_H_ #include "src/base/bit-field.h" #include "src/base/bits.h" #include "src/base/discriminated-union.h" #include "src/base/enum-set.h" #include "src/base/logging.h" #include "src/base/macros.h" #include "src/base/small-vector.h" #include "src/base/threaded-list.h" #include "src/codegen/label.h" #include "src/codegen/machine-type.h" #include "src/codegen/reglist.h" #include "src/codegen/source-position.h" #include "src/common/globals.h" #include "src/common/operation.h" #include "src/compiler/access-info.h" #include "src/compiler/backend/instruction.h" #include "src/compiler/feedback-source.h" #include "src/compiler/heap-refs.h" // TODO(dmercadier): move the Turboshaft utils functions to shared code (in // particular, any_of, which is the reason we're including this Turboshaft // header) #include "src/compiler/turboshaft/snapshot-table.h" #include "src/compiler/turboshaft/utils.h" #include "src/deoptimizer/deoptimize-reason.h" #include "src/interpreter/bytecode-flags.h" #include "src/interpreter/bytecode-register.h" #include "src/maglev/maglev-compilation-unit.h" #include "src/objects/smi.h" #include "src/objects/tagged-index.h" #include "src/roots/roots.h" #include "src/utils/utils.h" #include "src/zone/zone.h" namespace v8 { namespace internal { enum Condition : int; namespace maglev { class BasicBlock; class ProcessingState; class MaglevAssembler; class MaglevCodeGenState; class MaglevCompilationUnit; class MaglevGraphLabeller; class MaglevVregAllocationState; class CompactInterpreterFrameState; class MergePointInterpreterFrameState; // Nodes are either // 1. side-effecting or value-holding SSA nodes in the body of basic blocks, or // 2. Control nodes that store the control flow at the end of basic blocks, and // form a separate node hierarchy to non-control nodes. // // The macro lists below must match the node class hierarchy. #define GENERIC_OPERATIONS_NODE_LIST(V) \ V(GenericAdd) \ V(GenericSubtract) \ V(GenericMultiply) \ V(GenericDivide) \ V(GenericModulus) \ V(GenericExponentiate) \ V(GenericBitwiseAnd) \ V(GenericBitwiseOr) \ V(GenericBitwiseXor) \ V(GenericShiftLeft) \ V(GenericShiftRight) \ V(GenericShiftRightLogical) \ V(GenericBitwiseNot) \ V(GenericNegate) \ V(GenericIncrement) \ V(GenericDecrement) \ V(GenericEqual) \ V(GenericStrictEqual) \ V(GenericLessThan) \ V(GenericLessThanOrEqual) \ V(GenericGreaterThan) \ V(GenericGreaterThanOrEqual) #define INT32_OPERATIONS_NODE_LIST(V) \ V(Int32AddWithOverflow) \ V(Int32SubtractWithOverflow) \ V(Int32MultiplyWithOverflow) \ V(Int32DivideWithOverflow) \ V(Int32ModulusWithOverflow) \ V(Int32BitwiseAnd) \ V(Int32BitwiseOr) \ V(Int32BitwiseXor) \ V(Int32ShiftLeft) \ V(Int32ShiftRight) \ V(Int32ShiftRightLogical) \ V(Int32BitwiseNot) \ V(Int32NegateWithOverflow) \ V(Int32IncrementWithOverflow) \ V(Int32DecrementWithOverflow) \ V(Int32Compare) \ V(Int32ToBoolean) #define FLOAT64_OPERATIONS_NODE_LIST(V) \ V(Float64Add) \ V(Float64Subtract) \ V(Float64Multiply) \ V(Float64Divide) \ V(Float64Exponentiate) \ V(Float64Modulus) \ V(Float64Negate) \ V(Float64Round) \ V(Float64Compare) \ V(Float64ToBoolean) \ V(Float64Ieee754Unary) #define SMI_OPERATIONS_NODE_LIST(V) \ V(CheckedSmiIncrement) \ V(CheckedSmiDecrement) #define CONSTANT_VALUE_NODE_LIST(V) \ V(Constant) \ V(ExternalConstant) \ V(Float64Constant) \ V(Int32Constant) \ V(RootConstant) \ V(SmiConstant) \ V(TaggedIndexConstant) #define INLINE_BUILTIN_NODE_LIST(V) \ V(BuiltinStringFromCharCode) \ V(BuiltinStringPrototypeCharCodeOrCodePointAt) #define VALUE_NODE_LIST(V) \ V(Identity) \ V(AllocateRaw) \ V(Call) \ V(CallBuiltin) \ V(CallCPPBuiltin) \ V(CallRuntime) \ V(CallWithArrayLike) \ V(CallWithSpread) \ V(CallKnownApiFunction) \ V(CallKnownJSFunction) \ V(CallSelf) \ V(Construct) \ V(CheckConstructResult) \ V(CheckDerivedConstructResult) \ V(CheckNotHole) \ V(ConstructWithSpread) \ V(ConvertReceiver) \ V(ConvertHoleToUndefined) \ V(CreateArrayLiteral) \ V(CreateShallowArrayLiteral) \ V(CreateObjectLiteral) \ V(CreateShallowObjectLiteral) \ V(CreateFunctionContext) \ V(CreateClosure) \ V(FastCreateClosure) \ V(CreateRegExpLiteral) \ V(DeleteProperty) \ V(EnsureWritableFastElements) \ V(FoldedAllocation) \ V(ForInPrepare) \ V(ForInNext) \ V(GeneratorRestoreRegister) \ V(GetIterator) \ V(GetSecondReturnedValue) \ V(GetTemplateObject) \ V(HasInPrototypeChain) \ V(InitialValue) \ V(LoadPolymorphicDoubleField) \ V(LoadPolymorphicTaggedField) \ V(LoadTaggedField) \ V(LoadDoubleField) \ V(LoadTaggedFieldByFieldIndex) \ V(LoadFixedArrayElement) \ V(LoadFixedDoubleArrayElement) \ V(LoadHoleyFixedDoubleArrayElement) \ V(LoadHoleyFixedDoubleArrayElementCheckedNotHole) \ V(LoadSignedIntDataViewElement) \ V(LoadDoubleDataViewElement) \ V(LoadSignedIntTypedArrayElement) \ V(LoadSignedIntTypedArrayElementNoDeopt) \ V(LoadUnsignedIntTypedArrayElement) \ V(LoadUnsignedIntTypedArrayElementNoDeopt) \ V(LoadDoubleTypedArrayElement) \ V(LoadDoubleTypedArrayElementNoDeopt) \ V(LoadEnumCacheLength) \ V(LoadGlobal) \ V(LoadNamedGeneric) \ V(LoadNamedFromSuperGeneric) \ V(MaybeGrowAndEnsureWritableFastElements) \ V(SetNamedGeneric) \ V(DefineNamedOwnGeneric) \ V(StoreInArrayLiteralGeneric) \ V(StoreGlobal) \ V(GetKeyedGeneric) \ V(SetKeyedGeneric) \ V(DefineKeyedOwnGeneric) \ V(Phi) \ V(RegisterInput) \ V(CheckedSmiSizedInt32) \ V(CheckedSmiTagInt32) \ V(CheckedSmiTagUint32) \ V(UnsafeSmiTag) \ V(CheckedSmiUntag) \ V(UnsafeSmiUntag) \ V(CheckedInternalizedString) \ V(CheckedObjectToIndex) \ V(CheckedTruncateNumberOrOddballToInt32) \ V(CheckedInt32ToUint32) \ V(CheckedUint32ToInt32) \ V(ChangeInt32ToFloat64) \ V(ChangeUint32ToFloat64) \ V(CheckedTruncateFloat64ToInt32) \ V(CheckedTruncateFloat64ToUint32) \ V(TruncateNumberOrOddballToInt32) \ V(TruncateUint32ToInt32) \ V(TruncateFloat64ToInt32) \ V(UnsafeTruncateUint32ToInt32) \ V(UnsafeTruncateFloat64ToInt32) \ V(Int32ToUint8Clamped) \ V(Uint32ToUint8Clamped) \ V(Float64ToUint8Clamped) \ V(CheckedNumberToUint8Clamped) \ V(Int32ToNumber) \ V(Uint32ToNumber) \ V(Float64ToTagged) \ V(HoleyFloat64ToTagged) \ V(CheckedSmiTagFloat64) \ V(CheckedNumberOrOddballToFloat64) \ V(UncheckedNumberOrOddballToFloat64) \ V(CheckedHoleyFloat64ToFloat64) \ V(HoleyFloat64ToMaybeNanFloat64) \ V(LogicalNot) \ V(SetPendingMessage) \ V(StringAt) \ V(StringEqual) \ V(StringLength) \ V(StringConcat) \ V(ToBoolean) \ V(ToBooleanLogicalNot) \ V(TaggedEqual) \ V(TaggedNotEqual) \ V(TestInstanceOf) \ V(TestUndetectable) \ V(TestTypeOf) \ V(ToName) \ V(ToNumberOrNumeric) \ V(ToObject) \ V(ToString) \ V(NumberToString) \ CONSTANT_VALUE_NODE_LIST(V) \ INT32_OPERATIONS_NODE_LIST(V) \ FLOAT64_OPERATIONS_NODE_LIST(V) \ SMI_OPERATIONS_NODE_LIST(V) \ GENERIC_OPERATIONS_NODE_LIST(V) \ INLINE_BUILTIN_NODE_LIST(V) #define GAP_MOVE_NODE_LIST(V) \ V(ConstantGapMove) \ V(GapMove) #define NODE_LIST(V) \ V(AssertInt32) \ V(CheckDynamicValue) \ V(CheckInt32IsSmi) \ V(CheckUint32IsSmi) \ V(CheckHoleyFloat64IsSmi) \ V(CheckHeapObject) \ V(CheckInt32Condition) \ V(CheckFixedArrayNonEmpty) \ V(CheckJSDataViewBounds) \ V(CheckJSTypedArrayBounds) \ V(CheckMaps) \ V(CheckMapsWithMigration) \ V(CheckNumber) \ V(CheckSmi) \ V(CheckString) \ V(CheckSymbol) \ V(CheckValue) \ V(CheckValueEqualsInt32) \ V(CheckValueEqualsFloat64) \ V(CheckValueEqualsString) \ V(CheckInstanceType) \ V(DebugBreak) \ V(FunctionEntryStackCheck) \ V(GeneratorStore) \ V(TryOnStackReplacement) \ V(StoreMap) \ V(StoreDoubleField) \ V(StoreFixedArrayElementWithWriteBarrier) \ V(StoreFixedArrayElementNoWriteBarrier) \ V(StoreFixedDoubleArrayElement) \ V(StoreFloat64) \ V(StoreIntTypedArrayElement) \ V(StoreIntTypedArrayElementNoDeopt) \ V(StoreDoubleTypedArrayElement) \ V(StoreDoubleTypedArrayElementNoDeopt) \ V(StoreSignedIntDataViewElement) \ V(StoreDoubleDataViewElement) \ V(StoreTaggedFieldNoWriteBarrier) \ V(StoreTaggedFieldWithWriteBarrier) \ V(HandleNoHeapWritesInterrupt) \ V(ReduceInterruptBudgetForLoop) \ V(ReduceInterruptBudgetForReturn) \ V(ThrowReferenceErrorIfHole) \ V(ThrowSuperNotCalledIfHole) \ V(ThrowSuperAlreadyCalledIfNotHole) \ V(ThrowIfNotCallable) \ V(ThrowIfNotSuperConstructor) \ V(TransitionElementsKindOrCheckMap) \ V(UpdateJSArrayLength) \ GAP_MOVE_NODE_LIST(V) \ VALUE_NODE_LIST(V) #define BRANCH_CONTROL_NODE_LIST(V) \ V(BranchIfRootConstant) \ V(BranchIfToBooleanTrue) \ V(BranchIfInt32ToBooleanTrue) \ V(BranchIfFloat64ToBooleanTrue) \ V(BranchIfFloat64IsHole) \ V(BranchIfReferenceEqual) \ V(BranchIfInt32Compare) \ V(BranchIfFloat64Compare) \ V(BranchIfUndefinedOrNull) \ V(BranchIfUndetectable) \ V(BranchIfJSReceiver) \ V(BranchIfTypeOf) #define CONDITIONAL_CONTROL_NODE_LIST(V) \ V(Switch) \ BRANCH_CONTROL_NODE_LIST(V) #define UNCONDITIONAL_CONTROL_NODE_LIST(V) \ V(Jump) \ V(JumpLoop) #define TERMINAL_CONTROL_NODE_LIST(V) \ V(Abort) \ V(Return) \ V(Deopt) #define CONTROL_NODE_LIST(V) \ TERMINAL_CONTROL_NODE_LIST(V) \ CONDITIONAL_CONTROL_NODE_LIST(V) \ UNCONDITIONAL_CONTROL_NODE_LIST(V) #define NODE_BASE_LIST(V) \ NODE_LIST(V) \ CONTROL_NODE_LIST(V) // Define the opcode enum. #define DEF_OPCODES(type) k##type, enum class Opcode : uint16_t { NODE_BASE_LIST(DEF_OPCODES) }; #undef DEF_OPCODES #define PLUS_ONE(type) +1 static constexpr int kOpcodeCount = NODE_BASE_LIST(PLUS_ONE); static constexpr Opcode kFirstOpcode = static_cast<Opcode>(0); static constexpr Opcode kLastOpcode = static_cast<Opcode>(kOpcodeCount - 1); #undef PLUS_ONE const char* OpcodeToString(Opcode opcode); inline std::ostream& operator<<(std::ostream& os, Opcode opcode) { return os << OpcodeToString(opcode); } #define V(Name) Opcode::k##Name, static constexpr Opcode kFirstValueNodeOpcode = std::min({VALUE_NODE_LIST(V) kLastOpcode}); static constexpr Opcode kLastValueNodeOpcode = std::max({VALUE_NODE_LIST(V) kFirstOpcode}); static constexpr Opcode kFirstConstantNodeOpcode = std::min({CONSTANT_VALUE_NODE_LIST(V) kLastOpcode}); static constexpr Opcode kLastConstantNodeOpcode = std::max({CONSTANT_VALUE_NODE_LIST(V) kFirstOpcode}); static constexpr Opcode kFirstGapMoveNodeOpcode = std::min({GAP_MOVE_NODE_LIST(V) kLastOpcode}); static constexpr Opcode kLastGapMoveNodeOpcode = std::max({GAP_MOVE_NODE_LIST(V) kFirstOpcode}); static constexpr Opcode kFirstNodeOpcode = std::min({NODE_LIST(V) kLastOpcode}); static constexpr Opcode kLastNodeOpcode = std::max({NODE_LIST(V) kFirstOpcode}); static constexpr Opcode kFirstBranchControlNodeOpcode = std::min({BRANCH_CONTROL_NODE_LIST(V) kLastOpcode}); static constexpr Opcode kLastBranchControlNodeOpcode = std::max({BRANCH_CONTROL_NODE_LIST(V) kFirstOpcode}); static constexpr Opcode kFirstConditionalControlNodeOpcode = std::min({CONDITIONAL_CONTROL_NODE_LIST(V) kLastOpcode}); static constexpr Opcode kLastConditionalControlNodeOpcode = std::max({CONDITIONAL_CONTROL_NODE_LIST(V) kFirstOpcode}); static constexpr Opcode kLastUnconditionalControlNodeOpcode = std::max({UNCONDITIONAL_CONTROL_NODE_LIST(V) kFirstOpcode}); static constexpr Opcode kFirstUnconditionalControlNodeOpcode = std::min({UNCONDITIONAL_CONTROL_NODE_LIST(V) kLastOpcode}); static constexpr Opcode kLastTerminalControlNodeOpcode = std::max({TERMINAL_CONTROL_NODE_LIST(V) kFirstOpcode}); static constexpr Opcode kFirstTerminalControlNodeOpcode = std::min({TERMINAL_CONTROL_NODE_LIST(V) kLastOpcode}); static constexpr Opcode kFirstControlNodeOpcode = std::min({CONTROL_NODE_LIST(V) kLastOpcode}); static constexpr Opcode kLastControlNodeOpcode = std::max({CONTROL_NODE_LIST(V) kFirstOpcode}); #undef V constexpr bool IsValueNode(Opcode opcode) { return kFirstValueNodeOpcode <= opcode && opcode <= kLastValueNodeOpcode; } constexpr bool IsConstantNode(Opcode opcode) { return kFirstConstantNodeOpcode <= opcode && opcode <= kLastConstantNodeOpcode; } constexpr bool IsGapMoveNode(Opcode opcode) { return kFirstGapMoveNodeOpcode <= opcode && opcode <= kLastGapMoveNodeOpcode; } constexpr bool IsControlNode(Opcode opcode) { return kFirstControlNodeOpcode <= opcode && opcode <= kLastControlNodeOpcode; } constexpr bool IsBranchControlNode(Opcode opcode) { return kFirstBranchControlNodeOpcode <= opcode && opcode <= kLastBranchControlNodeOpcode; } constexpr bool IsConditionalControlNode(Opcode opcode) { return kFirstConditionalControlNodeOpcode <= opcode && opcode <= kLastConditionalControlNodeOpcode; } constexpr bool IsUnconditionalControlNode(Opcode opcode) { return kFirstUnconditionalControlNodeOpcode <= opcode && opcode <= kLastUnconditionalControlNodeOpcode; } constexpr bool IsTerminalControlNode(Opcode opcode) { return kFirstTerminalControlNodeOpcode <= opcode && opcode <= kLastTerminalControlNodeOpcode; } // Forward-declare NodeBase sub-hierarchies. class Node; class ControlNode; class ConditionalControlNode; class BranchControlNode; class UnconditionalControlNode; class TerminalControlNode; class ValueNode; enum class ValueRepresentation : uint8_t { kTagged, kInt32, kUint32, kFloat64, kHoleyFloat64, kWord64 }; inline constexpr bool IsDoubleRepresentation(ValueRepresentation repr) { return repr == ValueRepresentation::kFloat64 || repr == ValueRepresentation::kHoleyFloat64; } /* * The intersection (using `&`) of any two NodeTypes must be a valid NodeType * (possibly "kUnknown", modulo heap object bit). * * All heap object types include the heap object bit, so that they can be * checked for AnyHeapObject with a single bit check. * * Here is a diagram of the relations between the types, where (*) means that * they have the kAnyHeapObject bit set. * * NumberOrOddball JsReceiver* Name* * / \ | / \ * Oddball* Number Callable* String* Symbol* * | / \ | * Boolean* Smi HeapNumber* InternalizedString* * */ #define NODE_TYPE_LIST(V) \ V(Unknown, 0) \ V(NumberOrOddball, (1 << 1)) \ V(Number, (1 << 2) | kNumberOrOddball) \ V(Smi, (1 << 4) | kNumber) \ V(AnyHeapObject, (1 << 5)) \ V(Oddball, (1 << 6) | kAnyHeapObject | kNumberOrOddball) \ V(Boolean, (1 << 7) | kOddball) \ V(Name, (1 << 8) | kAnyHeapObject) \ V(String, (1 << 9) | kName) \ V(InternalizedString, (1 << 10) | kString) \ V(Symbol, (1 << 11) | kName) \ V(JSReceiver, (1 << 12) | kAnyHeapObject) \ V(Callable, (1 << 13) | kJSReceiver | kAnyHeapObject) \ V(HeapNumber, kAnyHeapObject | kNumber) enum class NodeType : uint16_t { #define DEFINE_NODE_TYPE(Name, Value) k##Name = Value, NODE_TYPE_LIST(DEFINE_NODE_TYPE) #undef DEFINE_NODE_TYPE }; inline NodeType CombineType(NodeType left, NodeType right) { return static_cast<NodeType>(static_cast<int>(left) | static_cast<int>(right)); } inline NodeType IntersectType(NodeType left, NodeType right) { return static_cast<NodeType>(static_cast<int>(left) & static_cast<int>(right)); } inline bool NodeTypeIs(NodeType type, NodeType to_check) { int right = static_cast<int>(to_check); return (static_cast<int>(type) & right) == right; } inline NodeType StaticTypeForMap(compiler::MapRef map) { if (map.IsHeapNumberMap()) return NodeType::kHeapNumber; if (map.IsInternalizedStringMap()) return NodeType::kInternalizedString; if (map.IsStringMap()) return NodeType::kString; if (map.IsJSReceiverMap()) return NodeType::kJSReceiver; return NodeType::kAnyHeapObject; } inline NodeType StaticTypeForConstant(compiler::JSHeapBroker* broker, compiler::ObjectRef ref) { if (ref.IsSmi()) return NodeType::kSmi; return StaticTypeForMap(ref.AsHeapObject().map(broker)); } inline bool IsInstanceOfNodeType(compiler::MapRef map, NodeType type, compiler::JSHeapBroker* broker) { switch (type) { case NodeType::kUnknown: return true; case NodeType::kNumberOrOddball: return map.IsHeapNumberMap() || map.IsOddballMap(); case NodeType::kSmi: return false; case NodeType::kNumber: case NodeType::kHeapNumber: return map.IsHeapNumberMap(); case NodeType::kAnyHeapObject: return true; case NodeType::kOddball: return map.IsOddballMap(); case NodeType::kBoolean: return map.IsOddballMap() && map.oddball_type(broker) == compiler::OddballType::kBoolean; case NodeType::kName: return map.IsNameMap(); case NodeType::kString: return map.IsStringMap(); case NodeType::kInternalizedString: return map.IsInternalizedStringMap(); case NodeType::kSymbol: return map.IsSymbolMap(); case NodeType::kJSReceiver: return map.IsJSReceiverMap(); case NodeType::kCallable: return map.is_callable(); } // This is some composed type. We could speed this up by exploiting the tree // structure of the types. #define CASE(Name, _) \ if (NodeTypeIs(type, NodeType::k##Name)) { \ if (!IsInstanceOfNodeType(map, NodeType::k##Name, broker)) { \ return false; \ } \ } NODE_TYPE_LIST(CASE) #undef CASE return true; } inline std::ostream& operator<<(std::ostream& out, const NodeType& type) { switch (type) { #define CASE(Name, _) \ case NodeType::k##Name: \ out << #Name; \ break; NODE_TYPE_LIST(CASE) #undef CASE default: #define CASE(Name, _) \ if (NodeTypeIs(type, NodeType::k##Name)) { \ out << #Name ","; \ } NODE_TYPE_LIST(CASE) #undef CASE } return out; } #define DEFINE_NODE_TYPE_CHECK(Type, _) \ inline bool NodeTypeIs##Type(NodeType type) { \ return NodeTypeIs(type, NodeType::k##Type); \ } NODE_TYPE_LIST(DEFINE_NODE_TYPE_CHECK) #undef DEFINE_NODE_TYPE_CHECK enum class TaggedToFloat64ConversionType : uint8_t { kOnlyNumber, kNumberOrOddball, }; constexpr Condition ConditionFor(Operation cond); constexpr Condition ConditionForNaN(); bool FromConstantToBool(LocalIsolate* local_isolate, ValueNode* node); bool FromConstantToBool(MaglevAssembler* masm, ValueNode* node); inline int ExternalArrayElementSize(const ExternalArrayType element_type) { switch (element_type) { #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \ case kExternal##Type##Array: \ DCHECK_LE(sizeof(ctype), 8); \ return sizeof(ctype); TYPED_ARRAYS(TYPED_ARRAY_CASE) default: UNREACHABLE(); #undef TYPED_ARRAY_CASE } } inline int ElementsKindSize(ElementsKind element_kind) { switch (element_kind) { #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \ case TYPE##_ELEMENTS: \ DCHECK_LE(sizeof(ctype), 8); \ return sizeof(ctype); TYPED_ARRAYS(TYPED_ARRAY_CASE) default: UNREACHABLE(); #undef TYPED_ARRAY_CASE } } inline std::ostream& operator<<(std::ostream& os, const ValueRepresentation& repr) { switch (repr) { case ValueRepresentation::kTagged: return os << "Tagged"; case ValueRepresentation::kInt32: return os << "Int32"; case ValueRepresentation::kUint32: return os << "Uint32"; case ValueRepresentation::kFloat64: return os << "Float64"; case ValueRepresentation::kHoleyFloat64: return os << "HoleyFloat64"; case ValueRepresentation::kWord64: return os << "Word64"; } } inline std::ostream& operator<<( std::ostream& os, const TaggedToFloat64ConversionType& conversion_type) { switch (conversion_type) { case TaggedToFloat64ConversionType::kOnlyNumber: return os << "Number"; case TaggedToFloat64ConversionType::kNumberOrOddball: return os << "NumberOrOddball"; } } inline bool HasOnlyJSTypedArrayMaps(base::Vector<const compiler::MapRef> maps) { for (compiler::MapRef map : maps) { if (!map.IsJSTypedArrayMap()) return false; } return true; } inline bool HasOnlyJSArrayMaps(base::Vector<const compiler::MapRef> maps) { for (compiler::MapRef map : maps) { if (!map.IsJSArrayMap()) return false; } return true; } inline bool HasOnlyJSObjectMaps(base::Vector<const compiler::MapRef> maps) { for (compiler::MapRef map : maps) { if (!map.IsJSObjectMap()) return false; } return true; } inline bool HasOnlyStringMaps(base::Vector<const compiler::MapRef> maps) { for (compiler::MapRef map : maps) { if (!map.IsStringMap()) return false; } return true; } inline bool HasOnlyNumberMaps(base::Vector<const compiler::MapRef> maps) { for (compiler::MapRef map : maps) { if (map.instance_type() != HEAP_NUMBER_TYPE) return false; } return true; } #define DEF_FORWARD_DECLARATION(type, ...) class type; NODE_BASE_LIST(DEF_FORWARD_DECLARATION) #undef DEF_FORWARD_DECLARATION using NodeIdT = uint32_t; static constexpr uint32_t kInvalidNodeId = 0; static constexpr uint32_t kFirstValidNodeId = 1; // Represents either a direct BasicBlock pointer, or an entry in a list of // unresolved BasicBlockRefs which will be mutated (in place) at some point into // direct BasicBlock pointers. class BasicBlockRef { struct BasicBlockRefBuilder; public: BasicBlockRef() : next_ref_(nullptr) { #ifdef DEBUG state_ = kRefList; #endif } explicit BasicBlockRef(BasicBlock* block) : block_ptr_(block) { #ifdef DEBUG state_ = kBlockPointer; #endif } // Refs can't be copied or moved, since they are referenced by `this` pointer // in the ref list. BasicBlockRef(const BasicBlockRef&) = delete; BasicBlockRef(BasicBlockRef&&) = delete; BasicBlockRef& operator=(const BasicBlockRef&) = delete; BasicBlockRef& operator=(BasicBlockRef&&) = delete; // Construct a new ref-list mode BasicBlockRef and add it to the given ref // list. explicit BasicBlockRef(BasicBlockRef* ref_list_head) : BasicBlockRef() { BasicBlockRef* old_next_ptr = MoveToRefList(ref_list_head); USE(old_next_ptr); DCHECK_NULL(old_next_ptr); } // Change this ref to a direct basic block pointer, returning the old "next" // pointer of the current ref. BasicBlockRef* SetToBlockAndReturnNext(BasicBlock* block) { DCHECK_EQ(state_, kRefList); BasicBlockRef* old_next_ptr = next_ref_; block_ptr_ = block; #ifdef DEBUG state_ = kBlockPointer; #endif return old_next_ptr; } // Reset this ref list to null, returning the old ref list (i.e. the old // "next" pointer). BasicBlockRef* Reset() { DCHECK_EQ(state_, kRefList); BasicBlockRef* old_next_ptr = next_ref_; next_ref_ = nullptr; return old_next_ptr; } // Move this ref to the given ref list, returning the old "next" pointer of // the current ref. BasicBlockRef* MoveToRefList(BasicBlockRef* ref_list_head) { DCHECK_EQ(state_, kRefList); DCHECK_EQ(ref_list_head->state_, kRefList); BasicBlockRef* old_next_ptr = next_ref_; next_ref_ = ref_list_head->next_ref_; ref_list_head->next_ref_ = this; return old_next_ptr; } void Bind(BasicBlock* block) { DCHECK_EQ(state_, kRefList); BasicBlockRef* next_ref = SetToBlockAndReturnNext(block); while (next_ref != nullptr) { next_ref = next_ref->SetToBlockAndReturnNext(block); } DCHECK_EQ(block_ptr(), block); } BasicBlock* block_ptr() const { DCHECK_EQ(state_, kBlockPointer); return block_ptr_; } BasicBlockRef* next_ref() const { DCHECK_EQ(state_, kRefList); return next_ref_; } bool has_ref() const { DCHECK_EQ(state_, kRefList); return next_ref_ != nullptr; } private: union { BasicBlock* block_ptr_; BasicBlockRef* next_ref_; }; #ifdef DEBUG enum { kBlockPointer, kRefList } state_; #endif // DEBUG }; class OpProperties { public: constexpr bool is_call() const { // Only returns true for non-deferred calls. Use `is_any_call` to check // deferred calls as well. return kIsCallBit::decode(bitfield_); } constexpr bool is_any_call() const { return is_call() || is_deferred_call(); } constexpr bool can_eager_deopt() const { return kCanEagerDeoptBit::decode(bitfield_); } constexpr bool can_lazy_deopt() const { return kCanLazyDeoptBit::decode(bitfield_); } constexpr bool can_deopt() const { return can_eager_deopt() || can_lazy_deopt(); } constexpr bool can_throw() const { return kCanThrowBit::decode(bitfield_) && can_lazy_deopt(); } constexpr bool can_read() const { return kCanReadBit::decode(bitfield_); } constexpr bool can_write() const { return kCanWriteBit::decode(bitfield_); } constexpr bool can_allocate() const { return kCanAllocate::decode(bitfield_); } constexpr ValueRepresentation value_representation() const { return kValueRepresentationBits::decode(bitfield_); } constexpr bool is_tagged() const { return value_representation() == ValueRepresentation::kTagged; } constexpr bool is_conversion() const { return kIsConversionBit::decode(bitfield_); } constexpr bool needs_register_snapshot() const { return kNeedsRegisterSnapshotBit::decode(bitfield_); } constexpr bool is_pure() const { return (bitfield_ & kPureMask) == kPureValue; } constexpr bool is_required_when_unused() const { if (is_conversion()) { // Calls in conversions are not counted as a side-effect as far as // is_required_when_unused is concerned, since they should always be to // the Allocate builtin. return can_write() || can_throw() || can_deopt(); } else { return can_write() || can_throw() || can_deopt() || is_any_call(); } } constexpr OpProperties operator|(const OpProperties& that) { return OpProperties(bitfield_ | that.bitfield_); } static constexpr OpProperties Pure() { return OpProperties(kPureValue); } static constexpr OpProperties Call() { return OpProperties(kIsCallBit::encode(true)); } static constexpr OpProperties EagerDeopt() { return OpProperties(kCanEagerDeoptBit::encode(true)); } static constexpr OpProperties LazyDeopt() { return OpProperties(kCanLazyDeoptBit::encode(true)); } static constexpr OpProperties CanThrow() { return OpProperties(kCanThrowBit::encode(true)) | LazyDeopt(); } static constexpr OpProperties CanRead() { return OpProperties(kCanReadBit::encode(true)); } static constexpr OpProperties CanWrite() { return OpProperties(kCanWriteBit::encode(true)); } static constexpr OpProperties CanAllocate() { return OpProperties(kCanAllocate::encode(true)); } static constexpr OpProperties TaggedValue() { return OpProperties( kValueRepresentationBits::encode(ValueRepresentation::kTagged)); } static constexpr OpProperties ExternalReference() { return OpProperties( kValueRepresentationBits::encode(ValueRepresentation::kWord64)); } static constexpr OpProperties Int32() { return OpProperties( kValueRepresentationBits::encode(ValueRepresentation::kInt32)); } static constexpr OpProperties Uint32() { return OpProperties( kValueRepresentationBits::encode(ValueRepresentation::kUint32)); } static constexpr OpProperties Float64() { return OpProperties( kValueRepresentationBits::encode(ValueRepresentation::kFloat64)); } static constexpr OpProperties HoleyFloat64() { return OpProperties( kValueRepresentationBits::encode(ValueRepresentation::kHoleyFloat64)); } static constexpr OpProperties ConversionNode() { return OpProperties(kIsConversionBit::encode(true)); } static constexpr OpProperties CanCallUserCode() { return AnySideEffects() | LazyDeopt() | CanThrow(); } // Without auditing the call target, we must assume it can cause a lazy deopt // and throw. Use this when codegen calls runtime or a builtin, unless // certain that the target either doesn't throw or cannot deopt. // TODO(jgruber): Go through all nodes marked with this property and decide // whether to keep it (or remove either the lazy-deopt or throw flag). static constexpr OpProperties GenericRuntimeOrBuiltinCall() { return Call() | CanCallUserCode(); } static constexpr OpProperties JSCall() { return Call() | CanCallUserCode(); } static constexpr OpProperties AnySideEffects() { return CanRead() | CanWrite() | CanAllocate(); } static constexpr OpProperties DeferredCall() { // Operations with a deferred call need a snapshot of register state, // because they need to be able to push registers to save them, and annotate // the safepoint with information about which registers are tagged. return NeedsRegisterSnapshot(); } constexpr explicit OpProperties(uint32_t bitfield) : bitfield_(bitfield) {} operator uint32_t() const { return bitfield_; } OpProperties WithNewValueRepresentation(ValueRepresentation new_repr) const { return OpProperties(kValueRepresentationBits::update(bitfield_, new_repr)); } OpProperties WithoutDeopt() const { return OpProperties(kCanLazyDeoptBit::update( kCanEagerDeoptBit::update(bitfield_, false), false)); } private: using kIsCallBit = base::BitField<bool, 0, 1>; using kCanEagerDeoptBit = kIsCallBit::Next<bool, 1>; using kCanLazyDeoptBit = kCanEagerDeoptBit::Next<bool, 1>; using kCanThrowBit = kCanLazyDeoptBit::Next<bool, 1>; using kCanReadBit = kCanThrowBit::Next<bool, 1>; using kCanWriteBit = kCanReadBit::Next<bool, 1>; using kCanAllocate = kCanWriteBit::Next<bool, 1>; using kValueRepresentationBits = kCanAllocate::Next<ValueRepresentation, 3>; using kIsConversionBit = kValueRepresentationBits::Next<bool, 1>; using kNeedsRegisterSnapshotBit = kIsConversionBit::Next<bool, 1>; static const uint32_t kPureMask = kCanReadBit::kMask | kCanWriteBit::kMask | kCanAllocate::kMask; static const uint32_t kPureValue = kCanReadBit::encode(false) | kCanWriteBit::encode(false) | kCanAllocate::encode(false); // NeedsRegisterSnapshot is only used for DeferredCall, and we rely on this in // `is_deferred_call` to detect deferred calls. If you need to use // NeedsRegisterSnapshot for something else that DeferredCalls, then you'll // have to update `is_any_call`. static constexpr OpProperties NeedsRegisterSnapshot() { return OpProperties(kNeedsRegisterSnapshotBit::encode(true)); } const uint32_t bitfield_; public: static const size_t kSize = kNeedsRegisterSnapshotBit::kLastUsedBit + 1; constexpr bool is_deferred_call() const { // Currently, there is no kDeferredCall bit, but DeferredCall only sets a // single bit: kNeedsRegisterSnapShot. If this static assert breaks, it // means that you added additional properties to DeferredCall, and you // should update this function accordingly. static_assert(DeferredCall().bitfield_ == kNeedsRegisterSnapshotBit::encode(true)); return needs_register_snapshot(); } }; constexpr inline OpProperties StaticPropertiesForOpcode(Opcode opcode); class ValueLocation { public: ValueLocation() = default; template <typename... Args> void SetUnallocated(Args&&... args) { DCHECK(operand_.IsInvalid()); operand_ = compiler::UnallocatedOperand(args...); } template <typename... Args> void SetAllocated(Args&&... args) { DCHECK(operand_.IsUnallocated()); operand_ = compiler::AllocatedOperand(args...); } // Only to be used on inputs that inherit allocation. void InjectLocation(compiler::InstructionOperand location) { operand_ = location; } // We use USED_AT_START to indicate that the input will be clobbered. bool Cloberred() { DCHECK(operand_.IsUnallocated()); return compiler::UnallocatedOperand::cast(operand_).IsUsedAtStart(); } template <typename... Args> void SetConstant(Args&&... args) { DCHECK(operand_.IsUnallocated()); operand_ = compiler::ConstantOperand(args...); } Register AssignedGeneralRegister() const { DCHECK(!IsDoubleRegister()); return compiler::AllocatedOperand::cast(operand_).GetRegister(); } DoubleRegister AssignedDoubleRegister() const { DCHECK(IsDoubleRegister()); return compiler::AllocatedOperand::cast(operand_).GetDoubleRegister(); } bool IsAnyRegister() const { return operand_.IsAnyRegister(); } bool IsGeneralRegister() const { return operand_.IsRegister(); } bool IsDoubleRegister() const { return operand_.IsDoubleRegister(); } const compiler::InstructionOperand& operand() const { return operand_; } const compiler::InstructionOperand& operand() { return operand_; } private: compiler::InstructionOperand operand_; }; class InputLocation : public ValueLocation { public: NodeIdT next_use_id() const { return next_use_id_; } // Used in ValueNode::mark_use NodeIdT* get_next_use_id_address() { return &next_use_id_; } private: NodeIdT next_use_id_ = kInvalidNodeId; }; class Input : public InputLocation { public: explicit Input(ValueNode* node) : node_(node) {} ValueNode* node() const { return node_; } private: ValueNode* node_; }; class InterpretedDeoptFrame; class InlinedArgumentsDeoptFrame; class ConstructInvokeStubDeoptFrame; class BuiltinContinuationDeoptFrame; class DeoptFrame { public: enum class FrameType { kInterpretedFrame, kInlinedArgumentsFrame, kConstructInvokeStubFrame, kBuiltinContinuationFrame, }; struct InterpretedFrameData { const MaglevCompilationUnit& unit; const CompactInterpreterFrameState* frame_state; ValueNode* closure; const BytecodeOffset bytecode_position; const SourcePosition source_position; }; struct InlinedArgumentsFrameData { const MaglevCompilationUnit& unit; const BytecodeOffset bytecode_position; ValueNode* closure; const base::Vector<ValueNode*> arguments; }; struct ConstructInvokeStubFrameData { const MaglevCompilationUnit& unit; const SourcePosition source_position; ValueNode* receiver; ValueNode* context; }; struct BuiltinContinuationFrameData { const Builtin builtin_id; const base::Vector<ValueNode*> parameters; ValueNode* context; compiler::OptionalJSFunctionRef maybe_js_target; }; using FrameData = base::DiscriminatedUnion< FrameType, InterpretedFrameData, InlinedArgumentsFrameData, ConstructInvokeStubFrameData, BuiltinContinuationFrameData>; DeoptFrame(FrameData&& data, DeoptFrame* parent) : data_(std::move(data)), parent_(parent) {} FrameType type() const { return data_.tag(); } DeoptFrame* parent() { return parent_; } const DeoptFrame* parent() const { return parent_; } inline const InterpretedDeoptFrame& as_interpreted() const; inline const InlinedArgumentsDeoptFrame& as_inlined_arguments() const; inline const ConstructInvokeStubDeoptFrame& as_construct_stub() const; inline const BuiltinContinuationDeoptFrame& as_builtin_continuation() const; inline InterpretedDeoptFrame& as_interpreted(); inline InlinedArgumentsDeoptFrame& as_inlined_arguments(); inline ConstructInvokeStubDeoptFrame& as_construct_stub(); inline BuiltinContinuationDeoptFrame& as_builtin_continuation(); inline bool IsJsFrame() const; protected: DeoptFrame(InterpretedFrameData&& data, DeoptFrame* parent) : data_(std::move(data)), parent_(parent) {} DeoptFrame(InlinedArgumentsFrameData&& data, DeoptFrame* parent) : data_(std::move(data)), parent_(parent) {} DeoptFrame(ConstructInvokeStubFrameData&& data, DeoptFrame* parent) : data_(std::move(data)), parent_(parent) {} DeoptFrame(BuiltinContinuationFrameData&& data, DeoptFrame* parent) : data_(std::move(data)), parent_(parent) {} FrameData data_; DeoptFrame* const parent_; }; class InterpretedDeoptFrame : public DeoptFrame { public: InterpretedDeoptFrame(const MaglevCompilationUnit& unit, const CompactInterpreterFrameState* frame_state, ValueNode* closure, BytecodeOffset bytecode_position, SourcePosition source_position, DeoptFrame* parent) : DeoptFrame(InterpretedFrameData{unit, frame_state, closure, bytecode_position, source_position}, parent) {} const MaglevCompilationUnit& unit() const { return data().unit; } const CompactInterpreterFrameState* frame_state() const { return data().frame_state; } ValueNode*& closure() { return data().closure; } ValueNode* closure() const { return data().closure; } BytecodeOffset bytecode_position() const { return data().bytecode_position; } SourcePosition source_position() const { return data().source_position; } private: InterpretedFrameData& data() { return data_.get<InterpretedFrameData>(); } const InterpretedFrameData& data() const { return data_.get<InterpretedFrameData>(); } }; // Make sure storing/passing deopt frames by value doesn't truncate them. static_assert(sizeof(InterpretedDeoptFrame) == sizeof(DeoptFrame)); inline const InterpretedDeoptFrame& DeoptFrame::as_interpreted() const { DCHECK_EQ(type(), FrameType::kInterpretedFrame); return static_cast<const InterpretedDeoptFrame&>(*this); } inline InterpretedDeoptFrame& DeoptFrame::as_interpreted() { DCHECK_EQ(type(), FrameType::kInterpretedFrame); return static_cast<InterpretedDeoptFrame&>(*this); } class InlinedArgumentsDeoptFrame : public DeoptFrame { public: InlinedArgumentsDeoptFrame(const MaglevCompilationUnit& unit, BytecodeOffset bytecode_position, ValueNode* closure, base::Vector<ValueNode*> arguments, DeoptFrame* parent) : DeoptFrame(InlinedArgumentsFrameData{unit, bytecode_position, closure, arguments}, parent) {} const MaglevCompilationUnit& unit() const { return data().unit; } BytecodeOffset bytecode_position() const { return data().bytecode_position; } ValueNode*& closure() { return data().closure; } ValueNode* closure() const { return data().closure; } base::Vector<ValueNode*> arguments() const { return data().arguments; } private: InlinedArgumentsFrameData& data() { return data_.get<InlinedArgumentsFrameData>(); } const InlinedArgumentsFrameData& data() const { return data_.get<InlinedArgumentsFrameData>(); } }; // Make sure storing/passing deopt frames by value doesn't truncate them. static_assert(sizeof(InlinedArgumentsDeoptFrame) == sizeof(DeoptFrame)); inline const InlinedArgumentsDeoptFrame& DeoptFrame::as_inlined_arguments() const { DCHECK_EQ(type(), FrameType::kInlinedArgumentsFrame); return static_cast<const InlinedArgumentsDeoptFrame&>(*this); } inline InlinedArgumentsDeoptFrame& DeoptFrame::as_inlined_arguments() { DCHECK_EQ(type(), FrameType::kInlinedArgumentsFrame); return static_cast<InlinedArgumentsDeoptFrame&>(*this); } class ConstructInvokeStubDeoptFrame : public DeoptFrame { public: ConstructInvokeStubDeoptFrame(const MaglevCompilationUnit& unit, SourcePosition source_position, ValueNode* receiver, ValueNode* context, DeoptFrame* parent) : DeoptFrame(ConstructInvokeStubFrameData{unit, source_position, receiver, context}, parent) {} const MaglevCompilationUnit& unit() const { return data().unit; } ValueNode*& receiver() { return data().receiver; } ValueNode* receiver() const { return data().receiver; } ValueNode*& context() { return data().context; } ValueNode* context() const { return data().context; } SourcePosition source_position() const { return data().source_position; } private: ConstructInvokeStubFrameData& data() { return data_.get<ConstructInvokeStubFrameData>(); } const ConstructInvokeStubFrameData& data() const { return data_.get<ConstructInvokeStubFrameData>(); } }; // Make sure storing/passing deopt frames by value doesn't truncate them. static_assert(sizeof(ConstructInvokeStubDeoptFrame) == sizeof(DeoptFrame)); inline const ConstructInvokeStubDeoptFrame& DeoptFrame::as_construct_stub() const { DCHECK_EQ(type(), FrameType::kConstructInvokeStubFrame); return static_cast<const ConstructInvokeStubDeoptFrame&>(*this); } inline ConstructInvokeStubDeoptFrame& DeoptFrame::as_construct_stub() { DCHECK_EQ(type(), FrameType::kConstructInvokeStubFrame); return static_cast<ConstructInvokeStubDeoptFrame&>(*this); } class BuiltinContinuationDeoptFrame : public DeoptFrame { public: BuiltinContinuationDeoptFrame(Builtin builtin_id, base::Vector<ValueNode*> parameters, ValueNode* context, compiler::OptionalJSFunctionRef maybe_js_target, DeoptFrame* parent) : DeoptFrame(BuiltinContinuationFrameData{builtin_id, parameters, context, maybe_js_target}, parent) {} const Builtin& builtin_id() const { return data().builtin_id; } base::Vector<ValueNode*> parameters() const { return data().parameters; } ValueNode*& context() { return data().context; } ValueNode* context() const { return data().context; } bool is_javascript() const { return data().maybe_js_target.has_value(); } compiler::JSFunctionRef javascript_target() const { return data().maybe_js_target.value(); } private: BuiltinContinuationFrameData& data() { return data_.get<BuiltinContinuationFrameData>(); } const BuiltinContinuationFrameData& data() const { return data_.get<BuiltinContinuationFrameData>(); } }; // Make sure storing/passing deopt frames by value doesn't truncate them. static_assert(sizeof(BuiltinContinuationDeoptFrame) == sizeof(DeoptFrame)); inline const BuiltinContinuationDeoptFrame& DeoptFrame::as_builtin_continuation() const { DCHECK_EQ(type(), FrameType::kBuiltinContinuationFrame); return static_cast<const BuiltinContinuationDeoptFrame&>(*this); } inline BuiltinContinuationDeoptFrame& DeoptFrame::as_builtin_continuation() { DCHECK_EQ(type(), FrameType::kBuiltinContinuationFrame); return static_cast<BuiltinContinuationDeoptFrame&>(*this); } inline bool DeoptFrame::IsJsFrame() const { // This must be in sync with TRANSLATION_JS_FRAME_OPCODE_LIST in // translation-opcode.h or bad things happen. switch (data_.tag()) { case FrameType::kInterpretedFrame: return true; case FrameType::kBuiltinContinuationFrame: return as_builtin_continuation().is_javascript(); case FrameType::kConstructInvokeStubFrame: case FrameType::kInlinedArgumentsFrame: return false; } } class DeoptInfo { protected: DeoptInfo(Zone* zone, const DeoptFrame top_frame, compiler::FeedbackSource feedback_to_update); public: DeoptFrame& top_frame() { return top_frame_; } const DeoptFrame& top_frame() const { return top_frame_; } const compiler::FeedbackSource& feedback_to_update() const { return feedback_to_update_; } InputLocation* input_locations() const { return input_locations_; } Label* deopt_entry_label() { return &deopt_entry_label_; } int translation_index() const { return translation_index_; } void set_translation_index(int index) { translation_index_ = index; } private: DeoptFrame top_frame_; const compiler::FeedbackSource feedback_to_update_; InputLocation* const input_locations_; Label deopt_entry_label_; int translation_index_ = -1; }; struct RegisterSnapshot { RegList live_registers; RegList live_tagged_registers; DoubleRegList live_double_registers; }; class EagerDeoptInfo : public DeoptInfo { public: EagerDeoptInfo(Zone* zone, const DeoptFrame top_frame, compiler::FeedbackSource feedback_to_update) : DeoptInfo(zone, top_frame, feedback_to_update) {} DeoptimizeReason reason() const { return reason_; } void set_reason(DeoptimizeReason reason) { reason_ = reason; } private: DeoptimizeReason reason_ = DeoptimizeReason::kUnknown; }; class LazyDeoptInfo : public DeoptInfo { public: LazyDeoptInfo(Zone* zone, const DeoptFrame top_frame, interpreter::Register result_location, int result_size, compiler::FeedbackSource feedback_to_update) : DeoptInfo(zone, top_frame, feedback_to_update), result_location_(result_location), bitfield_( DeoptingCallReturnPcField::encode(kUninitializedCallReturnPc) | ResultSizeField::encode(result_size)) {} interpreter::Register result_location() const { DCHECK(IsConsideredForResultLocation()); return result_location_; } int result_size() const { DCHECK(IsConsideredForResultLocation()); return ResultSizeField::decode(bitfield_); } bool IsResultRegister(interpreter::Register reg) const; void UpdateResultLocation(interpreter::Register result_location, int result_size) { // We should only update to a subset of the existing result location. DCHECK_GE(result_location.index(), result_location_.index()); DCHECK_LE(result_location.index() + result_size, result_location_.index() + this->result_size()); result_location_ = result_location; bitfield_ = ResultSizeField::update(bitfield_, result_size); } bool HasResultLocation() const { DCHECK(IsConsideredForResultLocation()); return result_location_.is_valid(); } int deopting_call_return_pc() const { DCHECK_NE(DeoptingCallReturnPcField::decode(bitfield_), kUninitializedCallReturnPc); return DeoptingCallReturnPcField::decode(bitfield_); } void set_deopting_call_return_pc(int pc) { DCHECK_EQ(DeoptingCallReturnPcField::decode(bitfield_), kUninitializedCallReturnPc); bitfield_ = DeoptingCallReturnPcField::update(bitfield_, pc); } private: #ifdef DEBUG bool IsConsideredForResultLocation() const { switch (top_frame().type()) { case DeoptFrame::FrameType::kInterpretedFrame: // Interpreted frames obviously need a result location. return true; case DeoptFrame::FrameType::kInlinedArgumentsFrame: case DeoptFrame::FrameType::kConstructInvokeStubFrame: return false; case DeoptFrame::FrameType::kBuiltinContinuationFrame: // Normally if the function is going to be deoptimized then the top // frame should be an interpreted one. The only exception is the case // when the lazy deopt point was added only for the sake of recoring // an inlined Api function instance in the deopt info for exception // stack trace reconstruction. return top_frame().as_builtin_continuation().builtin_id() == Builtin::kGenericLazyDeoptContinuation; } } #endif // DEBUG using DeoptingCallReturnPcField = base::BitField<unsigned int, 0, 30>; using ResultSizeField = DeoptingCallReturnPcField::Next<unsigned int, 2>; // The max code size is enforced by the various assemblers, but it's not // visible here, so static assert against the magic constant that we happen // to know is correct. static constexpr int kMaxCodeSize = 512 * MB; static constexpr unsigned int kUninitializedCallReturnPc = DeoptingCallReturnPcField::kMax; static_assert(DeoptingCallReturnPcField::is_valid(kMaxCodeSize)); static_assert(kMaxCodeSize != kUninitializedCallReturnPc); // Lazy deopts can have at most two result registers -- temporarily three for // ForInPrepare. static_assert(ResultSizeField::kMax >= 3); interpreter::Register result_location_; uint32_t bitfield_; }; class ExceptionHandlerInfo { public: const int kNoExceptionHandlerPCOffsetMarker = 0xdeadbeef; ExceptionHandlerInfo() : catch_block(), pc_offset(kNoExceptionHandlerPCOffsetMarker) {} explicit ExceptionHandlerInfo(BasicBlockRef* catch_block_ref) : catch_block(catch_block_ref), pc_offset(-1) {} bool HasExceptionHandler() { return pc_offset != kNoExceptionHandlerPCOffsetMarker; } BasicBlockRef catch_block; Label trampoline_entry; int pc_offset; }; // Dummy type for the initial raw allocation. struct NodeWithInlineInputs {}; namespace detail { // Helper for getting the static opcode of a Node subclass. This is in a // "detail" namespace rather than in NodeBase because we can't template // specialize outside of namespace scopes before C++17. template <class T> struct opcode_of_helper; #define DEF_OPCODE_OF(Name) \ template <> \ struct opcode_of_helper<Name> { \ static constexpr Opcode value = Opcode::k##Name; \ }; NODE_BASE_LIST(DEF_OPCODE_OF) #undef DEF_OPCODE_OF template <typename T> constexpr T* ObjectPtrBeforeAddress(void* address) { char* address_as_char_ptr = reinterpret_cast<char*>(address); char* object_ptr_as_char_ptr = address_as_char_ptr - sizeof(T); return reinterpret_cast<T*>(object_ptr_as_char_ptr); } template <typename T> constexpr const T* ObjectPtrBeforeAddress(const void* address) { const char* address_as_char_ptr = reinterpret_cast<const char*>(address); const char* object_ptr_as_char_ptr = address_as_char_ptr - sizeof(T); return reinterpret_cast<const T*>(object_ptr_as_char_ptr); } } // namespace detail class NodeBase : public ZoneObject { private: // Bitfield specification. using OpcodeField = base::BitField64<Opcode, 0, 16>; static_assert(OpcodeField::is_valid(kLastOpcode)); using OpPropertiesField = OpcodeField::Next<OpProperties, OpProperties::kSize>; using NumTemporariesNeededField = OpPropertiesField::Next<uint8_t, 2>; using NumDoubleTemporariesNeededField = NumTemporariesNeededField::Next<uint8_t, 1>; // Align input count to 32-bit. using UnusedField = NumDoubleTemporariesNeededField::Next<bool, 1>; using InputCountField = UnusedField::Next<size_t, 17>; static_assert(InputCountField::kShift == 32); protected: using SingleSpareBitField = UnusedField; // Subclasses may use the remaining bitfield bits. template <class T, int size> using NextBitField = InputCountField::Next<T, size>; static constexpr int kMaxInputs = InputCountField::kMax; public: template <class T> static constexpr Opcode opcode_of = detail::opcode_of_helper<T>::value; template <class Derived, typename... Args> static Derived* New(Zone* zone, std::initializer_list<ValueNode*> inputs, Args&&... args) { Derived* node = Allocate<Derived>(zone, inputs.size(), std::forward<Args>(args)...); int i = 0; for (ValueNode* input : inputs) { DCHECK_NOT_NULL(input); node->set_input(i++, input); } return node; } // Inputs must be initialized manually. template <class Derived, typename... Args> static Derived* New(Zone* zone, size_t input_count, Args&&... args) { Derived* node = Allocate<Derived>(zone, input_count, std::forward<Args>(args)...); return node; } // Overwritten by subclasses. static constexpr OpProperties kProperties = OpProperties::Pure() | OpProperties::TaggedValue(); constexpr Opcode opcode() const { return OpcodeField::decode(bitfield_); } constexpr OpProperties properties() const { return OpPropertiesField::decode(bitfield_); } void set_properties(OpProperties properties) { bitfield_ = OpPropertiesField::update(bitfield_, properties); } template <class T> constexpr bool Is() const; template <class T> constexpr T* Cast() { DCHECK(Is<T>()); return static_cast<T*>(this); } template <class T> constexpr const T* Cast() const { DCHECK(Is<T>()); return static_cast<const T*>(this); } template <class T> constexpr T* TryCast() { return Is<T>() ? static_cast<T*>(this) : nullptr; } constexpr bool has_inputs() const { return input_count() > 0; } constexpr int input_count() const { static_assert(InputCountField::kMax <= kMaxInt); return static_cast<int>(InputCountField::decode(bitfield_)); } constexpr Input& input(int index) { DCHECK_LT(index, input_count()); return *(input_base() - index); } constexpr const Input& input(int index) const { DCHECK_LT(index, input_count()); return *(input_base() - index); } // Input iterators, use like: // // for (Input& input : *node) { ... } constexpr auto begin() { return std::make_reverse_iterator(&input(-1)); } constexpr auto end() { return std::make_reverse_iterator(&input(input_count() - 1)); } constexpr bool has_id() const { return id_ != kInvalidNodeId; } constexpr NodeIdT id() const { DCHECK_NE(id_, kInvalidNodeId); return id_; } void set_id(NodeIdT id) { DCHECK_EQ(id_, kInvalidNodeId); DCHECK_NE(id, kInvalidNodeId); id_ = id; } template <typename RegisterT> uint8_t num_temporaries_needed() const { if constexpr (std::is_same_v<RegisterT, Register>) { return NumTemporariesNeededField::decode(bitfield_); } else { return NumDoubleTemporariesNeededField::decode(bitfield_); } } template <typename RegisterT> RegListBase<RegisterT>& temporaries() { if constexpr (std::is_same_v<RegisterT, Register>) { return temporaries_; } else { return double_temporaries_; } } RegList& general_temporaries() { return temporaries_; } DoubleRegList& double_temporaries() { return double_temporaries_; } template <typename RegisterT> void assign_temporaries(RegListBase<RegisterT> list) { if constexpr (std::is_same_v<RegisterT, Register>) { temporaries_ = list; } else { double_temporaries_ = list; } } enum class InputAllocationPolicy { kFixedRegister, kArbitraryRegister, kAny }; // Some parts of Maglev require a specific iteration order of the inputs (such // as UseMarkingProcessor::MarkInputUses or // StraightForwardRegisterAllocator::AssignInputs). For such cases, // `ForAllInputsInRegallocAssignmentOrder` can be called with a callback `f` // that will be called for each input in the "correct" order. template <typename Function> void ForAllInputsInRegallocAssignmentOrder(Function&& f); void Print(std::ostream& os, MaglevGraphLabeller*, bool skip_targets = false) const; // For GDB: Print any Node with `print node->Print()`. void Print() const; EagerDeoptInfo* eager_deopt_info() { DCHECK(properties().can_eager_deopt()); DCHECK(!properties().can_lazy_deopt()); return reinterpret_cast<EagerDeoptInfo*>(deopt_info_address()); } LazyDeoptInfo* lazy_deopt_info() { DCHECK(properties().can_lazy_deopt()); DCHECK(!properties().can_eager_deopt()); return reinterpret_cast<LazyDeoptInfo*>(deopt_info_address()); } const RegisterSnapshot& register_snapshot() const { DCHECK(properties().needs_register_snapshot()); return *reinterpret_cast<RegisterSnapshot*>(register_snapshot_address()); } ExceptionHandlerInfo* exception_handler_info() { DCHECK(properties().can_throw()); return reinterpret_cast<ExceptionHandlerInfo*>(exception_handler_address()); } void set_register_snapshot(RegisterSnapshot snapshot) { DCHECK(properties().needs_register_snapshot()); *reinterpret_cast<RegisterSnapshot*>(register_snapshot_address()) = snapshot; } inline void change_input(int index, ValueNode* node); void change_representation(ValueRepresentation new_repr) { DCHECK_EQ(opcode(), Opcode::kPhi); bitfield_ = OpPropertiesField::update( bitfield_, properties().WithNewValueRepresentation(new_repr)); } void set_opcode(Opcode new_opcode) { bitfield_ = OpcodeField::update(bitfield_, new_opcode); } void CopyEagerDeoptInfoOf(NodeBase* other, Zone* zone) { new (eager_deopt_info()) EagerDeoptInfo(zone, other->eager_deopt_info()->top_frame(), other->eager_deopt_info()->feedback_to_update()); } void SetEagerDeoptInfo(Zone* zone, DeoptFrame deopt_frame, compiler::FeedbackSource feedback_to_update = compiler::FeedbackSource()) { DCHECK(properties().can_eager_deopt()); new (eager_deopt_info()) EagerDeoptInfo(zone, deopt_frame, feedback_to_update); } template <typename NodeT> void OverwriteWith() { OverwriteWith(NodeBase::opcode_of<NodeT>, NodeT::kProperties); } void OverwriteWith( Opcode new_opcode, base::Optional<OpProperties> maybe_new_properties = base::nullopt) { OpProperties new_properties = maybe_new_properties.has_value() ? maybe_new_properties.value() : StaticPropertiesForOpcode(new_opcode); #ifdef DEBUG CheckCanOverwriteWith(new_opcode, new_properties); #endif set_opcode(new_opcode); set_properties(new_properties); } protected: explicit NodeBase(uint64_t bitfield) : bitfield_(bitfield) {} // Allow updating bits above NextBitField from subclasses constexpr uint64_t bitfield() const { return bitfield_; } void set_bitfield(uint64_t new_bitfield) { #ifdef DEBUG // Make sure that all the base bitfield bits (all bits before the next // bitfield start, excluding any spare bits) are equal in the new value. const uint64_t base_bitfield_mask = ((uint64_t{1} << NextBitField<bool, 1>::kShift) - 1) & ~SingleSpareBitField::kMask; DCHECK_EQ(bitfield_ & base_bitfield_mask, new_bitfield & base_bitfield_mask); #endif bitfield_ = new_bitfield; } constexpr Input* input_base() { return detail::ObjectPtrBeforeAddress<Input>(this); } constexpr const Input* input_base() const { return detail::ObjectPtrBeforeAddress<Input>(this); } Input* last_input() { return &input(input_count() - 1); } const Input* last_input() const { return &input(input_count() - 1); } Address last_input_address() const { return reinterpret_cast<Address>(last_input()); } inline void initialize_input_null(int index); inline void set_input(int index, ValueNode* node); // For nodes that don't have data past the input, allow trimming the input // count. This is used by Phis to reduce inputs when merging in dead control // flow. void reduce_input_count() { DCHECK_EQ(opcode(), Opcode::kPhi); DCHECK(!properties().can_lazy_deopt()); DCHECK(!properties().can_eager_deopt()); bitfield_ = InputCountField::update(bitfield_, input_count() - 1); } // Specify that there need to be a certain number of registers free (i.e. // useable as scratch registers) on entry into this node. // // Does not include any registers requested by RequireSpecificTemporary. void set_temporaries_needed(uint8_t value) { DCHECK_EQ(num_temporaries_needed<Register>(), 0); bitfield_ = NumTemporariesNeededField::update(bitfield_, value); } void set_double_temporaries_needed(uint8_t value) { DCHECK_EQ(num_temporaries_needed<DoubleRegister>(), 0); bitfield_ = NumDoubleTemporariesNeededField::update(bitfield_, value); } // Require that a specific register is free (and therefore clobberable) by the // entry into this node. void RequireSpecificTemporary(Register reg) { temporaries_.set(reg); } void RequireSpecificDoubleTemporary(DoubleRegister reg) { double_temporaries_.set(reg); } private: template <class Derived, typename... Args> static Derived* Allocate(Zone* zone, size_t input_count, Args&&... args) { static_assert( !Derived::kProperties.can_eager_deopt() || !Derived::kProperties.can_lazy_deopt(), "The current deopt info representation, at the end of inputs, requires " "that we cannot have both lazy and eager deopts on a node. If we ever " "need this, we have to update accessors to check node->properties() " "for which deopts are active."); constexpr size_t size_before_inputs = ExceptionHandlerInfoSize(Derived::kProperties) + RegisterSnapshotSize(Derived::kProperties) + EagerDeoptInfoSize(Derived::kProperties) + LazyDeoptInfoSize(Derived::kProperties); static_assert(IsAligned(size_before_inputs, alignof(Input))); const size_t size_before_node = size_before_inputs + input_count * sizeof(Input); DCHECK(IsAligned(size_before_inputs, alignof(Derived))); const size_t size = size_before_node + sizeof(Derived); intptr_t raw_buffer = reinterpret_cast<intptr_t>(zone->Allocate<NodeWithInlineInputs>(size)); #ifdef DEBUG memset(reinterpret_cast<void*>(raw_buffer), 0, size); #endif void* node_buffer = reinterpret_cast<void*>(raw_buffer + size_before_node); uint64_t bitfield = OpcodeField::encode(opcode_of<Derived>) | OpPropertiesField::encode(Derived::kProperties) | InputCountField::encode(input_count); Derived* node = new (node_buffer) Derived(bitfield, std::forward<Args>(args)...); return node; } static constexpr size_t ExceptionHandlerInfoSize(OpProperties properties) { return RoundUp<alignof(Input)>( properties.can_throw() ? sizeof(ExceptionHandlerInfo) : 0); } static constexpr size_t RegisterSnapshotSize(OpProperties properties) { return RoundUp<alignof(Input)>( properties.needs_register_snapshot() ? sizeof(RegisterSnapshot) : 0); } static constexpr size_t EagerDeoptInfoSize(OpProperties properties) { return RoundUp<alignof(Input)>( properties.can_eager_deopt() ? sizeof(EagerDeoptInfo) : 0); } static constexpr size_t LazyDeoptInfoSize(OpProperties properties) { return RoundUp<alignof(Input)>( properties.can_lazy_deopt() ? sizeof(LazyDeoptInfo) : 0); } // Returns the position of deopt info if it exists, otherwise returns // its position as if DeoptInfo size were zero. Address deopt_info_address() const { DCHECK(!properties().can_eager_deopt() || !properties().can_lazy_deopt()); size_t extra = EagerDeoptInfoSize(properties()) + LazyDeoptInfoSize(properties()); return last_input_address() - extra; } // Returns the position of register snapshot if it exists, otherwise returns // its position as if RegisterSnapshot size were zero. Address register_snapshot_address() const { size_t extra = RegisterSnapshotSize(properties()); return deopt_info_address() - extra; } // Returns the position of exception handler info if it exists, otherwise // returns its position as if ExceptionHandlerInfo size were zero. Address exception_handler_address() const { size_t extra = ExceptionHandlerInfoSize(properties()); return register_snapshot_address() - extra; } void CheckCanOverwriteWith(Opcode new_opcode, OpProperties new_properties); uint64_t bitfield_; NodeIdT id_ = kInvalidNodeId; RegList temporaries_; DoubleRegList double_temporaries_; NodeBase() = delete; NodeBase(const NodeBase&) = delete; NodeBase(NodeBase&&) = delete; NodeBase& operator=(const NodeBase&) = delete; NodeBase& operator=(NodeBase&&) = delete; }; template <class T> constexpr bool NodeBase::Is() const { return opcode() == opcode_of<T>; } // Specialized sub-hierarchy type checks. template <> constexpr bool NodeBase::Is<ValueNode>() const { return IsValueNode(opcode()); } template <> constexpr bool NodeBase::Is<ControlNode>() const { return IsControlNode(opcode()); } template <> constexpr bool NodeBase::Is<BranchControlNode>() const { return IsBranchControlNode(opcode()); } template <> constexpr bool NodeBase::Is<ConditionalControlNode>() const { return IsConditionalControlNode(opcode()); } template <> constexpr bool NodeBase::Is<UnconditionalControlNode>() const { return IsUnconditionalControlNode(opcode()); } template <> constexpr bool NodeBase::Is<TerminalControlNode>() const { return IsTerminalControlNode(opcode()); } void CheckValueInputIs(const NodeBase* node, int i, ValueRepresentation expected, MaglevGraphLabeller* graph_labeller); // The Node class hierarchy contains all non-control nodes. class Node : public NodeBase { public: using List = base::ThreadedListWithUnsafeInsertions<Node>; inline ValueLocation& result(); Node* NextNode() const { return next_; } protected: using NodeBase::NodeBase; private: Node** next() { return &next_; } Node* next_ = nullptr; friend List; friend base::ThreadedListTraits<Node>; }; // All non-control nodes with a result. class ValueNode : public Node { private: using TaggedResultNeedsDecompressField = NodeBase::SingleSpareBitField; protected: using SingleSpareBitField = void; public: ValueLocation& result() { return result_; } const ValueLocation& result() const { return result_; } int use_count() const { // Invalid to check use_count externally once an id is allocated. DCHECK(!has_id()); return use_count_; } bool is_used() const { return use_count_ > 0; } bool unused_inputs_were_visited() const { return use_count_ == -1; } void add_use() { // Make sure a saturated use count won't overflow. DCHECK_LT(use_count_, kMaxInt); use_count_++; } void remove_use() { // Make sure a saturated use count won't drop below zero. DCHECK_GT(use_count_, 0); use_count_--; } // Avoid revisiting nodes when processing an unused node's inputs, by marking // it as visited. void mark_unused_inputs_visited() { DCHECK_EQ(use_count_, 0); use_count_ = -1; } void SetHint(compiler::InstructionOperand hint); void ClearHint() { hint_ = compiler::InstructionOperand(); } bool has_hint() { return !hint_.IsInvalid(); } template <typename RegisterT> RegisterT GetRegisterHint() { if (hint_.IsInvalid()) return RegisterT::no_reg(); return RegisterT::from_code( compiler::UnallocatedOperand::cast(hint_).fixed_register_index()); } const compiler::InstructionOperand& hint() const { DCHECK(hint_.IsInvalid() || hint_.IsUnallocated()); return hint_; } bool is_loadable() const { DCHECK_EQ(state_, kSpill); return spill_.IsConstant() || spill_.IsAnyStackSlot(); } bool is_spilled() const { DCHECK_EQ(state_, kSpill); return spill_.IsAnyStackSlot(); } void SetNoSpill(); void SetConstantLocation(); /* For constants only. */ void LoadToRegister(MaglevAssembler*, Register); void LoadToRegister(MaglevAssembler*, DoubleRegister); void DoLoadToRegister(MaglevAssembler*, Register); void DoLoadToRegister(MaglevAssembler*, DoubleRegister); Handle<Object> Reify(LocalIsolate* isolate) const; void Spill(compiler::AllocatedOperand operand) { #ifdef DEBUG if (state_ == kLastUse) { state_ = kSpill; } else { DCHECK(!is_loadable()); } #endif // DEBUG DCHECK(!IsConstantNode(opcode())); DCHECK(operand.IsAnyStackSlot()); spill_ = operand; DCHECK(spill_.IsAnyStackSlot()); } compiler::AllocatedOperand spill_slot() const { DCHECK(is_spilled()); return compiler::AllocatedOperand::cast(loadable_slot()); } compiler::InstructionOperand loadable_slot() const { DCHECK_EQ(state_, kSpill); DCHECK(is_loadable()); return spill_; } void record_next_use(NodeIdT id, InputLocation* input_location) { DCHECK_EQ(state_, kLastUse); DCHECK_NE(id, kInvalidNodeId); DCHECK_LT(start_id(), id); DCHECK_IMPLIES(has_valid_live_range(), id >= end_id_); end_id_ = id; *last_uses_next_use_id_ = id; last_uses_next_use_id_ = input_location->get_next_use_id_address(); DCHECK_EQ(*last_uses_next_use_id_, kInvalidNodeId); } struct LiveRange { NodeIdT start = kInvalidNodeId; NodeIdT end = kInvalidNodeId; // Inclusive. }; bool has_valid_live_range() const { return end_id_ != 0; } LiveRange live_range() const { return {start_id(), end_id_}; } NodeIdT current_next_use() const { return next_use_; } // The following metods should only be used during register allocation, to // mark the _current_ state of this Node according to the register allocator. void advance_next_use(NodeIdT use) { next_use_ = use; } bool has_no_more_uses() const { return next_use_ == kInvalidNodeId; } constexpr bool use_double_register() const { return IsDoubleRepresentation(properties().value_representation()); } constexpr bool is_tagged() const { return (properties().value_representation() == ValueRepresentation::kTagged); } #ifdef V8_COMPRESS_POINTERS constexpr bool decompresses_tagged_result() const { return TaggedResultNeedsDecompressField::decode(bitfield()); } void SetTaggedResultNeedsDecompress() { static_assert(PointerCompressionIsEnabled()); DCHECK_IMPLIES(!Is<Identity>(), is_tagged()); DCHECK_IMPLIES(Is<Identity>(), input(0).node()->is_tagged()); set_bitfield(TaggedResultNeedsDecompressField::update(bitfield(), true)); if (Is<Phi>()) { for (Input& input : *this) { // Avoid endless recursion by terminating on values already marked. if (input.node()->decompresses_tagged_result()) continue; input.node()->SetTaggedResultNeedsDecompress(); } } else if (Is<Identity>()) { DCHECK_EQ(input_count(), 0); input(0).node()->SetTaggedResultNeedsDecompress(); } } #else constexpr bool decompresses_tagged_result() const { return false; } #endif constexpr ValueRepresentation value_representation() const { return properties().value_representation(); } constexpr MachineRepresentation GetMachineRepresentation() const { switch (properties().value_representation()) { case ValueRepresentation::kTagged: return MachineRepresentation::kTagged; case ValueRepresentation::kInt32: case ValueRepresentation::kUint32: return MachineRepresentation::kWord32; case ValueRepresentation::kWord64: return MachineRepresentation::kWord64; case ValueRepresentation::kFloat64: return MachineRepresentation::kFloat64; case ValueRepresentation::kHoleyFloat64: return MachineRepresentation::kFloat64; } } void InitializeRegisterData() { if (use_double_register()) { double_registers_with_result_ = kEmptyDoubleRegList; } else { registers_with_result_ = kEmptyRegList; } } void AddRegister(Register reg) { DCHECK(!use_double_register()); registers_with_result_.set(reg); } void AddRegister(DoubleRegister reg) { DCHECK(use_double_register()); double_registers_with_result_.set(reg); } void RemoveRegister(Register reg) { DCHECK(!use_double_register()); registers_with_result_.clear(reg); } void RemoveRegister(DoubleRegister reg) { DCHECK(use_double_register()); double_registers_with_result_.clear(reg); } template <typename T> inline RegListBase<T> ClearRegisters(); int num_registers() const { if (use_double_register()) { return double_registers_with_result_.Count(); } return registers_with_result_.Count(); } bool has_register() const { if (use_double_register()) { return double_registers_with_result_ != kEmptyDoubleRegList; } return registers_with_result_ != kEmptyRegList; } bool is_in_register(Register reg) const { DCHECK(!use_double_register()); return registers_with_result_.has(reg); } bool is_in_register(DoubleRegister reg) const { DCHECK(use_double_register()); return double_registers_with_result_.has(reg); } template <typename T> RegListBase<T> result_registers() { if constexpr (std::is_same<T, DoubleRegister>::value) { DCHECK(use_double_register()); return double_registers_with_result_; } else { DCHECK(!use_double_register()); return registers_with_result_; } } compiler::InstructionOperand allocation() const { if (has_register()) { return compiler::AllocatedOperand(compiler::LocationOperand::REGISTER, GetMachineRepresentation(), FirstRegisterCode()); } DCHECK(is_loadable()); return spill_; } protected: explicit ValueNode(uint64_t bitfield) : Node(bitfield), last_uses_next_use_id_(&next_use_), hint_(compiler::InstructionOperand()), use_count_(0) #ifdef DEBUG , state_(kLastUse) #endif // DEBUG { InitializeRegisterData(); } int FirstRegisterCode() const { if (use_double_register()) { return double_registers_with_result_.first().code(); } return registers_with_result_.first().code(); } // Rename for better pairing with `end_id`. NodeIdT start_id() const { return id(); } NodeIdT end_id_ = kInvalidNodeId; NodeIdT next_use_ = kInvalidNodeId; ValueLocation result_; union { RegList registers_with_result_; DoubleRegList double_registers_with_result_; }; union { // Pointer to the current last use's next_use_id field. Most of the time // this will be a pointer to an Input's next_use_id_ field, but it's // initialized to this node's next_use_ to track the first use. NodeIdT* last_uses_next_use_id_; compiler::InstructionOperand spill_; }; compiler::InstructionOperand hint_; // TODO(leszeks): Union this into another field. int use_count_; #ifdef DEBUG enum {kLastUse, kSpill} state_; #endif // DEBUG }; inline void NodeBase::initialize_input_null(int index) { // Should already be null in debug, make sure it's null on release too. DCHECK_EQ(input(index).node(), nullptr); new (&input(index)) Input(nullptr); } inline void NodeBase::set_input(int index, ValueNode* node) { DCHECK_NOT_NULL(node); DCHECK_EQ(input(index).node(), nullptr); node->add_use(); new (&input(index)) Input(node); } inline void NodeBase::change_input(int index, ValueNode* node) { DCHECK_NE(input(index).node(), nullptr); input(index).node()->remove_use(); #ifdef DEBUG input(index) = Input(nullptr); #endif set_input(index, node); } template <> inline RegList ValueNode::ClearRegisters() { DCHECK(!use_double_register()); return std::exchange(registers_with_result_, kEmptyRegList); } template <> inline DoubleRegList ValueNode::ClearRegisters() { DCHECK(use_double_register()); return std::exchange(double_registers_with_result_, kEmptyDoubleRegList); } ValueLocation& Node::result() { DCHECK(Is<ValueNode>()); return Cast<ValueNode>()->result(); } // Mixin for a node with known class (and therefore known opcode and static // properties), but possibly unknown numbers of inputs. template <typename Base, typename Derived> class NodeTMixin : public Base { public: // Shadowing for static knowledge. constexpr Opcode opcode() const { return NodeBase::opcode_of<Derived>; } constexpr const OpProperties& properties() const { return Derived::kProperties; } template <typename... Args> static Derived* New(Zone* zone, std::initializer_list<ValueNode*> inputs, Args&&... args) { return NodeBase::New<Derived>(zone, inputs, std::forward<Args>...); } template <typename... Args> static Derived* New(Zone* zone, size_t input_count, Args&&... args) { return NodeBase::New<Derived>(zone, input_count, std::forward<Args>...); } protected: template <typename... Args> explicit NodeTMixin(uint64_t bitfield, Args&&... args) : Base(bitfield, std::forward<Args>(args)...) { DCHECK_EQ(this->NodeBase::opcode(), NodeBase::opcode_of<Derived>); DCHECK_EQ(this->NodeBase::properties(), Derived::kProperties); } }; namespace detail { // Helper class for defining input types as a std::array, but without // accidental initialisation with the wrong sized initializer_list. template <size_t Size> class ArrayWrapper : public std::array<ValueRepresentation, Size> { public: template <typename... Args> explicit constexpr ArrayWrapper(Args&&... args) : std::array<ValueRepresentation, Size>({args...}) { static_assert(sizeof...(args) == Size); } }; struct YouNeedToDefineAnInputTypesArrayInYourDerivedClass {}; } // namespace detail // Mixin for a node with known class (and therefore known opcode and static // properties), and known numbers of inputs. template <size_t InputCount, typename Base, typename Derived> class FixedInputNodeTMixin : public NodeTMixin<Base, Derived> { static constexpr size_t kInputCount = InputCount; public: // Shadowing for static knowledge. constexpr bool has_inputs() const { return input_count() > 0; } constexpr uint16_t input_count() const { return kInputCount; } constexpr auto end() { return std::make_reverse_iterator(&this->input(input_count() - 1)); } void VerifyInputs(MaglevGraphLabeller* graph_labeller) const { if constexpr (kInputCount != 0) { static_assert( std::is_same_v<const InputTypes, decltype(Derived::kInputTypes)>); static_assert(kInputCount == Derived::kInputTypes.size()); for (int i = 0; i < static_cast<int>(kInputCount); ++i) { CheckValueInputIs(this, i, Derived::kInputTypes[i], graph_labeller); } } } #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing() const { if constexpr (kInputCount != 0) { static_assert( std::is_same_v<const InputTypes, decltype(Derived::kInputTypes)>); static_assert(kInputCount == Derived::kInputTypes.size()); for (int i = 0; i < static_cast<int>(kInputCount); ++i) { if (Derived::kInputTypes[i] == ValueRepresentation::kTagged) { ValueNode* input_node = this->input(i).node(); input_node->SetTaggedResultNeedsDecompress(); } } } } #endif protected: using InputTypes = detail::ArrayWrapper<kInputCount>; detail::YouNeedToDefineAnInputTypesArrayInYourDerivedClass kInputTypes; template <typename... Args> explicit FixedInputNodeTMixin(uint64_t bitfield, Args&&... args) : NodeTMixin<Base, Derived>(bitfield, std::forward<Args>(args)...) { DCHECK_EQ(this->NodeBase::input_count(), kInputCount); } }; template <class Derived> using NodeT = NodeTMixin<Node, Derived>; template <class Derived> using ValueNodeT = NodeTMixin<ValueNode, Derived>; template <size_t InputCount, class Derived> using FixedInputNodeT = FixedInputNodeTMixin<InputCount, NodeT<Derived>, Derived>; template <size_t InputCount, class Derived> using FixedInputValueNodeT = FixedInputNodeTMixin<InputCount, ValueNodeT<Derived>, Derived>; class Identity : public FixedInputValueNodeT<1, Identity> { using Base = FixedInputValueNodeT<1, Identity>; public: static constexpr OpProperties kProperties = OpProperties::Pure(); explicit Identity(uint64_t bitfield) : Base(bitfield) {} void VerifyInputs(MaglevGraphLabeller*) const { // Identity is valid for all input types. } #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing() { // Do not mark inputs as decompressing here, since we don't yet know whether // this Phi needs decompression. Instead, let // Node::SetTaggedResultNeedsDecompress pass through phis. } #endif void SetValueLocationConstraints() {} void GenerateCode(MaglevAssembler*, const ProcessingState&) {} void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; template <class Derived, Operation kOperation> class UnaryWithFeedbackNode : public FixedInputValueNodeT<1, Derived> { using Base = FixedInputValueNodeT<1, Derived>; public: // The implementation currently calls runtime. static constexpr OpProperties kProperties = OpProperties::JSCall(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; static constexpr int kOperandIndex = 0; Input& operand_input() { return Node::input(kOperandIndex); } compiler::FeedbackSource feedback() const { return feedback_; } protected: explicit UnaryWithFeedbackNode(uint64_t bitfield, const compiler::FeedbackSource& feedback) : Base(bitfield), feedback_(feedback) {} void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} const compiler::FeedbackSource feedback_; }; template <class Derived, Operation kOperation> class BinaryWithFeedbackNode : public FixedInputValueNodeT<2, Derived> { using Base = FixedInputValueNodeT<2, Derived>; public: // The implementation currently calls runtime. static constexpr OpProperties kProperties = OpProperties::JSCall(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged}; static constexpr int kLeftIndex = 0; static constexpr int kRightIndex = 1; Input& left_input() { return Node::input(kLeftIndex); } Input& right_input() { return Node::input(kRightIndex); } compiler::FeedbackSource feedback() const { return feedback_; } protected: BinaryWithFeedbackNode(uint64_t bitfield, const compiler::FeedbackSource& feedback) : Base(bitfield), feedback_(feedback) {} void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} const compiler::FeedbackSource feedback_; }; #define DEF_OPERATION_WITH_FEEDBACK_NODE(Name, Super, OpName) \ class Name : public Super<Name, Operation::k##OpName> { \ using Base = Super<Name, Operation::k##OpName>; \ \ public: \ Name(uint64_t bitfield, const compiler::FeedbackSource& feedback) \ : Base(bitfield, feedback) {} \ int MaxCallStackArgs() const { return 0; } \ void SetValueLocationConstraints(); \ void GenerateCode(MaglevAssembler*, const ProcessingState&); \ void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} \ }; #define DEF_UNARY_WITH_FEEDBACK_NODE(Name) \ DEF_OPERATION_WITH_FEEDBACK_NODE(Generic##Name, UnaryWithFeedbackNode, Name) #define DEF_BINARY_WITH_FEEDBACK_NODE(Name) \ DEF_OPERATION_WITH_FEEDBACK_NODE(Generic##Name, BinaryWithFeedbackNode, Name) UNARY_OPERATION_LIST(DEF_UNARY_WITH_FEEDBACK_NODE) ARITHMETIC_OPERATION_LIST(DEF_BINARY_WITH_FEEDBACK_NODE) COMPARISON_OPERATION_LIST(DEF_BINARY_WITH_FEEDBACK_NODE) #undef DEF_UNARY_WITH_FEEDBACK_NODE #undef DEF_BINARY_WITH_FEEDBACK_NODE #undef DEF_OPERATION_WITH_FEEDBACK_NODE template <class Derived, Operation kOperation> class Int32BinaryWithOverflowNode : public FixedInputValueNodeT<2, Derived> { using Base = FixedInputValueNodeT<2, Derived>; public: static constexpr OpProperties kProperties = OpProperties::EagerDeopt() | OpProperties::Int32(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kInt32, ValueRepresentation::kInt32}; static constexpr int kLeftIndex = 0; static constexpr int kRightIndex = 1; Input& left_input() { return Node::input(kLeftIndex); } Input& right_input() { return Node::input(kRightIndex); } protected: explicit Int32BinaryWithOverflowNode(uint64_t bitfield) : Base(bitfield) {} void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; #define DEF_OPERATION_NODE(Name, Super, OpName) \ class Name : public Super<Name, Operation::k##OpName> { \ using Base = Super<Name, Operation::k##OpName>; \ \ public: \ explicit Name(uint64_t bitfield) : Base(bitfield) {} \ void SetValueLocationConstraints(); \ void GenerateCode(MaglevAssembler*, const ProcessingState&); \ void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} \ }; #define DEF_INT32_BINARY_WITH_OVERFLOW_NODE(Name) \ DEF_OPERATION_NODE(Int32##Name##WithOverflow, Int32BinaryWithOverflowNode, \ Name) DEF_INT32_BINARY_WITH_OVERFLOW_NODE(Add) DEF_INT32_BINARY_WITH_OVERFLOW_NODE(Subtract) DEF_INT32_BINARY_WITH_OVERFLOW_NODE(Multiply) DEF_INT32_BINARY_WITH_OVERFLOW_NODE(Divide) DEF_INT32_BINARY_WITH_OVERFLOW_NODE(Modulus) #undef DEF_INT32_BINARY_WITH_OVERFLOW_NODE template <class Derived, Operation kOperation> class Int32BinaryNode : public FixedInputValueNodeT<2, Derived> { using Base = FixedInputValueNodeT<2, Derived>; public: static constexpr OpProperties kProperties = OpProperties::Int32(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kInt32, ValueRepresentation::kInt32}; static constexpr int kLeftIndex = 0; static constexpr int kRightIndex = 1; Input& left_input() { return Node::input(kLeftIndex); } Input& right_input() { return Node::input(kRightIndex); } protected: explicit Int32BinaryNode(uint64_t bitfield) : Base(bitfield) {} }; #define DEF_INT32_BINARY_NODE(Name) \ DEF_OPERATION_NODE(Int32##Name, Int32BinaryNode, Name) DEF_INT32_BINARY_NODE(BitwiseAnd) DEF_INT32_BINARY_NODE(BitwiseOr) DEF_INT32_BINARY_NODE(BitwiseXor) DEF_INT32_BINARY_NODE(ShiftLeft) DEF_INT32_BINARY_NODE(ShiftRight) #undef DEF_INT32_BINARY_NODE class Int32BitwiseNot : public FixedInputValueNodeT<1, Int32BitwiseNot> { using Base = FixedInputValueNodeT<1, Int32BitwiseNot>; public: explicit Int32BitwiseNot(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::Int32(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kInt32}; static constexpr int kValueIndex = 0; Input& value_input() { return Node::input(kValueIndex); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; template <class Derived, Operation kOperation> class Int32UnaryWithOverflowNode : public FixedInputValueNodeT<1, Derived> { using Base = FixedInputValueNodeT<1, Derived>; public: static constexpr OpProperties kProperties = OpProperties::EagerDeopt() | OpProperties::Int32(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kInt32}; static constexpr int kValueIndex = 0; Input& value_input() { return Node::input(kValueIndex); } protected: explicit Int32UnaryWithOverflowNode(uint64_t bitfield) : Base(bitfield) {} }; #define DEF_INT32_UNARY_WITH_OVERFLOW_NODE(Name) \ DEF_OPERATION_NODE(Int32##Name##WithOverflow, Int32UnaryWithOverflowNode, \ Name) DEF_INT32_UNARY_WITH_OVERFLOW_NODE(Negate) DEF_INT32_UNARY_WITH_OVERFLOW_NODE(Increment) DEF_INT32_UNARY_WITH_OVERFLOW_NODE(Decrement) #undef DEF_INT32_UNARY_WITH_OVERFLOW_NODE class Int32ShiftRightLogical : public FixedInputValueNodeT<2, Int32ShiftRightLogical> { using Base = FixedInputValueNodeT<2, Int32ShiftRightLogical>; public: explicit Int32ShiftRightLogical(uint64_t bitfield) : Base(bitfield) {} // Unlike the other Int32 nodes, logical right shift returns a Uint32. static constexpr OpProperties kProperties = OpProperties::Uint32(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kInt32, ValueRepresentation::kInt32}; static constexpr int kLeftIndex = 0; static constexpr int kRightIndex = 1; Input& left_input() { return Node::input(kLeftIndex); } Input& right_input() { return Node::input(kRightIndex); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class Int32Compare : public FixedInputValueNodeT<2, Int32Compare> { using Base = FixedInputValueNodeT<2, Int32Compare>; public: explicit Int32Compare(uint64_t bitfield, Operation operation) : Base(OperationBitField::update(bitfield, operation)) {} static constexpr Base::InputTypes kInputTypes{ValueRepresentation::kInt32, ValueRepresentation::kInt32}; static constexpr int kLeftIndex = 0; static constexpr int kRightIndex = 1; Input& left_input() { return Node::input(kLeftIndex); } Input& right_input() { return Node::input(kRightIndex); } constexpr Operation operation() const { return OperationBitField::decode(bitfield()); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: using OperationBitField = NextBitField<Operation, 5>; }; class Int32ToBoolean : public FixedInputValueNodeT<1, Int32ToBoolean> { using Base = FixedInputValueNodeT<1, Int32ToBoolean>; public: explicit Int32ToBoolean(uint64_t bitfield, bool flip) : Base(FlipBitField::update(bitfield, flip)) {} static constexpr Base::InputTypes kInputTypes{ValueRepresentation::kInt32}; Input& value() { return Node::input(0); } constexpr bool flip() const { return FlipBitField::decode(bitfield()); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: using FlipBitField = NextBitField<bool, 1>; }; class CheckedSmiIncrement : public FixedInputValueNodeT<1, CheckedSmiIncrement> { using Base = FixedInputValueNodeT<1, CheckedSmiIncrement>; public: explicit CheckedSmiIncrement(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; static constexpr int kValueIndex = 0; Input& value_input() { return Node::input(kValueIndex); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class CheckedSmiDecrement : public FixedInputValueNodeT<1, CheckedSmiDecrement> { using Base = FixedInputValueNodeT<1, CheckedSmiDecrement>; public: explicit CheckedSmiDecrement(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; static constexpr int kValueIndex = 0; Input& value_input() { return Node::input(kValueIndex); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; template <class Derived, Operation kOperation> class Float64BinaryNode : public FixedInputValueNodeT<2, Derived> { using Base = FixedInputValueNodeT<2, Derived>; public: static constexpr OpProperties kProperties = OpProperties::Float64(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kHoleyFloat64, ValueRepresentation::kHoleyFloat64}; static constexpr int kLeftIndex = 0; static constexpr int kRightIndex = 1; Input& left_input() { return Node::input(kLeftIndex); } Input& right_input() { return Node::input(kRightIndex); } protected: explicit Float64BinaryNode(uint64_t bitfield) : Base(bitfield) {} void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; #define DEF_OPERATION_NODE_WITH_CALL(Name, Super, OpName) \ class Name : public Super<Name, Operation::k##OpName> { \ using Base = Super<Name, Operation::k##OpName>; \ \ public: \ explicit Name(uint64_t bitfield) : Base(bitfield) {} \ int MaxCallStackArgs() const; \ void SetValueLocationConstraints(); \ void GenerateCode(MaglevAssembler*, const ProcessingState&); \ void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} \ }; template <class Derived, Operation kOperation> class Float64BinaryNodeWithCall : public FixedInputValueNodeT<2, Derived> { using Base = FixedInputValueNodeT<2, Derived>; public: static constexpr OpProperties kProperties = OpProperties::Float64() | OpProperties::Call(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kHoleyFloat64, ValueRepresentation::kHoleyFloat64}; static constexpr int kLeftIndex = 0; static constexpr int kRightIndex = 1; Input& left_input() { return Node::input(kLeftIndex); } Input& right_input() { return Node::input(kRightIndex); } protected: explicit Float64BinaryNodeWithCall(uint64_t bitfield) : Base(bitfield) {} void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; #define DEF_FLOAT64_BINARY_NODE(Name) \ DEF_OPERATION_NODE(Float64##Name, Float64BinaryNode, Name) #define DEF_FLOAT64_BINARY_NODE_WITH_CALL(Name) \ DEF_OPERATION_NODE_WITH_CALL(Float64##Name, Float64BinaryNodeWithCall, Name) DEF_FLOAT64_BINARY_NODE(Add) DEF_FLOAT64_BINARY_NODE(Subtract) DEF_FLOAT64_BINARY_NODE(Multiply) DEF_FLOAT64_BINARY_NODE(Divide) #if defined(V8_TARGET_ARCH_ARM64) || defined(V8_TARGET_ARCH_ARM) // On Arm/Arm64, floating point modulus is implemented with a call to a C++ // function, while on x64, it's implemented natively without call. DEF_FLOAT64_BINARY_NODE_WITH_CALL(Modulus) #else DEF_FLOAT64_BINARY_NODE(Modulus) #endif DEF_FLOAT64_BINARY_NODE_WITH_CALL(Exponentiate) #undef DEF_FLOAT64_BINARY_NODE #undef DEF_FLOAT64_BINARY_NODE_WITH_CALL #undef DEF_OPERATION_NODE #undef DEF_OPERATION_NODE_WITH_CALL class Float64Compare : public FixedInputValueNodeT<2, Float64Compare> { using Base = FixedInputValueNodeT<2, Float64Compare>; public: explicit Float64Compare(uint64_t bitfield, Operation operation) : Base(OperationBitField::update(bitfield, operation)) {} static constexpr Base::InputTypes kInputTypes{ValueRepresentation::kFloat64, ValueRepresentation::kFloat64}; static constexpr int kLeftIndex = 0; static constexpr int kRightIndex = 1; Input& left_input() { return Node::input(kLeftIndex); } Input& right_input() { return Node::input(kRightIndex); } constexpr Operation operation() const { return OperationBitField::decode(bitfield()); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: using OperationBitField = NextBitField<Operation, 5>; }; class Float64ToBoolean : public FixedInputValueNodeT<1, Float64ToBoolean> { using Base = FixedInputValueNodeT<1, Float64ToBoolean>; public: explicit Float64ToBoolean(uint64_t bitfield, bool flip) : Base(FlipBitField::update(bitfield, flip)) {} static constexpr Base::InputTypes kInputTypes{ ValueRepresentation::kHoleyFloat64}; Input& value() { return Node::input(0); } constexpr bool flip() const { return FlipBitField::decode(bitfield()); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: using FlipBitField = NextBitField<bool, 1>; }; class Float64Negate : public FixedInputValueNodeT<1, Float64Negate> { using Base = FixedInputValueNodeT<1, Float64Negate>; public: explicit Float64Negate(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::Float64(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kHoleyFloat64}; Input& input() { return Node::input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class Float64Ieee754Unary : public FixedInputValueNodeT<1, Float64Ieee754Unary> { using Base = FixedInputValueNodeT<1, Float64Ieee754Unary>; public: explicit Float64Ieee754Unary(uint64_t bitfield, ExternalReference ieee_function) : Base(bitfield), ieee_function_(ieee_function) {} static constexpr OpProperties kProperties = OpProperties::Float64() | OpProperties::Call(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kHoleyFloat64}; Input& input() { return Node::input(0); } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: ExternalReference ieee_function_; }; class CheckInt32IsSmi : public FixedInputNodeT<1, CheckInt32IsSmi> { using Base = FixedInputNodeT<1, CheckInt32IsSmi>; public: explicit CheckInt32IsSmi(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kInt32}; Input& input() { return Node::input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class CheckUint32IsSmi : public FixedInputNodeT<1, CheckUint32IsSmi> { using Base = FixedInputNodeT<1, CheckUint32IsSmi>; public: explicit CheckUint32IsSmi(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kUint32}; Input& input() { return Node::input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class CheckHoleyFloat64IsSmi : public FixedInputNodeT<1, CheckHoleyFloat64IsSmi> { using Base = FixedInputNodeT<1, CheckHoleyFloat64IsSmi>; public: explicit CheckHoleyFloat64IsSmi(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kHoleyFloat64}; Input& input() { return Node::input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class CheckedSmiTagInt32 : public FixedInputValueNodeT<1, CheckedSmiTagInt32> { using Base = FixedInputValueNodeT<1, CheckedSmiTagInt32>; public: explicit CheckedSmiTagInt32(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt() | OpProperties::ConversionNode(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kInt32}; Input& input() { return Node::input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; // This is a check disguised as a conversion node so we can use it to override // untagging conversions. // TODO(olivf): Support overriding bigger with smaller instruction so we can use // CheckInt32IsSmi instead. class CheckedSmiSizedInt32 : public FixedInputValueNodeT<1, CheckedSmiSizedInt32> { using Base = FixedInputValueNodeT<1, CheckedSmiSizedInt32>; public: explicit CheckedSmiSizedInt32(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt() | OpProperties::Int32() | OpProperties::ConversionNode(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kInt32}; Input& input() { return Node::input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class CheckedSmiTagUint32 : public FixedInputValueNodeT<1, CheckedSmiTagUint32> { using Base = FixedInputValueNodeT<1, CheckedSmiTagUint32>; public: explicit CheckedSmiTagUint32(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt() | OpProperties::ConversionNode(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kUint32}; Input& input() { return Node::input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; // Input must guarantee to fit in a Smi. class UnsafeSmiTag : public FixedInputValueNodeT<1, UnsafeSmiTag> { using Base = FixedInputValueNodeT<1, UnsafeSmiTag>; public: explicit UnsafeSmiTag(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::ConversionNode(); Input& input() { return Node::input(0); } void VerifyInputs(MaglevGraphLabeller*) const; #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing() { // No tagged inputs. } #endif void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class CheckedSmiUntag : public FixedInputValueNodeT<1, CheckedSmiUntag> { using Base = FixedInputValueNodeT<1, CheckedSmiUntag>; public: explicit CheckedSmiUntag(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt() | OpProperties::Int32() | OpProperties::ConversionNode(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; Input& input() { return Node::input(0); } #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing() { // Don't need to decompress to untag. } #endif void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class UnsafeSmiUntag : public FixedInputValueNodeT<1, UnsafeSmiUntag> { using Base = FixedInputValueNodeT<1, UnsafeSmiUntag>; public: explicit UnsafeSmiUntag(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::Int32() | OpProperties::ConversionNode(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; Input& input() { return Node::input(0); } #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing() { // Don't need to decompress to untag. } #endif void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class Int32Constant : public FixedInputValueNodeT<0, Int32Constant> { using Base = FixedInputValueNodeT<0, Int32Constant>; public: using OutputRegister = Register; explicit Int32Constant(uint64_t bitfield, int32_t value) : Base(bitfield), value_(value) {} static constexpr OpProperties kProperties = OpProperties::Int32(); int32_t value() const { return value_; } bool ToBoolean(LocalIsolate* local_isolate) const { return value_ != 0; } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; void DoLoadToRegister(MaglevAssembler*, OutputRegister); Handle<Object> DoReify(LocalIsolate* isolate) const; private: const int32_t value_; }; class Float64Constant : public FixedInputValueNodeT<0, Float64Constant> { using Base = FixedInputValueNodeT<0, Float64Constant>; public: using OutputRegister = DoubleRegister; explicit Float64Constant(uint64_t bitfield, Float64 value) : Base(bitfield), value_(value) {} static constexpr OpProperties kProperties = OpProperties::Float64(); Float64 value() const { return value_; } bool ToBoolean(LocalIsolate* local_isolate) const { return value_.get_scalar() != 0.0 && !value_.is_nan(); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; void DoLoadToRegister(MaglevAssembler*, OutputRegister); Handle<Object> DoReify(LocalIsolate* isolate) const; private: const Float64 value_; }; class Int32ToUint8Clamped : public FixedInputValueNodeT<1, Int32ToUint8Clamped> { using Base = FixedInputValueNodeT<1, Int32ToUint8Clamped>; public: explicit Int32ToUint8Clamped(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::Int32(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kInt32}; Input& input() { return Node::input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class Uint32ToUint8Clamped : public FixedInputValueNodeT<1, Uint32ToUint8Clamped> { using Base = FixedInputValueNodeT<1, Uint32ToUint8Clamped>; public: explicit Uint32ToUint8Clamped(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::Int32(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kUint32}; Input& input() { return Node::input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class Float64ToUint8Clamped : public FixedInputValueNodeT<1, Float64ToUint8Clamped> { using Base = FixedInputValueNodeT<1, Float64ToUint8Clamped>; public: explicit Float64ToUint8Clamped(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::Int32(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kHoleyFloat64}; Input& input() { return Node::input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class CheckedNumberToUint8Clamped : public FixedInputValueNodeT<1, CheckedNumberToUint8Clamped> { using Base = FixedInputValueNodeT<1, CheckedNumberToUint8Clamped>; public: explicit CheckedNumberToUint8Clamped(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt() | OpProperties::Int32(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; Input& input() { return Node::input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class Int32ToNumber : public FixedInputValueNodeT<1, Int32ToNumber> { using Base = FixedInputValueNodeT<1, Int32ToNumber>; public: explicit Int32ToNumber(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::CanAllocate() | OpProperties::DeferredCall() | OpProperties::ConversionNode(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kInt32}; Input& input() { return Node::input(0); } int MaxCallStackArgs() const { return 0; } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class Uint32ToNumber : public FixedInputValueNodeT<1, Uint32ToNumber> { using Base = FixedInputValueNodeT<1, Uint32ToNumber>; public: explicit Uint32ToNumber(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::CanAllocate() | OpProperties::DeferredCall() | OpProperties::ConversionNode(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kUint32}; Input& input() { return Node::input(0); } int MaxCallStackArgs() const { return 0; } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class Float64ToTagged : public FixedInputValueNodeT<1, Float64ToTagged> { using Base = FixedInputValueNodeT<1, Float64ToTagged>; public: enum class ConversionMode { kCanonicalizeSmi, kForceHeapNumber }; explicit Float64ToTagged( uint64_t bitfield, ConversionMode mode = ConversionMode::kCanonicalizeSmi) : Base(ConversionModeBitField::update(bitfield, mode)) {} static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kFloat64}; static constexpr OpProperties kProperties = OpProperties::CanAllocate() | OpProperties::DeferredCall() | OpProperties::ConversionNode(); Input& input() { return Node::input(0); } int MaxCallStackArgs() const { return 0; } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: bool canonicalize_smi() { return ConversionModeBitField::decode(bitfield()) == ConversionMode::kCanonicalizeSmi; } using ConversionModeBitField = NextBitField<ConversionMode, 1>; }; class HoleyFloat64ToTagged : public FixedInputValueNodeT<1, HoleyFloat64ToTagged> { using Base = FixedInputValueNodeT<1, HoleyFloat64ToTagged>; public: enum class ConversionMode { kCanonicalizeSmi, kForceHeapNumber }; explicit HoleyFloat64ToTagged( uint64_t bitfield, ConversionMode mode = ConversionMode::kCanonicalizeSmi) : Base(ConversionModeBitField::update(bitfield, mode)) {} static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kHoleyFloat64}; static constexpr OpProperties kProperties = OpProperties::CanAllocate() | OpProperties::DeferredCall() | OpProperties::ConversionNode(); Input& input() { return Node::input(0); } int MaxCallStackArgs() const { return 0; } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: bool canonicalize_smi() { return ConversionModeBitField::decode(bitfield()) == ConversionMode::kCanonicalizeSmi; } using ConversionModeBitField = NextBitField<ConversionMode, 1>; }; class CheckedSmiTagFloat64 : public FixedInputValueNodeT<1, CheckedSmiTagFloat64> { using Base = FixedInputValueNodeT<1, CheckedSmiTagFloat64>; public: explicit CheckedSmiTagFloat64(uint64_t bitfield) : Base(bitfield) {} static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kHoleyFloat64}; static constexpr OpProperties kProperties = OpProperties::EagerDeopt() | OpProperties::ConversionNode(); Input& input() { return Node::input(0); } int MaxCallStackArgs() const { return 0; } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class CheckedInt32ToUint32 : public FixedInputValueNodeT<1, CheckedInt32ToUint32> { using Base = FixedInputValueNodeT<1, CheckedInt32ToUint32>; public: explicit CheckedInt32ToUint32(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::Uint32() | OpProperties::ConversionNode() | OpProperties::EagerDeopt(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kInt32}; Input& input() { return Node::input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class CheckedUint32ToInt32 : public FixedInputValueNodeT<1, CheckedUint32ToInt32> { using Base = FixedInputValueNodeT<1, CheckedUint32ToInt32>; public: explicit CheckedUint32ToInt32(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::Int32() | OpProperties::ConversionNode() | OpProperties::EagerDeopt(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kUint32}; Input& input() { return Node::input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class ChangeInt32ToFloat64 : public FixedInputValueNodeT<1, ChangeInt32ToFloat64> { using Base = FixedInputValueNodeT<1, ChangeInt32ToFloat64>; public: explicit ChangeInt32ToFloat64(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::Float64() | OpProperties::ConversionNode(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kInt32}; Input& input() { return Node::input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class ChangeUint32ToFloat64 : public FixedInputValueNodeT<1, ChangeUint32ToFloat64> { using Base = FixedInputValueNodeT<1, ChangeUint32ToFloat64>; public: explicit ChangeUint32ToFloat64(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::Float64() | OpProperties::ConversionNode(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kUint32}; Input& input() { return Node::input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class CheckedTruncateFloat64ToInt32 : public FixedInputValueNodeT<1, CheckedTruncateFloat64ToInt32> { using Base = FixedInputValueNodeT<1, CheckedTruncateFloat64ToInt32>; public: explicit CheckedTruncateFloat64ToInt32(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt() | OpProperties::Int32() | OpProperties::ConversionNode(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kHoleyFloat64}; Input& input() { return Node::input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class Float64Round : public FixedInputValueNodeT<1, Float64Round> { using Base = FixedInputValueNodeT<1, Float64Round>; public: enum class Kind { kFloor, kCeil, kNearest }; static Builtin continuation(Kind kind) { switch (kind) { case Kind::kCeil: return Builtin::kMathCeilContinuation; case Kind::kFloor: return Builtin::kMathFloorContinuation; case Kind::kNearest: return Builtin::kMathRoundContinuation; } } explicit Float64Round(uint64_t bitfield, Kind kind) : Base(bitfield), kind_(kind) {} static constexpr OpProperties kProperties = OpProperties::Float64(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kHoleyFloat64}; Input& input() { return Node::input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: Kind kind_; }; class CheckedTruncateFloat64ToUint32 : public FixedInputValueNodeT<1, CheckedTruncateFloat64ToUint32> { using Base = FixedInputValueNodeT<1, CheckedTruncateFloat64ToUint32>; public: explicit CheckedTruncateFloat64ToUint32(uint64_t bitfield) : Base(bitfield) {} static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kHoleyFloat64}; static constexpr OpProperties kProperties = OpProperties::EagerDeopt() | OpProperties::Uint32() | OpProperties::ConversionNode(); Input& input() { return Node::input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; #define DEFINE_TRUNCATE_NODE(name, from_repr, properties) \ class name : public FixedInputValueNodeT<1, name> { \ using Base = FixedInputValueNodeT<1, name>; \ \ public: \ explicit name(uint64_t bitfield) : Base(bitfield) {} \ \ static constexpr OpProperties kProperties = properties; \ static constexpr typename Base::InputTypes kInputTypes{ \ ValueRepresentation::k##from_repr}; \ \ Input& input() { return Node::input(0); } \ \ void SetValueLocationConstraints(); \ void GenerateCode(MaglevAssembler*, const ProcessingState&); \ void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} \ }; DEFINE_TRUNCATE_NODE(TruncateUint32ToInt32, Uint32, OpProperties::Int32()) DEFINE_TRUNCATE_NODE(TruncateFloat64ToInt32, HoleyFloat64, OpProperties::Int32()) DEFINE_TRUNCATE_NODE(UnsafeTruncateUint32ToInt32, Uint32, OpProperties::Int32()) DEFINE_TRUNCATE_NODE(UnsafeTruncateFloat64ToInt32, HoleyFloat64, OpProperties::Int32()) #undef DEFINE_TRUNCATE_NODE class CheckedNumberOrOddballToFloat64 : public FixedInputValueNodeT<1, CheckedNumberOrOddballToFloat64> { using Base = FixedInputValueNodeT<1, CheckedNumberOrOddballToFloat64>; public: explicit CheckedNumberOrOddballToFloat64( uint64_t bitfield, TaggedToFloat64ConversionType conversion_type) : Base(TaggedToFloat64ConversionTypeOffset::update(bitfield, conversion_type)) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt() | OpProperties::Float64() | OpProperties::ConversionNode(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; Input& input() { return Node::input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; TaggedToFloat64ConversionType conversion_type() const { return TaggedToFloat64ConversionTypeOffset::decode(bitfield()); } private: using TaggedToFloat64ConversionTypeOffset = NextBitField<TaggedToFloat64ConversionType, 1>; }; class UncheckedNumberOrOddballToFloat64 : public FixedInputValueNodeT<1, UncheckedNumberOrOddballToFloat64> { using Base = FixedInputValueNodeT<1, UncheckedNumberOrOddballToFloat64>; public: explicit UncheckedNumberOrOddballToFloat64( uint64_t bitfield, TaggedToFloat64ConversionType conversion_type) : Base(TaggedToFloat64ConversionTypeOffset::update(bitfield, conversion_type)) {} static constexpr OpProperties kProperties = OpProperties::Float64() | OpProperties::ConversionNode(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; Input& input() { return Node::input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; TaggedToFloat64ConversionType conversion_type() const { return TaggedToFloat64ConversionTypeOffset::decode(bitfield()); } private: using TaggedToFloat64ConversionTypeOffset = NextBitField<TaggedToFloat64ConversionType, 1>; }; class CheckedHoleyFloat64ToFloat64 : public FixedInputValueNodeT<1, CheckedHoleyFloat64ToFloat64> { using Base = FixedInputValueNodeT<1, CheckedHoleyFloat64ToFloat64>; public: explicit CheckedHoleyFloat64ToFloat64(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt() | OpProperties::Float64() | OpProperties::ConversionNode(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kHoleyFloat64}; Input& input() { return Node::input(0); } int MaxCallStackArgs() const { return 0; } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class HoleyFloat64ToMaybeNanFloat64 : public FixedInputValueNodeT<1, HoleyFloat64ToMaybeNanFloat64> { using Base = FixedInputValueNodeT<1, HoleyFloat64ToMaybeNanFloat64>; public: explicit HoleyFloat64ToMaybeNanFloat64(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::Float64(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kHoleyFloat64}; Input& input() { return Node::input(0); } int MaxCallStackArgs() const { return 0; } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class TruncateNumberOrOddballToInt32 : public FixedInputValueNodeT<1, TruncateNumberOrOddballToInt32> { using Base = FixedInputValueNodeT<1, TruncateNumberOrOddballToInt32>; public: explicit TruncateNumberOrOddballToInt32( uint64_t bitfield, TaggedToFloat64ConversionType conversion_type) : Base(TaggedToFloat64ConversionTypeOffset::update(bitfield, conversion_type)) {} static constexpr OpProperties kProperties = OpProperties::Int32(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; Input& input() { return Node::input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; TaggedToFloat64ConversionType conversion_type() const { return TaggedToFloat64ConversionTypeOffset::decode(bitfield()); } private: using TaggedToFloat64ConversionTypeOffset = NextBitField<TaggedToFloat64ConversionType, 1>; }; class CheckedTruncateNumberOrOddballToInt32 : public FixedInputValueNodeT<1, CheckedTruncateNumberOrOddballToInt32> { using Base = FixedInputValueNodeT<1, CheckedTruncateNumberOrOddballToInt32>; public: explicit CheckedTruncateNumberOrOddballToInt32( uint64_t bitfield, TaggedToFloat64ConversionType conversion_type) : Base(TaggedToFloat64ConversionTypeOffset::update(bitfield, conversion_type)) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt() | OpProperties::Int32(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; Input& input() { return Node::input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; TaggedToFloat64ConversionType conversion_type() const { return TaggedToFloat64ConversionTypeOffset::decode(bitfield()); } private: using TaggedToFloat64ConversionTypeOffset = NextBitField<TaggedToFloat64ConversionType, 1>; }; class LogicalNot : public FixedInputValueNodeT<1, LogicalNot> { using Base = FixedInputValueNodeT<1, LogicalNot>; public: explicit LogicalNot(uint64_t bitfield) : Base(bitfield) {} static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; Input& value() { return Node::input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class SetPendingMessage : public FixedInputValueNodeT<1, SetPendingMessage> { using Base = FixedInputValueNodeT<1, SetPendingMessage>; public: explicit SetPendingMessage(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::CanWrite() | OpProperties::CanRead(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; Input& value() { return Node::input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; enum class CheckType { kCheckHeapObject, kOmitHeapObjectCheck }; class ToBoolean : public FixedInputValueNodeT<1, ToBoolean> { using Base = FixedInputValueNodeT<1, ToBoolean>; public: explicit ToBoolean(uint64_t bitfield, CheckType check_type) : Base(CheckTypeBitField::update(bitfield, check_type)) {} static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; Input& value() { return Node::input(0); } CheckType check_type() const { return CheckTypeBitField::decode(bitfield()); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: using CheckTypeBitField = NextBitField<CheckType, 1>; }; class ToBooleanLogicalNot : public FixedInputValueNodeT<1, ToBooleanLogicalNot> { using Base = FixedInputValueNodeT<1, ToBooleanLogicalNot>; public: explicit ToBooleanLogicalNot(uint64_t bitfield, CheckType check_type) : Base(CheckTypeBitField::update(bitfield, check_type)) {} static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; Input& value() { return Node::input(0); } CheckType check_type() const { return CheckTypeBitField::decode(bitfield()); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: using CheckTypeBitField = NextBitField<CheckType, 1>; }; class StringEqual : public FixedInputValueNodeT<2, StringEqual> { using Base = FixedInputValueNodeT<2, StringEqual>; public: explicit StringEqual(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::Call() | OpProperties::LazyDeopt(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged}; Input& lhs() { return Node::input(0); } Input& rhs() { return Node::input(1); } int MaxCallStackArgs() const { return 0; } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class TaggedEqual : public FixedInputValueNodeT<2, TaggedEqual> { using Base = FixedInputValueNodeT<2, TaggedEqual>; public: explicit TaggedEqual(uint64_t bitfield) : Base(bitfield) {} static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged}; Input& lhs() { return Node::input(0); } Input& rhs() { return Node::input(1); } #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing() { // Don't need to decompress to compare reference equality. } #endif void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class TaggedNotEqual : public FixedInputValueNodeT<2, TaggedNotEqual> { using Base = FixedInputValueNodeT<2, TaggedNotEqual>; public: explicit TaggedNotEqual(uint64_t bitfield) : Base(bitfield) {} static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged}; Input& lhs() { return Node::input(0); } Input& rhs() { return Node::input(1); } #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing() { // Don't need to decompress to compare reference equality. } #endif void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class TestInstanceOf : public FixedInputValueNodeT<3, TestInstanceOf> { using Base = FixedInputValueNodeT<3, TestInstanceOf>; public: explicit TestInstanceOf(uint64_t bitfield, compiler::FeedbackSource feedback) : Base(bitfield), feedback_(feedback) {} // The implementation currently calls runtime. static constexpr OpProperties kProperties = OpProperties::JSCall(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged, ValueRepresentation::kTagged}; Input& context() { return input(0); } Input& object() { return input(1); } Input& callable() { return input(2); } compiler::FeedbackSource feedback() const { return feedback_; } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: const compiler::FeedbackSource feedback_; }; class TestUndetectable : public FixedInputValueNodeT<1, TestUndetectable> { using Base = FixedInputValueNodeT<1, TestUndetectable>; public: explicit TestUndetectable(uint64_t bitfield, CheckType check_type) : Base(CheckTypeBitField::update(bitfield, check_type)) {} static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; Input& value() { return Node::input(0); } CheckType check_type() const { return CheckTypeBitField::decode(bitfield()); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: using CheckTypeBitField = NextBitField<CheckType, 1>; }; class TestTypeOf : public FixedInputValueNodeT<1, TestTypeOf> { using Base = FixedInputValueNodeT<1, TestTypeOf>; public: explicit TestTypeOf(uint64_t bitfield, interpreter::TestTypeOfFlags::LiteralFlag literal) : Base(bitfield), literal_(literal) {} static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; Input& value() { return Node::input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: interpreter::TestTypeOfFlags::LiteralFlag literal_; }; class ToName : public FixedInputValueNodeT<2, ToName> { using Base = FixedInputValueNodeT<2, ToName>; public: explicit ToName(uint64_t bitfield) : Base(bitfield) {} // The implementation currently calls runtime. static constexpr OpProperties kProperties = OpProperties::JSCall(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged}; Input& context() { return Node::input(0); } Input& value_input() { return Node::input(1); } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class ToNumberOrNumeric : public FixedInputValueNodeT<1, ToNumberOrNumeric> { using Base = FixedInputValueNodeT<1, ToNumberOrNumeric>; public: explicit ToNumberOrNumeric(uint64_t bitfield, Object::Conversion mode) : Base(bitfield), mode_(mode) {} static constexpr OpProperties kProperties = OpProperties::DeferredCall() | OpProperties::CanCallUserCode(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; Input& value_input() { return Node::input(0); } Object::Conversion mode() const { return mode_; } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: const Object::Conversion mode_; }; class DeleteProperty : public FixedInputValueNodeT<3, DeleteProperty> { using Base = FixedInputValueNodeT<3, DeleteProperty>; public: explicit DeleteProperty(uint64_t bitfield, LanguageMode mode) : Base(bitfield), mode_(mode) {} // The implementation currently calls runtime. static constexpr OpProperties kProperties = OpProperties::JSCall(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged, ValueRepresentation::kTagged}; Input& context() { return Node::input(0); } Input& object() { return Node::input(1); } Input& key() { return Node::input(2); } LanguageMode mode() const { return mode_; } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: const LanguageMode mode_; }; class GeneratorStore : public NodeT<GeneratorStore> { using Base = NodeT<GeneratorStore>; public: // We assume the context as fixed input. static constexpr int kContextIndex = 0; static constexpr int kGeneratorIndex = 1; static constexpr int kFixedInputCount = 2; // This ctor is used when for variable input counts. // Inputs must be initialized manually. GeneratorStore(uint64_t bitfield, ValueNode* context, ValueNode* generator, int suspend_id, int bytecode_offset) : Base(bitfield), suspend_id_(suspend_id), bytecode_offset_(bytecode_offset) { set_input(kContextIndex, context); set_input(kGeneratorIndex, generator); } static constexpr OpProperties kProperties = OpProperties::DeferredCall(); int suspend_id() const { return suspend_id_; } int bytecode_offset() const { return bytecode_offset_; } Input& context_input() { return input(kContextIndex); } Input& generator_input() { return input(kGeneratorIndex); } int num_parameters_and_registers() const { return input_count() - kFixedInputCount; } Input& parameters_and_registers(int i) { return input(i + kFixedInputCount); } void set_parameters_and_registers(int i, ValueNode* node) { set_input(i + kFixedInputCount, node); } int MaxCallStackArgs() const; void VerifyInputs(MaglevGraphLabeller* graph_labeller) const; #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing() { // Don't need to decompress to store. } #endif void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: const int suspend_id_; const int bytecode_offset_; }; class TryOnStackReplacement : public FixedInputNodeT<1, TryOnStackReplacement> { using Base = FixedInputNodeT<1, TryOnStackReplacement>; public: explicit TryOnStackReplacement(uint64_t bitfield, int32_t loop_depth, FeedbackSlot feedback_slot, BytecodeOffset osr_offset, MaglevCompilationUnit* unit) : Base(bitfield), loop_depth_(loop_depth), feedback_slot_(feedback_slot), osr_offset_(osr_offset), unit_(unit) {} static constexpr OpProperties kProperties = OpProperties::DeferredCall() | OpProperties::EagerDeopt() | OpProperties::Call(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; Input& closure() { return Node::input(0); } const MaglevCompilationUnit* unit() const { return unit_; } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: // For OSR. const int32_t loop_depth_; const FeedbackSlot feedback_slot_; const BytecodeOffset osr_offset_; MaglevCompilationUnit* const unit_; }; class ForInPrepare : public FixedInputValueNodeT<2, ForInPrepare> { using Base = FixedInputValueNodeT<2, ForInPrepare>; public: explicit ForInPrepare(uint64_t bitfield, compiler::FeedbackSource& feedback) : Base(bitfield), feedback_(feedback) {} static constexpr OpProperties kProperties = OpProperties::GenericRuntimeOrBuiltinCall(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged}; compiler::FeedbackSource feedback() const { return feedback_; } Input& context() { return Node::input(0); } Input& enumerator() { return Node::input(1); } int ReturnCount() const { return 2; } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: const compiler::FeedbackSource feedback_; }; class ForInNext : public FixedInputValueNodeT<5, ForInNext> { using Base = FixedInputValueNodeT<5, ForInNext>; public: explicit ForInNext(uint64_t bitfield, compiler::FeedbackSource& feedback) : Base(bitfield), feedback_(feedback) {} static constexpr OpProperties kProperties = OpProperties::JSCall(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged, ValueRepresentation::kTagged, ValueRepresentation::kTagged, ValueRepresentation::kTagged}; compiler::FeedbackSource feedback() const { return feedback_; } Input& context() { return Node::input(0); } Input& receiver() { return Node::input(1); } Input& cache_array() { return Node::input(2); } Input& cache_type() { return Node::input(3); } Input& cache_index() { return Node::input(4); } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: const compiler::FeedbackSource feedback_; }; class GetIterator : public FixedInputValueNodeT<2, GetIterator> { using Base = FixedInputValueNodeT<2, GetIterator>; public: explicit GetIterator(uint64_t bitfield, int load_slot, int call_slot, compiler::FeedbackVectorRef feedback) : Base(bitfield), load_slot_(load_slot), call_slot_(call_slot), feedback_(feedback.object()) {} static constexpr OpProperties kProperties = OpProperties::JSCall(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged}; Input& context() { return input(0); } Input& receiver() { return input(1); } int load_slot() const { return load_slot_; } int call_slot() const { return call_slot_; } Handle<FeedbackVector> feedback() const { return feedback_; } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: const int load_slot_; const int call_slot_; const Handle<FeedbackVector> feedback_; }; class GetSecondReturnedValue : public FixedInputValueNodeT<0, GetSecondReturnedValue> { using Base = FixedInputValueNodeT<0, GetSecondReturnedValue>; public: explicit GetSecondReturnedValue(uint64_t bitfield) : Base(bitfield) {} void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class ToObject : public FixedInputValueNodeT<2, ToObject> { using Base = FixedInputValueNodeT<2, ToObject>; public: explicit ToObject(uint64_t bitfield, CheckType check_type) : Base(CheckTypeBitField::update(bitfield, check_type)) {} // The implementation currently calls runtime. static constexpr OpProperties kProperties = OpProperties::JSCall(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged}; Input& context() { return Node::input(0); } Input& value_input() { return Node::input(1); } CheckType check_type() const { return CheckTypeBitField::decode(bitfield()); } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: using CheckTypeBitField = NextBitField<CheckType, 1>; }; class ToString : public FixedInputValueNodeT<2, ToString> { using Base = FixedInputValueNodeT<2, ToString>; public: enum ConversionMode { kConvertSymbol, kThrowOnSymbol }; explicit ToString(uint64_t bitfield, ConversionMode mode) : Base(ConversionModeBitField::update(bitfield, mode)) {} // The implementation currently calls runtime. static constexpr OpProperties kProperties = OpProperties::JSCall(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged}; Input& context() { return Node::input(0); } Input& value_input() { return Node::input(1); } ConversionMode mode() const { return ConversionModeBitField::decode(bitfield()); } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: using ConversionModeBitField = NextBitField<ConversionMode, 1>; }; class NumberToString : public FixedInputValueNodeT<1, NumberToString> { using Base = FixedInputValueNodeT<1, NumberToString>; public: explicit NumberToString(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::Call() | OpProperties::LazyDeopt(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; Input& value_input() { return Node::input(0); } int MaxCallStackArgs() const { return 0; } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class GeneratorRestoreRegister : public FixedInputValueNodeT<2, GeneratorRestoreRegister> { using Base = FixedInputValueNodeT<2, GeneratorRestoreRegister>; public: explicit GeneratorRestoreRegister(uint64_t bitfield, int index) : Base(bitfield), index_(index) {} static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged}; Input& array_input() { return input(0); } Input& stale_input() { return input(1); } int index() const { return index_; } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: const int index_; }; class InitialValue : public FixedInputValueNodeT<0, InitialValue> { using Base = FixedInputValueNodeT<0, InitialValue>; public: explicit InitialValue(uint64_t bitfield, interpreter::Register source); interpreter::Register source() const { return source_; } uint32_t stack_slot() const; static uint32_t stack_slot(uint32_t register_idx); void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: const interpreter::Register source_; }; class RegisterInput : public FixedInputValueNodeT<0, RegisterInput> { using Base = FixedInputValueNodeT<0, RegisterInput>; public: explicit RegisterInput(uint64_t bitfield, Register input) : Base(bitfield), input_(input) {} Register input() const { return input_; } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: const Register input_; }; class SmiConstant : public FixedInputValueNodeT<0, SmiConstant> { using Base = FixedInputValueNodeT<0, SmiConstant>; public: using OutputRegister = Register; explicit SmiConstant(uint64_t bitfield, Tagged<Smi> value) : Base(bitfield), value_(value) {} Tagged<Smi> value() const { return value_; } bool ToBoolean(LocalIsolate* local_isolate) const { return value_ != Smi::FromInt(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; void DoLoadToRegister(MaglevAssembler*, OutputRegister); Handle<Object> DoReify(LocalIsolate* isolate) const; private: const Tagged<Smi> value_; }; class TaggedIndexConstant : public FixedInputValueNodeT<0, TaggedIndexConstant> { using Base = FixedInputValueNodeT<0, TaggedIndexConstant>; public: using OutputRegister = Register; explicit TaggedIndexConstant(uint64_t bitfield, Tagged<TaggedIndex> value) : Base(bitfield), value_(value) {} Tagged<TaggedIndex> value() const { return value_; } bool ToBoolean(LocalIsolate* local_isolate) const { UNREACHABLE(); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; void DoLoadToRegister(MaglevAssembler*, OutputRegister); Handle<Object> DoReify(LocalIsolate* isolate) const; private: const Tagged<TaggedIndex> value_; }; class ExternalConstant : public FixedInputValueNodeT<0, ExternalConstant> { using Base = FixedInputValueNodeT<0, ExternalConstant>; public: using OutputRegister = Register; explicit ExternalConstant(uint64_t bitfield, const ExternalReference& reference) : Base(bitfield), reference_(reference) {} static constexpr OpProperties kProperties = OpProperties::Pure() | OpProperties::ExternalReference(); ExternalReference reference() const { return reference_; } bool ToBoolean(LocalIsolate* local_isolate) const { UNREACHABLE(); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; void DoLoadToRegister(MaglevAssembler*, OutputRegister); Handle<Object> DoReify(LocalIsolate* isolate) const; private: const ExternalReference reference_; }; class Constant : public FixedInputValueNodeT<0, Constant> { using Base = FixedInputValueNodeT<0, Constant>; public: using OutputRegister = Register; explicit Constant(uint64_t bitfield, compiler::HeapObjectRef object) : Base(bitfield), object_(object) {} bool ToBoolean(LocalIsolate* local_isolate) const { return Object::BooleanValue(*object_.object(), local_isolate); } bool IsTheHole(compiler::JSHeapBroker* broker) const { return object_.IsTheHole(); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; compiler::HeapObjectRef object() { return object_; } void DoLoadToRegister(MaglevAssembler*, OutputRegister); Handle<Object> DoReify(LocalIsolate* isolate) const; compiler::HeapObjectRef ref() const { return object_; } private: const compiler::HeapObjectRef object_; }; class RootConstant : public FixedInputValueNodeT<0, RootConstant> { using Base = FixedInputValueNodeT<0, RootConstant>; public: using OutputRegister = Register; explicit RootConstant(uint64_t bitfield, RootIndex index) : Base(bitfield), index_(index) {} bool ToBoolean(LocalIsolate* local_isolate) const; RootIndex index() const { return index_; } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; void DoLoadToRegister(MaglevAssembler*, OutputRegister); Handle<Object> DoReify(LocalIsolate* isolate) const; private: const RootIndex index_; }; class CreateArrayLiteral : public FixedInputValueNodeT<0, CreateArrayLiteral> { using Base = FixedInputValueNodeT<0, CreateArrayLiteral>; public: explicit CreateArrayLiteral(uint64_t bitfield, compiler::HeapObjectRef constant_elements, const compiler::FeedbackSource& feedback, int flags) : Base(bitfield), constant_elements_(constant_elements), feedback_(feedback), flags_(flags) {} compiler::HeapObjectRef constant_elements() { return constant_elements_; } compiler::FeedbackSource feedback() const { return feedback_; } int flags() const { return flags_; } // The implementation currently calls runtime. static constexpr OpProperties kProperties = OpProperties::Call() | OpProperties::CanThrow() | OpProperties::LazyDeopt(); int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: const compiler::HeapObjectRef constant_elements_; const compiler::FeedbackSource feedback_; const int flags_; }; class CreateShallowArrayLiteral : public FixedInputValueNodeT<0, CreateShallowArrayLiteral> { using Base = FixedInputValueNodeT<0, CreateShallowArrayLiteral>; public: explicit CreateShallowArrayLiteral(uint64_t bitfield, compiler::HeapObjectRef constant_elements, const compiler::FeedbackSource& feedback, int flags) : Base(bitfield), constant_elements_(constant_elements), feedback_(feedback), flags_(flags) {} compiler::HeapObjectRef constant_elements() { return constant_elements_; } compiler::FeedbackSource feedback() const { return feedback_; } int flags() const { return flags_; } // The implementation currently calls runtime. static constexpr OpProperties kProperties = OpProperties::GenericRuntimeOrBuiltinCall(); int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: const compiler::HeapObjectRef constant_elements_; const compiler::FeedbackSource feedback_; const int flags_; }; class CreateObjectLiteral : public FixedInputValueNodeT<0, CreateObjectLiteral> { using Base = FixedInputValueNodeT<0, CreateObjectLiteral>; public: explicit CreateObjectLiteral( uint64_t bitfield, compiler::ObjectBoilerplateDescriptionRef boilerplate_descriptor, const compiler::FeedbackSource& feedback, int flags) : Base(bitfield), boilerplate_descriptor_(boilerplate_descriptor), feedback_(feedback), flags_(flags) {} compiler::ObjectBoilerplateDescriptionRef boilerplate_descriptor() { return boilerplate_descriptor_; } compiler::FeedbackSource feedback() const { return feedback_; } int flags() const { return flags_; } // The implementation currently calls runtime. static constexpr OpProperties kProperties = OpProperties::Call() | OpProperties::CanThrow() | OpProperties::LazyDeopt(); int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: const compiler::ObjectBoilerplateDescriptionRef boilerplate_descriptor_; const compiler::FeedbackSource feedback_; const int flags_; }; class CreateShallowObjectLiteral : public FixedInputValueNodeT<0, CreateShallowObjectLiteral> { using Base = FixedInputValueNodeT<0, CreateShallowObjectLiteral>; public: explicit CreateShallowObjectLiteral( uint64_t bitfield, compiler::ObjectBoilerplateDescriptionRef boilerplate_descriptor, const compiler::FeedbackSource& feedback, int flags) : Base(bitfield), boilerplate_descriptor_(boilerplate_descriptor), feedback_(feedback), flags_(flags) {} // TODO(victorgomes): We should not need a boilerplate descriptor in // CreateShallowObjectLiteral. compiler::ObjectBoilerplateDescriptionRef boilerplate_descriptor() { return boilerplate_descriptor_; } compiler::FeedbackSource feedback() const { return feedback_; } int flags() const { return flags_; } // The implementation currently calls runtime. static constexpr OpProperties kProperties = OpProperties::GenericRuntimeOrBuiltinCall(); int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: const compiler::ObjectBoilerplateDescriptionRef boilerplate_descriptor_; const compiler::FeedbackSource feedback_; const int flags_; }; class AllocateRaw : public FixedInputValueNodeT<0, AllocateRaw> { using Base = FixedInputValueNodeT<0, AllocateRaw>; public: explicit AllocateRaw(uint64_t bitfield, AllocationType allocation_type, int size) : Base(bitfield), allocation_type_(allocation_type), size_(size) {} static constexpr OpProperties kProperties = OpProperties::CanAllocate() | OpProperties::DeferredCall(); int MaxCallStackArgs() const { return 0; } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; AllocationType allocation_type() const { return allocation_type_; } int size() const { return size_; } // Allow increasing the size for allocation folding. void extend(int size) { DCHECK_GT(size, 0); size_ += size; } private: AllocationType allocation_type_; int size_; }; class FoldedAllocation : public FixedInputValueNodeT<1, FoldedAllocation> { using Base = FixedInputValueNodeT<1, FoldedAllocation>; public: explicit FoldedAllocation(uint64_t bitfield, int offset) : Base(bitfield), offset_(offset) {} Input& raw_allocation() { return input(0); } static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; void VerifyInputs(MaglevGraphLabeller* graph_labeller) const; int offset() const { return offset_; } private: int offset_; }; class CreateFunctionContext : public FixedInputValueNodeT<1, CreateFunctionContext> { using Base = FixedInputValueNodeT<1, CreateFunctionContext>; public: explicit CreateFunctionContext(uint64_t bitfield, compiler::ScopeInfoRef scope_info, uint32_t slot_count, ScopeType scope_type) : Base(bitfield), scope_info_(scope_info), slot_count_(slot_count), scope_type_(scope_type) {} compiler::ScopeInfoRef scope_info() const { return scope_info_; } uint32_t slot_count() const { return slot_count_; } ScopeType scope_type() const { return scope_type_; } Input& context() { return input(0); } // The implementation currently calls runtime. static constexpr OpProperties kProperties = OpProperties::GenericRuntimeOrBuiltinCall(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: const compiler::ScopeInfoRef scope_info_; const uint32_t slot_count_; ScopeType scope_type_; }; class FastCreateClosure : public FixedInputValueNodeT<1, FastCreateClosure> { using Base = FixedInputValueNodeT<1, FastCreateClosure>; public: explicit FastCreateClosure( uint64_t bitfield, compiler::SharedFunctionInfoRef shared_function_info, compiler::FeedbackCellRef feedback_cell) : Base(bitfield), shared_function_info_(shared_function_info), feedback_cell_(feedback_cell) {} compiler::SharedFunctionInfoRef shared_function_info() const { return shared_function_info_; } compiler::FeedbackCellRef feedback_cell() const { return feedback_cell_; } Input& context() { return input(0); } // The implementation currently calls runtime. static constexpr OpProperties kProperties = OpProperties::Call() | OpProperties::CanAllocate() | OpProperties::LazyDeopt(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: const compiler::SharedFunctionInfoRef shared_function_info_; const compiler::FeedbackCellRef feedback_cell_; }; class CreateRegExpLiteral : public FixedInputValueNodeT<0, CreateRegExpLiteral> { using Base = FixedInputValueNodeT<0, CreateRegExpLiteral>; public: explicit CreateRegExpLiteral(uint64_t bitfield, compiler::StringRef pattern, const compiler::FeedbackSource& feedback, int flags) : Base(bitfield), pattern_(pattern), feedback_(feedback), flags_(flags) {} compiler::StringRef pattern() { return pattern_; } compiler::FeedbackSource feedback() const { return feedback_; } int flags() const { return flags_; } // The implementation currently calls runtime. static constexpr OpProperties kProperties = OpProperties::Call(); int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: compiler::StringRef pattern_; const compiler::FeedbackSource feedback_; const int flags_; }; class CreateClosure : public FixedInputValueNodeT<1, CreateClosure> { using Base = FixedInputValueNodeT<1, CreateClosure>; public: explicit CreateClosure(uint64_t bitfield, compiler::SharedFunctionInfoRef shared_function_info, compiler::FeedbackCellRef feedback_cell, bool pretenured) : Base(bitfield), shared_function_info_(shared_function_info), feedback_cell_(feedback_cell), pretenured_(pretenured) {} compiler::SharedFunctionInfoRef shared_function_info() const { return shared_function_info_; } compiler::FeedbackCellRef feedback_cell() const { return feedback_cell_; } bool pretenured() const { return pretenured_; } Input& context() { return input(0); } // The implementation currently calls runtime. static constexpr OpProperties kProperties = OpProperties::Call(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: const compiler::SharedFunctionInfoRef shared_function_info_; const compiler::FeedbackCellRef feedback_cell_; const bool pretenured_; }; #define ASSERT_CONDITION(V) \ V(Equal) \ V(NotEqual) \ V(LessThan) \ V(LessThanEqual) \ V(GreaterThan) \ V(GreaterThanEqual) \ V(UnsignedLessThan) \ V(UnsignedLessThanEqual) \ V(UnsignedGreaterThan) \ V(UnsignedGreaterThanEqual) enum class AssertCondition { #define D(Name) k##Name, ASSERT_CONDITION(D) #undef D }; static constexpr int kNumAssertConditions = #define D(Name) +1 0 ASSERT_CONDITION(D); #undef D inline std::ostream& operator<<(std::ostream& os, const AssertCondition cond) { switch (cond) { #define CASE(Name) \ case AssertCondition::k##Name: \ os << #Name; \ break; ASSERT_CONDITION(CASE) #undef CASE } return os; } class AssertInt32 : public FixedInputNodeT<2, AssertInt32> { using Base = FixedInputNodeT<2, AssertInt32>; public: explicit AssertInt32(uint64_t bitfield, AssertCondition condition, AbortReason reason) : Base(bitfield), condition_(condition), reason_(reason) {} static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kInt32, ValueRepresentation::kInt32}; Input& left_input() { return input(0); } Input& right_input() { return input(1); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: AssertCondition condition_; AbortReason reason_; }; class CheckMaps : public FixedInputNodeT<1, CheckMaps> { using Base = FixedInputNodeT<1, CheckMaps>; public: explicit CheckMaps(uint64_t bitfield, const compiler::ZoneRefSet<Map>& maps, CheckType check_type) : Base(CheckTypeBitField::update(bitfield, check_type)), maps_(maps) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; const compiler::ZoneRefSet<Map>& maps() const { return maps_; } CheckType check_type() const { return CheckTypeBitField::decode(bitfield()); } static constexpr int kReceiverIndex = 0; Input& receiver_input() { return input(kReceiverIndex); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: using CheckTypeBitField = NextBitField<CheckType, 1>; const compiler::ZoneRefSet<Map> maps_; }; class CheckValue : public FixedInputNodeT<1, CheckValue> { using Base = FixedInputNodeT<1, CheckValue>; public: explicit CheckValue(uint64_t bitfield, compiler::HeapObjectRef value) : Base(bitfield), value_(value) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; compiler::HeapObjectRef value() const { return value_; } static constexpr int kTargetIndex = 0; Input& target_input() { return input(kTargetIndex); } #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing() { // Don't need to decompress to compare reference equality. } #endif void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: const compiler::HeapObjectRef value_; }; class CheckValueEqualsInt32 : public FixedInputNodeT<1, CheckValueEqualsInt32> { using Base = FixedInputNodeT<1, CheckValueEqualsInt32>; public: explicit CheckValueEqualsInt32(uint64_t bitfield, int32_t value) : Base(bitfield), value_(value) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kInt32}; int32_t value() const { return value_; } static constexpr int kTargetIndex = 0; Input& target_input() { return input(kTargetIndex); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: const int32_t value_; }; class CheckValueEqualsFloat64 : public FixedInputNodeT<1, CheckValueEqualsFloat64> { using Base = FixedInputNodeT<1, CheckValueEqualsFloat64>; public: explicit CheckValueEqualsFloat64(uint64_t bitfield, double value) : Base(bitfield), value_(value) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kFloat64}; double value() const { return value_; } static constexpr int kTargetIndex = 0; Input& target_input() { return input(kTargetIndex); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: const double value_; }; class CheckValueEqualsString : public FixedInputNodeT<1, CheckValueEqualsString> { using Base = FixedInputNodeT<1, CheckValueEqualsString>; public: explicit CheckValueEqualsString(uint64_t bitfield, compiler::InternalizedStringRef value) : Base(bitfield), value_(value) {} // Can allocate if strings are flattened for comparison. static constexpr OpProperties kProperties = OpProperties::CanAllocate() | OpProperties::EagerDeopt() | OpProperties::DeferredCall(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; compiler::InternalizedStringRef value() const { return value_; } static constexpr int kTargetIndex = 0; Input& target_input() { return input(kTargetIndex); } int MaxCallStackArgs() const { return 0; } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: const compiler::InternalizedStringRef value_; }; class CheckDynamicValue : public FixedInputNodeT<2, CheckDynamicValue> { using Base = FixedInputNodeT<2, CheckDynamicValue>; public: explicit CheckDynamicValue(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged}; static constexpr int kFirstIndex = 0; static constexpr int kSecondIndex = 1; Input& first_input() { return input(kFirstIndex); } Input& second_input() { return input(kSecondIndex); } #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing() { // Don't need to decompress to compare reference equality. } #endif void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class CheckSmi : public FixedInputNodeT<1, CheckSmi> { using Base = FixedInputNodeT<1, CheckSmi>; public: explicit CheckSmi(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; static constexpr int kReceiverIndex = 0; Input& receiver_input() { return input(kReceiverIndex); } using Node::set_input; #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing() { // Don't need to decompress to check Smi bits. } #endif void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class CheckNumber : public FixedInputNodeT<1, CheckNumber> { using Base = FixedInputNodeT<1, CheckNumber>; public: explicit CheckNumber(uint64_t bitfield, Object::Conversion mode) : Base(bitfield), mode_(mode) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; static constexpr int kReceiverIndex = 0; Input& receiver_input() { return input(kReceiverIndex); } Object::Conversion mode() const { return mode_; } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: const Object::Conversion mode_; }; class CheckHeapObject : public FixedInputNodeT<1, CheckHeapObject> { using Base = FixedInputNodeT<1, CheckHeapObject>; public: explicit CheckHeapObject(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; static constexpr int kReceiverIndex = 0; Input& receiver_input() { return input(kReceiverIndex); } #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing() { // Don't need to decompress to check Smi bits. } #endif void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class CheckSymbol : public FixedInputNodeT<1, CheckSymbol> { using Base = FixedInputNodeT<1, CheckSymbol>; public: explicit CheckSymbol(uint64_t bitfield, CheckType check_type) : Base(CheckTypeBitField::update(bitfield, check_type)) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; static constexpr int kReceiverIndex = 0; Input& receiver_input() { return input(kReceiverIndex); } CheckType check_type() const { return CheckTypeBitField::decode(bitfield()); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: using CheckTypeBitField = NextBitField<CheckType, 1>; }; class CheckInstanceType : public FixedInputNodeT<1, CheckInstanceType> { using Base = FixedInputNodeT<1, CheckInstanceType>; public: explicit CheckInstanceType(uint64_t bitfield, CheckType check_type, InstanceType instance_type) : CheckInstanceType(bitfield, check_type, instance_type, instance_type) {} explicit CheckInstanceType(uint64_t bitfield, CheckType check_type, InstanceType first_instance_type, InstanceType last_instance_type) : Base(CheckTypeBitField::update(bitfield, check_type)), first_instance_type_(first_instance_type), last_instance_type_(last_instance_type) { DCHECK_LE(first_instance_type, last_instance_type); } static constexpr OpProperties kProperties = OpProperties::EagerDeopt(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; static constexpr int kReceiverIndex = 0; Input& receiver_input() { return input(kReceiverIndex); } CheckType check_type() const { return CheckTypeBitField::decode(bitfield()); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: using CheckTypeBitField = NextBitField<CheckType, 1>; const InstanceType first_instance_type_; const InstanceType last_instance_type_; }; class CheckString : public FixedInputNodeT<1, CheckString> { using Base = FixedInputNodeT<1, CheckString>; public: explicit CheckString(uint64_t bitfield, CheckType check_type) : Base(CheckTypeBitField::update(bitfield, check_type)) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; static constexpr int kReceiverIndex = 0; Input& receiver_input() { return input(kReceiverIndex); } CheckType check_type() const { return CheckTypeBitField::decode(bitfield()); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: using CheckTypeBitField = NextBitField<CheckType, 1>; }; class CheckMapsWithMigration : public FixedInputNodeT<1, CheckMapsWithMigration> { using Base = FixedInputNodeT<1, CheckMapsWithMigration>; public: explicit CheckMapsWithMigration(uint64_t bitfield, const compiler::ZoneRefSet<Map>& maps, CheckType check_type) : Base(CheckTypeBitField::update(bitfield, check_type)), maps_(maps) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt() | OpProperties::DeferredCall() | OpProperties::CanAllocate() | OpProperties::CanWrite() | OpProperties::CanRead(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; const compiler::ZoneRefSet<Map>& maps() const { return maps_; } static constexpr int kReceiverIndex = 0; Input& receiver_input() { return input(kReceiverIndex); } CheckType check_type() const { return CheckTypeBitField::decode(bitfield()); } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: using CheckTypeBitField = NextBitField<CheckType, 1>; const compiler::ZoneRefSet<Map> maps_; }; class CheckFixedArrayNonEmpty : public FixedInputNodeT<1, CheckFixedArrayNonEmpty> { using Base = FixedInputNodeT<1, CheckFixedArrayNonEmpty>; public: explicit CheckFixedArrayNonEmpty(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; static constexpr int kReceiverIndex = 0; Input& receiver_input() { return input(kReceiverIndex); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class CheckJSDataViewBounds : public FixedInputNodeT<2, CheckJSDataViewBounds> { using Base = FixedInputNodeT<2, CheckJSDataViewBounds>; public: explicit CheckJSDataViewBounds(uint64_t bitfield, ExternalArrayType element_type) : Base(bitfield), element_type_(element_type) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kInt32}; static constexpr int kReceiverIndex = 0; static constexpr int kIndexIndex = 1; Input& receiver_input() { return input(kReceiverIndex); } Input& index_input() { return input(kIndexIndex); } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: ExternalArrayType element_type_; }; class CheckJSTypedArrayBounds : public FixedInputNodeT<2, CheckJSTypedArrayBounds> { using Base = FixedInputNodeT<2, CheckJSTypedArrayBounds>; public: explicit CheckJSTypedArrayBounds(uint64_t bitfield, ElementsKind elements_kind) : Base(bitfield), elements_kind_(elements_kind) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kUint32}; static constexpr int kReceiverIndex = 0; static constexpr int kIndexIndex = 1; Input& receiver_input() { return input(kReceiverIndex); } Input& index_input() { return input(kIndexIndex); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: ElementsKind elements_kind_; }; class CheckInt32Condition : public FixedInputNodeT<2, CheckInt32Condition> { using Base = FixedInputNodeT<2, CheckInt32Condition>; public: explicit CheckInt32Condition(uint64_t bitfield, AssertCondition condition, DeoptimizeReason reason) : Base(bitfield | ConditionField::encode(condition) | ReasonField::encode(reason)) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kInt32, ValueRepresentation::kInt32}; static constexpr int kLeftIndex = 0; static constexpr int kRightIndex = 1; Input& left_input() { return input(kLeftIndex); } Input& right_input() { return input(kRightIndex); } AssertCondition condition() const { return ConditionField::decode(bitfield()); } DeoptimizeReason reason() const { return ReasonField::decode(bitfield()); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: using ConditionField = NextBitField<AssertCondition, base::bits::WhichPowerOfTwo<size_t>( base::bits::RoundUpToPowerOfTwo32( kNumAssertConditions))>; using ReasonField = ConditionField::Next<DeoptimizeReason, base::bits::WhichPowerOfTwo<size_t>( base::bits::RoundUpToPowerOfTwo32( kDeoptimizeReasonCount))>; }; class DebugBreak : public FixedInputNodeT<0, DebugBreak> { using Base = FixedInputNodeT<0, DebugBreak>; public: explicit DebugBreak(uint64_t bitfield) : Base(bitfield) {} void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class FunctionEntryStackCheck : public FixedInputNodeT<0, FunctionEntryStackCheck> { using Base = FixedInputNodeT<0, FunctionEntryStackCheck>; public: explicit FunctionEntryStackCheck(uint64_t bitfield) : Base(bitfield) {} // Although FunctionEntryStackCheck calls a builtin, we don't mark it as Call // here. The register allocator should not spill any live registers, since the // builtin already handles it. The only possible live register is // kJavaScriptCallNewTargetRegister. static constexpr OpProperties kProperties = OpProperties::LazyDeopt() | OpProperties::DeferredCall(); int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class CheckedInternalizedString : public FixedInputValueNodeT<1, CheckedInternalizedString> { using Base = FixedInputValueNodeT<1, CheckedInternalizedString>; public: explicit CheckedInternalizedString(uint64_t bitfield, CheckType check_type) : Base(CheckTypeBitField::update(bitfield, check_type)) { CHECK_EQ(properties().value_representation(), ValueRepresentation::kTagged); } static constexpr OpProperties kProperties = OpProperties::EagerDeopt() | OpProperties::TaggedValue(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; static constexpr int kObjectIndex = 0; Input& object_input() { return Node::input(kObjectIndex); } CheckType check_type() const { return CheckTypeBitField::decode(bitfield()); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: using CheckTypeBitField = NextBitField<CheckType, 1>; }; class CheckedObjectToIndex : public FixedInputValueNodeT<1, CheckedObjectToIndex> { using Base = FixedInputValueNodeT<1, CheckedObjectToIndex>; public: explicit CheckedObjectToIndex(uint64_t bitfield, CheckType check_type) : Base(CheckTypeBitField::update(bitfield, check_type)) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt() | OpProperties::Int32() | OpProperties::DeferredCall() | OpProperties::ConversionNode(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; static constexpr int kObjectIndex = 0; Input& object_input() { return Node::input(kObjectIndex); } CheckType check_type() const { return CheckTypeBitField::decode(bitfield()); } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: using CheckTypeBitField = NextBitField<CheckType, 1>; }; class GetTemplateObject : public FixedInputValueNodeT<1, GetTemplateObject> { using Base = FixedInputValueNodeT<1, GetTemplateObject>; public: explicit GetTemplateObject( uint64_t bitfield, compiler::SharedFunctionInfoRef shared_function_info, const compiler::FeedbackSource& feedback) : Base(bitfield), shared_function_info_(shared_function_info), feedback_(feedback) {} // The implementation currently calls runtime. static constexpr OpProperties kProperties = OpProperties::GenericRuntimeOrBuiltinCall(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; Input& description() { return input(0); } compiler::SharedFunctionInfoRef shared_function_info() { return shared_function_info_; } compiler::FeedbackSource feedback() const { return feedback_; } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: compiler::SharedFunctionInfoRef shared_function_info_; const compiler::FeedbackSource feedback_; }; class HasInPrototypeChain : public FixedInputValueNodeT<1, HasInPrototypeChain> { using Base = FixedInputValueNodeT<1, HasInPrototypeChain>; public: explicit HasInPrototypeChain(uint64_t bitfield, compiler::HeapObjectRef prototype) : Base(bitfield), prototype_(prototype) {} // The implementation can enter user code in the deferred call (due to // proxied getPrototypeOf). static constexpr OpProperties kProperties = OpProperties::DeferredCall() | OpProperties::CanCallUserCode(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; Input& object() { return input(0); } compiler::HeapObjectRef prototype() { return prototype_; } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: compiler::HeapObjectRef prototype_; }; class BuiltinStringFromCharCode : public FixedInputValueNodeT<1, BuiltinStringFromCharCode> { using Base = FixedInputValueNodeT<1, BuiltinStringFromCharCode>; public: explicit BuiltinStringFromCharCode(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::CanAllocate() | OpProperties::DeferredCall(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kInt32}; Input& code_input() { return input(0); } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class BuiltinStringPrototypeCharCodeOrCodePointAt : public FixedInputValueNodeT<2, BuiltinStringPrototypeCharCodeOrCodePointAt> { using Base = FixedInputValueNodeT<2, BuiltinStringPrototypeCharCodeOrCodePointAt>; public: enum Mode { kCharCodeAt, kCodePointAt, }; explicit BuiltinStringPrototypeCharCodeOrCodePointAt(uint64_t bitfield, Mode mode) : Base(bitfield), mode_(mode) {} static constexpr OpProperties kProperties = OpProperties::CanAllocate() | OpProperties::CanRead() | OpProperties::DeferredCall() | OpProperties::Int32(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kInt32}; static constexpr int kStringIndex = 0; static constexpr int kIndexIndex = 1; Input& string_input() { return input(kStringIndex); } Input& index_input() { return input(kIndexIndex); } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: Mode mode_; }; class PolymorphicAccessInfo { public: enum Kind { kNotFound, kConstant, kDataLoad, kModuleExport, kStringLength, }; static PolymorphicAccessInfo NotFound( const ZoneVector<compiler::MapRef>& maps) { return PolymorphicAccessInfo(kNotFound, maps, Representation::Tagged()); } static PolymorphicAccessInfo Constant( const ZoneVector<compiler::MapRef>& maps, compiler::ObjectRef constant) { return PolymorphicAccessInfo(kConstant, maps, Representation::Tagged(), constant); } static PolymorphicAccessInfo DataLoad( const ZoneVector<compiler::MapRef>& maps, Representation representation, compiler::OptionalJSObjectRef holder, FieldIndex field_index) { return PolymorphicAccessInfo(kDataLoad, maps, representation, holder, field_index); } static PolymorphicAccessInfo ModuleExport( const ZoneVector<compiler::MapRef>& maps, compiler::CellRef cell) { return PolymorphicAccessInfo(kModuleExport, maps, Representation::Tagged(), cell); } static PolymorphicAccessInfo StringLength( const ZoneVector<compiler::MapRef>& maps) { return PolymorphicAccessInfo(kStringLength, maps, Representation::Smi()); } Kind kind() const { return kind_; } const ZoneVector<compiler::MapRef>& maps() const { return maps_; } Handle<Object> constant() const { DCHECK_EQ(kind_, kConstant); return constant_.object(); } Handle<Cell> cell() const { DCHECK_EQ(kind_, kModuleExport); return constant_.AsCell().object(); } compiler::OptionalJSObjectRef holder() const { DCHECK_EQ(kind_, kDataLoad); return data_load_.holder_; } FieldIndex field_index() const { DCHECK_EQ(kind_, kDataLoad); return data_load_.field_index_; } Representation field_representation() const { return representation_; } private: explicit PolymorphicAccessInfo(Kind kind, const ZoneVector<compiler::MapRef>& maps, Representation representation) : kind_(kind), maps_(maps), representation_(representation) { DCHECK(kind == kNotFound || kind == kStringLength); } PolymorphicAccessInfo(Kind kind, const ZoneVector<compiler::MapRef>& maps, Representation representation, compiler::ObjectRef constant) : kind_(kind), maps_(maps), representation_(representation), constant_(constant) { DCHECK(kind == kConstant || kind == kModuleExport); } PolymorphicAccessInfo(Kind kind, const ZoneVector<compiler::MapRef>& maps, Representation representation, compiler::OptionalJSObjectRef holder, FieldIndex field_index) : kind_(kind), maps_(maps), representation_(representation), data_load_{holder, field_index} { DCHECK_EQ(kind, kDataLoad); } const Kind kind_; // TODO(victorgomes): Create a PolymorphicMapChecks and avoid the maps here. const ZoneVector<compiler::MapRef> maps_; const Representation representation_; union { const compiler::ObjectRef constant_; struct { const compiler::OptionalJSObjectRef holder_; const FieldIndex field_index_; } data_load_; }; }; class LoadPolymorphicTaggedField : public FixedInputValueNodeT<1, LoadPolymorphicTaggedField> { using Base = FixedInputValueNodeT<1, LoadPolymorphicTaggedField>; public: explicit LoadPolymorphicTaggedField( uint64_t bitfield, Representation field_representation, ZoneVector<PolymorphicAccessInfo>&& access_info) : Base(bitfield), field_representation_(field_representation), access_infos_(access_info) {} static constexpr OpProperties kProperties = OpProperties::CanAllocate() | OpProperties::CanRead() | OpProperties::EagerDeopt() | OpProperties::DeferredCall(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; static constexpr int kObjectIndex = 0; Input& object_input() { return input(kObjectIndex); } Representation field_representation() const { return field_representation_; } const ZoneVector<PolymorphicAccessInfo> access_infos() const { return access_infos_; } int MaxCallStackArgs() const { return 0; } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: Representation field_representation_; ZoneVector<PolymorphicAccessInfo> access_infos_; }; class LoadPolymorphicDoubleField : public FixedInputValueNodeT<1, LoadPolymorphicDoubleField> { using Base = FixedInputValueNodeT<1, LoadPolymorphicDoubleField>; public: explicit LoadPolymorphicDoubleField( uint64_t bitfield, ZoneVector<PolymorphicAccessInfo>&& access_info) : Base(bitfield), access_infos_(access_info) {} static constexpr OpProperties kProperties = OpProperties::CanRead() | OpProperties::EagerDeopt() | OpProperties::Float64(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; static constexpr int kObjectIndex = 0; Input& object_input() { return input(kObjectIndex); } const ZoneVector<PolymorphicAccessInfo> access_infos() const { return access_infos_; } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: ZoneVector<PolymorphicAccessInfo> access_infos_; }; class LoadTaggedField : public FixedInputValueNodeT<1, LoadTaggedField> { using Base = FixedInputValueNodeT<1, LoadTaggedField>; public: explicit LoadTaggedField(uint64_t bitfield, int offset) : Base(bitfield), offset_(offset) {} static constexpr OpProperties kProperties = OpProperties::CanRead(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; int offset() const { return offset_; } static constexpr int kObjectIndex = 0; Input& object_input() { return input(kObjectIndex); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: const int offset_; }; class LoadDoubleField : public FixedInputValueNodeT<1, LoadDoubleField> { using Base = FixedInputValueNodeT<1, LoadDoubleField>; public: explicit LoadDoubleField(uint64_t bitfield, int offset) : Base(bitfield), offset_(offset) {} static constexpr OpProperties kProperties = OpProperties::CanRead() | OpProperties::Float64(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; int offset() const { return offset_; } static constexpr int kObjectIndex = 0; Input& object_input() { return input(kObjectIndex); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: const int offset_; }; class LoadTaggedFieldByFieldIndex : public FixedInputValueNodeT<2, LoadTaggedFieldByFieldIndex> { using Base = FixedInputValueNodeT<2, LoadTaggedFieldByFieldIndex>; public: explicit LoadTaggedFieldByFieldIndex(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::CanAllocate() | OpProperties::CanRead() | OpProperties::DeferredCall(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged}; static constexpr int kObjectIndex = 0; static constexpr int kIndexIndex = 1; Input& object_input() { return input(kObjectIndex); } Input& index_input() { return input(kIndexIndex); } #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing() { // Only need to decompress the object, the index should be a Smi. object_input().node()->SetTaggedResultNeedsDecompress(); } #endif int MaxCallStackArgs() const { return 0; } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class LoadFixedArrayElement : public FixedInputValueNodeT<2, LoadFixedArrayElement> { using Base = FixedInputValueNodeT<2, LoadFixedArrayElement>; public: explicit LoadFixedArrayElement(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::CanRead(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kInt32}; static constexpr int kElementsIndex = 0; static constexpr int kIndexIndex = 1; Input& elements_input() { return input(kElementsIndex); } Input& index_input() { return input(kIndexIndex); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; }; class EnsureWritableFastElements : public FixedInputValueNodeT<2, EnsureWritableFastElements> { using Base = FixedInputValueNodeT<2, EnsureWritableFastElements>; public: explicit EnsureWritableFastElements(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::CanAllocate() | OpProperties::DeferredCall(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged}; static constexpr int kElementsIndex = 0; static constexpr int kObjectIndex = 1; Input& elements_input() { return input(kElementsIndex); } Input& object_input() { return input(kObjectIndex); } int MaxCallStackArgs() const { return 0; } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class MaybeGrowAndEnsureWritableFastElements : public FixedInputValueNodeT<4, MaybeGrowAndEnsureWritableFastElements> { using Base = FixedInputValueNodeT<4, MaybeGrowAndEnsureWritableFastElements>; public: explicit MaybeGrowAndEnsureWritableFastElements(uint64_t bitfield, ElementsKind elements_kind) : Base(bitfield), elements_kind_(elements_kind) {} static constexpr OpProperties kProperties = OpProperties::CanAllocate() | OpProperties::DeferredCall() | OpProperties::EagerDeopt(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged, ValueRepresentation::kInt32, ValueRepresentation::kInt32}; static constexpr int kElementsIndex = 0; static constexpr int kObjectIndex = 1; static constexpr int kIndexIndex = 2; static constexpr int kElementsLengthIndex = 3; Input& elements_input() { return input(kElementsIndex); } Input& object_input() { return input(kObjectIndex); } Input& index_input() { return input(kIndexIndex); } Input& elements_length_input() { return input(kElementsLengthIndex); } ElementsKind elements_kind() const { return elements_kind_; } int MaxCallStackArgs() const { return 0; } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: const ElementsKind elements_kind_; }; class StoreFixedArrayElementWithWriteBarrier : public FixedInputNodeT<3, StoreFixedArrayElementWithWriteBarrier> { using Base = FixedInputNodeT<3, StoreFixedArrayElementWithWriteBarrier>; public: explicit StoreFixedArrayElementWithWriteBarrier(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::CanWrite() | OpProperties::DeferredCall(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kInt32, ValueRepresentation::kTagged}; static constexpr int kElementsIndex = 0; static constexpr int kIndexIndex = 1; static constexpr int kValueIndex = 2; Input& elements_input() { return input(kElementsIndex); } Input& index_input() { return input(kIndexIndex); } Input& value_input() { return input(kValueIndex); } int MaxCallStackArgs() const { return 0; } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; // StoreFixedArrayElementNoWriteBarrier never does a Deferred Call. However, // PhiRepresentationSelector can cause some StoreFixedArrayElementNoWriteBarrier // to become StoreFixedArrayElementWithWriteBarrier, which can do Deferred // Calls, and thus need the register snapshot. We thus set the DeferredCall // property in StoreFixedArrayElementNoWriteBarrier so that it's allocated with // enough space for the register snapshot. class StoreFixedArrayElementNoWriteBarrier : public FixedInputNodeT<3, StoreFixedArrayElementNoWriteBarrier> { using Base = FixedInputNodeT<3, StoreFixedArrayElementNoWriteBarrier>; public: explicit StoreFixedArrayElementNoWriteBarrier(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::CanWrite() | OpProperties::DeferredCall(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kInt32, ValueRepresentation::kTagged}; static constexpr int kElementsIndex = 0; static constexpr int kIndexIndex = 1; static constexpr int kValueIndex = 2; Input& elements_input() { return input(kElementsIndex); } Input& index_input() { return input(kIndexIndex); } Input& value_input() { return input(kValueIndex); } int MaxCallStackArgs() const { // StoreFixedArrayElementNoWriteBarrier never really does any call. return 0; } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class LoadFixedDoubleArrayElement : public FixedInputValueNodeT<2, LoadFixedDoubleArrayElement> { using Base = FixedInputValueNodeT<2, LoadFixedDoubleArrayElement>; public: explicit LoadFixedDoubleArrayElement(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::CanRead() | OpProperties::Float64(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kInt32}; static constexpr int kElementsIndex = 0; static constexpr int kIndexIndex = 1; Input& elements_input() { return input(kElementsIndex); } Input& index_input() { return input(kIndexIndex); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class LoadHoleyFixedDoubleArrayElement : public FixedInputValueNodeT<2, LoadHoleyFixedDoubleArrayElement> { using Base = FixedInputValueNodeT<2, LoadHoleyFixedDoubleArrayElement>; public: explicit LoadHoleyFixedDoubleArrayElement(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::CanRead() | OpProperties::HoleyFloat64(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kInt32}; static constexpr int kElementsIndex = 0; static constexpr int kIndexIndex = 1; Input& elements_input() { return input(kElementsIndex); } Input& index_input() { return input(kIndexIndex); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class LoadHoleyFixedDoubleArrayElementCheckedNotHole : public FixedInputValueNodeT< 2, LoadHoleyFixedDoubleArrayElementCheckedNotHole> { using Base = FixedInputValueNodeT<2, LoadHoleyFixedDoubleArrayElementCheckedNotHole>; public: explicit LoadHoleyFixedDoubleArrayElementCheckedNotHole(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::CanRead() | OpProperties::Float64() | OpProperties::EagerDeopt(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kInt32}; static constexpr int kElementsIndex = 0; static constexpr int kIndexIndex = 1; Input& elements_input() { return input(kElementsIndex); } Input& index_input() { return input(kIndexIndex); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class StoreFixedDoubleArrayElement : public FixedInputNodeT<3, StoreFixedDoubleArrayElement> { using Base = FixedInputNodeT<3, StoreFixedDoubleArrayElement>; public: explicit StoreFixedDoubleArrayElement(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::CanWrite(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kInt32, ValueRepresentation::kHoleyFloat64}; static constexpr int kElementsIndex = 0; static constexpr int kIndexIndex = 1; static constexpr int kValueIndex = 2; Input& elements_input() { return input(kElementsIndex); } Input& index_input() { return input(kIndexIndex); } Input& value_input() { return input(kValueIndex); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class LoadSignedIntDataViewElement : public FixedInputValueNodeT<3, LoadSignedIntDataViewElement> { using Base = FixedInputValueNodeT<3, LoadSignedIntDataViewElement>; public: explicit LoadSignedIntDataViewElement(uint64_t bitfield, ExternalArrayType type) : Base(bitfield), type_(type) { DCHECK(type == ExternalArrayType::kExternalInt8Array || type == ExternalArrayType::kExternalInt16Array || type == ExternalArrayType::kExternalInt32Array); } static constexpr OpProperties kProperties = OpProperties::CanRead() | OpProperties::Int32(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kInt32, ValueRepresentation::kTagged}; static constexpr int kObjectIndex = 0; static constexpr int kIndexIndex = 1; static constexpr int kIsLittleEndianIndex = 2; Input& object_input() { return input(kObjectIndex); } Input& index_input() { return input(kIndexIndex); } Input& is_little_endian_input() { return input(kIsLittleEndianIndex); } bool is_little_endian_constant() { return IsConstantNode(is_little_endian_input().node()->opcode()); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: ExternalArrayType type_; }; class LoadDoubleDataViewElement : public FixedInputValueNodeT<3, LoadDoubleDataViewElement> { using Base = FixedInputValueNodeT<3, LoadDoubleDataViewElement>; public: explicit LoadDoubleDataViewElement(uint64_t bitfield, ExternalArrayType type) : Base(bitfield) { DCHECK_EQ(type, ExternalArrayType::kExternalFloat64Array); } static constexpr OpProperties kProperties = OpProperties::CanRead() | OpProperties::Float64(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kInt32, ValueRepresentation::kTagged}; static constexpr int kObjectIndex = 0; static constexpr int kIndexIndex = 1; static constexpr int kIsLittleEndianIndex = 2; Input& object_input() { return input(kObjectIndex); } Input& index_input() { return input(kIndexIndex); } Input& is_little_endian_input() { return input(kIsLittleEndianIndex); } bool is_little_endian_constant() { return IsConstantNode(is_little_endian_input().node()->opcode()); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; #define LOAD_TYPED_ARRAY(name, properties, ...) \ class name : public FixedInputValueNodeT<2, name> { \ using Base = FixedInputValueNodeT<2, name>; \ \ public: \ explicit name(uint64_t bitfield, ElementsKind elements_kind) \ : Base(bitfield), elements_kind_(elements_kind) { \ DCHECK(elements_kind == \ v8::internal::compiler::turboshaft::any_of(__VA_ARGS__)); \ } \ \ static constexpr OpProperties kProperties = \ OpProperties::CanRead() | properties; \ static constexpr typename Base::InputTypes kInputTypes{ \ ValueRepresentation::kTagged, ValueRepresentation::kUint32}; \ \ static constexpr int kObjectIndex = 0; \ static constexpr int kIndexIndex = 1; \ Input& object_input() { return input(kObjectIndex); } \ Input& index_input() { return input(kIndexIndex); } \ \ void SetValueLocationConstraints(); \ void GenerateCode(MaglevAssembler*, const ProcessingState&); \ void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} \ \ private: \ ElementsKind elements_kind_; \ }; // Nodes that can deopt are larger, since they contain the DeoptInfo. Thus, to // have better performance, we split the LoadxxxTypedArrayElement nodes in two: // those who can deopt and those who can't. Deoptimization in a // LoadxxxTypedArrayElement node is always because of a detached array buffer. // The NoDeopt versions of the nodes rely on the ArrayBufferDetachingProtector, // while the deopting versions have a runtime check that triggers a deopt if the // buffer is detached. LOAD_TYPED_ARRAY(LoadSignedIntTypedArrayElement, OpProperties::EagerDeopt() | OpProperties::Int32(), INT8_ELEMENTS, INT16_ELEMENTS, INT32_ELEMENTS) LOAD_TYPED_ARRAY(LoadSignedIntTypedArrayElementNoDeopt, OpProperties::Int32(), INT8_ELEMENTS, INT16_ELEMENTS, INT32_ELEMENTS) LOAD_TYPED_ARRAY(LoadUnsignedIntTypedArrayElement, OpProperties::EagerDeopt() | OpProperties::Uint32(), UINT8_ELEMENTS, UINT8_CLAMPED_ELEMENTS, UINT16_ELEMENTS, UINT16_ELEMENTS, UINT32_ELEMENTS) LOAD_TYPED_ARRAY(LoadUnsignedIntTypedArrayElementNoDeopt, OpProperties::Uint32(), UINT8_ELEMENTS, UINT8_CLAMPED_ELEMENTS, UINT16_ELEMENTS, UINT16_ELEMENTS, UINT32_ELEMENTS) LOAD_TYPED_ARRAY(LoadDoubleTypedArrayElement, OpProperties::EagerDeopt() | OpProperties::Float64(), FLOAT32_ELEMENTS, FLOAT64_ELEMENTS) LOAD_TYPED_ARRAY(LoadDoubleTypedArrayElementNoDeopt, OpProperties::Float64(), FLOAT32_ELEMENTS, FLOAT64_ELEMENTS) #undef LOAD_TYPED_ARRAY #define STORE_TYPED_ARRAY(name, properties, type, ...) \ class name : public FixedInputNodeT<3, name> { \ using Base = FixedInputNodeT<3, name>; \ \ public: \ explicit name(uint64_t bitfield, ElementsKind elements_kind) \ : Base(bitfield), elements_kind_(elements_kind) { \ DCHECK(elements_kind == \ v8::internal::compiler::turboshaft::any_of(__VA_ARGS__)); \ } \ \ static constexpr OpProperties kProperties = properties; \ static constexpr typename Base::InputTypes kInputTypes{ \ ValueRepresentation::kTagged, ValueRepresentation::kUint32, type}; \ \ static constexpr int kObjectIndex = 0; \ static constexpr int kIndexIndex = 1; \ static constexpr int kValueIndex = 2; \ Input& object_input() { return input(kObjectIndex); } \ Input& index_input() { return input(kIndexIndex); } \ Input& value_input() { return input(kValueIndex); } \ \ void SetValueLocationConstraints(); \ void GenerateCode(MaglevAssembler*, const ProcessingState&); \ void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} \ \ private: \ ElementsKind elements_kind_; \ }; // Nodes that can deopt are larger, since they contain the DeoptInfo. Thus, to // have better performance, we split the StorexxxTypedArrayElement nodes in two: // those who can deopt and those who can't. Deoptimization in a // StorexxxTypedArrayElement node is always because of a detached array buffer. // The NoDeopt versions of the nodes rely on the ArrayBufferDetachingProtector, // while the deopting versions have a runtime check that triggers a deopt if the // buffer is detached. STORE_TYPED_ARRAY(StoreIntTypedArrayElement, OpProperties::EagerDeopt() | OpProperties::CanWrite(), ValueRepresentation::kInt32, INT8_ELEMENTS, INT16_ELEMENTS, INT32_ELEMENTS, UINT8_ELEMENTS, UINT8_CLAMPED_ELEMENTS, UINT16_ELEMENTS, UINT16_ELEMENTS, UINT32_ELEMENTS) STORE_TYPED_ARRAY(StoreIntTypedArrayElementNoDeopt, OpProperties::CanWrite(), ValueRepresentation::kInt32, INT8_ELEMENTS, INT16_ELEMENTS, INT32_ELEMENTS, UINT8_ELEMENTS, UINT8_CLAMPED_ELEMENTS, UINT16_ELEMENTS, UINT16_ELEMENTS, UINT32_ELEMENTS) STORE_TYPED_ARRAY(StoreDoubleTypedArrayElement, OpProperties::EagerDeopt() | OpProperties::CanWrite(), ValueRepresentation::kHoleyFloat64, FLOAT32_ELEMENTS, FLOAT64_ELEMENTS) STORE_TYPED_ARRAY(StoreDoubleTypedArrayElementNoDeopt, OpProperties::CanWrite(), ValueRepresentation::kHoleyFloat64, FLOAT32_ELEMENTS, FLOAT64_ELEMENTS) #undef STORE_TYPED_ARRAY class StoreSignedIntDataViewElement : public FixedInputNodeT<4, StoreSignedIntDataViewElement> { using Base = FixedInputNodeT<4, StoreSignedIntDataViewElement>; public: explicit StoreSignedIntDataViewElement(uint64_t bitfield, ExternalArrayType type) : Base(bitfield), type_(type) { DCHECK(type == ExternalArrayType::kExternalInt8Array || type == ExternalArrayType::kExternalInt16Array || type == ExternalArrayType::kExternalInt32Array); } static constexpr OpProperties kProperties = OpProperties::CanWrite(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kInt32, ValueRepresentation::kInt32, ValueRepresentation::kTagged}; static constexpr int kObjectIndex = 0; static constexpr int kIndexIndex = 1; static constexpr int kValueIndex = 2; static constexpr int kIsLittleEndianIndex = 3; Input& object_input() { return input(kObjectIndex); } Input& index_input() { return input(kIndexIndex); } Input& value_input() { return input(kValueIndex); } Input& is_little_endian_input() { return input(kIsLittleEndianIndex); } bool is_little_endian_constant() { return IsConstantNode(is_little_endian_input().node()->opcode()); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: ExternalArrayType type_; }; class StoreDoubleDataViewElement : public FixedInputNodeT<4, StoreDoubleDataViewElement> { using Base = FixedInputNodeT<4, StoreDoubleDataViewElement>; public: explicit StoreDoubleDataViewElement(uint64_t bitfield, ExternalArrayType type) : Base(bitfield) { DCHECK_EQ(type, ExternalArrayType::kExternalFloat64Array); } static constexpr OpProperties kProperties = OpProperties::CanWrite(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kInt32, ValueRepresentation::kHoleyFloat64, ValueRepresentation::kTagged}; static constexpr int kObjectIndex = 0; static constexpr int kIndexIndex = 1; static constexpr int kValueIndex = 2; static constexpr int kIsLittleEndianIndex = 3; Input& object_input() { return input(kObjectIndex); } Input& index_input() { return input(kIndexIndex); } Input& value_input() { return input(kValueIndex); } Input& is_little_endian_input() { return input(kIsLittleEndianIndex); } bool is_little_endian_constant() { return IsConstantNode(is_little_endian_input().node()->opcode()); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class StoreDoubleField : public FixedInputNodeT<2, StoreDoubleField> { using Base = FixedInputNodeT<2, StoreDoubleField>; public: explicit StoreDoubleField(uint64_t bitfield, int offset) : Base(bitfield), offset_(offset) {} static constexpr OpProperties kProperties = OpProperties::CanWrite(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kFloat64}; int offset() const { return offset_; } static constexpr int kObjectIndex = 0; static constexpr int kValueIndex = 1; Input& object_input() { return input(kObjectIndex); } Input& value_input() { return input(kValueIndex); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: const int offset_; }; class StoreFloat64 : public FixedInputNodeT<2, StoreFloat64> { using Base = FixedInputNodeT<2, StoreFloat64>; public: explicit StoreFloat64(uint64_t bitfield, int offset) : Base(bitfield), offset_(offset) {} static constexpr OpProperties kProperties = OpProperties::CanWrite(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kFloat64}; int offset() const { return offset_; } static constexpr int kObjectIndex = 0; static constexpr int kValueIndex = 1; Input& object_input() { return input(kObjectIndex); } Input& value_input() { return input(kValueIndex); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: const int offset_; }; class StoreTaggedFieldNoWriteBarrier : public FixedInputNodeT<2, StoreTaggedFieldNoWriteBarrier> { using Base = FixedInputNodeT<2, StoreTaggedFieldNoWriteBarrier>; public: explicit StoreTaggedFieldNoWriteBarrier(uint64_t bitfield, int offset) : Base(bitfield), offset_(offset) {} // StoreTaggedFieldNoWriteBarrier never does a Deferred Call. However, // PhiRepresentationSelector can cause some StoreTaggedFieldNoWriteBarrier to // become StoreTaggedFieldWithWriteBarrier, which can do Deferred Calls, and // thus need the register snapshot. We thus set the DeferredCall property in // StoreTaggedFieldNoWriteBarrier so that it's allocated with enough space for // the register snapshot. static constexpr OpProperties kProperties = OpProperties::CanWrite() | OpProperties::DeferredCall(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged}; int offset() const { return offset_; } static constexpr int kObjectIndex = 0; static constexpr int kValueIndex = 1; Input& object_input() { return input(kObjectIndex); } Input& value_input() { return input(kValueIndex); } #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing() { object_input().node()->SetTaggedResultNeedsDecompress(); // Don't need to decompress value to store it. } #endif int MaxCallStackArgs() const { // StoreTaggedFieldNoWriteBarrier never really does any call. return 0; } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: const int offset_; }; class StoreMap : public FixedInputNodeT<1, StoreMap> { using Base = FixedInputNodeT<1, StoreMap>; public: explicit StoreMap(uint64_t bitfield, compiler::MapRef map) : Base(bitfield), map_(map) {} static constexpr OpProperties kProperties = OpProperties::CanWrite() | OpProperties::DeferredCall(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; static constexpr int kObjectIndex = 0; Input& object_input() { return input(kObjectIndex); } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: const compiler::MapRef map_; }; class StoreTaggedFieldWithWriteBarrier : public FixedInputNodeT<2, StoreTaggedFieldWithWriteBarrier> { using Base = FixedInputNodeT<2, StoreTaggedFieldWithWriteBarrier>; public: explicit StoreTaggedFieldWithWriteBarrier(uint64_t bitfield, int offset) : Base(bitfield), offset_(offset) {} static constexpr OpProperties kProperties = OpProperties::CanWrite() | OpProperties::DeferredCall(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged}; int offset() const { return offset_; } static constexpr int kObjectIndex = 0; static constexpr int kValueIndex = 1; Input& object_input() { return input(kObjectIndex); } Input& value_input() { return input(kValueIndex); } #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing() { object_input().node()->SetTaggedResultNeedsDecompress(); // Don't need to decompress value to store it. } #endif int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: const int offset_; }; class LoadGlobal : public FixedInputValueNodeT<1, LoadGlobal> { using Base = FixedInputValueNodeT<1, LoadGlobal>; public: explicit LoadGlobal(uint64_t bitfield, compiler::NameRef name, const compiler::FeedbackSource& feedback, TypeofMode typeof_mode) : Base(bitfield), name_(name), feedback_(feedback), typeof_mode_(typeof_mode) {} // The implementation currently calls runtime. static constexpr OpProperties kProperties = OpProperties::JSCall(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; compiler::NameRef name() const { return name_; } compiler::FeedbackSource feedback() const { return feedback_; } TypeofMode typeof_mode() const { return typeof_mode_; } Input& context() { return input(0); } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: const compiler::NameRef name_; const compiler::FeedbackSource feedback_; const TypeofMode typeof_mode_; }; class StoreGlobal : public FixedInputValueNodeT<2, StoreGlobal> { using Base = FixedInputValueNodeT<2, StoreGlobal>; public: explicit StoreGlobal(uint64_t bitfield, compiler::NameRef name, const compiler::FeedbackSource& feedback) : Base(bitfield), name_(name), feedback_(feedback) {} // The implementation currently calls runtime. static constexpr OpProperties kProperties = OpProperties::JSCall(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged}; compiler::NameRef name() const { return name_; } compiler::FeedbackSource feedback() const { return feedback_; } Input& context() { return input(0); } Input& value() { return input(1); } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: const compiler::NameRef name_; const compiler::FeedbackSource feedback_; }; class UpdateJSArrayLength : public FixedInputNodeT<3, UpdateJSArrayLength> { using Base = FixedInputNodeT<3, UpdateJSArrayLength>; public: explicit UpdateJSArrayLength(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::CanWrite(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kInt32, ValueRepresentation::kInt32}; static constexpr int kObjectIndex = 0; static constexpr int kIndexIndex = 1; static constexpr int kLengthIndex = 2; Input& object_input() { return input(kObjectIndex); } Input& index_input() { return input(kIndexIndex); } Input& length_input() { return input(kLengthIndex); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class LoadNamedGeneric : public FixedInputValueNodeT<2, LoadNamedGeneric> { using Base = FixedInputValueNodeT<2, LoadNamedGeneric>; public: explicit LoadNamedGeneric(uint64_t bitfield, compiler::NameRef name, const compiler::FeedbackSource& feedback) : Base(bitfield), name_(name), feedback_(feedback) {} // The implementation currently calls runtime. static constexpr OpProperties kProperties = OpProperties::JSCall(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged}; compiler::NameRef name() const { return name_; } compiler::FeedbackSource feedback() const { return feedback_; } static constexpr int kContextIndex = 0; static constexpr int kObjectIndex = 1; Input& context() { return input(kContextIndex); } Input& object_input() { return input(kObjectIndex); } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: const compiler::NameRef name_; const compiler::FeedbackSource feedback_; }; class LoadNamedFromSuperGeneric : public FixedInputValueNodeT<3, LoadNamedFromSuperGeneric> { using Base = FixedInputValueNodeT<3, LoadNamedFromSuperGeneric>; public: explicit LoadNamedFromSuperGeneric(uint64_t bitfield, compiler::NameRef name, const compiler::FeedbackSource& feedback) : Base(bitfield), name_(name), feedback_(feedback) {} // The implementation currently calls runtime. static constexpr OpProperties kProperties = OpProperties::JSCall(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged, ValueRepresentation::kTagged}; compiler::NameRef name() const { return name_; } compiler::FeedbackSource feedback() const { return feedback_; } static constexpr int kContextIndex = 0; static constexpr int kReceiverIndex = 1; static constexpr int kLookupStartObjectIndex = 2; Input& context() { return input(kContextIndex); } Input& receiver() { return input(kReceiverIndex); } Input& lookup_start_object() { return input(kLookupStartObjectIndex); } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: const compiler::NameRef name_; const compiler::FeedbackSource feedback_; }; class SetNamedGeneric : public FixedInputValueNodeT<3, SetNamedGeneric> { using Base = FixedInputValueNodeT<3, SetNamedGeneric>; public: explicit SetNamedGeneric(uint64_t bitfield, compiler::NameRef name, const compiler::FeedbackSource& feedback) : Base(bitfield), name_(name), feedback_(feedback) {} // The implementation currently calls runtime. static constexpr OpProperties kProperties = OpProperties::JSCall(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged, ValueRepresentation::kTagged}; compiler::NameRef name() const { return name_; } compiler::FeedbackSource feedback() const { return feedback_; } static constexpr int kContextIndex = 0; static constexpr int kObjectIndex = 1; static constexpr int kValueIndex = 2; Input& context() { return input(kContextIndex); } Input& object_input() { return input(kObjectIndex); } Input& value_input() { return input(kValueIndex); } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: const compiler::NameRef name_; const compiler::FeedbackSource feedback_; }; class LoadEnumCacheLength : public FixedInputValueNodeT<1, LoadEnumCacheLength> { using Base = FixedInputValueNodeT<1, LoadEnumCacheLength>; public: explicit LoadEnumCacheLength(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::CanRead() | OpProperties::Int32(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; static constexpr int kMapInput = 0; Input& map_input() { return input(kMapInput); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class StringAt : public FixedInputValueNodeT<2, StringAt> { using Base = FixedInputValueNodeT<2, StringAt>; public: explicit StringAt(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::CanRead() | OpProperties::CanAllocate() | OpProperties::DeferredCall(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kInt32}; static constexpr int kStringIndex = 0; static constexpr int kIndexIndex = 1; Input& string_input() { return input(kStringIndex); } Input& index_input() { return input(kIndexIndex); } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class StringLength : public FixedInputValueNodeT<1, StringLength> { using Base = FixedInputValueNodeT<1, StringLength>; public: explicit StringLength(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::CanRead() | OpProperties::Int32(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; static constexpr int kObjectIndex = 0; Input& object_input() { return input(kObjectIndex); } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class StringConcat : public FixedInputValueNodeT<2, StringConcat> { using Base = FixedInputValueNodeT<2, StringConcat>; public: explicit StringConcat(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::Call() | OpProperties::LazyDeopt() | OpProperties::CanThrow(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged}; Input& lhs() { return Node::input(0); } Input& rhs() { return Node::input(1); } int MaxCallStackArgs() const { return 0; } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class DefineNamedOwnGeneric : public FixedInputValueNodeT<3, DefineNamedOwnGeneric> { using Base = FixedInputValueNodeT<3, DefineNamedOwnGeneric>; public: explicit DefineNamedOwnGeneric(uint64_t bitfield, compiler::NameRef name, const compiler::FeedbackSource& feedback) : Base(bitfield), name_(name), feedback_(feedback) {} // The implementation currently calls runtime. static constexpr OpProperties kProperties = OpProperties::JSCall(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged, ValueRepresentation::kTagged}; compiler::NameRef name() const { return name_; } compiler::FeedbackSource feedback() const { return feedback_; } static constexpr int kContextIndex = 0; static constexpr int kObjectIndex = 1; static constexpr int kValueIndex = 2; Input& context() { return input(kContextIndex); } Input& object_input() { return input(kObjectIndex); } Input& value_input() { return input(kValueIndex); } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: const compiler::NameRef name_; const compiler::FeedbackSource feedback_; }; class StoreInArrayLiteralGeneric : public FixedInputValueNodeT<4, StoreInArrayLiteralGeneric> { using Base = FixedInputValueNodeT<4, StoreInArrayLiteralGeneric>; public: explicit StoreInArrayLiteralGeneric(uint64_t bitfield, const compiler::FeedbackSource& feedback) : Base(bitfield), feedback_(feedback) {} // The implementation currently calls runtime. static constexpr OpProperties kProperties = OpProperties::JSCall(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged, ValueRepresentation::kTagged, ValueRepresentation::kTagged}; compiler::FeedbackSource feedback() const { return feedback_; } static constexpr int kContextIndex = 0; static constexpr int kObjectIndex = 1; static constexpr int kNameIndex = 2; static constexpr int kValueIndex = 3; Input& context() { return input(kContextIndex); } Input& object_input() { return input(kObjectIndex); } Input& name_input() { return input(kNameIndex); } Input& value_input() { return input(kValueIndex); } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: const compiler::FeedbackSource feedback_; }; class GetKeyedGeneric : public FixedInputValueNodeT<3, GetKeyedGeneric> { using Base = FixedInputValueNodeT<3, GetKeyedGeneric>; public: explicit GetKeyedGeneric(uint64_t bitfield, const compiler::FeedbackSource& feedback) : Base(bitfield), feedback_(feedback) {} // The implementation currently calls runtime. static constexpr OpProperties kProperties = OpProperties::JSCall(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged, ValueRepresentation::kTagged}; compiler::FeedbackSource feedback() const { return feedback_; } static constexpr int kContextIndex = 0; static constexpr int kObjectIndex = 1; static constexpr int kKeyIndex = 2; Input& context() { return input(kContextIndex); } Input& object_input() { return input(kObjectIndex); } Input& key_input() { return input(kKeyIndex); } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: const compiler::FeedbackSource feedback_; }; class SetKeyedGeneric : public FixedInputValueNodeT<4, SetKeyedGeneric> { using Base = FixedInputValueNodeT<4, SetKeyedGeneric>; public: explicit SetKeyedGeneric(uint64_t bitfield, const compiler::FeedbackSource& feedback) : Base(bitfield), feedback_(feedback) {} // The implementation currently calls runtime. static constexpr OpProperties kProperties = OpProperties::JSCall(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged, ValueRepresentation::kTagged, ValueRepresentation::kTagged}; compiler::FeedbackSource feedback() const { return feedback_; } static constexpr int kContextIndex = 0; static constexpr int kObjectIndex = 1; static constexpr int kKeyIndex = 2; static constexpr int kValueIndex = 3; Input& context() { return input(kContextIndex); } Input& object_input() { return input(kObjectIndex); } Input& key_input() { return input(kKeyIndex); } Input& value_input() { return input(kValueIndex); } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: const compiler::FeedbackSource feedback_; }; class DefineKeyedOwnGeneric : public FixedInputValueNodeT<5, DefineKeyedOwnGeneric> { using Base = FixedInputValueNodeT<5, DefineKeyedOwnGeneric>; public: explicit DefineKeyedOwnGeneric(uint64_t bitfield, const compiler::FeedbackSource& feedback) : Base(bitfield), feedback_(feedback) {} // The implementation currently calls runtime. static constexpr OpProperties kProperties = OpProperties::JSCall(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged, ValueRepresentation::kTagged, ValueRepresentation::kTagged, ValueRepresentation::kTagged}; compiler::FeedbackSource feedback() const { return feedback_; } static constexpr int kContextIndex = 0; static constexpr int kObjectIndex = 1; static constexpr int kKeyIndex = 2; static constexpr int kValueIndex = 3; static constexpr int kFlagsIndex = 4; Input& context() { return input(kContextIndex); } Input& object_input() { return input(kObjectIndex); } Input& key_input() { return input(kKeyIndex); } Input& value_input() { return input(kValueIndex); } Input& flags_input() { return input(kFlagsIndex); } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: const compiler::FeedbackSource feedback_; }; class GapMove : public FixedInputNodeT<0, GapMove> { using Base = FixedInputNodeT<0, GapMove>; public: GapMove(uint64_t bitfield, compiler::AllocatedOperand source, compiler::AllocatedOperand target) : Base(bitfield), source_(source), target_(target) {} compiler::AllocatedOperand source() const { return source_; } compiler::AllocatedOperand target() const { return target_; } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: compiler::AllocatedOperand source_; compiler::AllocatedOperand target_; }; class ConstantGapMove : public FixedInputNodeT<0, ConstantGapMove> { using Base = FixedInputNodeT<0, ConstantGapMove>; public: ConstantGapMove(uint64_t bitfield, ValueNode* node, compiler::AllocatedOperand target) : Base(bitfield), node_(node), target_(target) {} compiler::AllocatedOperand target() const { return target_; } ValueNode* node() const { return node_; } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: ValueNode* node_; compiler::InstructionOperand source_; compiler::AllocatedOperand target_; }; class MergePointInterpreterFrameState; // ValueRepresentation doesn't distinguish between Int32 and TruncatedInt32: // both are Int32. For Phi untagging however, it's interesting to have a // difference between the 2, as a TruncatedInt32 would allow untagging to // Float64, whereas a Int32 use wouldn't (because it would require a deopting // Float64->Int32 conversion, whereas the truncating version of this conversion // cannot deopt). We thus use a UseRepresentation to record use hints for Phis. enum class UseRepresentation : uint8_t { kTagged, kInt32, kTruncatedInt32, kUint32, kFloat64, kHoleyFloat64, }; inline std::ostream& operator<<(std::ostream& os, const UseRepresentation& repr) { switch (repr) { case UseRepresentation::kTagged: return os << "Tagged"; case UseRepresentation::kInt32: return os << "Int32"; case UseRepresentation::kTruncatedInt32: return os << "TruncatedInt32"; case UseRepresentation::kUint32: return os << "Uint32"; case UseRepresentation::kFloat64: return os << "Float64"; case UseRepresentation::kHoleyFloat64: return os << "HoleyFloat64"; } } typedef base::EnumSet<ValueRepresentation, int8_t> ValueRepresentationSet; typedef base::EnumSet<UseRepresentation, int8_t> UseRepresentationSet; // TODO(verwaest): It may make more sense to buffer phis in merged_states until // we set up the interpreter frame state for code generation. At that point we // can generate correctly-sized phis. class Phi : public ValueNodeT<Phi> { using Base = ValueNodeT<Phi>; public: using List = base::ThreadedList<Phi>; // TODO(jgruber): More intuitive constructors, if possible. Phi(uint64_t bitfield, MergePointInterpreterFrameState* merge_state, interpreter::Register owner) : Base(bitfield), owner_(owner), merge_state_(merge_state), type_(NodeType::kUnknown), post_loop_type_(NodeType::kUnknown) { DCHECK_NOT_NULL(merge_state); } interpreter::Register owner() const { return owner_; } const MergePointInterpreterFrameState* merge_state() const { return merge_state_; } using Node::initialize_input_null; using Node::reduce_input_count; using Node::set_input; bool is_exception_phi() const { return input_count() == 0; } bool is_loop_phi() const; bool is_backedge_offset(int i) const { return is_loop_phi() && i == input_count() - 1; } void VerifyInputs(MaglevGraphLabeller* graph_labeller) const; #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing() { // Do not mark inputs as decompressing here, since we don't yet know whether // this Phi needs decompression. Instead, let // Node::SetTaggedResultNeedsDecompress pass through phis. } #endif void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; BasicBlock* predecessor_at(int i); void RecordUseReprHint(UseRepresentation repr, int current_offset) { RecordUseReprHint(UseRepresentationSet{repr}, current_offset); } void RecordUseReprHint(UseRepresentationSet repr_mask, int current_offset); UseRepresentationSet get_uses_repr_hints() { return uses_repr_hint_; } UseRepresentationSet get_same_loop_uses_repr_hints() { return same_loop_uses_repr_hint_; } void merge_post_loop_type(NodeType type) { DCHECK(!has_key_); post_loop_type_ = IntersectType(post_loop_type_, type); } void set_post_loop_type(NodeType type) { DCHECK(!has_key_); post_loop_type_ = type; } void promote_post_loop_type() { DCHECK(!has_key_); type_ = post_loop_type_; } void merge_type(NodeType type) { DCHECK(!has_key_); type_ = IntersectType(type_, type); } void set_type(NodeType type) { DCHECK(!has_key_); type_ = type; } NodeType type() const { DCHECK(!has_key_); return type_; } using Key = compiler::turboshaft::SnapshotTable<ValueNode*>::Key; bool has_key() const { return has_key_; } Key key() const { DCHECK(has_key_); return key_; } void set_key(Key key) { has_key_ = true; key_ = key; } // Remembers if a use is unsafely untagged. If that happens we must ensure to // stay within the smi range, even when untagging. void SetUseRequires31BitValue(); bool uses_require_31_bit_value() { return uses_require_31_bit_value_; } private: Phi** next() { return &next_; } const interpreter::Register owner_; // True if the {key_} field has been initialized. bool has_key_ = false; bool uses_require_31_bit_value_ = false; UseRepresentationSet uses_repr_hint_ = {}; UseRepresentationSet same_loop_uses_repr_hint_ = {}; Phi* next_ = nullptr; MergePointInterpreterFrameState* const merge_state_; union { struct { // The type of this Phi based on its predecessors' types. NodeType type_; // {type_} for loop Phis should always be Unknown until their backedge has // been bound (because we don't know what will be the type of the // backedge). However, once the backedge is bound, we might be able to // refine it. {post_loop_type_} is thus used to keep track of loop Phi // types: for loop Phis, we update {post_loop_type_} when we merge // predecessors, but keep {type_} as Unknown. Once the backedge is bound, // we set {type_} as {post_loop_type_}. NodeType post_loop_type_; }; // After graph building, {type_} and {post_loop_type_} are not used anymore, // so we reuse this memory to store the SnapshotTable Key for this Phi for // phi untagging. Key key_; }; friend base::ThreadedListTraits<Phi>; }; class Call : public ValueNodeT<Call> { using Base = ValueNodeT<Call>; public: enum class TargetType { kJSFunction, kAny }; // We assume function and context as fixed inputs. static constexpr int kFunctionIndex = 0; static constexpr int kContextIndex = 1; static constexpr int kFixedInputCount = 2; // We need enough inputs to have these fixed inputs plus the maximum arguments // to a function call. static_assert(kMaxInputs >= kFixedInputCount + Code::kMaxArguments); // This ctor is used when for variable input counts. // Inputs must be initialized manually. Call(uint64_t bitfield, ConvertReceiverMode mode, TargetType target_type, ValueNode* function, ValueNode* context) : Base(bitfield), receiver_mode_(mode), target_type_(target_type) { set_input(kFunctionIndex, function); set_input(kContextIndex, context); } static constexpr OpProperties kProperties = OpProperties::JSCall(); Input& function() { return input(kFunctionIndex); } const Input& function() const { return input(kFunctionIndex); } Input& context() { return input(kContextIndex); } const Input& context() const { return input(kContextIndex); } int num_args() const { return input_count() - kFixedInputCount; } Input& arg(int i) { return input(i + kFixedInputCount); } void set_arg(int i, ValueNode* node) { set_input(i + kFixedInputCount, node); } auto args_begin() { return std::make_reverse_iterator(&arg(-1)); } auto args_end() { return std::make_reverse_iterator(&arg(num_args() - 1)); } void VerifyInputs(MaglevGraphLabeller* graph_labeller) const; #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing(); #endif int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: ConvertReceiverMode receiver_mode_; TargetType target_type_; }; class Construct : public ValueNodeT<Construct> { using Base = ValueNodeT<Construct>; public: // We assume function and context as fixed inputs. static constexpr int kFunctionIndex = 0; static constexpr int kNewTargetIndex = 1; static constexpr int kContextIndex = 2; static constexpr int kFixedInputCount = 3; // We need enough inputs to have these fixed inputs plus the maximum arguments // to a function call. static_assert(kMaxInputs >= kFixedInputCount + Code::kMaxArguments); // This ctor is used when for variable input counts. // Inputs must be initialized manually. Construct(uint64_t bitfield, const compiler::FeedbackSource& feedback, ValueNode* function, ValueNode* new_target, ValueNode* context) : Base(bitfield), feedback_(feedback) { set_input(kFunctionIndex, function); set_input(kNewTargetIndex, new_target); set_input(kContextIndex, context); } static constexpr OpProperties kProperties = OpProperties::JSCall(); Input& function() { return input(kFunctionIndex); } const Input& function() const { return input(kFunctionIndex); } Input& new_target() { return input(kNewTargetIndex); } const Input& new_target() const { return input(kNewTargetIndex); } Input& context() { return input(kContextIndex); } const Input& context() const { return input(kContextIndex); } int num_args() const { return input_count() - kFixedInputCount; } Input& arg(int i) { return input(i + kFixedInputCount); } void set_arg(int i, ValueNode* node) { set_input(i + kFixedInputCount, node); } auto args_begin() { return std::make_reverse_iterator(&arg(-1)); } auto args_end() { return std::make_reverse_iterator(&arg(num_args() - 1)); } compiler::FeedbackSource feedback() const { return feedback_; } void VerifyInputs(MaglevGraphLabeller* graph_labeller) const; #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing(); #endif int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: const compiler::FeedbackSource feedback_; }; class CallBuiltin : public ValueNodeT<CallBuiltin> { using Base = ValueNodeT<CallBuiltin>; public: enum FeedbackSlotType { kTaggedIndex, kSmi }; // This ctor is used when for variable input counts. // Inputs must be initialized manually. CallBuiltin(uint64_t bitfield, Builtin builtin) : Base(bitfield), builtin_(builtin) { DCHECK( !Builtins::CallInterfaceDescriptorFor(builtin).HasContextParameter()); } // This ctor is used when for variable input counts. // Inputs must be initialized manually. CallBuiltin(uint64_t bitfield, Builtin builtin, ValueNode* context) : Base(bitfield), builtin_(builtin) { DCHECK(Builtins::CallInterfaceDescriptorFor(builtin).HasContextParameter()); // We use the last valid input for the context. set_input(input_count() - 1, context); } // This is an overestimation, since some builtins might not call JS code. static constexpr OpProperties kProperties = OpProperties::JSCall(); bool has_feedback() const { return feedback_.has_value(); } compiler::FeedbackSource feedback() const { DCHECK(has_feedback()); return feedback_.value(); } FeedbackSlotType slot_type() const { DCHECK(has_feedback()); return slot_type_; } void set_feedback(compiler::FeedbackSource const& feedback, FeedbackSlotType slot_type) { feedback_ = feedback; slot_type_ = slot_type; } Builtin builtin() const { return builtin_; } int InputCountWithoutContext() const { auto descriptor = Builtins::CallInterfaceDescriptorFor(builtin_); bool has_context = descriptor.HasContextParameter(); int extra_input_count = has_context ? 1 : 0; return input_count() - extra_input_count; } int InputsInRegisterCount() const { auto descriptor = Builtins::CallInterfaceDescriptorFor(builtin_); if (has_feedback()) { int slot_index = InputCountWithoutContext(); int vector_index = slot_index + 1; // There are three possibilities: // 1. Feedback slot and vector are in register. // 2. Feedback slot is in register and vector is on stack. // 3. Feedback slot and vector are on stack. if (vector_index < descriptor.GetRegisterParameterCount()) { return descriptor.GetRegisterParameterCount() - 2; } else if (vector_index == descriptor.GetRegisterParameterCount()) { return descriptor.GetRegisterParameterCount() - 1; } else { return descriptor.GetRegisterParameterCount(); } } return descriptor.GetRegisterParameterCount(); } auto stack_args_begin() { return std::make_reverse_iterator(&input(InputsInRegisterCount() - 1)); } auto stack_args_end() { return std::make_reverse_iterator(&input(InputCountWithoutContext() - 1)); } void set_arg(int i, ValueNode* node) { set_input(i, node); } int ReturnCount() const { return Builtins::CallInterfaceDescriptorFor(builtin_).GetReturnCount(); } void VerifyInputs(MaglevGraphLabeller* graph_labeller) const; #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing(); #endif int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: template <typename... Args> void PushArguments(MaglevAssembler* masm, Args... extra_args); void PassFeedbackSlotInRegister(MaglevAssembler*); void PushFeedbackAndArguments(MaglevAssembler*); Builtin builtin_; base::Optional<compiler::FeedbackSource> feedback_; FeedbackSlotType slot_type_ = kTaggedIndex; }; class CallCPPBuiltin : public ValueNodeT<CallCPPBuiltin> { using Base = ValueNodeT<CallCPPBuiltin>; // Only 1 return value with arguments on the stack is supported. static constexpr Builtin kCEntry_Builtin = Builtin::kCEntry_Return1_ArgvOnStack_BuiltinExit; public: static constexpr int kTargetIndex = 0; static constexpr int kNewTargetIndex = 1; static constexpr int kContextIndex = 2; static constexpr int kFixedInputCount = 3; CallCPPBuiltin(uint64_t bitfield, Builtin builtin, ValueNode* target, ValueNode* new_target, ValueNode* context) : Base(bitfield), builtin_(builtin) { DCHECK(Builtins::CallInterfaceDescriptorFor(builtin).HasContextParameter()); DCHECK_EQ(Builtins::CallInterfaceDescriptorFor(builtin).GetReturnCount(), 1); set_input(kTargetIndex, target); set_input(kNewTargetIndex, new_target); set_input(kContextIndex, context); } // This is an overestimation, since some builtins might not call JS code. static constexpr OpProperties kProperties = OpProperties::JSCall(); Builtin builtin() const { return builtin_; } Input& target() { return input(kTargetIndex); } const Input& target() const { return input(kTargetIndex); } Input& new_target() { return input(kNewTargetIndex); } const Input& new_target() const { return input(kNewTargetIndex); } Input& context() { return input(kContextIndex); } const Input& context() const { return input(kContextIndex); } int num_args() const { return input_count() - kFixedInputCount; } Input& arg(int i) { return input(i + kFixedInputCount); } void set_arg(int i, ValueNode* node) { set_input(i + kFixedInputCount, node); } auto args_begin() { return std::make_reverse_iterator(&arg(-1)); } auto args_end() { return std::make_reverse_iterator(&arg(num_args() - 1)); } void VerifyInputs(MaglevGraphLabeller* graph_labeller) const; #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing(); #endif int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: Builtin builtin_; }; class CallRuntime : public ValueNodeT<CallRuntime> { using Base = ValueNodeT<CallRuntime>; public: // We assume the context as fixed input. static constexpr int kContextIndex = 0; static constexpr int kFixedInputCount = 1; // This ctor is used when for variable input counts. // Inputs must be initialized manually. CallRuntime(uint64_t bitfield, Runtime::FunctionId function_id, ValueNode* context) : Base(bitfield), function_id_(function_id) { set_input(kContextIndex, context); } static constexpr OpProperties kProperties = OpProperties::JSCall(); Runtime::FunctionId function_id() const { return function_id_; } Input& context() { return input(kContextIndex); } const Input& context() const { return input(kContextIndex); } int num_args() const { return input_count() - kFixedInputCount; } Input& arg(int i) { return input(i + kFixedInputCount); } void set_arg(int i, ValueNode* node) { set_input(i + kFixedInputCount, node); } auto args_begin() { return std::make_reverse_iterator(&arg(-1)); } auto args_end() { return std::make_reverse_iterator(&arg(num_args() - 1)); } int ReturnCount() const { return Runtime::FunctionForId(function_id())->result_size; } void VerifyInputs(MaglevGraphLabeller* graph_labeller) const; #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing(); #endif int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: Runtime::FunctionId function_id_; }; class CallWithSpread : public ValueNodeT<CallWithSpread> { using Base = ValueNodeT<CallWithSpread>; public: // We assume function and context as fixed inputs. static constexpr int kFunctionIndex = 0; static constexpr int kContextIndex = 1; static constexpr int kFixedInputCount = 2; // This ctor is used when for variable input counts. // Inputs must be initialized manually. CallWithSpread(uint64_t bitfield, ValueNode* function, ValueNode* context) : Base(bitfield) { set_input(kFunctionIndex, function); set_input(kContextIndex, context); } static constexpr OpProperties kProperties = OpProperties::JSCall(); Input& function() { return input(kFunctionIndex); } const Input& function() const { return input(kFunctionIndex); } Input& context() { return input(kContextIndex); } const Input& context() const { return input(kContextIndex); } int num_args() const { return input_count() - kFixedInputCount; } int num_args_no_spread() const { DCHECK_GT(num_args(), 0); return num_args() - 1; } Input& arg(int i) { return input(i + kFixedInputCount); } void set_arg(int i, ValueNode* node) { set_input(i + kFixedInputCount, node); } auto args_no_spread_begin() { return std::make_reverse_iterator(&arg(-1)); } auto args_no_spread_end() { return std::make_reverse_iterator(&arg(num_args_no_spread() - 1)); } Input& spread() { // Spread is the last argument/input. return input(input_count() - 1); } Input& receiver() { return arg(0); } void VerifyInputs(MaglevGraphLabeller* graph_labeller) const; #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing(); #endif int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class CallWithArrayLike : public FixedInputValueNodeT<4, CallWithArrayLike> { using Base = FixedInputValueNodeT<4, CallWithArrayLike>; public: // We assume function and context as fixed inputs. static constexpr int kFunctionIndex = 0; static constexpr int kReceiverIndex = 1; static constexpr int kArgumentsListIndex = 2; static constexpr int kContextIndex = 3; // This ctor is used when for variable input counts. // Inputs must be initialized manually. explicit CallWithArrayLike(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::JSCall(); Input& function() { return input(kFunctionIndex); } Input& receiver() { return input(kReceiverIndex); } Input& arguments_list() { return input(kArgumentsListIndex); } Input& context() { return input(kContextIndex); } void VerifyInputs(MaglevGraphLabeller* graph_labeller) const; #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing(); #endif int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class CallSelf : public ValueNodeT<CallSelf> { using Base = ValueNodeT<CallSelf>; public: static constexpr int kClosureIndex = 0; static constexpr int kContextIndex = 1; static constexpr int kReceiverIndex = 2; static constexpr int kNewTargetIndex = 3; static constexpr int kFixedInputCount = 4; // We need enough inputs to have these fixed inputs plus the maximum arguments // to a function call. static_assert(kMaxInputs >= kFixedInputCount + Code::kMaxArguments); // This ctor is used when for variable input counts. // Inputs must be initialized manually. CallSelf(uint64_t bitfield, compiler::SharedFunctionInfoRef shared_function_info, ValueNode* closure, ValueNode* context, ValueNode* receiver, ValueNode* new_target) : Base(bitfield), shared_function_info_(shared_function_info), expected_parameter_count_( shared_function_info .internal_formal_parameter_count_with_receiver()) { set_input(kClosureIndex, closure); set_input(kContextIndex, context); set_input(kReceiverIndex, receiver); set_input(kNewTargetIndex, new_target); } static constexpr OpProperties kProperties = OpProperties::JSCall(); Input& closure() { return input(kClosureIndex); } const Input& closure() const { return input(kClosureIndex); } Input& context() { return input(kContextIndex); } const Input& context() const { return input(kContextIndex); } Input& receiver() { return input(kReceiverIndex); } const Input& receiver() const { return input(kReceiverIndex); } Input& new_target() { return input(kNewTargetIndex); } const Input& new_target() const { return input(kNewTargetIndex); } int num_args() const { return input_count() - kFixedInputCount; } Input& arg(int i) { return input(i + kFixedInputCount); } void set_arg(int i, ValueNode* node) { set_input(i + kFixedInputCount, node); } auto args_begin() { return std::make_reverse_iterator(&arg(-1)); } auto args_end() { return std::make_reverse_iterator(&arg(num_args() - 1)); } compiler::SharedFunctionInfoRef shared_function_info() const { return shared_function_info_; } void VerifyInputs(MaglevGraphLabeller* graph_labeller) const; #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing(); #endif int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: const compiler::SharedFunctionInfoRef shared_function_info_; // Cache the expected parameter count so that we can access it in // MaxCallStackArgs without needing to unpark the local isolate. int expected_parameter_count_; }; class CallKnownJSFunction : public ValueNodeT<CallKnownJSFunction> { using Base = ValueNodeT<CallKnownJSFunction>; public: static constexpr int kClosureIndex = 0; static constexpr int kContextIndex = 1; static constexpr int kReceiverIndex = 2; static constexpr int kNewTargetIndex = 3; static constexpr int kFixedInputCount = 4; // We need enough inputs to have these fixed inputs plus the maximum arguments // to a function call. static_assert(kMaxInputs >= kFixedInputCount + Code::kMaxArguments); // This ctor is used when for variable input counts. // Inputs must be initialized manually. CallKnownJSFunction(uint64_t bitfield, compiler::SharedFunctionInfoRef shared_function_info, ValueNode* closure, ValueNode* context, ValueNode* receiver, ValueNode* new_target) : Base(bitfield), shared_function_info_(shared_function_info), expected_parameter_count_( shared_function_info .internal_formal_parameter_count_with_receiver()) { set_input(kClosureIndex, closure); set_input(kContextIndex, context); set_input(kReceiverIndex, receiver); set_input(kNewTargetIndex, new_target); } static constexpr OpProperties kProperties = OpProperties::JSCall(); Input& closure() { return input(kClosureIndex); } const Input& closure() const { return input(kClosureIndex); } Input& context() { return input(kContextIndex); } const Input& context() const { return input(kContextIndex); } Input& receiver() { return input(kReceiverIndex); } const Input& receiver() const { return input(kReceiverIndex); } Input& new_target() { return input(kNewTargetIndex); } const Input& new_target() const { return input(kNewTargetIndex); } int num_args() const { return input_count() - kFixedInputCount; } Input& arg(int i) { return input(i + kFixedInputCount); } void set_arg(int i, ValueNode* node) { set_input(i + kFixedInputCount, node); } auto args_begin() { return std::make_reverse_iterator(&arg(-1)); } auto args_end() { return std::make_reverse_iterator(&arg(num_args() - 1)); } compiler::SharedFunctionInfoRef shared_function_info() const { return shared_function_info_; } void VerifyInputs(MaglevGraphLabeller* graph_labeller) const; #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing(); #endif int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: const compiler::SharedFunctionInfoRef shared_function_info_; // Cache the expected parameter count so that we can access it in // MaxCallStackArgs without needing to unpark the local isolate. int expected_parameter_count_; }; class CallKnownApiFunction : public ValueNodeT<CallKnownApiFunction> { using Base = ValueNodeT<CallKnownApiFunction>; public: enum Mode { // Use Builtin::kCallApiCallbackOptimizedNoProfiling. kNoProfiling, // Inline API call sequence into the generated code. kNoProfilingInlined, // Use Builtin::kCallApiCallbackOptimized. kGeneric, }; static constexpr int kContextIndex = 0; static constexpr int kReceiverIndex = 1; static constexpr int kFixedInputCount = 2; // We need enough inputs to have these fixed inputs plus the maximum arguments // to a function call. static_assert(kMaxInputs >= kFixedInputCount + Code::kMaxArguments); // This ctor is used when for variable input counts. // Inputs must be initialized manually. CallKnownApiFunction(uint64_t bitfield, Mode mode, compiler::FunctionTemplateInfoRef function_template_info, compiler::CallHandlerInfoRef call_handler_info, compiler::ObjectRef data, compiler::OptionalJSObjectRef api_holder, ValueNode* context, ValueNode* receiver) : Base(bitfield | ModeField::encode(mode)), function_template_info_(function_template_info), call_handler_info_(call_handler_info), data_(data), api_holder_(api_holder) { set_input(kContextIndex, context); set_input(kReceiverIndex, receiver); } // TODO(ishell): introduce JSApiCall() which will take C++ ABI into account // when deciding which registers to splill. static constexpr OpProperties kProperties = OpProperties::JSCall(); // Input& closure() { return input(kClosureIndex); } // const Input& closure() const { return input(kClosureIndex); } Input& context() { return input(kContextIndex); } const Input& context() const { return input(kContextIndex); } Input& receiver() { return input(kReceiverIndex); } const Input& receiver() const { return input(kReceiverIndex); } int num_args() const { return input_count() - kFixedInputCount; } Input& arg(int i) { return input(i + kFixedInputCount); } void set_arg(int i, ValueNode* node) { set_input(i + kFixedInputCount, node); } auto args_begin() { return std::make_reverse_iterator(&arg(-1)); } auto args_end() { return std::make_reverse_iterator(&arg(num_args() - 1)); } Mode mode() const { return ModeField::decode(bitfield()); } compiler::FunctionTemplateInfoRef function_template_info() const { return function_template_info_; } compiler::CallHandlerInfoRef call_handler_info() const { return call_handler_info_; } compiler::ObjectRef data() const { return data_; } bool inline_builtin() const { return mode() == kNoProfilingInlined; } void VerifyInputs(MaglevGraphLabeller* graph_labeller) const; #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing(); #endif int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: using ModeField = NextBitField<Mode, 2>; void GenerateCallApiCallbackOptimizedInline(MaglevAssembler* masm, const ProcessingState& state); const compiler::FunctionTemplateInfoRef function_template_info_; const compiler::CallHandlerInfoRef call_handler_info_; const compiler::ObjectRef data_; const compiler::OptionalJSObjectRef api_holder_; }; class ConstructWithSpread : public ValueNodeT<ConstructWithSpread> { using Base = ValueNodeT<ConstructWithSpread>; public: // We assume function and context as fixed inputs. static constexpr int kFunctionIndex = 0; static constexpr int kNewTargetIndex = 1; static constexpr int kContextIndex = 2; static constexpr int kFixedInputCount = 3; // This ctor is used when for variable input counts. // Inputs must be initialized manually. ConstructWithSpread(uint64_t bitfield, compiler::FeedbackSource feedback, ValueNode* function, ValueNode* new_target, ValueNode* context) : Base(bitfield), feedback_(feedback) { set_input(kFunctionIndex, function); set_input(kNewTargetIndex, new_target); set_input(kContextIndex, context); } static constexpr OpProperties kProperties = OpProperties::JSCall(); Input& function() { return input(kFunctionIndex); } const Input& function() const { return input(kFunctionIndex); } Input& new_target() { return input(kNewTargetIndex); } const Input& new_target() const { return input(kNewTargetIndex); } Input& context() { return input(kContextIndex); } const Input& context() const { return input(kContextIndex); } int num_args() const { return input_count() - kFixedInputCount; } int num_args_no_spread() const { DCHECK_GT(num_args(), 0); return num_args() - 1; } Input& arg(int i) { return input(i + kFixedInputCount); } void set_arg(int i, ValueNode* node) { set_input(i + kFixedInputCount, node); } Input& spread() { // Spread is the last argument/input. return input(input_count() - 1); } auto args_no_spread_begin() { return std::make_reverse_iterator(&arg(-1)); } auto args_no_spread_end() { return std::make_reverse_iterator(&arg(num_args_no_spread() - 1)); } compiler::FeedbackSource feedback() const { return feedback_; } void VerifyInputs(MaglevGraphLabeller* graph_labeller) const; #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing(); #endif int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: const compiler::FeedbackSource feedback_; }; class ConvertReceiver : public FixedInputValueNodeT<1, ConvertReceiver> { using Base = FixedInputValueNodeT<1, ConvertReceiver>; public: explicit ConvertReceiver(uint64_t bitfield, compiler::NativeContextRef native_context, ConvertReceiverMode mode) : Base(bitfield), native_context_(native_context), mode_(mode) {} Input& receiver_input() { return input(0); } // The implementation currently calls runtime. static constexpr OpProperties kProperties = OpProperties::Call(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: const compiler::NativeContextRef native_context_; ConvertReceiverMode mode_; }; class CheckConstructResult : public FixedInputValueNodeT<2, CheckConstructResult> { using Base = FixedInputValueNodeT<2, CheckConstructResult>; public: explicit CheckConstructResult(uint64_t bitfield) : Base(bitfield) {} Input& construct_result_input() { return input(0); } Input& implicit_receiver_input() { return input(1); } static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged}; int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class CheckDerivedConstructResult : public FixedInputValueNodeT<1, CheckDerivedConstructResult> { using Base = FixedInputValueNodeT<1, CheckDerivedConstructResult>; public: explicit CheckDerivedConstructResult(uint64_t bitfield) : Base(bitfield) {} Input& construct_result_input() { return input(0); } static constexpr OpProperties kProperties = OpProperties::CanThrow() | OpProperties::DeferredCall(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; bool for_derived_constructor(); int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class CheckNotHole : public FixedInputValueNodeT<1, CheckNotHole> { using Base = FixedInputValueNodeT<1, CheckNotHole>; public: explicit CheckNotHole(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::EagerDeopt(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; Input& object_input() { return input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class ConvertHoleToUndefined : public FixedInputValueNodeT<1, ConvertHoleToUndefined> { using Base = FixedInputValueNodeT<1, ConvertHoleToUndefined>; public: explicit ConvertHoleToUndefined(uint64_t bitfield) : Base(bitfield) {} static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; Input& object_input() { return input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class HandleNoHeapWritesInterrupt : public FixedInputNodeT<0, HandleNoHeapWritesInterrupt> { using Base = FixedInputNodeT<0, HandleNoHeapWritesInterrupt>; public: explicit HandleNoHeapWritesInterrupt(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::DeferredCall() | OpProperties::LazyDeopt(); void SetValueLocationConstraints() {} void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} int MaxCallStackArgs() const { return 0; } }; class ReduceInterruptBudgetForLoop : public FixedInputNodeT<0, ReduceInterruptBudgetForLoop> { using Base = FixedInputNodeT<0, ReduceInterruptBudgetForLoop>; public: explicit ReduceInterruptBudgetForLoop(uint64_t bitfield, int amount) : Base(bitfield), amount_(amount) { DCHECK_GT(amount, 0); } static constexpr OpProperties kProperties = OpProperties::DeferredCall() | OpProperties::LazyDeopt(); int amount() const { return amount_; } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: const int amount_; }; class ReduceInterruptBudgetForReturn : public FixedInputNodeT<0, ReduceInterruptBudgetForReturn> { using Base = FixedInputNodeT<0, ReduceInterruptBudgetForReturn>; public: explicit ReduceInterruptBudgetForReturn(uint64_t bitfield, int amount) : Base(bitfield), amount_(amount) { DCHECK_GT(amount, 0); } static constexpr OpProperties kProperties = OpProperties::DeferredCall(); int amount() const { return amount_; } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: const int amount_; }; class ThrowReferenceErrorIfHole : public FixedInputNodeT<1, ThrowReferenceErrorIfHole> { using Base = FixedInputNodeT<1, ThrowReferenceErrorIfHole>; public: explicit ThrowReferenceErrorIfHole(uint64_t bitfield, compiler::NameRef name) : Base(bitfield), name_(name) {} static constexpr OpProperties kProperties = OpProperties::CanThrow() | OpProperties::DeferredCall(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; compiler::NameRef name() const { return name_; } Input& value() { return Node::input(0); } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: const compiler::NameRef name_; }; class ThrowSuperNotCalledIfHole : public FixedInputNodeT<1, ThrowSuperNotCalledIfHole> { using Base = FixedInputNodeT<1, ThrowSuperNotCalledIfHole>; public: explicit ThrowSuperNotCalledIfHole(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::CanThrow() | OpProperties::DeferredCall(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; Input& value() { return Node::input(0); } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class ThrowSuperAlreadyCalledIfNotHole : public FixedInputNodeT<1, ThrowSuperAlreadyCalledIfNotHole> { using Base = FixedInputNodeT<1, ThrowSuperAlreadyCalledIfNotHole>; public: explicit ThrowSuperAlreadyCalledIfNotHole(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::CanThrow() | OpProperties::DeferredCall(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; Input& value() { return Node::input(0); } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class ThrowIfNotCallable : public FixedInputNodeT<1, ThrowIfNotCallable> { using Base = FixedInputNodeT<1, ThrowIfNotCallable>; public: explicit ThrowIfNotCallable(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::CanThrow() | OpProperties::DeferredCall(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; Input& value() { return Node::input(0); } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class ThrowIfNotSuperConstructor : public FixedInputNodeT<2, ThrowIfNotSuperConstructor> { using Base = FixedInputNodeT<2, ThrowIfNotSuperConstructor>; public: explicit ThrowIfNotSuperConstructor(uint64_t bitfield) : Base(bitfield) {} static constexpr OpProperties kProperties = OpProperties::CanThrow() | OpProperties::DeferredCall(); static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged}; Input& constructor() { return Node::input(0); } Input& function() { return Node::input(1); } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class TransitionElementsKindOrCheckMap : public FixedInputNodeT<1, TransitionElementsKindOrCheckMap> { using Base = FixedInputNodeT<1, TransitionElementsKindOrCheckMap>; public: explicit TransitionElementsKindOrCheckMap( uint64_t bitfield, base::Vector<const compiler::MapRef> transition_sources, compiler::MapRef transition_target, CheckType check_type) : Base(CheckTypeBitField::update(bitfield, check_type)), transition_sources_(transition_sources), transition_target_(transition_target) {} // TODO(leszeks): Special case the case where all transitions are fast. static constexpr OpProperties kProperties = OpProperties::AnySideEffects() | OpProperties::DeferredCall() | OpProperties::EagerDeopt(); static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; CheckType check_type() const { return CheckTypeBitField::decode(bitfield()); } Input& object_input() { return Node::input(0); } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: using CheckTypeBitField = NextBitField<CheckType, 1>; const base::Vector<const compiler::MapRef> transition_sources_; const compiler::MapRef transition_target_; }; class ControlNode : public NodeBase { public: // A "hole" in control flow is a control node that unconditionally interrupts // linear control flow (either by jumping or by exiting). // // A "post-dominating" hole is a hole that is guaranteed to be be reached in // control flow after this node (i.e. it is a hole that is a post-dominator // of this node). ControlNode* next_post_dominating_hole() const { return next_post_dominating_hole_; } void set_next_post_dominating_hole(ControlNode* node) { DCHECK_IMPLIES(node != nullptr, node->Is<UnconditionalControlNode>() || node->Is<TerminalControlNode>() || node->Is<Switch>()); next_post_dominating_hole_ = node; } protected: using NodeBase::NodeBase; private: ControlNode* next_post_dominating_hole_ = nullptr; }; class UnconditionalControlNode : public ControlNode { public: BasicBlock* target() const { return target_.block_ptr(); } int predecessor_id() const { return predecessor_id_; } void set_predecessor_id(int id) { predecessor_id_ = id; } protected: explicit UnconditionalControlNode(uint64_t bitfield, BasicBlockRef* target_refs) : ControlNode(bitfield), target_(target_refs) {} explicit UnconditionalControlNode(uint64_t bitfield, BasicBlock* target) : ControlNode(bitfield), target_(target) {} private: const BasicBlockRef target_; int predecessor_id_ = 0; }; template <class Derived> class UnconditionalControlNodeT : public FixedInputNodeTMixin<0, UnconditionalControlNode, Derived> { static_assert(IsUnconditionalControlNode(NodeBase::opcode_of<Derived>)); protected: explicit UnconditionalControlNodeT(uint64_t bitfield, BasicBlockRef* target_refs) : FixedInputNodeTMixin<0, UnconditionalControlNode, Derived>( bitfield, target_refs) {} explicit UnconditionalControlNodeT(uint64_t bitfield, BasicBlock* target) : FixedInputNodeTMixin<0, UnconditionalControlNode, Derived>(bitfield, target) {} }; class ConditionalControlNode : public ControlNode { public: explicit ConditionalControlNode(uint64_t bitfield) : ControlNode(bitfield) {} }; class BranchControlNode : public ConditionalControlNode { public: BranchControlNode(uint64_t bitfield, BasicBlockRef* if_true_refs, BasicBlockRef* if_false_refs) : ConditionalControlNode(bitfield), if_true_(if_true_refs), if_false_(if_false_refs) {} BasicBlock* if_true() const { return if_true_.block_ptr(); } BasicBlock* if_false() const { return if_false_.block_ptr(); } private: BasicBlockRef if_true_; BasicBlockRef if_false_; }; class TerminalControlNode : public ControlNode { protected: explicit TerminalControlNode(uint64_t bitfield) : ControlNode(bitfield) {} }; template <size_t InputCount, class Derived> class TeminalControlNodeT : public FixedInputNodeTMixin<InputCount, TerminalControlNode, Derived> { static_assert(IsTerminalControlNode(NodeBase::opcode_of<Derived>)); protected: explicit TeminalControlNodeT(uint64_t bitfield) : FixedInputNodeTMixin<InputCount, TerminalControlNode, Derived>( bitfield) {} }; template <size_t InputCount, class Derived> class BranchControlNodeT : public FixedInputNodeTMixin<InputCount, BranchControlNode, Derived> { static_assert(IsBranchControlNode(NodeBase::opcode_of<Derived>)); protected: explicit BranchControlNodeT(uint64_t bitfield, BasicBlockRef* if_true_refs, BasicBlockRef* if_false_refs) : FixedInputNodeTMixin<InputCount, BranchControlNode, Derived>( bitfield, if_true_refs, if_false_refs) {} }; class Jump : public UnconditionalControlNodeT<Jump> { using Base = UnconditionalControlNodeT<Jump>; public: Jump(uint64_t bitfield, BasicBlockRef* target_refs) : Base(bitfield, target_refs) {} void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class JumpLoop : public UnconditionalControlNodeT<JumpLoop> { using Base = UnconditionalControlNodeT<JumpLoop>; public: explicit JumpLoop(uint64_t bitfield, BasicBlock* target) : Base(bitfield, target) {} explicit JumpLoop(uint64_t bitfield, BasicBlockRef* ref) : Base(bitfield, ref) {} void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} base::Vector<Input> used_nodes() { return used_node_locations_; } void set_used_nodes(base::Vector<Input> locations) { used_node_locations_ = locations; } private: base::Vector<Input> used_node_locations_; }; class Abort : public TeminalControlNodeT<0, Abort> { using Base = TeminalControlNodeT<0, Abort>; public: explicit Abort(uint64_t bitfield, AbortReason reason) : Base(bitfield), reason_(reason) { DCHECK_EQ(NodeBase::opcode(), opcode_of<Abort>); } static constexpr OpProperties kProperties = OpProperties::Call(); AbortReason reason() const { return reason_; } int MaxCallStackArgs() const; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: const AbortReason reason_; }; class Return : public TeminalControlNodeT<1, Return> { using Base = TeminalControlNodeT<1, Return>; public: explicit Return(uint64_t bitfield) : Base(bitfield) { DCHECK_EQ(NodeBase::opcode(), opcode_of<Return>); } static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; Input& value_input() { return input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class Deopt : public TeminalControlNodeT<0, Deopt> { using Base = TeminalControlNodeT<0, Deopt>; public: explicit Deopt(uint64_t bitfield, DeoptimizeReason reason) : Base(bitfield), reason_(reason) { DCHECK_EQ(NodeBase::opcode(), opcode_of<Deopt>); } static constexpr OpProperties kProperties = OpProperties::EagerDeopt(); DeoptimizeReason reason() const { return reason_; } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: DeoptimizeReason reason_; }; class Switch : public FixedInputNodeTMixin<1, ConditionalControlNode, Switch> { using Base = FixedInputNodeTMixin<1, ConditionalControlNode, Switch>; public: explicit Switch(uint64_t bitfield, int value_base, BasicBlockRef* targets, int size) : Base(bitfield), value_base_(value_base), targets_(targets), size_(size), fallthrough_() {} explicit Switch(uint64_t bitfield, int value_base, BasicBlockRef* targets, int size, BasicBlockRef* fallthrough) : Base(bitfield), value_base_(value_base), targets_(targets), size_(size), fallthrough_(fallthrough) {} static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kInt32}; int value_base() const { return value_base_; } const BasicBlockRef* targets() const { return targets_; } int size() const { return size_; } bool has_fallthrough() const { return fallthrough_.has_value(); } BasicBlock* fallthrough() const { DCHECK(has_fallthrough()); return fallthrough_.value().block_ptr(); } Input& value() { return input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: const int value_base_; const BasicBlockRef* targets_; const int size_; base::Optional<BasicBlockRef> fallthrough_; }; class BranchIfRootConstant : public BranchControlNodeT<1, BranchIfRootConstant> { using Base = BranchControlNodeT<1, BranchIfRootConstant>; public: explicit BranchIfRootConstant(uint64_t bitfield, RootIndex root_index, BasicBlockRef* if_true_refs, BasicBlockRef* if_false_refs) : Base(bitfield, if_true_refs, if_false_refs), root_index_(root_index) {} static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; RootIndex root_index() { return root_index_; } Input& condition_input() { return input(0); } #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing() { // Don't need to decompress values to reference compare. } #endif void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: RootIndex root_index_; }; class BranchIfUndefinedOrNull : public BranchControlNodeT<1, BranchIfUndefinedOrNull> { using Base = BranchControlNodeT<1, BranchIfUndefinedOrNull>; public: explicit BranchIfUndefinedOrNull(uint64_t bitfield, BasicBlockRef* if_true_refs, BasicBlockRef* if_false_refs) : Base(bitfield, if_true_refs, if_false_refs) {} static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; Input& condition_input() { return input(0); } #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing() { // Don't need to decompress values to reference compare. } #endif void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class BranchIfUndetectable : public BranchControlNodeT<1, BranchIfUndetectable> { using Base = BranchControlNodeT<1, BranchIfUndetectable>; public: explicit BranchIfUndetectable(uint64_t bitfield, CheckType check_type, BasicBlockRef* if_true_refs, BasicBlockRef* if_false_refs) : Base(CheckTypeBitField::update(bitfield, check_type), if_true_refs, if_false_refs) {} static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; Input& condition_input() { return input(0); } CheckType check_type() const { return CheckTypeBitField::decode(bitfield()); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: using CheckTypeBitField = NextBitField<CheckType, 1>; }; class BranchIfJSReceiver : public BranchControlNodeT<1, BranchIfJSReceiver> { using Base = BranchControlNodeT<1, BranchIfJSReceiver>; public: explicit BranchIfJSReceiver(uint64_t bitfield, BasicBlockRef* if_true_refs, BasicBlockRef* if_false_refs) : Base(bitfield, if_true_refs, if_false_refs) {} static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; Input& condition_input() { return input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class BranchIfToBooleanTrue : public BranchControlNodeT<1, BranchIfToBooleanTrue> { using Base = BranchControlNodeT<1, BranchIfToBooleanTrue>; public: explicit BranchIfToBooleanTrue(uint64_t bitfield, CheckType check_type, BasicBlockRef* if_true_refs, BasicBlockRef* if_false_refs) : Base(CheckTypeBitField::update(bitfield, check_type), if_true_refs, if_false_refs) {} static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; Input& condition_input() { return input(0); } CheckType check_type() const { return CheckTypeBitField::decode(bitfield()); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} private: using CheckTypeBitField = NextBitField<CheckType, 1>; }; class BranchIfInt32ToBooleanTrue : public BranchControlNodeT<1, BranchIfInt32ToBooleanTrue> { using Base = BranchControlNodeT<1, BranchIfInt32ToBooleanTrue>; public: explicit BranchIfInt32ToBooleanTrue(uint64_t bitfield, BasicBlockRef* if_true_refs, BasicBlockRef* if_false_refs) : Base(bitfield, if_true_refs, if_false_refs) {} static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kInt32}; Input& condition_input() { return input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class BranchIfFloat64ToBooleanTrue : public BranchControlNodeT<1, BranchIfFloat64ToBooleanTrue> { using Base = BranchControlNodeT<1, BranchIfFloat64ToBooleanTrue>; public: explicit BranchIfFloat64ToBooleanTrue(uint64_t bitfield, BasicBlockRef* if_true_refs, BasicBlockRef* if_false_refs) : Base(bitfield, if_true_refs, if_false_refs) {} static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kHoleyFloat64}; Input& condition_input() { return input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class BranchIfFloat64IsHole : public BranchControlNodeT<1, BranchIfFloat64IsHole> { using Base = BranchControlNodeT<1, BranchIfFloat64IsHole>; public: explicit BranchIfFloat64IsHole(uint64_t bitfield, BasicBlockRef* if_true_refs, BasicBlockRef* if_false_refs) : Base(bitfield, if_true_refs, if_false_refs) {} static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kHoleyFloat64}; Input& condition_input() { return input(0); } void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class BranchIfInt32Compare : public BranchControlNodeT<2, BranchIfInt32Compare> { using Base = BranchControlNodeT<2, BranchIfInt32Compare>; public: static constexpr int kLeftIndex = 0; static constexpr int kRightIndex = 1; Input& left_input() { return NodeBase::input(kLeftIndex); } Input& right_input() { return NodeBase::input(kRightIndex); } explicit BranchIfInt32Compare(uint64_t bitfield, Operation operation, BasicBlockRef* if_true_refs, BasicBlockRef* if_false_refs) : Base(bitfield, if_true_refs, if_false_refs), operation_(operation) {} static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kInt32, ValueRepresentation::kInt32}; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: Operation operation_; }; class BranchIfFloat64Compare : public BranchControlNodeT<2, BranchIfFloat64Compare> { using Base = BranchControlNodeT<2, BranchIfFloat64Compare>; public: static constexpr int kLeftIndex = 0; static constexpr int kRightIndex = 1; Input& left_input() { return NodeBase::input(kLeftIndex); } Input& right_input() { return NodeBase::input(kRightIndex); } explicit BranchIfFloat64Compare(uint64_t bitfield, Operation operation, BasicBlockRef* if_true_refs, BasicBlockRef* if_false_refs) : Base(bitfield, if_true_refs, if_false_refs), operation_(operation) {} static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kFloat64, ValueRepresentation::kFloat64}; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: Operation operation_; }; class BranchIfReferenceEqual : public BranchControlNodeT<2, BranchIfReferenceEqual> { using Base = BranchControlNodeT<2, BranchIfReferenceEqual>; public: static constexpr int kLeftIndex = 0; static constexpr int kRightIndex = 1; Input& left_input() { return NodeBase::input(kLeftIndex); } Input& right_input() { return NodeBase::input(kRightIndex); } explicit BranchIfReferenceEqual(uint64_t bitfield, BasicBlockRef* if_true_refs, BasicBlockRef* if_false_refs) : Base(bitfield, if_true_refs, if_false_refs) {} static constexpr typename Base::InputTypes kInputTypes{ ValueRepresentation::kTagged, ValueRepresentation::kTagged}; #ifdef V8_COMPRESS_POINTERS void MarkTaggedInputsAsDecompressing() { // Don't need to decompress values to reference compare. } #endif void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} }; class BranchIfTypeOf : public BranchControlNodeT<1, BranchIfTypeOf> { using Base = BranchControlNodeT<1, BranchIfTypeOf>; public: static constexpr int kValueIndex = 0; Input& value_input() { return NodeBase::input(kValueIndex); } explicit BranchIfTypeOf(uint64_t bitfield, interpreter::TestTypeOfFlags::LiteralFlag literal, BasicBlockRef* if_true_refs, BasicBlockRef* if_false_refs) : Base(bitfield, if_true_refs, if_false_refs), literal_(literal) {} static constexpr typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged}; void SetValueLocationConstraints(); void GenerateCode(MaglevAssembler*, const ProcessingState&); void PrintParams(std::ostream&, MaglevGraphLabeller*) const; private: interpreter::TestTypeOfFlags::LiteralFlag literal_; }; constexpr inline OpProperties StaticPropertiesForOpcode(Opcode opcode) { switch (opcode) { #define CASE(op) \ case Opcode::k##op: \ return op::kProperties; NODE_BASE_LIST(CASE) #undef CASE } } template <typename Function> inline void NodeBase::ForAllInputsInRegallocAssignmentOrder(Function&& f) { auto iterate_inputs = [&](InputAllocationPolicy category) { for (Input& input : *this) { switch (compiler::UnallocatedOperand::cast(input.operand()) .extended_policy()) { case compiler::UnallocatedOperand::MUST_HAVE_REGISTER: if (category == InputAllocationPolicy::kArbitraryRegister) f(category, &input); break; case compiler::UnallocatedOperand::REGISTER_OR_SLOT_OR_CONSTANT: if (category == InputAllocationPolicy::kAny) f(category, &input); break; case compiler::UnallocatedOperand::FIXED_REGISTER: case compiler::UnallocatedOperand::FIXED_FP_REGISTER: if (category == InputAllocationPolicy::kFixedRegister) f(category, &input); break; case compiler::UnallocatedOperand::REGISTER_OR_SLOT: case compiler::UnallocatedOperand::SAME_AS_INPUT: case compiler::UnallocatedOperand::NONE: case compiler::UnallocatedOperand::MUST_HAVE_SLOT: UNREACHABLE(); } } }; iterate_inputs(InputAllocationPolicy::kFixedRegister); iterate_inputs(InputAllocationPolicy::kArbitraryRegister); iterate_inputs(InputAllocationPolicy::kAny); } } // namespace maglev } // namespace internal } // namespace v8 #endif // V8_MAGLEV_MAGLEV_IR_H_