فهرست منبع

Add an instruction for vptr initialization (#4633)

This fixes a crash in lowering, at least (though only initializes the
vptr to
null for now) - certainly open to naming feedback on the instruction, or
the
exact semantics (we could have a global vptr instruction that's
referenced from
the existing instructions for reading globals, for instance).

I guess we'll want one type parameter for the vptr_init instruction,
which is
the type that this is a vptr for? (can do that here or in a follow-on
patch)
David Blaikie 1 سال پیش
والد
کامیت
a2c939a2a2

+ 12 - 2
toolchain/check/convert.cpp

@@ -461,8 +461,18 @@ static auto ConvertStructToStructOrClass(Context& context,
                             dest_elem_fields.size()});
   for (auto [i, dest_field] : llvm::enumerate(dest_elem_fields)) {
     if (dest_field.name_id == SemIR::NameId::Vptr) {
-      // TODO: Initialize the vptr to point to a vtable.
-      new_block.Set(i, SemIR::ErrorInst::SingletonInstId);
+      // CARBON_CHECK(ToClass, "Only classes should have vptrs.");
+      auto dest_id = context.AddInst<SemIR::ClassElementAccess>(
+          value_loc_id, {.type_id = dest_field.type_id,
+                         .base_id = target.init_id,
+                         .index = SemIR::ElementIndex(i)});
+      auto vtable_ptr_id = context.AddInst<SemIR::VtablePtr>(
+          value_loc_id, {.type_id = dest_field.type_id});
+      auto init_id = context.AddInst<SemIR::InitializeFrom>(
+          value_loc_id, {.type_id = dest_field.type_id,
+                         .src_id = vtable_ptr_id,
+                         .dest_id = dest_id});
+      new_block.Set(i, init_id);
       continue;
     }
 

+ 1 - 0
toolchain/check/eval.cpp

@@ -1579,6 +1579,7 @@ static auto TryEvalInstInContext(EvalContext& eval_context,
     case SemIR::Temporary::Kind:
     case SemIR::TemporaryStorage::Kind:
     case SemIR::ValueAsRef::Kind:
+    case SemIR::VtablePtr::Kind:
       break;
 
     case CARBON_KIND(SemIR::SymbolicBindingPattern bind): {

+ 25 - 16
toolchain/check/testdata/class/virtual_modifiers.carbon

@@ -297,8 +297,11 @@ class Derived {
 // CHECK:STDOUT:   %v.var: ref %Base = var v
 // CHECK:STDOUT:   %v: ref %Base = bind_name v, %v.var
 // CHECK:STDOUT:   %.loc7_28.1: %empty_struct_type = struct_literal ()
-// CHECK:STDOUT:   %.loc7_28.2: init %Base = class_init (<error>), %v.var [template = <error>]
-// CHECK:STDOUT:   %.loc7_29: init %Base = converted %.loc7_28.1, %.loc7_28.2 [template = <error>]
+// CHECK:STDOUT:   %.loc7_28.2: ref %ptr.1 = class_element_access %v.var, element0
+// CHECK:STDOUT:   %.loc7_28.3: ref %ptr.1 = vtable_ptr
+// CHECK:STDOUT:   %.loc7_28.4: init %ptr.1 = 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_29: init %Base = converted %.loc7_28.1, %.loc7_28.5
 // CHECK:STDOUT:   assign %v.var, %.loc7_29
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
@@ -593,14 +596,17 @@ class Derived {
 // CHECK:STDOUT:   %i.ref.loc14_25: ref %i32 = name_ref i, %i
 // CHECK:STDOUT:   %i.ref.loc14_34: ref %i32 = name_ref i, %i
 // CHECK:STDOUT:   %.loc14_35.1: %struct_type.m2.m1.1 = struct_literal (%i.ref.loc14_25, %i.ref.loc14_34)
+// CHECK:STDOUT:   %.loc14_35.2: ref %ptr.1 = class_element_access %b1.var, element0
+// CHECK:STDOUT:   %.loc14_35.3: ref %ptr.1 = vtable_ptr
+// CHECK:STDOUT:   %.loc14_35.4: init %ptr.1 = initialize_from %.loc14_35.3 to %.loc14_35.2
 // CHECK:STDOUT:   %.loc14_34: %i32 = bind_value %i.ref.loc14_34
-// CHECK:STDOUT:   %.loc14_35.2: ref %i32 = class_element_access %b1.var, element2
-// CHECK:STDOUT:   %.loc14_35.3: init %i32 = initialize_from %.loc14_34 to %.loc14_35.2
+// CHECK:STDOUT:   %.loc14_35.5: ref %i32 = class_element_access %b1.var, element2
+// CHECK:STDOUT:   %.loc14_35.6: init %i32 = initialize_from %.loc14_34 to %.loc14_35.5
 // CHECK:STDOUT:   %.loc14_25: %i32 = bind_value %i.ref.loc14_25
-// CHECK:STDOUT:   %.loc14_35.4: ref %i32 = class_element_access %b1.var, element1
-// CHECK:STDOUT:   %.loc14_35.5: init %i32 = initialize_from %.loc14_25 to %.loc14_35.4
-// CHECK:STDOUT:   %.loc14_35.6: init %Base = class_init (<error>, %.loc14_35.3, %.loc14_35.5), %b1.var
-// CHECK:STDOUT:   %.loc14_36: init %Base = converted %.loc14_35.1, %.loc14_35.6
+// CHECK:STDOUT:   %.loc14_35.7: ref %i32 = class_element_access %b1.var, element1
+// CHECK:STDOUT:   %.loc14_35.8: init %i32 = initialize_from %.loc14_25 to %.loc14_35.7
+// CHECK:STDOUT:   %.loc14_35.9: init %Base = class_init (%.loc14_35.4, %.loc14_35.6, %.loc14_35.8), %b1.var
+// CHECK:STDOUT:   %.loc14_36: init %Base = converted %.loc14_35.1, %.loc14_35.9
 // CHECK:STDOUT:   assign %b1.var, %.loc14_36
 // CHECK:STDOUT:   %Base.ref.loc15: type = name_ref Base, file.%Base.decl [template = constants.%Base]
 // CHECK:STDOUT:   %b2.var: ref %Base = var b2
@@ -608,22 +614,25 @@ class Derived {
 // CHECK:STDOUT:   %int_3.loc15: Core.IntLiteral = int_value 3 [template = constants.%int_3.1]
 // CHECK:STDOUT:   %int_5: Core.IntLiteral = int_value 5 [template = constants.%int_5.1]
 // CHECK:STDOUT:   %.loc15_35.1: %struct_type.m2.m1.2 = struct_literal (%int_3.loc15, %int_5)
+// CHECK:STDOUT:   %.loc15_35.2: ref %ptr.1 = class_element_access %b2.var, element0
+// CHECK:STDOUT:   %.loc15_35.3: ref %ptr.1 = vtable_ptr
+// CHECK:STDOUT:   %.loc15_35.4: init %ptr.1 = initialize_from %.loc15_35.3 to %.loc15_35.2
 // CHECK:STDOUT:   %impl.elem0.loc15_35.1: %Convert.type.2 = interface_witness_access constants.%interface.9, element0 [template = constants.%Convert.14]
 // CHECK:STDOUT:   %Convert.bound.loc15_35.1: <bound method> = bound_method %int_5, %impl.elem0.loc15_35.1 [template = constants.%Convert.bound.2]
 // CHECK:STDOUT:   %Convert.specific_fn.loc15_35.1: <specific function> = specific_function %Convert.bound.loc15_35.1, @Convert.2(constants.%int_32) [template = constants.%Convert.specific_fn.2]
 // CHECK:STDOUT:   %int.convert_checked.loc15_35.1: init %i32 = call %Convert.specific_fn.loc15_35.1(%int_5) [template = constants.%int_5.2]
-// CHECK:STDOUT:   %.loc15_35.2: init %i32 = converted %int_5, %int.convert_checked.loc15_35.1 [template = constants.%int_5.2]
-// CHECK:STDOUT:   %.loc15_35.3: ref %i32 = class_element_access %b2.var, element2
-// CHECK:STDOUT:   %.loc15_35.4: init %i32 = initialize_from %.loc15_35.2 to %.loc15_35.3 [template = constants.%int_5.2]
+// CHECK:STDOUT:   %.loc15_35.5: init %i32 = converted %int_5, %int.convert_checked.loc15_35.1 [template = constants.%int_5.2]
+// CHECK:STDOUT:   %.loc15_35.6: ref %i32 = class_element_access %b2.var, element2
+// CHECK:STDOUT:   %.loc15_35.7: init %i32 = initialize_from %.loc15_35.5 to %.loc15_35.6 [template = constants.%int_5.2]
 // CHECK:STDOUT:   %impl.elem0.loc15_35.2: %Convert.type.2 = interface_witness_access constants.%interface.9, element0 [template = constants.%Convert.14]
 // CHECK:STDOUT:   %Convert.bound.loc15_35.2: <bound method> = bound_method %int_3.loc15, %impl.elem0.loc15_35.2 [template = constants.%Convert.bound.1]
 // CHECK:STDOUT:   %Convert.specific_fn.loc15_35.2: <specific function> = specific_function %Convert.bound.loc15_35.2, @Convert.2(constants.%int_32) [template = constants.%Convert.specific_fn.1]
 // CHECK:STDOUT:   %int.convert_checked.loc15_35.2: init %i32 = call %Convert.specific_fn.loc15_35.2(%int_3.loc15) [template = constants.%int_3.2]
-// CHECK:STDOUT:   %.loc15_35.5: init %i32 = converted %int_3.loc15, %int.convert_checked.loc15_35.2 [template = constants.%int_3.2]
-// CHECK:STDOUT:   %.loc15_35.6: ref %i32 = class_element_access %b2.var, element1
-// CHECK:STDOUT:   %.loc15_35.7: init %i32 = initialize_from %.loc15_35.5 to %.loc15_35.6 [template = constants.%int_3.2]
-// CHECK:STDOUT:   %.loc15_35.8: init %Base = class_init (<error>, %.loc15_35.4, %.loc15_35.7), %b2.var [template = <error>]
-// CHECK:STDOUT:   %.loc15_36: init %Base = converted %.loc15_35.1, %.loc15_35.8 [template = <error>]
+// CHECK:STDOUT:   %.loc15_35.8: init %i32 = converted %int_3.loc15, %int.convert_checked.loc15_35.2 [template = constants.%int_3.2]
+// CHECK:STDOUT:   %.loc15_35.9: ref %i32 = class_element_access %b2.var, element1
+// CHECK:STDOUT:   %.loc15_35.10: init %i32 = initialize_from %.loc15_35.8 to %.loc15_35.9 [template = constants.%int_3.2]
+// CHECK:STDOUT:   %.loc15_35.11: init %Base = class_init (%.loc15_35.4, %.loc15_35.7, %.loc15_35.10), %b2.var
+// CHECK:STDOUT:   %.loc15_36: init %Base = converted %.loc15_35.1, %.loc15_35.11
 // CHECK:STDOUT:   assign %b2.var, %.loc15_36
 // CHECK:STDOUT:   %b1.ref: ref %Base = name_ref b1, %b1
 // CHECK:STDOUT:   %m2.ref: %Base.elem = name_ref m2, @Base.%.loc6_9 [template = @Base.%.loc6_9]

+ 9 - 0
toolchain/lower/handle.cpp

@@ -253,4 +253,13 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
                                                   /*ArraySize=*/nullptr));
 }
 
+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)));
+}
+
 }  // namespace Carbon::Lower

