Refactor InstKind to move metadata from macros to the type. (#4119)

This adds `DefinitionInfo` for `Define`-based configuration so that
parameters are optional. It also makes it easier to provide the
equivalent functions on both `Definition` and `Define`.

A common pattern used here is to change from a `switch` with in-line
`case`s to instead have `case`s that call an overloaded function. What's
happening here is that the instruction type is used to select an
overload, and if an overload is not defined, a compiler error would
result. Meanwhile, clusters of overloads are being defined using
`requires`-based templating, so that equivalent implementations are not
copied. This addresses a limitation of a vanilla `switch` approach where
it's hard to have redundant cases using conditional logic, while also
getting compiler errors when adding new `InstKind` entries, which had
been a significant part of why we used macros previously.

This starts hitting some odd clang-format edge cases causing
`CARBON_KIND_SWITCH(inst){` (missing space), which I haven't seen
before. Adding `CARBON_KIND_SWITCH` to .clang-format works around it.
This commit is contained in:
Jon Ross-Perkins
2024-07-11 21:39:25 +00:00
committed by GitHub
parent 6682241ea0
commit 469f1c8e64
14 changed files with 639 additions and 634 deletions
+29 -14
View File
@@ -60,6 +60,17 @@ static auto FatalErrorIfEncountered(InstT inst) -> void {
<< inst;
}
// For instructions that are always of type `type`, produce the trivial runtime
// representation of type `type`.
static auto SetTrivialType(FunctionContext& context, SemIR::InstId inst_id)
-> void {
context.SetLocal(inst_id, context.GetTypeAsValue());
}
// TODO: Consider renaming Handle##Name, instead relying on typed_inst overload
// resolution. That would allow putting the nonexistent handler implementations
// in `requires`-style overloads.
// NOLINTNEXTLINE(readability-function-size): The define confuses lint.
auto FunctionContext::LowerInst(SemIR::InstId inst_id) -> void {
// Skip over constants. `FileContext::GetGlobal` lowers them as needed.
if (sem_ir().constant_values().Get(inst_id).is_constant()) {
@@ -69,25 +80,29 @@ auto FunctionContext::LowerInst(SemIR::InstId inst_id) -> void {
auto inst = sem_ir().insts().Get(inst_id);
CARBON_VLOG() << "Lowering " << inst_id << ": " << inst << "\n";
builder_.getInserter().SetCurrentInstId(inst_id);
CARBON_KIND_SWITCH(inst) {
#define CARBON_SEM_IR_INST_KIND_CONSTANT_ALWAYS(Name)
#define CARBON_SEM_IR_INST_KIND(Name) \
case CARBON_KIND(SemIR::Name typed_inst): { \
if constexpr (SemIR::Name::Kind.is_lowered()) { \
Handle##Name(*this, inst_id, typed_inst); \
} else { \
FatalErrorIfEncountered(typed_inst); \
} \
break; \
#define CARBON_SEM_IR_INST_KIND(Name) \
case CARBON_KIND(SemIR::Name typed_inst): { \
if constexpr (!SemIR::Name::Kind.is_lowered()) { \
FatalErrorIfEncountered(typed_inst); \
} else if constexpr (SemIR::Name::Kind.constant_kind() == \
SemIR::InstConstantKind::Always) { \
CARBON_FATAL() << "Missing constant value for constant instruction " \
<< inst; \
} else if constexpr (SemIR::Name::Kind.is_type() == \
SemIR::InstIsType::Always) { \
SetTrivialType(*this, inst_id); \
} else { \
Handle##Name(*this, inst_id, typed_inst); \
} \
break; \
}
#include "toolchain/sem_ir/inst_kind.def"
default:
CARBON_FATAL() << "Missing constant value for constant instruction "
<< inst;
}
builder_.getInserter().SetCurrentInstId(SemIR::InstId::Invalid);
} // namespace Carbon::Lower
}
auto FunctionContext::GetBlockArg(SemIR::InstBlockId block_id,
SemIR::TypeId type_id) -> llvm::PHINode* {