inst.h 17 KB

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