Skip to content

Commit c0199d2

Browse files
committed
[MooreToCore] Implement ClassDeclOp -> LLVMStructType conversion
Add support for converting `moore.class.classdecl` operations into identified LLVM struct types during the MooreToCore lowering. - Introduced **`ClassTypeCache`** for mapping class symbols to LLVM structs and field paths. - Added utilities: - `mangleClassName` generates `moore.class.<name>` identifiers. - `getOrCreateOpaqueStruct` creates identified LLVM struct types. - Implemented `resolveClassStructBody` to: - Lay out classes in **base-first** order. - Convert class properties to LLVM member types. - Inherit base field paths. - Added `ClassDeclOpConversion` to handle class lowering and removal. - Extended type conversion: - `ClassHandleType` -> `!llvm.ptr` - Integrated cache handling into `MooreToCorePass`.
1 parent e727789 commit c0199d2

File tree

2 files changed

+175
-2
lines changed

2 files changed

+175
-2
lines changed

lib/Conversion/MooreToCore/MooreToCore.cpp

Lines changed: 156 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,129 @@ using llvm::SmallDenseSet;
4848

4949
namespace {
5050

51+
/// Cache for identified structs and field GEP paths keyed by class symbol.
52+
struct ClassTypeCache {
53+
struct ClassStructInfo {
54+
LLVM::LLVMStructType classBody;
55+
56+
// field name -> GEP path inside ident (excluding the leading pointer index)
57+
DenseMap<StringRef, SmallVector<unsigned, 2>> propertyPath;
58+
59+
// TODO: Add classVTable in here.
60+
/// Record/overwrite the field path to a single property for a class.
61+
void setFieldPath(StringRef propertyName, ArrayRef<unsigned> path) {
62+
this->propertyPath[propertyName] =
63+
llvm::SmallVector<unsigned, 2>(path.begin(), path.end());
64+
}
65+
66+
/// Lookup the full GEP path for a (class, field).
67+
std::optional<ArrayRef<unsigned>>
68+
getFieldPath(StringRef propertySym) const {
69+
if (auto prop = this->propertyPath.find(propertySym);
70+
prop != this->propertyPath.end())
71+
return ArrayRef<unsigned>(prop->second);
72+
return std::nullopt;
73+
}
74+
};
75+
76+
/// Record the identified struct body for a class.
77+
/// Implicitly finalizes the class to struct conversion.
78+
void setClassInfo(SymbolRefAttr classSym, const ClassStructInfo &info) {
79+
auto &dst = classToStructMap[classSym];
80+
dst = info;
81+
}
82+
83+
/// Lookup the identified struct body for a class.
84+
std::optional<ClassStructInfo> getStructInfo(SymbolRefAttr classSym) const {
85+
if (auto it = classToStructMap.find(classSym); it != classToStructMap.end())
86+
return it->second;
87+
return std::nullopt;
88+
}
89+
90+
private:
91+
// Keyed by the SymbolRefAttr of the class.
92+
// Kept private so all accesses are done with helpers which preserve
93+
// invariants
94+
DenseMap<Attribute, ClassStructInfo> classToStructMap;
95+
};
96+
97+
/// Helper function to create an opaque LLVM Struct Type which corresponds
98+
/// to the sym
99+
static LLVM::LLVMStructType getOrCreateOpaqueStruct(MLIRContext *ctx,
100+
SymbolRefAttr className) {
101+
return LLVM::LLVMStructType::getIdentified(ctx, className.getRootReference());
102+
}
103+
104+
static LogicalResult resolveClassStructBody(ClassDeclOp op,
105+
TypeConverter const &typeConverter,
106+
ClassTypeCache &cache) {
107+
108+
auto classSym = SymbolRefAttr::get(op.getSymNameAttr());
109+
auto structInfo = cache.getStructInfo(classSym);
110+
if (structInfo)
111+
// We already have a resolved class struct body.
112+
return success();
113+
114+
// Otherwise we need to resolve.
115+
ClassTypeCache::ClassStructInfo structBody;
116+
SmallVector<Type> structBodyMembers;
117+
118+
// Base-first (prefix) layout for single inheritance.
119+
unsigned derivedStartIdx = 0;
120+
121+
if (auto baseClass = op.getBaseAttr()) {
122+
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);
138+
}
139+
}
140+
141+
// Properties in source order.
142+
unsigned iterator = derivedStartIdx;
143+
auto &block = op.getBody().front();
144+
for (Operation &child : block) {
145+
if (auto prop = dyn_cast<ClassPropertyDeclOp>(child)) {
146+
Type mooreTy = prop.getPropertyType();
147+
Type llvmTy = typeConverter.convertType(mooreTy);
148+
if (!llvmTy)
149+
return prop.emitOpError()
150+
<< "failed to convert property type " << mooreTy;
151+
152+
structBodyMembers.push_back(llvmTy);
153+
154+
// Derived field path: either {i} or {1+i} if base is present.
155+
SmallVector<unsigned, 2> path{iterator};
156+
structBody.setFieldPath(prop.getSymName(), path);
157+
++iterator;
158+
}
159+
}
160+
161+
// TODO: Handle vtable generation over ClassMethodDeclOp here.
162+
auto llvmStructTy = getOrCreateOpaqueStruct(op.getContext(), classSym);
163+
// Empty structs may be kept opaque
164+
if (!structBodyMembers.empty() &&
165+
failed(llvmStructTy.setBody(structBodyMembers, false)))
166+
return op.emitOpError() << "Failed to set LLVM Struct body";
167+
168+
structBody.classBody = llvmStructTy;
169+
cache.setClassInfo(classSym, structBody);
170+
171+
return success();
172+
}
173+
51174
/// Returns the passed value if the integer width is already correct.
52175
/// Zero-extends if it is too narrow.
53176
/// Truncates if the integer is too wide and the truncated part is zero, if it
@@ -555,6 +678,26 @@ static Value createZeroValue(Type type, Location loc,
555678
return rewriter.createOrFold<hw::BitcastOp>(loc, type, constZero);
556679
}
557680

