// Copyright 2015 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_COMPILER_CODE_ASSEMBLER_H_ #define V8_COMPILER_CODE_ASSEMBLER_H_ #include #include // Clients of this interface shouldn't depend on lots of compiler internals. // Do not include anything from src/compiler here! #include "src/allocation.h" #include "src/base/macros.h" #include "src/builtins/builtins.h" #include "src/code-factory.h" #include "src/globals.h" #include "src/heap/heap.h" #include "src/machine-type.h" #include "src/objects.h" #include "src/objects/data-handler.h" #include "src/objects/map.h" #include "src/objects/maybe-object.h" #include "src/runtime/runtime.h" #include "src/zone/zone-containers.h" namespace v8 { namespace internal { class Callable; class CallInterfaceDescriptor; class Isolate; class JSCollection; class JSRegExpStringIterator; class JSWeakCollection; class JSWeakMap; class JSWeakSet; class MaybeObject; class PromiseCapability; class PromiseFulfillReactionJobTask; class PromiseReaction; class PromiseReactionJobTask; class PromiseRejectReactionJobTask; class InterpreterData; class Factory; class Zone; template class Signature; struct UntaggedT {}; struct IntegralT : UntaggedT {}; struct WordT : IntegralT { static const MachineRepresentation kMachineRepresentation = (kPointerSize == 4) ? MachineRepresentation::kWord32 : MachineRepresentation::kWord64; }; struct RawPtrT : WordT { static constexpr MachineType kMachineType = MachineType::Pointer(); }; template struct RawPtr : RawPtrT {}; struct Word32T : IntegralT { static const MachineRepresentation kMachineRepresentation = MachineRepresentation::kWord32; }; struct Int32T : Word32T { static constexpr MachineType kMachineType = MachineType::Int32(); }; struct Uint32T : Word32T { static constexpr MachineType kMachineType = MachineType::Uint32(); }; struct Word64T : IntegralT { static const MachineRepresentation kMachineRepresentation = MachineRepresentation::kWord64; }; struct Int64T : Word64T { static constexpr MachineType kMachineType = MachineType::Int64(); }; struct Uint64T : Word64T { static constexpr MachineType kMachineType = MachineType::Uint64(); }; struct IntPtrT : WordT { static constexpr MachineType kMachineType = MachineType::IntPtr(); }; struct UintPtrT : WordT { static constexpr MachineType kMachineType = MachineType::UintPtr(); }; struct Float32T : UntaggedT { static const MachineRepresentation kMachineRepresentation = MachineRepresentation::kFloat32; static constexpr MachineType kMachineType = MachineType::Float32(); }; struct Float64T : UntaggedT { static const MachineRepresentation kMachineRepresentation = MachineRepresentation::kFloat64; static constexpr MachineType kMachineType = MachineType::Float64(); }; // Result of a comparison operation. struct BoolT : Word32T {}; // Value type of a Turbofan node with two results. template struct PairT {}; inline constexpr MachineType CommonMachineType(MachineType type1, MachineType type2) { return (type1 == type2) ? type1 : ((type1.IsTagged() && type2.IsTagged()) ? MachineType::AnyTagged() : MachineType::None()); } template struct MachineTypeOf { static constexpr MachineType value = Type::kMachineType; }; template constexpr MachineType MachineTypeOf::value; template <> struct MachineTypeOf { static constexpr MachineType value = MachineType::AnyTagged(); }; template <> struct MachineTypeOf { static constexpr MachineType value = MachineType::AnyTagged(); }; template <> struct MachineTypeOf { static constexpr MachineType value = MachineType::TaggedSigned(); }; template struct MachineTypeOf::value>::type> { static constexpr MachineType value = MachineType::TaggedPointer(); }; template constexpr MachineType MachineTypeOf< HeapObjectSubtype, typename std::enable_if::value>::type>::value; template struct MachineRepresentationOf { static const MachineRepresentation value = Type::kMachineRepresentation; }; template struct MachineRepresentationOf< T, typename std::enable_if::value>::type> { static const MachineRepresentation value = MachineTypeOf::value.representation(); }; template struct MachineRepresentationOf< T, typename std::enable_if::value>::type> { static const MachineRepresentation value = MachineTypeOf::value.representation(); }; template struct is_valid_type_tag { static const bool value = std::is_base_of::value || std::is_base_of::value || std::is_base_of::value || std::is_same::value; static const bool is_tagged = std::is_base_of::value || std::is_base_of::value; }; template struct is_valid_type_tag> { static const bool value = is_valid_type_tag::value && is_valid_type_tag::value; static const bool is_tagged = false; }; template struct UnionT; template struct is_valid_type_tag> { static const bool is_tagged = is_valid_type_tag::is_tagged && is_valid_type_tag::is_tagged; static const bool value = is_tagged; }; template struct UnionT { static constexpr MachineType kMachineType = CommonMachineType(MachineTypeOf::value, MachineTypeOf::value); static const MachineRepresentation kMachineRepresentation = kMachineType.representation(); static_assert(kMachineRepresentation != MachineRepresentation::kNone, "no common representation"); static_assert(is_valid_type_tag::is_tagged && is_valid_type_tag::is_tagged, "union types are only possible for tagged values"); }; using Number = UnionT; using Numeric = UnionT; #define ENUM_ELEMENT(Name) k##Name, #define ENUM_STRUCT_ELEMENT(NAME, Name, name) k##Name, enum class ObjectType { kObject, OBJECT_TYPE_LIST(ENUM_ELEMENT) HEAP_OBJECT_TYPE_LIST(ENUM_ELEMENT) STRUCT_LIST(ENUM_STRUCT_ELEMENT) }; #undef ENUM_ELEMENT #undef ENUM_STRUCT_ELEMENT class AccessCheckNeeded; class BigIntWrapper; class ClassBoilerplate; class BooleanWrapper; class CompilationCacheTable; class Constructor; class Filler; class InternalizedString; class JSArgumentsObject; class JSContextExtensionObject; class JSError; class JSSloppyArgumentsObject; class MapCache; class MutableHeapNumber; class NativeContext; class NumberWrapper; class ScriptWrapper; class SloppyArgumentsElements; class StringWrapper; class SymbolWrapper; class Undetectable; class UniqueName; class WasmExportedFunctionData; class WasmGlobalObject; class WasmMemoryObject; class WasmModuleObject; class WasmTableObject; template struct ObjectTypeOf {}; #define OBJECT_TYPE_CASE(Name) \ template <> \ struct ObjectTypeOf { \ static const ObjectType value = ObjectType::k##Name; \ }; #define OBJECT_TYPE_STRUCT_CASE(NAME, Name, name) \ template <> \ struct ObjectTypeOf { \ static const ObjectType value = ObjectType::k##Name; \ }; #define OBJECT_TYPE_TEMPLATE_CASE(Name) \ template \ struct ObjectTypeOf> { \ static const ObjectType value = ObjectType::k##Name; \ }; OBJECT_TYPE_CASE(Object) OBJECT_TYPE_LIST(OBJECT_TYPE_CASE) HEAP_OBJECT_ORDINARY_TYPE_LIST(OBJECT_TYPE_CASE) STRUCT_LIST(OBJECT_TYPE_STRUCT_CASE) HEAP_OBJECT_TEMPLATE_TYPE_LIST(OBJECT_TYPE_TEMPLATE_CASE) #undef OBJECT_TYPE_CASE #undef OBJECT_TYPE_STRUCT_CASE #undef OBJECT_TYPE_TEMPLATE_CASE Smi* CheckObjectType(Object* value, Smi* type, String* location); namespace compiler { class CallDescriptor; class CodeAssemblerLabel; class CodeAssemblerVariable; template class TypedCodeAssemblerVariable; class CodeAssemblerState; class Node; class RawMachineAssembler; class RawMachineLabel; typedef ZoneVector CodeAssemblerVariableList; typedef std::function CodeAssemblerCallback; template struct is_subtype { static const bool value = std::is_base_of::value; }; template struct is_subtype, U> { static const bool value = is_subtype::value && is_subtype::value; }; template struct is_subtype> { static const bool value = is_subtype::value || is_subtype::value; }; template struct is_subtype, UnionT> { static const bool value = (is_subtype::value || is_subtype::value) && (is_subtype::value || is_subtype::value); }; template struct types_have_common_values { static const bool value = is_subtype::value || is_subtype::value; }; template struct types_have_common_values { static const bool value = types_have_common_values::value; }; template struct types_have_common_values { static const bool value = types_have_common_values::value; }; template struct types_have_common_values { static const bool value = types_have_common_values::value; }; template struct types_have_common_values { static const bool value = types_have_common_values::value; }; template struct types_have_common_values { static const bool value = types_have_common_values::value; }; template struct types_have_common_values { static const bool value = types_have_common_values::value; }; template struct types_have_common_values, U> { static const bool value = types_have_common_values::value || types_have_common_values::value; }; template struct types_have_common_values> { static const bool value = types_have_common_values::value || types_have_common_values::value; }; template struct types_have_common_values, UnionT> { static const bool value = types_have_common_values::value || types_have_common_values::value || types_have_common_values::value || types_have_common_values::value; }; template struct types_have_common_values { static const bool value = types_have_common_values::value; }; template struct types_have_common_values { static const bool value = types_have_common_values::value; }; // TNode is an SSA value with the static type tag T, which is one of the // following: // - a subclass of internal::Object represents a tagged type // - a subclass of internal::UntaggedT represents an untagged type // - ExternalReference // - PairT for an operation returning two values, with types T1 // and T2 // - UnionT represents either a value of type T1 or of type T2. template class TNode { public: static_assert(is_valid_type_tag::value, "invalid type tag"); template ::value, int>::type = 0> TNode(const TNode& other) : node_(other) {} TNode() : node_(nullptr) {} TNode operator=(TNode other) { DCHECK_NOT_NULL(other.node_); node_ = other.node_; return *this; } operator compiler::Node*() const { return node_; } static TNode UncheckedCast(compiler::Node* node) { return TNode(node); } protected: explicit TNode(compiler::Node* node) : node_(node) {} private: compiler::Node* node_; }; // SloppyTNode is a variant of TNode and allows implicit casts from // Node*. It is intended for function arguments as long as some call sites // still use untyped Node* arguments. // TODO(tebbi): Delete this class once transition is finished. template class SloppyTNode : public TNode { public: SloppyTNode(compiler::Node* node) // NOLINT(runtime/explicit) : TNode(node) {} template ::value, int>::type = 0> SloppyTNode(const TNode& other) // NOLINT(runtime/explicit) : TNode(other) {} }; // This macro alias allows to use PairT as a macro argument. #define PAIR_TYPE(T1, T2) PairT #define CODE_ASSEMBLER_COMPARE_BINARY_OP_LIST(V) \ V(Float32Equal, BoolT, Float32T, Float32T) \ V(Float32LessThan, BoolT, Float32T, Float32T) \ V(Float32LessThanOrEqual, BoolT, Float32T, Float32T) \ V(Float32GreaterThan, BoolT, Float32T, Float32T) \ V(Float32GreaterThanOrEqual, BoolT, Float32T, Float32T) \ V(Float64Equal, BoolT, Float64T, Float64T) \ V(Float64NotEqual, BoolT, Float64T, Float64T) \ V(Float64LessThan, BoolT, Float64T, Float64T) \ V(Float64LessThanOrEqual, BoolT, Float64T, Float64T) \ V(Float64GreaterThan, BoolT, Float64T, Float64T) \ V(Float64GreaterThanOrEqual, BoolT, Float64T, Float64T) \ /* Use Word32Equal if you need Int32Equal */ \ V(Int32GreaterThan, BoolT, Word32T, Word32T) \ V(Int32GreaterThanOrEqual, BoolT, Word32T, Word32T) \ V(Int32LessThan, BoolT, Word32T, Word32T) \ V(Int32LessThanOrEqual, BoolT, Word32T, Word32T) \ /* Use WordEqual if you need IntPtrEqual */ \ V(IntPtrLessThan, BoolT, WordT, WordT) \ V(IntPtrLessThanOrEqual, BoolT, WordT, WordT) \ V(IntPtrGreaterThan, BoolT, WordT, WordT) \ V(IntPtrGreaterThanOrEqual, BoolT, WordT, WordT) \ /* Use Word32Equal if you need Uint32Equal */ \ V(Uint32LessThan, BoolT, Word32T, Word32T) \ V(Uint32LessThanOrEqual, BoolT, Word32T, Word32T) \ V(Uint32GreaterThan, BoolT, Word32T, Word32T) \ V(Uint32GreaterThanOrEqual, BoolT, Word32T, Word32T) \ /* Use WordEqual if you need UintPtrEqual */ \ V(UintPtrLessThan, BoolT, WordT, WordT) \ V(UintPtrLessThanOrEqual, BoolT, WordT, WordT) \ V(UintPtrGreaterThan, BoolT, WordT, WordT) \ V(UintPtrGreaterThanOrEqual, BoolT, WordT, WordT) #define CODE_ASSEMBLER_BINARY_OP_LIST(V) \ CODE_ASSEMBLER_COMPARE_BINARY_OP_LIST(V) \ V(Float64Add, Float64T, Float64T, Float64T) \ V(Float64Sub, Float64T, Float64T, Float64T) \ V(Float64Mul, Float64T, Float64T, Float64T) \ V(Float64Div, Float64T, Float64T, Float64T) \ V(Float64Mod, Float64T, Float64T, Float64T) \ V(Float64Atan2, Float64T, Float64T, Float64T) \ V(Float64Pow, Float64T, Float64T, Float64T) \ V(Float64Max, Float64T, Float64T, Float64T) \ V(Float64Min, Float64T, Float64T, Float64T) \ V(Float64InsertLowWord32, Float64T, Float64T, Word32T) \ V(Float64InsertHighWord32, Float64T, Float64T, Word32T) \ V(IntPtrAddWithOverflow, PAIR_TYPE(IntPtrT, BoolT), IntPtrT, IntPtrT) \ V(IntPtrSubWithOverflow, PAIR_TYPE(IntPtrT, BoolT), IntPtrT, IntPtrT) \ V(Int32Add, Word32T, Word32T, Word32T) \ V(Int32AddWithOverflow, PAIR_TYPE(Int32T, BoolT), Int32T, Int32T) \ V(Int32Sub, Word32T, Word32T, Word32T) \ V(Int32SubWithOverflow, PAIR_TYPE(Int32T, BoolT), Int32T, Int32T) \ V(Int32Mul, Word32T, Word32T, Word32T) \ V(Int32MulWithOverflow, PAIR_TYPE(Int32T, BoolT), Int32T, Int32T) \ V(Int32Div, Int32T, Int32T, Int32T) \ V(Int32Mod, Int32T, Int32T, Int32T) \ V(WordRor, WordT, WordT, IntegralT) \ V(Word32Ror, Word32T, Word32T, Word32T) \ V(Word64Ror, Word64T, Word64T, Word64T) TNode Float64Add(TNode a, TNode b); #define CODE_ASSEMBLER_UNARY_OP_LIST(V) \ V(Float64Abs, Float64T, Float64T) \ V(Float64Acos, Float64T, Float64T) \ V(Float64Acosh, Float64T, Float64T) \ V(Float64Asin, Float64T, Float64T) \ V(Float64Asinh, Float64T, Float64T) \ V(Float64Atan, Float64T, Float64T) \ V(Float64Atanh, Float64T, Float64T) \ V(Float64Cos, Float64T, Float64T) \ V(Float64Cosh, Float64T, Float64T) \ V(Float64Exp, Float64T, Float64T) \ V(Float64Expm1, Float64T, Float64T) \ V(Float64Log, Float64T, Float64T) \ V(Float64Log1p, Float64T, Float64T) \ V(Float64Log2, Float64T, Float64T) \ V(Float64Log10, Float64T, Float64T) \ V(Float64Cbrt, Float64T, Float64T) \ V(Float64Neg, Float64T, Float64T) \ V(Float64Sin, Float64T, Float64T) \ V(Float64Sinh, Float64T, Float64T) \ V(Float64Sqrt, Float64T, Float64T) \ V(Float64Tan, Float64T, Float64T) \ V(Float64Tanh, Float64T, Float64T) \ V(Float64ExtractLowWord32, Word32T, Float64T) \ V(Float64ExtractHighWord32, Word32T, Float64T) \ V(BitcastTaggedToWord, IntPtrT, Object) \ V(BitcastMaybeObjectToWord, IntPtrT, MaybeObject) \ V(BitcastWordToTagged, Object, WordT) \ V(BitcastWordToTaggedSigned, Smi, WordT) \ V(TruncateFloat64ToFloat32, Float32T, Float64T) \ V(TruncateFloat64ToWord32, Word32T, Float64T) \ V(TruncateInt64ToInt32, Int32T, Int64T) \ V(ChangeFloat32ToFloat64, Float64T, Float32T) \ V(ChangeFloat64ToUint32, Uint32T, Float64T) \ V(ChangeFloat64ToUint64, Uint64T, Float64T) \ V(ChangeInt32ToFloat64, Float64T, Int32T) \ V(ChangeInt32ToInt64, Int64T, Int32T) \ V(ChangeUint32ToFloat64, Float64T, Word32T) \ V(ChangeUint32ToUint64, Uint64T, Word32T) \ V(BitcastInt32ToFloat32, Float32T, Word32T) \ V(BitcastFloat32ToInt32, Word32T, Float32T) \ V(RoundFloat64ToInt32, Int32T, Float64T) \ V(RoundInt32ToFloat32, Int32T, Float32T) \ V(Float64SilenceNaN, Float64T, Float64T) \ V(Float64RoundDown, Float64T, Float64T) \ V(Float64RoundUp, Float64T, Float64T) \ V(Float64RoundTiesEven, Float64T, Float64T) \ V(Float64RoundTruncate, Float64T, Float64T) \ V(Word32Clz, Int32T, Word32T) \ V(Word32BitwiseNot, Word32T, Word32T) \ V(WordNot, WordT, WordT) \ V(Int32AbsWithOverflow, PAIR_TYPE(Int32T, BoolT), Int32T) \ V(Int64AbsWithOverflow, PAIR_TYPE(Int64T, BoolT), Int64T) \ V(IntPtrAbsWithOverflow, PAIR_TYPE(IntPtrT, BoolT), IntPtrT) \ V(Word32BinaryNot, BoolT, Word32T) // A "public" interface used by components outside of compiler directory to // create code objects with TurboFan's backend. This class is mostly a thin // shim around the RawMachineAssembler, and its primary job is to ensure that // the innards of the RawMachineAssembler and other compiler implementation // details don't leak outside of the the compiler directory.. // // V8 components that need to generate low-level code using this interface // should include this header--and this header only--from the compiler // directory (this is actually enforced). Since all interesting data // structures are forward declared, it's not possible for clients to peek // inside the compiler internals. // // In addition to providing isolation between TurboFan and code generation // clients, CodeAssembler also provides an abstraction for creating variables // and enhanced Label functionality to merge variable values along paths where // they have differing values, including loops. // // The CodeAssembler itself is stateless (and instances are expected to be // temporary-scoped and short-lived); all its state is encapsulated into // a CodeAssemblerState instance. class V8_EXPORT_PRIVATE CodeAssembler { public: explicit CodeAssembler(CodeAssemblerState* state) : state_(state) {} ~CodeAssembler(); static Handle GenerateCode(CodeAssemblerState* state, const AssemblerOptions& options); bool Is64() const; bool IsFloat64RoundUpSupported() const; bool IsFloat64RoundDownSupported() const; bool IsFloat64RoundTiesEvenSupported() const; bool IsFloat64RoundTruncateSupported() const; bool IsInt32AbsWithOverflowSupported() const; bool IsInt64AbsWithOverflowSupported() const; bool IsIntPtrAbsWithOverflowSupported() const; // Shortened aliases for use in CodeAssembler subclasses. using Label = CodeAssemblerLabel; using Variable = CodeAssemblerVariable; template using TVariable = TypedCodeAssemblerVariable; using VariableList = CodeAssemblerVariableList; // =========================================================================== // Base Assembler // =========================================================================== template class CheckedNode { public: #ifdef DEBUG CheckedNode(Node* node, CodeAssembler* code_assembler, const char* location) : node_(node), code_assembler_(code_assembler), location_(location) {} #else CheckedNode(compiler::Node* node, CodeAssembler*, const char*) : node_(node) {} #endif template operator TNode() { static_assert( !std::is_same::value, "Can't cast to MaybeObject, use explicit conversion functions. "); static_assert(types_have_common_values::value, "Incompatible types: this cast can never succeed."); static_assert(std::is_convertible, TNode>::value, "Coercion to untagged values cannot be " "checked."); static_assert( !FromTyped || !std::is_convertible, TNode>::value, "Unnecessary CAST: types are convertible."); #ifdef DEBUG if (FLAG_debug_code) { if (std::is_same::value) { code_assembler_->GenerateCheckMaybeObjectIsObject(node_, location_); } Node* function = code_assembler_->ExternalConstant( ExternalReference::check_object_type()); code_assembler_->CallCFunction3( MachineType::AnyTagged(), MachineType::AnyTagged(), MachineType::TaggedSigned(), MachineType::AnyTagged(), function, node_, code_assembler_->SmiConstant( static_cast(ObjectTypeOf::value)), code_assembler_->StringConstant(location_)); } #endif return TNode::UncheckedCast(node_); } template operator SloppyTNode() { return implicit_cast>(*this); } Node* node() const { return node_; } private: Node* node_; #ifdef DEBUG CodeAssembler* code_assembler_; const char* location_; #endif }; template TNode UncheckedCast(Node* value) { return TNode::UncheckedCast(value); } template TNode UncheckedCast(TNode value) { static_assert(types_have_common_values::value, "Incompatible types: this cast can never succeed."); return TNode::UncheckedCast(value); } // ReinterpretCast(v) has the power to cast even when the type of v is // unrelated to T. Use with care. template TNode ReinterpretCast(Node* value) { return TNode::UncheckedCast(value); } CheckedNode Cast(Node* value, const char* location = "") { return {value, this, location}; } template CheckedNode Cast(TNode value, const char* location = "") { return {value, this, location}; } #ifdef DEBUG #define STRINGIFY(x) #x #define TO_STRING_LITERAL(x) STRINGIFY(x) #define CAST(x) \ Cast(x, "CAST(" #x ") at " __FILE__ ":" TO_STRING_LITERAL(__LINE__)) #else #define CAST(x) Cast(x) #endif #ifdef DEBUG void GenerateCheckMaybeObjectIsObject(Node* node, const char* location); #endif // Constants. TNode Int32Constant(int32_t value); TNode Int64Constant(int64_t value); TNode IntPtrConstant(intptr_t value); TNode NumberConstant(double value); TNode SmiConstant(Smi* value); TNode SmiConstant(int value); template ::value>::type> TNode SmiConstant(E value) { STATIC_ASSERT(sizeof(E) <= sizeof(int)); return SmiConstant(static_cast(value)); } TNode UntypedHeapConstant(Handle object); template TNode HeapConstant(Handle object) { return UncheckedCast(UntypedHeapConstant(object)); } TNode StringConstant(const char* str); TNode BooleanConstant(bool value); TNode ExternalConstant(ExternalReference address); TNode Float64Constant(double value); TNode NaNConstant(); TNode Int32TrueConstant() { return ReinterpretCast(Int32Constant(1)); } TNode Int32FalseConstant() { return ReinterpretCast(Int32Constant(0)); } TNode BoolConstant(bool value) { return value ? Int32TrueConstant() : Int32FalseConstant(); } bool ToInt32Constant(Node* node, int32_t& out_value); bool ToInt64Constant(Node* node, int64_t& out_value); bool ToSmiConstant(Node* node, Smi*& out_value); bool ToIntPtrConstant(Node* node, intptr_t& out_value); bool IsUndefinedConstant(TNode node); bool IsNullConstant(TNode node); TNode Signed(TNode x) { return UncheckedCast(x); } TNode Signed(TNode x) { return UncheckedCast(x); } TNode Unsigned(TNode x) { return UncheckedCast(x); } TNode Unsigned(TNode x) { return UncheckedCast(x); } static constexpr int kTargetParameterIndex = -1; Node* Parameter(int value); TNode GetJSContextParameter(); void Return(SloppyTNode value); void Return(SloppyTNode value1, SloppyTNode value2); void Return(SloppyTNode value1, SloppyTNode value2, SloppyTNode value3); void PopAndReturn(Node* pop, Node* value); void ReturnIf(Node* condition, Node* value); void ReturnRaw(Node* value); void DebugAbort(Node* message); void DebugBreak(); void Unreachable(); void Comment(const char* format, ...); void Bind(Label* label); #if DEBUG void Bind(Label* label, AssemblerDebugInfo debug_info); #endif // DEBUG void Goto(Label* label); void GotoIf(SloppyTNode condition, Label* true_label); void GotoIfNot(SloppyTNode condition, Label* false_label); void Branch(SloppyTNode condition, Label* true_label, Label* false_label); void Branch(TNode condition, std::function true_body, std::function false_body); void Branch(TNode condition, Label* true_label, std::function false_body); void Branch(TNode condition, std::function true_body, Label* false_label); void Switch(Node* index, Label* default_label, const int32_t* case_values, Label** case_labels, size_t case_count); // Access to the frame pointer Node* LoadFramePointer(); Node* LoadParentFramePointer(); // Access to the stack pointer Node* LoadStackPointer(); // Poison |value| on speculative paths. TNode TaggedPoisonOnSpeculation(SloppyTNode value); TNode WordPoisonOnSpeculation(SloppyTNode value); // Load raw memory location. Node* Load(MachineType rep, Node* base, LoadSensitivity needs_poisoning = LoadSensitivity::kSafe); template TNode Load(MachineType rep, TNode> base) { DCHECK( IsSubtype(rep.representation(), MachineRepresentationOf::value)); return UncheckedCast(Load(rep, static_cast(base))); } Node* Load(MachineType rep, Node* base, Node* offset, LoadSensitivity needs_poisoning = LoadSensitivity::kSafe); Node* AtomicLoad(MachineType rep, Node* base, Node* offset); // Load a value from the root array. TNode LoadRoot(Heap::RootListIndex root_index); // Store value to raw memory location. Node* Store(Node* base, Node* value); Node* Store(Node* base, Node* offset, Node* value); Node* StoreWithMapWriteBarrier(Node* base, Node* offset, Node* value); Node* StoreNoWriteBarrier(MachineRepresentation rep, Node* base, Node* value); Node* StoreNoWriteBarrier(MachineRepresentation rep, Node* base, Node* offset, Node* value); Node* AtomicStore(MachineRepresentation rep, Node* base, Node* offset, Node* value); // Exchange value at raw memory location Node* AtomicExchange(MachineType type, Node* base, Node* offset, Node* value); // Compare and Exchange value at raw memory location Node* AtomicCompareExchange(MachineType type, Node* base, Node* offset, Node* old_value, Node* new_value); Node* AtomicAdd(MachineType type, Node* base, Node* offset, Node* value); Node* AtomicSub(MachineType type, Node* base, Node* offset, Node* value); Node* AtomicAnd(MachineType type, Node* base, Node* offset, Node* value); Node* AtomicOr(MachineType type, Node* base, Node* offset, Node* value); Node* AtomicXor(MachineType type, Node* base, Node* offset, Node* value); // Store a value to the root array. Node* StoreRoot(Heap::RootListIndex root_index, Node* value); // Basic arithmetic operations. #define DECLARE_CODE_ASSEMBLER_BINARY_OP(name, ResType, Arg1Type, Arg2Type) \ TNode name(SloppyTNode a, SloppyTNode b); CODE_ASSEMBLER_BINARY_OP_LIST(DECLARE_CODE_ASSEMBLER_BINARY_OP) #undef DECLARE_CODE_ASSEMBLER_BINARY_OP TNode WordShr(TNode left, TNode right) { return UncheckedCast( WordShr(static_cast(left), static_cast(right))); } TNode WordAnd(TNode left, TNode right) { return UncheckedCast( WordAnd(static_cast(left), static_cast(right))); } template ::value && std::is_base_of::value>::type> TNode WordEqual(TNode left, TNode right) { return WordEqual(ReinterpretCast(left), ReinterpretCast(right)); } TNode WordEqual(TNode left, Node* right) { return WordEqual(ReinterpretCast(left), ReinterpretCast(right)); } TNode WordEqual(Node* left, TNode right) { return WordEqual(ReinterpretCast(left), ReinterpretCast(right)); } template ::value && std::is_base_of::value>::type> TNode WordNotEqual(TNode left, TNode right) { return WordNotEqual(ReinterpretCast(left), ReinterpretCast(right)); } TNode WordNotEqual(TNode left, Node* right) { return WordNotEqual(ReinterpretCast(left), ReinterpretCast(right)); } TNode WordNotEqual(Node* left, TNode right) { return WordNotEqual(ReinterpretCast(left), ReinterpretCast(right)); } TNode IntPtrEqual(SloppyTNode left, SloppyTNode right); TNode WordEqual(SloppyTNode left, SloppyTNode right); TNode WordNotEqual(SloppyTNode left, SloppyTNode right); TNode Word32Equal(SloppyTNode left, SloppyTNode right); TNode Word32NotEqual(SloppyTNode left, SloppyTNode right); TNode Word64Equal(SloppyTNode left, SloppyTNode right); TNode Word64NotEqual(SloppyTNode left, SloppyTNode right); TNode Int32Add(TNode left, TNode right) { return Signed( Int32Add(static_cast(left), static_cast(right))); } TNode IntPtrAdd(SloppyTNode left, SloppyTNode right); TNode IntPtrSub(SloppyTNode left, SloppyTNode right); TNode IntPtrMul(SloppyTNode left, SloppyTNode right); TNode IntPtrAdd(TNode left, TNode right) { return Signed( IntPtrAdd(static_cast(left), static_cast(right))); } TNode IntPtrSub(TNode left, TNode right) { return Signed( IntPtrSub(static_cast(left), static_cast(right))); } TNode IntPtrMul(TNode left, TNode right) { return Signed( IntPtrMul(static_cast(left), static_cast(right))); } TNode WordShl(SloppyTNode value, int shift); TNode WordShr(SloppyTNode value, int shift); TNode WordSar(SloppyTNode value, int shift); TNode WordShr(TNode value, int shift) { return UncheckedCast(WordShr(static_cast(value), shift)); } TNode Word32Shr(SloppyTNode value, int shift); TNode WordOr(SloppyTNode left, SloppyTNode right); TNode WordAnd(SloppyTNode left, SloppyTNode right); TNode WordXor(SloppyTNode left, SloppyTNode right); TNode WordShl(SloppyTNode left, SloppyTNode right); TNode WordShr(SloppyTNode left, SloppyTNode right); TNode WordSar(SloppyTNode left, SloppyTNode right); TNode Word32Or(SloppyTNode left, SloppyTNode right); TNode Word32And(SloppyTNode left, SloppyTNode right); TNode Word32Xor(SloppyTNode left, SloppyTNode right); TNode Word32Shl(SloppyTNode left, SloppyTNode right); TNode Word32Shr(SloppyTNode left, SloppyTNode right); TNode Word32Sar(SloppyTNode left, SloppyTNode right); TNode Word64Or(SloppyTNode left, SloppyTNode right); TNode Word64And(SloppyTNode left, SloppyTNode right); TNode Word64Xor(SloppyTNode left, SloppyTNode right); TNode Word64Shl(SloppyTNode left, SloppyTNode right); TNode Word64Shr(SloppyTNode left, SloppyTNode right); TNode Word64Sar(SloppyTNode left, SloppyTNode right); // Unary #define DECLARE_CODE_ASSEMBLER_UNARY_OP(name, ResType, ArgType) \ TNode name(SloppyTNode a); CODE_ASSEMBLER_UNARY_OP_LIST(DECLARE_CODE_ASSEMBLER_UNARY_OP) #undef DECLARE_CODE_ASSEMBLER_UNARY_OP // Changes a double to an inptr_t for pointer arithmetic outside of Smi range. // Assumes that the double can be exactly represented as an int. TNode ChangeFloat64ToUintPtr(SloppyTNode value); // Changes an intptr_t to a double, e.g. for storing an element index // outside Smi range in a HeapNumber. Lossless on 32-bit, // rounds on 64-bit (which doesn't affect valid element indices). Node* RoundIntPtrToFloat64(Node* value); // No-op on 32-bit, otherwise zero extend. TNode ChangeUint32ToWord(SloppyTNode value); // No-op on 32-bit, otherwise sign extend. TNode ChangeInt32ToIntPtr(SloppyTNode value); // No-op that guarantees that the value is kept alive till this point even // if GC happens. Node* Retain(Node* value); // Projections Node* Projection(int index, Node* value); template TNode>::type> Projection(TNode> value) { return UncheckedCast< typename std::tuple_element>::type>( Projection(index, value)); } // Calls template TNode CallRuntime(Runtime::FunctionId function, SloppyTNode context, TArgs... args) { return CallRuntimeImpl(function, context, {implicit_cast>(args)...}); } template TNode CallRuntimeWithCEntry(Runtime::FunctionId function, TNode centry, SloppyTNode context, TArgs... args) { return CallRuntimeWithCEntryImpl(function, centry, context, {args...}); } template void TailCallRuntime(Runtime::FunctionId function, SloppyTNode context, TArgs... args) { int argc = static_cast(sizeof...(args)); TNode arity = Int32Constant(argc); return TailCallRuntimeImpl(function, arity, context, {implicit_cast>(args)...}); } template void TailCallRuntime(Runtime::FunctionId function, TNode arity, SloppyTNode context, TArgs... args) { return TailCallRuntimeImpl(function, arity, context, {implicit_cast>(args)...}); } template void TailCallRuntimeWithCEntry(Runtime::FunctionId function, TNode centry, TNode context, TArgs... args) { int argc = sizeof...(args); TNode arity = Int32Constant(argc); return TailCallRuntimeWithCEntryImpl( function, arity, centry, context, {implicit_cast>(args)...}); } // // If context passed to CallStub is nullptr, it won't be passed to the stub. // template TNode CallStub(Callable const& callable, SloppyTNode context, TArgs... args) { TNode target = HeapConstant(callable.code()); return CallStub(callable.descriptor(), target, context, args...); } template TNode CallStub(const CallInterfaceDescriptor& descriptor, SloppyTNode target, SloppyTNode context, TArgs... args) { return UncheckedCast(CallStubR(descriptor, 1, target, context, args...)); } template Node* CallStubR(const CallInterfaceDescriptor& descriptor, size_t result_size, SloppyTNode target, SloppyTNode context, TArgs... args) { return CallStubRImpl(descriptor, result_size, target, context, {args...}); } Node* CallStubN(const CallInterfaceDescriptor& descriptor, size_t result_size, int input_count, Node* const* inputs); template void TailCallStub(Callable const& callable, SloppyTNode context, TArgs... args) { TNode target = HeapConstant(callable.code()); return TailCallStub(callable.descriptor(), target, context, args...); } template void TailCallStub(const CallInterfaceDescriptor& descriptor, SloppyTNode target, SloppyTNode context, TArgs... args) { return TailCallStubImpl(descriptor, target, context, {args...}); } template Node* TailCallBytecodeDispatch(const CallInterfaceDescriptor& descriptor, Node* target, TArgs... args); template Node* TailCallStubThenBytecodeDispatch( const CallInterfaceDescriptor& descriptor, Node* target, Node* context, TArgs... args) { return TailCallStubThenBytecodeDispatchImpl(descriptor, target, context, {args...}); } // Tailcalls to the given code object with JSCall linkage. The JS arguments // (including receiver) are supposed to be already on the stack. // This is a building block for implementing trampoline stubs that are // installed instead of code objects with JSCall linkage. // Note that no arguments adaption is going on here - all the JavaScript // arguments are left on the stack unmodified. Therefore, this tail call can // only be used after arguments adaptation has been performed already. TNode TailCallJSCode(TNode code, TNode context, TNode function, TNode new_target, TNode arg_count); template Node* CallJS(Callable const& callable, Node* context, Node* function, Node* receiver, TArgs... args) { int argc = static_cast(sizeof...(args)); Node* arity = Int32Constant(argc); return CallStub(callable, context, function, arity, receiver, args...); } template Node* ConstructJS(Callable const& callable, Node* context, Node* new_target, TArgs... args) { int argc = static_cast(sizeof...(args)); Node* arity = Int32Constant(argc); Node* receiver = LoadRoot(Heap::kUndefinedValueRootIndex); // Construct(target, new_target, arity, receiver, arguments...) return CallStub(callable, context, new_target, new_target, arity, receiver, args...); } Node* CallCFunctionN(Signature* signature, int input_count, Node* const* inputs); // Call to a C function with one argument. Node* CallCFunction1(MachineType return_type, MachineType arg0_type, Node* function, Node* arg0); // Call to a C function with one argument, while saving/restoring caller // registers except the register used for return value. Node* CallCFunction1WithCallerSavedRegisters(MachineType return_type, MachineType arg0_type, Node* function, Node* arg0, SaveFPRegsMode mode); // Call to a C function with two arguments. Node* CallCFunction2(MachineType return_type, MachineType arg0_type, MachineType arg1_type, Node* function, Node* arg0, Node* arg1); // Call to a C function with three arguments. Node* CallCFunction3(MachineType return_type, MachineType arg0_type, MachineType arg1_type, MachineType arg2_type, Node* function, Node* arg0, Node* arg1, Node* arg2); // Call to a C function with three arguments, while saving/restoring caller // registers except the register used for return value. Node* CallCFunction3WithCallerSavedRegisters( MachineType return_type, MachineType arg0_type, MachineType arg1_type, MachineType arg2_type, Node* function, Node* arg0, Node* arg1, Node* arg2, SaveFPRegsMode mode); // Call to a C function with four arguments. Node* CallCFunction4(MachineType return_type, MachineType arg0_type, MachineType arg1_type, MachineType arg2_type, MachineType arg3_type, Node* function, Node* arg0, Node* arg1, Node* arg2, Node* arg3); // Call to a C function with five arguments. Node* CallCFunction5(MachineType return_type, MachineType arg0_type, MachineType arg1_type, MachineType arg2_type, MachineType arg3_type, MachineType arg4_type, Node* function, Node* arg0, Node* arg1, Node* arg2, Node* arg3, Node* arg4); // Call to a C function with six arguments. Node* CallCFunction6(MachineType return_type, MachineType arg0_type, MachineType arg1_type, MachineType arg2_type, MachineType arg3_type, MachineType arg4_type, MachineType arg5_type, Node* function, Node* arg0, Node* arg1, Node* arg2, Node* arg3, Node* arg4, Node* arg5); // Call to a C function with nine arguments. Node* CallCFunction9(MachineType return_type, MachineType arg0_type, MachineType arg1_type, MachineType arg2_type, MachineType arg3_type, MachineType arg4_type, MachineType arg5_type, MachineType arg6_type, MachineType arg7_type, MachineType arg8_type, Node* function, Node* arg0, Node* arg1, Node* arg2, Node* arg3, Node* arg4, Node* arg5, Node* arg6, Node* arg7, Node* arg8); // Exception handling support. void GotoIfException(Node* node, Label* if_exception, Variable* exception_var = nullptr); // Helpers which delegate to RawMachineAssembler. Factory* factory() const; Isolate* isolate() const; Zone* zone() const; CodeAssemblerState* state() { return state_; } void BreakOnNode(int node_id); bool UnalignedLoadSupported(MachineRepresentation rep) const; bool UnalignedStoreSupported(MachineRepresentation rep) const; protected: void RegisterCallGenerationCallbacks( const CodeAssemblerCallback& call_prologue, const CodeAssemblerCallback& call_epilogue); void UnregisterCallGenerationCallbacks(); bool Word32ShiftIsSafe() const; PoisoningMitigationLevel poisoning_level() const; bool IsJSFunctionCall() const; private: TNode CallRuntimeImpl(Runtime::FunctionId function, TNode context, std::initializer_list> args); TNode CallRuntimeWithCEntryImpl( Runtime::FunctionId function, TNode centry, TNode context, std::initializer_list> args); void TailCallRuntimeImpl(Runtime::FunctionId function, TNode arity, TNode context, std::initializer_list> args); void TailCallRuntimeWithCEntryImpl(Runtime::FunctionId function, TNode arity, TNode centry, TNode context, std::initializer_list> args); void TailCallStubImpl(const CallInterfaceDescriptor& descriptor, TNode target, TNode context, std::initializer_list args); Node* TailCallStubThenBytecodeDispatchImpl( const CallInterfaceDescriptor& descriptor, Node* target, Node* context, std::initializer_list args); Node* CallStubRImpl(const CallInterfaceDescriptor& descriptor, size_t result_size, SloppyTNode target, SloppyTNode context, std::initializer_list args); // These two don't have definitions and are here only for catching use cases // where the cast is not necessary. TNode Signed(TNode x); TNode Unsigned(TNode x); RawMachineAssembler* raw_assembler() const; // Calls respective callback registered in the state. void CallPrologue(); void CallEpilogue(); CodeAssemblerState* state_; DISALLOW_COPY_AND_ASSIGN(CodeAssembler); }; class CodeAssemblerVariable { public: explicit CodeAssemblerVariable(CodeAssembler* assembler, MachineRepresentation rep); CodeAssemblerVariable(CodeAssembler* assembler, MachineRepresentation rep, Node* initial_value); #if DEBUG CodeAssemblerVariable(CodeAssembler* assembler, AssemblerDebugInfo debug_info, MachineRepresentation rep); CodeAssemblerVariable(CodeAssembler* assembler, AssemblerDebugInfo debug_info, MachineRepresentation rep, Node* initial_value); #endif // DEBUG ~CodeAssemblerVariable(); void Bind(Node* value); Node* value() const; MachineRepresentation rep() const; bool IsBound() const; private: class Impl; friend class CodeAssemblerLabel; friend class CodeAssemblerState; friend std::ostream& operator<<(std::ostream&, const Impl&); friend std::ostream& operator<<(std::ostream&, const CodeAssemblerVariable&); Impl* impl_; CodeAssemblerState* state_; DISALLOW_COPY_AND_ASSIGN(CodeAssemblerVariable); }; std::ostream& operator<<(std::ostream&, const CodeAssemblerVariable&); std::ostream& operator<<(std::ostream&, const CodeAssemblerVariable::Impl&); template class TypedCodeAssemblerVariable : public CodeAssemblerVariable { public: TypedCodeAssemblerVariable(TNode initial_value, CodeAssembler* assembler) : CodeAssemblerVariable(assembler, MachineRepresentationOf::value, initial_value) {} explicit TypedCodeAssemblerVariable(CodeAssembler* assembler) : CodeAssemblerVariable(assembler, MachineRepresentationOf::value) {} #if DEBUG TypedCodeAssemblerVariable(AssemblerDebugInfo debug_info, CodeAssembler* assembler) : CodeAssemblerVariable(assembler, debug_info, MachineRepresentationOf::value) {} TypedCodeAssemblerVariable(AssemblerDebugInfo debug_info, TNode initial_value, CodeAssembler* assembler) : CodeAssemblerVariable(assembler, debug_info, MachineRepresentationOf::value, initial_value) {} #endif // DEBUG TNode value() const { return TNode::UncheckedCast(CodeAssemblerVariable::value()); } void operator=(TNode value) { Bind(value); } void operator=(const TypedCodeAssemblerVariable& variable) { Bind(variable.value()); } private: using CodeAssemblerVariable::Bind; }; class CodeAssemblerLabel { public: enum Type { kDeferred, kNonDeferred }; explicit CodeAssemblerLabel( CodeAssembler* assembler, CodeAssemblerLabel::Type type = CodeAssemblerLabel::kNonDeferred) : CodeAssemblerLabel(assembler, 0, nullptr, type) {} CodeAssemblerLabel( CodeAssembler* assembler, const CodeAssemblerVariableList& merged_variables, CodeAssemblerLabel::Type type = CodeAssemblerLabel::kNonDeferred) : CodeAssemblerLabel(assembler, merged_variables.size(), &(merged_variables[0]), type) {} CodeAssemblerLabel( CodeAssembler* assembler, size_t count, CodeAssemblerVariable* const* vars, CodeAssemblerLabel::Type type = CodeAssemblerLabel::kNonDeferred); CodeAssemblerLabel( CodeAssembler* assembler, std::initializer_list vars, CodeAssemblerLabel::Type type = CodeAssemblerLabel::kNonDeferred) : CodeAssemblerLabel(assembler, vars.size(), vars.begin(), type) {} CodeAssemblerLabel( CodeAssembler* assembler, CodeAssemblerVariable* merged_variable, CodeAssemblerLabel::Type type = CodeAssemblerLabel::kNonDeferred) : CodeAssemblerLabel(assembler, 1, &merged_variable, type) {} ~CodeAssemblerLabel(); inline bool is_bound() const { return bound_; } inline bool is_used() const { return merge_count_ != 0; } private: friend class CodeAssembler; void Bind(); #if DEBUG void Bind(AssemblerDebugInfo debug_info); #endif // DEBUG void UpdateVariablesAfterBind(); void MergeVariables(); bool bound_; size_t merge_count_; CodeAssemblerState* state_; RawMachineLabel* label_; // Map of variables that need to be merged to their phi nodes (or placeholders // for those phis). std::map variable_phis_; // Map of variables to the list of value nodes that have been added from each // merge path in their order of merging. std::map> variable_merges_; }; class V8_EXPORT_PRIVATE CodeAssemblerState { public: // Create with CallStub linkage. // |result_size| specifies the number of results returned by the stub. // TODO(rmcilroy): move result_size to the CallInterfaceDescriptor. CodeAssemblerState(Isolate* isolate, Zone* zone, const CallInterfaceDescriptor& descriptor, Code::Kind kind, const char* name, PoisoningMitigationLevel poisoning_level, uint32_t stub_key = 0, int32_t builtin_index = Builtins::kNoBuiltinId); // Create with JSCall linkage. CodeAssemblerState(Isolate* isolate, Zone* zone, int parameter_count, Code::Kind kind, const char* name, PoisoningMitigationLevel poisoning_level, int32_t builtin_index = Builtins::kNoBuiltinId); ~CodeAssemblerState(); const char* name() const { return name_; } int parameter_count() const; #if DEBUG void PrintCurrentBlock(std::ostream& os); bool InsideBlock(); #endif // DEBUG void SetInitialDebugInformation(const char* msg, const char* file, int line); private: friend class CodeAssembler; friend class CodeAssemblerLabel; friend class CodeAssemblerVariable; friend class CodeAssemblerTester; CodeAssemblerState(Isolate* isolate, Zone* zone, CallDescriptor* call_descriptor, Code::Kind kind, const char* name, PoisoningMitigationLevel poisoning_level, uint32_t stub_key, int32_t builtin_index); std::unique_ptr raw_assembler_; Code::Kind kind_; const char* name_; uint32_t stub_key_; int32_t builtin_index_; bool code_generated_; ZoneSet variables_; CodeAssemblerCallback call_prologue_; CodeAssemblerCallback call_epilogue_; DISALLOW_COPY_AND_ASSIGN(CodeAssemblerState); }; } // namespace compiler } // namespace internal } // namespace v8 #endif // V8_COMPILER_CODE_ASSEMBLER_H_