@@ -48,6 +48,129 @@ using llvm::SmallDenseSet;
4848
4949namespace {
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+
558701struct 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
19262074static 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() {
20862239void 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 ();
0 commit comments