Rebuild all constants in the eval block. (#4155)

Instead of reusing instructions from the generic entity in the eval
block, rebuild constants in the same way we rebuild types. The previous
attempt to not rebuild these constants assumed that every constant used
in a generic would be built in that generic, and not referenced directly
or referenced from some enclosing scope, which isn't true in practice
and is a fragile assumption in any case.

We could add back some reuse of instructions from the generic -- if we
happen to see the right instruction to build a constant, we could
opportunistically reuse it -- but given the complexity added by doing
so, I'm not pursuing that here.

Now that the eval block for a generic consists of instructions uniquely
owned by that generic, rather than often being shared with another
entity, include the generic in the formatted SemIR output. I'm using the
same scope name for the generic object itself as for the parameterized
class / function / interface, because there are very frequently
references between them and this keeps the IR simpler and more readable,
and avoids needing to invent a second name for the scope.
This commit is contained in:
Richard Smith
2024-07-23 21:51:08 +00:00
committed by GitHub
parent db022658c6
commit fc8e686607
96 changed files with 3038 additions and 2385 deletions
+114 -100
View File
@@ -151,14 +151,6 @@ class FormatterImpl {
Indent(-2);
}
// Wraps the current line, prior to some text that we expect to be quite long
// and more readable on a separate line. This is indented two levels more than
// the ambient text.
auto WrapLine() -> void {
out_ << '\n';
Indent(4);
}
auto FormatConstants() -> void {
if (!sem_ir_.constants().size()) {
return;
@@ -186,15 +178,31 @@ class FormatterImpl {
out_ << "\n\n";
}
template <typename IdT>
auto FormatEntityStart(llvm::StringRef entity_kind, GenericId generic_id,
IdT entity_id) -> void {
if (generic_id.is_valid()) {
FormatGenericStart(entity_kind, generic_id);
out_ << "\n";
Indent();
out_ << entity_kind;
} else {
out_ << "\n";
Indent();
out_ << entity_kind << " ";
FormatName(entity_id);
}
}
auto FormatEntityEnd(GenericId generic_id) -> void {
if (generic_id.is_valid()) {
FormatGenericEnd();
}
}
auto FormatClass(ClassId id) -> void {
const Class& class_info = sem_ir_.classes().Get(id);
out_ << "\nclass ";
FormatClassName(id);
if (class_info.generic_id.is_valid()) {
FormatGeneric(class_info.generic_id);
}
FormatEntityStart("class", class_info.generic_id, id);
llvm::SaveAndRestore class_scope(scope_, inst_namer_->GetScopeFor(id));
@@ -208,17 +216,13 @@ class FormatterImpl {
} else {
out_ << ";\n";
}
FormatEntityEnd(class_info.generic_id);
}
auto FormatInterface(InterfaceId id) -> void {
const Interface& interface_info = sem_ir_.interfaces().Get(id);
out_ << "\ninterface ";
FormatInterfaceName(id);
if (interface_info.generic_id.is_valid()) {
FormatGeneric(interface_info.generic_id);
}
FormatEntityStart("interface", interface_info.generic_id, id);
llvm::SaveAndRestore interface_scope(scope_, inst_namer_->GetScopeFor(id));
@@ -243,13 +247,14 @@ class FormatterImpl {
} else {
out_ << ";\n";
}
FormatEntityEnd(interface_info.generic_id);
}
auto FormatImpl(ImplId id) -> void {
const Impl& impl_info = sem_ir_.impls().Get(id);
FormatEntityStart("impl", SemIR::GenericId::Invalid, id);
out_ << "\nimpl ";
FormatImplName(id);
out_ << ": ";
// TODO: Include the deduced parameter list if present.
FormatType(impl_info.self_id);
@@ -283,15 +288,7 @@ class FormatterImpl {
auto FormatFunction(FunctionId id) -> void {
const Function& fn = sem_ir_.functions().Get(id);
out_ << "\n";
if (fn.is_extern) {
out_ << "extern ";
}
out_ << "fn ";
FormatFunctionName(id);
FormatEntityStart(fn.is_extern ? "extern fn" : "fn", fn.generic_id, id);
llvm::SaveAndRestore function_scope(scope_, inst_namer_->GetScopeFor(id));
@@ -310,7 +307,7 @@ class FormatterImpl {
if (fn.return_storage_id.is_valid()) {
out_ << " -> ";
if (!fn.body_block_ids.empty() && fn.has_return_slot()) {
FormatInstName(fn.return_storage_id);
FormatName(fn.return_storage_id);
out_ << ": ";
}
FormatType(sem_ir_.insts().Get(fn.return_storage_id).type_id());
@@ -323,10 +320,6 @@ class FormatterImpl {
out_ << "\"";
}
if (fn.generic_id.is_valid()) {
FormatGeneric(fn.generic_id);
}
if (!fn.body_block_ids.empty()) {
out_ << ' ';
OpenBrace();
@@ -344,17 +337,37 @@ class FormatterImpl {
} else {
out_ << ";\n";
}
FormatEntityEnd(fn.generic_id);
}
auto FormatGeneric(GenericId generic_id) -> void {
auto FormatGenericStart(llvm::StringRef entity_kind, GenericId generic_id)
-> void {
const auto& generic = sem_ir_.generics().Get(generic_id);
out_ << "\n";
Indent();
out_ << "generic " << entity_kind << " ";
FormatName(generic_id);
WrapLine();
out_ << "generic [";
llvm::SaveAndRestore generic_scope(scope_,
inst_namer_->GetScopeFor(generic_id));
out_ << "(";
FormatParamList(generic.bindings_id);
out_ << "]";
// TODO: Format at least the portions of the declaration and definition
// blocks that don't duplicate portions of the generic body.
out_ << ") ";
OpenBrace();
FormatCodeBlock(generic.decl_block_id);
if (generic.definition_block_id.is_valid()) {
IndentLabel();
out_ << "!definition:\n";
FormatCodeBlock(generic.definition_block_id);
}
}
auto FormatGenericEnd() -> void {
CloseBrace();
out_ << '\n';
}
auto FormatSpecificRegion(const Generic& generic,
@@ -365,15 +378,33 @@ class FormatterImpl {
return;
}
IndentLabel();
out_ << region_name << ":\n";
for (auto [generic_inst_id, specific_inst_id] :
llvm::zip(sem_ir_.inst_blocks().Get(generic.GetEvalBlock(region)),
sem_ir_.inst_blocks().Get(specific.GetValueBlock(region)))) {
if (!region_name.empty()) {
IndentLabel();
out_ << "!" << region_name << ":\n";
}
for (auto [generic_inst_id, specific_inst_id] : llvm::zip_longest(
sem_ir_.inst_blocks().Get(generic.GetEvalBlock(region)),
sem_ir_.inst_blocks().Get(specific.GetValueBlock(region)))) {
if (generic_inst_id && specific_inst_id &&
sem_ir_.insts().Is<StructTypeField>(*generic_inst_id) &&
sem_ir_.insts().Is<StructTypeField>(*specific_inst_id)) {
// Skip printing struct type fields to match the way we print the
// generic.
continue;
}
Indent();
FormatInstName(generic_inst_id);
if (generic_inst_id) {
FormatName(*generic_inst_id);
} else {
out_ << "<missing>";
}
out_ << " => ";
FormatInstName(specific_inst_id);
if (specific_inst_id) {
FormatName(*specific_inst_id);
} else {
out_ << "<missing>";
}
out_ << "\n";
}
}
@@ -384,7 +415,7 @@ class FormatterImpl {
out_ << "\n";
out_ << "specific ";
FormatSpecificName(id);
FormatName(id);
// TODO: Remove once we stop forming generic specifics with no generic
// during import.
@@ -395,10 +426,12 @@ class FormatterImpl {
out_ << " ";
const auto& generic = sem_ir_.generics().Get(specific.generic_id);
llvm::SaveAndRestore generic_scope(
scope_, inst_namer_->GetScopeFor(specific.generic_id));
OpenBrace();
FormatSpecificRegion(generic, specific,
GenericInstIndex::Region::Declaration, "declaration");
GenericInstIndex::Region::Declaration, "");
FormatSpecificRegion(generic, specific,
GenericInstIndex::Region::Definition, "definition");
CloseBrace();
@@ -418,7 +451,7 @@ class FormatterImpl {
out_ << "addr ";
param_id = addr->inner_id;
}
FormatInstName(param_id);
FormatName(param_id);
out_ << ": ";
FormatType(sem_ir_.insts().Get(param_id).type_id());
}
@@ -472,7 +505,7 @@ class FormatterImpl {
break;
}
out_ << " = ";
FormatInstName(inst_id);
FormatName(inst_id);
out_ << "\n";
}
@@ -560,7 +593,7 @@ class FormatterImpl {
auto FormatInstLHS(InstId inst_id, Inst inst) -> void {
switch (inst.kind().value_kind()) {
case InstValueKind::Typed:
FormatInstName(inst_id);
FormatName(inst_id);
out_ << ": ";
switch (GetExprCategory(sem_ir_, inst_id)) {
case ExprCategory::NotExpr:
@@ -586,14 +619,14 @@ class FormatterImpl {
// Format ImportDecl with its name.
auto FormatInstLHS(InstId inst_id, ImportDecl /*inst*/) -> void {
FormatInstName(inst_id);
FormatName(inst_id);
out_ << " = ";
}
// Print ImportRefUnloaded with type-like semantics even though it lacks a
// type_id.
auto FormatInstLHS(InstId inst_id, ImportRefUnloaded /*inst*/) -> void {
FormatInstName(inst_id);
FormatName(inst_id);
out_ << " = ";
}
@@ -638,7 +671,7 @@ class FormatterImpl {
Indent();
}
out_ << "if ";
FormatInstName(inst.cond_id);
FormatName(inst.cond_id);
out_ << " " << Branch::Kind.ir_name() << " ";
FormatLabel(inst.target_id);
out_ << " else ";
@@ -652,7 +685,7 @@ class FormatterImpl {
out_ << BranchWithArg::Kind.ir_name() << " ";
FormatLabel(inst.target_id);
out_ << "(";
FormatInstName(inst.arg_id);
FormatName(inst.arg_id);
out_ << ")\n";
in_terminator_sequence_ = false;
}
@@ -826,17 +859,17 @@ class FormatterImpl {
}
}
auto FormatArg(FunctionId id) -> void { FormatFunctionName(id); }
auto FormatArg(FunctionId id) -> void { FormatName(id); }
auto FormatArg(ClassId id) -> void { FormatClassName(id); }
auto FormatArg(ClassId id) -> void { FormatName(id); }
auto FormatArg(InterfaceId id) -> void { FormatInterfaceName(id); }
auto FormatArg(InterfaceId id) -> void { FormatName(id); }
auto FormatArg(IntKind k) -> void { k.Print(out_); }
auto FormatArg(FloatKind k) -> void { k.Print(out_); }
auto FormatArg(ImplId id) -> void { FormatImplName(id); }
auto FormatArg(ImplId id) -> void { FormatName(id); }
auto FormatArg(ImportIRId id) -> void {
if (!id.is_valid()) {
@@ -891,7 +924,7 @@ class FormatterImpl {
CloseBrace();
}
auto FormatArg(InstId id) -> void { FormatInstName(id); }
auto FormatArg(InstId id) -> void { FormatName(id); }
auto FormatArg(InstBlockId id) -> void {
if (!id.is_valid()) {
@@ -908,7 +941,7 @@ class FormatterImpl {
out_ << ')';
}
auto FormatArg(GenericInstanceId id) -> void { FormatSpecificName(id); }
auto FormatArg(GenericInstanceId id) -> void { FormatName(id); }
auto FormatArg(RealId id) -> void {
// TODO: Format with a `.` when the exponent is near zero.
@@ -947,44 +980,25 @@ class FormatterImpl {
out_ << sem_ir_.names().GetFormatted(id);
}
auto FormatInstName(InstId id) -> void {
auto FormatName(InstId id) -> void {
out_ << inst_namer_->GetNameFor(scope_, id);
}
template <typename IdT>
auto FormatName(IdT id) -> void {
out_ << inst_namer_->GetNameFor(id);
}
auto FormatName(GenericInstanceId id) -> void {
const auto& specific = sem_ir_.generic_instances().Get(id);
FormatName(specific.generic_id);
FormatArg(specific.args_id);
}
auto FormatLabel(InstBlockId id) -> void {
out_ << inst_namer_->GetLabelFor(scope_, id);
}
auto FormatFunctionName(FunctionId id) -> void {
out_ << inst_namer_->GetNameFor(id);
}
auto FormatClassName(ClassId id) -> void {
out_ << inst_namer_->GetNameFor(id);
}
auto FormatInterfaceName(InterfaceId id) -> void {
out_ << inst_namer_->GetNameFor(id);
}
auto FormatImplName(ImplId id) -> void {
out_ << inst_namer_->GetNameFor(id);
}
auto FormatSpecificName(GenericInstanceId id) -> void {
const auto& specific = sem_ir_.generic_instances().Get(id);
// TODO: We don't yet import generics properly, and instead form specifics
// with an invalid generic ID. In this case, just print a placeholder for
// now. Once import works, we can remove this code.
if (!specific.generic_id.is_valid()) {
out_ << "<invalid>";
} else {
const Generic& generic = sem_ir_.generics().Get(specific.generic_id);
FormatInstName(generic.decl_id);
}
FormatArg(specific.args_id);
}
auto FormatConstant(ConstantId id) -> void {
if (!id.is_valid()) {
out_ << "<not constant>";
@@ -999,17 +1013,17 @@ class FormatterImpl {
if (symbolic_constant.generic_id.is_valid()) {
const auto& generic =
sem_ir_.generics().Get(symbolic_constant.generic_id);
FormatInstName(sem_ir_.inst_blocks().Get(generic.GetEvalBlock(
FormatName(sem_ir_.inst_blocks().Get(generic.GetEvalBlock(
symbolic_constant.index
.region()))[symbolic_constant.index.index()]);
out_ << " (";
FormatInstName(sem_ir_.constant_values().GetInstId(id));
FormatName(sem_ir_.constant_values().GetInstId(id));
out_ << ")";
return;
}
}
FormatInstName(sem_ir_.constant_values().GetInstId(id));
FormatName(sem_ir_.constant_values().GetInstId(id));
}
auto FormatType(TypeId id) -> void {