// 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 <initializer_list> #include <map> #include <memory> // Clients of this interface shouldn't depend on lots of compiler internals. // Do not include anything from src/compiler here! #include "src/base/macros.h" #include "src/base/type-traits.h" #include "src/builtins/builtins.h" #include "src/codegen/code-factory.h" #include "src/codegen/machine-type.h" #include "src/codegen/source-position.h" #include "src/codegen/tnode.h" #include "src/heap/heap.h" #include "src/objects/arguments.h" #include "src/objects/data-handler.h" #include "src/objects/heap-number.h" #include "src/objects/js-array-buffer.h" #include "src/objects/js-collection.h" #include "src/objects/js-proxy.h" #include "src/objects/map.h" #include "src/objects/maybe-object.h" #include "src/objects/objects.h" #include "src/objects/oddball.h" #include "src/objects/smi.h" #include "src/objects/tagged-index.h" #include "src/runtime/runtime.h" #include "src/utils/allocation.h" #include "src/zone/zone-containers.h" namespace v8 { namespace internal { // Forward declarations. class AsmWasmData; class AsyncGeneratorRequest; struct AssemblerOptions; class BigInt; class CallInterfaceDescriptor; class Callable; class Factory; class InterpreterData; class Isolate; class JSAsyncFunctionObject; class JSAsyncGeneratorObject; class JSCollator; class JSCollection; class JSDateTimeFormat; class JSDisplayNames; class JSListFormat; class JSLocale; class JSNumberFormat; class JSPluralRules; class JSRegExpStringIterator; class JSRelativeTimeFormat; class JSSegmentIterator; class JSSegmenter; class JSSegments; class JSV8BreakIterator; class JSWeakCollection; class JSFinalizationRegistry; class JSWeakMap; class JSWeakRef; class JSWeakSet; class ProfileDataFromFile; class PromiseCapability; class PromiseFulfillReactionJobTask; class PromiseReaction; class PromiseReactionJobTask; class PromiseRejectReactionJobTask; class Zone; #define MAKE_FORWARD_DECLARATION(Name) class Name; TORQUE_DEFINED_CLASS_LIST(MAKE_FORWARD_DECLARATION) #undef MAKE_FORWARD_DECLARATION template <typename T> class Signature; #define ENUM_ELEMENT(Name) k##Name, #define ENUM_STRUCT_ELEMENT(NAME, Name, name) k##Name, enum class ObjectType { ENUM_ELEMENT(Object) // ENUM_ELEMENT(Smi) // ENUM_ELEMENT(TaggedIndex) // ENUM_ELEMENT(HeapObject) // OBJECT_TYPE_LIST(ENUM_ELEMENT) // HEAP_OBJECT_TYPE_LIST(ENUM_ELEMENT) // STRUCT_LIST(ENUM_STRUCT_ELEMENT) // }; #undef ENUM_ELEMENT #undef ENUM_STRUCT_ELEMENT enum class CheckBounds { kAlways, kDebugOnly }; inline bool NeedsBoundsCheck(CheckBounds check_bounds) { switch (check_bounds) { case CheckBounds::kAlways: return true; case CheckBounds::kDebugOnly: return DEBUG_BOOL; } } enum class StoreToObjectWriteBarrier { kNone, kMap, kFull }; class AccessCheckNeeded; class BigIntBase; class BigIntWrapper; class ClassBoilerplate; class BooleanWrapper; class CompilationCacheTable; class Constructor; class Filler; class FunctionTemplateRareData; class HeapNumber; class InternalizedString; class JSArgumentsObject; class JSArrayBufferView; class JSContextExtensionObject; class JSError; class JSSloppyArgumentsObject; class MapCache; class NativeContext; class NumberWrapper; class ScriptWrapper; class SloppyArgumentsElements; class StringWrapper; class SymbolWrapper; class Undetectable; class UniqueName; class WasmCapiFunctionData; class WasmExceptionObject; class WasmExceptionPackage; class WasmExceptionTag; class WasmExportedFunctionData; class WasmGlobalObject; class WasmIndirectFunctionTable; class WasmJSFunctionData; class WasmMemoryObject; class WasmModuleObject; class WasmTableObject; template <class T> struct ObjectTypeOf {}; #define OBJECT_TYPE_CASE(Name) \ template <> \ struct ObjectTypeOf<Name> { \ static const ObjectType value = ObjectType::k##Name; \ }; #define OBJECT_TYPE_STRUCT_CASE(NAME, Name, name) \ template <> \ struct ObjectTypeOf<Name> { \ static const ObjectType value = ObjectType::k##Name; \ }; #define OBJECT_TYPE_TEMPLATE_CASE(Name) \ template <class... Args> \ struct ObjectTypeOf<Name<Args...>> { \ static const ObjectType value = ObjectType::k##Name; \ }; OBJECT_TYPE_CASE(Object) OBJECT_TYPE_CASE(Smi) OBJECT_TYPE_CASE(TaggedIndex) OBJECT_TYPE_CASE(HeapObject) 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 // {raw_value} must be a tagged Object. // {raw_type} must be a tagged Smi. // {raw_location} must be a tagged String. // Returns a tagged Smi. Address CheckObjectType(Address raw_value, Address raw_type, Address raw_location); namespace compiler { class CallDescriptor; class CodeAssemblerLabel; class CodeAssemblerVariable; template <class T> class TypedCodeAssemblerVariable; class CodeAssemblerState; class JSGraph; class Node; class RawMachineAssembler; class RawMachineLabel; class SourcePositionTable; using CodeAssemblerVariableList = ZoneVector<CodeAssemblerVariable*>; using CodeAssemblerCallback = std::function<void()>; template <class... Types> class CodeAssemblerParameterizedLabel; // This macro alias allows to use PairT<T1, T2> as a macro argument. #define PAIR_TYPE(T1, T2) PairT<T1, T2> #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(IntPtrAdd, WordT, WordT, WordT) \ V(IntPtrSub, WordT, WordT, WordT) \ V(IntPtrMul, WordT, WordT, WordT) \ V(IntPtrDiv, IntPtrT, IntPtrT, IntPtrT) \ 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(WordOr, WordT, WordT, WordT) \ V(WordAnd, WordT, WordT, WordT) \ V(WordXor, WordT, WordT, WordT) \ V(WordRor, WordT, WordT, IntegralT) \ V(WordShl, WordT, WordT, IntegralT) \ V(WordShr, WordT, WordT, IntegralT) \ V(WordSar, WordT, WordT, IntegralT) \ V(WordSarShiftOutZeros, WordT, WordT, IntegralT) \ V(Word32Or, Word32T, Word32T, Word32T) \ V(Word32And, Word32T, Word32T, Word32T) \ V(Word32Xor, Word32T, Word32T, Word32T) \ V(Word32Ror, Word32T, Word32T, Word32T) \ V(Word32Shl, Word32T, Word32T, Word32T) \ V(Word32Shr, Word32T, Word32T, Word32T) \ V(Word32Sar, Word32T, Word32T, Word32T) \ V(Word32SarShiftOutZeros, Word32T, Word32T, Word32T) \ V(Word64And, Word64T, Word64T, Word64T) \ V(Word64Or, Word64T, Word64T, Word64T) \ V(Word64Xor, Word64T, Word64T, Word64T) \ V(Word64Ror, Word64T, Word64T, Word64T) \ V(Word64Shl, Word64T, Word64T, Word64T) \ V(Word64Shr, Word64T, Word64T, Word64T) \ V(Word64Sar, Word64T, Word64T, Word64T) TNode<Float64T> Float64Add(TNode<Float64T> a, TNode<Float64T> 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, Uint32T, Float64T) \ V(Float64ExtractHighWord32, Uint32T, Float64T) \ V(BitcastTaggedToWord, IntPtrT, Object) \ V(BitcastTaggedToWordForTagAndSmiBits, IntPtrT, AnyTaggedT) \ V(BitcastMaybeObjectToWord, IntPtrT, MaybeObject) \ V(BitcastWordToTagged, Object, WordT) \ V(BitcastWordToTaggedSigned, Smi, WordT) \ V(TruncateFloat64ToFloat32, Float32T, Float64T) \ V(TruncateFloat64ToWord32, Uint32T, 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, Uint32T, Float32T) \ V(RoundFloat64ToInt32, Int32T, Float64T) \ V(RoundInt32ToFloat32, Float32T, Int32T) \ 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) \ V(StackPointerGreaterThan, BoolT, WordT) // 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<Code> GenerateCode(CodeAssemblerState* state, const AssemblerOptions& options, const ProfileDataFromFile* profile_data); bool Is64() const; bool Is32() 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; template <class T> using TVariable = TypedCodeAssemblerVariable<T>; using VariableList = CodeAssemblerVariableList; // =========================================================================== // Base Assembler // =========================================================================== template <class PreviousType, bool FromTyped> 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 <class A> operator TNode<A>() { static_assert( !std::is_same<A, MaybeObject>::value, "Can't cast to MaybeObject, use explicit conversion functions. "); static_assert(types_have_common_values<A, PreviousType>::value, "Incompatible types: this cast can never succeed."); static_assert(std::is_convertible<TNode<A>, TNode<Object>>::value, "Coercion to untagged values cannot be " "checked."); static_assert( !FromTyped || !std::is_convertible<TNode<PreviousType>, TNode<A>>::value, "Unnecessary CAST: types are convertible."); #ifdef DEBUG if (FLAG_debug_code) { if (std::is_same<PreviousType, MaybeObject>::value) { code_assembler_->GenerateCheckMaybeObjectIsObject(node_, location_); } TNode<ExternalReference> function = code_assembler_->ExternalConstant( ExternalReference::check_object_type()); code_assembler_->CallCFunction( function, MachineType::AnyTagged(), std::make_pair(MachineType::AnyTagged(), node_), std::make_pair(MachineType::TaggedSigned(), code_assembler_->SmiConstant( static_cast<int>(ObjectTypeOf<A>::value))), std::make_pair(MachineType::AnyTagged(), code_assembler_->StringConstant(location_))); } #endif return TNode<A>::UncheckedCast(node_); } template <class A> operator SloppyTNode<A>() { return implicit_cast<TNode<A>>(*this); } Node* node() const { return node_; } private: Node* node_; #ifdef DEBUG CodeAssembler* code_assembler_; const char* location_; #endif }; template <class T> TNode<T> UncheckedCast(Node* value) { return TNode<T>::UncheckedCast(value); } template <class T, class U> TNode<T> UncheckedCast(TNode<U> value) { static_assert(types_have_common_values<T, U>::value, "Incompatible types: this cast can never succeed."); return TNode<T>::UncheckedCast(value); } // ReinterpretCast<T>(v) has the power to cast even when the type of v is // unrelated to T. Use with care. template <class T> TNode<T> ReinterpretCast(Node* value) { return TNode<T>::UncheckedCast(value); } CheckedNode<Object, false> Cast(Node* value, const char* location = "") { return {value, this, location}; } template <class T> CheckedNode<T, true> Cast(TNode<T> 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__)) #define TORQUE_CAST(x) \ ca_.Cast(x, "CAST(" #x ") at " __FILE__ ":" TO_STRING_LITERAL(__LINE__)) #else #define CAST(x) Cast(x) #define TORQUE_CAST(x) ca_.Cast(x) #endif #ifdef DEBUG void GenerateCheckMaybeObjectIsObject(Node* node, const char* location); #endif // Constants. TNode<Int32T> Int32Constant(int32_t value); TNode<Int64T> Int64Constant(int64_t value); TNode<IntPtrT> IntPtrConstant(intptr_t value); TNode<Uint32T> Uint32Constant(uint32_t value) { return Unsigned(Int32Constant(bit_cast<int32_t>(value))); } TNode<UintPtrT> UintPtrConstant(uintptr_t value) { return Unsigned(IntPtrConstant(bit_cast<intptr_t>(value))); } TNode<TaggedIndex> TaggedIndexConstant(intptr_t value); TNode<RawPtrT> PointerConstant(void* value) { return ReinterpretCast<RawPtrT>(IntPtrConstant(bit_cast<intptr_t>(value))); } TNode<Number> NumberConstant(double value); TNode<Smi> SmiConstant(Smi value); TNode<Smi> SmiConstant(int value); template <typename E, typename = typename std::enable_if<std::is_enum<E>::value>::type> TNode<Smi> SmiConstant(E value) { STATIC_ASSERT(sizeof(E) <= sizeof(int)); return SmiConstant(static_cast<int>(value)); } TNode<HeapObject> UntypedHeapConstant(Handle<HeapObject> object); template <class Type> TNode<Type> HeapConstant(Handle<Type> object) { return UncheckedCast<Type>(UntypedHeapConstant(object)); } TNode<String> StringConstant(const char* str); TNode<Oddball> BooleanConstant(bool value); TNode<ExternalReference> ExternalConstant(ExternalReference address); TNode<Float32T> Float32Constant(double value); TNode<Float64T> Float64Constant(double value); TNode<BoolT> Int32TrueConstant() { return ReinterpretCast<BoolT>(Int32Constant(1)); } TNode<BoolT> Int32FalseConstant() { return ReinterpretCast<BoolT>(Int32Constant(0)); } TNode<BoolT> BoolConstant(bool value) { return value ? Int32TrueConstant() : Int32FalseConstant(); } bool ToInt32Constant(Node* node, int32_t* out_value); bool ToInt64Constant(Node* node, int64_t* out_value); bool ToIntPtrConstant(Node* node, intptr_t* out_value); bool ToSmiConstant(Node* node, Smi* out_value); bool IsUndefinedConstant(TNode<Object> node); bool IsNullConstant(TNode<Object> node); TNode<Int32T> Signed(TNode<Word32T> x) { return UncheckedCast<Int32T>(x); } TNode<IntPtrT> Signed(TNode<WordT> x) { return UncheckedCast<IntPtrT>(x); } TNode<Uint32T> Unsigned(TNode<Word32T> x) { return UncheckedCast<Uint32T>(x); } TNode<UintPtrT> Unsigned(TNode<WordT> x) { return UncheckedCast<UintPtrT>(x); } static constexpr int kTargetParameterIndex = -1; Node* Parameter(int value); TNode<Context> GetJSContextParameter(); void Return(TNode<Object> value); void Return(TNode<Object> value1, TNode<Object> value2); void Return(TNode<Object> value1, TNode<Object> value2, TNode<Object> value3); void Return(TNode<Int32T> value); void Return(TNode<Uint32T> value); void Return(TNode<WordT> value); void Return(TNode<Float32T> value); void Return(TNode<Float64T> value); void Return(TNode<WordT> value1, TNode<WordT> value2); void PopAndReturn(Node* pop, Node* value); void ReturnIf(TNode<BoolT> condition, TNode<Object> value); void AbortCSAAssert(Node* message); void DebugBreak(); void Unreachable(); void Comment(const char* msg) { if (!FLAG_code_comments) return; Comment(std::string(msg)); } void Comment(std::string msg); template <class... Args> void Comment(Args&&... args) { if (!FLAG_code_comments) return; std::ostringstream s; USE((s << std::forward<Args>(args))...); Comment(s.str()); } void StaticAssert(TNode<BoolT> value, const char* source = "unknown position"); // The following methods refer to source positions in CSA or Torque code // compiled during mksnapshot, not JS compiled at runtime. void SetSourcePosition(const char* file, int line); void PushSourcePosition(); void PopSourcePosition(); class SourcePositionScope { public: explicit SourcePositionScope(CodeAssembler* ca) : ca_(ca) { ca->PushSourcePosition(); } ~SourcePositionScope() { ca_->PopSourcePosition(); } private: CodeAssembler* ca_; }; const std::vector<FileAndLine>& GetMacroSourcePositionStack() const; void Bind(Label* label); #if DEBUG void Bind(Label* label, AssemblerDebugInfo debug_info); #endif // DEBUG void Goto(Label* label); void GotoIf(TNode<IntegralT> condition, Label* true_label); void GotoIfNot(TNode<IntegralT> condition, Label* false_label); void Branch(TNode<IntegralT> condition, Label* true_label, Label* false_label); template <class T> TNode<T> Uninitialized() { return {}; } template <class... T> void Bind(CodeAssemblerParameterizedLabel<T...>* label, TNode<T>*... phis) { Bind(label->plain_label()); label->CreatePhis(phis...); } template <class... T, class... Args> void Branch(TNode<BoolT> condition, CodeAssemblerParameterizedLabel<T...>* if_true, CodeAssemblerParameterizedLabel<T...>* if_false, Args... args) { if_true->AddInputs(args...); if_false->AddInputs(args...); Branch(condition, if_true->plain_label(), if_false->plain_label()); } template <class... T, class... U> void Branch(TNode<BoolT> condition, CodeAssemblerParameterizedLabel<T...>* if_true, std::vector<Node*> args_true, CodeAssemblerParameterizedLabel<U...>* if_false, std::vector<Node*> args_false) { if_true->AddInputsVector(std::move(args_true)); if_false->AddInputsVector(std::move(args_false)); Branch(condition, if_true->plain_label(), if_false->plain_label()); } template <class... T, class... Args> void Goto(CodeAssemblerParameterizedLabel<T...>* label, Args... args) { label->AddInputs(args...); Goto(label->plain_label()); } void Branch(TNode<BoolT> condition, const std::function<void()>& true_body, const std::function<void()>& false_body); void Branch(TNode<BoolT> condition, Label* true_label, const std::function<void()>& false_body); void Branch(TNode<BoolT> condition, const std::function<void()>& 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 TNode<RawPtrT> LoadFramePointer(); TNode<RawPtrT> LoadParentFramePointer(); // Poison |value| on speculative paths. TNode<Object> TaggedPoisonOnSpeculation(TNode<Object> value); TNode<WordT> WordPoisonOnSpeculation(TNode<WordT> value); // Load raw memory location. Node* Load(MachineType type, Node* base, LoadSensitivity needs_poisoning = LoadSensitivity::kSafe); template <class Type> TNode<Type> Load(MachineType type, TNode<RawPtr<Type>> base) { DCHECK( IsSubtype(type.representation(), MachineRepresentationOf<Type>::value)); return UncheckedCast<Type>(Load(type, static_cast<Node*>(base))); } Node* Load(MachineType type, Node* base, Node* offset, LoadSensitivity needs_poisoning = LoadSensitivity::kSafe); template <class Type> TNode<Type> Load(Node* base, LoadSensitivity needs_poisoning = LoadSensitivity::kSafe) { return UncheckedCast<Type>( Load(MachineTypeOf<Type>::value, base, needs_poisoning)); } template <class Type> TNode<Type> Load(Node* base, SloppyTNode<WordT> offset, LoadSensitivity needs_poisoning = LoadSensitivity::kSafe) { return UncheckedCast<Type>( Load(MachineTypeOf<Type>::value, base, offset, needs_poisoning)); } Node* AtomicLoad(MachineType type, Node* base, Node* offset); // Load uncompressed tagged value from (most likely off JS heap) memory // location. TNode<Object> LoadFullTagged( Node* base, LoadSensitivity needs_poisoning = LoadSensitivity::kSafe); TNode<Object> LoadFullTagged( Node* base, Node* offset, LoadSensitivity needs_poisoning = LoadSensitivity::kSafe); Node* LoadFromObject(MachineType type, TNode<HeapObject> object, TNode<IntPtrT> offset); // Load a value from the root array. TNode<Object> LoadRoot(RootIndex root_index); // Store value to raw memory location. Node* Store(Node* base, Node* value); Node* Store(Node* base, Node* offset, Node* value); Node* StoreEphemeronKey(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* UnsafeStoreNoWriteBarrier(MachineRepresentation rep, Node* base, Node* value); Node* UnsafeStoreNoWriteBarrier(MachineRepresentation rep, Node* base, Node* offset, Node* value); // Stores uncompressed tagged value to (most likely off JS heap) memory // location without write barrier. Node* StoreFullTaggedNoWriteBarrier(Node* base, Node* tagged_value); Node* StoreFullTaggedNoWriteBarrier(Node* base, Node* offset, Node* tagged_value); // Optimized memory operations that map to Turbofan simplified nodes. TNode<HeapObject> OptimizedAllocate(TNode<IntPtrT> size, AllocationType allocation, AllowLargeObjects allow_large_objects); void StoreToObject(MachineRepresentation rep, TNode<HeapObject> object, TNode<IntPtrT> offset, Node* value, StoreToObjectWriteBarrier write_barrier); void OptimizedStoreField(MachineRepresentation rep, TNode<HeapObject> object, int offset, Node* value); void OptimizedStoreFieldAssertNoWriteBarrier(MachineRepresentation rep, TNode<HeapObject> object, int offset, Node* value); void OptimizedStoreFieldUnsafeNoWriteBarrier(MachineRepresentation rep, TNode<HeapObject> object, int offset, Node* value); void OptimizedStoreMap(TNode<HeapObject> object, TNode<Map>); // {value_high} is used for 64-bit stores on 32-bit platforms, must be // nullptr in other cases. Node* AtomicStore(MachineRepresentation rep, Node* base, Node* offset, Node* value, Node* value_high = nullptr); // Exchange value at raw memory location Node* AtomicExchange(MachineType type, Node* base, Node* offset, Node* value, Node* value_high = nullptr); // Compare and Exchange value at raw memory location Node* AtomicCompareExchange(MachineType type, Node* base, Node* offset, Node* old_value, Node* new_value, Node* old_value_high = nullptr, Node* new_value_high = nullptr); Node* AtomicAdd(MachineType type, Node* base, Node* offset, Node* value, Node* value_high = nullptr); Node* AtomicSub(MachineType type, Node* base, Node* offset, Node* value, Node* value_high = nullptr); Node* AtomicAnd(MachineType type, Node* base, Node* offset, Node* value, Node* value_high = nullptr); Node* AtomicOr(MachineType type, Node* base, Node* offset, Node* value, Node* value_high = nullptr); Node* AtomicXor(MachineType type, Node* base, Node* offset, Node* value, Node* value_high = nullptr); // Store a value to the root array. Node* StoreRoot(RootIndex root_index, Node* value); // Basic arithmetic operations. #define DECLARE_CODE_ASSEMBLER_BINARY_OP(name, ResType, Arg1Type, Arg2Type) \ TNode<ResType> name(SloppyTNode<Arg1Type> a, SloppyTNode<Arg2Type> b); CODE_ASSEMBLER_BINARY_OP_LIST(DECLARE_CODE_ASSEMBLER_BINARY_OP) #undef DECLARE_CODE_ASSEMBLER_BINARY_OP TNode<UintPtrT> WordShr(TNode<UintPtrT> left, TNode<IntegralT> right) { return Unsigned( WordShr(static_cast<Node*>(left), static_cast<Node*>(right))); } TNode<IntPtrT> WordSar(TNode<IntPtrT> left, TNode<IntegralT> right) { return Signed(WordSar(static_cast<Node*>(left), static_cast<Node*>(right))); } TNode<IntPtrT> WordShl(TNode<IntPtrT> left, TNode<IntegralT> right) { return Signed(WordShl(static_cast<Node*>(left), static_cast<Node*>(right))); } TNode<UintPtrT> WordShl(TNode<UintPtrT> left, TNode<IntegralT> right) { return Unsigned( WordShl(static_cast<Node*>(left), static_cast<Node*>(right))); } TNode<Int32T> Word32Shl(TNode<Int32T> left, TNode<Int32T> right) { return Signed( Word32Shl(static_cast<Node*>(left), static_cast<Node*>(right))); } TNode<Uint32T> Word32Shl(TNode<Uint32T> left, TNode<Uint32T> right) { return Unsigned( Word32Shl(static_cast<Node*>(left), static_cast<Node*>(right))); } TNode<Uint32T> Word32Shr(TNode<Uint32T> left, TNode<Uint32T> right) { return Unsigned( Word32Shr(static_cast<Node*>(left), static_cast<Node*>(right))); } TNode<Int32T> Word32Sar(TNode<Int32T> left, TNode<Int32T> right) { return Signed( Word32Sar(static_cast<Node*>(left), static_cast<Node*>(right))); } TNode<IntPtrT> WordAnd(TNode<IntPtrT> left, TNode<IntPtrT> right) { return Signed(WordAnd(static_cast<Node*>(left), static_cast<Node*>(right))); } TNode<UintPtrT> WordAnd(TNode<UintPtrT> left, TNode<UintPtrT> right) { return Unsigned( WordAnd(static_cast<Node*>(left), static_cast<Node*>(right))); } TNode<Int32T> Word32And(TNode<Int32T> left, TNode<Int32T> right) { return Signed( Word32And(static_cast<Node*>(left), static_cast<Node*>(right))); } TNode<Uint32T> Word32And(TNode<Uint32T> left, TNode<Uint32T> right) { return Unsigned( Word32And(static_cast<Node*>(left), static_cast<Node*>(right))); } TNode<Int32T> Word32Or(TNode<Int32T> left, TNode<Int32T> right) { return Signed( Word32Or(static_cast<Node*>(left), static_cast<Node*>(right))); } TNode<Uint32T> Word32Or(TNode<Uint32T> left, TNode<Uint32T> right) { return Unsigned( Word32Or(static_cast<Node*>(left), static_cast<Node*>(right))); } TNode<BoolT> IntPtrEqual(TNode<WordT> left, TNode<WordT> right); TNode<BoolT> WordEqual(TNode<WordT> left, TNode<WordT> right); TNode<BoolT> WordNotEqual(TNode<WordT> left, TNode<WordT> right); TNode<BoolT> Word32Equal(TNode<Word32T> left, TNode<Word32T> right); TNode<BoolT> Word32NotEqual(TNode<Word32T> left, TNode<Word32T> right); TNode<BoolT> Word64Equal(TNode<Word64T> left, TNode<Word64T> right); TNode<BoolT> Word64NotEqual(TNode<Word64T> left, TNode<Word64T> right); TNode<BoolT> Word32Or(TNode<BoolT> left, TNode<BoolT> right) { return UncheckedCast<BoolT>( Word32Or(static_cast<Node*>(left), static_cast<Node*>(right))); } TNode<BoolT> Word32And(TNode<BoolT> left, TNode<BoolT> right) { return UncheckedCast<BoolT>( Word32And(static_cast<Node*>(left), static_cast<Node*>(right))); } TNode<Int32T> Int32Add(TNode<Int32T> left, TNode<Int32T> right) { return Signed( Int32Add(static_cast<Node*>(left), static_cast<Node*>(right))); } TNode<Uint32T> Uint32Add(TNode<Uint32T> left, TNode<Uint32T> right) { return Unsigned( Int32Add(static_cast<Node*>(left), static_cast<Node*>(right))); } TNode<Int32T> Int32Sub(TNode<Int32T> left, TNode<Int32T> right) { return Signed( Int32Sub(static_cast<Node*>(left), static_cast<Node*>(right))); } TNode<Int32T> Int32Mul(TNode<Int32T> left, TNode<Int32T> right) { return Signed( Int32Mul(static_cast<Node*>(left), static_cast<Node*>(right))); } TNode<IntPtrT> IntPtrAdd(TNode<IntPtrT> left, TNode<IntPtrT> right) { return Signed( IntPtrAdd(static_cast<Node*>(left), static_cast<Node*>(right))); } TNode<IntPtrT> IntPtrSub(TNode<IntPtrT> left, TNode<IntPtrT> right) { return Signed( IntPtrSub(static_cast<Node*>(left), static_cast<Node*>(right))); } TNode<IntPtrT> IntPtrMul(TNode<IntPtrT> left, TNode<IntPtrT> right) { return Signed( IntPtrMul(static_cast<Node*>(left), static_cast<Node*>(right))); } TNode<UintPtrT> UintPtrAdd(TNode<UintPtrT> left, TNode<UintPtrT> right) { return Unsigned( IntPtrAdd(static_cast<Node*>(left), static_cast<Node*>(right))); } TNode<UintPtrT> UintPtrSub(TNode<UintPtrT> left, TNode<UintPtrT> right) { return Unsigned( IntPtrSub(static_cast<Node*>(left), static_cast<Node*>(right))); } TNode<RawPtrT> RawPtrAdd(TNode<RawPtrT> left, TNode<IntPtrT> right) { return ReinterpretCast<RawPtrT>(IntPtrAdd(left, right)); } TNode<RawPtrT> RawPtrSub(TNode<RawPtrT> left, TNode<IntPtrT> right) { return ReinterpretCast<RawPtrT>(IntPtrSub(left, right)); } TNode<IntPtrT> RawPtrSub(TNode<RawPtrT> left, TNode<RawPtrT> right) { return Signed( IntPtrSub(static_cast<Node*>(left), static_cast<Node*>(right))); } TNode<WordT> WordShl(SloppyTNode<WordT> value, int shift); TNode<WordT> WordShr(SloppyTNode<WordT> value, int shift); TNode<WordT> WordSar(SloppyTNode<WordT> value, int shift); TNode<IntPtrT> WordShr(TNode<IntPtrT> value, int shift) { return UncheckedCast<IntPtrT>(WordShr(static_cast<Node*>(value), shift)); } TNode<IntPtrT> WordSar(TNode<IntPtrT> value, int shift) { return UncheckedCast<IntPtrT>(WordSar(static_cast<Node*>(value), shift)); } TNode<Word32T> Word32Shr(SloppyTNode<Word32T> value, int shift); TNode<Word32T> Word32Sar(SloppyTNode<Word32T> value, int shift); // Unary #define DECLARE_CODE_ASSEMBLER_UNARY_OP(name, ResType, ArgType) \ TNode<ResType> name(SloppyTNode<ArgType> a); CODE_ASSEMBLER_UNARY_OP_LIST(DECLARE_CODE_ASSEMBLER_UNARY_OP) #undef DECLARE_CODE_ASSEMBLER_UNARY_OP template <class Dummy = void> TNode<IntPtrT> BitcastTaggedToWord(TNode<Smi> node) { static_assert(sizeof(Dummy) < 0, "Should use BitcastTaggedToWordForTagAndSmiBits instead."); } // 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<IntPtrT> ChangeFloat64ToIntPtr(TNode<Float64T> value); TNode<UintPtrT> ChangeFloat64ToUintPtr(TNode<Float64T> value); // Same in the opposite direction. TNode<Float64T> ChangeUintPtrToFloat64(TNode<UintPtrT> 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). TNode<Float64T> RoundIntPtrToFloat64(Node* value); // No-op on 32-bit, otherwise zero extend. TNode<UintPtrT> ChangeUint32ToWord(TNode<Word32T> value); // No-op on 32-bit, otherwise sign extend. TNode<IntPtrT> ChangeInt32ToIntPtr(TNode<Word32T> value); // Truncates a float to a 32-bit integer. If the float is outside of 32-bit // range, make sure that overflow detection is easy. In particular, return // int_min instead of int_max on arm platforms by using parameter // kSetOverflowToMin. TNode<Int32T> TruncateFloat32ToInt32(SloppyTNode<Float32T> 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 <int index, class T1, class T2> TNode<typename std::tuple_element<index, std::tuple<T1, T2>>::type> Projection(TNode<PairT<T1, T2>> value) { return UncheckedCast< typename std::tuple_element<index, std::tuple<T1, T2>>::type>( Projection(index, value)); } // Calls template <class... TArgs> TNode<Object> CallRuntime(Runtime::FunctionId function, TNode<Object> context, TArgs... args) { return CallRuntimeImpl(function, context, {implicit_cast<TNode<Object>>(args)...}); } template <class... TArgs> void TailCallRuntime(Runtime::FunctionId function, TNode<Object> context, TArgs... args) { int argc = static_cast<int>(sizeof...(args)); TNode<Int32T> arity = Int32Constant(argc); return TailCallRuntimeImpl(function, arity, context, {implicit_cast<TNode<Object>>(args)...}); } template <class... TArgs> void TailCallRuntime(Runtime::FunctionId function, TNode<Int32T> arity, TNode<Object> context, TArgs... args) { return TailCallRuntimeImpl(function, arity, context, {implicit_cast<TNode<Object>>(args)...}); } // // If context passed to CallStub is nullptr, it won't be passed to the stub. // template <class T = Object, class... TArgs> TNode<T> CallStub(Callable const& callable, TNode<Object> context, TArgs... args) { TNode<Code> target = HeapConstant(callable.code()); return CallStub<T>(callable.descriptor(), target, context, args...); } template <class T = Object, class... TArgs> TNode<T> CallStub(const CallInterfaceDescriptor& descriptor, TNode<Code> target, TNode<Object> context, TArgs... args) { return UncheckedCast<T>(CallStubR(StubCallMode::kCallCodeObject, descriptor, 1, target, context, args...)); } template <class... TArgs> Node* CallStubR(StubCallMode call_mode, const CallInterfaceDescriptor& descriptor, size_t result_size, TNode<Object> target, TNode<Object> context, TArgs... args) { return CallStubRImpl(call_mode, descriptor, result_size, target, context, {args...}); } Node* CallStubN(StubCallMode call_mode, const CallInterfaceDescriptor& descriptor, size_t result_size, int input_count, Node* const* inputs); template <class T = Object, class... TArgs> TNode<T> CallBuiltinPointer(const CallInterfaceDescriptor& descriptor, TNode<BuiltinPtr> target, TNode<Object> context, TArgs... args) { return UncheckedCast<T>(CallStubR(StubCallMode::kCallBuiltinPointer, descriptor, 1, target, context, args...)); } template <class... TArgs> void TailCallStub(Callable const& callable, TNode<Object> context, TArgs... args) { TNode<Code> target = HeapConstant(callable.code()); TailCallStub(callable.descriptor(), target, context, args...); } template <class... TArgs> void TailCallStub(const CallInterfaceDescriptor& descriptor, TNode<Code> target, TNode<Object> context, TArgs... args) { TailCallStubImpl(descriptor, target, context, {args...}); } template <class... TArgs> void TailCallBytecodeDispatch(const CallInterfaceDescriptor& descriptor, TNode<RawPtrT> target, TArgs... args); template <class... TArgs> void TailCallStubThenBytecodeDispatch( const CallInterfaceDescriptor& descriptor, Node* target, Node* context, TArgs... args) { 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. void TailCallJSCode(TNode<Code> code, TNode<Context> context, TNode<JSFunction> function, TNode<Object> new_target, TNode<Int32T> arg_count); template <class... TArgs> TNode<Object> CallJS(Callable const& callable, Node* context, Node* function, Node* receiver, TArgs... args) { int argc = static_cast<int>(sizeof...(args)); TNode<Int32T> arity = Int32Constant(argc); TNode<Code> target = HeapConstant(callable.code()); return CAST(CallJSStubImpl(callable.descriptor(), target, CAST(context), CAST(function), TNode<Object>(), arity, {receiver, args...})); } template <class... TArgs> Node* ConstructJSWithTarget(Callable const& callable, Node* context, Node* function, Node* new_target, TArgs... args) { int argc = static_cast<int>(sizeof...(args)); TNode<Int32T> arity = Int32Constant(argc); TNode<Object> receiver = LoadRoot(RootIndex::kUndefinedValue); TNode<Code> target = HeapConstant(callable.code()); return CallJSStubImpl(callable.descriptor(), target, CAST(context), CAST(function), CAST(new_target), arity, {receiver, args...}); } template <class... TArgs> Node* ConstructJS(Callable const& callable, Node* context, Node* new_target, TArgs... args) { return ConstructJSWithTarget(callable, context, new_target, new_target, args...); } Node* CallCFunctionN(Signature<MachineType>* signature, int input_count, Node* const* inputs); // Type representing C function argument with type info. using CFunctionArg = std::pair<MachineType, Node*>; // Call to a C function. template <class... CArgs> Node* CallCFunction(Node* function, MachineType return_type, CArgs... cargs) { static_assert(v8::internal::conjunction< std::is_convertible<CArgs, CFunctionArg>...>::value, "invalid argument types"); return CallCFunction(function, return_type, {cargs...}); } // Call to a C function without a function discriptor on AIX. template <class... CArgs> Node* CallCFunctionWithoutFunctionDescriptor(Node* function, MachineType return_type, CArgs... cargs) { static_assert(v8::internal::conjunction< std::is_convertible<CArgs, CFunctionArg>...>::value, "invalid argument types"); return CallCFunctionWithoutFunctionDescriptor(function, return_type, {cargs...}); } // Call to a C function, while saving/restoring caller registers. template <class... CArgs> Node* CallCFunctionWithCallerSavedRegisters(Node* function, MachineType return_type, SaveFPRegsMode mode, CArgs... cargs) { static_assert(v8::internal::conjunction< std::is_convertible<CArgs, CFunctionArg>...>::value, "invalid argument types"); return CallCFunctionWithCallerSavedRegisters(function, return_type, mode, {cargs...}); } // 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; bool IsExceptionHandlerActive() 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: void HandleException(Node* result); Node* CallCFunction(Node* function, MachineType return_type, std::initializer_list<CFunctionArg> args); Node* CallCFunctionWithoutFunctionDescriptor( Node* function, MachineType return_type, std::initializer_list<CFunctionArg> args); Node* CallCFunctionWithCallerSavedRegisters( Node* function, MachineType return_type, SaveFPRegsMode mode, std::initializer_list<CFunctionArg> args); TNode<Object> CallRuntimeImpl(Runtime::FunctionId function, TNode<Object> context, std::initializer_list<TNode<Object>> args); void TailCallRuntimeImpl(Runtime::FunctionId function, TNode<Int32T> arity, TNode<Object> context, std::initializer_list<TNode<Object>> args); void TailCallStubImpl(const CallInterfaceDescriptor& descriptor, TNode<Code> target, TNode<Object> context, std::initializer_list<Node*> args); void TailCallStubThenBytecodeDispatchImpl( const CallInterfaceDescriptor& descriptor, Node* target, Node* context, std::initializer_list<Node*> args); Node* CallStubRImpl(StubCallMode call_mode, const CallInterfaceDescriptor& descriptor, size_t result_size, TNode<Object> target, TNode<Object> context, std::initializer_list<Node*> args); Node* CallJSStubImpl(const CallInterfaceDescriptor& descriptor, TNode<Object> target, TNode<Object> context, TNode<Object> function, TNode<Object> new_target, TNode<Int32T> arity, std::initializer_list<Node*> args); // These two don't have definitions and are here only for catching use cases // where the cast is not necessary. TNode<Int32T> Signed(TNode<Int32T> x); TNode<Uint32T> Unsigned(TNode<Uint32T> x); RawMachineAssembler* raw_assembler() const; JSGraph* jsgraph() const; // Calls respective callback registered in the state. void CallPrologue(); void CallEpilogue(); CodeAssemblerState* state_; DISALLOW_COPY_AND_ASSIGN(CodeAssembler); }; // TODO(solanes, v8:6949): this class should be merged into // TypedCodeAssemblerVariable. It's required to be separate for // CodeAssemblerVariableLists. class V8_EXPORT_PRIVATE CodeAssemblerVariable { public: Node* value() const; MachineRepresentation rep() const; bool IsBound() const; protected: 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); 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&); struct ImplComparator { bool operator()(const CodeAssemblerVariable::Impl* a, const CodeAssemblerVariable::Impl* b) const; }; 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 T> class TypedCodeAssemblerVariable : public CodeAssemblerVariable { public: TypedCodeAssemblerVariable(TNode<T> initial_value, CodeAssembler* assembler) : CodeAssemblerVariable(assembler, PhiMachineRepresentationOf<T>, initial_value) {} explicit TypedCodeAssemblerVariable(CodeAssembler* assembler) : CodeAssemblerVariable(assembler, PhiMachineRepresentationOf<T>) {} #if DEBUG TypedCodeAssemblerVariable(AssemblerDebugInfo debug_info, CodeAssembler* assembler) : CodeAssemblerVariable(assembler, debug_info, PhiMachineRepresentationOf<T>) {} TypedCodeAssemblerVariable(AssemblerDebugInfo debug_info, TNode<T> initial_value, CodeAssembler* assembler) : CodeAssemblerVariable(assembler, debug_info, PhiMachineRepresentationOf<T>, initial_value) {} #endif // DEBUG TNode<T> value() const { return TNode<T>::UncheckedCast(CodeAssemblerVariable::value()); } void operator=(TNode<T> value) { Bind(value); } void operator=(const TypedCodeAssemblerVariable<T>& variable) { Bind(variable.value()); } private: using CodeAssemblerVariable::Bind; }; class V8_EXPORT_PRIVATE 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<CodeAssemblerVariable*> 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<CodeAssemblerVariable::Impl*, Node*, CodeAssemblerVariable::ImplComparator> 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<CodeAssemblerVariable::Impl*, std::vector<Node*>, CodeAssemblerVariable::ImplComparator> variable_merges_; // Cannot be copied because the destructor explicitly call the destructor of // the underlying {RawMachineLabel}, hence only one pointer can point to it. DISALLOW_COPY_AND_ASSIGN(CodeAssemblerLabel); }; class CodeAssemblerParameterizedLabelBase { public: bool is_used() const { return plain_label_.is_used(); } explicit CodeAssemblerParameterizedLabelBase(CodeAssembler* assembler, size_t arity, CodeAssemblerLabel::Type type) : state_(assembler->state()), phi_inputs_(arity), plain_label_(assembler, type) {} protected: CodeAssemblerLabel* plain_label() { return &plain_label_; } void AddInputs(std::vector<Node*> inputs); Node* CreatePhi(MachineRepresentation rep, const std::vector<Node*>& inputs); const std::vector<Node*>& CreatePhis( std::vector<MachineRepresentation> representations); private: CodeAssemblerState* state_; std::vector<std::vector<Node*>> phi_inputs_; std::vector<Node*> phi_nodes_; CodeAssemblerLabel plain_label_; }; template <class... Types> class CodeAssemblerParameterizedLabel : public CodeAssemblerParameterizedLabelBase { public: static constexpr size_t kArity = sizeof...(Types); explicit CodeAssemblerParameterizedLabel(CodeAssembler* assembler, CodeAssemblerLabel::Type type) : CodeAssemblerParameterizedLabelBase(assembler, kArity, type) {} private: friend class CodeAssembler; void AddInputsVector(std::vector<Node*> inputs) { CodeAssemblerParameterizedLabelBase::AddInputs(std::move(inputs)); } void AddInputs(TNode<Types>... inputs) { CodeAssemblerParameterizedLabelBase::AddInputs( std::vector<Node*>{inputs...}); } void CreatePhis(TNode<Types>*... results) { const std::vector<Node*>& phi_nodes = CodeAssemblerParameterizedLabelBase::CreatePhis( {PhiMachineRepresentationOf<Types>...}); auto it = phi_nodes.begin(); USE(it); ITERATE_PACK(AssignPhi(results, *(it++))); } template <class T> static void AssignPhi(TNode<T>* result, Node* phi) { if (phi != nullptr) *result = TNode<T>::UncheckedCast(phi); } }; using CodeAssemblerExceptionHandlerLabel = CodeAssemblerParameterizedLabel<Object>; 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, CodeKind kind, const char* name, PoisoningMitigationLevel poisoning_level, int32_t builtin_index = Builtins::kNoBuiltinId); // Create with JSCall linkage. CodeAssemblerState(Isolate* isolate, Zone* zone, int parameter_count, CodeKind 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); #endif // DEBUG bool InsideBlock(); void SetInitialDebugInformation(const char* msg, const char* file, int line); private: friend class CodeAssembler; friend class CodeAssemblerLabel; friend class CodeAssemblerVariable; friend class CodeAssemblerTester; friend class CodeAssemblerParameterizedLabelBase; friend class ScopedExceptionHandler; CodeAssemblerState(Isolate* isolate, Zone* zone, CallDescriptor* call_descriptor, CodeKind kind, const char* name, PoisoningMitigationLevel poisoning_level, int32_t builtin_index); void PushExceptionHandler(CodeAssemblerExceptionHandlerLabel* label); void PopExceptionHandler(); std::unique_ptr<RawMachineAssembler> raw_assembler_; CodeKind kind_; const char* name_; int32_t builtin_index_; bool code_generated_; ZoneSet<CodeAssemblerVariable::Impl*, CodeAssemblerVariable::ImplComparator> variables_; CodeAssemblerCallback call_prologue_; CodeAssemblerCallback call_epilogue_; std::vector<CodeAssemblerExceptionHandlerLabel*> exception_handler_labels_; using VariableId = uint32_t; VariableId next_variable_id_ = 0; JSGraph* jsgraph_; // Only used by CodeStubAssembler builtins. std::vector<FileAndLine> macro_call_stack_; VariableId NextVariableId() { return next_variable_id_++; } DISALLOW_COPY_AND_ASSIGN(CodeAssemblerState); }; class V8_EXPORT_PRIVATE ScopedExceptionHandler { public: ScopedExceptionHandler(CodeAssembler* assembler, CodeAssemblerExceptionHandlerLabel* label); // Use this constructor for compatability/ports of old CSA code only. New code // should use the CodeAssemblerExceptionHandlerLabel version. ScopedExceptionHandler(CodeAssembler* assembler, CodeAssemblerLabel* label, TypedCodeAssemblerVariable<Object>* exception); ~ScopedExceptionHandler(); private: bool has_handler_; CodeAssembler* assembler_; CodeAssemblerLabel* compatibility_label_; std::unique_ptr<CodeAssemblerExceptionHandlerLabel> label_; TypedCodeAssemblerVariable<Object>* exception_; }; } // namespace compiler #if defined(V8_HOST_ARCH_32_BIT) #define BINT_IS_SMI using BInt = Smi; #elif defined(V8_HOST_ARCH_64_BIT) #define BINT_IS_INTPTR using BInt = IntPtrT; #else #error Unknown architecture. #endif } // namespace internal } // namespace v8 #endif // V8_COMPILER_CODE_ASSEMBLER_H_