blob: 69dfbd8fc3a6a15ef46d9a9abfdac54833aa6afc [file] [log] [blame]
// 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;
// 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<InstructionBase> Clone() const override; \
void Assign(const InstructionBase& other) override; \
void TypeInstruction(Stack<const Type*>* stack, ControlFlowGraph* cfg) \
const override; \
void RecomputeDefinitionLocations(Stack<DefinitionLocation>* locations, \
Worklist<Block*>* 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<const Block*>(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<const InstructionBase*>(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<int>(kind_) < static_cast<int>(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<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;
virtual void RecomputeDefinitionLocations(
Stack<DefinitionLocation>* locations,
Worklist<Block*>* worklist) 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);
}
void RecomputeDefinitionLocations(Stack<DefinitionLocation>* locations,
Worklist<Block*>* worklist) const {
instruction_->RecomputeDefinitionLocations(locations, worklist);
}
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) {}
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<std::string> 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<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);
}
base::Optional<DefinitionLocation> GetExceptionObjectDefinition() const;
std::size_t GetValueDefinitionCount() const;
DefinitionLocation GetValueDefinition(std::size_t index) const;
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);
}
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<DefinitionLocation> GetExceptionObjectDefinition() const;
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);
}
std::size_t GetValueDefinitionCount() const;
DefinitionLocation GetValueDefinition(std::size_t index) const;
base::Optional<DefinitionLocation> GetExceptionObjectDefinition() const;
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) {}
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<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);
}
std::size_t GetValueDefinitionCount() const;
DefinitionLocation GetValueDefinition(std::size_t index) const;
base::Optional<DefinitionLocation> GetExceptionObjectDefinition() const;
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) {}
DefinitionLocation GetValueDefinition() const;
const Type* destination_type;
};
} // namespace torque
} // namespace internal
} // namespace v8
#endif // V8_TORQUE_INSTRUCTIONS_H_