// Copyright 2018 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_TORQUE_INSTRUCTIONS_H_ #define V8_TORQUE_INSTRUCTIONS_H_ #include #include "src/torque/ast.h" #include "src/torque/source-positions.h" #include "src/torque/types.h" #include "src/torque/utils.h" namespace v8 { namespace internal { namespace torque { class Block; class Builtin; class ControlFlowGraph; class Intrinsic; class Macro; class NamespaceConstant; class RuntimeFunction; // Instructions where all backends generate code the same way. #define TORQUE_BACKEND_AGNOSTIC_INSTRUCTION_LIST(V) \ V(PeekInstruction) \ V(PokeInstruction) \ V(DeleteRangeInstruction) // Instructions where different backends may generate different code. #define TORQUE_BACKEND_DEPENDENT_INSTRUCTION_LIST(V) \ V(PushUninitializedInstruction) \ V(PushBuiltinPointerInstruction) \ V(LoadReferenceInstruction) \ V(StoreReferenceInstruction) \ V(LoadBitFieldInstruction) \ V(StoreBitFieldInstruction) \ V(CallCsaMacroInstruction) \ V(CallIntrinsicInstruction) \ V(NamespaceConstantInstruction) \ V(CallCsaMacroAndBranchInstruction) \ V(CallBuiltinInstruction) \ V(CallRuntimeInstruction) \ V(CallBuiltinPointerInstruction) \ V(BranchInstruction) \ V(ConstexprBranchInstruction) \ V(GotoInstruction) \ V(GotoExternalInstruction) \ V(ReturnInstruction) \ V(PrintConstantStringInstruction) \ V(AbortInstruction) \ V(UnsafeCastInstruction) #define TORQUE_INSTRUCTION_LIST(V) \ TORQUE_BACKEND_AGNOSTIC_INSTRUCTION_LIST(V) \ TORQUE_BACKEND_DEPENDENT_INSTRUCTION_LIST(V) #define TORQUE_INSTRUCTION_BOILERPLATE() \ static const InstructionKind kKind; \ std::unique_ptr Clone() const override; \ void Assign(const InstructionBase& other) override; \ void TypeInstruction(Stack* stack, ControlFlowGraph* cfg) \ const override; \ void RecomputeDefinitionLocations(Stack* locations, \ Worklist* worklist) \ const override; enum class InstructionKind { #define ENUM_ITEM(name) k##name, TORQUE_INSTRUCTION_LIST(ENUM_ITEM) #undef ENUM_ITEM }; struct InstructionBase; class DefinitionLocation { public: enum class Kind { kInvalid, kParameter, kPhi, kInstruction, }; DefinitionLocation() : kind_(Kind::kInvalid), location_(nullptr), index_(0) {} static DefinitionLocation Parameter(std::size_t index) { return DefinitionLocation(Kind::kParameter, nullptr, index); } static DefinitionLocation Phi(const Block* block, std::size_t index) { return DefinitionLocation(Kind::kPhi, block, index); } static DefinitionLocation Instruction(const InstructionBase* instruction, std::size_t index = 0) { return DefinitionLocation(Kind::kInstruction, instruction, index); } Kind GetKind() const { return kind_; } bool IsValid() const { return kind_ != Kind::kInvalid; } bool IsParameter() const { return kind_ == Kind::kParameter; } bool IsPhi() const { return kind_ == Kind::kPhi; } bool IsInstruction() const { return kind_ == Kind::kInstruction; } std::size_t GetParameterIndex() const { DCHECK(IsParameter()); return index_; } const Block* GetPhiBlock() const { DCHECK(IsPhi()); return reinterpret_cast(location_); } bool IsPhiFromBlock(const Block* block) const { return IsPhi() && GetPhiBlock() == block; } std::size_t GetPhiIndex() const { DCHECK(IsPhi()); return index_; } const InstructionBase* GetInstruction() const { DCHECK(IsInstruction()); return reinterpret_cast(location_); } std::size_t GetInstructionIndex() const { DCHECK(IsInstruction()); return index_; } bool operator==(const DefinitionLocation& other) const { if (kind_ != other.kind_) return false; if (location_ != other.location_) return false; return index_ == other.index_; } bool operator!=(const DefinitionLocation& other) const { return !operator==(other); } bool operator<(const DefinitionLocation& other) const { if (kind_ != other.kind_) { return static_cast(kind_) < static_cast(other.kind_); } if (location_ != other.location_) { return location_ < other.location_; } return index_ < other.index_; } private: DefinitionLocation(Kind kind, const void* location, std::size_t index) : kind_(kind), location_(location), index_(index) {} Kind kind_; const void* location_; std::size_t index_; }; inline std::ostream& operator<<(std::ostream& stream, const DefinitionLocation& loc) { switch (loc.GetKind()) { case DefinitionLocation::Kind::kInvalid: return stream << "DefinitionLocation::Invalid()"; case DefinitionLocation::Kind::kParameter: return stream << "DefinitionLocation::Parameter(" << loc.GetParameterIndex() << ")"; case DefinitionLocation::Kind::kPhi: return stream << "DefinitionLocation::Phi(" << std::hex << loc.GetPhiBlock() << std::dec << ", " << loc.GetPhiIndex() << ")"; case DefinitionLocation::Kind::kInstruction: return stream << "DefinitionLocation::Instruction(" << std::hex << loc.GetInstruction() << std::dec << ", " << loc.GetInstructionIndex() << ")"; } } struct InstructionBase { InstructionBase() : pos(CurrentSourcePosition::Get()) {} virtual std::unique_ptr Clone() const = 0; virtual void Assign(const InstructionBase& other) = 0; virtual ~InstructionBase() = default; virtual void TypeInstruction(Stack* stack, ControlFlowGraph* cfg) const = 0; virtual void RecomputeDefinitionLocations( Stack* locations, Worklist* worklist) const = 0; void InvalidateTransientTypes(Stack* stack) const; virtual bool IsBlockTerminator() const { return false; } virtual void AppendSuccessorBlocks(std::vector* block_list) const {} SourcePosition pos; }; class Instruction { public: template Instruction(T instr) // NOLINT(runtime/explicit) : kind_(T::kKind), instruction_(new T(std::move(instr))) {} template T& Cast() { DCHECK(Is()); return static_cast(*instruction_); } template const T& Cast() const { DCHECK(Is()); return static_cast(*instruction_); } template bool Is() const { return kind_ == T::kKind; } template T* DynamicCast() { if (Is()) return &Cast(); return nullptr; } template const T* DynamicCast() const { if (Is()) return &Cast(); return nullptr; } Instruction(const Instruction& other) V8_NOEXCEPT : kind_(other.kind_), instruction_(other.instruction_->Clone()) {} Instruction& operator=(const Instruction& other) V8_NOEXCEPT { if (kind_ == other.kind_) { instruction_->Assign(*other.instruction_); } else { kind_ = other.kind_; instruction_ = other.instruction_->Clone(); } return *this; } InstructionKind kind() const { return kind_; } const char* Mnemonic() const { switch (kind()) { #define ENUM_ITEM(name) \ case InstructionKind::k##name: \ return #name; TORQUE_INSTRUCTION_LIST(ENUM_ITEM) #undef ENUM_ITEM default: UNREACHABLE(); } } void TypeInstruction(Stack* stack, ControlFlowGraph* cfg) const { return instruction_->TypeInstruction(stack, cfg); } void RecomputeDefinitionLocations(Stack* locations, Worklist* worklist) const { instruction_->RecomputeDefinitionLocations(locations, worklist); } InstructionBase* operator->() { return instruction_.get(); } const InstructionBase* operator->() const { return instruction_.get(); } private: InstructionKind kind_; std::unique_ptr instruction_; }; struct PeekInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() PeekInstruction(BottomOffset slot, base::Optional widened_type) : slot(slot), widened_type(widened_type) {} BottomOffset slot; base::Optional widened_type; }; struct PokeInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() PokeInstruction(BottomOffset slot, base::Optional widened_type) : slot(slot), widened_type(widened_type) {} BottomOffset slot; base::Optional widened_type; }; // Preserve the top {preserved_slots} number of slots, and delete // {deleted_slots} number or slots below. struct DeleteRangeInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() explicit DeleteRangeInstruction(StackRange range) : range(range) {} StackRange range; }; struct PushUninitializedInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() explicit PushUninitializedInstruction(const Type* type) : type(type) {} DefinitionLocation GetValueDefinition() const; const Type* type; }; struct PushBuiltinPointerInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() PushBuiltinPointerInstruction(std::string external_name, const Type* type) : external_name(std::move(external_name)), type(type) { DCHECK(type->IsBuiltinPointerType()); } DefinitionLocation GetValueDefinition() const; std::string external_name; const Type* type; }; struct NamespaceConstantInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() explicit NamespaceConstantInstruction(NamespaceConstant* constant) : constant(constant) {} std::size_t GetValueDefinitionCount() const; DefinitionLocation GetValueDefinition(std::size_t index) const; NamespaceConstant* constant; }; struct LoadReferenceInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() explicit LoadReferenceInstruction(const Type* type) : type(type) {} DefinitionLocation GetValueDefinition() const; const Type* type; }; struct StoreReferenceInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() explicit StoreReferenceInstruction(const Type* type) : type(type) {} const Type* type; }; // Pops a bitfield struct; pushes a bitfield value extracted from it. struct LoadBitFieldInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() LoadBitFieldInstruction(const Type* bit_field_struct_type, BitField bit_field) : bit_field_struct_type(bit_field_struct_type), bit_field(std::move(bit_field)) {} DefinitionLocation GetValueDefinition() const; const Type* bit_field_struct_type; BitField bit_field; }; // Pops a bitfield value and a bitfield struct; pushes a new bitfield struct // containing the updated value. struct StoreBitFieldInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() StoreBitFieldInstruction(const Type* bit_field_struct_type, BitField bit_field, bool starts_as_zero) : bit_field_struct_type(bit_field_struct_type), bit_field(std::move(bit_field)), starts_as_zero(starts_as_zero) {} DefinitionLocation GetValueDefinition() const; const Type* bit_field_struct_type; BitField bit_field; // Allows skipping the mask step if we know the starting value is zero. bool starts_as_zero; }; struct CallIntrinsicInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() CallIntrinsicInstruction(Intrinsic* intrinsic, TypeVector specialization_types, std::vector constexpr_arguments) : intrinsic(intrinsic), specialization_types(std::move(specialization_types)), constexpr_arguments(constexpr_arguments) {} std::size_t GetValueDefinitionCount() const; DefinitionLocation GetValueDefinition(std::size_t index) const; Intrinsic* intrinsic; TypeVector specialization_types; std::vector constexpr_arguments; }; struct CallCsaMacroInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() CallCsaMacroInstruction(Macro* macro, std::vector constexpr_arguments, base::Optional catch_block) : macro(macro), constexpr_arguments(constexpr_arguments), catch_block(catch_block) {} void AppendSuccessorBlocks(std::vector* block_list) const override { if (catch_block) block_list->push_back(*catch_block); } base::Optional GetExceptionObjectDefinition() const; std::size_t GetValueDefinitionCount() const; DefinitionLocation GetValueDefinition(std::size_t index) const; Macro* macro; std::vector constexpr_arguments; base::Optional catch_block; }; struct CallCsaMacroAndBranchInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() CallCsaMacroAndBranchInstruction(Macro* macro, std::vector constexpr_arguments, base::Optional return_continuation, std::vector label_blocks, base::Optional catch_block) : macro(macro), constexpr_arguments(constexpr_arguments), return_continuation(return_continuation), label_blocks(label_blocks), catch_block(catch_block) {} bool IsBlockTerminator() const override { return true; } void AppendSuccessorBlocks(std::vector* block_list) const override { if (catch_block) block_list->push_back(*catch_block); if (return_continuation) block_list->push_back(*return_continuation); for (Block* block : label_blocks) block_list->push_back(block); } std::size_t GetLabelCount() const; std::size_t GetLabelValueDefinitionCount(std::size_t label) const; DefinitionLocation GetLabelValueDefinition(std::size_t label, std::size_t index) const; std::size_t GetValueDefinitionCount() const; DefinitionLocation GetValueDefinition(std::size_t index) const; base::Optional GetExceptionObjectDefinition() const; Macro* macro; std::vector constexpr_arguments; base::Optional return_continuation; std::vector label_blocks; base::Optional catch_block; }; struct CallBuiltinInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() bool IsBlockTerminator() const override { return is_tailcall; } CallBuiltinInstruction(bool is_tailcall, Builtin* builtin, size_t argc, base::Optional catch_block) : is_tailcall(is_tailcall), builtin(builtin), argc(argc), catch_block(catch_block) {} void AppendSuccessorBlocks(std::vector* block_list) const override { if (catch_block) block_list->push_back(*catch_block); } std::size_t GetValueDefinitionCount() const; DefinitionLocation GetValueDefinition(std::size_t index) const; base::Optional GetExceptionObjectDefinition() const; bool is_tailcall; Builtin* builtin; size_t argc; base::Optional catch_block; }; struct CallBuiltinPointerInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() bool IsBlockTerminator() const override { return is_tailcall; } CallBuiltinPointerInstruction(bool is_tailcall, const BuiltinPointerType* type, size_t argc) : is_tailcall(is_tailcall), type(type), argc(argc) {} std::size_t GetValueDefinitionCount() const; DefinitionLocation GetValueDefinition(std::size_t index) const; bool is_tailcall; const BuiltinPointerType* type; size_t argc; }; struct CallRuntimeInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() bool IsBlockTerminator() const override; CallRuntimeInstruction(bool is_tailcall, RuntimeFunction* runtime_function, size_t argc, base::Optional catch_block) : is_tailcall(is_tailcall), runtime_function(runtime_function), argc(argc), catch_block(catch_block) {} void AppendSuccessorBlocks(std::vector* block_list) const override { if (catch_block) block_list->push_back(*catch_block); } std::size_t GetValueDefinitionCount() const; DefinitionLocation GetValueDefinition(std::size_t index) const; base::Optional GetExceptionObjectDefinition() const; bool is_tailcall; RuntimeFunction* runtime_function; size_t argc; base::Optional catch_block; }; struct BranchInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() bool IsBlockTerminator() const override { return true; } void AppendSuccessorBlocks(std::vector* block_list) const override { block_list->push_back(if_true); block_list->push_back(if_false); } BranchInstruction(Block* if_true, Block* if_false) : if_true(if_true), if_false(if_false) {} Block* if_true; Block* if_false; }; struct ConstexprBranchInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() bool IsBlockTerminator() const override { return true; } void AppendSuccessorBlocks(std::vector* block_list) const override { block_list->push_back(if_true); block_list->push_back(if_false); } ConstexprBranchInstruction(std::string condition, Block* if_true, Block* if_false) : condition(condition), if_true(if_true), if_false(if_false) {} std::string condition; Block* if_true; Block* if_false; }; struct GotoInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() bool IsBlockTerminator() const override { return true; } void AppendSuccessorBlocks(std::vector* block_list) const override { block_list->push_back(destination); } explicit GotoInstruction(Block* destination) : destination(destination) {} Block* destination; }; struct GotoExternalInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() bool IsBlockTerminator() const override { return true; } GotoExternalInstruction(std::string destination, std::vector variable_names) : destination(std::move(destination)), variable_names(std::move(variable_names)) {} std::string destination; std::vector variable_names; }; struct ReturnInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() bool IsBlockTerminator() const override { return true; } }; struct PrintConstantStringInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() explicit PrintConstantStringInstruction(std::string message) : message(std::move(message)) {} std::string message; }; struct AbortInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() enum class Kind { kDebugBreak, kUnreachable, kAssertionFailure }; bool IsBlockTerminator() const override { return kind != Kind::kDebugBreak; } explicit AbortInstruction(Kind kind, std::string message = "") : kind(kind), message(std::move(message)) {} Kind kind; std::string message; }; struct UnsafeCastInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() explicit UnsafeCastInstruction(const Type* destination_type) : destination_type(destination_type) {} DefinitionLocation GetValueDefinition() const; const Type* destination_type; }; } // namespace torque } // namespace internal } // namespace v8 #endif // V8_TORQUE_INSTRUCTIONS_H_