| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753 |
- // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
- // Exceptions. See /LICENSE for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- #ifndef CARBON_TOOLCHAIN_SEM_IR_INST_H_
- #define CARBON_TOOLCHAIN_SEM_IR_INST_H_
- #include <concepts>
- #include <cstdint>
- #include "common/check.h"
- #include "common/hashing.h"
- #include "common/ostream.h"
- #include "common/raw_string_ostream.h"
- #include "common/struct_reflection.h"
- #include "toolchain/base/block_value_store.h"
- #include "toolchain/base/index_base.h"
- #include "toolchain/base/int.h"
- #include "toolchain/base/value_store.h"
- #include "toolchain/sem_ir/id_kind.h"
- #include "toolchain/sem_ir/ids.h"
- #include "toolchain/sem_ir/inst_kind.h"
- #include "toolchain/sem_ir/singleton_insts.h"
- #include "toolchain/sem_ir/typed_insts.h"
- namespace Carbon::SemIR {
- template <typename... TypedInsts>
- struct CategoryOf;
- class File;
- // InstLikeTypeInfo is an implementation detail, and not public API.
- namespace Internal {
- // Information about an instruction-like type, which is a type that an Inst can
- // be converted to and from.
- template <typename InstLikeType>
- struct InstLikeTypeInfo;
- // A helper base class for instruction-like types that are structs.
- template <typename InstLikeType>
- struct InstLikeTypeInfoBase {
- // A corresponding std::tuple<...> type.
- using Tuple =
- decltype(StructReflection::AsTuple(std::declval<InstLikeType>()));
- static constexpr int FirstArgField =
- HasKindMemberAsField<InstLikeType> + HasTypeIdMember<InstLikeType>;
- static constexpr int NumArgs = std::tuple_size_v<Tuple> - FirstArgField;
- static_assert(NumArgs <= 2,
- "Unsupported: typed inst has more than two data fields");
- template <int N>
- using ArgType = std::tuple_element_t<FirstArgField + N, Tuple>;
- template <int N>
- static auto Get(InstLikeType inst) -> ArgType<N> {
- return std::get<FirstArgField + N>(StructReflection::AsTuple(inst));
- }
- };
- // A particular type of instruction is instruction-like.
- template <typename TypedInst>
- requires std::same_as<const InstKind::Definition<
- typename decltype(TypedInst::Kind)::TypedNodeId>,
- decltype(TypedInst::Kind)>
- struct InstLikeTypeInfo<TypedInst> : InstLikeTypeInfoBase<TypedInst> {
- static_assert(!HasKindMemberAsField<TypedInst>,
- "Instruction type should not have a kind field");
- static auto GetKind(TypedInst /*inst*/) -> InstKind {
- return TypedInst::Kind;
- }
- static constexpr auto IsKind(InstKind kind) -> bool {
- return kind == TypedInst::Kind;
- }
- // A name that can be streamed to an llvm::raw_ostream.
- static auto DebugName() -> InstKind { return TypedInst::Kind; }
- };
- // If `TypedInst` has an Nth field, validates that `CategoryInst` has a
- // corresponding field with a compatible type.
- template <typename CategoryInst, typename TypedInst, size_t N>
- static consteval auto ValidateCategoryFieldForTypedInst() -> void {
- if constexpr (InstLikeTypeInfoBase<TypedInst>::NumArgs > N) {
- if constexpr (!std::is_same_v<typename InstLikeTypeInfoBase<
- CategoryInst>::template ArgType<N>,
- AnyRawId>) {
- static_assert(
- std::is_same_v<
- typename InstLikeTypeInfoBase<CategoryInst>::template ArgType<N>,
- typename InstLikeTypeInfoBase<TypedInst>::template ArgType<N>>,
- "Inst category field should be the same type as the "
- "corresponding fields of its typed insts, or AnyRawId if "
- "they have different types");
- }
- }
- }
- // Validates that `CategoryInst` is compatible with `TypedInst`
- template <typename CategoryInst, typename TypedInst>
- static consteval auto ValidateCategoryForTypedInst() -> void {
- static_assert(Internal::HasKindMemberAsField<CategoryInst>,
- "Inst category should have an `InstKind` field");
- static_assert(!HasTypeIdMember<TypedInst> || HasTypeIdMember<CategoryInst>,
- "Inst category should have a `TypeId` field if any of its "
- "typed insts do");
- static_assert(InstLikeTypeInfoBase<CategoryInst>::NumArgs >=
- InstLikeTypeInfoBase<TypedInst>::NumArgs,
- "Inst category should have as many fields as any of its typed "
- "insts");
- ValidateCategoryFieldForTypedInst<CategoryInst, TypedInst, 0>();
- ValidateCategoryFieldForTypedInst<CategoryInst, TypedInst, 1>();
- }
- // Validates that `CategoryInst` is compatible with all of `TypedInsts`.
- // Always returns true; validation failure will cause build errors when
- // instantiating the function.
- template <typename CategoryInst, typename... TypedInsts>
- static consteval auto ValidateCategory(
- CategoryOf<TypedInsts...> /*category_info*/) -> bool {
- (ValidateCategoryForTypedInst<CategoryInst, TypedInsts>(), ...);
- return true;
- }
- // An instruction category is instruction-like.
- template <typename InstCat>
- requires requires { typename InstCat::CategoryInfo; }
- struct InstLikeTypeInfo<InstCat> : InstLikeTypeInfoBase<InstCat> {
- static auto GetKind(InstCat cat) -> InstKind { return cat.kind; }
- static constexpr auto IsKind(InstKind kind) -> bool {
- for (InstKind k : InstCat::CategoryInfo::Kinds) {
- if (k == kind) {
- return true;
- }
- }
- return false;
- }
- // A name that can be streamed to an llvm::raw_ostream.
- static auto DebugName() -> std::string {
- RawStringOstream out;
- out << "{";
- llvm::ListSeparator sep;
- for (auto kind : InstCat::CategoryInfo::Kinds) {
- out << sep << kind;
- }
- out << "}";
- return out.TakeStr();
- }
- private:
- // Trigger validation of `InstCat`.
- static_assert(ValidateCategory<InstCat>(typename InstCat::CategoryInfo()));
- };
- // HasInstCategory is true if T::Kind is an element of InstCat::Kinds.
- template <typename InstCat, typename T>
- concept HasInstCategory = InstLikeTypeInfo<InstCat>::IsKind(T::Kind);
- // A type is InstLike if InstLikeTypeInfo is defined for it.
- template <typename T>
- concept InstLikeType = requires { sizeof(InstLikeTypeInfo<T>); };
- } // namespace Internal
- // A type-erased representation of a SemIR instruction, that may be constructed
- // from the specific kinds of instruction defined in `typed_insts.h`. This
- // provides access to common fields present on most or all kinds of
- // instructions:
- //
- // - `kind` for run-time logic when the input Kind is unknown.
- // - `type_id` for quick type checking.
- //
- // In addition, kind-specific data can be accessed by casting to the specific
- // kind of instruction:
- //
- // - Use `inst.kind()` or `Is<InstLikeType>` to determine what kind of
- // instruction it is.
- // - Cast to a specific type using `inst.As<InstLikeType>()`
- // - Using the wrong kind in `inst.As<InstLikeType>()` is a programming error,
- // and will CHECK-fail in debug modes (opt may too, but it's not an API
- // guarantee).
- // - Use `inst.TryAs<InstLikeType>()` to safely access type-specific instruction
- // data where the instruction's kind is not known.
- class Inst : public Printable<Inst> {
- public:
- // Associates an argument (arg0 or arg1) with its IdKind.
- class ArgAndKind {
- public:
- explicit ArgAndKind(IdKind kind, int32_t value)
- : kind_(kind), value_(value) {}
- // Converts to `IdT`, validating the `kind` matches.
- template <typename IdT>
- auto As() const -> IdT {
- CARBON_DCHECK(kind_ == IdKind::For<IdT>);
- return IdT(value_);
- }
- // Converts to `IdT`, returning nullopt if the kind is incorrect.
- template <typename IdT>
- auto TryAs() const -> std::optional<IdT> {
- if (kind_ != IdKind::For<IdT>) {
- return std::nullopt;
- }
- return IdT(value_);
- }
- auto kind() const -> IdKind { return kind_; }
- auto value() const -> int32_t { return value_; }
- private:
- IdKind kind_;
- int32_t value_;
- };
- // Makes an instruction for a singleton. This exists to support simple
- // construction of all singletons by File.
- static auto MakeSingleton(InstKind kind) -> Inst {
- CARBON_CHECK(IsSingletonInstKind(kind));
- // Error uses a self-referential type so that it's not accidentally treated
- // as a normal type. Every other builtin is a type, including the
- // self-referential TypeType.
- auto type_id =
- kind == InstKind::ErrorInst ? ErrorInst::TypeId : TypeType::TypeId;
- return Inst(kind, type_id, InstId::NoneIndex, InstId::NoneIndex);
- }
- template <typename TypedInst>
- requires Internal::InstLikeType<TypedInst>
- explicit(false) Inst(TypedInst typed_inst)
- // kind_ is always overwritten below.
- : kind_(),
- type_id_(TypeId::None),
- arg0_(InstId::NoneIndex),
- arg1_(InstId::NoneIndex) {
- if constexpr (Internal::HasKindMemberAsField<TypedInst>) {
- kind_ = typed_inst.kind.AsInt();
- } else {
- kind_ = TypedInst::Kind.AsInt();
- }
- if constexpr (Internal::HasTypeIdMember<TypedInst>) {
- type_id_ = typed_inst.type_id;
- }
- using Info = Internal::InstLikeTypeInfo<TypedInst>;
- if constexpr (Info::NumArgs > 0) {
- arg0_ = ToRaw(Info::template Get<0>(typed_inst));
- }
- if constexpr (Info::NumArgs > 1) {
- arg1_ = ToRaw(Info::template Get<1>(typed_inst));
- }
- }
- // Returns whether this instruction has the specified type.
- template <typename TypedInst>
- requires Internal::InstLikeType<TypedInst>
- auto Is() const -> bool {
- return Internal::InstLikeTypeInfo<TypedInst>::IsKind(kind());
- }
- // Returns whether this instruction has one of the specified types.
- template <typename... TypedInsts>
- requires(... && Internal::InstLikeType<TypedInsts>)
- auto IsOneOf() const -> bool {
- return (... || Internal::InstLikeTypeInfo<TypedInsts>::IsKind(kind()));
- }
- // Casts this instruction to the given typed instruction, which must match the
- // instruction's kind, and returns the typed instruction.
- template <typename TypedInst>
- requires Internal::InstLikeType<TypedInst>
- auto As() const -> TypedInst {
- using Info = Internal::InstLikeTypeInfo<TypedInst>;
- CARBON_CHECK(Is<TypedInst>(), "Casting inst {0} to wrong kind {1}", *this,
- Info::DebugName());
- auto build_with_type_id_onwards = [&](auto... type_id_onwards) {
- if constexpr (Internal::HasKindMemberAsField<TypedInst>) {
- return TypedInst{kind(), type_id_onwards...};
- } else {
- return TypedInst{type_id_onwards...};
- }
- };
- auto build_with_args = [&](auto... args) {
- if constexpr (Internal::HasTypeIdMember<TypedInst>) {
- return build_with_type_id_onwards(type_id(), args...);
- } else {
- return build_with_type_id_onwards(args...);
- }
- };
- if constexpr (Info::NumArgs == 0) {
- return build_with_args();
- } else if constexpr (Info::NumArgs == 1) {
- return build_with_args(
- FromRaw<typename Info::template ArgType<0>>(arg0_));
- } else if constexpr (Info::NumArgs == 2) {
- return build_with_args(
- FromRaw<typename Info::template ArgType<0>>(arg0_),
- FromRaw<typename Info::template ArgType<1>>(arg1_));
- }
- }
- // If this instruction is the given kind, returns a typed instruction,
- // otherwise returns nullopt.
- template <typename TypedInst>
- requires Internal::InstLikeType<TypedInst>
- auto TryAs() const -> std::optional<TypedInst> {
- if (Is<TypedInst>()) {
- return As<TypedInst>();
- } else {
- return std::nullopt;
- }
- }
- auto kind() const -> InstKind { return InstKind::FromInt(kind_); }
- // Gets the type of the value produced by evaluating this instruction.
- auto type_id() const -> TypeId { return type_id_; }
- // Gets the first argument of the instruction. NoneIndex if there is no such
- // argument.
- auto arg0() const -> int32_t { return arg0_; }
- // Gets the second argument of the instruction. NoneIndex if there is no such
- // argument.
- auto arg1() const -> int32_t { return arg1_; }
- // Returns arguments with their IdKind.
- auto arg0_and_kind() const -> ArgAndKind {
- return ArgAndKind(ArgKindTable[kind_].first, arg0_);
- }
- auto arg1_and_kind() const -> ArgAndKind {
- return ArgAndKind(ArgKindTable[kind_].second, arg1_);
- }
- // Sets the type of this instruction.
- auto SetType(TypeId type_id) -> void { type_id_ = type_id; }
- // Sets the arguments of this instruction.
- auto SetArgs(int32_t arg0, int32_t arg1) -> void {
- arg0_ = arg0;
- arg1_ = arg1;
- }
- // Convert a field to its raw representation, used as `arg0_` / `arg1_`.
- static constexpr auto ToRaw(AnyIdBase base) -> int32_t { return base.index; }
- static constexpr auto ToRaw(IntId id) -> int32_t { return id.AsRaw(); }
- // Convert a field from its raw representation.
- template <typename T>
- requires IdKind::Contains<T>
- static constexpr auto FromRaw(int32_t raw) -> T {
- return T(raw);
- }
- template <>
- constexpr auto FromRaw<IntId>(int32_t raw) -> IntId {
- return IntId::MakeRaw(raw);
- }
- auto Print(llvm::raw_ostream& out) const -> void;
- friend auto operator==(Inst lhs, Inst rhs) -> bool {
- return std::memcmp(&lhs, &rhs, sizeof(Inst)) == 0;
- }
- private:
- friend class InstTestHelper;
- // Table mapping instruction kinds to their argument kinds.
- //
- // TODO: ArgKindTable would ideally live on InstKind, but can't be there for
- // layering reasons.
- static const std::pair<IdKind, IdKind> ArgKindTable[];
- // Raw constructor, used for testing.
- explicit Inst(InstKind kind, TypeId type_id, int32_t arg0, int32_t arg1)
- : Inst(kind.AsInt(), type_id, arg0, arg1) {}
- explicit constexpr Inst(int32_t kind, TypeId type_id, int32_t arg0,
- int32_t arg1)
- : kind_(kind), type_id_(type_id), arg0_(arg0), arg1_(arg1) {}
- int32_t kind_;
- TypeId type_id_;
- // Use `As` to access arg0 and arg1.
- int32_t arg0_;
- int32_t arg1_;
- };
- // TODO: This is currently 16 bytes because we sometimes have 2 arguments for a
- // pair of Insts. However, InstKind is 1 byte; if args were 3.5 bytes, we could
- // potentially shrink Inst by 4 bytes. This may be worth investigating further.
- // Note though that 16 bytes is an ideal size for registers, we may want more
- // flags, and 12 bytes would be a more marginal improvement.
- static_assert(sizeof(Inst) == 16, "Unexpected Inst size");
- // Instruction-like types can be printed by converting them to instructions.
- template <typename TypedInst>
- requires Internal::InstLikeType<TypedInst>
- inline auto operator<<(llvm::raw_ostream& out, TypedInst inst)
- -> llvm::raw_ostream& {
- Inst(inst).Print(out);
- return out;
- }
- // Associates a LocId and Inst in order to provide type-checking that the
- // TypedNodeId corresponds to the InstT.
- struct LocIdAndInst {
- // Constructs a LocIdAndInst with no associated location. This should be used
- // very sparingly: only when it doesn't make sense to store a location even
- // when the instruction kind usually has one, such as for instructions in the
- // constants block.
- template <typename InstT>
- static auto NoLoc(InstT inst) -> LocIdAndInst {
- return LocIdAndInst(LocId::None, inst, /*is_unchecked=*/true);
- }
- // Constructs a `LocIdAndInst` with a runtime verification of the location.
- //
- // Prefer `LocIdAndInst` constructors with compile-time verification,
- // or `AddInst` overloads which make use of those constructors.
- static auto RuntimeVerified(const File& file, LocId loc_id, Inst inst)
- -> LocIdAndInst;
- // Construction for the common case with a typed node.
- template <typename InstT>
- requires(Internal::HasNodeId<InstT>)
- LocIdAndInst(decltype(InstT::Kind)::TypedNodeId node_id, InstT inst)
- : loc_id(node_id), inst(inst) {}
- // Construction for the case where the instruction can have any associated
- // node.
- template <typename InstT>
- requires(Internal::HasUntypedNodeId<InstT>)
- LocIdAndInst(LocId loc_id, InstT inst) : loc_id(loc_id), inst(inst) {}
- // For `ImportIRInstId`, use `RuntimeVerified`.
- template <typename InstT>
- LocIdAndInst(ImportIRInstId loc_id, InstT inst) = delete;
- LocId loc_id;
- Inst inst;
- private:
- // For `InstStore::GetWithLocId` to construct unchecked values.
- friend class InstStore;
- // Note `is_unchecked` serves to disambiguate from public constructors.
- explicit LocIdAndInst(LocId loc_id, Inst inst, bool /*is_unchecked*/)
- : loc_id(loc_id), inst(inst) {}
- };
- // Provides a ValueStore wrapper for an API specific to instructions.
- //
- // InstIds in this store are tagged by an IdTag using the File's CheckIRId as
- // the tag value.
- class InstStore {
- public:
- using IdType = InstId;
- using IdTagType = IdTag<IdType, Tag<CheckIRId>>;
- explicit InstStore(File* file, int32_t reserved_inst_ids);
- // Adds an instruction to the instruction list, returning an ID to reference
- // the instruction. Note that this doesn't add the instruction to any
- // instruction block. Check::Context::AddInst or InstBlockStack::AddInst
- // should usually be used instead, to add the instruction to the current
- // block.
- auto AddInNoBlock(LocIdAndInst loc_id_and_inst) -> InstId {
- loc_ids_.push_back(loc_id_and_inst.loc_id);
- return values_.Add(loc_id_and_inst.inst);
- }
- // Returns the requested instruction. The returned instruction always has an
- // unattached type, even if an attached type is stored for it.
- auto Get(InstId inst_id) const -> Inst {
- Inst result = values_.Get(inst_id);
- auto type_id = result.type_id();
- if (type_id.has_value() && type_id.is_symbolic()) {
- result.SetType(GetUnattachedType(type_id));
- }
- return result;
- }
- // Returns the requested instruction, which is known to have the specified
- // type.
- template <typename InstT>
- auto Get(KnownInstId<InstT> inst_id) const -> InstT {
- return Get(static_cast<InstId>(inst_id)).As<InstT>();
- }
- // Returns the requested instruction, preserving its attached type.
- auto GetWithAttachedType(InstId inst_id) const -> Inst {
- return values_.Get(inst_id);
- }
- // Returns the type of the instruction as an attached type.
- auto GetAttachedType(InstId inst_id) const -> TypeId {
- return GetWithAttachedType(inst_id).type_id();
- }
- // Returns the requested instruction and its location ID.
- auto GetWithLocId(InstId inst_id) const -> LocIdAndInst {
- return LocIdAndInst(LocId(inst_id), Get(inst_id), /*is_unchecked=*/true);
- }
- // Returns whether the requested instruction is the specified type.
- template <typename InstT>
- auto Is(InstId inst_id) const -> bool {
- return Get(inst_id).Is<InstT>();
- }
- // Returns whether the requested instruction is one of the specified types.
- template <typename... InstTs>
- auto IsOneOf(InstId inst_id) const -> bool {
- return Get(inst_id).IsOneOf<InstTs...>();
- }
- // Returns the requested instruction, which is known to have the specified
- // type.
- template <typename InstT>
- auto GetAs(InstId inst_id) const -> InstT {
- return Get(inst_id).As<InstT>();
- }
- // Returns the requested instruction as the specified type, if it is of that
- // type.
- template <typename InstT>
- auto TryGetAs(InstId inst_id) const -> std::optional<InstT> {
- return Get(inst_id).TryAs<InstT>();
- }
- // Use `Get()` when the instruction type is known.
- template <typename InstT, typename KnownInstT>
- auto TryGetAs(KnownInstId<KnownInstT> inst_id) const = delete;
- // Returns the requested instruction as the specified type, if it is valid and
- // of that type. Otherwise returns nullopt.
- template <typename InstT>
- auto TryGetAsIfValid(InstId inst_id) const -> std::optional<InstT> {
- if (!inst_id.has_value()) {
- return std::nullopt;
- }
- return TryGetAs<InstT>(inst_id);
- }
- // Returns the `KnownInstId` form of `inst_id`. Requires a matching
- // instruction type.
- template <typename InstT>
- auto GetAsKnownInstId(InstId inst_id) const -> KnownInstId<InstT> {
- CARBON_CHECK(Is<InstT>(inst_id), "Casting inst {0} to wrong kind {1}",
- Get(inst_id), Internal::InstLikeTypeInfo<InstT>::DebugName());
- return KnownInstId<InstT>::UnsafeMake(inst_id);
- }
- template <typename InstT>
- struct GetAsWithIdResult {
- KnownInstId<InstT> inst_id;
- InstT inst;
- };
- // Returns the requested instruction, if it is of that type, along with the
- // original `InstId`, encoding the work of checking its type in a
- // `KnownInstId`.
- template <typename InstT>
- auto TryGetAsWithId(InstId inst_id) const
- -> std::optional<GetAsWithIdResult<InstT>> {
- auto inst = TryGetAs<InstT>(inst_id);
- if (!inst) {
- return std::nullopt;
- }
- return {
- {.inst_id = KnownInstId<InstT>::UnsafeMake(inst_id), .inst = *inst}};
- }
- // Attempts to convert the given instruction to the type that contains
- // `member`. If it can be converted, the instruction ID and instruction are
- // replaced by the unwrapped value of that member, and the converted wrapper
- // instruction and its ID are returned. Otherwise returns {nullopt, None}.
- template <typename InstT, typename InstIdT>
- requires std::derived_from<InstIdT, InstId>
- auto TryUnwrap(Inst& inst, InstId& inst_id, InstIdT InstT::* member) const
- -> std::pair<std::optional<InstT>, KnownInstId<InstT>> {
- if (auto wrapped_inst = inst.TryAs<InstT>()) {
- auto wrapped_inst_id = KnownInstId<InstT>::UnsafeMake(inst_id);
- inst_id = (*wrapped_inst).*member;
- inst = Get(inst_id);
- return {wrapped_inst, wrapped_inst_id};
- }
- return {std::nullopt, KnownInstId<InstT>::None};
- }
- // Returns a resolved LocId, which will point to a parse node, an import, or
- // be None.
- //
- // Unresolved LocIds can be backed by an InstId which may or may not have a
- // value after being resolved, so this operation needs to be done before using
- // most operations on LocId.
- auto GetCanonicalLocId(LocId loc_id) const -> LocId {
- while (loc_id.kind() == LocId::Kind::InstId) {
- loc_id = GetNonCanonicalLocId(loc_id.inst_id());
- }
- return loc_id;
- }
- // Gets the resolved LocId for an instruction. InstId can directly construct
- // an unresolved LocId. This skips that step when a resolved LocId is needed.
- auto GetCanonicalLocId(InstId inst_id) const -> LocId {
- return GetCanonicalLocId(GetNonCanonicalLocId(inst_id));
- }
- // Returns a virtual location to use for the desugaring of the code at the
- // specified location.
- auto GetLocIdForDesugaring(LocId loc_id) const -> LocId {
- return GetCanonicalLocId(loc_id).AsDesugared();
- }
- auto GetLocIdForDesugaring(InstId inst_id) const -> LocId {
- return GetCanonicalLocId(inst_id).AsDesugared();
- }
- // Returns the instruction that this instruction was imported from, or
- // ImportIRInstId::None if this instruction was not generated by importing
- // another instruction.
- auto GetImportSource(InstId inst_id) const -> ImportIRInstId {
- auto loc_id = GetNonCanonicalLocId(inst_id);
- return loc_id.kind() == LocId::Kind::ImportIRInstId
- ? loc_id.import_ir_inst_id()
- : ImportIRInstId::None;
- }
- // Overwrites a given instruction with a new value.
- auto Set(InstId inst_id, Inst inst) -> void { values_.Get(inst_id) = inst; }
- // Overwrites a given instruction's location with a new value.
- auto SetLocId(InstId inst_id, LocId loc_id) -> void {
- auto index = values_.GetRawIndex(inst_id);
- loc_ids_[index] = loc_id;
- }
- // Overwrites a given instruction and location ID with a new value.
- auto SetLocIdAndInst(InstId inst_id, LocIdAndInst loc_id_and_inst) -> void {
- Set(inst_id, loc_id_and_inst.inst);
- SetLocId(inst_id, loc_id_and_inst.loc_id);
- }
- // Reserves space.
- auto Reserve(size_t size) -> void {
- loc_ids_.reserve(size);
- values_.Reserve(size);
- }
- // Collects memory usage of members.
- auto CollectMemUsage(MemUsage& mem_usage, llvm::StringRef label) const
- -> void {
- mem_usage.Collect(MemUsage::ConcatLabel(label, "loc_ids_"), loc_ids_);
- mem_usage.Collect(MemUsage::ConcatLabel(label, "values_"), values_);
- }
- auto values() const [[clang::lifetimebound]]
- -> ValueStore<InstId, Inst, Tag<CheckIRId>>::Range {
- return values_.values();
- }
- auto size() const -> int { return values_.size(); }
- auto enumerate() const [[clang::lifetimebound]] -> auto {
- return values_.enumerate();
- }
- auto GetRawIndex(InstId id) const -> int32_t {
- return values_.GetRawIndex(id);
- }
- auto GetIdTag() const -> IdTagType { return values_.GetIdTag(); }
- private:
- // Given a symbolic type, get the corresponding unattached type.
- auto GetUnattachedType(TypeId type_id) const -> TypeId;
- // Gets the specified location for an instruction, without performing any
- // canonicalization.
- auto GetNonCanonicalLocId(InstId inst_id) const -> LocId {
- auto index = values_.GetRawIndex(inst_id);
- CARBON_CHECK(static_cast<size_t>(index) < loc_ids_.size(), "{0} {1}", index,
- loc_ids_.size());
- return loc_ids_[index];
- }
- File* file_;
- llvm::SmallVector<LocId> loc_ids_;
- ValueStore<InstId, Inst, Tag<CheckIRId>> values_;
- };
- // Adapts BlockValueStore for instruction blocks.
- class InstBlockStore
- : public BlockValueStore<InstBlockId, InstId, Tag<CheckIRId>> {
- public:
- using BaseType = BlockValueStore<InstBlockId, InstId, Tag<CheckIRId>>;
- explicit InstBlockStore(llvm::BumpPtrAllocator& allocator,
- CheckIRId check_ir_id = CheckIRId::None)
- : BaseType(allocator, check_ir_id, InstBlockId::ReservedIds.size()) {
- CARBON_CHECK(size() == 1, "Empty is added by `BlockValueStore`");
- for (auto reserved_id : InstBlockId::ReservedIds) {
- if (reserved_id == InstBlockId::Empty) {
- continue;
- }
- auto id = AddPlaceholder();
- CARBON_CHECK(id == reserved_id);
- }
- CARBON_CHECK(size() == InstBlockId::ReservedIds.size());
- }
- // Adds an uninitialized block of the given size. The caller is expected to
- // modify values.
- auto AddUninitialized(size_t size) -> InstBlockId {
- return values().Add(AllocateUninitialized(size));
- }
- // Reserves and returns a block ID. The contents of the block should be
- // specified by calling ReplacePlaceholder.
- auto AddPlaceholder() -> InstBlockId {
- return values().Add(llvm::MutableArrayRef<InstId>());
- }
- // Sets the contents of a placeholder block to the given content.
- auto ReplacePlaceholder(InstBlockId block_id, llvm::ArrayRef<InstId> content)
- -> void {
- CARBON_CHECK(block_id != InstBlockId::Empty);
- CARBON_CHECK(Get(block_id).empty(),
- "inst block content set more than once");
- values().Get(block_id) = AllocateCopy(content);
- }
- // Returns the contents of the specified block, or an empty array if the block
- // is invalid.
- auto GetOrEmpty(InstBlockId block_id) const -> llvm::ArrayRef<InstId> {
- return block_id.has_value() ? Get(block_id) : llvm::ArrayRef<InstId>();
- }
- };
- // See common/hashing.h.
- inline auto CarbonHashValue(const Inst& value, uint64_t seed) -> HashCode {
- Hasher hasher(seed);
- hasher.HashRaw(value);
- return static_cast<HashCode>(hasher);
- }
- } // namespace Carbon::SemIR
- #endif // CARBON_TOOLCHAIN_SEM_IR_INST_H_
|