From f45cbc602813232992dcf4c985407dbe6c8f2226 Mon Sep 17 00:00:00 2001 From: Jon Ross-Perkins Date: Tue, 3 Dec 2024 12:03:37 -0800 Subject: [PATCH] Add framework for singleton instructions. (#4582) 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` 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`, 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` 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 Co-authored-by: Dana Jansens Co-authored-by: Richard Smith Co-authored-by: josh11b <15258583+josh11b@users.noreply.github.com> Co-authored-by: Josh L Co-authored-by: Chandler Carruth Co-authored-by: Carbon Infra Bot Co-authored-by: Boaz Brickner Co-authored-by: Geoff Romer --- .../testdata/basics/builtin_insts.carbon | 20 +- toolchain/sem_ir/BUILD | 2 + toolchain/sem_ir/file.cpp | 34 ++-- toolchain/sem_ir/file.h | 18 +- toolchain/sem_ir/ids.cpp | 174 ++++++++++++++++++ toolchain/sem_ir/ids.h | 163 ++-------------- toolchain/sem_ir/inst.h | 13 ++ toolchain/sem_ir/inst_kind.def | 10 +- toolchain/sem_ir/inst_kind.h | 1 + toolchain/sem_ir/singleton_insts.h | 76 ++++++++ toolchain/sem_ir/typed_insts.h | 67 ++++--- 11 files changed, 345 insertions(+), 233 deletions(-) create mode 100644 toolchain/sem_ir/ids.cpp create mode 100644 toolchain/sem_ir/singleton_insts.h diff --git a/toolchain/check/testdata/basics/builtin_insts.carbon b/toolchain/check/testdata/basics/builtin_insts.carbon index f5e51deca940e..923caf812914d 100644 --- a/toolchain/check/testdata/basics/builtin_insts.carbon +++ b/toolchain/check/testdata/basics/builtin_insts.carbon @@ -33,31 +33,31 @@ // CHECK:STDOUT: type_block0: {} // CHECK:STDOUT: insts: // CHECK:STDOUT: instTypeType: {kind: TypeType, type: typeTypeType} -// CHECK:STDOUT: instErrorInst: {kind: ErrorInst, type: typeError} // CHECK:STDOUT: instAutoType: {kind: AutoType, type: typeTypeType} // CHECK:STDOUT: instBoolType: {kind: BoolType, type: typeTypeType} +// CHECK:STDOUT: instBoundMethodType: {kind: BoundMethodType, type: typeTypeType} +// CHECK:STDOUT: instErrorInst: {kind: ErrorInst, type: typeError} // CHECK:STDOUT: instIntLiteralType: {kind: IntLiteralType, type: typeTypeType} // CHECK:STDOUT: instLegacyFloatType: {kind: LegacyFloatType, type: typeTypeType} -// CHECK:STDOUT: instStringType: {kind: StringType, type: typeTypeType} -// CHECK:STDOUT: instBoundMethodType: {kind: BoundMethodType, type: typeTypeType} -// CHECK:STDOUT: instSpecificFunctionType: {kind: SpecificFunctionType, type: typeTypeType} // CHECK:STDOUT: instNamespaceType: {kind: NamespaceType, type: typeTypeType} -// CHECK:STDOUT: instWitnessType: {kind: WitnessType, type: typeTypeType} +// CHECK:STDOUT: instSpecificFunctionType: {kind: SpecificFunctionType, type: typeTypeType} +// CHECK:STDOUT: instStringType: {kind: StringType, type: typeTypeType} // CHECK:STDOUT: instVtableType: {kind: VtableType, type: typeTypeType} +// CHECK:STDOUT: instWitnessType: {kind: WitnessType, type: typeTypeType} // CHECK:STDOUT: 'inst+0': {kind: Namespace, arg0: name_scope0, arg1: inst, type: type(instNamespaceType)} // CHECK:STDOUT: constant_values: // CHECK:STDOUT: instTypeType: templateConstant(instTypeType) -// CHECK:STDOUT: instErrorInst: templateConstant(instErrorInst) // CHECK:STDOUT: instAutoType: templateConstant(instAutoType) // CHECK:STDOUT: instBoolType: templateConstant(instBoolType) +// CHECK:STDOUT: instBoundMethodType: templateConstant(instBoundMethodType) +// CHECK:STDOUT: instErrorInst: templateConstant(instErrorInst) // CHECK:STDOUT: instIntLiteralType: templateConstant(instIntLiteralType) // CHECK:STDOUT: instLegacyFloatType: templateConstant(instLegacyFloatType) -// CHECK:STDOUT: instStringType: templateConstant(instStringType) -// CHECK:STDOUT: instBoundMethodType: templateConstant(instBoundMethodType) -// CHECK:STDOUT: instSpecificFunctionType: templateConstant(instSpecificFunctionType) // CHECK:STDOUT: instNamespaceType: templateConstant(instNamespaceType) -// CHECK:STDOUT: instWitnessType: templateConstant(instWitnessType) +// CHECK:STDOUT: instSpecificFunctionType: templateConstant(instSpecificFunctionType) +// CHECK:STDOUT: instStringType: templateConstant(instStringType) // CHECK:STDOUT: instVtableType: templateConstant(instVtableType) +// CHECK:STDOUT: instWitnessType: templateConstant(instWitnessType) // CHECK:STDOUT: 'inst+0': templateConstant(inst+0) // CHECK:STDOUT: symbolic_constants: {} // CHECK:STDOUT: inst_blocks: diff --git a/toolchain/sem_ir/BUILD b/toolchain/sem_ir/BUILD index 6a6635dd35d46..e64d5e4f984ed 100644 --- a/toolchain/sem_ir/BUILD +++ b/toolchain/sem_ir/BUILD @@ -22,6 +22,7 @@ cc_library( name = "typed_insts", srcs = [ "builtin_inst_kind.cpp", + "ids.cpp", "inst_kind.cpp", ], hdrs = [ @@ -29,6 +30,7 @@ cc_library( "id_kind.h", "ids.h", "inst_kind.h", + "singleton_insts.h", "typed_insts.h", ], textual_hdrs = ["inst_kind.def"], diff --git a/toolchain/sem_ir/file.cpp b/toolchain/sem_ir/file.cpp index 0cb334482b264..d9987d161b11a 100644 --- a/toolchain/sem_ir/file.cpp +++ b/toolchain/sem_ir/file.cpp @@ -11,7 +11,6 @@ #include "toolchain/base/shared_value_stores.h" #include "toolchain/base/yaml.h" #include "toolchain/parse/node_ids.h" -#include "toolchain/sem_ir/builtin_inst_kind.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/inst_kind.h" @@ -42,23 +41,12 @@ File::File(CheckIRId check_ir_id, types_.SetValueRepr(TypeId::Error, {.kind = ValueRepr::Copy, .type_id = TypeId::Error}); - insts_.Reserve(BuiltinInstKind::ValidCount); -// Error uses a self-referential type so that it's not accidentally treated as -// a normal type. Every other builtin is a type, including the -// self-referential TypeType. -#define CARBON_SEM_IR_BUILTIN_INST_KIND(Name) \ - insts_.AddInNoBlock(LocIdAndInst::NoLoc( \ - {.type_id = BuiltinInstKind::Name == BuiltinInstKind::ErrorInst \ - ? TypeId::Error \ - : TypeId::TypeType})); -#include "toolchain/sem_ir/inst_kind.def" - CARBON_CHECK(insts_.size() == BuiltinInstKind::ValidCount, - "Builtins should produce {0} insts, actual: {1}", - BuiltinInstKind::ValidCount, insts_.size()); - for (auto i : llvm::seq(BuiltinInstKind::ValidCount)) { - auto builtin_id = SemIR::InstId(i); - constant_values_.Set(builtin_id, - SemIR::ConstantId::ForTemplateConstant(builtin_id)); + insts_.Reserve(SingletonInstKinds.size()); + for (auto kind : SingletonInstKinds) { + auto inst_id = + insts_.AddInNoBlock(LocIdAndInst::NoLoc(Inst::MakeSingleton(kind))); + constant_values_.Set(inst_id, + SemIR::ConstantId::ForTemplateConstant(inst_id)); } } @@ -99,9 +87,9 @@ auto File::Verify() const -> ErrorOr { return Success(); } -auto File::OutputYaml(bool include_builtins) const -> Yaml::OutputMapping { - return Yaml::OutputMapping([this, - include_builtins](Yaml::OutputMapping::Map map) { +auto File::OutputYaml(bool include_singletons) const -> Yaml::OutputMapping { + return Yaml::OutputMapping([this, include_singletons]( + Yaml::OutputMapping::Map map) { map.Add("filename", filename_); map.Add( "sem_ir", Yaml::OutputMapping([&](Yaml::OutputMapping::Map map) { @@ -118,7 +106,7 @@ auto File::OutputYaml(bool include_builtins) const -> Yaml::OutputMapping { map.Add("type_blocks", type_blocks_.OutputYaml()); map.Add( "insts", Yaml::OutputMapping([&](Yaml::OutputMapping::Map map) { - int start = include_builtins ? 0 : BuiltinInstKind::ValidCount; + int start = include_singletons ? 0 : SingletonInstKinds.size(); for (int i : llvm::seq(start, insts_.size())) { auto id = InstId(i); map.Add(PrintToString(id), @@ -128,7 +116,7 @@ auto File::OutputYaml(bool include_builtins) const -> Yaml::OutputMapping { map.Add("constant_values", Yaml::OutputMapping([&](Yaml::OutputMapping::Map map) { int start = - include_builtins ? 0 : BuiltinInstKind::ValidCount; + include_singletons ? 0 : SingletonInstKinds.size(); for (int i : llvm::seq(start, insts_.size())) { auto id = InstId(i); auto value = constant_values_.Get(id); diff --git a/toolchain/sem_ir/file.h b/toolchain/sem_ir/file.h index b9ac4afe9d70e..528ff15e1eb93 100644 --- a/toolchain/sem_ir/file.h +++ b/toolchain/sem_ir/file.h @@ -28,6 +28,7 @@ #include "toolchain/sem_ir/interface.h" #include "toolchain/sem_ir/name.h" #include "toolchain/sem_ir/name_scope.h" +#include "toolchain/sem_ir/singleton_insts.h" #include "toolchain/sem_ir/struct_type_field.h" #include "toolchain/sem_ir/type.h" #include "toolchain/sem_ir/type_info.h" @@ -48,16 +49,13 @@ class File : public Printable { // Verifies that invariants of the semantics IR hold. auto Verify() const -> ErrorOr; - // Prints the full IR. Allow omitting builtins so that unrelated changes are - // less likely to alter test golden files. - // TODO: In the future, the things to print may change, for example by adding - // preludes. We may then want the ability to omit other things similar to - // builtins. - auto Print(llvm::raw_ostream& out, bool include_builtins = false) const + // Prints the full IR. Allow omitting singletons so that changes to the list + // of singletons won't churn golden test file content. + auto Print(llvm::raw_ostream& out, bool include_singletons = false) const -> void { - Yaml::Print(out, OutputYaml(include_builtins)); + Yaml::Print(out, OutputYaml(include_singletons)); } - auto OutputYaml(bool include_builtins) const -> Yaml::OutputMapping; + auto OutputYaml(bool include_singletons) const -> Yaml::OutputMapping; // Collects memory usage of members. auto CollectMemUsage(MemUsage& mem_usage, llvm::StringRef label) const @@ -241,8 +239,8 @@ class File : public Printable { // the data is provided by allocator_. BlockValueStore type_blocks_; - // All instructions. The first entries will always be BuiltinInsts, at - // indices matching BuiltinInstKind ordering. + // All instructions. The first entries will always be the singleton + // instructions. InstStore insts_; // Storage for name scopes. diff --git a/toolchain/sem_ir/ids.cpp b/toolchain/sem_ir/ids.cpp new file mode 100644 index 0000000000000..6acce679e7209 --- /dev/null +++ b/toolchain/sem_ir/ids.cpp @@ -0,0 +1,174 @@ +// 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/sem_ir/ids.h" + +#include "toolchain/sem_ir/singleton_insts.h" + +namespace Carbon::SemIR { + +auto InstId::Print(llvm::raw_ostream& out) const -> void { + out << "inst"; + if (!is_valid()) { + IdBase::Print(out); + } else if (is_builtin()) { + out << builtin_inst_kind(); + } else { + // Use the `+` as a small reminder that this is a delta, rather than an + // absolute index. + out << "+" << index - SingletonInstKinds.size(); + } +} + +auto ConstantId::Print(llvm::raw_ostream& out, bool disambiguate) const + -> void { + if (!is_valid()) { + IdBase::Print(out); + } else if (is_template()) { + if (disambiguate) { + out << "templateConstant("; + } + out << template_inst_id(); + if (disambiguate) { + out << ")"; + } + } else if (is_symbolic()) { + out << "symbolicConstant" << symbolic_index(); + } else { + out << "runtime"; + } +} + +auto RuntimeParamIndex::Print(llvm::raw_ostream& out) const -> void { + out << "runtime_param"; + if (*this == Unknown) { + out << ""; + } else { + IndexBase::Print(out); + } +} + +auto GenericInstIndex::Print(llvm::raw_ostream& out) const -> void { + out << "genericInst"; + if (is_valid()) { + out << (region() == Declaration ? "InDecl" : "InDef") << index(); + } else { + out << ""; + } +} + +auto BoolValue::Print(llvm::raw_ostream& out) const -> void { + if (*this == False) { + out << "false"; + } else if (*this == True) { + out << "true"; + } else { + CARBON_FATAL("Invalid bool value {0}", index); + } +} + +auto IntKind::Print(llvm::raw_ostream& out) const -> void { + if (*this == Unsigned) { + out << "unsigned"; + } else if (*this == Signed) { + out << "signed"; + } else { + CARBON_FATAL("Invalid int kind value {0}", index); + } +} + +auto NameId::ForIdentifier(IdentifierId id) -> NameId { + if (id.index >= 0) { + return NameId(id.index); + } else if (!id.is_valid()) { + return NameId::Invalid; + } else { + CARBON_FATAL("Unexpected identifier ID {0}", id); + } +} + +auto NameId::Print(llvm::raw_ostream& out) const -> void { + out << "name"; + if (*this == SelfValue) { + out << "SelfValue"; + } else if (*this == SelfType) { + out << "SelfType"; + } else if (*this == PeriodSelf) { + out << "PeriodSelf"; + } else if (*this == ReturnSlot) { + out << "ReturnSlot"; + } else if (*this == PackageNamespace) { + out << "PackageNamespace"; + } else if (*this == Base) { + out << "Base"; + } else { + CARBON_CHECK(!is_valid() || index >= 0, "Unknown index {0}", index); + IdBase::Print(out); + } +} + +auto InstBlockId::Print(llvm::raw_ostream& out) const -> void { + if (*this == Unreachable) { + out << "unreachable"; + } else if (*this == Empty) { + out << "empty"; + } else if (*this == Exports) { + out << "exports"; + } else if (*this == ImportRefs) { + out << "import_refs"; + } else if (*this == GlobalInit) { + out << "global_init"; + } else { + out << "block"; + IdBase::Print(out); + } +} + +auto TypeId::Print(llvm::raw_ostream& out) const -> void { + out << "type"; + if (*this == TypeType) { + out << "TypeType"; + } else if (*this == AutoType) { + out << "AutoType"; + } else if (*this == Error) { + out << "Error"; + } else { + out << "("; + AsConstantId().Print(out, /*disambiguate=*/false); + out << ")"; + } +} + +auto LibraryNameId::ForStringLiteralValueId(StringLiteralValueId id) + -> LibraryNameId { + CARBON_CHECK(id.index >= InvalidIndex, "Unexpected library name ID {0}", id); + if (id == StringLiteralValueId::Invalid) { + // Prior to SemIR, we use invalid to indicate `default`. + return LibraryNameId::Default; + } else { + return LibraryNameId(id.index); + } +} + +auto LibraryNameId::Print(llvm::raw_ostream& out) const -> void { + out << "libraryName"; + if (*this == Default) { + out << "Default"; + } else if (*this == Error) { + out << ""; + } else { + IdBase::Print(out); + } +} + +auto LocId::Print(llvm::raw_ostream& out) const -> void { + out << "loc_"; + if (is_node_id() || !is_valid()) { + out << node_id(); + } else { + out << import_ir_inst_id(); + } +} + +} // namespace Carbon::SemIR diff --git a/toolchain/sem_ir/ids.h b/toolchain/sem_ir/ids.h index c0e25d15ae2c1..08f3a4c30800a 100644 --- a/toolchain/sem_ir/ids.h +++ b/toolchain/sem_ir/ids.h @@ -66,18 +66,7 @@ struct InstId : public IdBase, public Printable { return BuiltinInstKind::FromInt(index); } - auto Print(llvm::raw_ostream& out) const -> void { - out << "inst"; - if (!is_valid()) { - IdBase::Print(out); - } else if (is_builtin()) { - out << builtin_inst_kind(); - } else { - // Use the `+` as a small reminder that this is a delta, rather than an - // absolute index. - out << "+" << index - BuiltinInstKind::ValidCount; - } - } + auto Print(llvm::raw_ostream& out) const -> void; }; constexpr InstId InstId::Invalid = InstId(InvalidIndex); @@ -165,23 +154,7 @@ struct ConstantId : public IdBase, public Printable { // template constants should be wrapped with "templateConstant(...)" so that // they aren't printed the same as an InstId. This can be set to false if // there is no risk of ambiguity. - auto Print(llvm::raw_ostream& out, bool disambiguate = true) const -> void { - if (!is_valid()) { - IdBase::Print(out); - } else if (is_template()) { - if (disambiguate) { - out << "templateConstant("; - } - out << template_inst_id(); - if (disambiguate) { - out << ")"; - } - } else if (is_symbolic()) { - out << "symbolicConstant" << symbolic_index(); - } else { - out << "runtime"; - } - } + auto Print(llvm::raw_ostream& out, bool disambiguate = true) const -> void; private: friend class ConstantValueStore; @@ -266,14 +239,7 @@ struct RuntimeParamIndex : public IndexBase, using IndexBase::IndexBase; - auto Print(llvm::raw_ostream& out) const -> void { - out << "runtime_param"; - if (*this == Unknown) { - out << ""; - } else { - IndexBase::Print(out); - } - } + auto Print(llvm::raw_ostream& out) const -> void; }; constexpr RuntimeParamIndex RuntimeParamIndex::Invalid = @@ -441,14 +407,7 @@ struct GenericInstIndex : public IndexBase, public Printable { return IndexBase::index >= 0 ? Declaration : Definition; } - auto Print(llvm::raw_ostream& out) const -> void { - out << "genericInst"; - if (is_valid()) { - out << (region() == Declaration ? "InDecl" : "InDef") << index(); - } else { - out << ""; - } - } + auto Print(llvm::raw_ostream& out) const -> void; private: static constexpr auto MakeInvalid() -> GenericInstIndex { @@ -501,15 +460,7 @@ struct BoolValue : public IdBase, public Printable { } using IdBase::IdBase; - auto Print(llvm::raw_ostream& out) const -> void { - if (*this == False) { - out << "false"; - } else if (*this == True) { - out << "true"; - } else { - CARBON_FATAL("Invalid bool value {0}", index); - } - } + auto Print(llvm::raw_ostream& out) const -> void; }; constexpr BoolValue BoolValue::False = BoolValue(0); @@ -528,15 +479,7 @@ struct IntKind : public IdBase, public Printable { // Returns whether this type is signed. constexpr auto is_signed() -> bool { return *this == Signed; } - auto Print(llvm::raw_ostream& out) const -> void { - if (*this == Unsigned) { - out << "unsigned"; - } else if (*this == Signed) { - out << "signed"; - } else { - CARBON_FATAL("Invalid int kind value {0}", index); - } - } + auto Print(llvm::raw_ostream& out) const -> void; }; constexpr IntKind IntKind::Unsigned = IntKind(0); @@ -577,15 +520,7 @@ struct NameId : public IdBase, public Printable { static const int NonIndexValueCount; // Returns the NameId corresponding to a particular IdentifierId. - static auto ForIdentifier(IdentifierId id) -> NameId { - if (id.index >= 0) { - return NameId(id.index); - } else if (!id.is_valid()) { - return NameId::Invalid; - } else { - CARBON_FATAL("Unexpected identifier ID {0}", id); - } - } + static auto ForIdentifier(IdentifierId id) -> NameId; using IdBase::IdBase; @@ -595,25 +530,7 @@ struct NameId : public IdBase, public Printable { return index >= 0 ? IdentifierId(index) : IdentifierId::Invalid; } - auto Print(llvm::raw_ostream& out) const -> void { - out << "name"; - if (*this == SelfValue) { - out << "SelfValue"; - } else if (*this == SelfType) { - out << "SelfType"; - } else if (*this == PeriodSelf) { - out << "PeriodSelf"; - } else if (*this == ReturnSlot) { - out << "ReturnSlot"; - } else if (*this == PackageNamespace) { - out << "PackageNamespace"; - } else if (*this == Base) { - out << "Base"; - } else { - CARBON_CHECK(!is_valid() || index >= 0, "Unknown index {0}", index); - IdBase::Print(out); - } - } + auto Print(llvm::raw_ostream& out) const -> void; }; constexpr NameId NameId::Invalid = NameId(InvalidIndex); @@ -676,22 +593,7 @@ struct InstBlockId : public IdBase, public Printable { static const InstBlockId Unreachable; using IdBase::IdBase; - auto Print(llvm::raw_ostream& out) const -> void { - if (*this == Unreachable) { - out << "unreachable"; - } else if (*this == Empty) { - out << "empty"; - } else if (*this == Exports) { - out << "exports"; - } else if (*this == ImportRefs) { - out << "import_refs"; - } else if (*this == GlobalInit) { - out << "global_init"; - } else { - out << "block"; - IdBase::Print(out); - } - } + auto Print(llvm::raw_ostream& out) const -> void; }; constexpr InstBlockId InstBlockId::Empty = InstBlockId(0); @@ -756,20 +658,7 @@ struct TypeId : public IdBase, public Printable { // Returns the constant ID that defines the type. auto AsConstantId() const -> ConstantId { return ConstantId(index); } - auto Print(llvm::raw_ostream& out) const -> void { - out << "type"; - if (*this == TypeType) { - out << "TypeType"; - } else if (*this == AutoType) { - out << "AutoType"; - } else if (*this == Error) { - out << "Error"; - } else { - out << "("; - AsConstantId().Print(out, /*disambiguate=*/false); - out << ")"; - } - } + auto Print(llvm::raw_ostream& out) const -> void; }; constexpr TypeId TypeId::TypeType = TypeId::ForTypeConstant( @@ -829,17 +718,7 @@ struct LibraryNameId : public IdBase, public Printable { static const LibraryNameId Error; // Returns the LibraryNameId for a library name as a string literal. - static auto ForStringLiteralValueId(StringLiteralValueId id) - -> LibraryNameId { - CARBON_CHECK(id.index >= InvalidIndex, "Unexpected library name ID {0}", - id); - if (id == StringLiteralValueId::Invalid) { - // Prior to SemIR, we use invalid to indicate `default`. - return LibraryNameId::Default; - } else { - return LibraryNameId(id.index); - } - } + static auto ForStringLiteralValueId(StringLiteralValueId id) -> LibraryNameId; using IdBase::IdBase; @@ -849,16 +728,7 @@ struct LibraryNameId : public IdBase, public Printable { return StringLiteralValueId(index); } - auto Print(llvm::raw_ostream& out) const -> void { - out << "libraryName"; - if (*this == Default) { - out << "Default"; - } else if (*this == Error) { - out << ""; - } else { - IdBase::Print(out); - } - } + auto Print(llvm::raw_ostream& out) const -> void; }; constexpr LibraryNameId LibraryNameId::Invalid = LibraryNameId(InvalidIndex); @@ -945,14 +815,7 @@ struct LocId : public IdBase, public Printable { return ImportIRInstId(InvalidIndex + ImportIRInstId::InvalidIndex - index); } - auto Print(llvm::raw_ostream& out) const -> void { - out << "loc_"; - if (is_node_id() || !is_valid()) { - out << node_id(); - } else { - out << import_ir_inst_id(); - } - } + auto Print(llvm::raw_ostream& out) const -> void; }; constexpr LocId LocId::Invalid = LocId(Parse::NodeId::Invalid); diff --git a/toolchain/sem_ir/inst.h b/toolchain/sem_ir/inst.h index ae43afc861aff..ba02d016e8ce4 100644 --- a/toolchain/sem_ir/inst.h +++ b/toolchain/sem_ir/inst.h @@ -19,6 +19,7 @@ #include "toolchain/sem_ir/builtin_inst_kind.h" #include "toolchain/sem_ir/id_kind.h" #include "toolchain/sem_ir/inst_kind.h" +#include "toolchain/sem_ir/singleton_insts.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::SemIR { @@ -127,6 +128,18 @@ concept InstLikeType = requires { sizeof(InstLikeTypeInfo); }; // data where the instruction's kind is not known. class Inst : public Printable { public: + // Makes an instruction for a singleton. This exists to support simple + // construction of all singletons by File. + static auto MakeSingleton(InstKind kind) -> Inst { + CARBON_CHECK(IsSingletonInstKind(kind)); + // Error uses a self-referential type so that it's not accidentally treated + // as a normal type. Every other builtin is a type, including the + // self-referential TypeType. + auto type_id = + kind == InstKind::ErrorInst ? TypeId::Error : TypeId::TypeType; + return Inst(kind, type_id, InstId::InvalidIndex, InstId::InvalidIndex); + } + template requires Internal::InstLikeType // NOLINTNEXTLINE(google-explicit-constructor) diff --git a/toolchain/sem_ir/inst_kind.def b/toolchain/sem_ir/inst_kind.def index 141f0b2ed6987..28b9ddf3965dc 100644 --- a/toolchain/sem_ir/inst_kind.def +++ b/toolchain/sem_ir/inst_kind.def @@ -37,17 +37,17 @@ #endif CARBON_SEM_IR_BUILTIN_INST_KIND(TypeType) -CARBON_SEM_IR_BUILTIN_INST_KIND(ErrorInst) CARBON_SEM_IR_BUILTIN_INST_KIND(AutoType) CARBON_SEM_IR_BUILTIN_INST_KIND(BoolType) +CARBON_SEM_IR_BUILTIN_INST_KIND(BoundMethodType) +CARBON_SEM_IR_BUILTIN_INST_KIND(ErrorInst) CARBON_SEM_IR_BUILTIN_INST_KIND(IntLiteralType) CARBON_SEM_IR_BUILTIN_INST_KIND(LegacyFloatType) -CARBON_SEM_IR_BUILTIN_INST_KIND(StringType) -CARBON_SEM_IR_BUILTIN_INST_KIND(BoundMethodType) -CARBON_SEM_IR_BUILTIN_INST_KIND(SpecificFunctionType) CARBON_SEM_IR_BUILTIN_INST_KIND(NamespaceType) -CARBON_SEM_IR_BUILTIN_INST_KIND(WitnessType) +CARBON_SEM_IR_BUILTIN_INST_KIND(SpecificFunctionType) +CARBON_SEM_IR_BUILTIN_INST_KIND(StringType) CARBON_SEM_IR_BUILTIN_INST_KIND(VtableType) +CARBON_SEM_IR_BUILTIN_INST_KIND(WitnessType) // For each instruction kind declared here there is a matching definition in // `typed_insts.h`. diff --git a/toolchain/sem_ir/inst_kind.h b/toolchain/sem_ir/inst_kind.h index 671c90cc0ac96..eb603e377c80e 100644 --- a/toolchain/sem_ir/inst_kind.h +++ b/toolchain/sem_ir/inst_kind.h @@ -104,6 +104,7 @@ class InstKind : public CARBON_ENUM_BASE(InstKind) { using EnumBase::AsInt; using EnumBase::FromInt; + using EnumBase::Make; // Returns true if the kind matches any of the provided instructions' kinds. template diff --git a/toolchain/sem_ir/singleton_insts.h b/toolchain/sem_ir/singleton_insts.h new file mode 100644 index 0000000000000..043c507fb8b02 --- /dev/null +++ b/toolchain/sem_ir/singleton_insts.h @@ -0,0 +1,76 @@ +// 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_SINGLETON_INSTS_H_ +#define CARBON_TOOLCHAIN_SEM_IR_SINGLETON_INSTS_H_ + +#include "toolchain/sem_ir/ids.h" +#include "toolchain/sem_ir/inst_kind.h" + +namespace Carbon::SemIR { + +// The canonical list of singleton kinds. The order of `TypeType` is +// significant because other singletons use it as a type. +static constexpr std::array SingletonInstKinds = { + InstKind::TypeType, + InstKind::AutoType, + InstKind::BoolType, + InstKind::BoundMethodType, + InstKind::ErrorInst, + InstKind::IntLiteralType, + InstKind::LegacyFloatType, + InstKind::NamespaceType, + InstKind::SpecificFunctionType, + InstKind::StringType, + InstKind::VtableType, + InstKind::WitnessType, +}; + +// Returns true if the InstKind is a singleton. +inline constexpr auto IsSingletonInstKind(InstKind kind) -> bool; + +// Provides the InstId for singleton instructions. These are exposed as +// `InstT::SingletonInstId` in `typed_insts.h`. +template + requires(IsSingletonInstKind(InstKind::Make(Kind))) +inline constexpr auto MakeSingletonInstId() -> InstId; + +// Only implementation details are below. + +namespace Internal { + +// Returns the index for a singleton instruction, or -1 if it's not a singleton. +inline constexpr auto GetSingletonInstIndex(InstKind kind) -> int32_t { + for (int32_t i = 0; i < static_cast(SingletonInstKinds.size()); + ++i) { + if (SingletonInstKinds[i] == kind) { + return i; + } + } + return -1; +} + +} // namespace Internal + +inline constexpr auto IsSingletonInstKind(InstKind kind) -> bool { + return Internal::GetSingletonInstIndex(kind) >= 0; +} + +template + requires(IsSingletonInstKind(InstKind::Make(Kind))) +inline constexpr auto MakeSingletonInstId() -> InstId { + auto index = Internal::GetSingletonInstIndex(InstKind::Make(Kind)); + return InstId(index); +} + +// TODO: This verifies values match while working on removing +// `CARBON_SEM_IR_BUILTIN_INST_KIND`. +#define CARBON_SEM_IR_BUILTIN_INST_KIND(Name) \ + static_assert(InstId::Builtin##Name == \ + MakeSingletonInstId()); +#include "toolchain/sem_ir/inst_kind.def" + +} // namespace Carbon::SemIR + +#endif // CARBON_TOOLCHAIN_SEM_IR_SINGLETON_INSTS_H_ diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index 73469dd6e0341..c0bb7c61414ef 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -10,6 +10,7 @@ #include "toolchain/sem_ir/builtin_inst_kind.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst_kind.h" +#include "toolchain/sem_ir/singleton_insts.h" // Representations for specific kinds of instructions. // @@ -17,6 +18,7 @@ // // - Either a `Kind` constant, or a `Kinds` constant and an `InstKind kind;` // member. These are described below. +// - Optionally, a `SingletonInstId` if it is a singleton instruction. // - Optionally, a `TypeId type_id;` member, for instructions that produce a // value. This includes instructions that produce an abstract value, such as a // `Namespace`, for which a placeholder type should be used. @@ -49,28 +51,25 @@ namespace Carbon::SemIR { // Used for the type of patterns that do not match a fixed type. -// -// TODO: Annotate as a builtin. struct AutoType { static constexpr auto Kind = InstKind::AutoType.Define( {.ir_name = "auto", .is_type = InstIsType::Always, .constant_kind = InstConstantKind::Always}); + static constexpr auto SingletonInstId = MakeSingletonInstId(); TypeId type_id; }; // The type of bool literals and branch conditions, bool. -// -// Although this is a builtin, it may still evolve to a more standard type and -// be removed. -// -// TODO: Annotate as a builtin. struct BoolType { static constexpr auto Kind = InstKind::BoolType.Define( {.ir_name = "bool", .is_type = InstIsType::Always, .constant_kind = InstConstantKind::Always}); + // This is a singleton instruction. However, it may still evolve into a more + // standard type and be removed. + static constexpr auto SingletonInstId = MakeSingletonInstId(); TypeId type_id; }; @@ -401,15 +400,15 @@ struct BoundMethod { }; // The type of bound method values. -// -// Although this is a builtin, it may still evolve to a more standard type and -// be removed. struct BoundMethodType { static constexpr auto Kind = InstKind::BoundMethodType.Define( {.ir_name = "", .is_type = InstIsType::Always, .constant_kind = InstConstantKind::Always}); + // This is a singleton instruction. However, it may still evolve into a more + // standard type and be removed. + static constexpr auto SingletonInstId = MakeSingletonInstId(); TypeId type_id; }; @@ -584,13 +583,12 @@ struct Deref { // required. For example, when there is a type checking issue, this will be used // in the type_id. It's typically used as a cue that semantic checking doesn't // need to issue further diagnostics. -// -// TODO: Annotate as a builtin. struct ErrorInst { static constexpr auto Kind = InstKind::ErrorInst.Define( {.ir_name = "", .is_type = InstIsType::Always, .constant_kind = InstConstantKind::Always}); + static constexpr auto SingletonInstId = MakeSingletonInstId(); TypeId type_id; }; @@ -703,15 +701,15 @@ struct FloatType { // The legacy float type. This is currently used for real literals, and is // treated as f64. It's separate from `FloatType`, and should change to mirror // integers, likely replacing this with a `FloatLiteralType`. -// -// Although this is a builtin, it may still evolve to a more standard type and -// be removed. struct LegacyFloatType { static constexpr auto Kind = InstKind::LegacyFloatType.Define( {.ir_name = "f64", .is_type = InstIsType::Always, .constant_kind = InstConstantKind::Always}); + // This is a singleton instruction. However, it may still evolve into a more + // standard type and be removed. + static constexpr auto SingletonInstId = MakeSingletonInstId(); TypeId type_id; }; @@ -899,15 +897,15 @@ struct IntValue { // literals and as the parameter type of `Core.Int` and `Core.Float`. This type // only provides compile-time operations, and is represented as an empty type at // runtime. -// -// Although this is a builtin, it may still evolve to a more standard type and -// be removed. struct IntLiteralType { static constexpr auto Kind = InstKind::IntLiteralType.Define( {.ir_name = "Core.IntLiteral", .is_type = InstIsType::Always, .constant_kind = InstConstantKind::Always}); + // This is a singleton instruction. However, it may still evolve into a more + // standard type and be removed. + static constexpr auto SingletonInstId = MakeSingletonInstId(); TypeId type_id; }; @@ -953,15 +951,15 @@ struct Namespace { }; // The type of namespace and imported package names. -// -// Although this is a builtin, it may still evolve to a more standard type and -// be removed. struct NamespaceType { static constexpr auto Kind = InstKind::NamespaceType.Define( {.ir_name = "", .is_type = InstIsType::Always, .constant_kind = InstConstantKind::Always}); + // This is a singleton instruction. However, it may still evolve into a more + // standard type and be removed. + static constexpr auto SingletonInstId = MakeSingletonInstId(); TypeId type_id; }; @@ -1185,15 +1183,15 @@ struct SpecificFunction { }; // The type of specific functions. -// -// Although this is a builtin, it may still evolve to a more standard type and -// be removed. struct SpecificFunctionType { static constexpr auto Kind = InstKind::SpecificFunctionType.Define( {.ir_name = "", .is_type = InstIsType::Always, .constant_kind = InstConstantKind::Always}); + // This is a singleton instruction. However, it may still evolve into a more + // standard type and be removed. + static constexpr auto SingletonInstId = MakeSingletonInstId(); TypeId type_id; }; @@ -1223,15 +1221,15 @@ struct StringLiteral { }; // The type of string values and String literals. -// -// Although this is a builtin, it may still evolve to a more standard type and -// be removed. struct StringType { static constexpr auto Kind = InstKind::StringType.Define( {.ir_name = "String", .is_type = InstIsType::Always, .constant_kind = InstConstantKind::Always}); + // This is a singleton instruction. However, it may still evolve into a more + // standard type and be removed. + static constexpr auto SingletonInstId = MakeSingletonInstId(); TypeId type_id; }; @@ -1370,13 +1368,12 @@ struct TupleValue { // Tracks expressions which are valid as types. This has a deliberately // self-referential type. -// -// TODO: Annotate as a builtin. struct TypeType { static constexpr auto Kind = InstKind::TypeType.Define( {.ir_name = "type", .is_type = InstIsType::Always, .constant_kind = InstConstantKind::Always}); + static constexpr auto SingletonInstId = MakeSingletonInstId(); TypeId type_id; }; @@ -1444,15 +1441,15 @@ struct VarStorage { }; // The type of virtual function tables. -// -// Although this is a builtin, it may still evolve to a more standard type and -// be removed. struct VtableType { static constexpr auto Kind = InstKind::VtableType.Define( {.ir_name = "", .is_type = InstIsType::Always, .constant_kind = InstConstantKind::Always}); + // This is a singleton instruction. However, it may still evolve into a more + // standard type and be removed. + static constexpr auto SingletonInstId = MakeSingletonInstId(); TypeId type_id; }; @@ -1472,15 +1469,15 @@ struct WhereExpr { }; // The type of witnesses. -// -// Although this is a builtin, it may still evolve to a more standard type and -// be removed. struct WitnessType { static constexpr auto Kind = InstKind::WitnessType.Define( {.ir_name = "", .is_type = InstIsType::Always, .constant_kind = InstConstantKind::Always}); + // This is a singleton instruction. However, it may still evolve into a more + // standard type and be removed. + static constexpr auto SingletonInstId = MakeSingletonInstId(); TypeId type_id; };