Skip to content

Commit 2584399

Browse files
authored
Factor IdKind enum out of node stack. (#3787)
Provide a general mechanism for determining the kind of the args of an instruction. Use this to simplify instruction profiling a little. The intent is to also use this mechanism as the basis of a substitution mechanism, which will be part of a future patch. Note that this causes us to do three table lookups and two indirect calls in inst_profile per instruction, instead of one table lookup and one indirect call. We can revisit this if it shows up in profiles.
1 parent 312d158 commit 2584399

File tree

6 files changed

+186
-77
lines changed

6 files changed

+186
-77
lines changed

toolchain/check/node_stack.h

Lines changed: 9 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,70 +5,47 @@
55
#ifndef CARBON_TOOLCHAIN_CHECK_NODE_STACK_H_
66
#define CARBON_TOOLCHAIN_CHECK_NODE_STACK_H_
77

8-
#include <type_traits>
9-
108
#include "common/vlog.h"
119
#include "llvm/ADT/SmallVector.h"
1210
#include "toolchain/parse/node_ids.h"
1311
#include "toolchain/parse/node_kind.h"
1412
#include "toolchain/parse/tree.h"
1513
#include "toolchain/parse/typed_nodes.h"
14+
#include "toolchain/sem_ir/id_kind.h"
1615
#include "toolchain/sem_ir/ids.h"
1716

1817
namespace Carbon::Check {
1918

2019
// A non-discriminated union of ID types.
21-
template <typename... IdTypes>
2220
class IdUnion {
2321
public:
2422
// The default constructor forms an invalid ID.
2523
explicit constexpr IdUnion() : index(IdBase::InvalidIndex) {}
2624

2725
template <typename IdT>
28-
requires(std::same_as<IdT, IdTypes> || ...)
26+
requires SemIR::IdKind::Contains<IdT>
2927
explicit constexpr IdUnion(IdT id) : index(id.index) {}
3028

31-
static constexpr std::size_t NumValidKinds = sizeof...(IdTypes);
32-
33-
// A numbering for the associated ID types.
34-
enum class Kind : int8_t {
35-
// The first `sizeof...(IdTypes)` indexes correspond to the types in
36-
// `IdTypes`.
37-
38-
// An explicit invalid state.
39-
Invalid = NumValidKinds,
40-
41-
// No active union element.
42-
None,
43-
};
29+
using Kind = SemIR::IdKind::RawEnumType;
4430

4531
// Returns the ID given its type.
4632
template <typename IdT>
47-
requires(std::same_as<IdT, IdTypes> || ...)
33+
requires SemIR::IdKind::Contains<IdT>
4834
constexpr auto As() const -> IdT {
4935
return IdT(index);
5036
}
5137

5238
// Returns the ID given its kind.
53-
template <Kind K>
54-
requires(static_cast<size_t>(K) < sizeof...(IdTypes))
55-
constexpr auto As() const {
56-
using IdT = __type_pack_element<static_cast<size_t>(K), IdTypes...>;
57-
return As<IdT>();
39+
template <SemIR::IdKind::RawEnumType K>
40+
constexpr auto As() const -> SemIR::IdKind::TypeFor<K> {
41+
return As<SemIR::IdKind::TypeFor<K>>();
5842
}
5943

6044
// Translates an ID type to the enum ID kind. Returns Invalid if `IdT` isn't
6145
// a type that can be stored in this union.
6246
template <typename IdT>
6347
static constexpr auto KindFor() -> Kind {
64-
// A bool for each type saying whether it matches. The result is the index
65-
// of the first `true` in this list. If none matches, then the result is the
66-
// length of the list, which is mapped to `Invalid`.
67-
constexpr bool TypeMatches[] = {std::same_as<IdT, IdTypes>...};
68-
constexpr int Index =
69-
std::find(TypeMatches, TypeMatches + sizeof...(IdTypes), true) -
70-
TypeMatches;
71-
return static_cast<Kind>(Index);
48+
return SemIR::IdKind::For<IdT>;
7249
}
7350

7451
private:
@@ -361,9 +338,7 @@ class NodeStack {
361338
// that the parse node has no associated ID, in which case the *SoloNodeId
362339
// functions should be used to push and pop it. Id::Kind::Invalid indicates
363340
// that the parse node should not appear in the node stack at all.
364-
using Id = IdUnion<SemIR::InstId, SemIR::InstBlockId, SemIR::FunctionId,
365-
SemIR::ClassId, SemIR::InterfaceId, SemIR::ImplId,
366-
SemIR::NameId, SemIR::TypeId>;
341+
using Id = IdUnion;
367342

368343
// An entry in stack_.
369344
struct Entry {

toolchain/sem_ir/BUILD

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ cc_library(
2626

2727
cc_library(
2828
name = "ids",
29-
hdrs = ["ids.h"],
29+
hdrs = [
30+
"id_kind.h",
31+
"ids.h",
32+
],
3033
deps = [
3134
"//common:check",
3235
"//common:ostream",
@@ -61,6 +64,7 @@ cc_library(
6164
deps = [
6265
":block_value_store",
6366
":builtin_kind",
67+
":ids",
6468
":inst_kind",
6569
"//common:check",
6670
"//common:ostream",

toolchain/sem_ir/id_kind.h

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
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+
5+
#ifndef CARBON_TOOLCHAIN_SEM_IR_ID_KIND_H_
6+
#define CARBON_TOOLCHAIN_SEM_IR_ID_KIND_H_
7+
8+
#include <algorithm>
9+
10+
#include "toolchain/sem_ir/ids.h"
11+
12+
namespace Carbon::SemIR {
13+
14+
// An enum whose values are the specified types.
15+
template <typename... Types>
16+
class TypeEnum {
17+
public:
18+
static constexpr std::size_t NumTypes = sizeof...(Types);
19+
static constexpr std::size_t NumValues = NumTypes + 2;
20+
21+
static_assert(NumValues <= 256, "Too many types for raw enum.");
22+
23+
// The underlying raw enumeration type.
24+
enum class RawEnumType : uint8_t {
25+
// The first sizeof...(Types) values correspond to the types.
26+
27+
// An explicitly invalid value.
28+
Invalid = NumTypes,
29+
30+
// Indicates that no type should be used.
31+
// TODO: This doesn't really fit the model of this type, but it's convenient
32+
// for all of its users.
33+
None,
34+
};
35+
36+
// Accesses the type given an enum value.
37+
template <RawEnumType K>
38+
requires(K != RawEnumType::Invalid)
39+
using TypeFor = __type_pack_element<static_cast<size_t>(K), Types...>;
40+
41+
// Workarond for Clang bug https://github.com/llvm/llvm-project/issues/85461
42+
template <RawEnumType Value>
43+
static constexpr auto FromRaw = TypeEnum(Value);
44+
45+
// Names for the `Invalid` and `None` enumeration values.
46+
static constexpr const TypeEnum& Invalid = FromRaw<RawEnumType::Invalid>;
47+
static constexpr const TypeEnum& None = FromRaw<RawEnumType::None>;
48+
49+
// Accesses the enumeration value for the type `IdT`. If `AllowInvalid` is
50+
// set, any unexpected type is mapped to `Invalid`, otherwise an invalid type
51+
// results in a compile error.
52+
//
53+
// The `Self` parameter is an implementation detail to allow `ForImpl` to be
54+
// defined after this template, and should not be specified.
55+
template <typename IdT, bool AllowInvalid = false, typename Self = TypeEnum>
56+
static constexpr auto For = Self::template ForImpl<IdT, AllowInvalid>();
57+
58+
// This bool indicates whether the specified type corresponds to a value in
59+
// this enum.
60+
template <typename IdT>
61+
static constexpr bool Contains = For<IdT, true>.is_valid();
62+
63+
// Explicitly convert from the raw enum type.
64+
explicit constexpr TypeEnum(RawEnumType value) : value_(value) {}
65+
66+
// Implicitly convert to the raw enum type, for use in `switch`.
67+
//
68+
// NOLINTNEXTLINE(google-explicit-constructor)
69+
constexpr operator RawEnumType() const { return value_; }
70+
71+
// Conversion to bool is deleted to prevent direct use in an `if` condition
72+
// instead of comparing with another value.
73+
explicit operator bool() const = delete;
74+
75+
// Returns the raw enum value.
76+
constexpr auto ToRaw() const -> RawEnumType { return value_; }
77+
78+
// Returns a value that can be used as an array index. Returned value will be
79+
// < NumValues.
80+
constexpr auto ToIndex() const -> std::size_t {
81+
return static_cast<std::size_t>(value_);
82+
}
83+
84+
// Returns whether this is a valid value, not `Invalid`.
85+
constexpr auto is_valid() const -> bool {
86+
return value_ != RawEnumType::Invalid;
87+
}
88+
89+
private:
90+
// Translates a type to its enum value, or `Invalid`.
91+
template <typename IdT, bool AllowInvalid>
92+
static constexpr auto ForImpl() -> TypeEnum {
93+
// A bool for each type saying whether it matches. The result is the index
94+
// of the first `true` in this list. If none matches, then the result is the
95+
// length of the list, which is mapped to `Invalid`.
96+
constexpr bool TypeMatches[] = {std::same_as<IdT, Types>...};
97+
constexpr int Index =
98+
std::find(TypeMatches, TypeMatches + NumTypes, true) - TypeMatches;
99+
static_assert(Index != NumTypes || AllowInvalid,
100+
"Unexpected type passed to TypeEnum::For<...>");
101+
return TypeEnum(static_cast<RawEnumType>(Index));
102+
}
103+
104+
RawEnumType value_;
105+
};
106+
107+
// An enum of all the ID types used as instruction operands.
108+
using IdKind = TypeEnum<
109+
// From sem_ir/builtin_kind.h.
110+
BuiltinKind,
111+
// From base/value_store.h.
112+
IntId, RealId, StringLiteralValueId,
113+
// From sem_ir/id.h.
114+
InstId, ConstantId, BindNameId, FunctionId, ClassId, InterfaceId, ImplId,
115+
ImportIRId, BoolValue, NameId, NameScopeId, InstBlockId, TypeId,
116+
TypeBlockId, ElementIndex>;
117+
118+
} // namespace Carbon::SemIR
119+
120+
#endif // CARBON_TOOLCHAIN_SEM_IR_ID_KIND_H_

toolchain/sem_ir/inst.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,22 @@ auto Inst::Print(llvm::raw_ostream& out) const -> void {
3232
out << "}";
3333
}
3434

35+
// Returns the IdKind of an instruction's argument, or None if there is no
36+
// argument with that index.
37+
template <typename InstKind, int ArgIndex>
38+
static constexpr auto IdKindFor() -> IdKind {
39+
using Info = Internal::InstLikeTypeInfo<InstKind>;
40+
if constexpr (ArgIndex < Info::NumArgs) {
41+
return IdKind::For<typename Info::template ArgType<ArgIndex>>;
42+
} else {
43+
return IdKind::None;
44+
}
45+
}
46+
47+
const std::pair<IdKind, IdKind> Inst::ArgKindTable[] = {
48+
#define CARBON_SEM_IR_INST_KIND(Name) \
49+
{IdKindFor<Name, 0>(), IdKindFor<Name, 1>()},
50+
#include "toolchain/sem_ir/inst_kind.def"
51+
};
52+
3553
} // namespace Carbon::SemIR

toolchain/sem_ir/inst.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "toolchain/base/index_base.h"
1515
#include "toolchain/sem_ir/block_value_store.h"
1616
#include "toolchain/sem_ir/builtin_kind.h"
17+
#include "toolchain/sem_ir/id_kind.h"
1718
#include "toolchain/sem_ir/inst_kind.h"
1819
#include "toolchain/sem_ir/typed_insts.h"
1920

@@ -209,6 +210,18 @@ class Inst : public Printable<Inst> {
209210
// Gets the type of the value produced by evaluating this instruction.
210211
auto type_id() const -> TypeId { return type_id_; }
211212

213+
// Gets the kinds of IDs used for arg0 and arg1 of the specified kind of
214+
// instruction.
215+
//
216+
// TODO: This would ideally live on InstKind, but can't be there for layering
217+
// reasons.
218+
static auto ArgKinds(InstKind kind) -> std::pair<IdKind, IdKind> {
219+
return ArgKindTable[kind.AsInt()];
220+
}
221+
222+
// Gets the kinds of IDs used for arg0 and arg1 of this instruction.
223+
auto ArgKinds() const -> std::pair<IdKind, IdKind> { return ArgKinds(kind_); }
224+
212225
// Gets the first argument of the instruction. InvalidIndex if there is no
213226
// such argument.
214227
auto arg0() const -> int32_t { return arg0_; }
@@ -222,6 +235,9 @@ class Inst : public Printable<Inst> {
222235
private:
223236
friend class InstTestHelper;
224237

238+
// Table mapping instruction kinds to their argument kinds.
239+
static const std::pair<IdKind, IdKind> ArgKindTable[];
240+
225241
// Raw constructor, used for testing.
226242
explicit Inst(InstKind kind, TypeId type_id, int32_t arg0, int32_t arg1)
227243
: kind_(kind), type_id_(type_id), arg0_(arg0), arg1_(arg1) {}

toolchain/sem_ir/inst_profile.cpp

Lines changed: 18 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -65,54 +65,30 @@ static auto RealProfileArgFunction(llvm::FoldingSetNodeID& id,
6565
id.AddBoolean(real.is_decimal);
6666
}
6767

68-
// Selects the function to use to profile argument N of instruction InstT. We
69-
// compute this in advance so that we can reuse the profiling code for all
70-
// instructions that are profiled in the same way. For example, all instructions
71-
// that take two IDs that are profiled by value use the same profiling code,
72-
// namely `ProfileArgs<DefaultProfileArgFunction, DefaultProfileArgFunction>`.
73-
template <typename InstT, int N>
74-
static constexpr auto SelectProfileArgFunction() -> ProfileArgFunction* {
75-
if constexpr (N >= Internal::InstLikeTypeInfo<InstT>::NumArgs) {
76-
// This argument is not used by this instruction; don't profile it.
77-
return NullProfileArgFunction;
78-
} else {
79-
using ArgT = Internal::InstLikeTypeInfo<InstT>::template ArgType<N>;
80-
if constexpr (std::is_same_v<ArgT, InstBlockId>) {
81-
return InstBlockProfileArgFunction;
82-
} else if constexpr (std::is_same_v<ArgT, TypeBlockId>) {
83-
return TypeBlockProfileArgFunction;
84-
} else if constexpr (std::is_same_v<ArgT, IntId>) {
85-
return IntProfileArgFunction;
86-
} else if constexpr (std::is_same_v<ArgT, RealId>) {
87-
return RealProfileArgFunction;
88-
} else {
89-
return DefaultProfileArgFunction;
90-
}
91-
}
92-
}
93-
94-
// Profiles the given instruction arguments using the specified functions.
95-
template <ProfileArgFunction* ProfileArg0, ProfileArgFunction* ProfileArg1>
96-
static auto ProfileArgs(llvm::FoldingSetNodeID& id, const File& sem_ir,
97-
int32_t arg0, int32_t arg1) -> void {
98-
ProfileArg0(id, sem_ir, arg0);
99-
ProfileArg1(id, sem_ir, arg1);
68+
// Profiles the given instruction argument, which is of the specified kind.
69+
static auto ProfileArg(llvm::FoldingSetNodeID& id, const File& sem_ir,
70+
IdKind arg_kind, int32_t arg) -> void {
71+
static constexpr std::array<ProfileArgFunction*, IdKind::NumValues>
72+
ProfileFunctions = [] {
73+
std::array<ProfileArgFunction*, IdKind::NumValues> array;
74+
array.fill(DefaultProfileArgFunction);
75+
array[IdKind::None.ToIndex()] = NullProfileArgFunction;
76+
array[IdKind::For<InstBlockId>.ToIndex()] = InstBlockProfileArgFunction;
77+
array[IdKind::For<TypeBlockId>.ToIndex()] = TypeBlockProfileArgFunction;
78+
array[IdKind::For<IntId>.ToIndex()] = IntProfileArgFunction;
79+
array[IdKind::For<RealId>.ToIndex()] = RealProfileArgFunction;
80+
return array;
81+
}();
82+
ProfileFunctions[arg_kind.ToIndex()](id, sem_ir, arg);
10083
}
10184

10285
auto ProfileConstant(llvm::FoldingSetNodeID& id, const File& sem_ir, Inst inst)
10386
-> void {
104-
using ProfileArgsFunction =
105-
auto(llvm::FoldingSetNodeID&, const File&, int32_t, int32_t)->void;
106-
static constexpr ProfileArgsFunction* ProfileFunctions[] = {
107-
#define CARBON_SEM_IR_INST_KIND(KindName) \
108-
ProfileArgs<SelectProfileArgFunction<KindName, 0>(), \
109-
SelectProfileArgFunction<KindName, 1>()>,
110-
#include "toolchain/sem_ir/inst_kind.def"
111-
};
112-
11387
inst.kind().Profile(id);
11488
id.AddInteger(inst.type_id().index);
115-
ProfileFunctions[inst.kind().AsInt()](id, sem_ir, inst.arg0(), inst.arg1());
89+
auto arg_kinds = inst.ArgKinds();
90+
ProfileArg(id, sem_ir, arg_kinds.first, inst.arg0());
91+
ProfileArg(id, sem_ir, arg_kinds.second, inst.arg1());
11692
}
11793

11894
} // namespace Carbon::SemIR

0 commit comments

Comments
 (0)