inst.h 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715
  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/block_value_store.h"
  14. #include "toolchain/base/index_base.h"
  15. #include "toolchain/base/int.h"
  16. #include "toolchain/base/value_store.h"
  17. #include "toolchain/sem_ir/id_kind.h"
  18. #include "toolchain/sem_ir/ids.h"
  19. #include "toolchain/sem_ir/inst_kind.h"
  20. #include "toolchain/sem_ir/singleton_insts.h"
  21. #include "toolchain/sem_ir/typed_insts.h"
  22. namespace Carbon::SemIR {
  23. template <typename... TypedInsts>
  24. struct CategoryOf;
  25. // InstLikeTypeInfo is an implementation detail, and not public API.
  26. namespace Internal {
  27. // Information about an instruction-like type, which is a type that an Inst can
  28. // be converted to and from.
  29. template <typename InstLikeType>
  30. struct InstLikeTypeInfo;
  31. // A helper base class for instruction-like types that are structs.
  32. template <typename InstLikeType>
  33. struct InstLikeTypeInfoBase {
  34. // A corresponding std::tuple<...> type.
  35. using Tuple =
  36. decltype(StructReflection::AsTuple(std::declval<InstLikeType>()));
  37. static constexpr int FirstArgField =
  38. HasKindMemberAsField<InstLikeType> + HasTypeIdMember<InstLikeType>;
  39. static constexpr int NumArgs = std::tuple_size_v<Tuple> - FirstArgField;
  40. static_assert(NumArgs <= 2,
  41. "Unsupported: typed inst has more than two data fields");
  42. template <int N>
  43. using ArgType = std::tuple_element_t<FirstArgField + N, Tuple>;
  44. template <int N>
  45. static auto Get(InstLikeType inst) -> ArgType<N> {
  46. return std::get<FirstArgField + N>(StructReflection::AsTuple(inst));
  47. }
  48. };
  49. // A particular type of instruction is instruction-like.
  50. template <typename TypedInst>
  51. requires std::same_as<const InstKind::Definition<
  52. typename decltype(TypedInst::Kind)::TypedNodeId>,
  53. decltype(TypedInst::Kind)>
  54. struct InstLikeTypeInfo<TypedInst> : InstLikeTypeInfoBase<TypedInst> {
  55. static_assert(!HasKindMemberAsField<TypedInst>,
  56. "Instruction type should not have a kind field");
  57. static auto GetKind(TypedInst /*inst*/) -> InstKind {
  58. return TypedInst::Kind;
  59. }
  60. static constexpr auto IsKind(InstKind kind) -> bool {
  61. return kind == TypedInst::Kind;
  62. }
  63. // A name that can be streamed to an llvm::raw_ostream.
  64. static auto DebugName() -> InstKind { return TypedInst::Kind; }
  65. };
  66. // If `TypedInst` has an Nth field, validates that `CategoryInst` has a
  67. // corresponding field with a compatible type.
  68. template <typename CategoryInst, typename TypedInst, size_t N>
  69. static consteval auto ValidateCategoryFieldForTypedInst() -> void {
  70. if constexpr (InstLikeTypeInfoBase<TypedInst>::NumArgs > N) {
  71. if constexpr (!std::is_same_v<typename InstLikeTypeInfoBase<
  72. CategoryInst>::template ArgType<N>,
  73. AnyRawId>) {
  74. static_assert(
  75. std::is_same_v<
  76. typename InstLikeTypeInfoBase<CategoryInst>::template ArgType<N>,
  77. typename InstLikeTypeInfoBase<TypedInst>::template ArgType<N>>,
  78. "Inst category field should be the same type as the "
  79. "corresponding fields of its typed insts, or AnyRawId if "
  80. "they have different types");
  81. }
  82. }
  83. }
  84. // Validates that `CategoryInst` is compatible with `TypedInst`
  85. template <typename CategoryInst, typename TypedInst>
  86. static consteval auto ValidateCategoryForTypedInst() -> void {
  87. static_assert(Internal::HasKindMemberAsField<CategoryInst>,
  88. "Inst category should have an `InstKind` field");
  89. static_assert(!HasTypeIdMember<TypedInst> || HasTypeIdMember<CategoryInst>,
  90. "Inst category should have a `TypeId` field if any of its "
  91. "typed insts do");
  92. static_assert(InstLikeTypeInfoBase<CategoryInst>::NumArgs >=
  93. InstLikeTypeInfoBase<TypedInst>::NumArgs,
  94. "Inst category should have as many fields as any of its typed "
  95. "insts");
  96. ValidateCategoryFieldForTypedInst<CategoryInst, TypedInst, 0>();
  97. ValidateCategoryFieldForTypedInst<CategoryInst, TypedInst, 1>();
  98. }
  99. // Validates that `CategoryInst` is compatible with all of `TypedInsts`.
  100. // Always returns true; validation failure will cause build errors when
  101. // instantiating the function.
  102. template <typename CategoryInst, typename... TypedInsts>
  103. static consteval auto ValidateCategory(
  104. CategoryOf<TypedInsts...> /*category_info*/) -> bool {
  105. (ValidateCategoryForTypedInst<CategoryInst, TypedInsts>(), ...);
  106. return true;
  107. }
  108. // An instruction category is instruction-like.
  109. template <typename InstCat>
  110. requires requires { typename InstCat::CategoryInfo; }
  111. struct InstLikeTypeInfo<InstCat> : InstLikeTypeInfoBase<InstCat> {
  112. static auto GetKind(InstCat cat) -> InstKind { return cat.kind; }
  113. static constexpr auto IsKind(InstKind kind) -> bool {
  114. for (InstKind k : InstCat::CategoryInfo::Kinds) {
  115. if (k == kind) {
  116. return true;
  117. }
  118. }
  119. return false;
  120. }
  121. // A name that can be streamed to an llvm::raw_ostream.
  122. static auto DebugName() -> std::string {
  123. RawStringOstream out;
  124. out << "{";
  125. llvm::ListSeparator sep;
  126. for (auto kind : InstCat::CategoryInfo::Kinds) {
  127. out << sep << kind;
  128. }
  129. out << "}";
  130. return out.TakeStr();
  131. }
  132. private:
  133. // Trigger validation of `InstCat`.
  134. static_assert(ValidateCategory<InstCat>(typename InstCat::CategoryInfo()));
  135. };
  136. // HasInstCategory is true if T::Kind is an element of InstCat::Kinds.
  137. template <typename InstCat, typename T>
  138. concept HasInstCategory = InstLikeTypeInfo<InstCat>::IsKind(T::Kind);
  139. // A type is InstLike if InstLikeTypeInfo is defined for it.
  140. template <typename T>
  141. concept InstLikeType = requires { sizeof(InstLikeTypeInfo<T>); };
  142. } // namespace Internal
  143. // A type-erased representation of a SemIR instruction, that may be constructed
  144. // from the specific kinds of instruction defined in `typed_insts.h`. This
  145. // provides access to common fields present on most or all kinds of
  146. // instructions:
  147. //
  148. // - `kind` for run-time logic when the input Kind is unknown.
  149. // - `type_id` for quick type checking.
  150. //
  151. // In addition, kind-specific data can be accessed by casting to the specific
  152. // kind of instruction:
  153. //
  154. // - Use `inst.kind()` or `Is<InstLikeType>` to determine what kind of
  155. // instruction it is.
  156. // - Cast to a specific type using `inst.As<InstLikeType>()`
  157. // - Using the wrong kind in `inst.As<InstLikeType>()` is a programming error,
  158. // and will CHECK-fail in debug modes (opt may too, but it's not an API
  159. // guarantee).
  160. // - Use `inst.TryAs<InstLikeType>()` to safely access type-specific instruction
  161. // data where the instruction's kind is not known.
  162. class Inst : public Printable<Inst> {
  163. public:
  164. // Associates an argument (arg0 or arg1) with its IdKind.
  165. class ArgAndKind {
  166. public:
  167. explicit ArgAndKind(IdKind kind, int32_t value)
  168. : kind_(kind), value_(value) {}
  169. // Converts to `IdT`, validating the `kind` matches.
  170. template <typename IdT>
  171. auto As() const -> IdT {
  172. CARBON_DCHECK(kind_ == IdKind::For<IdT>);
  173. return IdT(value_);
  174. }
  175. // Converts to `IdT`, returning nullopt if the kind is incorrect.
  176. template <typename IdT>
  177. auto TryAs() const -> std::optional<IdT> {
  178. if (kind_ != IdKind::For<IdT>) {
  179. return std::nullopt;
  180. }
  181. return IdT(value_);
  182. }
  183. auto kind() const -> IdKind { return kind_; }
  184. auto value() const -> int32_t { return value_; }
  185. private:
  186. IdKind kind_;
  187. int32_t value_;
  188. };
  189. // Makes an instruction for a singleton. This exists to support simple
  190. // construction of all singletons by File.
  191. static auto MakeSingleton(InstKind kind) -> Inst {
  192. CARBON_CHECK(IsSingletonInstKind(kind));
  193. // Error uses a self-referential type so that it's not accidentally treated
  194. // as a normal type. Every other builtin is a type, including the
  195. // self-referential TypeType.
  196. auto type_id =
  197. kind == InstKind::ErrorInst ? ErrorInst::TypeId : TypeType::TypeId;
  198. return Inst(kind, type_id, InstId::NoneIndex, InstId::NoneIndex);
  199. }
  200. template <typename TypedInst>
  201. requires Internal::InstLikeType<TypedInst>
  202. // NOLINTNEXTLINE(google-explicit-constructor)
  203. Inst(TypedInst typed_inst)
  204. // kind_ is always overwritten below.
  205. : kind_(),
  206. type_id_(TypeId::None),
  207. arg0_(InstId::NoneIndex),
  208. arg1_(InstId::NoneIndex) {
  209. if constexpr (Internal::HasKindMemberAsField<TypedInst>) {
  210. kind_ = typed_inst.kind.AsInt();
  211. } else {
  212. kind_ = TypedInst::Kind.AsInt();
  213. }
  214. if constexpr (Internal::HasTypeIdMember<TypedInst>) {
  215. type_id_ = typed_inst.type_id;
  216. }
  217. using Info = Internal::InstLikeTypeInfo<TypedInst>;
  218. if constexpr (Info::NumArgs > 0) {
  219. arg0_ = ToRaw(Info::template Get<0>(typed_inst));
  220. }
  221. if constexpr (Info::NumArgs > 1) {
  222. arg1_ = ToRaw(Info::template Get<1>(typed_inst));
  223. }
  224. }
  225. // Returns whether this instruction has the specified type.
  226. template <typename TypedInst>
  227. requires Internal::InstLikeType<TypedInst>
  228. auto Is() const -> bool {
  229. return Internal::InstLikeTypeInfo<TypedInst>::IsKind(kind());
  230. }
  231. // Casts this instruction to the given typed instruction, which must match the
  232. // instruction's kind, and returns the typed instruction.
  233. template <typename TypedInst>
  234. requires Internal::InstLikeType<TypedInst>
  235. auto As() const -> TypedInst {
  236. using Info = Internal::InstLikeTypeInfo<TypedInst>;
  237. CARBON_CHECK(Is<TypedInst>(), "Casting inst {0} to wrong kind {1}", *this,
  238. Info::DebugName());
  239. auto build_with_type_id_onwards = [&](auto... type_id_onwards) {
  240. if constexpr (Internal::HasKindMemberAsField<TypedInst>) {
  241. return TypedInst{kind(), type_id_onwards...};
  242. } else {
  243. return TypedInst{type_id_onwards...};
  244. }
  245. };
  246. auto build_with_args = [&](auto... args) {
  247. if constexpr (Internal::HasTypeIdMember<TypedInst>) {
  248. return build_with_type_id_onwards(type_id(), args...);
  249. } else {
  250. return build_with_type_id_onwards(args...);
  251. }
  252. };
  253. if constexpr (Info::NumArgs == 0) {
  254. return build_with_args();
  255. } else if constexpr (Info::NumArgs == 1) {
  256. return build_with_args(
  257. FromRaw<typename Info::template ArgType<0>>(arg0_));
  258. } else if constexpr (Info::NumArgs == 2) {
  259. return build_with_args(
  260. FromRaw<typename Info::template ArgType<0>>(arg0_),
  261. FromRaw<typename Info::template ArgType<1>>(arg1_));
  262. }
  263. }
  264. // If this instruction is the given kind, returns a typed instruction,
  265. // otherwise returns nullopt.
  266. template <typename TypedInst>
  267. requires Internal::InstLikeType<TypedInst>
  268. auto TryAs() const -> std::optional<TypedInst> {
  269. if (Is<TypedInst>()) {
  270. return As<TypedInst>();
  271. } else {
  272. return std::nullopt;
  273. }
  274. }
  275. auto kind() const -> InstKind { return InstKind::FromInt(kind_); }
  276. // Gets the type of the value produced by evaluating this instruction.
  277. auto type_id() const -> TypeId { return type_id_; }
  278. // Gets the first argument of the instruction. NoneIndex if there is no such
  279. // argument.
  280. auto arg0() const -> int32_t { return arg0_; }
  281. // Gets the second argument of the instruction. NoneIndex if there is no such
  282. // argument.
  283. auto arg1() const -> int32_t { return arg1_; }
  284. // Returns arguments with their IdKind.
  285. auto arg0_and_kind() const -> ArgAndKind {
  286. return ArgAndKind(ArgKindTable[kind_].first, arg0_);
  287. }
  288. auto arg1_and_kind() const -> ArgAndKind {
  289. return ArgAndKind(ArgKindTable[kind_].second, arg1_);
  290. }
  291. // Sets the type of this instruction.
  292. auto SetType(TypeId type_id) -> void { type_id_ = type_id; }
  293. // Sets the arguments of this instruction.
  294. auto SetArgs(int32_t arg0, int32_t arg1) -> void {
  295. arg0_ = arg0;
  296. arg1_ = arg1;
  297. }
  298. // Convert a field to its raw representation, used as `arg0_` / `arg1_`.
  299. static constexpr auto ToRaw(AnyIdBase base) -> int32_t { return base.index; }
  300. static constexpr auto ToRaw(IntId id) -> int32_t { return id.AsRaw(); }
  301. // Convert a field from its raw representation.
  302. template <typename T>
  303. requires IdKind::Contains<T>
  304. static constexpr auto FromRaw(int32_t raw) -> T {
  305. return T(raw);
  306. }
  307. template <>
  308. constexpr auto FromRaw<IntId>(int32_t raw) -> IntId {
  309. return IntId::MakeRaw(raw);
  310. }
  311. auto Print(llvm::raw_ostream& out) const -> void;
  312. friend auto operator==(Inst lhs, Inst rhs) -> bool {
  313. return std::memcmp(&lhs, &rhs, sizeof(Inst)) == 0;
  314. }
  315. private:
  316. friend class InstTestHelper;
  317. // Table mapping instruction kinds to their argument kinds.
  318. //
  319. // TODO: ArgKindTable would ideally live on InstKind, but can't be there for
  320. // layering reasons.
  321. static const std::pair<IdKind, IdKind> ArgKindTable[];
  322. // Raw constructor, used for testing.
  323. explicit Inst(InstKind kind, TypeId type_id, int32_t arg0, int32_t arg1)
  324. : Inst(kind.AsInt(), type_id, arg0, arg1) {}
  325. explicit constexpr Inst(int32_t kind, TypeId type_id, int32_t arg0,
  326. int32_t arg1)
  327. : kind_(kind), type_id_(type_id), arg0_(arg0), arg1_(arg1) {}
  328. int32_t kind_;
  329. TypeId type_id_;
  330. // Use `As` to access arg0 and arg1.
  331. int32_t arg0_;
  332. int32_t arg1_;
  333. };
  334. // TODO: This is currently 16 bytes because we sometimes have 2 arguments for a
  335. // pair of Insts. However, InstKind is 1 byte; if args were 3.5 bytes, we could
  336. // potentially shrink Inst by 4 bytes. This may be worth investigating further.
  337. // Note though that 16 bytes is an ideal size for registers, we may want more
  338. // flags, and 12 bytes would be a more marginal improvement.
  339. static_assert(sizeof(Inst) == 16, "Unexpected Inst size");
  340. // Instruction-like types can be printed by converting them to instructions.
  341. template <typename TypedInst>
  342. requires Internal::InstLikeType<TypedInst>
  343. inline auto operator<<(llvm::raw_ostream& out, TypedInst inst)
  344. -> llvm::raw_ostream& {
  345. Inst(inst).Print(out);
  346. return out;
  347. }
  348. // Associates a LocId and Inst in order to provide type-checking that the
  349. // TypedNodeId corresponds to the InstT.
  350. struct LocIdAndInst {
  351. // Constructs a LocIdAndInst with no associated location. This should be used
  352. // very sparingly: only when it doesn't make sense to store a location even
  353. // when the instruction kind usually has one, such as for instructions in the
  354. // constants block.
  355. template <typename InstT>
  356. static auto NoLoc(InstT inst) -> LocIdAndInst {
  357. return LocIdAndInst(LocId::None, inst, /*is_unchecked=*/true);
  358. }
  359. // Unsafely form a pair of a location and an instruction. Used in the cases
  360. // where we can't statically enforce the type matches.
  361. static auto UncheckedLoc(LocId loc_id, Inst inst) -> LocIdAndInst {
  362. return LocIdAndInst(loc_id, inst, /*is_unchecked=*/true);
  363. }
  364. // Construction for the common case with a typed node.
  365. template <typename InstT>
  366. requires(Internal::HasNodeId<InstT>)
  367. LocIdAndInst(decltype(InstT::Kind)::TypedNodeId node_id, InstT inst)
  368. : loc_id(node_id), inst(inst) {}
  369. // Construction for the case where the instruction can have any associated
  370. // node.
  371. template <typename InstT>
  372. requires(Internal::HasUntypedNodeId<InstT>)
  373. LocIdAndInst(LocId loc_id, InstT inst) : loc_id(loc_id), inst(inst) {}
  374. LocId loc_id;
  375. Inst inst;
  376. private:
  377. // Note `is_unchecked` serves to disambiguate from public constructors.
  378. explicit LocIdAndInst(LocId loc_id, Inst inst, bool /*is_unchecked*/)
  379. : loc_id(loc_id), inst(inst) {}
  380. };
  381. // Provides a ValueStore wrapper for an API specific to instructions.
  382. class InstStore {
  383. public:
  384. using IdType = InstId;
  385. explicit InstStore(File* file) : file_(file) {}
  386. // Adds an instruction to the instruction list, returning an ID to reference
  387. // the instruction. Note that this doesn't add the instruction to any
  388. // instruction block. Check::Context::AddInst or InstBlockStack::AddInst
  389. // should usually be used instead, to add the instruction to the current
  390. // block.
  391. auto AddInNoBlock(LocIdAndInst loc_id_and_inst) -> InstId {
  392. loc_ids_.push_back(loc_id_and_inst.loc_id);
  393. return values_.Add(loc_id_and_inst.inst);
  394. }
  395. // Returns the requested instruction. The returned instruction always has an
  396. // unattached type, even if an attached type is stored for it.
  397. auto Get(InstId inst_id) const -> Inst {
  398. Inst result = values_.Get(inst_id);
  399. auto type_id = result.type_id();
  400. if (type_id.has_value() && type_id.is_symbolic()) {
  401. result.SetType(GetUnattachedType(type_id));
  402. }
  403. return result;
  404. }
  405. // Returns the requested instruction, which is known to have the specified
  406. // type.
  407. template <typename InstT>
  408. auto Get(KnownInstId<InstT> inst_id) const -> InstT {
  409. return Get(static_cast<InstId>(inst_id)).As<InstT>();
  410. }
  411. // Returns the requested instruction, preserving its attached type.
  412. auto GetWithAttachedType(InstId inst_id) const -> Inst {
  413. return values_.Get(inst_id);
  414. }
  415. // Returns the type of the instruction as an attached type.
  416. auto GetAttachedType(InstId inst_id) const -> TypeId {
  417. return GetWithAttachedType(inst_id).type_id();
  418. }
  419. // Returns the requested instruction and its location ID.
  420. auto GetWithLocId(InstId inst_id) const -> LocIdAndInst {
  421. return LocIdAndInst::UncheckedLoc(LocId(inst_id), Get(inst_id));
  422. }
  423. // Returns whether the requested instruction is the specified type.
  424. template <typename InstT>
  425. auto Is(InstId inst_id) const -> bool {
  426. return Get(inst_id).Is<InstT>();
  427. }
  428. // Returns the requested instruction, which is known to have the specified
  429. // type.
  430. template <typename InstT>
  431. auto GetAs(InstId inst_id) const -> InstT {
  432. return Get(inst_id).As<InstT>();
  433. }
  434. // Returns the requested instruction as the specified type, if it is of that
  435. // type.
  436. template <typename InstT>
  437. auto TryGetAs(InstId inst_id) const -> std::optional<InstT> {
  438. return Get(inst_id).TryAs<InstT>();
  439. }
  440. // Use `Get()` when the instruction type is known.
  441. template <typename InstT, typename KnownInstT>
  442. auto TryGetAs(KnownInstId<KnownInstT> inst_id) const = delete;
  443. // Returns the requested instruction as the specified type, if it is valid and
  444. // of that type. Otherwise returns nullopt.
  445. template <typename InstT>
  446. auto TryGetAsIfValid(InstId inst_id) const -> std::optional<InstT> {
  447. if (!inst_id.has_value()) {
  448. return std::nullopt;
  449. }
  450. return TryGetAs<InstT>(inst_id);
  451. }
  452. template <class InstT>
  453. struct GetAsWithIdResult {
  454. SemIR::KnownInstId<InstT> inst_id;
  455. InstT inst;
  456. };
  457. // Returns the requested instruction, which is known to have the specified
  458. // type, along with the original `InstId`, encoding the work of checking its
  459. // type in a `KnownInstId`.
  460. template <typename InstT>
  461. auto GetAsWithId(InstId inst_id) const -> GetAsWithIdResult<InstT> {
  462. auto inst = GetAs<InstT>(inst_id);
  463. return {.inst_id = SemIR::KnownInstId<InstT>::UnsafeMake(inst_id),
  464. .inst = inst};
  465. }
  466. // Returns the requested instruction, if it is of that type, along with the
  467. // original `InstId`, encoding the work of checking its type in a
  468. // `KnownInstId`.
  469. template <typename InstT>
  470. auto TryGetAsWithId(InstId inst_id) const
  471. -> std::optional<GetAsWithIdResult<InstT>> {
  472. auto inst = TryGetAs<InstT>(inst_id);
  473. if (!inst) {
  474. return std::nullopt;
  475. }
  476. return {{.inst_id = SemIR::KnownInstId<InstT>::UnsafeMake(inst_id),
  477. .inst = *inst}};
  478. }
  479. // Attempts to convert the given instruction to the type that contains
  480. // `member`. If it can be converted, the instruction ID and instruction are
  481. // replaced by the unwrapped value of that member, and the converted wrapper
  482. // instruction and its ID are returned. Otherwise returns {nullopt, None}.
  483. template <typename InstT, typename InstIdT>
  484. requires std::derived_from<InstIdT, InstId>
  485. auto TryUnwrap(Inst& inst, InstId& inst_id, InstIdT InstT::* member) const
  486. -> std::pair<std::optional<InstT>, InstId> {
  487. if (auto wrapped_inst = inst.TryAs<InstT>()) {
  488. auto wrapped_inst_id = inst_id;
  489. inst_id = (*wrapped_inst).*member;
  490. inst = Get(inst_id);
  491. return {wrapped_inst, wrapped_inst_id};
  492. }
  493. return {std::nullopt, InstId::None};
  494. }
  495. // Returns a resolved LocId, which will point to a parse node, an import, or
  496. // be None.
  497. //
  498. // Unresolved LocIds can be backed by an InstId which may or may not have a
  499. // value after being resolved, so this operation needs to be done before using
  500. // most operations on LocId.
  501. auto GetCanonicalLocId(LocId loc_id) const -> LocId {
  502. while (loc_id.kind() == LocId::Kind::InstId) {
  503. loc_id = GetNonCanonicalLocId(loc_id.inst_id());
  504. }
  505. return loc_id;
  506. }
  507. // Gets the resolved LocId for an instruction. InstId can directly construct
  508. // an unresolved LocId. This skips that step when a resolved LocId is needed.
  509. auto GetCanonicalLocId(InstId inst_id) const -> LocId {
  510. return GetCanonicalLocId(GetNonCanonicalLocId(inst_id));
  511. }
  512. // Returns a virtual location to use for the desugaring of the code at the
  513. // specified location.
  514. auto GetLocIdForDesugaring(LocId loc_id) const -> LocId {
  515. return GetCanonicalLocId(loc_id).AsDesugared();
  516. }
  517. auto GetLocIdForDesugaring(InstId inst_id) const -> LocId {
  518. return GetCanonicalLocId(inst_id).AsDesugared();
  519. }
  520. // Returns the instruction that this instruction was imported from, or
  521. // ImportIRInstId::None if this instruction was not generated by importing
  522. // another instruction.
  523. auto GetImportSource(InstId inst_id) const -> ImportIRInstId {
  524. auto loc_id = GetNonCanonicalLocId(inst_id);
  525. return loc_id.kind() == LocId::Kind::ImportIRInstId
  526. ? loc_id.import_ir_inst_id()
  527. : ImportIRInstId::None;
  528. }
  529. // Overwrites a given instruction with a new value.
  530. auto Set(InstId inst_id, Inst inst) -> void { values_.Get(inst_id) = inst; }
  531. // Overwrites a given instruction's location with a new value.
  532. auto SetLocId(InstId inst_id, LocId loc_id) -> void {
  533. loc_ids_[inst_id.index] = loc_id;
  534. }
  535. // Overwrites a given instruction and location ID with a new value.
  536. auto SetLocIdAndInst(InstId inst_id, LocIdAndInst loc_id_and_inst) -> void {
  537. Set(inst_id, loc_id_and_inst.inst);
  538. SetLocId(inst_id, loc_id_and_inst.loc_id);
  539. }
  540. // Reserves space.
  541. auto Reserve(size_t size) -> void {
  542. loc_ids_.reserve(size);
  543. values_.Reserve(size);
  544. }
  545. // Collects memory usage of members.
  546. auto CollectMemUsage(MemUsage& mem_usage, llvm::StringRef label) const
  547. -> void {
  548. mem_usage.Collect(MemUsage::ConcatLabel(label, "loc_ids_"), loc_ids_);
  549. mem_usage.Collect(MemUsage::ConcatLabel(label, "values_"), values_);
  550. }
  551. auto values() const [[clang::lifetimebound]]
  552. -> ValueStore<InstId, Inst>::Range {
  553. return values_.values();
  554. }
  555. auto size() const -> int { return values_.size(); }
  556. auto enumerate() const [[clang::lifetimebound]] -> auto {
  557. return values_.enumerate();
  558. }
  559. private:
  560. // Given a symbolic type, get the corresponding unattached type.
  561. auto GetUnattachedType(TypeId type_id) const -> TypeId;
  562. // Gets the specified location for an instruction, without performing any
  563. // canonicalization.
  564. auto GetNonCanonicalLocId(InstId inst_id) const -> LocId {
  565. CARBON_CHECK(static_cast<size_t>(inst_id.index) < loc_ids_.size(),
  566. "{0} {1}", inst_id.index, loc_ids_.size());
  567. return loc_ids_[inst_id.index];
  568. }
  569. File* file_;
  570. llvm::SmallVector<LocId> loc_ids_;
  571. ValueStore<InstId, Inst> values_;
  572. };
  573. // Adapts BlockValueStore for instruction blocks.
  574. class InstBlockStore : public BlockValueStore<InstBlockId, InstId> {
  575. public:
  576. using BaseType = BlockValueStore<InstBlockId, InstId>;
  577. explicit InstBlockStore(llvm::BumpPtrAllocator& allocator)
  578. : BaseType(allocator) {
  579. auto exports_id = AddPlaceholder();
  580. CARBON_CHECK(exports_id == InstBlockId::Exports);
  581. auto imports_id = AddPlaceholder();
  582. CARBON_CHECK(imports_id == InstBlockId::Imports);
  583. auto global_init_id = AddPlaceholder();
  584. CARBON_CHECK(global_init_id == InstBlockId::GlobalInit);
  585. }
  586. // Adds an uninitialized block of the given size. The caller is expected to
  587. // modify values.
  588. auto AddUninitialized(size_t size) -> InstBlockId {
  589. return values().Add(AllocateUninitialized(size));
  590. }
  591. // Reserves and returns a block ID. The contents of the block should be
  592. // specified by calling ReplacePlaceholder.
  593. auto AddPlaceholder() -> InstBlockId {
  594. return values().Add(llvm::MutableArrayRef<InstId>());
  595. }
  596. // Sets the contents of a placeholder block to the given content.
  597. auto ReplacePlaceholder(InstBlockId block_id, llvm::ArrayRef<InstId> content)
  598. -> void {
  599. CARBON_CHECK(block_id != InstBlockId::Empty);
  600. CARBON_CHECK(Get(block_id).empty(),
  601. "inst block content set more than once");
  602. values().Get(block_id) = AllocateCopy(content);
  603. }
  604. // Returns the contents of the specified block, or an empty array if the block
  605. // is invalid.
  606. auto GetOrEmpty(InstBlockId block_id) const -> llvm::ArrayRef<InstId> {
  607. return block_id.has_value() ? Get(block_id) : llvm::ArrayRef<InstId>();
  608. }
  609. };
  610. // See common/hashing.h.
  611. inline auto CarbonHashValue(const Inst& value, uint64_t seed) -> HashCode {
  612. Hasher hasher(seed);
  613. hasher.HashRaw(value);
  614. return static_cast<HashCode>(hasher);
  615. }
  616. } // namespace Carbon::SemIR
  617. #endif // CARBON_TOOLCHAIN_SEM_IR_INST_H_