681+
struct ClassDeclOpConversion : public OpConversionPattern<ClassDeclOp> {
682+
ClassDeclOpConversion(TypeConverter &tc, MLIRContext *ctx,
683+
ClassTypeCache &cache)
684+
: OpConversionPattern<ClassDeclOp>(tc, ctx), cache(cache) {}
685+
686+
LogicalResult
687+
matchAndRewrite(ClassDeclOp op, OpAdaptor,
688+
ConversionPatternRewriter &rewriter) const override {
689+
if (failed(resolveClassStructBody(op, *typeConverter, cache)))
690+
return failure();
691+
692+
// The declaration itself is a no-op
693+
rewriter.eraseOp(op);
694+
return success();
695+
}
696+
697+
private:
698+
ClassTypeCache &cache; // shared, owned by the pass
699+
};
700+
558701
struct VariableOpConversion : public OpConversionPattern<VariableOp> {
559702
using OpConversionPattern::OpConversionPattern;
560703

@@ -1864,6 +2007,11 @@ static void populateTypeConversion(TypeConverter &typeConverter) {
18642007
typeConverter.addConversion(
18652008
[](LLVM::LLVMPointerType t) -> std::optional<Type> { return t; });
18662009

2010+
// ClassHandleType -> !llvm.ptr
2011+
typeConverter.addConversion([&](ClassHandleType type) -> std::optional<Type> {
2012+
return LLVM::LLVMPointerType::get(type.getContext());
2013+
});
2014+
18672015
typeConverter.addConversion([&](RefType type) -> std::optional<Type> {
18682016
if (auto innerType = typeConverter.convertType(type.getNestedType()))
18692017
return llhd::RefType::get(innerType);
@@ -1924,7 +2072,12 @@ static void populateTypeConversion(TypeConverter &typeConverter) {
19242072
}
19252073

19262074
static void populateOpConversion(ConversionPatternSet &patterns,
1927-
TypeConverter &typeConverter) {
2075+
TypeConverter &typeConverter,
2076+
ClassTypeCache &classCache) {
2077+
2078+
patterns.add<ClassDeclOpConversion>(typeConverter, patterns.getContext(),
2079+
classCache);
2080+
19282081
// clang-format off
19292082
patterns.add<
19302083
// Patterns of declaration operations.
@@ -2086,6 +2239,7 @@ std::unique_ptr<OperationPass<ModuleOp>> circt::createConvertMooreToCorePass() {
20862239
void MooreToCorePass::runOnOperation() {
20872240
MLIRContext &context = getContext();
20882241
ModuleOp module = getOperation();
2242+
ClassTypeCache classCache;
20892243

20902244
IRRewriter rewriter(module);
20912245
(void)mlir::eraseUnreachableBlocks(rewriter, module->getRegions());
@@ -2097,7 +2251,7 @@ void MooreToCorePass::runOnOperation() {
20972251
populateLegality(target, typeConverter);
20982252

20992253
ConversionPatternSet patterns(&context, typeConverter);
2100-
populateOpConversion(patterns, typeConverter);
2254+
populateOpConversion(patterns, typeConverter, classCache);
21012255

21022256
if (failed(applyFullConversion(module, target, std::move(patterns))))
21032257
signalPassFailure();
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// RUN: circt-opt %s --convert-moore-to-core --verify-diagnostics | FileCheck %s
2+
3+
/// Check that a classdecl gets noop'd and handles are lowered to !llvm.ptr
4+
5+
// CHECK-LABEL: func.func @ClassType(%arg0: !llvm.ptr) {
6+
// CHECK: return
7+
// CHECK: }
8+
// CHECK-NOT: moore.class.classdecl
9+
// CHECK-NOT: moore.class<@PropertyCombo>
10+
11+
moore.class.classdecl @PropertyCombo {
12+
moore.class.propertydecl @pubAutoI32 : !moore.i32
13+
moore.class.propertydecl @protStatL18 : !moore.l18
14+
moore.class.propertydecl @localAutoI32 : !moore.i32
15+
}
16+
17+
func.func @ClassType(%arg0: !moore.class<@PropertyCombo>) {
18+
return
19+
}

0 commit comments

Comments
 (0)