inst.h 19 KB

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