| // 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 <memory> |
| |
| #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; |
| |
| #define TORQUE_INSTRUCTION_LIST(V) \ |
| V(PeekInstruction) \ |
| V(PokeInstruction) \ |
| V(DeleteRangeInstruction) \ |
| V(PushUninitializedInstruction) \ |
| V(PushBuiltinPointerInstruction) \ |
| V(CreateFieldReferenceInstruction) \ |
| V(LoadReferenceInstruction) \ |
| V(StoreReferenceInstruction) \ |
| 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_BOILERPLATE() \ |
| static const InstructionKind kKind; \ |
| std::unique_ptr<InstructionBase> Clone() const override; \ |
| void Assign(const InstructionBase& other) override; \ |
| void TypeInstruction(Stack<const Type*>* stack, ControlFlowGraph* cfg) \ |
| const override; |
| |
| enum class InstructionKind { |
| #define ENUM_ITEM(name) k##name, |
| TORQUE_INSTRUCTION_LIST(ENUM_ITEM) |
| #undef ENUM_ITEM |
| }; |
| |
| struct InstructionBase { |
| InstructionBase() : pos(CurrentSourcePosition::Get()) {} |
| virtual std::unique_ptr<InstructionBase> Clone() const = 0; |
| virtual void Assign(const InstructionBase& other) = 0; |
| virtual ~InstructionBase() = default; |
| |
| virtual void TypeInstruction(Stack<const Type*>* stack, |
| ControlFlowGraph* cfg) const = 0; |
| void InvalidateTransientTypes(Stack<const Type*>* stack) const; |
| virtual bool IsBlockTerminator() const { return false; } |
| virtual void AppendSuccessorBlocks(std::vector<Block*>* block_list) const {} |
| |
| SourcePosition pos; |
| }; |
| |
| class Instruction { |
| public: |
| template <class T> |
| Instruction(T instr) // NOLINT(runtime/explicit) |
| : kind_(T::kKind), instruction_(new T(std::move(instr))) {} |
| |
| template <class T> |
| T& Cast() { |
| DCHECK(Is<T>()); |
| return static_cast<T&>(*instruction_); |
| } |
| |
| template <class T> |
| const T& Cast() const { |
| DCHECK(Is<T>()); |
| return static_cast<const T&>(*instruction_); |
| } |
| |
| template <class T> |
| bool Is() const { |
| return kind_ == T::kKind; |
| } |
| |
| template <class T> |
| T* DynamicCast() { |
| if (Is<T>()) return &Cast<T>(); |
| return nullptr; |
| } |
| |
| template <class T> |
| const T* DynamicCast() const { |
| if (Is<T>()) return &Cast<T>(); |
| 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<const Type*>* stack, ControlFlowGraph* cfg) const { |
| return instruction_->TypeInstruction(stack, cfg); |
| } |
| |
| InstructionBase* operator->() { return instruction_.get(); } |
| const InstructionBase* operator->() const { return instruction_.get(); } |
| |
| private: |
| InstructionKind kind_; |
| std::unique_ptr<InstructionBase> instruction_; |
| }; |
| |
| struct PeekInstruction : InstructionBase { |
| TORQUE_INSTRUCTION_BOILERPLATE() |
| |
| PeekInstruction(BottomOffset slot, base::Optional<const Type*> widened_type) |
| : slot(slot), widened_type(widened_type) {} |
| |
| BottomOffset slot; |
| base::Optional<const Type*> widened_type; |
| }; |
| |
| struct PokeInstruction : InstructionBase { |
| TORQUE_INSTRUCTION_BOILERPLATE() |
| |
| PokeInstruction(BottomOffset slot, base::Optional<const Type*> widened_type) |
| : slot(slot), widened_type(widened_type) {} |
| |
| BottomOffset slot; |
| base::Optional<const Type*> 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) {} |
| |
| 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()); |
| } |
| |
| std::string external_name; |
| const Type* type; |
| }; |
| |
| struct NamespaceConstantInstruction : InstructionBase { |
| TORQUE_INSTRUCTION_BOILERPLATE() |
| explicit NamespaceConstantInstruction(NamespaceConstant* constant) |
| : constant(constant) {} |
| |
| NamespaceConstant* constant; |
| }; |
| |
| struct CreateFieldReferenceInstruction : InstructionBase { |
| TORQUE_INSTRUCTION_BOILERPLATE() |
| CreateFieldReferenceInstruction(const ClassType* class_type, |
| std::string field_name) |
| : class_type(class_type), field_name(std::move(field_name)) {} |
| const ClassType* class_type; |
| std::string field_name; |
| }; |
| |
| struct LoadReferenceInstruction : InstructionBase { |
| TORQUE_INSTRUCTION_BOILERPLATE() |
| explicit LoadReferenceInstruction(const Type* type) : type(type) {} |
| const Type* type; |
| }; |
| |
| struct StoreReferenceInstruction : InstructionBase { |
| TORQUE_INSTRUCTION_BOILERPLATE() |
| explicit StoreReferenceInstruction(const Type* type) : type(type) {} |
| const Type* type; |
| }; |
| |
| struct CallIntrinsicInstruction : InstructionBase { |
| TORQUE_INSTRUCTION_BOILERPLATE() |
| CallIntrinsicInstruction(Intrinsic* intrinsic, |
| TypeVector specialization_types, |
| std::vector<std::string> constexpr_arguments) |
| : intrinsic(intrinsic), |
| specialization_types(std::move(specialization_types)), |
| constexpr_arguments(constexpr_arguments) {} |
| |
| Intrinsic* intrinsic; |
| TypeVector specialization_types; |
| std::vector<std::string> constexpr_arguments; |
| }; |
| |
| struct CallCsaMacroInstruction : InstructionBase { |
| TORQUE_INSTRUCTION_BOILERPLATE() |
| CallCsaMacroInstruction(Macro* macro, |
| std::vector<std::string> constexpr_arguments, |
| base::Optional<Block*> catch_block) |
| : macro(macro), |
| constexpr_arguments(constexpr_arguments), |
| catch_block(catch_block) {} |
| void AppendSuccessorBlocks(std::vector<Block*>* block_list) const override { |
| if (catch_block) block_list->push_back(*catch_block); |
| } |
| |
| Macro* macro; |
| std::vector<std::string> constexpr_arguments; |
| base::Optional<Block*> catch_block; |
| }; |
| |
| struct CallCsaMacroAndBranchInstruction : InstructionBase { |
| TORQUE_INSTRUCTION_BOILERPLATE() |
| CallCsaMacroAndBranchInstruction(Macro* macro, |
| std::vector<std::string> constexpr_arguments, |
| base::Optional<Block*> return_continuation, |
| std::vector<Block*> label_blocks, |
| base::Optional<Block*> 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*>* 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); |
| } |
| |
| Macro* macro; |
| std::vector<std::string> constexpr_arguments; |
| base::Optional<Block*> return_continuation; |
| std::vector<Block*> label_blocks; |
| base::Optional<Block*> 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<Block*> catch_block) |
| : is_tailcall(is_tailcall), |
| builtin(builtin), |
| argc(argc), |
| catch_block(catch_block) {} |
| void AppendSuccessorBlocks(std::vector<Block*>* block_list) const override { |
| if (catch_block) block_list->push_back(*catch_block); |
| } |
| |
| bool is_tailcall; |
| Builtin* builtin; |
| size_t argc; |
| base::Optional<Block*> 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) {} |
| |
| 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<Block*> catch_block) |
| : is_tailcall(is_tailcall), |
| runtime_function(runtime_function), |
| argc(argc), |
| catch_block(catch_block) {} |
| void AppendSuccessorBlocks(std::vector<Block*>* block_list) const override { |
| if (catch_block) block_list->push_back(*catch_block); |
| } |
| |
| bool is_tailcall; |
| RuntimeFunction* runtime_function; |
| size_t argc; |
| base::Optional<Block*> catch_block; |
| }; |
| |
| struct BranchInstruction : InstructionBase { |
| TORQUE_INSTRUCTION_BOILERPLATE() |
| bool IsBlockTerminator() const override { return true; } |
| void AppendSuccessorBlocks(std::vector<Block*>* 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*>* 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*>* 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<std::string> variable_names) |
| : destination(std::move(destination)), |
| variable_names(std::move(variable_names)) {} |
| |
| std::string destination; |
| std::vector<std::string> 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) {} |
| |
| const Type* destination_type; |
| }; |
| |
| } // namespace torque |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_TORQUE_INSTRUCTIONS_H_ |