+ 7 - 5
toolchain/lower/testdata/class/virtual.carbon

@@ -119,9 +119,11 @@ fn Fn() {
 // CHECK:STDOUT:   %i.var = alloca i32, align 4, !dbg !7
 // CHECK:STDOUT:   store i32 3, ptr %i.var, align 4, !dbg !8
 // CHECK:STDOUT:   %v.var = alloca { ptr, i32 }, align 8, !dbg !9
-// CHECK:STDOUT:   %.loc12_23 = load i32, ptr %i.var, align 4, !dbg !10
-// CHECK:STDOUT:   %.loc12_24.2.m = getelementptr inbounds nuw { ptr, i32 }, ptr %v.var, i32 0, i32 1, !dbg !11
-// CHECK:STDOUT:   store i32 %.loc12_23, ptr %.loc12_24.2.m, align 4, !dbg !11
+// CHECK:STDOUT:   %.loc12_24.2.vptr = getelementptr inbounds nuw { ptr, i32 }, ptr %v.var, i32 0, i32 0, !dbg !10
+// CHECK:STDOUT:   store ptr null, ptr %.loc12_24.2.vptr, align 8, !dbg !10
+// CHECK:STDOUT:   %.loc12_23 = load i32, ptr %i.var, align 4, !dbg !11
+// CHECK:STDOUT:   %.loc12_24.5.m = getelementptr inbounds nuw { ptr, i32 }, ptr %v.var, i32 0, i32 1, !dbg !10
+// CHECK:STDOUT:   store i32 %.loc12_23, ptr %.loc12_24.5.m, align 4, !dbg !10
 // CHECK:STDOUT:   %.loc15_4.m = getelementptr inbounds nuw { ptr, i32 }, ptr %v.var, i32 0, i32 1, !dbg !12
 // CHECK:STDOUT:   store i32 5, ptr %.loc15_4.m, align 4, !dbg !12
 // CHECK:STDOUT:   ret void, !dbg !13
@@ -140,7 +142,7 @@ fn Fn() {
 // CHECK:STDOUT: !7 = !DILocation(line: 10, column: 7, scope: !4)
 // CHECK:STDOUT: !8 = !DILocation(line: 10, column: 3, scope: !4)
 // CHECK:STDOUT: !9 = !DILocation(line: 12, column: 7, scope: !4)
-// CHECK:STDOUT: !10 = !DILocation(line: 12, column: 23, scope: !4)
-// CHECK:STDOUT: !11 = !DILocation(line: 12, column: 17, scope: !4)
+// CHECK:STDOUT: !10 = !DILocation(line: 12, column: 17, scope: !4)
+// CHECK:STDOUT: !11 = !DILocation(line: 12, column: 23, scope: !4)
 // CHECK:STDOUT: !12 = !DILocation(line: 15, column: 3, scope: !4)
 // CHECK:STDOUT: !13 = !DILocation(line: 9, column: 1, scope: !4)

+ 3 - 0
toolchain/sem_ir/file.cpp

@@ -311,6 +311,9 @@ auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory {
         continue;
       }
 
+      case VtablePtr::Kind:
+        return ExprCategory::EphemeralRef;
+
       case CARBON_KIND(ClassElementAccess inst): {
         inst_id = inst.base_id;
         // A value of class type is a pointer to an object representation.

+ 1 - 0
toolchain/sem_ir/inst_kind.def

@@ -118,6 +118,7 @@ CARBON_SEM_IR_INST_KIND(ValueParam)
 CARBON_SEM_IR_INST_KIND(ValueParamPattern)
 CARBON_SEM_IR_INST_KIND(VarStorage)
 CARBON_SEM_IR_INST_KIND(VtableType)
+CARBON_SEM_IR_INST_KIND(VtablePtr)
 CARBON_SEM_IR_INST_KIND(WhereExpr)
 CARBON_SEM_IR_INST_KIND(WitnessType)
 

+ 4 - 0
toolchain/sem_ir/stringify_type.cpp

@@ -424,6 +424,10 @@ auto StringifyTypeExpr(const SemIR::File& sem_ir, InstId outer_inst_id)
         step_stack.PushTypeId(inst.class_type_id);
         break;
       }
+      case VtablePtr::Kind: {
+        out << "<vtable ptr>";
+        break;
+      }
       case AdaptDecl::Kind:
       case AddrOf::Kind:
       case AddrPattern::Kind:

+ 7 - 0
toolchain/sem_ir/typed_insts.h

@@ -1463,6 +1463,13 @@ struct VtableType {
   TypeId type_id;
 };
 
+// Initializer for virtual function table pointers in object initialization.
+struct VtablePtr {
+  static constexpr auto Kind =
+      InstKind::VtablePtr.Define<Parse::NodeId>({.ir_name = "vtable_ptr"});
+  TypeId type_id;
+};
+
 // An `expr where requirements` expression.
 struct WhereExpr {
   static constexpr auto Kind = InstKind::WhereExpr.Define<Parse::WhereExprId>(