Skip to content

Commit

Permalink
Add framework for singleton instructions. (carbon-language#4582)
Browse files Browse the repository at this point in the history
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 <[email protected]>
Co-authored-by: Dana Jansens <[email protected]>
Co-authored-by: Richard Smith <[email protected]>
Co-authored-by: josh11b <[email protected]>
Co-authored-by: Josh L <[email protected]>
Co-authored-by: Chandler Carruth <[email protected]>
Co-authored-by: Carbon Infra Bot <[email protected]>
Co-authored-by: Boaz Brickner <[email protected]>
Co-authored-by: Geoff Romer <[email protected]>
  • Loading branch information
10 people authored Dec 3, 2024
1 parent d434828 commit f45cbc6
Show file tree
Hide file tree
Showing 11 changed files with 345 additions and 233 deletions.
20 changes: 10 additions & 10 deletions toolchain/check/testdata/basics/builtin_insts.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -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<invalid>, 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:
Expand Down
2 changes: 2 additions & 0 deletions toolchain/sem_ir/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ cc_library(
name = "typed_insts",
srcs = [
"builtin_inst_kind.cpp",
"ids.cpp",
"inst_kind.cpp",
],
hdrs = [
"builtin_inst_kind.h",
"id_kind.h",
"ids.h",
"inst_kind.h",
"singleton_insts.h",
"typed_insts.h",
],
textual_hdrs = ["inst_kind.def"],
Expand Down
34 changes: 11 additions & 23 deletions toolchain/sem_ir/file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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<Name>( \
{.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));
}
}

Expand Down Expand Up @@ -99,9 +87,9 @@ auto File::Verify() const -> ErrorOr<Success> {
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) {
Expand All @@ -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),
Expand All @@ -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);
Expand Down
18 changes: 8 additions & 10 deletions toolchain/sem_ir/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -48,16 +49,13 @@ class File : public Printable<File> {
// Verifies that invariants of the semantics IR hold.
auto Verify() const -> ErrorOr<Success>;

// 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
Expand Down Expand Up @@ -241,8 +239,8 @@ class File : public Printable<File> {
// the data is provided by allocator_.
BlockValueStore<TypeBlockId> 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.
Expand Down
174 changes: 174 additions & 0 deletions toolchain/sem_ir/ids.cpp
Original file line number Diff line number Diff line change
@@ -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 << "<unknown>";
} 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 << "<invalid>";
}
}

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 << "<error>";
} 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
Loading

0 comments on commit f45cbc6

Please sign in to comment.