Skip to content

Commit 9da5c15

Browse files
committed
[MooreToCore] Add support to lower ClassNewOp to llvm.malloc
- Add `getOrCreateMalloc` to declare `llvm.func @malloc(i64) -> !llvm.ptr` (opaque ptr). - Implement `ClassNewOpConversion`: - Resolve class struct body, compute size via `DataLayout`, call `malloc`. - Replace `moore.class.new` with result of `malloc` (no cast in opaque-ptr mode). - Make base-class struct resolution lazy: - If a base’s struct isn’t finalized, resolve it on demand. - Add overload `resolveClassStructBody(ModuleOp, SymbolRefAttr, ...)`. - Register new conversion pattern in `populateOpConversion`.
1 parent c0199d2 commit 9da5c15

File tree

2 files changed

+163
-18
lines changed

2 files changed

+163
-18
lines changed

lib/Conversion/MooreToCore/MooreToCore.cpp

Lines changed: 100 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ struct ClassTypeCache {
6060
/// Record/overwrite the field path to a single property for a class.
6161
void setFieldPath(StringRef propertyName, ArrayRef<unsigned> path) {
6262
this->propertyPath[propertyName] =
63-
llvm::SmallVector<unsigned, 2>(path.begin(), path.end());
63+
SmallVector<unsigned, 2>(path.begin(), path.end());
6464
}
6565

6666
/// Lookup the full GEP path for a (class, field).
@@ -94,6 +94,24 @@ struct ClassTypeCache {
9494
DenseMap<Attribute, ClassStructInfo> classToStructMap;
9595
};
9696

97+
/// Ensure we have `declare i8* @malloc(i64)` (opaque ptr prints as !llvm.ptr).
98+
static LLVM::LLVMFuncOp getOrCreateMalloc(ModuleOp mod, OpBuilder &b) {
99+
if (auto f = mod.lookupSymbol<LLVM::LLVMFuncOp>("malloc"))
100+
return f;
101+
102+
OpBuilder::InsertionGuard g(b);
103+
b.setInsertionPointToStart(mod.getBody());
104+
105+
auto i64Ty = IntegerType::get(mod.getContext(), 64);
106+
auto ptrTy = LLVM::LLVMPointerType::get(mod.getContext()); // opaque pointer
107+
auto fnTy = LLVM::LLVMFunctionType::get(ptrTy, {i64Ty}, false);
108+
109+
auto fn = LLVM::LLVMFuncOp::create(b, mod.getLoc(), "malloc", fnTy);
110+
// Link this in from somewhere else.
111+
fn.setLinkage(LLVM::Linkage::External);
112+
return fn;
113+
}
114+
97115
/// Helper function to create an opaque LLVM Struct Type which corresponds
98116
/// to the sym
99117
static LLVM::LLVMStructType getOrCreateOpaqueStruct(MLIRContext *ctx,
@@ -120,21 +138,30 @@ static LogicalResult resolveClassStructBody(ClassDeclOp op,
120138

121139
if (auto baseClass = op.getBaseAttr()) {
122140

123-
// Process base class' struct layout first
124-
auto baseClassStruct = cache.getStructInfo(baseClass);
125-
if (!baseClassStruct)
126-
return op.emitOpError() << "Base class " << baseClass << " of "
127-
<< classSym << " has not been converted.";
128-
129-
structBodyMembers.push_back(baseClassStruct->classBody);
130-
derivedStartIdx = 1;
131-
132-
// Inherit base field paths with a leading 0.
133-
for (auto &kv : baseClassStruct->propertyPath) {
134-
SmallVector<unsigned, 2> path;
135-
path.push_back(0); // into base subobject
136-
path.append(kv.second.begin(), kv.second.end());
137-
structBody.setFieldPath(kv.first, path);
141+
ModuleOp mod = op->getParentOfType<ModuleOp>();
142+
// It's possible the base class does not resolve since it materialzes
143+
// no methods or properties. In that case we can simply ignore inheritance.
144+
if (auto *opSym = mod.lookupSymbol(baseClass)) {
145+
auto classDeclOp = dyn_cast<ClassDeclOp>(opSym);
146+
if (!classDeclOp)
147+
return op.emitError() << "Found symbol corresponding to " << baseClass
148+
<< " but it wasn't a classdeclop!";
149+
150+
if (failed(resolveClassStructBody(classDeclOp, typeConverter, cache)))
151+
return failure();
152+
153+
// Process base class' struct layout first
154+
auto baseClassStruct = cache.getStructInfo(baseClass);
155+
structBodyMembers.push_back(baseClassStruct->classBody);
156+
derivedStartIdx = 1;
157+
158+
// Inherit base field paths with a leading 0.
159+
for (auto &kv : baseClassStruct->propertyPath) {
160+
SmallVector<unsigned, 2> path;
161+
path.push_back(0); // into base subobject
162+
path.append(kv.second.begin(), kv.second.end());
163+
structBody.setFieldPath(kv.first, path);
164+
}
138165
}
139166
}
140167

@@ -171,6 +198,14 @@ static LogicalResult resolveClassStructBody(ClassDeclOp op,
171198
return success();
172199
}
173200

201+
/// Convenience overload that looks up ClassDeclOp
202+
static LogicalResult resolveClassStructBody(ModuleOp mod, SymbolRefAttr op,
203+
TypeConverter const &typeConverter,
204+
ClassTypeCache &cache) {
205+
auto classDeclOp = cast<ClassDeclOp>(*mod.lookupSymbol(op));
206+
return resolveClassStructBody(classDeclOp, typeConverter, cache);
207+
}
208+
174209
/// Returns the passed value if the integer width is already correct.
175210
/// Zero-extends if it is too narrow.
176211
/// Truncates if the integer is too wide and the truncated part is zero, if it
@@ -678,6 +713,52 @@ static Value createZeroValue(Type type, Location loc,
678713
return rewriter.createOrFold<hw::BitcastOp>(loc, type, constZero);
679714
}
680715

716+
/// moore.class.new lowering: heap-allocate storage for the class object.
717+
struct ClassNewOpConversion : public OpConversionPattern<ClassNewOp> {
718+
ClassNewOpConversion(TypeConverter &tc, MLIRContext *ctx,
719+
ClassTypeCache &cache)
720+
: OpConversionPattern<ClassNewOp>(tc, ctx), cache(cache) {}
721+
722+
LogicalResult
723+
matchAndRewrite(ClassNewOp op, OpAdaptor adaptor,
724+
ConversionPatternRewriter &rewriter) const override {
725+
Location loc = op.getLoc();
726+
MLIRContext *ctx = rewriter.getContext();
727+
728+
auto handleTy = cast<ClassHandleType>(op.getResult().getType());
729+
auto sym = handleTy.getClassSym();
730+
731+
ModuleOp mod = op->getParentOfType<ModuleOp>();
732+
733+
if (failed(resolveClassStructBody(mod, sym, *typeConverter, cache)))
734+
return op.emitError() << "Could not resolve class struct for " << sym;
735+
736+
auto structTy = cache.getStructInfo(sym)->classBody;
737+
738+
DataLayout dl(mod);
739+
// DataLayout::getTypeSize gives a byte count for LLVM types.
740+
uint64_t byteSize = dl.getTypeSize(structTy);
741+
auto i64Ty = IntegerType::get(ctx, 64);
742+
auto cSize = LLVM::ConstantOp::create(rewriter, loc, i64Ty,
743+
rewriter.getI64IntegerAttr(byteSize));
744+
745+
// Get or declare malloc and call it.
746+
auto mallocFn = getOrCreateMalloc(mod, rewriter);
747+
auto ptrTy = LLVM::LLVMPointerType::get(ctx); // opaque pointer result
748+
auto call =
749+
LLVM::CallOp::create(rewriter, loc, TypeRange{ptrTy},
750+
SymbolRefAttr::get(mallocFn), ValueRange{cSize});
751+
752+
// Replace the new op with the malloc pointer (no cast needed with opaque
753+
// ptrs).
754+
rewriter.replaceOp(op, call.getResult());
755+
return success();
756+
}
757+
758+
private:
759+
ClassTypeCache &cache; // shared, owned by the pass
760+
};
761+
681762
struct ClassDeclOpConversion : public OpConversionPattern<ClassDeclOp> {
682763
ClassDeclOpConversion(TypeConverter &tc, MLIRContext *ctx,
683764
ClassTypeCache &cache)
@@ -686,9 +767,9 @@ struct ClassDeclOpConversion : public OpConversionPattern<ClassDeclOp> {
686767
LogicalResult
687768
matchAndRewrite(ClassDeclOp op, OpAdaptor,
688769
ConversionPatternRewriter &rewriter) const override {
770+
689771
if (failed(resolveClassStructBody(op, *typeConverter, cache)))
690772
return failure();
691-
692773
// The declaration itself is a no-op
693774
rewriter.eraseOp(op);
694775
return success();
@@ -2077,7 +2158,8 @@ static void populateOpConversion(ConversionPatternSet &patterns,
20772158

20782159
patterns.add<ClassDeclOpConversion>(typeConverter, patterns.getContext(),
20792160
classCache);
2080-
2161+
patterns.add<ClassNewOpConversion>(typeConverter, patterns.getContext(),
2162+
classCache);
20812163
// clang-format off
20822164
patterns.add<
20832165
// Patterns of declaration operations.

test/Conversion/MooreToCore/classes.mlir

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,66 @@ moore.class.classdecl @PropertyCombo {
1717
func.func @ClassType(%arg0: !moore.class<@PropertyCombo>) {
1818
return
1919
}
20+
21+
/// Check that new lowers to malloc
22+
23+
// malloc should be declared in the LLVM dialect.
24+
// CHECK-LABEL: func.func private @test_new2
25+
// CHECK: [[SIZE:%.*]] = llvm.mlir.constant(12 : i64) : i64
26+
// CHECK: [[PTR:%.*]] = llvm.call @malloc([[SIZE]]) : (i64) -> !llvm.ptr
27+
// CHECK: return
28+
29+
// CHECK-NOT: moore.class.new
30+
// CHECK-NOT: moore.class.classdecl
31+
32+
// Allocate a new instance; should lower to llvm.call @malloc(i64).
33+
func.func private @test_new2() {
34+
%h = moore.class.new : <@C>
35+
return
36+
}
37+
// Minimal class so the identified struct has a concrete body.
38+
moore.class.classdecl @C {
39+
moore.class.propertydecl @a : !moore.i32
40+
moore.class.propertydecl @b : !moore.l32
41+
moore.class.propertydecl @c : !moore.l32
42+
}
43+
44+
/// Check that new lowers to malloc with inheritance without shadowing
45+
46+
// CHECK-LABEL: func.func private @test_new3
47+
// CHECK: [[SIZE:%.*]] = llvm.mlir.constant(28 : i64) : i64
48+
// CHECK: [[PTR:%.*]] = llvm.call @malloc([[SIZE]]) : (i64) -> !llvm.ptr
49+
// CHECK: return
50+
51+
// CHECK-NOT: moore.class.new
52+
// CHECK-NOT: moore.class.classdecl
53+
54+
func.func private @test_new3() {
55+
%h = moore.class.new : <@D>
56+
return
57+
}
58+
moore.class.classdecl @D extends @C {
59+
moore.class.propertydecl @d : !moore.l32
60+
moore.class.propertydecl @e : !moore.l64
61+
moore.class.propertydecl @f : !moore.i16
62+
}
63+
64+
/// Check that new lowers to malloc with inheritance & shadowing
65+
66+
// CHECK-LABEL: func.func private @test_new4
67+
// CHECK: [[SIZE:%.*]] = llvm.mlir.constant(24 : i64) : i64
68+
// CHECK: [[PTR:%.*]] = llvm.call @malloc([[SIZE]]) : (i64) -> !llvm.ptr
69+
// CHECK: return
70+
71+
// CHECK-NOT: moore.class.new
72+
// CHECK-NOT: moore.class.classdecl
73+
74+
func.func private @test_new4() {
75+
%h = moore.class.new : <@E>
76+
return
77+
}
78+
moore.class.classdecl @E extends @C {
79+
moore.class.propertydecl @a : !moore.i32
80+
moore.class.propertydecl @b : !moore.l32
81+
moore.class.propertydecl @c : !moore.l32
82+
}

0 commit comments

Comments
 (0)