mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-04 07:41:04 +01:00
Adds a singleton framework, and converts `File` and `InstId::Print` to demonstrate functionality. Moves various functions from `ids.h` to `ids.cpp` because `Inst::Print` needs the file if `singleton_insts.h` is split out, so the small bit of cleanup feels consistent. I added `Inst::MakeSingleton` because getting the type of the instruction to make from an `InstKind` felt too hard. Singleton instructions follow a basic structure, so I'm just putting that instruction structure into `Inst`. Previously we required macros to do this, and I'm trying to remove macro dependencies. I'm trying to remove builtin/singleton-related functionality from `InstId` in order to get a clearer boundary for the functionality. The other builtin functions should be removed as part of the bigger migration, but I'm trying to carefully scope changes to verify agreement on the singleton approach in use first. This provides `InstT::SingletonInstId` because that'll often be written as `SemIR::TypeType::SingletonInstId`. The alternative of something like `SemIR::SingletonInstId<SemIR::TypeType>` is just a little more verbose due to the repeated `SemIR`, and it's more consistent with `TypeType::Kind`. An alternative I considered was consolidating singleton information to `InstKind`. This felt challenging because of the `InstId::BuiltinTypeType` and similar values. Maintaining those would turn into something like `InstKind::IsSingleton()` and `InstId::Singleton<TypeType>`, which didn't feel like as good a split. `InstId::Print` remains aware of singletons, but I'm hoping to remove other builtin-related calls from `InstId`. Another thing I considered was adding `.is_singleton = true` to `InstKind::Definition`. I don't think we could rely on that to get `InstId::Singleton<TypeType>` set up as `constexpr`, though. At that point, I think it'd mainly be _just_ a comment-like annotation, maybe validated with `CHECK` but not having any effect on its own. So I decided not to do that, just adding comments instead. Sidenotes: - "singleton" naming was discussed [on #toolchain](https://discord.com/channels/655572317891461132/655578254970716160/1308870233729269781). - The TODO in file.h about possibly excluding other things than singletons seems moot. That's for raw IR, and we're much more focused on textual IR these days. --------- Co-authored-by: David Blaikie <dblaikie@gmail.com> Co-authored-by: Dana Jansens <danakj@orodu.net> Co-authored-by: Richard Smith <richard@metafoo.co.uk> Co-authored-by: josh11b <15258583+josh11b@users.noreply.github.com> Co-authored-by: Josh L <josh11b@users.noreply.github.com> Co-authored-by: Chandler Carruth <chandlerc@gmail.com> Co-authored-by: Carbon Infra Bot <carbon-external-infra@google.com> Co-authored-by: Boaz Brickner <brickner@google.com> Co-authored-by: Geoff Romer <gromer@google.com>
220 lines
8.0 KiB
C++
220 lines
8.0 KiB
C++
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
|
// Exceptions. See /LICENSE for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
#ifndef CARBON_TOOLCHAIN_SEM_IR_INST_KIND_H_
|
|
#define CARBON_TOOLCHAIN_SEM_IR_INST_KIND_H_
|
|
|
|
#include <cstdint>
|
|
|
|
#include "common/check.h"
|
|
#include "common/enum_base.h"
|
|
#include "llvm/ADT/FoldingSet.h"
|
|
|
|
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 {
|
|
// This instruction doesn't produce a value, and shouldn't be referenced by
|
|
// other instructions.
|
|
None,
|
|
// This instruction represents an expression or expression-like construct that
|
|
// produces a value of the type indicated by its `type_id` field.
|
|
Typed,
|
|
};
|
|
|
|
// Whether an instruction can be used to define a constant value. This specifies
|
|
// whether the instruction can be added to the `constants()` list. Note that
|
|
// even instructions that cannot define a constant value can still have an
|
|
// associated `constant_value()`, but the constant value will be a different
|
|
// kind of instruction.
|
|
enum class InstConstantKind : int8_t {
|
|
// This instruction never defines a constant value. For example,
|
|
// `UnaryOperatorNot` never defines a constant value; if its operand is a
|
|
// template constant, its constant value will instead be a `BoolLiteral`. This
|
|
// is also used for instructions that don't produce a value at all.
|
|
Never,
|
|
// This instruction may be a symbolic constant, depending on its operands, but
|
|
// is never a template constant. For example, a `Call` instruction can be a
|
|
// symbolic constant but never a template constant.
|
|
SymbolicOnly,
|
|
// This instruction can define a symbolic or template constant, but might not
|
|
// have a constant value, depending on its operands. For example, a
|
|
// `TupleValue` can define a constant if its operands are constants.
|
|
Conditional,
|
|
// This instruction always has a constant value of the same kind. For example,
|
|
// `IntValue`.
|
|
Always,
|
|
};
|
|
|
|
// Whether an instruction is a terminator or part of the terminator sequence.
|
|
// The instructions in a block appear in the order NotTerminator, then
|
|
// TerminatorSequence, then Terminator, which is also the numerical order of
|
|
// these values.
|
|
enum class TerminatorKind : int8_t {
|
|
// This instruction is not a terminator.
|
|
NotTerminator,
|
|
// This instruction is not itself a terminator, but forms part of a terminator
|
|
// sequence.
|
|
TerminatorSequence,
|
|
// This instruction is a terminator.
|
|
Terminator,
|
|
};
|
|
|
|
CARBON_DEFINE_RAW_ENUM_CLASS(InstKind, uint8_t) {
|
|
#define CARBON_SEM_IR_INST_KIND(Name) CARBON_RAW_ENUM_ENUMERATOR(Name)
|
|
#include "toolchain/sem_ir/inst_kind.def"
|
|
};
|
|
|
|
class InstKind : public CARBON_ENUM_BASE(InstKind) {
|
|
public:
|
|
#define CARBON_SEM_IR_INST_KIND(Name) CARBON_ENUM_CONSTANT_DECL(Name)
|
|
#include "toolchain/sem_ir/inst_kind.def"
|
|
|
|
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;
|
|
bool deduce_through = false;
|
|
};
|
|
|
|
// 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(DefinitionInfo info) const -> Definition<TypedNodeId>;
|
|
|
|
using EnumBase::AsInt;
|
|
using EnumBase::FromInt;
|
|
using EnumBase::Make;
|
|
|
|
// 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 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 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 {
|
|
return definition_info(*this).terminator_kind;
|
|
}
|
|
|
|
// Returns true if `Instruction(A)` == `Instruction(B)` allows deduction to
|
|
// conclude `A` == `B`.
|
|
auto deduce_through() const -> bool {
|
|
return definition_info(*this).deduce_through;
|
|
}
|
|
|
|
// 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) \
|
|
CARBON_ENUM_CONSTANT_DEFINITION(InstKind, Name)
|
|
#include "toolchain/sem_ir/inst_kind.def"
|
|
|
|
// We expect the instruction kind to fit compactly into 8 bits.
|
|
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.
|
|
template <typename TypedNodeIdArg>
|
|
class InstKind::Definition : public InstKind {
|
|
public:
|
|
using TypedNodeId = TypedNodeIdArg;
|
|
|
|
// Not copyable.
|
|
Definition(const Definition&) = delete;
|
|
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 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 info_.terminator_kind;
|
|
}
|
|
|
|
// Returns true if the instruction is lowered.
|
|
constexpr auto is_lowered() const -> bool { return info_.is_lowered; }
|
|
|
|
// Returns true if `Instruction(A)` == `Instruction(B)` allows deduction to
|
|
// conclude `A` == `B`.
|
|
constexpr auto deduce_through() const -> bool { return info_.deduce_through; }
|
|
|
|
private:
|
|
friend class InstKind;
|
|
|
|
constexpr Definition(InstKind kind, InstKind::DefinitionInfo info)
|
|
: InstKind(kind), info_(info) {}
|
|
|
|
InstKind::DefinitionInfo info_;
|
|
};
|
|
|
|
template <typename TypedNodeId>
|
|
constexpr auto InstKind::Define(DefinitionInfo info) const
|
|
-> Definition<TypedNodeId> {
|
|
return Definition<TypedNodeId>(*this, info);
|
|
}
|
|
|
|
} // namespace Carbon::SemIR
|
|
|
|
#endif // CARBON_TOOLCHAIN_SEM_IR_INST_KIND_H_
|