Skip to content

Commit 2a6b8d1

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 b4b774e commit 2a6b8d1

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
@@ -100,6 +100,23 @@ struct ClassTypeCache {
100100
llvm::DenseMap<mlir::Attribute, ClassStructInfo> classToStructMap;
101101
};
102102

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

151168
if (auto baseClass = op.getBaseAttr()) {
152169

170+
ModuleOp mod = op->getParentOfType<ModuleOp>();
171+
auto classDeclOp = cast<moore::ClassDeclOp>(*mod.lookupSymbol(baseClass));
172+
if (failed(resolveClassStructBody(classDeclOp, typeConverter, cache)))
173+
op.emitError() << "Failed to convert base class " << baseClass;
174+
153175
// Process base class' struct layout first
154176
auto baseClassStruct = cache.getStructInfo(baseClass);
155-
if (!baseClassStruct || !baseClassStruct->isFinalized)
156-
return op.emitOpError() << "Base class " << baseClass << " of "
157-
<< classSym << " has not been converted.";
158-
159177
structBodyMembers.push_back(baseClassStruct->classBody);
160178
derivedStartIdx = 1;
161179

@@ -200,6 +218,14 @@ static LogicalResult resolveClassStructBody(circt::moore::ClassDeclOp op,
200218
return success();
201219
}
202220

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

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

21192191
patterns.add<ClassDeclOpConversion>(typeConverter, patterns.getContext(),
21202192
classCache);
2121-
2193+
patterns.add<ClassNewOpConversion>(typeConverter, patterns.getContext(),
2194+
classCache);
21222195
// clang-format off
21232196
patterns.add<
21242197
// 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)