mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-04 11:21:05 +01:00
Initialize vptrs to point to vtables (#5244)
Adds a mapping to keep track of vtable LLVM IR decls/defs for use. Adds the vtable_id to the vtable_ptr initialize instruction for lookup. Adds emission of vtable declarations for use outside the file that defines the vtable. (this isn't done lazily, it's done for any imported class - it could be done lazily & maybe eventually has to be lazy to handle generics)
This commit is contained in:
+27
-21
@@ -156,7 +156,8 @@ static auto ConvertAggregateElement(
|
||||
llvm::ArrayRef<SemIR::InstId> src_literal_elems,
|
||||
ConversionTarget::Kind kind, SemIR::InstId target_id,
|
||||
SemIR::TypeId target_elem_type, PendingBlock* target_block,
|
||||
size_t src_field_index, size_t target_field_index) -> SemIR::InstId {
|
||||
size_t src_field_index, size_t target_field_index,
|
||||
SemIR::InstId vtable_id = SemIR::InstId::None) -> SemIR::InstId {
|
||||
// Compute the location of the source element. This goes into the current code
|
||||
// block, not into the target block.
|
||||
// TODO: Ideally we would discard this instruction if it's unused.
|
||||
@@ -179,7 +180,7 @@ static auto ConvertAggregateElement(
|
||||
target.init_id = MakeElementAccessInst<TargetAccessInstT>(
|
||||
context, loc_id, target_id, target_elem_type, *target_block,
|
||||
target_field_index);
|
||||
return Convert(context, loc_id, src_elem_id, target);
|
||||
return Convert(context, loc_id, src_elem_id, target, vtable_id);
|
||||
}
|
||||
|
||||
// Performs a conversion from a tuple to an array type. This function only
|
||||
@@ -372,12 +373,10 @@ static auto ConvertTupleToTuple(Context& context, SemIR::TupleType src_type,
|
||||
|
||||
// Common implementation for ConvertStructToStruct and ConvertStructToClass.
|
||||
template <typename TargetAccessInstT>
|
||||
static auto ConvertStructToStructOrClass(Context& context,
|
||||
SemIR::StructType src_type,
|
||||
SemIR::StructType dest_type,
|
||||
SemIR::InstId value_id,
|
||||
ConversionTarget target)
|
||||
-> SemIR::InstId {
|
||||
static auto ConvertStructToStructOrClass(
|
||||
Context& context, SemIR::StructType src_type, SemIR::StructType dest_type,
|
||||
SemIR::InstId value_id, ConversionTarget target,
|
||||
SemIR::InstId dest_vtable_id = SemIR::InstId::None) -> SemIR::InstId {
|
||||
static_assert(std::is_same_v<SemIR::ClassElementAccess, TargetAccessInstT> ||
|
||||
std::is_same_v<SemIR::StructAccess, TargetAccessInstT>);
|
||||
constexpr bool ToClass =
|
||||
@@ -466,7 +465,8 @@ static auto ConvertStructToStructOrClass(Context& context,
|
||||
.base_id = target.init_id,
|
||||
.index = SemIR::ElementIndex(i)});
|
||||
auto vtable_ptr_id = AddInst<SemIR::VtablePtr>(
|
||||
context, value_loc_id, {.type_id = dest_field.type_id});
|
||||
context, value_loc_id,
|
||||
{.type_id = dest_field.type_id, .vtable_id = dest_vtable_id});
|
||||
auto init_id =
|
||||
AddInst<SemIR::InitializeFrom>(context, value_loc_id,
|
||||
{.type_id = dest_field.type_id,
|
||||
@@ -512,7 +512,8 @@ static auto ConvertStructToStructOrClass(Context& context,
|
||||
ConvertAggregateElement<SemIR::StructAccess, TargetAccessInstT>(
|
||||
context, value_loc_id, value_id, src_field.type_id, literal_elems,
|
||||
inner_kind, target.init_id, dest_field.type_id, target.init_block,
|
||||
src_field_index, src_field_index + dest_vptr_offset);
|
||||
src_field_index, src_field_index + dest_vptr_offset,
|
||||
dest_vtable_id);
|
||||
if (init_id == SemIR::ErrorInst::SingletonInstId) {
|
||||
return SemIR::ErrorInst::SingletonInstId;
|
||||
}
|
||||
@@ -554,10 +555,10 @@ static auto ConvertStructToStruct(Context& context, SemIR::StructType src_type,
|
||||
// Performs a conversion from a struct to a class type. This function only
|
||||
// converts the type, and does not perform a final conversion to the requested
|
||||
// expression category.
|
||||
static auto ConvertStructToClass(Context& context, SemIR::StructType src_type,
|
||||
SemIR::ClassType dest_type,
|
||||
SemIR::InstId value_id,
|
||||
ConversionTarget target) -> SemIR::InstId {
|
||||
static auto ConvertStructToClass(
|
||||
Context& context, SemIR::StructType src_type, SemIR::ClassType dest_type,
|
||||
SemIR::InstId value_id, ConversionTarget target,
|
||||
SemIR::InstId dest_vtable_id = SemIR::InstId::None) -> SemIR::InstId {
|
||||
PendingBlock target_block(&context);
|
||||
auto& dest_class_info = context.classes().Get(dest_type.class_id);
|
||||
CARBON_CHECK(dest_class_info.inheritance_kind != SemIR::Class::Abstract);
|
||||
@@ -580,7 +581,8 @@ static auto ConvertStructToClass(Context& context, SemIR::StructType src_type,
|
||||
}
|
||||
|
||||
auto result_id = ConvertStructToStructOrClass<SemIR::ClassElementAccess>(
|
||||
context, src_type, dest_struct_type, value_id, target);
|
||||
context, src_type, dest_struct_type, value_id, target,
|
||||
dest_vtable_id.has_value() ? dest_vtable_id : dest_class_info.vtable_id);
|
||||
|
||||
if (need_temporary) {
|
||||
target_block.InsertHere();
|
||||
@@ -778,9 +780,10 @@ static auto DiagnoseConversionFailureToConstraintValue(
|
||||
}
|
||||
}
|
||||
|
||||
static auto PerformBuiltinConversion(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::InstId value_id,
|
||||
ConversionTarget target) -> SemIR::InstId {
|
||||
static auto PerformBuiltinConversion(
|
||||
Context& context, SemIR::LocId loc_id, SemIR::InstId value_id,
|
||||
ConversionTarget target, SemIR::InstId vtable_id = SemIR::InstId::None)
|
||||
-> SemIR::InstId {
|
||||
auto& sem_ir = context.sem_ir();
|
||||
auto value = sem_ir.insts().Get(value_id);
|
||||
auto value_type_id = value.type_id();
|
||||
@@ -952,7 +955,8 @@ static auto PerformBuiltinConversion(Context& context, SemIR::LocId loc_id,
|
||||
.Get(target_class_type->class_id)
|
||||
.adapt_id.has_value()) {
|
||||
return ConvertStructToClass(context, *src_struct_type,
|
||||
*target_class_type, value_id, target);
|
||||
*target_class_type, value_id, target,
|
||||
vtable_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1133,7 +1137,8 @@ auto PerformAction(Context& context, SemIR::LocId loc_id,
|
||||
}
|
||||
|
||||
auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
|
||||
ConversionTarget target) -> SemIR::InstId {
|
||||
ConversionTarget target, SemIR::InstId vtable_id)
|
||||
-> SemIR::InstId {
|
||||
auto& sem_ir = context.sem_ir();
|
||||
auto orig_expr_id = expr_id;
|
||||
|
||||
@@ -1197,7 +1202,8 @@ auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
|
||||
}
|
||||
|
||||
// Check whether any builtin conversion applies.
|
||||
expr_id = PerformBuiltinConversion(context, loc_id, expr_id, target);
|
||||
expr_id =
|
||||
PerformBuiltinConversion(context, loc_id, expr_id, target, vtable_id);
|
||||
if (expr_id == SemIR::ErrorInst::SingletonInstId) {
|
||||
return expr_id;
|
||||
}
|
||||
|
||||
@@ -55,8 +55,14 @@ struct ConversionTarget {
|
||||
};
|
||||
|
||||
// Convert a value to another type and expression category.
|
||||
// TODO: The `vtable_id` parameter is too much of a special case here, and
|
||||
// should be removed - once partial classes are implemented, the vtable pointer
|
||||
// initialization will be done not in this conversion, but during initialization
|
||||
// of the object of non-partial class time from the object of partial class
|
||||
// type.
|
||||
auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
|
||||
ConversionTarget target) -> SemIR::InstId;
|
||||
ConversionTarget target,
|
||||
SemIR::InstId vtable_id = SemIR::InstId::None) -> SemIR::InstId;
|
||||
|
||||
// Performs initialization of `target_id` from `value_id`. Returns the
|
||||
// possibly-converted initializing expression, which should be assigned to the
|
||||
|
||||
+112
-6
@@ -33,6 +33,10 @@ class Derived {
|
||||
impl fn H[self: Self]();
|
||||
}
|
||||
|
||||
fn Use() {
|
||||
var d: Derived = {.base = {}};
|
||||
}
|
||||
|
||||
// --- todo_fail_later_base.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
@@ -85,6 +89,12 @@ class C {
|
||||
impl fn F[self: Self]();
|
||||
}
|
||||
|
||||
fn Use() {
|
||||
var b1: B1 = {};
|
||||
var b2: B2 = {.base = {}};
|
||||
var c: C = {.base = {.base = {}}};
|
||||
}
|
||||
|
||||
// --- fail_modifiers.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
@@ -384,8 +394,12 @@ class T2 {
|
||||
// CHECK:STDOUT: %H.type.dba: type = fn_type @H.1 [concrete]
|
||||
// CHECK:STDOUT: %H.bce: %H.type.dba = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %.dce: <vtable> = vtable (%H.bce) [concrete]
|
||||
// CHECK:STDOUT: %struct_type.base: type = struct_type {.base: %Base} [concrete]
|
||||
// CHECK:STDOUT: %complete_type.0e2: <witness> = complete_type_witness %struct_type.base [concrete]
|
||||
// CHECK:STDOUT: %struct_type.base.96c: type = struct_type {.base: %Base} [concrete]
|
||||
// CHECK:STDOUT: %complete_type.0e2: <witness> = complete_type_witness %struct_type.base.96c [concrete]
|
||||
// CHECK:STDOUT: %Use.type: type = fn_type @Use [concrete]
|
||||
// CHECK:STDOUT: %Use: %Use.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
|
||||
// CHECK:STDOUT: %struct_type.base.f5e: type = struct_type {.base: %empty_struct_type} [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: imports {
|
||||
@@ -408,10 +422,12 @@ class T2 {
|
||||
// CHECK:STDOUT: .Core = imports.%Core
|
||||
// CHECK:STDOUT: .Modifiers = imports.%Modifiers
|
||||
// CHECK:STDOUT: .Derived = %Derived.decl
|
||||
// CHECK:STDOUT: .Use = %Use.decl
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %Core.import = import Core
|
||||
// CHECK:STDOUT: %Modifiers.import = import Modifiers
|
||||
// CHECK:STDOUT: %Derived.decl: type = class_decl @Derived [concrete = constants.%Derived] {} {}
|
||||
// CHECK:STDOUT: %Use.decl: %Use.type = fn_decl @Use [concrete = constants.%Use] {} {}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @Derived {
|
||||
@@ -427,7 +443,7 @@ class T2 {
|
||||
// CHECK:STDOUT: %self: %Derived = bind_name self, %self.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %.loc9: <vtable> = vtable (%H.decl) [concrete = constants.%.dce]
|
||||
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness %struct_type.base [concrete = constants.%complete_type.0e2]
|
||||
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness %struct_type.base.96c [concrete = constants.%complete_type.0e2]
|
||||
// CHECK:STDOUT: complete_type_witness = %complete_type
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
@@ -450,6 +466,29 @@ class T2 {
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @H.2[%self.param_patt: %Base]() [from "modifiers.carbon"];
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Use() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: name_binding_decl {
|
||||
// CHECK:STDOUT: %d.patt: %Derived = binding_pattern d
|
||||
// CHECK:STDOUT: %.loc12_3.1: %Derived = var_pattern %d.patt
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %d.var: ref %Derived = var d
|
||||
// CHECK:STDOUT: %.loc12_30.1: %empty_struct_type = struct_literal ()
|
||||
// CHECK:STDOUT: %.loc12_31.1: %struct_type.base.f5e = struct_literal (%.loc12_30.1)
|
||||
// CHECK:STDOUT: %.loc12_31.2: ref %Base = class_element_access %d.var, element0
|
||||
// CHECK:STDOUT: %.loc12_30.2: ref %ptr.454 = class_element_access %.loc12_31.2, element0
|
||||
// CHECK:STDOUT: %.loc12_30.3: ref %ptr.454 = vtable_ptr @Derived.%.loc9
|
||||
// CHECK:STDOUT: %.loc12_30.4: init %ptr.454 = initialize_from %.loc12_30.3 to %.loc12_30.2
|
||||
// CHECK:STDOUT: %.loc12_30.5: init %Base = class_init (%.loc12_30.4), %.loc12_31.2
|
||||
// CHECK:STDOUT: %.loc12_31.3: init %Base = converted %.loc12_30.1, %.loc12_30.5
|
||||
// CHECK:STDOUT: %.loc12_31.4: init %Derived = class_init (%.loc12_31.3), %d.var
|
||||
// CHECK:STDOUT: %.loc12_3.2: init %Derived = converted %.loc12_31.1, %.loc12_31.4
|
||||
// CHECK:STDOUT: assign %d.var, %.loc12_3.2
|
||||
// CHECK:STDOUT: %Derived.ref: type = name_ref Derived, file.%Derived.decl [concrete = constants.%Derived]
|
||||
// CHECK:STDOUT: %d: ref %Derived = bind_name d, %d.var
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- todo_fail_later_base.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
@@ -552,6 +591,7 @@ class T2 {
|
||||
// CHECK:STDOUT: import Modifiers//default
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %Modifiers.Base: type = import_ref Modifiers//default, Base, loaded [concrete = constants.%Base]
|
||||
// CHECK:STDOUT: %Modifiers.import_ref.ace = import_ref Modifiers//default, loc6_1, unloaded
|
||||
// CHECK:STDOUT: %Modifiers.import_ref.05e: <witness> = import_ref Modifiers//default, loc6_1, loaded [concrete = constants.%complete_type]
|
||||
// CHECK:STDOUT: %Modifiers.import_ref.1f3 = import_ref Modifiers//default, inst17 [no loc], unloaded
|
||||
// CHECK:STDOUT: %Modifiers.import_ref.2cc = import_ref Modifiers//default, loc5_29, unloaded
|
||||
@@ -585,7 +625,7 @@ class T2 {
|
||||
// CHECK:STDOUT: %v.var: ref %Base = var v
|
||||
// CHECK:STDOUT: %.loc7_28.1: %empty_struct_type = struct_literal ()
|
||||
// CHECK:STDOUT: %.loc7_28.2: ref %ptr.454 = class_element_access %v.var, element0
|
||||
// CHECK:STDOUT: %.loc7_28.3: ref %ptr.454 = vtable_ptr
|
||||
// CHECK:STDOUT: %.loc7_28.3: ref %ptr.454 = vtable_ptr imports.%Modifiers.import_ref.ace
|
||||
// CHECK:STDOUT: %.loc7_28.4: init %ptr.454 = initialize_from %.loc7_28.3 to %.loc7_28.2
|
||||
// CHECK:STDOUT: %.loc7_28.5: init %Base = class_init (%.loc7_28.4), %v.var
|
||||
// CHECK:STDOUT: %.loc7_3.2: init %Base = converted %.loc7_28.1, %.loc7_28.5
|
||||
@@ -704,6 +744,11 @@ class T2 {
|
||||
// CHECK:STDOUT: %.5f6: <vtable> = vtable (%F.437) [concrete]
|
||||
// CHECK:STDOUT: %struct_type.base.421: type = struct_type {.base: %B2} [concrete]
|
||||
// CHECK:STDOUT: %complete_type.066: <witness> = complete_type_witness %struct_type.base.421 [concrete]
|
||||
// CHECK:STDOUT: %Use.type: type = fn_type @Use [concrete]
|
||||
// CHECK:STDOUT: %Use: %Use.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
|
||||
// CHECK:STDOUT: %struct_type.base.f5e: type = struct_type {.base: %empty_struct_type} [concrete]
|
||||
// CHECK:STDOUT: %struct_type.base.a0c: type = struct_type {.base: %struct_type.base.f5e} [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: imports {
|
||||
@@ -719,11 +764,13 @@ class T2 {
|
||||
// CHECK:STDOUT: .B1 = %B1.decl
|
||||
// CHECK:STDOUT: .B2 = %B2.decl
|
||||
// CHECK:STDOUT: .C = %C.decl
|
||||
// CHECK:STDOUT: .Use = %Use.decl
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %Core.import = import Core
|
||||
// CHECK:STDOUT: %B1.decl: type = class_decl @B1 [concrete = constants.%B1] {} {}
|
||||
// CHECK:STDOUT: %B2.decl: type = class_decl @B2 [concrete = constants.%B2] {} {}
|
||||
// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
|
||||
// CHECK:STDOUT: %Use.decl: %Use.type = fn_decl @Use [concrete = constants.%Use] {} {}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @B1 {
|
||||
@@ -796,6 +843,65 @@ class T2 {
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: impl fn @F.3[%self.param_patt: %C]();
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Use() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: name_binding_decl {
|
||||
// CHECK:STDOUT: %b1.patt: %B1 = binding_pattern b1
|
||||
// CHECK:STDOUT: %.loc19_3.1: %B1 = var_pattern %b1.patt
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %b1.var: ref %B1 = var b1
|
||||
// CHECK:STDOUT: %.loc19_17.1: %empty_struct_type = struct_literal ()
|
||||
// CHECK:STDOUT: %.loc19_17.2: ref %ptr.454 = class_element_access %b1.var, element0
|
||||
// CHECK:STDOUT: %.loc19_17.3: ref %ptr.454 = vtable_ptr @B1.%.loc6
|
||||
// CHECK:STDOUT: %.loc19_17.4: init %ptr.454 = initialize_from %.loc19_17.3 to %.loc19_17.2
|
||||
// CHECK:STDOUT: %.loc19_17.5: init %B1 = class_init (%.loc19_17.4), %b1.var
|
||||
// CHECK:STDOUT: %.loc19_3.2: init %B1 = converted %.loc19_17.1, %.loc19_17.5
|
||||
// CHECK:STDOUT: assign %b1.var, %.loc19_3.2
|
||||
// CHECK:STDOUT: %B1.ref: type = name_ref B1, file.%B1.decl [concrete = constants.%B1]
|
||||
// CHECK:STDOUT: %b1: ref %B1 = bind_name b1, %b1.var
|
||||
// CHECK:STDOUT: name_binding_decl {
|
||||
// CHECK:STDOUT: %b2.patt: %B2 = binding_pattern b2
|
||||
// CHECK:STDOUT: %.loc20_3.1: %B2 = var_pattern %b2.patt
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %b2.var: ref %B2 = var b2
|
||||
// CHECK:STDOUT: %.loc20_26.1: %empty_struct_type = struct_literal ()
|
||||
// CHECK:STDOUT: %.loc20_27.1: %struct_type.base.f5e = struct_literal (%.loc20_26.1)
|
||||
// CHECK:STDOUT: %.loc20_27.2: ref %B1 = class_element_access %b2.var, element0
|
||||
// CHECK:STDOUT: %.loc20_26.2: ref %ptr.454 = class_element_access %.loc20_27.2, element0
|
||||
// CHECK:STDOUT: %.loc20_26.3: ref %ptr.454 = vtable_ptr @B2.%.loc11
|
||||
// CHECK:STDOUT: %.loc20_26.4: init %ptr.454 = initialize_from %.loc20_26.3 to %.loc20_26.2
|
||||
// CHECK:STDOUT: %.loc20_26.5: init %B1 = class_init (%.loc20_26.4), %.loc20_27.2
|
||||
// CHECK:STDOUT: %.loc20_27.3: init %B1 = converted %.loc20_26.1, %.loc20_26.5
|
||||
// CHECK:STDOUT: %.loc20_27.4: init %B2 = class_init (%.loc20_27.3), %b2.var
|
||||
// CHECK:STDOUT: %.loc20_3.2: init %B2 = converted %.loc20_27.1, %.loc20_27.4
|
||||
// CHECK:STDOUT: assign %b2.var, %.loc20_3.2
|
||||
// CHECK:STDOUT: %B2.ref: type = name_ref B2, file.%B2.decl [concrete = constants.%B2]
|
||||
// CHECK:STDOUT: %b2: ref %B2 = bind_name b2, %b2.var
|
||||
// CHECK:STDOUT: name_binding_decl {
|
||||
// CHECK:STDOUT: %c.patt: %C = binding_pattern c
|
||||
// CHECK:STDOUT: %.loc21_3.1: %C = var_pattern %c.patt
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %c.var: ref %C = var c
|
||||
// CHECK:STDOUT: %.loc21_33.1: %empty_struct_type = struct_literal ()
|
||||
// CHECK:STDOUT: %.loc21_34.1: %struct_type.base.f5e = struct_literal (%.loc21_33.1)
|
||||
// CHECK:STDOUT: %.loc21_35.1: %struct_type.base.a0c = struct_literal (%.loc21_34.1)
|
||||
// CHECK:STDOUT: %.loc21_35.2: ref %B2 = class_element_access %c.var, element0
|
||||
// CHECK:STDOUT: %.loc21_34.2: ref %B1 = class_element_access %.loc21_35.2, element0
|
||||
// CHECK:STDOUT: %.loc21_33.2: ref %ptr.454 = class_element_access %.loc21_34.2, element0
|
||||
// CHECK:STDOUT: %.loc21_33.3: ref %ptr.454 = vtable_ptr @C.%.loc16
|
||||
// CHECK:STDOUT: %.loc21_33.4: init %ptr.454 = initialize_from %.loc21_33.3 to %.loc21_33.2
|
||||
// CHECK:STDOUT: %.loc21_33.5: init %B1 = class_init (%.loc21_33.4), %.loc21_34.2
|
||||
// CHECK:STDOUT: %.loc21_34.3: init %B1 = converted %.loc21_33.1, %.loc21_33.5
|
||||
// CHECK:STDOUT: %.loc21_34.4: init %B2 = class_init (%.loc21_34.3), %.loc21_35.2
|
||||
// CHECK:STDOUT: %.loc21_35.3: init %B2 = converted %.loc21_34.1, %.loc21_34.4
|
||||
// CHECK:STDOUT: %.loc21_35.4: init %C = class_init (%.loc21_35.3), %c.var
|
||||
// CHECK:STDOUT: %.loc21_3.2: init %C = converted %.loc21_35.1, %.loc21_35.4
|
||||
// CHECK:STDOUT: assign %c.var, %.loc21_3.2
|
||||
// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C]
|
||||
// CHECK:STDOUT: %c: ref %C = bind_name c, %c.var
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- fail_modifiers.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
@@ -964,7 +1070,7 @@ class T2 {
|
||||
// CHECK:STDOUT: %i.ref.loc14_34: ref %i32 = name_ref i, %i
|
||||
// CHECK:STDOUT: %.loc14_35.1: %struct_type.m2.m1.68c = struct_literal (%i.ref.loc14_25, %i.ref.loc14_34)
|
||||
// CHECK:STDOUT: %.loc14_35.2: ref %ptr.454 = class_element_access %b1.var, element0
|
||||
// CHECK:STDOUT: %.loc14_35.3: ref %ptr.454 = vtable_ptr
|
||||
// CHECK:STDOUT: %.loc14_35.3: ref %ptr.454 = vtable_ptr @Base.%.loc9
|
||||
// CHECK:STDOUT: %.loc14_35.4: init %ptr.454 = initialize_from %.loc14_35.3 to %.loc14_35.2
|
||||
// CHECK:STDOUT: %.loc14_34: %i32 = bind_value %i.ref.loc14_34
|
||||
// CHECK:STDOUT: %.loc14_35.5: ref %i32 = class_element_access %b1.var, element2
|
||||
@@ -986,7 +1092,7 @@ class T2 {
|
||||
// CHECK:STDOUT: %int_5: Core.IntLiteral = int_value 5 [concrete = constants.%int_5.64b]
|
||||
// CHECK:STDOUT: %.loc15_35.1: %struct_type.m2.m1.5f2 = struct_literal (%int_3.loc15, %int_5)
|
||||
// CHECK:STDOUT: %.loc15_35.2: ref %ptr.454 = class_element_access %b2.var, element0
|
||||
// CHECK:STDOUT: %.loc15_35.3: ref %ptr.454 = vtable_ptr
|
||||
// CHECK:STDOUT: %.loc15_35.3: ref %ptr.454 = vtable_ptr @Base.%.loc9
|
||||
// CHECK:STDOUT: %.loc15_35.4: init %ptr.454 = initialize_from %.loc15_35.3 to %.loc15_35.2
|
||||
// CHECK:STDOUT: %impl.elem0.loc15_35.1: %.be7 = impl_witness_access constants.%impl_witness.d39, element0 [concrete = constants.%Convert.956]
|
||||
// CHECK:STDOUT: %bound_method.loc15_35.1: <bound method> = bound_method %int_5, %impl.elem0.loc15_35.1 [concrete = constants.%Convert.bound.4e6]
|
||||
|
||||
@@ -69,7 +69,9 @@ auto FileContext::Run() -> std::unique_ptr<llvm::Module> {
|
||||
}
|
||||
|
||||
for (const auto& class_info : sem_ir_->classes().array_ref()) {
|
||||
BuildVtable(class_info);
|
||||
if (auto* llvm_vtable = BuildVtable(class_info)) {
|
||||
global_variables_.Insert(class_info.vtable_id, llvm_vtable);
|
||||
}
|
||||
}
|
||||
|
||||
// Specific functions are lowered when we emit a reference to them.
|
||||
@@ -707,24 +709,35 @@ auto FileContext::GetLocForDI(SemIR::InstId inst_id) -> LocForDI {
|
||||
}
|
||||
}
|
||||
|
||||
auto FileContext::BuildVtable(const SemIR::Class& class_info) -> void {
|
||||
auto FileContext::BuildVtable(const SemIR::Class& class_info)
|
||||
-> llvm::GlobalVariable* {
|
||||
// Bail out if this class is not dynamic (this will account for classes that
|
||||
// are declared-and-not-defined (including extern declarations) as well).
|
||||
if (!class_info.is_dynamic) {
|
||||
return;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Mangler m(*this);
|
||||
std::string mangled_name = m.MangleVTable(class_info);
|
||||
|
||||
auto first_owning_decl_loc =
|
||||
sem_ir().insts().GetLocId(class_info.first_owning_decl_id);
|
||||
if (first_owning_decl_loc.is_import_ir_inst_id()) {
|
||||
return;
|
||||
// Emit a declaration of an imported vtable using a(n opaque) pointer type.
|
||||
// This doesn't have to match the definition that appears elsewhere, it'll
|
||||
// still get merged correctly.
|
||||
auto* gv = new llvm::GlobalVariable(
|
||||
llvm_module(),
|
||||
llvm::PointerType::get(llvm_context(), /*AddressSpace=*/0),
|
||||
/*isConstant=*/true, llvm::GlobalValue::ExternalLinkage, nullptr,
|
||||
mangled_name);
|
||||
gv->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
|
||||
return gv;
|
||||
}
|
||||
|
||||
auto canonical_vtable_id =
|
||||
sem_ir().constant_values().GetConstantInstId(class_info.vtable_id);
|
||||
if (canonical_vtable_id == SemIR::ErrorInst::SingletonInstId) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto vtable_inst_block =
|
||||
sem_ir().inst_blocks().Get(sem_ir()
|
||||
.insts()
|
||||
@@ -734,9 +747,6 @@ auto FileContext::BuildVtable(const SemIR::Class& class_info) -> void {
|
||||
auto* entry_type = llvm::IntegerType::getInt32Ty(llvm_context());
|
||||
auto* table_type = llvm::ArrayType::get(entry_type, vtable_inst_block.size());
|
||||
|
||||
Mangler m(*this);
|
||||
std::string mangled_name = m.MangleVTable(class_info);
|
||||
|
||||
auto* llvm_vtable = new llvm::GlobalVariable(
|
||||
llvm_module(), table_type, /*isConstant=*/true,
|
||||
llvm::GlobalValue::ExternalLinkage, nullptr, mangled_name);
|
||||
@@ -763,6 +773,8 @@ auto FileContext::BuildVtable(const SemIR::Class& class_info) -> void {
|
||||
|
||||
llvm_vtable->setInitializer(llvm::ConstantArray::get(table_type, vfuncs));
|
||||
llvm_vtable->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
|
||||
|
||||
return llvm_vtable;
|
||||
}
|
||||
|
||||
} // namespace Carbon::Lower
|
||||
|
||||
@@ -147,7 +147,7 @@ class FileContext {
|
||||
auto BuildGlobalVariableDecl(SemIR::VarStorage var_storage)
|
||||
-> llvm::GlobalVariable*;
|
||||
|
||||
auto BuildVtable(const SemIR::Class& class_info) -> void;
|
||||
auto BuildVtable(const SemIR::Class& class_info) -> llvm::GlobalVariable*;
|
||||
|
||||
// State for building the LLVM IR.
|
||||
llvm::LLVMContext* llvm_context_;
|
||||
|
||||
@@ -324,12 +324,8 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
||||
}
|
||||
|
||||
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
||||
SemIR::VtablePtr /*inst*/) -> void {
|
||||
// TODO: Initialize the virtual pointer to actually point to a virtual
|
||||
// function table.
|
||||
context.SetLocal(inst_id,
|
||||
llvm::ConstantPointerNull::get(
|
||||
llvm::PointerType::get(context.llvm_context(), 0)));
|
||||
SemIR::VtablePtr inst) -> void {
|
||||
context.SetLocal(inst_id, context.GetValue(inst.vtable_id));
|
||||
}
|
||||
|
||||
} // namespace Carbon::Lower
|
||||
|
||||
+44
-16
@@ -32,9 +32,11 @@ package Create;
|
||||
import Classes;
|
||||
|
||||
fn Create() {
|
||||
var b: Classes.Base;
|
||||
var i: Classes.Intermediate;
|
||||
var d: Classes.Derived;
|
||||
var b: Classes.Base = {};
|
||||
var i: Classes.Intermediate = {.base = {}};
|
||||
var d: Classes.Derived = {.base = {.base = {}}};
|
||||
// TODO: Support vptr initialization without explicit source initializers.
|
||||
var d2: Classes.Derived;
|
||||
}
|
||||
|
||||
fn Use(v: Classes.Intermediate*) {
|
||||
@@ -73,7 +75,6 @@ fn Use() {
|
||||
var v : Derived = {.base = {}};
|
||||
}
|
||||
|
||||
|
||||
// CHECK:STDOUT: ; ModuleID = 'classes.carbon'
|
||||
// CHECK:STDOUT: source_filename = "classes.carbon"
|
||||
// CHECK:STDOUT:
|
||||
@@ -106,21 +107,39 @@ fn Use() {
|
||||
// CHECK:STDOUT: ; ModuleID = 'create.carbon'
|
||||
// CHECK:STDOUT: source_filename = "create.carbon"
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: @"_CIntermediate.Classes.$vtable" = external unnamed_addr constant ptr
|
||||
// CHECK:STDOUT: @"_CDerived.Classes.$vtable" = external unnamed_addr constant ptr
|
||||
// CHECK:STDOUT: @Base.val.loc7_3.2 = internal constant {} zeroinitializer
|
||||
// CHECK:STDOUT: @Base.val.loc8_44.6 = internal constant {} zeroinitializer
|
||||
// CHECK:STDOUT: @Base.val.loc9_48.6 = internal constant {} zeroinitializer
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: define void @_CCreate.Create() !dbg !4 {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: %b.var = alloca {}, align 8, !dbg !7
|
||||
// CHECK:STDOUT: %i.var = alloca { ptr, {} }, align 8, !dbg !7
|
||||
// CHECK:STDOUT: %d.var = alloca { { ptr, {} } }, align 8, !dbg !7
|
||||
// CHECK:STDOUT: %d2.var = alloca { { ptr, {} } }, align 8, !dbg !7
|
||||
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(i64 0, ptr %b.var), !dbg !7
|
||||
// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %b.var, ptr align 1 @Base.val.loc7_3.2, i64 0, i1 false), !dbg !7
|
||||
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(i64 8, ptr %i.var), !dbg !7
|
||||
// CHECK:STDOUT: %.loc8_44.2.vptr = getelementptr inbounds nuw { ptr, {} }, ptr %i.var, i32 0, i32 0, !dbg !8
|
||||
// CHECK:STDOUT: store ptr @"_CIntermediate.Classes.$vtable", ptr %.loc8_44.2.vptr, align 8, !dbg !8
|
||||
// CHECK:STDOUT: %.loc8_44.5.base = getelementptr inbounds nuw { ptr, {} }, ptr %i.var, i32 0, i32 1, !dbg !8
|
||||
// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %.loc8_44.5.base, ptr align 1 @Base.val.loc8_44.6, i64 0, i1 false), !dbg !8
|
||||
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(i64 8, ptr %d.var), !dbg !7
|
||||
// CHECK:STDOUT: ret void, !dbg !8
|
||||
// CHECK:STDOUT: %.loc9_49.2.base = getelementptr inbounds nuw { { ptr, {} } }, ptr %d.var, i32 0, i32 0, !dbg !9
|
||||
// CHECK:STDOUT: %.loc9_48.2.vptr = getelementptr inbounds nuw { ptr, {} }, ptr %.loc9_49.2.base, i32 0, i32 0, !dbg !10
|
||||
// CHECK:STDOUT: store ptr @"_CDerived.Classes.$vtable", ptr %.loc9_48.2.vptr, align 8, !dbg !10
|
||||
// CHECK:STDOUT: %.loc9_48.5.base = getelementptr inbounds nuw { ptr, {} }, ptr %.loc9_49.2.base, i32 0, i32 1, !dbg !10
|
||||
// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %.loc9_48.5.base, ptr align 1 @Base.val.loc9_48.6, i64 0, i1 false), !dbg !10
|
||||
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(i64 8, ptr %d2.var), !dbg !7
|
||||
// CHECK:STDOUT: ret void, !dbg !11
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: define void @_CUse.Create(ptr %v) !dbg !9 {
|
||||
// CHECK:STDOUT: define void @_CUse.Create(ptr %v) !dbg !12 {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: call void @_CFn.Intermediate.Classes(ptr %v), !dbg !10
|
||||
// CHECK:STDOUT: ret void, !dbg !11
|
||||
// CHECK:STDOUT: call void @_CFn.Intermediate.Classes(ptr %v), !dbg !13
|
||||
// CHECK:STDOUT: ret void, !dbg !14
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: declare void @_CFn.Intermediate.Classes(ptr)
|
||||
@@ -128,10 +147,16 @@ fn Use() {
|
||||
// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||
// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr captures(none)) #0
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
|
||||
// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg) #1
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: ; uselistorder directives
|
||||
// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 2, 1, 0 }
|
||||
// CHECK:STDOUT: uselistorder i32 1, { 1, 3, 0, 2, 4, 5 }
|
||||
// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 3, 2, 1, 0 }
|
||||
// CHECK:STDOUT: uselistorder ptr @llvm.memcpy.p0.p0.i64, { 2, 1, 0 }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
|
||||
// CHECK:STDOUT: attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
|
||||
// CHECK:STDOUT: !llvm.dbg.cu = !{!2}
|
||||
@@ -144,10 +169,13 @@ fn Use() {
|
||||
// CHECK:STDOUT: !5 = !DISubroutineType(types: !6)
|
||||
// CHECK:STDOUT: !6 = !{}
|
||||
// CHECK:STDOUT: !7 = !DILocation(line: 7, column: 3, scope: !4)
|
||||
// CHECK:STDOUT: !8 = !DILocation(line: 6, column: 1, scope: !4)
|
||||
// CHECK:STDOUT: !9 = distinct !DISubprogram(name: "Use", linkageName: "_CUse.Create", scope: null, file: !3, line: 12, type: !5, spFlags: DISPFlagDefinition, unit: !2)
|
||||
// CHECK:STDOUT: !10 = !DILocation(line: 13, column: 3, scope: !9)
|
||||
// CHECK:STDOUT: !11 = !DILocation(line: 12, column: 1, scope: !9)
|
||||
// CHECK:STDOUT: !8 = !DILocation(line: 8, column: 33, scope: !4)
|
||||
// CHECK:STDOUT: !9 = !DILocation(line: 9, column: 28, scope: !4)
|
||||
// CHECK:STDOUT: !10 = !DILocation(line: 9, column: 37, scope: !4)
|
||||
// CHECK:STDOUT: !11 = !DILocation(line: 6, column: 1, scope: !4)
|
||||
// CHECK:STDOUT: !12 = distinct !DISubprogram(name: "Use", linkageName: "_CUse.Create", scope: null, file: !3, line: 14, type: !5, spFlags: DISPFlagDefinition, unit: !2)
|
||||
// CHECK:STDOUT: !13 = !DILocation(line: 15, column: 3, scope: !12)
|
||||
// CHECK:STDOUT: !14 = !DILocation(line: 14, column: 1, scope: !12)
|
||||
// CHECK:STDOUT: ; ModuleID = 'member_init.carbon'
|
||||
// CHECK:STDOUT: source_filename = "member_init.carbon"
|
||||
// CHECK:STDOUT:
|
||||
@@ -167,7 +195,7 @@ fn Use() {
|
||||
// CHECK:STDOUT: store i32 3, ptr %i.var, align 4, !dbg !9
|
||||
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(i64 16, ptr %v.var), !dbg !9
|
||||
// CHECK:STDOUT: %.loc11_24.2.vptr = getelementptr inbounds nuw { ptr, i32 }, ptr %v.var, i32 0, i32 0, !dbg !10
|
||||
// CHECK:STDOUT: store ptr null, ptr %.loc11_24.2.vptr, align 8, !dbg !10
|
||||
// CHECK:STDOUT: store ptr @"_CBase.MemberInit.$vtable", ptr %.loc11_24.2.vptr, align 8, !dbg !10
|
||||
// CHECK:STDOUT: %.loc11_23 = load i32, ptr %i.var, align 4, !dbg !11
|
||||
// CHECK:STDOUT: %.loc11_24.5.m = getelementptr inbounds nuw { ptr, i32 }, ptr %v.var, i32 0, i32 1, !dbg !10
|
||||
// CHECK:STDOUT: store i32 %.loc11_23, ptr %.loc11_24.5.m, align 4, !dbg !10
|
||||
@@ -175,7 +203,7 @@ fn Use() {
|
||||
// CHECK:STDOUT: store i32 5, ptr %.loc12_4.m, align 4, !dbg !12
|
||||
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(i64 16, ptr %u.var), !dbg !9
|
||||
// CHECK:STDOUT: %.loc13_24.2.vptr = getelementptr inbounds nuw { ptr, i32 }, ptr %u.var, i32 0, i32 0, !dbg !13
|
||||
// CHECK:STDOUT: store ptr null, ptr %.loc13_24.2.vptr, align 8, !dbg !13
|
||||
// CHECK:STDOUT: store ptr @"_CBase.MemberInit.$vtable", ptr %.loc13_24.2.vptr, align 8, !dbg !13
|
||||
// CHECK:STDOUT: %.loc13_24.6.m = getelementptr inbounds nuw { ptr, i32 }, ptr %u.var, i32 0, i32 1, !dbg !13
|
||||
// CHECK:STDOUT: store i32 3, ptr %.loc13_24.6.m, align 4, !dbg !13
|
||||
// CHECK:STDOUT: ret void, !dbg !14
|
||||
@@ -222,7 +250,7 @@ fn Use() {
|
||||
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(i64 8, ptr %v.var), !dbg !7
|
||||
// CHECK:STDOUT: %.loc13_32.2.base = getelementptr inbounds nuw { { ptr } }, ptr %v.var, i32 0, i32 0, !dbg !8
|
||||
// CHECK:STDOUT: %.loc13_31.2.vptr = getelementptr inbounds nuw { ptr }, ptr %.loc13_32.2.base, i32 0, i32 0, !dbg !9
|
||||
// CHECK:STDOUT: store ptr null, ptr %.loc13_31.2.vptr, align 8, !dbg !9
|
||||
// CHECK:STDOUT: store ptr @"_CDerived.Main.$vtable", ptr %.loc13_31.2.vptr, align 8, !dbg !9
|
||||
// CHECK:STDOUT: ret void, !dbg !10
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
|
||||
@@ -1792,9 +1792,10 @@ struct VtableType {
|
||||
|
||||
// Initializer for virtual function table pointers in object initialization.
|
||||
struct VtablePtr {
|
||||
static constexpr auto Kind =
|
||||
InstKind::VtablePtr.Define<Parse::NodeId>({.ir_name = "vtable_ptr"});
|
||||
static constexpr auto Kind = InstKind::VtablePtr.Define<Parse::NodeId>(
|
||||
{.ir_name = "vtable_ptr", .constant_kind = InstConstantKind::Never});
|
||||
TypeId type_id;
|
||||
InstId vtable_id;
|
||||
};
|
||||
|
||||
// Definition of ABI-neutral vtable information for a dynamic class.
|
||||
|
||||
Reference in New Issue
Block a user