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
+5 -1
View File
@@ -15,5 +15,9 @@ PointerAlignment: Left
# We abuse control macros for formatting other kinds of macros.
SpaceBeforeParens: ControlStatementsExceptControlMacros
IfMacros:
['CARBON_DEFINE_RAW_ENUM_CLASS', 'CARBON_DEFINE_RAW_ENUM_CLASS_NO_NAMES']
[
'CARBON_DEFINE_RAW_ENUM_CLASS',
'CARBON_DEFINE_RAW_ENUM_CLASS_NO_NAMES',
'CARBON_KIND_SWITCH',
]
StatementMacros: ['ABSTRACT']
+84 -71
View File
@@ -856,7 +856,7 @@ class TypeCompleter {
return value_rep;
}
auto BuildBuiltinValueRepr(SemIR::TypeId type_id,
auto BuildValueReprForInst(SemIR::TypeId type_id,
SemIR::BuiltinInst builtin) const
-> SemIR::ValueRepr {
switch (builtin.builtin_inst_kind) {
@@ -901,8 +901,8 @@ class TypeCompleter {
return MakePointerValueRepr(elementwise_rep, aggregate_kind);
}
auto BuildStructTypeValueRepr(SemIR::TypeId type_id,
SemIR::StructType struct_type) const
auto BuildValueReprForInst(SemIR::TypeId type_id,
SemIR::StructType struct_type) const
-> SemIR::ValueRepr {
// TODO: Share more code with tuples.
auto fields = context_.inst_blocks().Get(struct_type.fields_id);
@@ -935,8 +935,8 @@ class TypeCompleter {
same_as_object_rep);
}
auto BuildTupleTypeValueRepr(SemIR::TypeId type_id,
SemIR::TupleType tuple_type) const
auto BuildValueReprForInst(SemIR::TypeId type_id,
SemIR::TupleType tuple_type) const
-> SemIR::ValueRepr {
// TODO: Share more code with structs.
auto elements = context_.type_blocks().Get(tuple_type.elements_id);
@@ -964,78 +964,91 @@ class TypeCompleter {
same_as_object_rep);
}
auto BuildValueReprForInst(SemIR::TypeId type_id,
SemIR::ArrayType /*inst*/) const
-> SemIR::ValueRepr {
// For arrays, it's convenient to always use a pointer representation,
// even when the array has zero or one element, in order to support
// indexing.
return MakePointerValueRepr(type_id, SemIR::ValueRepr::ObjectAggregate);
}
auto BuildValueReprForInst(SemIR::TypeId /*type_id*/,
SemIR::ClassType inst) const -> SemIR::ValueRepr {
auto& class_info = context_.classes().Get(inst.class_id);
// The value representation of an adapter is the value representation of
// its adapted type.
if (class_info.adapt_id.is_valid()) {
return GetNestedValueRepr(class_info.object_repr_id);
}
// Otherwise, the value representation for a class is a pointer to the
// object representation.
// TODO: Support customized value representations for classes.
// TODO: Pick a better value representation when possible.
return MakePointerValueRepr(class_info.object_repr_id,
SemIR::ValueRepr::ObjectAggregate);
}
template <typename InstT>
requires(InstT::Kind.template IsAnyOf<
SemIR::AssociatedEntityType, SemIR::FunctionType,
SemIR::GenericClassType, SemIR::GenericInterfaceType,
SemIR::InterfaceType, SemIR::UnboundElementType>())
auto BuildValueReprForInst(SemIR::TypeId /*type_id*/, InstT /*inst*/) const
-> SemIR::ValueRepr {
// These types have no runtime operations, so we use an empty value
// representation.
//
// TODO: There is information we could model here:
// - For an interface, we could use a witness.
// - For an associated entity, we could use an index into the witness.
// - For an unbound element, we could use an index or offset.
return MakeEmptyValueRepr();
}
template <typename InstT>
requires(InstT::Kind.template IsAnyOf<SemIR::BindSymbolicName,
SemIR::InterfaceWitnessAccess>())
auto BuildValueReprForInst(SemIR::TypeId type_id, InstT /*inst*/) const
-> SemIR::ValueRepr {
// For symbolic types, we arbitrarily pick a copy representation.
return MakeCopyValueRepr(type_id);
}
template <typename InstT>
requires(InstT::Kind.template IsAnyOf<SemIR::FloatType, SemIR::IntType,
SemIR::PointerType>())
auto BuildValueReprForInst(SemIR::TypeId type_id, InstT /*inst*/) const
-> SemIR::ValueRepr {
return MakeCopyValueRepr(type_id);
}
auto BuildValueReprForInst(SemIR::TypeId /*type_id*/,
SemIR::ConstType inst) const -> SemIR::ValueRepr {
// The value representation of `const T` is the same as that of `T`.
// Objects are not modifiable through their value representations.
return GetNestedValueRepr(inst.inner_id);
}
template <typename InstT>
requires(InstT::Kind.is_type() == SemIR::InstIsType::Never)
auto BuildValueReprForInst(SemIR::TypeId /*type_id*/, InstT inst) const
-> SemIR::ValueRepr {
CARBON_FATAL() << "Type refers to non-type inst " << inst;
}
// Builds and returns the value representation for the given type. All nested
// types, as found by AddNestedIncompleteTypes, are known to be complete.
auto BuildValueRepr(SemIR::TypeId type_id, SemIR::Inst inst) const
-> SemIR::ValueRepr {
// Use overload resolution to select the implementation, producing compile
// errors when BuildTypeForInst isn't defined for a given instruction.
CARBON_KIND_SWITCH(inst) {
#define CARBON_SEM_IR_INST_KIND_TYPE_ALWAYS(...)
#define CARBON_SEM_IR_INST_KIND_TYPE_MAYBE(...)
#define CARBON_SEM_IR_INST_KIND(Name) case SemIR::Name::Kind:
#define CARBON_SEM_IR_INST_KIND(Name) \
case CARBON_KIND(SemIR::Name typed_inst): { \
return BuildValueReprForInst(type_id, typed_inst); \
}
#include "toolchain/sem_ir/inst_kind.def"
CARBON_FATAL() << "Type refers to non-type inst " << inst;
case SemIR::ArrayType::Kind: {
// For arrays, it's convenient to always use a pointer representation,
// even when the array has zero or one element, in order to support
// indexing.
return MakePointerValueRepr(type_id, SemIR::ValueRepr::ObjectAggregate);
}
case CARBON_KIND(SemIR::StructType struct_type): {
return BuildStructTypeValueRepr(type_id, struct_type);
}
case CARBON_KIND(SemIR::TupleType tuple_type): {
return BuildTupleTypeValueRepr(type_id, tuple_type);
}
case CARBON_KIND(SemIR::ClassType class_type): {
auto& class_info = context_.classes().Get(class_type.class_id);
// The value representation of an adapter is the value representation of
// its adapted type.
if (class_info.adapt_id.is_valid()) {
return GetNestedValueRepr(class_info.object_repr_id);
}
// Otherwise, the value representation for a class is a pointer to the
// object representation.
// TODO: Support customized value representations for classes.
// TODO: Pick a better value representation when possible.
return MakePointerValueRepr(class_info.object_repr_id,
SemIR::ValueRepr::ObjectAggregate);
}
case SemIR::AssociatedEntityType::Kind:
case SemIR::FunctionType::Kind:
case SemIR::GenericClassType::Kind:
case SemIR::GenericInterfaceType::Kind:
case SemIR::InterfaceType::Kind:
case SemIR::UnboundElementType::Kind: {
// These types have no runtime operations, so we use an empty value
// representation.
//
// TODO: There is information we could model here:
// - For an interface, we could use a witness.
// - For an associated entity, we could use an index into the witness.
// - For an unbound element, we could use an index or offset.
return MakeEmptyValueRepr();
}
case CARBON_KIND(SemIR::BuiltinInst builtin): {
return BuildBuiltinValueRepr(type_id, builtin);
}
case SemIR::BindSymbolicName::Kind:
case SemIR::InterfaceWitnessAccess::Kind:
// For symbolic types, we arbitrarily pick a copy representation.
return MakeCopyValueRepr(type_id);
case SemIR::FloatType::Kind:
case SemIR::IntType::Kind:
case SemIR::PointerType::Kind:
return MakeCopyValueRepr(type_id);
case CARBON_KIND(SemIR::ConstType const_type): {
// The value representation of `const T` is the same as that of `T`.
// Objects are not modifiable through their value representations.
return GetNestedValueRepr(const_type.inner_id);
}
}
}
+53 -62
View File
@@ -82,24 +82,6 @@ class ConstantContext {
int32_t last_lowered_constant_index_ = -1;
};
// For each instruction kind that can produce a constant, there is a function
// below to convert it to an `llvm::Constant*`:
//
// auto Emit<InstKind>AsConstant(ConstantContext& context,
// SemIR::<InstKind> inst) -> llvm::Constant*;
// For constants that are always of type `type`, produce the trivial runtime
// representation of type `type`.
#define CARBON_SEM_IR_INST_KIND_TYPE_NEVER(...)
#define CARBON_SEM_IR_INST_KIND_TYPE_MAYBE(...)
#define CARBON_SEM_IR_INST_KIND_CONSTANT_SYMBOLIC_ONLY(...)
#define CARBON_SEM_IR_INST_KIND(Name) \
static auto Emit##Name##AsConstant( \
ConstantContext& context, SemIR::Name /*inst*/) -> llvm::Constant* { \
return context.GetTypeAsValue(); \
}
#include "toolchain/sem_ir/inst_kind.def"
// Emits an aggregate constant of LLVM type `Type` whose elements are the
// contents of `refs_id`.
template <typename ConstantType, typename Type>
@@ -116,16 +98,37 @@ static auto EmitAggregateConstant(ConstantContext& context,
return ConstantType::get(llvm_type, elements);
}
static auto EmitStructValueAsConstant(ConstantContext& context,
SemIR::StructValue inst)
// For each instruction InstT, there is a function below to convert it to an
// `llvm::Constant*`:
//
// auto EmitAsConstant(ConstantContext& context, SemIR::InstT inst)
// -> llvm::Constant*;
template <typename InstT>
requires(InstT::Kind.constant_kind() == SemIR::InstConstantKind::Never ||
InstT::Kind.constant_kind() == SemIR::InstConstantKind::SymbolicOnly)
static auto EmitAsConstant(ConstantContext& /*context*/, InstT inst)
-> llvm::Constant* {
CARBON_FATAL() << "Unexpected constant instruction kind " << inst;
}
// For constants that are always of type `type`, produce the trivial runtime
// representation of type `type`.
template <typename InstT>
requires(InstT::Kind.is_type() == SemIR::InstIsType::Always)
static auto EmitAsConstant(ConstantContext& context, InstT /*inst*/)
-> llvm::Constant* {
return context.GetTypeAsValue();
}
static auto EmitAsConstant(ConstantContext& context, SemIR::StructValue inst)
-> llvm::Constant* {
return EmitAggregateConstant<llvm::ConstantStruct>(
context, inst.elements_id,
cast<llvm::StructType>(context.GetType(inst.type_id)));
}
static auto EmitTupleValueAsConstant(ConstantContext& context,
SemIR::TupleValue inst)
static auto EmitAsConstant(ConstantContext& context, SemIR::TupleValue inst)
-> llvm::Constant* {
// TODO: Add an ArrayValue instruction and stop using TupleValues to represent
// array constants.
@@ -140,80 +143,72 @@ static auto EmitTupleValueAsConstant(ConstantContext& context,
cast<llvm::StructType>(context.GetType(inst.type_id)));
}
static auto EmitAddrOfAsConstant(ConstantContext& /*context*/,
SemIR::AddrOf /*inst*/) -> llvm::Constant* {
static auto EmitAsConstant(ConstantContext& /*context*/, SemIR::AddrOf /*inst*/)
-> llvm::Constant* {
// TODO: Constant lvalue support. For now we have no constant lvalues, so we
// should never form a constant AddrOf.
CARBON_FATAL() << "AddrOf constants not supported yet";
}
static auto EmitAssociatedEntityAsConstant(ConstantContext& context,
SemIR::AssociatedEntity inst)
static auto EmitAsConstant(ConstantContext& context,
SemIR::AssociatedEntity inst) -> llvm::Constant* {
return context.GetUnusedConstant(inst.type_id);
}
static auto EmitAsConstant(ConstantContext& context, SemIR::BaseDecl inst)
-> llvm::Constant* {
return context.GetUnusedConstant(inst.type_id);
}
static auto EmitBaseDeclAsConstant(ConstantContext& context,
SemIR::BaseDecl inst) -> llvm::Constant* {
return context.GetUnusedConstant(inst.type_id);
}
static auto EmitBoolLiteralAsConstant(ConstantContext& context,
SemIR::BoolLiteral inst)
static auto EmitAsConstant(ConstantContext& context, SemIR::BoolLiteral inst)
-> llvm::Constant* {
return llvm::ConstantInt::get(llvm::Type::getInt1Ty(context.llvm_context()),
inst.value.index);
}
static auto EmitBoundMethodAsConstant(ConstantContext& context,
SemIR::BoundMethod inst)
static auto EmitAsConstant(ConstantContext& context, SemIR::BoundMethod inst)
-> llvm::Constant* {
// Propagate just the function; the object is separately provided to the
// enclosing call as an implicit argument.
return context.GetConstant(inst.function_id);
}
static auto EmitFieldDeclAsConstant(ConstantContext& context,
SemIR::FieldDecl inst) -> llvm::Constant* {
static auto EmitAsConstant(ConstantContext& context, SemIR::FieldDecl inst)
-> llvm::Constant* {
return context.GetUnusedConstant(inst.type_id);
}
static auto EmitFloatLiteralAsConstant(ConstantContext& context,
SemIR::FloatLiteral inst)
static auto EmitAsConstant(ConstantContext& context, SemIR::FloatLiteral inst)
-> llvm::Constant* {
const llvm::APFloat& value = context.sem_ir().floats().Get(inst.float_id);
return llvm::ConstantFP::get(context.GetType(inst.type_id), value);
}
static auto EmitInterfaceWitnessAsConstant(ConstantContext& context,
SemIR::InterfaceWitness inst)
-> llvm::Constant* {
static auto EmitAsConstant(ConstantContext& context,
SemIR::InterfaceWitness inst) -> llvm::Constant* {
// TODO: For dynamic dispatch, we might want to lower witness tables as
// constants.
return context.GetUnusedConstant(inst.type_id);
}
static auto EmitIntLiteralAsConstant(ConstantContext& context,
SemIR::IntLiteral inst)
static auto EmitAsConstant(ConstantContext& context, SemIR::IntLiteral inst)
-> llvm::Constant* {
return llvm::ConstantInt::get(context.GetType(inst.type_id),
context.sem_ir().ints().Get(inst.int_id));
}
static auto EmitNamespaceAsConstant(ConstantContext& context,
SemIR::Namespace inst) -> llvm::Constant* {
static auto EmitAsConstant(ConstantContext& context, SemIR::Namespace inst)
-> llvm::Constant* {
return context.GetUnusedConstant(inst.type_id);
}
static auto EmitStringLiteralAsConstant(ConstantContext& /*context*/,
SemIR::StringLiteral inst)
-> llvm::Constant* {
static auto EmitAsConstant(ConstantContext& /*context*/,
SemIR::StringLiteral inst) -> llvm::Constant* {
CARBON_FATAL() << "TODO: Add support: " << inst;
}
static auto EmitStructTypeFieldAsConstant(ConstantContext& /*context*/,
SemIR::StructTypeField /*inst*/)
-> llvm::Constant* {
static auto EmitAsConstant(ConstantContext& /*context*/,
SemIR::StructTypeField /*inst*/) -> llvm::Constant* {
// A StructTypeField isn't a value, so this constant value won't ever be used.
// It also doesn't even have a type, so we can't use GetUnusedConstant.
return nullptr;
@@ -240,21 +235,17 @@ auto LowerConstants(FileContext& file_context,
auto inst = file_context.sem_ir().insts().Get(inst_id);
llvm::Constant* value = nullptr;
CARBON_KIND_SWITCH(inst) {
#define CARBON_SEM_IR_INST_KIND_CONSTANT_NEVER(...)
#define CARBON_SEM_IR_INST_KIND_CONSTANT_SYMBOLIC_ONLY(...)
#define CARBON_SEM_IR_INST_KIND(Name) \
case CARBON_KIND(SemIR::Name const_inst): \
value = Emit##Name##AsConstant(context, const_inst); \
break;
#define CARBON_SEM_IR_INST_KIND(Name) \
case CARBON_KIND(SemIR::Name const_inst): { \
value = EmitAsConstant(context, const_inst); \
break; \
}
#include "toolchain/sem_ir/inst_kind.def"
default:
CARBON_FATAL() << "Unexpected constant instruction kind " << inst;
}
constants[inst_id.index] = value;
context.SetLastLoweredConstantIndex(inst_id.index);
}
}
} // namespace Carbon::Lower
} // namespace Carbon::Lower
+133 -97
View File
@@ -309,107 +309,143 @@ auto FileContext::BuildFunctionDefinition(SemIR::FunctionId function_id)
}
}
auto FileContext::BuildType(SemIR::InstId inst_id) -> llvm::Type* {
CARBON_KIND_SWITCH(sem_ir_->insts().Get(inst_id)) {
case CARBON_KIND(SemIR::ArrayType inst): {
return llvm::ArrayType::get(GetType(inst.element_type_id),
sem_ir_->GetArrayBoundValue(inst.bound_id));
}
case CARBON_KIND(SemIR::BuiltinInst inst): {
switch (inst.builtin_inst_kind) {
case SemIR::BuiltinInstKind::Invalid:
case SemIR::BuiltinInstKind::Error:
CARBON_FATAL() << "Unexpected builtin type in lowering.";
case SemIR::BuiltinInstKind::TypeType:
return GetTypeType();
case SemIR::BuiltinInstKind::FloatType:
return llvm::Type::getDoubleTy(*llvm_context_);
case SemIR::BuiltinInstKind::IntType:
return llvm::Type::getInt32Ty(*llvm_context_);
case SemIR::BuiltinInstKind::BoolType:
// TODO: We may want to have different representations for `bool`
// storage
// (`i8`) versus for `bool` values (`i1`).
return llvm::Type::getInt1Ty(*llvm_context_);
case SemIR::BuiltinInstKind::StringType:
// TODO: Decide how we want to represent `StringType`.
return llvm::PointerType::get(*llvm_context_, 0);
case SemIR::BuiltinInstKind::BoundMethodType:
case SemIR::BuiltinInstKind::NamespaceType:
case SemIR::BuiltinInstKind::WitnessType:
// Return an empty struct as a placeholder.
return llvm::StructType::get(*llvm_context_);
}
}
case CARBON_KIND(SemIR::ClassType inst): {
auto object_repr_id =
sem_ir_->classes().Get(inst.class_id).object_repr_id;
return GetType(object_repr_id);
}
case CARBON_KIND(SemIR::ConstType inst): {
return GetType(inst.inner_id);
}
case SemIR::FloatType::Kind: {
// TODO: Handle different sizes.
return llvm::Type::getDoubleTy(*llvm_context_);
}
case CARBON_KIND(SemIR::IntType inst): {
auto width =
sem_ir_->insts().TryGetAs<SemIR::IntLiteral>(inst.bit_width_id);
CARBON_CHECK(width) << "Can't lower int type with symbolic width";
return llvm::IntegerType::get(
*llvm_context_, sem_ir_->ints().Get(width->int_id).getZExtValue());
}
case SemIR::PointerType::Kind: {
return llvm::PointerType::get(*llvm_context_, /*AddressSpace=*/0);
}
case CARBON_KIND(SemIR::StructType inst): {
auto fields = sem_ir_->inst_blocks().Get(inst.fields_id);
llvm::SmallVector<llvm::Type*> subtypes;
subtypes.reserve(fields.size());
for (auto field_id : fields) {
auto field = sem_ir_->insts().GetAs<SemIR::StructTypeField>(field_id);
subtypes.push_back(GetType(field.field_type_id));
}
return llvm::StructType::get(*llvm_context_, subtypes);
}
case CARBON_KIND(SemIR::TupleType inst): {
// TODO: Investigate special-casing handling of empty tuples so that they
// can be collectively replaced with LLVM's void, particularly around
// function returns. LLVM doesn't allow declaring variables with a void
// type, so that may require significant special casing.
auto elements = sem_ir_->type_blocks().Get(inst.elements_id);
llvm::SmallVector<llvm::Type*> subtypes;
subtypes.reserve(elements.size());
for (auto element_id : elements) {
subtypes.push_back(GetType(element_id));
}
return llvm::StructType::get(*llvm_context_, subtypes);
}
case SemIR::AssociatedEntityType::Kind:
case SemIR::InterfaceType::Kind:
case SemIR::FunctionType::Kind:
case SemIR::GenericClassType::Kind:
case SemIR::GenericInterfaceType::Kind:
case SemIR::UnboundElementType::Kind: {
static auto BuildTypeForInst(FileContext& context, SemIR::ArrayType inst)
-> llvm::Type* {
return llvm::ArrayType::get(
context.GetType(inst.element_type_id),
context.sem_ir().GetArrayBoundValue(inst.bound_id));
}
static auto BuildTypeForInst(FileContext& context, SemIR::BuiltinInst inst)
-> llvm::Type* {
switch (inst.builtin_inst_kind) {
case SemIR::BuiltinInstKind::Invalid:
case SemIR::BuiltinInstKind::Error:
CARBON_FATAL() << "Unexpected builtin type in lowering.";
case SemIR::BuiltinInstKind::TypeType:
return context.GetTypeType();
case SemIR::BuiltinInstKind::FloatType:
return llvm::Type::getDoubleTy(context.llvm_context());
case SemIR::BuiltinInstKind::IntType:
return llvm::Type::getInt32Ty(context.llvm_context());
case SemIR::BuiltinInstKind::BoolType:
// TODO: We may want to have different representations for `bool`
// storage
// (`i8`) versus for `bool` values (`i1`).
return llvm::Type::getInt1Ty(context.llvm_context());
case SemIR::BuiltinInstKind::StringType:
// TODO: Decide how we want to represent `StringType`.
return llvm::PointerType::get(context.llvm_context(), 0);
case SemIR::BuiltinInstKind::BoundMethodType:
case SemIR::BuiltinInstKind::NamespaceType:
case SemIR::BuiltinInstKind::WitnessType:
// Return an empty struct as a placeholder.
// TODO: Should we model an interface as a witness table, or an associated
// entity as an index?
return llvm::StructType::get(*llvm_context_);
}
return llvm::StructType::get(context.llvm_context());
}
}
// Treat non-monomorphized symbolic types as opaque.
case SemIR::BindSymbolicName::Kind:
case SemIR::InterfaceWitnessAccess::Kind: {
return llvm::StructType::get(*llvm_context_);
}
// BuildTypeForInst is used to construct types for FileContext::BuildType below.
// Implementations return the LLVM type for the instruction. This first overload
// is the fallback handler for non-type instructions.
template <typename InstT>
requires(InstT::Kind.is_type() == SemIR::InstIsType::Never)
static auto BuildTypeForInst(FileContext& /*context*/, InstT inst)
-> llvm::Type* {
CARBON_FATAL() << "Cannot use inst as type: " << inst;
}
#define CARBON_SEM_IR_INST_KIND_TYPE_ALWAYS(...)
#define CARBON_SEM_IR_INST_KIND_TYPE_MAYBE(...)
#define CARBON_SEM_IR_INST_KIND(Name) case SemIR::Name::Kind:
static auto BuildTypeForInst(FileContext& context, SemIR::ClassType inst)
-> llvm::Type* {
auto object_repr_id =
context.sem_ir().classes().Get(inst.class_id).object_repr_id;
return context.GetType(object_repr_id);
}
static auto BuildTypeForInst(FileContext& context, SemIR::ConstType inst)
-> llvm::Type* {
return context.GetType(inst.inner_id);
}
static auto BuildTypeForInst(FileContext& context, SemIR::FloatType /*inst*/)
-> llvm::Type* {
// TODO: Handle different sizes.
return llvm::Type::getDoubleTy(context.llvm_context());
}
static auto BuildTypeForInst(FileContext& context, SemIR::IntType inst)
-> llvm::Type* {
auto width =
context.sem_ir().insts().TryGetAs<SemIR::IntLiteral>(inst.bit_width_id);
CARBON_CHECK(width) << "Can't lower int type with symbolic width";
return llvm::IntegerType::get(
context.llvm_context(),
context.sem_ir().ints().Get(width->int_id).getZExtValue());
}
static auto BuildTypeForInst(FileContext& context, SemIR::PointerType /*inst*/)
-> llvm::Type* {
return llvm::PointerType::get(context.llvm_context(), /*AddressSpace=*/0);
}
static auto BuildTypeForInst(FileContext& context, SemIR::StructType inst)
-> llvm::Type* {
auto fields = context.sem_ir().inst_blocks().Get(inst.fields_id);
llvm::SmallVector<llvm::Type*> subtypes;
subtypes.reserve(fields.size());
for (auto field_id : fields) {
auto field =
context.sem_ir().insts().GetAs<SemIR::StructTypeField>(field_id);
subtypes.push_back(context.GetType(field.field_type_id));
}
return llvm::StructType::get(context.llvm_context(), subtypes);
}
static auto BuildTypeForInst(FileContext& context, SemIR::TupleType inst)
-> llvm::Type* {
// TODO: Investigate special-casing handling of empty tuples so that they
// can be collectively replaced with LLVM's void, particularly around
// function returns. LLVM doesn't allow declaring variables with a void
// type, so that may require significant special casing.
auto elements = context.sem_ir().type_blocks().Get(inst.elements_id);
llvm::SmallVector<llvm::Type*> subtypes;
subtypes.reserve(elements.size());
for (auto element_id : elements) {
subtypes.push_back(context.GetType(element_id));
}
return llvm::StructType::get(context.llvm_context(), subtypes);
}
template <typename InstT>
requires(InstT::Kind.template IsAnyOf<
SemIR::AssociatedEntityType, SemIR::FunctionType,
SemIR::GenericClassType, SemIR::GenericInterfaceType,
SemIR::InterfaceType, SemIR::UnboundElementType>())
static auto BuildTypeForInst(FileContext& context, InstT /*inst*/)
-> llvm::Type* {
// Return an empty struct as a placeholder.
// TODO: Should we model an interface as a witness table, or an associated
// entity as an index?
return llvm::StructType::get(context.llvm_context());
}
// Treat non-monomorphized symbolic types as opaque.
template <typename InstT>
requires(InstT::Kind.template IsAnyOf<SemIR::BindSymbolicName,
SemIR::InterfaceWitnessAccess>())
static auto BuildTypeForInst(FileContext& context, InstT /*inst*/)
-> llvm::Type* {
return llvm::StructType::get(context.llvm_context());
}
auto FileContext::BuildType(SemIR::InstId inst_id) -> llvm::Type* {
// Use overload resolution to select the implementation, producing compile
// errors when BuildTypeForInst isn't defined for a given instruction.
CARBON_KIND_SWITCH(sem_ir_->insts().Get(inst_id)) {
#define CARBON_SEM_IR_INST_KIND(Name) \
case CARBON_KIND(SemIR::Name inst): { \
return BuildTypeForInst(*this, inst); \
}
#include "toolchain/sem_ir/inst_kind.def"
CARBON_FATAL() << "Cannot use inst as type: " << inst_id << " "
<< sem_ir_->insts().Get(inst_id);
}
}
+9 -9
View File
@@ -49,6 +49,15 @@ class FileContext {
// Returns a global value for the given instruction.
auto GetGlobal(SemIR::InstId inst_id) -> llvm::Value*;
// Returns the empty LLVM struct type used to represent the type `type`.
auto GetTypeType() -> llvm::StructType* {
if (!type_type_) {
// `type` is lowered to an empty LLVM StructType.
type_type_ = llvm::StructType::create(*llvm_context_, {}, "type");
}
return type_type_;
}
auto llvm_context() -> llvm::LLVMContext& { return *llvm_context_; }
auto llvm_module() -> llvm::Module& { return *llvm_module_; }
auto sem_ir() -> const SemIR::File& { return *sem_ir_; }
@@ -67,15 +76,6 @@ class FileContext {
// the caller.
auto BuildType(SemIR::InstId inst_id) -> llvm::Type*;
// Returns the empty LLVM struct type used to represent the type `type`.
auto GetTypeType() -> llvm::StructType* {
if (!type_type_) {
// `type` is lowered to an empty LLVM StructType.
type_type_ = llvm::StructType::create(*llvm_context_, {}, "type");
}
return type_type_;
}
// State for building the LLVM IR.
llvm::LLVMContext* llvm_context_;
std::unique_ptr<llvm::Module> llvm_module_;
+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* {
+2 -3
View File
@@ -159,9 +159,8 @@ class FunctionContext {
Map<SemIR::InstId, llvm::Value*> locals_;
};
// Declare handlers for each SemIR::File instruction that is not always
// constant.
#define CARBON_SEM_IR_INST_KIND_CONSTANT_ALWAYS(Name)
// Declare handlers for each SemIR::File instruction. Note that these aren't all
// defined.
#define CARBON_SEM_IR_INST_KIND(Name) \
auto Handle##Name(FunctionContext& context, SemIR::InstId inst_id, \
SemIR::Name inst) -> void;
+5
View File
@@ -160,6 +160,11 @@ auto HandleDeref(FunctionContext& context, SemIR::InstId inst_id,
context.SetLocal(inst_id, context.GetValue(inst.pointer_id));
}
auto HandleFacetTypeAccess(FunctionContext& context, SemIR::InstId inst_id,
SemIR::FacetTypeAccess /*inst*/) -> void {
context.SetLocal(inst_id, context.GetTypeAsValue());
}
auto HandleInitializeFrom(FunctionContext& context, SemIR::InstId /*inst_id*/,
SemIR::InitializeFrom inst) -> void {
auto storage_type_id = context.sem_ir().insts().Get(inst.dest_id).type_id();
-27
View File
@@ -1,27 +0,0 @@
// 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
#include "toolchain/lower/function_context.h"
#include "toolchain/sem_ir/typed_insts.h"
namespace Carbon::Lower {
// For instructions that are always of type `type`, produce the trivial runtime
// representation of type `type`.
#define CARBON_SEM_IR_INST_KIND_TYPE_NEVER(...)
#define CARBON_SEM_IR_INST_KIND_TYPE_MAYBE(...)
#define CARBON_SEM_IR_INST_KIND_CONSTANT_ALWAYS(...)
#define CARBON_SEM_IR_INST_KIND(Name) \
auto Handle##Name(FunctionContext& context, SemIR::InstId inst_id, \
SemIR::Name /*inst*/) -> void { \
context.SetLocal(inst_id, context.GetTypeAsValue()); \
}
#include "toolchain/sem_ir/inst_kind.def"
auto HandleFacetTypeAccess(FunctionContext& context, SemIR::InstId inst_id,
SemIR::FacetTypeAccess /*inst*/) -> void {
context.SetLocal(inst_id, context.GetTypeAsValue());
}
} // namespace Carbon::Lower
+8 -27
View File
@@ -176,34 +176,15 @@ auto File::OutputYaml(bool include_builtins) const -> Yaml::OutputMapping {
// precedence of that type's syntax. Higher numbers correspond to higher
// precedence.
static auto GetTypePrecedence(InstKind kind) -> int {
switch (kind) {
case ArrayType::Kind:
case AssociatedEntityType::Kind:
case BindSymbolicName::Kind:
case BuiltinInst::Kind:
case ClassType::Kind:
case FloatType::Kind:
case FunctionType::Kind:
case GenericClassType::Kind:
case GenericInterfaceType::Kind:
case InterfaceType::Kind:
case InterfaceWitnessAccess::Kind:
case IntType::Kind:
case StructType::Kind:
case TupleType::Kind:
case UnboundElementType::Kind:
return 0;
case ConstType::Kind:
return -1;
case PointerType::Kind:
return -2;
#define CARBON_SEM_IR_INST_KIND_TYPE_ALWAYS(...)
#define CARBON_SEM_IR_INST_KIND_TYPE_MAYBE(...)
#define CARBON_SEM_IR_INST_KIND(Name) case SemIR::Name::Kind:
#include "toolchain/sem_ir/inst_kind.def"
CARBON_FATAL() << "GetTypePrecedence for non-type inst kind " << kind;
CARBON_CHECK(kind.is_type() != InstIsType::Never)
<< "Only called for kinds which can define a type.";
if (kind == ConstType::Kind) {
return -1;
}
if (kind == PointerType::Kind) {
return -2;
}
return 0;
}
// Implements File::StringifyTypeExpr. Static to prevent accidental use of
+4 -26
View File
@@ -13,12 +13,12 @@ CARBON_DEFINE_ENUM_CLASS_NAMES(InstKind) = {
#include "toolchain/sem_ir/inst_kind.def"
};
auto InstKind::ir_name() const -> llvm::StringLiteral {
static constexpr const llvm::StringLiteral Table[] = {
#define CARBON_SEM_IR_INST_KIND(Name) SemIR::Name::Kind.ir_name(),
auto InstKind::definition_info(InstKind inst_kind) -> const DefinitionInfo& {
static constexpr InstKind::DefinitionInfo DefinitionInfos[] = {
#define CARBON_SEM_IR_INST_KIND(Name) SemIR::Name::Kind.info_,
#include "toolchain/sem_ir/inst_kind.def"
};
return Table[AsInt()];
return DefinitionInfos[inst_kind.AsInt()];
}
auto InstKind::value_kind() const -> InstValueKind {
@@ -31,26 +31,4 @@ auto InstKind::value_kind() const -> InstValueKind {
return Table[AsInt()];
}
auto InstKind::constant_kind() const -> InstConstantKind {
static constexpr InstConstantKind Table[] = {
#define CARBON_SEM_IR_INST_KIND_CONSTANT_NEVER(...) InstConstantKind::Never,
#define CARBON_SEM_IR_INST_KIND_CONSTANT_SYMBOLIC_ONLY(...) \
InstConstantKind::SymbolicOnly,
#define CARBON_SEM_IR_INST_KIND_CONSTANT_CONDITIONAL(...) \
InstConstantKind::Conditional,
#define CARBON_SEM_IR_INST_KIND_CONSTANT_ALWAYS(...) InstConstantKind::Always,
#define CARBON_SEM_IR_INST_KIND(Name)
#include "toolchain/sem_ir/inst_kind.def"
};
return Table[AsInt()];
}
auto InstKind::terminator_kind() const -> TerminatorKind {
static constexpr const TerminatorKind Table[] = {
#define CARBON_SEM_IR_INST_KIND(Name) SemIR::Name::Kind.terminator_kind(),
#include "toolchain/sem_ir/inst_kind.def"
};
return Table[AsInt()];
}
} // namespace Carbon::SemIR
+78 -148
View File
@@ -10,160 +10,90 @@
// This macro should be defined before including this header:
// - CARBON_SEM_IR_INST_KIND(Name)
// Invoked for each kind of semantic instruction.
//
// The invocation of the above macro will be wrapped in one macro from each of
// the following sets, which by default expand to their argument:
//
// Whether the instruction can define a type:
// - CARBON_SEM_IR_INST_KIND_TYPE_ALWAYS(...)
// Invoked for each instruction that is always of type `type`, and might
// define a type constant.
// - CARBON_SEM_IR_INST_KIND_TYPE_MAYBE(...)
// Invoked for each instruction that is sometimes of type `type`, and might
// define a type constant.
// - CARBON_SEM_IR_INST_KIND_TYPE_NEVER(...)
// Invoked for each instruction that can never define a type constant. Note
// that such instructions can still have type `type`, but are not the
// canonical definition of any type.
//
// Whether the instruction can define a constant, see `InstConstantKind`:
// - CARBON_SEM_IR_INST_KIND_CONSTANT_NEVER(...)
// Invoked when `constant_kind()` is `InstConstantKind::Never`.
// - CARBON_SEM_IR_INST_KIND_CONSTANT_SYMBOLIC_ONLY(...)
// Invoked when `constant_kind()` is `InstConstantKind::SymbolicOnly`.
// - CARBON_SEM_IR_INST_KIND_CONSTANT_CONDITIONAL(...)
// Invoked when `constant_kind()` is `InstConstantKind::Conditional`.
// - CARBON_SEM_IR_INST_KIND_CONSTANT_ALWAYS(...)
// Invoked when `constant_kind()` is `InstConstantKind::Always`.
//
// Defining these is optional.
#ifndef CARBON_SEM_IR_INST_KIND
#error "Must define the x-macro to use this file."
#endif
#ifndef CARBON_SEM_IR_INST_KIND_TYPE_NEVER
#define CARBON_SEM_IR_INST_KIND_TYPE_NEVER(...) __VA_ARGS__
#endif
#ifndef CARBON_SEM_IR_INST_KIND_TYPE_MAYBE
#define CARBON_SEM_IR_INST_KIND_TYPE_MAYBE(...) __VA_ARGS__
#endif
#ifndef CARBON_SEM_IR_INST_KIND_TYPE_ALWAYS
#define CARBON_SEM_IR_INST_KIND_TYPE_ALWAYS(...) __VA_ARGS__
#endif
#ifndef CARBON_SEM_IR_INST_KIND_CONSTANT_NEVER
#define CARBON_SEM_IR_INST_KIND_CONSTANT_NEVER(...) __VA_ARGS__
#endif
#ifndef CARBON_SEM_IR_INST_KIND_CONSTANT_SYMBOLIC_ONLY
#define CARBON_SEM_IR_INST_KIND_CONSTANT_SYMBOLIC_ONLY(...) __VA_ARGS__
#endif
#ifndef CARBON_SEM_IR_INST_KIND_CONSTANT_CONDITIONAL
#define CARBON_SEM_IR_INST_KIND_CONSTANT_CONDITIONAL(...) __VA_ARGS__
#endif
#ifndef CARBON_SEM_IR_INST_KIND_CONSTANT_ALWAYS
#define CARBON_SEM_IR_INST_KIND_CONSTANT_ALWAYS(...) __VA_ARGS__
#endif
#define CARBON_SEM_IR_INST_KIND_IMPL(Name, IsType, IsConstant) \
CARBON_SEM_IR_INST_KIND_##IsType( \
CARBON_SEM_IR_INST_KIND_##IsConstant(CARBON_SEM_IR_INST_KIND(Name)))
// For each instruction kind declared here there is a matching definition in
// `typed_insts.h`.
CARBON_SEM_IR_INST_KIND_IMPL(AdaptDecl, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(AddrOf, TYPE_NEVER, CONSTANT_CONDITIONAL)
CARBON_SEM_IR_INST_KIND_IMPL(AddrPattern, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(ArrayIndex, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(ArrayInit, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(ArrayType, TYPE_ALWAYS, CONSTANT_CONDITIONAL)
CARBON_SEM_IR_INST_KIND_IMPL(AsCompatible, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(Assign, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(AssociatedConstantDecl, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(AssociatedEntity, TYPE_NEVER, CONSTANT_ALWAYS)
CARBON_SEM_IR_INST_KIND_IMPL(AssociatedEntityType, TYPE_ALWAYS,
CONSTANT_CONDITIONAL)
CARBON_SEM_IR_INST_KIND_IMPL(BaseDecl, TYPE_NEVER, CONSTANT_ALWAYS)
CARBON_SEM_IR_INST_KIND_IMPL(BindAlias, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(ExportDecl, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(BindName, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(BindSymbolicName, TYPE_MAYBE,
CONSTANT_SYMBOLIC_ONLY)
CARBON_SEM_IR_INST_KIND_IMPL(BindValue, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(BlockArg, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(BoolLiteral, TYPE_NEVER, CONSTANT_ALWAYS)
CARBON_SEM_IR_INST_KIND_IMPL(BoundMethod, TYPE_NEVER, CONSTANT_CONDITIONAL)
CARBON_SEM_IR_INST_KIND_IMPL(Branch, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(BranchIf, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(BranchWithArg, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(BuiltinInst, TYPE_ALWAYS, CONSTANT_ALWAYS)
CARBON_SEM_IR_INST_KIND_IMPL(Call, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(ClassDecl, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(ClassElementAccess, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(ClassInit, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(ClassType, TYPE_ALWAYS, CONSTANT_ALWAYS)
CARBON_SEM_IR_INST_KIND_IMPL(ConstType, TYPE_ALWAYS, CONSTANT_CONDITIONAL)
CARBON_SEM_IR_INST_KIND_IMPL(Converted, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(Deref, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(FacetTypeAccess, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(FieldDecl, TYPE_NEVER, CONSTANT_ALWAYS)
CARBON_SEM_IR_INST_KIND_IMPL(FloatLiteral, TYPE_NEVER, CONSTANT_ALWAYS)
CARBON_SEM_IR_INST_KIND_IMPL(FloatType, TYPE_ALWAYS, CONSTANT_CONDITIONAL)
CARBON_SEM_IR_INST_KIND_IMPL(FunctionDecl, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(FunctionType, TYPE_ALWAYS, CONSTANT_CONDITIONAL)
CARBON_SEM_IR_INST_KIND_IMPL(GenericClassType, TYPE_ALWAYS,
CONSTANT_CONDITIONAL)
CARBON_SEM_IR_INST_KIND_IMPL(GenericInterfaceType, TYPE_ALWAYS,
CONSTANT_CONDITIONAL)
CARBON_SEM_IR_INST_KIND_IMPL(ImplDecl, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(ImportDecl, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(ImportRefUnloaded, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(ImportRefLoaded, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(InitializeFrom, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(InterfaceDecl, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(InterfaceType, TYPE_ALWAYS, CONSTANT_ALWAYS)
CARBON_SEM_IR_INST_KIND_IMPL(InterfaceWitness, TYPE_NEVER, CONSTANT_CONDITIONAL)
CARBON_SEM_IR_INST_KIND_IMPL(InterfaceWitnessAccess, TYPE_MAYBE,
CONSTANT_SYMBOLIC_ONLY)
CARBON_SEM_IR_INST_KIND_IMPL(IntLiteral, TYPE_NEVER, CONSTANT_ALWAYS)
CARBON_SEM_IR_INST_KIND_IMPL(IntType, TYPE_ALWAYS, CONSTANT_CONDITIONAL)
CARBON_SEM_IR_INST_KIND_IMPL(NameRef, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(Namespace, TYPE_NEVER, CONSTANT_ALWAYS)
CARBON_SEM_IR_INST_KIND_IMPL(Param, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(PointerType, TYPE_ALWAYS, CONSTANT_CONDITIONAL)
CARBON_SEM_IR_INST_KIND_IMPL(ReturnExpr, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(Return, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(SpliceBlock, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(StringLiteral, TYPE_NEVER, CONSTANT_ALWAYS)
CARBON_SEM_IR_INST_KIND_IMPL(StructAccess, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(StructInit, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(StructLiteral, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(StructType, TYPE_ALWAYS, CONSTANT_CONDITIONAL)
CARBON_SEM_IR_INST_KIND_IMPL(StructTypeField, TYPE_NEVER, CONSTANT_CONDITIONAL)
CARBON_SEM_IR_INST_KIND_IMPL(StructValue, TYPE_NEVER, CONSTANT_CONDITIONAL)
CARBON_SEM_IR_INST_KIND_IMPL(TemporaryStorage, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(Temporary, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(TupleAccess, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(TupleIndex, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(TupleInit, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(TupleLiteral, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(TupleType, TYPE_ALWAYS, CONSTANT_CONDITIONAL)
CARBON_SEM_IR_INST_KIND_IMPL(TupleValue, TYPE_NEVER, CONSTANT_CONDITIONAL)
CARBON_SEM_IR_INST_KIND_IMPL(UnaryOperatorNot, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(UnboundElementType, TYPE_ALWAYS,
CONSTANT_CONDITIONAL)
CARBON_SEM_IR_INST_KIND_IMPL(ValueAsRef, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(ValueOfInitializer, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND_IMPL(VarStorage, TYPE_NEVER, CONSTANT_NEVER)
CARBON_SEM_IR_INST_KIND(AdaptDecl)
CARBON_SEM_IR_INST_KIND(AddrOf)
CARBON_SEM_IR_INST_KIND(AddrPattern)
CARBON_SEM_IR_INST_KIND(ArrayIndex)
CARBON_SEM_IR_INST_KIND(ArrayInit)
CARBON_SEM_IR_INST_KIND(ArrayType)
CARBON_SEM_IR_INST_KIND(AsCompatible)
CARBON_SEM_IR_INST_KIND(Assign)
CARBON_SEM_IR_INST_KIND(AssociatedConstantDecl)
CARBON_SEM_IR_INST_KIND(AssociatedEntity)
CARBON_SEM_IR_INST_KIND(AssociatedEntityType)
CARBON_SEM_IR_INST_KIND(BaseDecl)
CARBON_SEM_IR_INST_KIND(BindAlias)
CARBON_SEM_IR_INST_KIND(ExportDecl)
CARBON_SEM_IR_INST_KIND(BindName)
CARBON_SEM_IR_INST_KIND(BindSymbolicName)
CARBON_SEM_IR_INST_KIND(BindValue)
CARBON_SEM_IR_INST_KIND(BlockArg)
CARBON_SEM_IR_INST_KIND(BoolLiteral)
CARBON_SEM_IR_INST_KIND(BoundMethod)
CARBON_SEM_IR_INST_KIND(Branch)
CARBON_SEM_IR_INST_KIND(BranchIf)
CARBON_SEM_IR_INST_KIND(BranchWithArg)
CARBON_SEM_IR_INST_KIND(BuiltinInst)
CARBON_SEM_IR_INST_KIND(Call)
CARBON_SEM_IR_INST_KIND(ClassDecl)
CARBON_SEM_IR_INST_KIND(ClassElementAccess)
CARBON_SEM_IR_INST_KIND(ClassInit)
CARBON_SEM_IR_INST_KIND(ClassType)
CARBON_SEM_IR_INST_KIND(ConstType)
CARBON_SEM_IR_INST_KIND(Converted)
CARBON_SEM_IR_INST_KIND(Deref)
CARBON_SEM_IR_INST_KIND(FacetTypeAccess)
CARBON_SEM_IR_INST_KIND(FieldDecl)
CARBON_SEM_IR_INST_KIND(FloatLiteral)
CARBON_SEM_IR_INST_KIND(FloatType)
CARBON_SEM_IR_INST_KIND(FunctionDecl)
CARBON_SEM_IR_INST_KIND(FunctionType)
CARBON_SEM_IR_INST_KIND(GenericClassType)
CARBON_SEM_IR_INST_KIND(GenericInterfaceType)
CARBON_SEM_IR_INST_KIND(ImplDecl)
CARBON_SEM_IR_INST_KIND(ImportDecl)
CARBON_SEM_IR_INST_KIND(ImportRefUnloaded)
CARBON_SEM_IR_INST_KIND(ImportRefLoaded)
CARBON_SEM_IR_INST_KIND(InitializeFrom)
CARBON_SEM_IR_INST_KIND(InterfaceDecl)
CARBON_SEM_IR_INST_KIND(InterfaceType)
CARBON_SEM_IR_INST_KIND(InterfaceWitness)
CARBON_SEM_IR_INST_KIND(InterfaceWitnessAccess)
CARBON_SEM_IR_INST_KIND(IntLiteral)
CARBON_SEM_IR_INST_KIND(IntType)
CARBON_SEM_IR_INST_KIND(NameRef)
CARBON_SEM_IR_INST_KIND(Namespace)
CARBON_SEM_IR_INST_KIND(Param)
CARBON_SEM_IR_INST_KIND(PointerType)
CARBON_SEM_IR_INST_KIND(ReturnExpr)
CARBON_SEM_IR_INST_KIND(Return)
CARBON_SEM_IR_INST_KIND(SpliceBlock)
CARBON_SEM_IR_INST_KIND(StringLiteral)
CARBON_SEM_IR_INST_KIND(StructAccess)
CARBON_SEM_IR_INST_KIND(StructInit)
CARBON_SEM_IR_INST_KIND(StructLiteral)
CARBON_SEM_IR_INST_KIND(StructType)
CARBON_SEM_IR_INST_KIND(StructTypeField)
CARBON_SEM_IR_INST_KIND(StructValue)
CARBON_SEM_IR_INST_KIND(TemporaryStorage)
CARBON_SEM_IR_INST_KIND(Temporary)
CARBON_SEM_IR_INST_KIND(TupleAccess)
CARBON_SEM_IR_INST_KIND(TupleIndex)
CARBON_SEM_IR_INST_KIND(TupleInit)
CARBON_SEM_IR_INST_KIND(TupleLiteral)
CARBON_SEM_IR_INST_KIND(TupleType)
CARBON_SEM_IR_INST_KIND(TupleValue)
CARBON_SEM_IR_INST_KIND(UnaryOperatorNot)
CARBON_SEM_IR_INST_KIND(UnboundElementType)
CARBON_SEM_IR_INST_KIND(ValueAsRef)
CARBON_SEM_IR_INST_KIND(ValueOfInitializer)
CARBON_SEM_IR_INST_KIND(VarStorage)
#undef CARBON_SEM_IR_INST_KIND_TYPE_ALWAYS
#undef CARBON_SEM_IR_INST_KIND_TYPE_MAYBE
#undef CARBON_SEM_IR_INST_KIND_TYPE_NEVER
#undef CARBON_SEM_IR_INST_KIND_CONSTANT_NEVER
#undef CARBON_SEM_IR_INST_KIND_CONSTANT_SYMBOLIC_ONLY
#undef CARBON_SEM_IR_INST_KIND_CONSTANT_CONDITIONAL
#undef CARBON_SEM_IR_INST_KIND_CONSTANT_ALWAYS
#undef CARBON_SEM_IR_INST_KIND_IMPL
#undef CARBON_SEM_IR_INST_KIND
+68 -44
View File
@@ -13,6 +13,17 @@
namespace Carbon::SemIR {
// Whether an instruction defines a type.
enum class InstIsType : int8_t {
// Always of type `type`, and might define a type constant.
Always,
// Sometimes of type `type`, and might define a type constant.
Maybe,
// Never defines a type constant. Note that such instructions can still have
// type `type`, but are not the canonical definition of any type.
Never,
};
// Whether an instruction produces or represents a value, and if so, what kind
// of value.
enum class InstValueKind : int8_t {
@@ -75,42 +86,62 @@ class InstKind : public CARBON_ENUM_BASE(InstKind) {
template <typename TypedNodeId>
class Definition;
// Information about a definition. See associated accessors below for
// comments.
struct DefinitionInfo {
llvm::StringLiteral ir_name;
InstIsType is_type = InstIsType::Never;
InstConstantKind constant_kind = InstConstantKind::Never;
TerminatorKind terminator_kind = TerminatorKind::NotTerminator;
bool is_lowered = true;
};
// Provides a definition for this instruction kind. Should only be called
// once, to construct the kind as part of defining it in `typed_insts.h`.
template <typename TypedNodeId>
constexpr auto Define(
llvm::StringLiteral ir_name,
TerminatorKind terminator_kind = TerminatorKind::NotTerminator) const
-> Definition<TypedNodeId>;
// As above, but for instructions which are not lowered; `Define(ir_name)`
// should be used instead of specifying `is_lowered = true`.
template <typename TypedNodeId>
constexpr auto Define(llvm::StringLiteral ir_name, bool is_lowered) const
-> Definition<TypedNodeId>;
constexpr auto Define(DefinitionInfo info) const -> Definition<TypedNodeId>;
using EnumBase::AsInt;
using EnumBase::Make;
// Returns the name to use for this instruction kind in Semantics IR.
auto ir_name() const -> llvm::StringLiteral;
// Returns true if the kind matches any of the provided instructions' kinds.
template <typename... InstT>
constexpr auto IsAnyOf() const -> bool {
return ((*this == InstT::Kind) || ...);
}
// Returns whether this kind of instruction is expected to produce a value.
// Returns the name to use for this instruction kind in Semantics IR.
auto ir_name() const -> llvm::StringLiteral {
return definition_info(*this).ir_name;
}
// Returns whether this instruction kind defines a type.
auto is_type() const -> InstIsType { return definition_info(*this).is_type; }
// Returns whether this instruction kind is expected to produce a value.
auto value_kind() const -> InstValueKind;
// Returns whether this kind of instruction is able to define a constant.
auto constant_kind() const -> InstConstantKind;
// Returns this instruction kind's category of allowed constants.
auto constant_kind() const -> InstConstantKind {
return definition_info(*this).constant_kind;
}
// Returns whether this instruction kind is a code block terminator, such as
// an unconditional branch instruction, or part of the termination sequence,
// such as a conditional branch instruction. The termination sequence of a
// code block appears after all other instructions, and ends with a
// terminator instruction.
auto terminator_kind() const -> TerminatorKind;
auto terminator_kind() const -> TerminatorKind {
return definition_info(*this).terminator_kind;
}
// Compute a fingerprint for this instruction kind, allowing its use as part
// of the key in a `FoldingSet`.
void Profile(llvm::FoldingSetNodeID& id) { id.AddInteger(AsInt()); }
private:
// Returns the DefinitionInfo for the kind.
static auto definition_info(InstKind kind) -> const DefinitionInfo&;
};
#define CARBON_SEM_IR_INST_KIND(Name) \
@@ -122,9 +153,9 @@ static_assert(sizeof(InstKind) == 1, "Kind objects include padding!");
// A definition of an instruction kind. This is an InstKind value, plus
// ancillary data such as the name to use for the node kind in LLVM IR. These
// are not copyable, and only one instance of this type is expected to exist per
// instruction kind, specifically `TypedInst::Kind`. Use `InstKind` instead as a
// thin wrapper around an instruction kind index.
// are not copyable, and only one instance of this type is expected to exist
// per instruction kind, specifically `TypedInst::Kind`. Use `InstKind`
// instead as a thin wrapper around an instruction kind index.
template <typename TypedNodeIdArg>
class InstKind::Definition : public InstKind {
public:
@@ -135,47 +166,40 @@ class InstKind::Definition : public InstKind {
auto operator=(const Definition&) -> Definition& = delete;
// Returns the name to use for this instruction kind in Semantics IR.
constexpr auto ir_name() const -> llvm::StringLiteral { return ir_name_; }
constexpr auto ir_name() const -> llvm::StringLiteral {
return info_.ir_name;
}
// Returns whether this instruction kind defines a type.
constexpr auto is_type() const -> InstIsType { return info_.is_type; }
// Returns this instruction kind's category of allowed constants.
constexpr auto constant_kind() const -> InstConstantKind {
return info_.constant_kind;
}
// Returns whether this instruction kind is a code block terminator. See
// InstKind::terminator_kind().
constexpr auto terminator_kind() const -> TerminatorKind {
return terminator_kind_;
return info_.terminator_kind;
}
// Returns true if the instruction is lowered.
constexpr auto is_lowered() const -> bool { return is_lowered_; }
constexpr auto is_lowered() const -> bool { return info_.is_lowered; }
private:
friend class InstKind;
constexpr Definition(InstKind kind, llvm::StringLiteral ir_name,
TerminatorKind terminator_kind, bool is_lowered)
: InstKind(kind),
ir_name_(ir_name),
terminator_kind_(terminator_kind),
is_lowered_(is_lowered) {}
constexpr Definition(InstKind kind, InstKind::DefinitionInfo info)
: InstKind(kind), info_(info) {}
llvm::StringLiteral ir_name_;
TerminatorKind terminator_kind_;
bool is_lowered_;
InstKind::DefinitionInfo info_;
};
template <typename TypedNodeId>
constexpr auto InstKind::Define(llvm::StringLiteral ir_name,
TerminatorKind terminator_kind) const
constexpr auto InstKind::Define(DefinitionInfo info) const
-> Definition<TypedNodeId> {
return Definition<TypedNodeId>(*this, ir_name, terminator_kind,
/*is_lowered=*/true);
}
template <typename TypedNodeId>
constexpr auto InstKind::Define(llvm::StringLiteral ir_name,
bool is_lowered) const
-> Definition<TypedNodeId> {
CARBON_CHECK(!is_lowered) << "Use Define(ir_name) instead";
return Definition<TypedNodeId>(*this, ir_name, TerminatorKind::NotTerminator,
/*is_lowered=*/is_lowered);
return Definition<TypedNodeId>(*this, info);
}
} // namespace Carbon::SemIR
+161 -105
View File
@@ -47,7 +47,7 @@ namespace Carbon::SemIR {
// An adapted type declaration in a class, of the form `adapt T;`.
struct AdaptDecl {
static constexpr auto Kind = InstKind::AdaptDecl.Define<Parse::AdaptDeclId>(
"adapt_decl", /*is_lowered=*/false);
{.ir_name = "adapt_decl", .is_lowered = false});
// No type_id; this is not a value.
TypeId adapted_type_id;
@@ -56,8 +56,8 @@ struct AdaptDecl {
// The `&` address-of operator, as in `&lvalue`.
struct AddrOf {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind =
InstKind::AddrOf.Define<Parse::NodeId>("addr_of");
static constexpr auto Kind = InstKind::AddrOf.Define<Parse::NodeId>(
{.ir_name = "addr_of", .constant_kind = InstConstantKind::Conditional});
TypeId type_id;
InstId lvalue_id;
@@ -67,7 +67,7 @@ struct AddrOf {
// generally be one of `AnyBindName`.
struct AddrPattern {
static constexpr auto Kind =
InstKind::AddrPattern.Define<Parse::AddrId>("addr_pattern");
InstKind::AddrPattern.Define<Parse::AddrId>({.ir_name = "addr_pattern"});
TypeId type_id;
// The `self` binding.
@@ -78,7 +78,7 @@ struct AddrPattern {
struct ArrayIndex {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind =
InstKind::ArrayIndex.Define<Parse::NodeId>("array_index");
InstKind::ArrayIndex.Define<Parse::NodeId>({.ir_name = "array_index"});
TypeId type_id;
InstId array_id;
@@ -138,7 +138,7 @@ struct AnyAggregateValue {
struct ArrayInit {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind =
InstKind::ArrayInit.Define<Parse::NodeId>("array_init");
InstKind::ArrayInit.Define<Parse::NodeId>({.ir_name = "array_init"});
TypeId type_id;
InstBlockId inits_id;
@@ -147,8 +147,10 @@ struct ArrayInit {
// An array of `element_type_id` values, sized to `bound_id`.
struct ArrayType {
static constexpr auto Kind =
InstKind::ArrayType.Define<Parse::ArrayExprId>("array_type");
static constexpr auto Kind = InstKind::ArrayType.Define<Parse::ArrayExprId>(
{.ir_name = "array_type",
.is_type = InstIsType::Always,
.constant_kind = InstConstantKind::Conditional});
TypeId type_id;
InstId bound_id;
@@ -157,8 +159,8 @@ struct ArrayType {
// Perform a no-op conversion to a compatible type.
struct AsCompatible {
static constexpr auto Kind =
InstKind::AsCompatible.Define<Parse::NodeId>("as_compatible");
static constexpr auto Kind = InstKind::AsCompatible.Define<Parse::NodeId>(
{.ir_name = "as_compatible"});
TypeId type_id;
InstId source_id;
@@ -170,7 +172,7 @@ struct AsCompatible {
struct Assign {
static constexpr auto Kind = InstKind::Assign.Define<
Parse::NodeIdOneOf<Parse::InfixOperatorEqualId, Parse::VariableDeclId>>(
"assign");
{.ir_name = "assign"});
// Assignments are statements, and so have no type.
InstId lhs_id;
@@ -181,7 +183,7 @@ struct Assign {
struct AssociatedConstantDecl {
static constexpr auto Kind =
InstKind::AssociatedConstantDecl.Define<Parse::NodeId>(
"assoc_const_decl", /*is_lowered=*/false);
{.ir_name = "assoc_const_decl", .is_lowered = false});
TypeId type_id;
NameId name_id;
@@ -192,8 +194,8 @@ struct AssociatedConstantDecl {
// This represents the entity before impl lookup is performed, and identifies
// the slot within a witness where the constant value will be found.
struct AssociatedEntity {
static constexpr auto Kind =
InstKind::AssociatedEntity.Define<Parse::NodeId>("assoc_entity");
static constexpr auto Kind = InstKind::AssociatedEntity.Define<Parse::NodeId>(
{.ir_name = "assoc_entity", .constant_kind = InstConstantKind::Always});
// The type of the associated entity. This is an AssociatedEntityType.
TypeId type_id;
@@ -206,7 +208,9 @@ struct AssociatedEntity {
struct AssociatedEntityType {
static constexpr auto Kind =
InstKind::AssociatedEntityType.Define<Parse::InvalidNodeId>(
"assoc_entity_type");
{.ir_name = "assoc_entity_type",
.is_type = InstIsType::Always,
.constant_kind = InstConstantKind::Conditional});
TypeId type_id;
InterfaceId interface_id;
@@ -217,8 +221,8 @@ struct AssociatedEntityType {
// element of the derived class, and the type of the `BaseDecl` instruction is
// an `UnboundElementType`.
struct BaseDecl {
static constexpr auto Kind =
InstKind::BaseDecl.Define<Parse::BaseDeclId>("base_decl");
static constexpr auto Kind = InstKind::BaseDecl.Define<Parse::BaseDeclId>(
{.ir_name = "base_decl", .constant_kind = InstConstantKind::Always});
TypeId type_id;
TypeId base_type_id;
@@ -253,7 +257,7 @@ struct AnyBindNameOrExportDecl {
// Binds a name as an alias.
struct BindAlias {
static constexpr auto Kind =
InstKind::BindAlias.Define<Parse::NodeId>("bind_alias");
InstKind::BindAlias.Define<Parse::NodeId>({.ir_name = "bind_alias"});
TypeId type_id;
BindNameId bind_name_id;
@@ -264,7 +268,7 @@ struct BindAlias {
struct BindName {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind =
InstKind::BindName.Define<Parse::NodeId>("bind_name");
InstKind::BindName.Define<Parse::NodeId>({.ir_name = "bind_name"});
TypeId type_id;
BindNameId bind_name_id;
@@ -275,8 +279,10 @@ struct BindName {
// Binds a symbolic name, such as `x` in `let x:! i32 = 7;`.
struct BindSymbolicName {
static constexpr auto Kind =
InstKind::BindSymbolicName.Define<Parse::NodeId>("bind_symbolic_name");
static constexpr auto Kind = InstKind::BindSymbolicName.Define<Parse::NodeId>(
{.ir_name = "bind_symbolic_name",
.is_type = InstIsType::Maybe,
.constant_kind = InstConstantKind::SymbolicOnly});
TypeId type_id;
BindNameId bind_name_id;
@@ -288,7 +294,7 @@ struct BindSymbolicName {
struct BindValue {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind =
InstKind::BindValue.Define<Parse::NodeId>("bind_value");
InstKind::BindValue.Define<Parse::NodeId>({.ir_name = "bind_value"});
TypeId type_id;
InstId value_id;
@@ -298,7 +304,7 @@ struct BindValue {
struct BlockArg {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind =
InstKind::BlockArg.Define<Parse::NodeId>("block_arg");
InstKind::BlockArg.Define<Parse::NodeId>({.ir_name = "block_arg"});
TypeId type_id;
InstBlockId block_id;
@@ -306,8 +312,8 @@ struct BlockArg {
// A literal bool value, `true` or `false`.
struct BoolLiteral {
static constexpr auto Kind =
InstKind::BoolLiteral.Define<Parse::NodeId>("bool_literal");
static constexpr auto Kind = InstKind::BoolLiteral.Define<Parse::NodeId>(
{.ir_name = "bool_literal", .constant_kind = InstConstantKind::Always});
TypeId type_id;
BoolValue value;
@@ -316,8 +322,9 @@ struct BoolLiteral {
// A bound method, that combines a function with the value to use for its
// `self` parameter, such as `object.MethodName`.
struct BoundMethod {
static constexpr auto Kind =
InstKind::BoundMethod.Define<Parse::NodeId>("bound_method");
static constexpr auto Kind = InstKind::BoundMethod.Define<Parse::NodeId>(
{.ir_name = "bound_method",
.constant_kind = InstConstantKind::Conditional});
TypeId type_id;
// The object argument in the bound method, which will be used to initialize
@@ -342,8 +349,8 @@ struct AnyBranch {
// Control flow to branch to the target block.
struct Branch {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind =
InstKind::Branch.Define<Parse::NodeId>("br", TerminatorKind::Terminator);
static constexpr auto Kind = InstKind::Branch.Define<Parse::NodeId>(
{.ir_name = "br", .terminator_kind = TerminatorKind::Terminator});
// Branches don't produce a value, so have no type.
InstBlockId target_id;
@@ -353,7 +360,7 @@ struct Branch {
struct BranchIf {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind = InstKind::BranchIf.Define<Parse::NodeId>(
"br", TerminatorKind::TerminatorSequence);
{.ir_name = "br", .terminator_kind = TerminatorKind::TerminatorSequence});
// Branches don't produce a value, so have no type.
InstBlockId target_id;
@@ -365,7 +372,7 @@ struct BranchIf {
struct BranchWithArg {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind = InstKind::BranchWithArg.Define<Parse::NodeId>(
"br", TerminatorKind::Terminator);
{.ir_name = "br", .terminator_kind = TerminatorKind::Terminator});
// Branches don't produce a value, so have no type.
InstBlockId target_id;
@@ -375,9 +382,12 @@ struct BranchWithArg {
// A builtin instruction, corresponding to instructions like
// InstId::BuiltinTypeType.
struct BuiltinInst {
// Builtin instructions don't have a parse node associated with them.
// Builtins don't have a parse node associated with them.
static constexpr auto Kind =
InstKind::BuiltinInst.Define<Parse::InvalidNodeId>("builtin");
InstKind::BuiltinInst.Define<Parse::InvalidNodeId>(
{.ir_name = "builtin",
.is_type = InstIsType::Always,
.constant_kind = InstConstantKind::Always});
TypeId type_id;
BuiltinInstKind builtin_inst_kind;
@@ -389,7 +399,8 @@ struct Call {
// For a syntactic call, the parse node will be a CallExprStartId. However,
// calls can arise from other syntaxes, such as operators and implicit
// conversions.
static constexpr auto Kind = InstKind::Call.Define<Parse::NodeId>("call");
static constexpr auto Kind =
InstKind::Call.Define<Parse::NodeId>({.ir_name = "call"});
TypeId type_id;
InstId callee_id;
@@ -403,7 +414,8 @@ struct Call {
// A class declaration.
struct ClassDecl {
static constexpr auto Kind =
InstKind::ClassDecl.Define<Parse::AnyClassDeclId>("class_decl");
InstKind::ClassDecl.Define<Parse::AnyClassDeclId>(
{.ir_name = "class_decl"});
TypeId type_id;
// TODO: For a generic class declaration, the name of the class declaration
@@ -420,7 +432,7 @@ struct ClassElementAccess {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind =
InstKind::ClassElementAccess.Define<Parse::NodeId>(
"class_element_access");
{.ir_name = "class_element_access"});
TypeId type_id;
InstId base_id;
@@ -431,7 +443,7 @@ struct ClassElementAccess {
struct ClassInit {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind =
InstKind::ClassInit.Define<Parse::NodeId>("class_init");
InstKind::ClassInit.Define<Parse::NodeId>({.ir_name = "class_init"});
TypeId type_id;
InstBlockId elements_id;
@@ -440,8 +452,10 @@ struct ClassInit {
// The type for a class, either non-generic or parameterized generic instance.
struct ClassType {
static constexpr auto Kind =
InstKind::ClassType.Define<Parse::NodeId>("class_type");
static constexpr auto Kind = InstKind::ClassType.Define<Parse::NodeId>(
{.ir_name = "class_type",
.is_type = InstIsType::Always,
.constant_kind = InstConstantKind::Always});
TypeId type_id;
ClassId class_id;
@@ -451,7 +465,10 @@ struct ClassType {
// Indicates `const` on a type, such as `var x: const i32`.
struct ConstType {
static constexpr auto Kind =
InstKind::ConstType.Define<Parse::PrefixOperatorConstId>("const_type");
InstKind::ConstType.Define<Parse::PrefixOperatorConstId>(
{.ir_name = "const_type",
.is_type = InstIsType::Always,
.constant_kind = InstConstantKind::Conditional});
TypeId type_id;
TypeId inner_id;
@@ -461,7 +478,7 @@ struct ConstType {
// result.
struct Converted {
static constexpr auto Kind =
InstKind::Converted.Define<Parse::NodeId>("converted");
InstKind::Converted.Define<Parse::NodeId>({.ir_name = "converted"});
TypeId type_id;
InstId original_id;
@@ -471,7 +488,8 @@ struct Converted {
// The `*` dereference operator, as in `*pointer`.
struct Deref {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind = InstKind::Deref.Define<Parse::NodeId>("deref");
static constexpr auto Kind =
InstKind::Deref.Define<Parse::NodeId>({.ir_name = "deref"});
TypeId type_id;
InstId pointer_id;
@@ -480,7 +498,7 @@ struct Deref {
// An `export bind_name` declaration.
struct ExportDecl {
static constexpr auto Kind =
InstKind::ExportDecl.Define<Parse::NodeId>("export");
InstKind::ExportDecl.Define<Parse::NodeId>({.ir_name = "export"});
TypeId type_id;
BindNameId bind_name_id;
@@ -491,8 +509,8 @@ struct ExportDecl {
// Represents accessing the `type` field in a facet value, which is notionally a
// pair of a type and a witness.
struct FacetTypeAccess {
static constexpr auto Kind =
InstKind::FacetTypeAccess.Define<Parse::NodeId>("facet_type_access");
static constexpr auto Kind = InstKind::FacetTypeAccess.Define<Parse::NodeId>(
{.ir_name = "facet_type_access"});
TypeId type_id;
InstId facet_id;
@@ -502,7 +520,8 @@ struct FacetTypeAccess {
// `FieldDecl` instruction is an `UnboundElementType`.
struct FieldDecl {
static constexpr auto Kind =
InstKind::FieldDecl.Define<Parse::BindingPatternId>("field_decl");
InstKind::FieldDecl.Define<Parse::BindingPatternId>(
{.ir_name = "field_decl", .constant_kind = InstConstantKind::Always});
TypeId type_id;
NameId name_id;
@@ -512,7 +531,9 @@ struct FieldDecl {
// A literal floating point value.
struct FloatLiteral {
static constexpr auto Kind =
InstKind::FloatLiteral.Define<Parse::RealLiteralId>("float_literal");
InstKind::FloatLiteral.Define<Parse::RealLiteralId>(
{.ir_name = "float_literal",
.constant_kind = InstConstantKind::Always});
TypeId type_id;
FloatId float_id;
@@ -520,8 +541,10 @@ struct FloatLiteral {
// A floating point type.
struct FloatType {
static constexpr auto Kind =
InstKind::FloatType.Define<Parse::NodeId>("float_type");
static constexpr auto Kind = InstKind::FloatType.Define<Parse::NodeId>(
{.ir_name = "float_type",
.is_type = InstIsType::Always,
.constant_kind = InstConstantKind::Conditional});
TypeId type_id;
// TODO: Consider adding a more compact way of representing either a small
@@ -533,7 +556,7 @@ struct FloatType {
struct FunctionDecl {
static constexpr auto Kind =
InstKind::FunctionDecl.Define<Parse::AnyFunctionDeclId>(
"fn_decl", /*is_lowered=*/false);
{.ir_name = "fn_decl", .is_lowered = false});
TypeId type_id;
FunctionId function_id;
@@ -545,7 +568,10 @@ struct FunctionDecl {
// The type of a function.
struct FunctionType {
static constexpr auto Kind =
InstKind::FunctionType.Define<Parse::AnyFunctionDeclId>("fn_type");
InstKind::FunctionType.Define<Parse::AnyFunctionDeclId>(
{.ir_name = "fn_type",
.is_type = InstIsType::Always,
.constant_kind = InstConstantKind::Conditional});
TypeId type_id;
FunctionId function_id;
@@ -557,7 +583,9 @@ struct GenericClassType {
// This is only ever created as a constant, so doesn't have a location.
static constexpr auto Kind =
InstKind::GenericClassType.Define<Parse::InvalidNodeId>(
"generic_class_type");
{.ir_name = "generic_class_type",
.is_type = InstIsType::Always,
.constant_kind = InstConstantKind::Conditional});
TypeId type_id;
ClassId class_id;
@@ -569,7 +597,9 @@ struct GenericInterfaceType {
// This is only ever created as a constant, so doesn't have a location.
static constexpr auto Kind =
InstKind::GenericInterfaceType.Define<Parse::InvalidNodeId>(
"generic_interface_type");
{.ir_name = "generic_interface_type",
.is_type = InstIsType::Always,
.constant_kind = InstConstantKind::Conditional});
TypeId type_id;
InterfaceId interface_id;
@@ -578,7 +608,7 @@ struct GenericInterfaceType {
// An `impl` declaration.
struct ImplDecl {
static constexpr auto Kind = InstKind::ImplDecl.Define<Parse::AnyImplDeclId>(
"impl_decl", /*is_lowered=*/false);
{.ir_name = "impl_decl", .is_lowered = false});
// No type: an impl declaration is not a value.
ImplId impl_id;
@@ -591,7 +621,7 @@ struct ImplDecl {
// correspondence with actual `import`s isn't guaranteed.
struct ImportDecl {
static constexpr auto Kind = InstKind::ImportDecl.Define<Parse::ImportDeclId>(
"import", /*is_lowered=*/false);
{.ir_name = "import", .is_lowered = false});
NameId package_id;
};
@@ -613,7 +643,7 @@ struct ImportRefUnloaded {
// No parse node: any parse node logic must use the referenced IR.
static constexpr auto Kind =
InstKind::ImportRefUnloaded.Define<Parse::InvalidNodeId>(
"import_ref", /*is_lowered=*/false);
{.ir_name = "import_ref", .is_lowered = false});
ImportIRInstId import_ir_inst_id;
BindNameId bind_name_id;
@@ -624,7 +654,7 @@ struct ImportRefLoaded {
// No parse node: any parse node logic must use the referenced IR.
static constexpr auto Kind =
InstKind::ImportRefLoaded.Define<Parse::InvalidNodeId>(
"import_ref", /*is_lowered=*/false);
{.ir_name = "import_ref", .is_lowered = false});
TypeId type_id;
ImportIRInstId import_ir_inst_id;
@@ -636,8 +666,8 @@ struct ImportRefLoaded {
// whose initialization is not in-place.
struct InitializeFrom {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind =
InstKind::InitializeFrom.Define<Parse::NodeId>("initialize_from");
static constexpr auto Kind = InstKind::InitializeFrom.Define<Parse::NodeId>(
{.ir_name = "initialize_from"});
TypeId type_id;
InstId src_id;
@@ -648,7 +678,7 @@ struct InitializeFrom {
struct InterfaceDecl {
static constexpr auto Kind =
InstKind::InterfaceDecl.Define<Parse::AnyInterfaceDeclId>(
"interface_decl", /*is_lowered=*/false);
{.ir_name = "interface_decl", .is_lowered = false});
TypeId type_id;
// TODO: For a generic interface declaration, the name of the interface
@@ -662,8 +692,10 @@ struct InterfaceDecl {
// The type for an interface, either non-generic or parameterized generic
// instance.
struct InterfaceType {
static constexpr auto Kind =
InstKind::InterfaceType.Define<Parse::NodeId>("interface_type");
static constexpr auto Kind = InstKind::InterfaceType.Define<Parse::NodeId>(
{.ir_name = "interface_type",
.is_type = InstIsType::Always,
.constant_kind = InstConstantKind::Always});
TypeId type_id;
InterfaceId interface_id;
@@ -674,7 +706,9 @@ struct InterfaceType {
struct InterfaceWitness {
static constexpr auto Kind =
InstKind::InterfaceWitness.Define<Parse::InvalidNodeId>(
"interface_witness", /*is_lowered=*/false);
{.ir_name = "interface_witness",
.constant_kind = InstConstantKind::Conditional,
.is_lowered = false});
TypeId type_id;
InstBlockId elements_id;
@@ -684,7 +718,10 @@ struct InterfaceWitness {
struct InterfaceWitnessAccess {
static constexpr auto Kind =
InstKind::InterfaceWitnessAccess.Define<Parse::InvalidNodeId>(
"interface_witness_access", /*is_lowered=*/false);
{.ir_name = "interface_witness_access",
.is_type = InstIsType::Maybe,
.constant_kind = InstConstantKind::SymbolicOnly,
.is_lowered = false});
TypeId type_id;
InstId witness_id;
@@ -694,8 +731,8 @@ struct InterfaceWitnessAccess {
// A literal integer value.
struct IntLiteral {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind =
InstKind::IntLiteral.Define<Parse::NodeId>("int_literal");
static constexpr auto Kind = InstKind::IntLiteral.Define<Parse::NodeId>(
{.ir_name = "int_literal", .constant_kind = InstConstantKind::Always});
TypeId type_id;
IntId int_id;
@@ -703,8 +740,10 @@ struct IntLiteral {
// An integer type.
struct IntType {
static constexpr auto Kind =
InstKind::IntType.Define<Parse::NodeId>("int_type");
static constexpr auto Kind = InstKind::IntType.Define<Parse::NodeId>(
{.ir_name = "int_type",
.is_type = InstIsType::Always,
.constant_kind = InstConstantKind::Conditional});
TypeId type_id;
IntKind int_kind;
@@ -718,7 +757,7 @@ struct IntType {
struct NameRef {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind =
InstKind::NameRef.Define<Parse::NodeId>("name_ref");
InstKind::NameRef.Define<Parse::NodeId>({.ir_name = "name_ref"});
TypeId type_id;
NameId name_id;
@@ -728,7 +767,8 @@ struct NameRef {
// A namespace declaration.
struct Namespace {
static constexpr auto Kind =
InstKind::Namespace.Define<Parse::AnyNamespaceId>("namespace");
InstKind::Namespace.Define<Parse::AnyNamespaceId>(
{.ir_name = "namespace", .constant_kind = InstConstantKind::Always});
TypeId type_id;
NameScopeId name_scope_id;
@@ -740,7 +780,8 @@ struct Namespace {
// A parameter for a function or other parameterized block.
struct Param {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind = InstKind::Param.Define<Parse::NodeId>("param");
static constexpr auto Kind =
InstKind::Param.Define<Parse::NodeId>({.ir_name = "param"});
TypeId type_id;
NameId name_id;
@@ -750,8 +791,10 @@ struct Param {
// `x: i32*`, where `pointee_id` is `i32` and `type_id` is `type`.
struct PointerType {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind =
InstKind::PointerType.Define<Parse::NodeId>("ptr_type");
static constexpr auto Kind = InstKind::PointerType.Define<Parse::NodeId>(
{.ir_name = "ptr_type",
.is_type = InstIsType::Always,
.constant_kind = InstConstantKind::Conditional});
TypeId type_id;
TypeId pointee_id;
@@ -761,7 +804,7 @@ struct Return {
static constexpr auto Kind =
InstKind::Return.Define<Parse::NodeIdOneOf<Parse::FunctionDefinitionId,
Parse::ReturnStatementId>>(
"return", TerminatorKind::Terminator);
{.ir_name = "return", .terminator_kind = TerminatorKind::Terminator});
// This is a statement, so has no type.
};
@@ -770,7 +813,7 @@ struct Return {
struct ReturnExpr {
static constexpr auto Kind =
InstKind::ReturnExpr.Define<Parse::ReturnStatementId>(
"return", TerminatorKind::Terminator);
{.ir_name = "return", .terminator_kind = TerminatorKind::Terminator});
// This is a statement, so has no type.
InstId expr_id;
@@ -785,7 +828,7 @@ struct ReturnExpr {
struct SpliceBlock {
// TODO: Can we make Parse::NodeId more specific?
static constexpr auto Kind =
InstKind::SpliceBlock.Define<Parse::NodeId>("splice_block");
InstKind::SpliceBlock.Define<Parse::NodeId>({.ir_name = "splice_block"});
TypeId type_id;
InstBlockId block_id;
@@ -795,7 +838,9 @@ struct SpliceBlock {
// A literal string value.
struct StringLiteral {
static constexpr auto Kind =
InstKind::StringLiteral.Define<Parse::StringLiteralId>("string_literal");
InstKind::StringLiteral.Define<Parse::StringLiteralId>(
{.ir_name = "string_literal",
.constant_kind = InstConstantKind::Always});
TypeId type_id;
StringLiteralValueId string_literal_id;
@@ -804,8 +849,8 @@ struct StringLiteral {
// Access to a struct type, with the index into the struct_id representation.
struct StructAccess {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind =
InstKind::StructAccess.Define<Parse::NodeId>("struct_access");
static constexpr auto Kind = InstKind::StructAccess.Define<Parse::NodeId>(
{.ir_name = "struct_access"});
TypeId type_id;
InstId struct_id;
@@ -816,7 +861,7 @@ struct StructAccess {
struct StructInit {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind =
InstKind::StructInit.Define<Parse::NodeId>("struct_init");
InstKind::StructInit.Define<Parse::NodeId>({.ir_name = "struct_init"});
TypeId type_id;
InstBlockId elements_id;
@@ -826,7 +871,8 @@ struct StructInit {
// A literal struct value, such as `{.a = 1, .b = 2}`.
struct StructLiteral {
static constexpr auto Kind =
InstKind::StructLiteral.Define<Parse::StructLiteralId>("struct_literal");
InstKind::StructLiteral.Define<Parse::StructLiteralId>(
{.ir_name = "struct_literal"});
TypeId type_id;
InstBlockId elements_id;
@@ -836,8 +882,10 @@ struct StructLiteral {
struct StructType {
// TODO: Make this more specific. It can be one of: ClassDefinitionId,
// StructLiteralId, StructTypeLiteralId
static constexpr auto Kind =
InstKind::StructType.Define<Parse::NodeId>("struct_type");
static constexpr auto Kind = InstKind::StructType.Define<Parse::NodeId>(
{.ir_name = "struct_type",
.is_type = InstIsType::Always,
.constant_kind = InstConstantKind::Conditional});
TypeId type_id;
InstBlockId fields_id;
@@ -850,8 +898,9 @@ struct StructType {
// instruction has no type.
struct StructTypeField {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind =
InstKind::StructTypeField.Define<Parse::NodeId>("struct_type_field");
static constexpr auto Kind = InstKind::StructTypeField.Define<Parse::NodeId>(
{.ir_name = "struct_type_field",
.constant_kind = InstConstantKind::Conditional});
NameId name_id;
TypeId field_type_id;
@@ -860,8 +909,9 @@ struct StructTypeField {
// A struct value.
struct StructValue {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind =
InstKind::StructValue.Define<Parse::NodeId>("struct_value");
static constexpr auto Kind = InstKind::StructValue.Define<Parse::NodeId>(
{.ir_name = "struct_value",
.constant_kind = InstConstantKind::Conditional});
TypeId type_id;
InstBlockId elements_id;
@@ -871,7 +921,7 @@ struct StructValue {
struct Temporary {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind =
InstKind::Temporary.Define<Parse::NodeId>("temporary");
InstKind::Temporary.Define<Parse::NodeId>({.ir_name = "temporary"});
TypeId type_id;
InstId storage_id;
@@ -881,8 +931,8 @@ struct Temporary {
// Storage for a temporary value.
struct TemporaryStorage {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind =
InstKind::TemporaryStorage.Define<Parse::NodeId>("temporary_storage");
static constexpr auto Kind = InstKind::TemporaryStorage.Define<Parse::NodeId>(
{.ir_name = "temporary_storage"});
TypeId type_id;
};
@@ -894,7 +944,7 @@ struct TemporaryStorage {
struct TupleAccess {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind =
InstKind::TupleAccess.Define<Parse::NodeId>("tuple_access");
InstKind::TupleAccess.Define<Parse::NodeId>({.ir_name = "tuple_access"});
TypeId type_id;
InstId tuple_id;
@@ -903,8 +953,8 @@ struct TupleAccess {
// Access to a tuple member by index, such as `tuple[index]`.
struct TupleIndex {
static constexpr auto Kind =
InstKind::TupleIndex.Define<Parse::IndexExprId>("tuple_index");
static constexpr auto Kind = InstKind::TupleIndex.Define<Parse::IndexExprId>(
{.ir_name = "tuple_index"});
TypeId type_id;
InstId tuple_id;
@@ -915,7 +965,7 @@ struct TupleIndex {
struct TupleInit {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind =
InstKind::TupleInit.Define<Parse::NodeId>("tuple_init");
InstKind::TupleInit.Define<Parse::NodeId>({.ir_name = "tuple_init"});
TypeId type_id;
InstBlockId elements_id;
@@ -925,7 +975,8 @@ struct TupleInit {
// A literal tuple value.
struct TupleLiteral {
static constexpr auto Kind =
InstKind::TupleLiteral.Define<Parse::TupleLiteralId>("tuple_literal");
InstKind::TupleLiteral.Define<Parse::TupleLiteralId>(
{.ir_name = "tuple_literal"});
TypeId type_id;
InstBlockId elements_id;
@@ -934,8 +985,10 @@ struct TupleLiteral {
// The type of a tuple.
struct TupleType {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind =
InstKind::TupleType.Define<Parse::NodeId>("tuple_type");
static constexpr auto Kind = InstKind::TupleType.Define<Parse::NodeId>(
{.ir_name = "tuple_type",
.is_type = InstIsType::Always,
.constant_kind = InstConstantKind::Conditional});
TypeId type_id;
TypeBlockId elements_id;
@@ -944,8 +997,9 @@ struct TupleType {
// A tuple value.
struct TupleValue {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind =
InstKind::TupleValue.Define<Parse::NodeId>("tuple_value");
static constexpr auto Kind = InstKind::TupleValue.Define<Parse::NodeId>(
{.ir_name = "tuple_value",
.constant_kind = InstConstantKind::Conditional});
TypeId type_id;
InstBlockId elements_id;
@@ -955,7 +1009,7 @@ struct TupleValue {
struct UnaryOperatorNot {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind =
InstKind::UnaryOperatorNot.Define<Parse::NodeId>("not");
InstKind::UnaryOperatorNot.Define<Parse::NodeId>({.ir_name = "not"});
TypeId type_id;
InstId operand_id;
@@ -967,7 +1021,9 @@ struct UnaryOperatorNot {
struct UnboundElementType {
static constexpr auto Kind = InstKind::UnboundElementType.Define<
Parse::NodeIdOneOf<Parse::BaseDeclId, Parse::BindingPatternId>>(
"unbound_element_type");
{.ir_name = "unbound_element_type",
.is_type = InstIsType::Always,
.constant_kind = InstConstantKind::Conditional});
TypeId type_id;
// The class that a value of this type is an element of.
@@ -981,8 +1037,8 @@ struct UnboundElementType {
// example, when indexing a value expression of array type, this is used to
// form a reference to the array object.
struct ValueAsRef {
static constexpr auto Kind =
InstKind::ValueAsRef.Define<Parse::IndexExprId>("value_as_ref");
static constexpr auto Kind = InstKind::ValueAsRef.Define<Parse::IndexExprId>(
{.ir_name = "value_as_ref"});
TypeId type_id;
InstId value_id;
@@ -995,7 +1051,7 @@ struct ValueOfInitializer {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind =
InstKind::ValueOfInitializer.Define<Parse::NodeId>(
"value_of_initializer");
{.ir_name = "value_of_initializer"});
TypeId type_id;
InstId init_id;
@@ -1005,7 +1061,7 @@ struct ValueOfInitializer {
struct VarStorage {
// TODO: Make Parse::NodeId more specific.
static constexpr auto Kind =
InstKind::VarStorage.Define<Parse::NodeId>("var");
InstKind::VarStorage.Define<Parse::NodeId>({.ir_name = "var"});
TypeId type_id;
NameId name_id;