Skip to content

Commit e127cd8

Browse files
committed
[MooreToCore] Lower vtable method loads and vptr init
Extend class allocation and dispatch lowering to use the concrete vtable ABI introduced in earlier steps. This factors out vtable lookup, initializes the object header's vptr when allocating classes that have a symbolic vtable, and lowers moore.vtable.load_method into header access, vtable load, slot GEP, and function-pointer load. Upcasted dispatch remains pointer-preserving and reuses the concrete object's vptr. Refine the header GEP sequence to first step through the object header and then index within the header struct, matching the canonical layout explicitly. Update MooreToCore tests to check vptr initialization during class.new, dynamic method slot lookup, indirect dispatch, and dispatch through an upcasted receiver.
1 parent f126a93 commit e127cd8

File tree

3 files changed

+173
-23
lines changed

3 files changed

+173
-23
lines changed

lib/Conversion/MooreToCore/MooreToCore.cpp

Lines changed: 112 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,16 @@ static std::string getVTableName(SymbolRefAttr className) {
259259
return className.getRootReference().str() + "::vtable";
260260
}
261261

262+
static VTableOp findVTableOp(ModuleOp mod, SymbolRefAttr classSym) {
263+
auto vtableSym =
264+
SymbolRefAttr::get(classSym.getRootReference(),
265+
FlatSymbolRefAttr::get(mod.getContext(), "vtable"));
266+
for (auto candidate : mod.getOps<VTableOp>())
267+
if (candidate.getSymNameAttr() == vtableSym)
268+
return candidate;
269+
return {};
270+
}
271+
262272
static void
263273
collectVTableEntries(VTableOp op,
264274
llvm::SmallDenseMap<StringRef, unsigned> &slots,
@@ -291,16 +301,7 @@ static FailureOr<ClassTypeCache::VTableInfo> getOrCreateVTableInfo(
291301
if (auto info = cache.getVTableInfo(classSym))
292302
return *info;
293303

294-
auto vtableSym =
295-
SymbolRefAttr::get(classSym.getRootReference(),
296-
FlatSymbolRefAttr::get(mod.getContext(), "vtable"));
297-
VTableOp vtableOp;
298-
for (auto candidate : mod.getOps<VTableOp>()) {
299-
if (candidate.getSymNameAttr() == vtableSym) {
300-
vtableOp = candidate;
301-
break;
302-
}
303-
}
304+
VTableOp vtableOp = findVTableOp(mod, classSym);
304305
if (!vtableOp)
305306
return failure();
306307

@@ -1186,10 +1187,12 @@ struct ClassUpcastOpConversion : public OpConversionPattern<ClassUpcastOp> {
11861187

11871188
/// moore.class.new lowering: heap-allocate storage for the class object.
11881189
struct ClassNewOpConversion : public OpConversionPattern<ClassNewOp> {
1189-
ClassNewOpConversion(TypeConverter &tc, MLIRContext *ctx,
1190-
ClassTypeCache &cache, FunctionCache &funcCache)
1190+
ClassNewOpConversion(LLVMTypeConverter &tc, MLIRContext *ctx,
1191+
ClassTypeCache &cache, FunctionCache &funcCache,
1192+
SymbolTableCollection &symbolTables)
11911193
: OpConversionPattern<ClassNewOp>(tc, ctx), cache(cache),
1192-
funcCache(funcCache) {}
1194+
funcCache(funcCache), symbolTables(symbolTables),
1195+
llvmTypeConverter(tc) {}
11931196

11941197
LogicalResult
11951198
matchAndRewrite(ClassNewOp op, OpAdaptor adaptor,
@@ -1242,10 +1245,31 @@ struct ClassNewOpConversion : public OpConversionPattern<ClassNewOp> {
12421245
rewriter, loc, i32Ty,
12431246
rewriter.getI32IntegerAttr(
12441247
cache.getStructInfo(sym)->typeInfoFieldIndex));
1245-
auto headerPtr =
1248+
auto headerBasePtr =
12461249
LLVM::GEPOp::create(rewriter, loc, ptrTy, structTy, call.getResult(0),
1247-
ValueRange{headerIdx, typeInfoIdx});
1248-
LLVM::StoreOp::create(rewriter, loc, typeInfoAddr, headerPtr);
1250+
ValueRange{headerIdx});
1251+
auto typeInfoPtr = LLVM::GEPOp::create(
1252+
rewriter, loc, ptrTy, cache.getStructInfo(sym)->headerTy, headerBasePtr,
1253+
ValueRange{headerIdx, typeInfoIdx});
1254+
LLVM::StoreOp::create(rewriter, loc, typeInfoAddr, typeInfoPtr);
1255+
1256+
if (findVTableOp(mod, sym)) {
1257+
auto vtableInfo = getOrCreateVTableInfo(
1258+
mod, sym, rewriter, llvmTypeConverter, symbolTables, cache);
1259+
if (failed(vtableInfo))
1260+
return op.emitError() << "Could not create vtable for " << sym;
1261+
1262+
auto vtableAddr =
1263+
LLVM::AddressOfOp::create(rewriter, loc, vtableInfo->global);
1264+
auto vtableIdx = LLVM::ConstantOp::create(
1265+
rewriter, loc, i32Ty,
1266+
rewriter.getI32IntegerAttr(
1267+
cache.getStructInfo(sym)->vtableFieldIndex));
1268+
auto vtablePtr = LLVM::GEPOp::create(
1269+
rewriter, loc, ptrTy, cache.getStructInfo(sym)->headerTy,
1270+
headerBasePtr, ValueRange{headerIdx, vtableIdx});
1271+
LLVM::StoreOp::create(rewriter, loc, vtableAddr, vtablePtr);
1272+
}
12491273

12501274
// Replace the new op with the malloc pointer (no cast needed with opaque
12511275
// ptrs).
@@ -1256,6 +1280,8 @@ struct ClassNewOpConversion : public OpConversionPattern<ClassNewOp> {
12561280
private:
12571281
ClassTypeCache &cache; // shared, owned by the pass
12581282
FunctionCache &funcCache;
1283+
SymbolTableCollection &symbolTables;
1284+
LLVMTypeConverter &llvmTypeConverter;
12591285
};
12601286

12611287
struct ClassDeclOpConversion : public OpConversionPattern<ClassDeclOp> {
@@ -1302,6 +1328,73 @@ struct VTableOpConversion : public OpConversionPattern<VTableOp> {
13021328
LLVMTypeConverter &llvmTypeConverter;
13031329
};
13041330

1331+
struct VTableLoadMethodOpConversion
1332+
: public OpConversionPattern<VTableLoadMethodOp> {
1333+
VTableLoadMethodOpConversion(LLVMTypeConverter &tc, MLIRContext *ctx,
1334+
ClassTypeCache &cache,
1335+
SymbolTableCollection &symbolTables)
1336+
: OpConversionPattern<VTableLoadMethodOp>(tc, ctx), cache(cache),
1337+
symbolTables(symbolTables), llvmTypeConverter(tc) {}
1338+
1339+
LogicalResult
1340+
matchAndRewrite(VTableLoadMethodOp op, OpAdaptor adaptor,
1341+
ConversionPatternRewriter &rewriter) const override {
1342+
auto objectTy = cast<ClassHandleType>(op.getObject().getType());
1343+
auto classSym = objectTy.getClassSym();
1344+
auto methodName = op.getMethodSymAttr().getLeafReference();
1345+
if (!methodName)
1346+
return rewriter.notifyMatchFailure(op, "missing method symbol");
1347+
1348+
auto mod = op->getParentOfType<ModuleOp>();
1349+
if (failed(resolveClassStructBody(mod, classSym, *typeConverter, cache)))
1350+
return op.emitError()
1351+
<< "Could not resolve class struct for " << classSym;
1352+
1353+
auto vtableInfo = getOrCreateVTableInfo(
1354+
mod, classSym, rewriter, llvmTypeConverter, symbolTables, cache);
1355+
if (failed(vtableInfo))
1356+
return op.emitError() << "Could not resolve vtable for " << classSym;
1357+
1358+
auto slotIt = vtableInfo->methodSlot.find(methodName.getValue());
1359+
if (slotIt == vtableInfo->methodSlot.end())
1360+
return op.emitError() << "No vtable slot for method " << methodName;
1361+
1362+
auto structTy = cache.getStructInfo(classSym)->classBody;
1363+
auto ptrTy = LLVM::LLVMPointerType::get(rewriter.getContext());
1364+
auto i32Ty = rewriter.getI32Type();
1365+
auto zero = LLVM::ConstantOp::create(rewriter, op.getLoc(), i32Ty,
1366+
rewriter.getI32IntegerAttr(0));
1367+
auto vtableIdx = LLVM::ConstantOp::create(
1368+
rewriter, op.getLoc(), i32Ty,
1369+
rewriter.getI32IntegerAttr(
1370+
cache.getStructInfo(classSym)->vtableFieldIndex));
1371+
auto slotIdx =
1372+
LLVM::ConstantOp::create(rewriter, op.getLoc(), i32Ty,
1373+
rewriter.getI32IntegerAttr(slotIt->second));
1374+
1375+
auto headerBasePtr =
1376+
LLVM::GEPOp::create(rewriter, op.getLoc(), ptrTy, structTy,
1377+
adaptor.getObject(), ValueRange{zero});
1378+
auto vtablePtrPtr = LLVM::GEPOp::create(
1379+
rewriter, op.getLoc(), ptrTy, cache.getStructInfo(classSym)->headerTy,
1380+
headerBasePtr, ValueRange{zero, vtableIdx});
1381+
auto vtablePtr =
1382+
LLVM::LoadOp::create(rewriter, op.getLoc(), ptrTy, vtablePtrPtr);
1383+
auto slotPtr =
1384+
LLVM::GEPOp::create(rewriter, op.getLoc(), ptrTy, vtableInfo->tableTy,
1385+
vtablePtr, ValueRange{zero, slotIdx});
1386+
auto methodPtr =
1387+
LLVM::LoadOp::create(rewriter, op.getLoc(), ptrTy, slotPtr);
1388+
rewriter.replaceOp(op, methodPtr.getResult());
1389+
return success();
1390+
}
1391+
1392+
private:
1393+
ClassTypeCache &cache;
1394+
SymbolTableCollection &symbolTables;
1395+
LLVMTypeConverter &llvmTypeConverter;
1396+
};
1397+
13051398
struct VariableOpConversion : public OpConversionPattern<VariableOp> {
13061399
using OpConversionPattern::OpConversionPattern;
13071400

@@ -3427,7 +3520,9 @@ static void populateOpConversion(ConversionPatternSet &patterns,
34273520
patterns.add<VTableOpConversion>(typeConverter, patterns.getContext(),
34283521
classCache, symbolTables);
34293522
patterns.add<ClassNewOpConversion>(typeConverter, patterns.getContext(),
3430-
classCache, funcCache);
3523+
classCache, funcCache, symbolTables);
3524+
patterns.add<VTableLoadMethodOpConversion>(
3525+
typeConverter, patterns.getContext(), classCache, symbolTables);
34313526
patterns.add<ClassPropertyRefOpConversion>(typeConverter,
34323527
patterns.getContext(), classCache);
34333528

test/Conversion/MooreToCore/classes.mlir

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ func.func @ClassType(%arg0: !moore.class<@PropertyCombo>) {
3535
// CHECK: [[PTR:%.*]] = call @malloc([[SIZE]]) : (i64) -> !llvm.ptr
3636
// CHECK: [[TYPEINFO:%.*]] = llvm.mlir.addressof @"C::typeinfo" : !llvm.ptr
3737
// CHECK: [[HEADERIDX:%.*]] = llvm.mlir.constant(0 : i32) : i32
38-
// CHECK: [[GEP:%.*]] = llvm.getelementptr [[PTR]][[[HEADERIDX]], 0] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<"C", (struct<(ptr, ptr)>, i32, i32, i32)>
39-
// CHECK: llvm.store [[TYPEINFO]], [[GEP]] : !llvm.ptr, !llvm.ptr
38+
// CHECK: [[HEADERPTR:%.*]] = llvm.getelementptr [[PTR]][[[HEADERIDX]]] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<"C", (struct<(ptr, ptr)>, i32, i32, i32)>
39+
// CHECK: [[TYPEINFOPTR:%.*]] = llvm.getelementptr [[HEADERPTR]][[[HEADERIDX]], 0] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<(ptr, ptr)>
40+
// CHECK: llvm.store [[TYPEINFO]], [[TYPEINFOPTR]] : !llvm.ptr, !llvm.ptr
4041
// CHECK: return
4142

4243
// CHECK-NOT: moore.class.new
@@ -61,8 +62,9 @@ moore.class.classdecl @C {
6162
// CHECK: [[PTR:%.*]] = call @malloc([[SIZE]]) : (i64) -> !llvm.ptr
6263
// CHECK: [[TYPEINFO:%.*]] = llvm.mlir.addressof @"D::typeinfo" : !llvm.ptr
6364
// CHECK: [[HEADERIDX:%.*]] = llvm.mlir.constant(0 : i32) : i32
64-
// CHECK: [[GEP:%.*]] = llvm.getelementptr [[PTR]][[[HEADERIDX]], 0] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<"D", (struct<(ptr, ptr)>, struct<"C", (struct<(ptr, ptr)>, i32, i32, i32)>, i32, i64, i16)>
65-
// CHECK: llvm.store [[TYPEINFO]], [[GEP]] : !llvm.ptr, !llvm.ptr
65+
// CHECK: [[HEADERPTR:%.*]] = llvm.getelementptr [[PTR]][[[HEADERIDX]]] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<"D", (struct<(ptr, ptr)>, struct<"C", (struct<(ptr, ptr)>, i32, i32, i32)>, i32, i64, i16)>
66+
// CHECK: [[TYPEINFOPTR:%.*]] = llvm.getelementptr [[HEADERPTR]][[[HEADERIDX]], 0] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<(ptr, ptr)>
67+
// CHECK: llvm.store [[TYPEINFO]], [[TYPEINFOPTR]] : !llvm.ptr, !llvm.ptr
6668
// CHECK: return
6769

6870
// CHECK-NOT: moore.class.new
@@ -147,8 +149,9 @@ moore.class.classdecl @G extends @C {
147149
// CHECK: [[PTR:%.*]] = call @malloc([[SIZE]]) : (i64) -> !llvm.ptr
148150
// CHECK: [[TYPEINFO:%.*]] = llvm.mlir.addressof @"VirtualC::typeinfo" : !llvm.ptr
149151
// CHECK: [[HEADERIDX:%.*]] = llvm.mlir.constant(0 : i32) : i32
150-
// CHECK: [[GEP:%.*]] = llvm.getelementptr [[PTR]][[[HEADERIDX]], 0] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<"VirtualC", (struct<(ptr, ptr)>, i32)>
151-
// CHECK: llvm.store [[TYPEINFO]], [[GEP]] : !llvm.ptr, !llvm.ptr
152+
// CHECK: [[HEADERPTR:%.*]] = llvm.getelementptr [[PTR]][[[HEADERIDX]]] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<"VirtualC", (struct<(ptr, ptr)>, i32)>
153+
// CHECK: [[TYPEINFOPTR:%.*]] = llvm.getelementptr [[HEADERPTR]][[[HEADERIDX]], 0] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<(ptr, ptr)>
154+
// CHECK: llvm.store [[TYPEINFO]], [[TYPEINFOPTR]] : !llvm.ptr, !llvm.ptr
152155
// CHECK: return
153156

154157
func.func private @test_new7() {

test/Conversion/MooreToCore/vtables.mlir

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,42 @@
2525
// CHECK-LABEL: llvm.func @"tClass::subroutine"(
2626
// CHECK: llvm.return
2727

28+
// CHECK-LABEL: func.func private @alloc_test()
29+
// CHECK: %[[SIZE:.*]] = llvm.mlir.constant
30+
// CHECK: %[[OBJ:.*]] = call @malloc(%[[SIZE]]) : (i64) -> !llvm.ptr
31+
// CHECK: %[[RTTI:.*]] = llvm.mlir.addressof @"testClass::typeinfo" : !llvm.ptr
32+
// CHECK: %[[HEADER_PTR:.*]] = llvm.getelementptr %[[OBJ]][%{{.*}}] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<"testClass"
33+
// CHECK: %[[TYPEINFO_PTR:.*]] = llvm.getelementptr %[[HEADER_PTR]][%{{.*}}, 0] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<(ptr, ptr)>
34+
// CHECK: llvm.store %[[RTTI]], %[[TYPEINFO_PTR]] : !llvm.ptr, !llvm.ptr
35+
// CHECK: %[[VTBL:.*]] = llvm.mlir.addressof @"testClass::vtable" : !llvm.ptr
36+
// CHECK: %[[VTBL_PTR:.*]] = llvm.getelementptr %[[HEADER_PTR]][%{{.*}}, 1] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<(ptr, ptr)>
37+
// CHECK: llvm.store %[[VTBL]], %[[VTBL_PTR]] : !llvm.ptr, !llvm.ptr
38+
// CHECK: return
39+
40+
// CHECK-LABEL: func.func private @dispatch_test(
41+
// CHECK-SAME: %[[OBJ:.*]]: !llvm.ptr
42+
// CHECK: %[[HEADER_PTR2:.*]] = llvm.getelementptr %[[OBJ]][%{{.*}}] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<"testClass"
43+
// CHECK: %[[VTBL_PTR_PTR:.*]] = llvm.getelementptr %[[HEADER_PTR2]][%{{.*}}, 1] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<(ptr, ptr)>
44+
// CHECK: %[[VTBL_PTR:.*]] = llvm.load %[[VTBL_PTR_PTR]] : !llvm.ptr
45+
// CHECK: %[[SLOT_PTR:.*]] = llvm.getelementptr %[[VTBL_PTR]][%{{.*}}, 0] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<(ptr, ptr)>
46+
// CHECK: %[[METH_PTR:.*]] = llvm.load %[[SLOT_PTR]] : !llvm.ptr
47+
// CHECK: llvm.call %[[METH_PTR]](%[[OBJ]]) : !llvm.ptr, (!llvm.ptr) -> ()
48+
// CHECK: return
49+
50+
// CHECK-LABEL: func.func private @dispatch_upcast_test(
51+
// CHECK-SAME: %[[DERIVED:.*]]: !llvm.ptr
52+
// CHECK-NOT: moore.class.upcast
53+
// CHECK: %[[HEADER_PTR3:.*]] = llvm.getelementptr %[[DERIVED]][%{{.*}}] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<"testClass"
54+
// CHECK: %[[VTBL_PTR_PTR2:.*]] = llvm.getelementptr %[[HEADER_PTR3]][%{{.*}}, 1] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<(ptr, ptr)>
55+
// CHECK: %[[VTBL_PTR2:.*]] = llvm.load %[[VTBL_PTR_PTR2]] : !llvm.ptr
56+
// CHECK: %[[SLOT_PTR2:.*]] = llvm.getelementptr %[[VTBL_PTR2]][%{{.*}}, 0] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<(ptr, ptr)>
57+
// CHECK: %[[METH_PTR2:.*]] = llvm.load %[[SLOT_PTR2]] : !llvm.ptr
58+
// CHECK: llvm.call %[[METH_PTR2]](%[[DERIVED]]) : !llvm.ptr, (!llvm.ptr) -> ()
59+
// CHECK: return
60+
2861
// CHECK-NOT: moore.vtable
2962
// CHECK-NOT: moore.vtable_entry
63+
// CHECK-NOT: moore.vtable.load_method
3064

3165
moore.class.classdecl @virtualFunctionClass {
3266
moore.class.methoddecl @subroutine : (!moore.class<@virtualFunctionClass>) -> ()
@@ -74,3 +108,21 @@ moore.vtable @tClass::@vtable {
74108
func.func private @"tClass::subroutine"(%arg0: !moore.class<@tClass>) {
75109
return
76110
}
111+
112+
func.func private @alloc_test() {
113+
%0 = moore.class.new : <@testClass>
114+
return
115+
}
116+
117+
func.func private @dispatch_test(%arg0: !moore.class<@testClass>) {
118+
%0 = moore.vtable.load_method %arg0 : @subroutine of <@testClass> -> (!moore.class<@testClass>) -> ()
119+
call_indirect %0(%arg0) : (!moore.class<@testClass>) -> ()
120+
return
121+
}
122+
123+
func.func private @dispatch_upcast_test(%arg0: !moore.class<@tClass>) {
124+
%0 = moore.class.upcast %arg0 : <@tClass> to <@testClass>
125+
%1 = moore.vtable.load_method %0 : @subroutine of <@testClass> -> (!moore.class<@testClass>) -> ()
126+
call_indirect %1(%0) : (!moore.class<@testClass>) -> ()
127+
return
128+
}

0 commit comments

Comments
 (0)