-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Represent vtables as a top level SemIR construct #5472
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: trunk
Are you sure you want to change the base?
Conversation
c6792eb
to
adede1b
Compare
…neric The goal was/is to reduce the overhead for vtables in generics - the previous representation/prior to this patch caused a new vtable to be created in every specific which isn't generally what we want for Carbon generics (the whole specific/generic thing is meant to avoid creating specific versions for things that can be a generic form parameterized by a specific instead of manifest as a unique entity per specific) So this moves vtables to a top level object (like functions, classes, etc). Each dynamic class will have a vtable in this list. Classes have a `vtable_ptr` instruction in them that points to the vtable. The actual generic support hasn't been implemented in this patch, as I've been struggling with just getting this part of the migration going & wanted to get it flushed out before adding the additional complications. It's possible more laziness when doing cross-file importing would be suitable - for instance if we only need to reference the vtable from another file, but don't need to know its individual contents, it may be beneficial for the functions in the vtable to be import_refs (or to add another layer of indirection - so it can be a single import_ref all-or-nothing for the functions in the vtable).
adede1b
to
13b8fc5
Compare
toolchain/check/class.cpp
Outdated
@@ -131,22 +131,14 @@ static auto AddStructTypeFields( | |||
|
|||
// Builds and returns a vtable for the current class. Assumes that the virtual | |||
// functions for the class are listed as the top element of the `vtable_stack`. | |||
static auto BuildVtable(Context& context, Parse::NodeId node_id, | |||
SemIR::InstId base_vtable_id, | |||
static auto BuildVtable(Context& context, Parse::NodeId /* node_id */, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Drop the node_id
parameter if it's not needed. Seems easy to add back if that changes.
@@ -155,7 +147,7 @@ static auto BuildVtable(Context& context, Parse::NodeId node_id, | |||
for (auto override_fn_decl_id : vtable_contents) { | |||
auto override_fn_decl = | |||
context.insts().GetAs<SemIR::FunctionDecl>(override_fn_decl_id); | |||
const auto& override_fn = | |||
auto& override_fn = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can fn
be const auto&
? Seems nice if we can mark the base class's functions as const here.
toolchain/check/class.cpp
Outdated
if (canonical_base_vtable_inst_id == SemIR::InstId::None) { | ||
return SemIR::ErrorInst::InstId; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Producing an error without generating a diagnostic, when there's no error marking (eg ErrorInst
) found, is unusual. If this is necessary, it'd be good to have a comment here explaining what's happening.
AddInst<SemIR::VtablePtr>(context, node_id, | ||
{.type_id = vptr_type_id, | ||
.vtable_id = vtable_id, | ||
.specific_id = SemIR::SpecificId::None}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be context.generics().GetSelfSpecific(class_info.generic_id)
, so that we compute a different vtable_ptr
for each specialization of the class. (This is more vtables <-> generics stuff, so maybe just a TODO here for now?)
const auto& base_vtable_ptr_inst = | ||
context.insts().GetAs<SemIR::VtablePtr>( | ||
canonical_base_vtable_inst_id); | ||
base_vtable_id = base_vtable_ptr_inst.vtable_id; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could also capture the specific_id
here and pass it into BuildVtable
for use in CheckFunctionTypeMatches
. (Maybe not in this PR, though; it's probably better to handle vtables <-> generics interactions in a separate change.)
@@ -1710,15 +1712,15 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver, | |||
? GetLocalConstantInstId(resolver, import_class.adapt_id) | |||
: SemIR::InstId::None; | |||
auto& new_class = resolver.local_classes().Get(class_id); | |||
auto vtable_ptr_const_id = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a TODO here to not load this eagerly?
for (const auto& [id, class_info] : sem_ir_->vtables().enumerate()) { | ||
if (auto* vtable = BuildVtable(class_info)) { | ||
vtables_.Insert(id, vtable); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the new cross-file lowering logic, moving this from LowerDefinitions
to PrepareToLower
will cause vtable definitions for file A to get emitted when generating code for file B, if B uses a generic defined in A. I tried to keep this as similar to the old code as possible when adding the new cross-file stuff to avoid creating merge conflicts here.
What we should be doing here is probably: in PrepareToLower
, generate declarations for vtables, and in LowerDefinitions
, add initializers to convert those declarations to definitions. That's roughly what we do for variables (except that for variables we generate the declarations as a side effect of lowering the constant, rather than as a separate step in PrepareToLower
).
For now, can we get away with keeping this loop in LowerDefinitions
, so that we can add the cross-file stuff in a different PR, or is that no longer working? If not, moving it here with a TODO for now might work.
toolchain/lower/handle.cpp
Outdated
@@ -333,7 +333,7 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, | |||
|
|||
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, | |||
SemIR::VtablePtr inst) -> void { | |||
context.SetLocal(inst_id, context.GetValue(inst.vtable_id)); | |||
context.SetLocal(inst_id, context.GetVtable(inst.vtable_id)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this reachable? I'd expect that we never see a non-constant vtable_ptr
instruction in lowering, and could CARBON_FATAL
here instead.
toolchain/sem_ir/inst_namer.cpp
Outdated
@@ -693,6 +709,10 @@ auto InstNamer::NamingContext::NameInst() -> void { | |||
AddInstName("complete_type"); | |||
return; | |||
} | |||
case VtablePtr::Kind: { | |||
AddInstName("vtable_ptr"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be nice to include the class name here, when the instruction is in the constants scope.
Co-authored-by: Richard Smith <richard@metafoo.co.uk>
The goal was/is to reduce the overhead for vtables in generics - the previous representation/prior to this patch caused a new vtable to be created in every specific which isn't generally what we want for Carbon generics (the whole specific/generic thing is meant to avoid creating specific versions for things that can be a generic form parameterized by a specific instead of manifest as a unique entity per specific)
So this moves vtables to a top level object (like functions, classes, etc). Each dynamic class will have a vtable in this list.
Classes have a
vtable_ptr
instruction in them that points to the vtable.The actual generic support hasn't been implemented in this patch, as I've been struggling with just getting this part of the migration going & wanted to get it flushed out before adding the additional complications.
It's possible more laziness when doing cross-file importing would be suitable - for instance if we only need to reference the vtable from another file, but don't need to know its individual contents, it may be beneficial for the functions in the vtable to be import_refs (or to add another layer of indirection - so it can be a single import_ref
all-or-nothing for the functions in the vtable).