inst.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  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 <concepts>
  7. #include <cstdint>
  8. #include "common/check.h"
  9. #include "common/ostream.h"
  10. #include "common/struct_reflection.h"
  11. #include "toolchain/base/index_base.h"
  12. #include "toolchain/sem_ir/block_value_store.h"
  13. #include "toolchain/sem_ir/builtin_kind.h"
  14. #include "toolchain/sem_ir/id_kind.h"
  15. #include "toolchain/sem_ir/inst_kind.h"
  16. #include "toolchain/sem_ir/typed_insts.h"
  17. namespace Carbon::SemIR {
  18. // InstLikeTypeInfo is an implementation detail, and not public API.
  19. namespace Internal {
  20. // Information about an instruction-like type, which is a type that an Inst can
  21. // be converted to and from. The `Enabled` parameter is used to check
  22. // requirements on the type in the specializations of this template.
  23. template <typename InstLikeType>
  24. struct InstLikeTypeInfo;
  25. // A helper base class for instruction-like types that are structs.
  26. template <typename InstLikeType>
  27. struct InstLikeTypeInfoBase {
  28. // A corresponding std::tuple<...> type.
  29. using Tuple =
  30. decltype(StructReflection::AsTuple(std::declval<InstLikeType>()));
  31. static constexpr int FirstArgField =
  32. HasKindMemberAsField<InstLikeType> + HasTypeIdMember<InstLikeType>;
  33. static constexpr int NumArgs = std::tuple_size_v<Tuple> - FirstArgField;
  34. static_assert(NumArgs <= 2,
  35. "Unsupported: typed inst has more than two data fields");
  36. template <int N>
  37. using ArgType = std::tuple_element_t<FirstArgField + N, Tuple>;
  38. template <int N>
  39. static auto Get(InstLikeType inst) -> ArgType<N> {
  40. return std::get<FirstArgField + N>(StructReflection::AsTuple(inst));
  41. }
  42. };
  43. // A particular type of instruction is instruction-like.
  44. template <typename TypedInst>
  45. requires std::same_as<const InstKind::Definition<
  46. typename decltype(TypedInst::Kind)::TypedNodeId>,
  47. decltype(TypedInst::Kind)>
  48. struct InstLikeTypeInfo<TypedInst> : InstLikeTypeInfoBase<TypedInst> {
  49. static_assert(!HasKindMemberAsField<TypedInst>,
  50. "Instruction type should not have a kind field");
  51. static auto GetKind(TypedInst /*inst*/) -> InstKind {
  52. return TypedInst::Kind;
  53. }
  54. static auto IsKind(InstKind kind) -> bool { return kind == TypedInst::Kind; }
  55. // A name that can be streamed to an llvm::raw_ostream.
  56. static auto DebugName() -> InstKind { return TypedInst::Kind; }
  57. };
  58. // An instruction category is instruction-like.
  59. template <typename InstCat>
  60. requires std::same_as<const InstKind&, decltype(InstCat::Kinds[0])>
  61. struct InstLikeTypeInfo<InstCat> : InstLikeTypeInfoBase<InstCat> {
  62. static_assert(HasKindMemberAsField<InstCat>,
  63. "Instruction category should have a kind field");
  64. static auto GetKind(InstCat cat) -> InstKind { return cat.kind; }
  65. static auto IsKind(InstKind kind) -> bool {
  66. for (InstKind k : InstCat::Kinds) {
  67. if (k == kind) {
  68. return true;
  69. }
  70. }
  71. return false;
  72. }
  73. // A name that can be streamed to an llvm::raw_ostream.
  74. static auto DebugName() -> std::string {
  75. std::string str;
  76. llvm::raw_string_ostream out(str);
  77. out << "{";
  78. llvm::ListSeparator sep;
  79. for (auto kind : InstCat::Kinds) {
  80. out << sep << kind;
  81. }
  82. out << "}";
  83. return out.str();
  84. }
  85. };
  86. // A type is InstLike if InstLikeTypeInfo is defined for it.
  87. template <typename T>
  88. concept InstLikeType = requires { sizeof(InstLikeTypeInfo<T>); };
  89. } // namespace Internal
  90. // A type-erased representation of a SemIR instruction, that may be constructed
  91. // from the specific kinds of instruction defined in `typed_insts.h`. This
  92. // provides access to common fields present on most or all kinds of
  93. // instructions:
  94. //
  95. // - `kind` for run-time logic when the input Kind is unknown.
  96. // - `type_id` for quick type checking.
  97. //
  98. // In addition, kind-specific data can be accessed by casting to the specific
  99. // kind of instruction:
  100. //
  101. // - Use `inst.kind()` or `Is<InstLikeType>` to determine what kind of
  102. // instruction it is.
  103. // - Cast to a specific type using `inst.As<InstLikeType>()`
  104. // - Using the wrong kind in `inst.As<InstLikeType>()` is a programming error,
  105. // and will CHECK-fail in debug modes (opt may too, but it's not an API
  106. // guarantee).
  107. // - Use `inst.TryAs<InstLikeType>()` to safely access type-specific instruction
  108. // data where the instruction's kind is not known.
  109. class Inst : public Printable<Inst> {
  110. public:
  111. template <typename TypedInst>
  112. requires Internal::InstLikeType<TypedInst>
  113. // NOLINTNEXTLINE(google-explicit-constructor)
  114. Inst(TypedInst typed_inst)
  115. // kind_ is always overwritten below.
  116. : kind_(InstKind::Make({})),
  117. type_id_(TypeId::Invalid),
  118. arg0_(InstId::InvalidIndex),
  119. arg1_(InstId::InvalidIndex) {
  120. if constexpr (Internal::HasKindMemberAsField<TypedInst>) {
  121. kind_ = typed_inst.kind;
  122. } else {
  123. kind_ = TypedInst::Kind;
  124. }
  125. if constexpr (Internal::HasTypeIdMember<TypedInst>) {
  126. type_id_ = typed_inst.type_id;
  127. }
  128. using Info = Internal::InstLikeTypeInfo<TypedInst>;
  129. if constexpr (Info::NumArgs > 0) {
  130. arg0_ = ToRaw(Info::template Get<0>(typed_inst));
  131. }
  132. if constexpr (Info::NumArgs > 1) {
  133. arg1_ = ToRaw(Info::template Get<1>(typed_inst));
  134. }
  135. }
  136. // Returns whether this instruction has the specified type.
  137. template <typename TypedInst>
  138. requires Internal::InstLikeType<TypedInst>
  139. auto Is() const -> bool {
  140. return Internal::InstLikeTypeInfo<TypedInst>::IsKind(kind());
  141. }
  142. // Casts this instruction to the given typed instruction, which must match the
  143. // instruction's kind, and returns the typed instruction.
  144. template <typename TypedInst>
  145. requires Internal::InstLikeType<TypedInst>
  146. auto As() const -> TypedInst {
  147. using Info = Internal::InstLikeTypeInfo<TypedInst>;
  148. CARBON_CHECK(Is<TypedInst>()) << "Casting inst of kind " << kind()
  149. << " to wrong kind " << Info::DebugName();
  150. auto build_with_type_id_onwards = [&](auto... type_id_onwards) {
  151. if constexpr (Internal::HasKindMemberAsField<TypedInst>) {
  152. return TypedInst{kind(), type_id_onwards...};
  153. } else {
  154. return TypedInst{type_id_onwards...};
  155. }
  156. };
  157. auto build_with_args = [&](auto... args) {
  158. if constexpr (Internal::HasTypeIdMember<TypedInst>) {
  159. return build_with_type_id_onwards(type_id(), args...);
  160. } else {
  161. return build_with_type_id_onwards(args...);
  162. }
  163. };
  164. if constexpr (Info::NumArgs == 0) {
  165. return build_with_args();
  166. } else if constexpr (Info::NumArgs == 1) {
  167. return build_with_args(
  168. FromRaw<typename Info::template ArgType<0>>(arg0_));
  169. } else if constexpr (Info::NumArgs == 2) {
  170. return build_with_args(
  171. FromRaw<typename Info::template ArgType<0>>(arg0_),
  172. FromRaw<typename Info::template ArgType<1>>(arg1_));
  173. }
  174. }
  175. // If this instruction is the given kind, returns a typed instruction,
  176. // otherwise returns nullopt.
  177. template <typename TypedInst>
  178. requires Internal::InstLikeType<TypedInst>
  179. auto TryAs() const -> std::optional<TypedInst> {
  180. if (Is<TypedInst>()) {
  181. return As<TypedInst>();
  182. } else {
  183. return std::nullopt;
  184. }
  185. }
  186. auto kind() const -> InstKind { return kind_; }
  187. // Gets the type of the value produced by evaluating this instruction.
  188. auto type_id() const -> TypeId { return type_id_; }
  189. // Gets the kinds of IDs used for arg0 and arg1 of the specified kind of
  190. // instruction.
  191. //
  192. // TODO: This would ideally live on InstKind, but can't be there for layering
  193. // reasons.
  194. static auto ArgKinds(InstKind kind) -> std::pair<IdKind, IdKind> {
  195. return ArgKindTable[kind.AsInt()];
  196. }
  197. // Gets the kinds of IDs used for arg0 and arg1 of this instruction.
  198. auto ArgKinds() const -> std::pair<IdKind, IdKind> { return ArgKinds(kind_); }
  199. // Gets the first argument of the instruction. InvalidIndex if there is no
  200. // such argument.
  201. auto arg0() const -> int32_t { return arg0_; }
  202. // Gets the second argument of the instruction. InvalidIndex if there is no
  203. // such argument.
  204. auto arg1() const -> int32_t { return arg1_; }
  205. // Sets the arguments of this instruction.
  206. auto SetArgs(int32_t arg0, int32_t arg1) {
  207. arg0_ = arg0;
  208. arg1_ = arg1;
  209. }
  210. auto Print(llvm::raw_ostream& out) const -> void;
  211. private:
  212. friend class InstTestHelper;
  213. // Table mapping instruction kinds to their argument kinds.
  214. static const std::pair<IdKind, IdKind> ArgKindTable[];
  215. // Raw constructor, used for testing.
  216. explicit Inst(InstKind kind, TypeId type_id, int32_t arg0, int32_t arg1)
  217. : kind_(kind), type_id_(type_id), arg0_(arg0), arg1_(arg1) {}
  218. // Convert a field to its raw representation, used as `arg0_` / `arg1_`.
  219. static constexpr auto ToRaw(IdBase base) -> int32_t { return base.index; }
  220. static constexpr auto ToRaw(BuiltinKind kind) -> int32_t {
  221. return kind.AsInt();
  222. }
  223. // Convert a field from its raw representation.
  224. template <typename T>
  225. static constexpr auto FromRaw(int32_t raw) -> T {
  226. return T(raw);
  227. }
  228. template <>
  229. constexpr auto FromRaw<BuiltinKind>(int32_t raw) -> BuiltinKind {
  230. return BuiltinKind::FromInt(raw);
  231. }
  232. InstKind kind_;
  233. TypeId type_id_;
  234. // Use `As` to access arg0 and arg1.
  235. int32_t arg0_;
  236. int32_t arg1_;
  237. };
  238. // TODO: This is currently 16 bytes because we sometimes have 2 arguments for a
  239. // pair of Insts. However, InstKind is 1 byte; if args were 3.5 bytes, we could
  240. // potentially shrink Inst by 4 bytes. This may be worth investigating further.
  241. // Note though that 16 bytes is an ideal size for registers, we may want more
  242. // flags, and 12 bytes would be a more marginal improvement.
  243. static_assert(sizeof(Inst) == 16, "Unexpected Inst size");
  244. // Instruction-like types can be printed by converting them to instructions.
  245. template <typename TypedInst>
  246. requires Internal::InstLikeType<TypedInst>
  247. inline auto operator<<(llvm::raw_ostream& out, TypedInst inst)
  248. -> llvm::raw_ostream& {
  249. Inst(inst).Print(out);
  250. return out;
  251. }
  252. // Associates a NodeId and Inst in order to provide type-checking that the
  253. // TypedNodeId corresponds to the InstT.
  254. struct NodeIdAndInst {
  255. // In cases where the NodeId is untyped or the InstT is unknown, the check
  256. // can't be done at compile time.
  257. // TODO: Consider runtime validation that InstT::Kind::TypedNodeId
  258. // corresponds.
  259. static auto Untyped(Parse::NodeId node_id, Inst inst) -> NodeIdAndInst {
  260. return NodeIdAndInst(node_id, inst, /*is_untyped=*/true);
  261. }
  262. // For the common case, support construction as:
  263. // context.AddInst({node_id, SemIR::MyInst{...}});
  264. template <typename InstT>
  265. requires(Internal::HasNodeId<InstT>)
  266. // NOLINTNEXTLINE(google-explicit-constructor)
  267. NodeIdAndInst(decltype(InstT::Kind)::TypedNodeId node_id, InstT inst)
  268. : node_id(node_id), inst(inst) {}
  269. // For cases with no parse node, support construction as:
  270. // context.AddInst({SemIR::MyInst{...}});
  271. template <typename InstT>
  272. requires(!Internal::HasNodeId<InstT>)
  273. // NOLINTNEXTLINE(google-explicit-constructor)
  274. NodeIdAndInst(InstT inst) : node_id(Parse::NodeId::Invalid), inst(inst) {}
  275. Parse::NodeId node_id;
  276. Inst inst;
  277. private:
  278. explicit NodeIdAndInst(Parse::NodeId node_id, Inst inst, bool /*is_untyped*/)
  279. : node_id(node_id), inst(inst) {}
  280. };
  281. // Provides a ValueStore wrapper for an API specific to instructions.
  282. class InstStore {
  283. public:
  284. // Adds an instruction to the instruction list, returning an ID to reference
  285. // the instruction. Note that this doesn't add the instruction to any
  286. // instruction block. Check::Context::AddInst or InstBlockStack::AddInst
  287. // should usually be used instead, to add the instruction to the current
  288. // block.
  289. auto AddInNoBlock(NodeIdAndInst node_id_and_inst) -> InstId {
  290. node_ids_.push_back(node_id_and_inst.node_id);
  291. return values_.Add(node_id_and_inst.inst);
  292. }
  293. // Returns the requested instruction.
  294. auto Get(InstId inst_id) const -> Inst { return values_.Get(inst_id); }
  295. // Returns the requested instruction and its parse node.
  296. auto GetWithNodeId(InstId inst_id) const -> NodeIdAndInst {
  297. return NodeIdAndInst::Untyped(GetNodeId(inst_id), Get(inst_id));
  298. }
  299. // Returns whether the requested instruction is the specified type.
  300. template <typename InstT>
  301. auto Is(InstId inst_id) const -> bool {
  302. return Get(inst_id).Is<InstT>();
  303. }
  304. // Returns the requested instruction, which is known to have the specified
  305. // type.
  306. template <typename InstT>
  307. auto GetAs(InstId inst_id) const -> InstT {
  308. return Get(inst_id).As<InstT>();
  309. }
  310. // Returns the requested instruction as the specified type, if it is of that
  311. // type.
  312. template <typename InstT>
  313. auto TryGetAs(InstId inst_id) const -> std::optional<InstT> {
  314. return Get(inst_id).TryAs<InstT>();
  315. }
  316. // Returns the requested instruction as the specified type, if it is valid and
  317. // of that type. Otherwise returns nullopt.
  318. template <typename InstT>
  319. auto TryGetAsIfValid(InstId inst_id) const -> std::optional<InstT> {
  320. if (!inst_id.is_valid()) {
  321. return std::nullopt;
  322. }
  323. return TryGetAs<InstT>(inst_id);
  324. }
  325. auto GetNodeId(InstId inst_id) const -> Parse::NodeId {
  326. return node_ids_[inst_id.index];
  327. }
  328. // Overwrites a given instruction and parse node with a new value.
  329. auto Set(InstId inst_id, NodeIdAndInst node_id_and_inst) -> void {
  330. values_.Get(inst_id) = node_id_and_inst.inst;
  331. node_ids_[inst_id.index] = node_id_and_inst.node_id;
  332. }
  333. auto SetNodeId(InstId inst_id, Parse::NodeId node_id) -> void {
  334. node_ids_[inst_id.index] = node_id;
  335. }
  336. // Reserves space.
  337. auto Reserve(size_t size) -> void {
  338. node_ids_.reserve(size);
  339. values_.Reserve(size);
  340. }
  341. auto array_ref() const -> llvm::ArrayRef<Inst> { return values_.array_ref(); }
  342. auto size() const -> int { return values_.size(); }
  343. private:
  344. llvm::SmallVector<Parse::NodeId> node_ids_;
  345. ValueStore<InstId> values_;
  346. };
  347. // Adapts BlockValueStore for instruction blocks.
  348. class InstBlockStore : public BlockValueStore<InstBlockId> {
  349. public:
  350. using BaseType = BlockValueStore<InstBlockId>;
  351. using BaseType::AddDefaultValue;
  352. using BaseType::AddUninitialized;
  353. explicit InstBlockStore(llvm::BumpPtrAllocator& allocator)
  354. : BaseType(allocator) {
  355. auto empty_id = AddDefaultValue();
  356. CARBON_CHECK(empty_id == InstBlockId::Empty);
  357. auto exports_id = AddDefaultValue();
  358. CARBON_CHECK(exports_id == InstBlockId::Exports);
  359. auto global_init_id = AddDefaultValue();
  360. CARBON_CHECK(global_init_id == InstBlockId::GlobalInit);
  361. }
  362. auto Set(InstBlockId block_id, llvm::ArrayRef<InstId> content) -> void {
  363. CARBON_CHECK(block_id != InstBlockId::Unreachable);
  364. BlockValueStore<InstBlockId>::Set(block_id, content);
  365. }
  366. };
  367. } // namespace Carbon::SemIR
  368. #endif // CARBON_TOOLCHAIN_SEM_IR_INST_H_