Skip to content

Commit c26734d

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 a02d30e commit c26734d

File tree

2 files changed

+164
-6
lines changed

2 files changed

+164
-6
lines changed

lib/Conversion/MooreToCore/MooreToCore.cpp

Lines changed: 79 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,23 @@ struct ClassTypeCache {
9797
llvm::DenseMap<mlir::Attribute, ClassStructInfo> classToStructMap;
9898
};
9999

100+
/// Ensure we have `declare i8* @malloc(i64)` (opaque ptr prints as !llvm.ptr).
101+
static LLVM::LLVMFuncOp getOrCreateMalloc(ModuleOp mod, OpBuilder &b) {
102+
if (auto f = mod.lookupSymbol<LLVM::LLVMFuncOp>("malloc"))
103+
return f;
104+
105+
OpBuilder::InsertionGuard g(b);
106+
b.setInsertionPointToStart(mod.getBody());
107+
108+
auto i64Ty = IntegerType::get(mod.getContext(), 64);
109+
auto ptrTy = LLVM::LLVMPointerType::get(mod.getContext()); // opaque pointer
110+
auto fnTy = LLVM::LLVMFunctionType::get(ptrTy, {i64Ty}, /*isVarArg=*/false);
111+
112+
auto fn = LLVM::LLVMFuncOp::create(b, mod.getLoc(), "malloc", fnTy);
113+
fn.setLinkage(LLVM::Linkage::External);
114+
return fn;
115+
}
116+
100117
/// Helper function to create an LLVM-friendly name from a class name symbol
101118
/// Classname is expected to be in a H1::H2::H3::@C format.
102119
static std::string mangleClassName(SymbolRefAttr className) {
@@ -147,12 +164,13 @@ static LogicalResult resolveClassStructBody(circt::moore::ClassDeclOp op,
147164

148165
if (auto baseClass = op.getBaseAttr()) {
149166

167+
ModuleOp mod = op->getParentOfType<ModuleOp>();
168+
auto classDeclOp = cast<moore::ClassDeclOp>(*mod.lookupSymbol(baseClass));
169+
if (failed(resolveClassStructBody(classDeclOp, typeConverter, cache)))
170+
op.emitError() << "Failed to convert base class " << baseClass;
171+
150172
// Process base class' struct layout first
151173
auto baseClassStruct = cache.getStructInfo(baseClass);
152-
if (!baseClassStruct)
153-
return op.emitOpError() << "Base class " << baseClass << " of "
154-
<< classSym << " has not been converted.";
155-
156174
structBodyMembers.push_back(baseClassStruct->classBody);
157175
derivedStartIdx = 1;
158176

@@ -198,6 +216,14 @@ static LogicalResult resolveClassStructBody(circt::moore::ClassDeclOp op,
198216
return success();
199217
}
200218

219+
/// Convenience overload if
220+
static LogicalResult resolveClassStructBody(ModuleOp mod, SymbolRefAttr op,
221+
TypeConverter const &typeConverter,
222+
ClassTypeCache &cache) {
223+
auto classDeclOp = cast<moore::ClassDeclOp>(*mod.lookupSymbol(op));
224+
return resolveClassStructBody(classDeclOp, typeConverter, cache);
225+
}
226+
201227
/// Returns the passed value if the integer width is already correct.
202228
/// Zero-extends if it is too narrow.
203229
/// Truncates if the integer is too wide and the truncated part is zero, if it
@@ -705,6 +731,52 @@ static Value createZeroValue(Type type, Location loc,
705731
return rewriter.createOrFold<hw::BitcastOp>(loc, type, constZero);
706732
}
707733

734+
/// moore.class.new lowering: heap-allocate storage for the class object.
735+
struct ClassNewOpConversion : public OpConversionPattern<ClassNewOp> {
736+
ClassNewOpConversion(TypeConverter &tc, MLIRContext *ctx,
737+
ClassTypeCache &cache)
738+
: OpConversionPattern<ClassNewOp>(tc, ctx), cache(cache) {}
739+
740+
LogicalResult
741+
matchAndRewrite(ClassNewOp op, OpAdaptor adaptor,
742+
ConversionPatternRewriter &rewriter) const override {
743+
Location loc = op.getLoc();
744+
MLIRContext *ctx = rewriter.getContext();
745+
746+
auto handleTy = cast<ClassHandleType>(op.getResult().getType());
747+
auto sym = handleTy.getClassSym();
748+
749+
ModuleOp mod = op->getParentOfType<ModuleOp>();
750+
751+
if (failed(resolveClassStructBody(mod, sym, *typeConverter, cache)))
752+
return op.emitError() << "Could not resolve class struct for " << sym;
753+
754+
auto structTy = cache.getStructInfo(sym)->classBody;
755+
756+
DataLayout dl(mod);
757+
// DataLayout::getTypeSize gives a byte count for LLVM types.
758+
uint64_t byteSize = dl.getTypeSize(structTy);
759+
auto i64Ty = IntegerType::get(ctx, 64);
760+
auto cSize = LLVM::ConstantOp::create(rewriter, loc, i64Ty,
761+
rewriter.getI64IntegerAttr(byteSize));
762+
763+
// Get or declare malloc and call it.
764+
auto mallocFn = getOrCreateMalloc(mod, rewriter);
765+
auto ptrTy = LLVM::LLVMPointerType::get(ctx); // opaque pointer result
766+
auto call =
767+
LLVM::CallOp::create(rewriter, loc, TypeRange{ptrTy},
768+
SymbolRefAttr::get(mallocFn), ValueRange{cSize});
769+
770+
// Replace the new op with the malloc pointer (no cast needed with opaque
771+
// ptrs).
772+
rewriter.replaceOp(op, call.getResult());
773+
return success();
774+
}
775+
776+
private:
777+
ClassTypeCache &cache; // shared, owned by the pass
778+
};
779+
708780
struct ClassDeclOpConversion : public OpConversionPattern<ClassDeclOp> {
709781
ClassDeclOpConversion(TypeConverter &tc, MLIRContext *ctx,
710782
ClassTypeCache &cache)
@@ -713,9 +785,9 @@ struct ClassDeclOpConversion : public OpConversionPattern<ClassDeclOp> {
713785
LogicalResult
714786
matchAndRewrite(ClassDeclOp op, OpAdaptor,
715787
ConversionPatternRewriter &rewriter) const override {
788+
716789
if (failed(resolveClassStructBody(op, *typeConverter, cache)))
717790
return failure();
718-
719791
// The declaration itself is a no-op
720792
rewriter.eraseOp(op);
721793
return success();
@@ -2104,7 +2176,8 @@ static void populateOpConversion(ConversionPatternSet &patterns,
21042176

21052177
patterns.add<ClassDeclOpConversion>(typeConverter, patterns.getContext(),
21062178
classCache);
2107-
2179+
patterns.add<ClassNewOpConversion>(typeConverter, patterns.getContext(),
2180+
classCache);
21082181
// clang-format off
21092182
patterns.add<
21102183
// Patterns of declaration operations.

test/Conversion/MooreToCore/classes.mlir

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,88 @@ module {
2828
: !llvm.struct<"moore.class.PropertyCombo", (i32, i18, i32)>
2929
}
3030
}
31+
32+
/// Check that new lowers to malloc
33+
34+
// malloc should be declared in the LLVM dialect.
35+
// CHECK: llvm.func @malloc(i64) -> !llvm.ptr
36+
// CHECK: func.func private @test_new2
37+
// CHECK: [[SIZE:%.*]] = llvm.mlir.constant(12 : i64) : i64
38+
// CHECK: [[PTR:%.*]] = llvm.call @malloc([[SIZE]]) : (i64) -> !llvm.ptr
39+
// CHECK: return
40+
41+
// Original op must be gone.
42+
// CHECK-NOT: moore.class.new
43+
44+
// Class decl should be erased by the class lowering pass.
45+
// CHECK-NOT: moore.class.classdecl
46+
47+
module {
48+
// Allocate a new instance; should lower to llvm.call @malloc(i64).
49+
func.func private @test_new2() {
50+
%h = moore.class.new : <@C>
51+
return
52+
}
53+
// Minimal class so the identified struct has a concrete body.
54+
moore.class.classdecl @C {
55+
moore.class.propertydecl @a : !moore.i32
56+
moore.class.propertydecl @b : !moore.l32
57+
moore.class.propertydecl @c : !moore.l32
58+
}
59+
}
60+
61+
/// Check that new lowers to malloc with inheritance without shadowing
62+
63+
// CHECK-LABEL: func.func private @test_new3
64+
// CHECK: [[SIZE:%.*]] = llvm.mlir.constant(28 : i64) : i64
65+
// CHECK: [[PTR:%.*]] = llvm.call @malloc([[SIZE]]) : (i64) -> !llvm.ptr
66+
// CHECK: return
67+
68+
// CHECK-NOT: moore.class.new
69+
// CHECK-NOT: moore.class.classdecl
70+
71+
module {
72+
func.func private @test_new3() {
73+
%h = moore.class.new : <@D>
74+
return
75+
}
76+
moore.class.classdecl @C {
77+
moore.class.propertydecl @a : !moore.i32
78+
moore.class.propertydecl @b : !moore.l32
79+
moore.class.propertydecl @c : !moore.l32
80+
}
81+
moore.class.classdecl @D extends @C {
82+
moore.class.propertydecl @d : !moore.l32
83+
moore.class.propertydecl @e : !moore.l64
84+
moore.class.propertydecl @f : !moore.i16
85+
}
86+
87+
}
88+
89+
/// Check that new lowers to malloc with inheritance & shadowing
90+
91+
// CHECK-LABEL: func.func private @test_new4
92+
// CHECK: [[SIZE:%.*]] = llvm.mlir.constant(24 : i64) : i64
93+
// CHECK: [[PTR:%.*]] = llvm.call @malloc([[SIZE]]) : (i64) -> !llvm.ptr
94+
// CHECK: return
95+
96+
// CHECK-NOT: moore.class.new
97+
// CHECK-NOT: moore.class.classdecl
98+
99+
module {
100+
func.func private @test_new4() {
101+
%h = moore.class.new : <@E>
102+
return
103+
}
104+
moore.class.classdecl @C {
105+
moore.class.propertydecl @a : !moore.i32
106+
moore.class.propertydecl @b : !moore.l32
107+
moore.class.propertydecl @c : !moore.l32
108+
}
109+
moore.class.classdecl @E extends @C {
110+
moore.class.propertydecl @a : !moore.i32
111+
moore.class.propertydecl @b : !moore.l32
112+
moore.class.propertydecl @c : !moore.l32
113+
}
114+
115+
}

0 commit comments

Comments
 (0)