inst.h 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #ifndef CARBON_TOOLCHAIN_SEM_IR_INST_H_
  5. #define CARBON_TOOLCHAIN_SEM_IR_INST_H_
  6. #include <cstdint>
  7. #include "common/check.h"
  8. #include "common/ostream.h"
  9. #include "common/struct_reflection.h"
  10. #include "toolchain/base/index_base.h"
  11. #include "toolchain/parse/tree.h"
  12. #include "toolchain/sem_ir/builtin_kind.h"
  13. #include "toolchain/sem_ir/inst_kind.h"
  14. #include "toolchain/sem_ir/typed_insts.h"
  15. namespace Carbon::SemIR {
  16. // Data about the arguments of a typed instruction, to aid in type erasure. The
  17. // `KindT` parameter is used to check that `TypedInst` is a typed instruction.
  18. template <typename TypedInst,
  19. const InstKind::Definition& KindT = TypedInst::Kind>
  20. struct TypedInstArgsInfo {
  21. // A corresponding std::tuple<...> type.
  22. using Tuple = decltype(StructReflection::AsTuple(std::declval<TypedInst>()));
  23. static constexpr int FirstArgField =
  24. HasParseNode<TypedInst> + HasTypeId<TypedInst>;
  25. static constexpr int NumArgs = std::tuple_size_v<Tuple> - FirstArgField;
  26. static_assert(NumArgs <= 2,
  27. "Unsupported: typed inst has more than two data fields");
  28. template <int N>
  29. using ArgType = std::tuple_element_t<FirstArgField + N, Tuple>;
  30. template <int N>
  31. static auto Get(TypedInst inst) -> ArgType<N> {
  32. return std::get<FirstArgField + N>(StructReflection::AsTuple(inst));
  33. }
  34. };
  35. // A type-erased representation of a SemIR instruction, that may be constructed
  36. // from the specific kinds of instruction defined in `typed_insts.h`. This
  37. // provides access to common fields present on most or all kinds of
  38. // instructions:
  39. //
  40. // - `parse_node` for error placement.
  41. // - `kind` for run-time logic when the input Kind is unknown.
  42. // - `type_id` for quick type checking.
  43. //
  44. // In addition, kind-specific data can be accessed by casting to the specific
  45. // kind of instruction:
  46. //
  47. // - Use `inst.kind()` or `Is<TypedInst>` to determine what kind of instruction
  48. // it is.
  49. // - Cast to a specific type using `inst.As<TypedInst>()`
  50. // - Using the wrong kind in `inst.As<TypedInst>()` is a programming error,
  51. // and will CHECK-fail in debug modes (opt may too, but it's not an API
  52. // guarantee).
  53. // - Use `inst.TryAs<TypedInst>()` to safely access type-specific instruction
  54. // data
  55. // where the instruction's kind is not known.
  56. class Inst : public Printable<Inst> {
  57. public:
  58. template <typename TypedInst, typename Info = TypedInstArgsInfo<TypedInst>>
  59. // NOLINTNEXTLINE(google-explicit-constructor)
  60. Inst(TypedInst typed_inst)
  61. : parse_node_(Parse::NodeId::Invalid),
  62. kind_(TypedInst::Kind),
  63. type_id_(TypeId::Invalid),
  64. arg0_(InstId::InvalidIndex),
  65. arg1_(InstId::InvalidIndex) {
  66. if constexpr (HasParseNode<TypedInst>) {
  67. parse_node_ = typed_inst.parse_node;
  68. }
  69. if constexpr (HasTypeId<TypedInst>) {
  70. type_id_ = typed_inst.type_id;
  71. }
  72. if constexpr (Info::NumArgs > 0) {
  73. arg0_ = ToRaw(Info::template Get<0>(typed_inst));
  74. }
  75. if constexpr (Info::NumArgs > 1) {
  76. arg1_ = ToRaw(Info::template Get<1>(typed_inst));
  77. }
  78. }
  79. // Returns whether this instruction has the specified type.
  80. template <typename TypedInst>
  81. auto Is() const -> bool {
  82. return kind() == TypedInst::Kind;
  83. }
  84. // Casts this instruction to the given typed instruction, which must match the
  85. // instruction's kind, and returns the typed instruction.
  86. template <typename TypedInst, typename Info = TypedInstArgsInfo<TypedInst>>
  87. auto As() const -> TypedInst {
  88. CARBON_CHECK(Is<TypedInst>()) << "Casting inst of kind " << kind()
  89. << " to wrong kind " << TypedInst::Kind;
  90. auto build_with_type_id_and_args = [&](auto... type_id_and_args) {
  91. if constexpr (HasParseNode<TypedInst>) {
  92. return TypedInst{parse_node(), type_id_and_args...};
  93. } else {
  94. return TypedInst{type_id_and_args...};
  95. }
  96. };
  97. auto build_with_args = [&](auto... args) {
  98. if constexpr (HasTypeId<TypedInst>) {
  99. return build_with_type_id_and_args(type_id(), args...);
  100. } else {
  101. return build_with_type_id_and_args(args...);
  102. }
  103. };
  104. if constexpr (Info::NumArgs == 0) {
  105. return build_with_args();
  106. } else if constexpr (Info::NumArgs == 1) {
  107. return build_with_args(
  108. FromRaw<typename Info::template ArgType<0>>(arg0_));
  109. } else if constexpr (Info::NumArgs == 2) {
  110. return build_with_args(
  111. FromRaw<typename Info::template ArgType<0>>(arg0_),
  112. FromRaw<typename Info::template ArgType<1>>(arg1_));
  113. }
  114. }
  115. // If this instruction is the given kind, returns a typed instruction,
  116. // otherwise returns nullopt.
  117. template <typename TypedInst>
  118. auto TryAs() const -> std::optional<TypedInst> {
  119. if (Is<TypedInst>()) {
  120. return As<TypedInst>();
  121. } else {
  122. return std::nullopt;
  123. }
  124. }
  125. auto parse_node() const -> Parse::NodeId { return parse_node_; }
  126. auto kind() const -> InstKind { return kind_; }
  127. // Gets the type of the value produced by evaluating this instruction.
  128. auto type_id() const -> TypeId { return type_id_; }
  129. auto Print(llvm::raw_ostream& out) const -> void;
  130. private:
  131. friend class InstTestHelper;
  132. // Raw constructor, used for testing.
  133. explicit Inst(InstKind kind, Parse::NodeId parse_node, TypeId type_id,
  134. int32_t arg0, int32_t arg1)
  135. : parse_node_(parse_node),
  136. kind_(kind),
  137. type_id_(type_id),
  138. arg0_(arg0),
  139. arg1_(arg1) {}
  140. // Convert a field to its raw representation, used as `arg0_` / `arg1_`.
  141. static constexpr auto ToRaw(IdBase base) -> int32_t { return base.index; }
  142. static constexpr auto ToRaw(BuiltinKind kind) -> int32_t {
  143. return kind.AsInt();
  144. }
  145. // Convert a field from its raw representation.
  146. template <typename T>
  147. static constexpr auto FromRaw(int32_t raw) -> T {
  148. return T(raw);
  149. }
  150. template <>
  151. constexpr auto FromRaw<BuiltinKind>(int32_t raw) -> BuiltinKind {
  152. return BuiltinKind::FromInt(raw);
  153. }
  154. Parse::NodeId parse_node_;
  155. InstKind kind_;
  156. TypeId type_id_;
  157. // Use `As` to access arg0 and arg1.
  158. int32_t arg0_;
  159. int32_t arg1_;
  160. };
  161. // TODO: This is currently 20 bytes because we sometimes have 2 arguments for a
  162. // pair of Insts. However, InstKind is 1 byte; if args
  163. // were 3.5 bytes, we could potentially shrink Inst by 4 bytes. This
  164. // may be worth investigating further.
  165. static_assert(sizeof(Inst) == 20, "Unexpected Inst size");
  166. // Typed instructions can be printed by converting them to instructions.
  167. template <typename TypedInst, typename = TypedInstArgsInfo<TypedInst>>
  168. inline auto operator<<(llvm::raw_ostream& out, TypedInst inst)
  169. -> llvm::raw_ostream& {
  170. Inst(inst).Print(out);
  171. return out;
  172. }
  173. } // namespace Carbon::SemIR
  174. #endif // CARBON_TOOLCHAIN_SEM_IR_INST_H_