@@ -48,6 +48,158 @@ 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+ mlir::LLVM::LLVMStructType classBody;
55+
56+ // field name -> GEP path inside ident (excluding the leading pointer index)
57+ llvm::DenseMap<llvm::StringRef, llvm::SmallVector<unsigned , 2 >>
58+ propertyPath;
59+
60+ // TODO: Add classVTable in here.
61+ bool isFinalized = false ;
62+
63+ // / Record/overwrite the field path to a single property for a class.
64+ void setFieldPath (llvm::StringRef propertyName,
65+ llvm::ArrayRef<unsigned > path) {
66+ this ->propertyPath [propertyName] =
67+ llvm::SmallVector<unsigned , 2 >(path.begin (), path.end ());
68+ }
69+
70+ // / Lookup the full GEP path for a (class, field).
71+ std::optional<llvm::ArrayRef<unsigned >>
72+ getFieldPath (mlir::FlatSymbolRefAttr propertySym) const {
73+ if (auto prop = this ->propertyPath .find (propertySym.getValue ());
74+ prop != this ->propertyPath .end ())
75+ return llvm::ArrayRef<unsigned >(prop->second );
76+ return std::nullopt ;
77+ }
78+ };
79+
80+ // / Record the identified struct body for a class.
81+ // / Implicitly finalizes the class to struct conversion.
82+ void setClassBody (mlir::SymbolRefAttr classSym,
83+ mlir::LLVM::LLVMStructType &classBody) {
84+ classToStructMap[classSym].classBody = classBody;
85+ classToStructMap[classSym].isFinalized = true ;
86+ }
87+
88+ // / Lookup the identified struct body for a class.
89+ std::optional<ClassStructInfo>
90+ getStructInfo (mlir::SymbolRefAttr classSym) const {
91+ if (auto it = classToStructMap.find (classSym); it != classToStructMap.end ())
92+ return it->second ;
93+ return std::nullopt ;
94+ }
95+
96+ private:
97+ // Keyed by the SymbolRefAttr of the class.
98+ // Kept private so all accesses are done with helpers which preserve
99+ // invariants
100+ llvm::DenseMap<mlir::Attribute, ClassStructInfo> classToStructMap;
101+ };
102+
103+ // / Helper function to create an LLVM-friendly name from a class name symbol
104+ // / Classname is expected to be in a H1::H2::H3::@C format.
105+ static std::string mangleClassName (SymbolRefAttr className) {
106+ std::string s;
107+ llvm::raw_string_ostream os (s);
108+ // Add a constant prefix "moore.class."
109+ os << " moore.class." ;
110+
111+ StringRef root = className.getRootReference ();
112+ SmallVector<StringRef> parts;
113+ root.split (parts, " ::" );
114+
115+ for (size_t i = 0 ; i < parts.size (); ++i) {
116+ if (i)
117+ os << ' .' ;
118+ os << parts[i];
119+ }
120+
121+ return os.str ();
122+ }
123+
124+ // / Helper function to create an opaque LLVM Struct Type which corresponds
125+ // / to the sym
126+ static LLVM::LLVMStructType getOrCreateOpaqueStruct (MLIRContext *ctx,
127+ SymbolRefAttr className) {
128+ auto name = mangleClassName (className);
129+ if (auto existing = LLVM::LLVMStructType::getIdentified (ctx, name))
130+ return existing;
131+ return LLVM::LLVMStructType::getIdentified (ctx, name);
132+ }
133+
134+ static LogicalResult resolveClassStructBody (circt::moore::ClassDeclOp op,
135+ TypeConverter const &typeConverter,
136+ ClassTypeCache &cache) {
137+
138+ auto classSym = SymbolRefAttr::get (op.getSymNameAttr ());
139+ auto structInfo = cache.getStructInfo (classSym);
140+ if (structInfo && structInfo->isFinalized )
141+ // We already have a resolved class struct body.
142+ return success ();
143+
144+ // Otherwise we need to resolve.
145+ ClassTypeCache::ClassStructInfo structBody;
146+ SmallVector<Type> structBodyMembers;
147+
148+ // Base-first (prefix) layout for single inheritance.
149+ unsigned derivedStartIdx = 0 ;
150+
151+ if (auto baseClass = op.getBaseAttr ()) {
152+
153+ // Process base class' struct layout first
154+ 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+
159+ structBodyMembers.push_back (baseClassStruct->classBody );
160+ derivedStartIdx = 1 ;
161+
162+ // Inherit base field paths with a leading 0.
163+ for (auto &kv : baseClassStruct->propertyPath ) {
164+ llvm::SmallVector<unsigned , 2 > path;
165+ path.push_back (0 ); // into base subobject
166+ path.append (kv.second .begin (), kv.second .end ());
167+ structBody.setFieldPath (kv.first , path);
168+ }
169+ }
170+
171+ // Properties in source order.
172+ unsigned iterator = derivedStartIdx;
173+ auto &block = op.getBody ().front ();
174+ for (Operation &child : block) {
175+ if (auto prop = dyn_cast<circt::moore::ClassPropertyDeclOp>(child)) {
176+ Type mooreTy = prop.getPropertyType ();
177+ Type llvmTy = typeConverter.convertType (mooreTy);
178+ if (!llvmTy)
179+ return prop.emitOpError ()
180+ << " failed to convert property type " << mooreTy;
181+
182+ structBodyMembers.push_back (llvmTy);
183+
184+ // Derived field path: either {i} or {1+i} if base is present.
185+ llvm::SmallVector<unsigned , 2 > path{iterator};
186+ structBody.setFieldPath (prop.getSymName (), path);
187+ ++iterator;
188+ }
189+ }
190+
191+ // TODO: Handle vtable generation over ClassMethodDeclOp here.
192+ auto llvmStructTy = getOrCreateOpaqueStruct (op.getContext (), classSym);
193+ // Empty structs may be kept opaque
194+ if (!structBodyMembers.empty () &&
195+ failed (llvmStructTy.setBody (structBodyMembers, false )))
196+ return op.emitOpError () << " Failed to set LLVM Struct body" ;
197+
198+ cache.setClassBody (classSym, llvmStructTy);
199+
200+ return success ();
201+ }
202+
51203// / Returns the passed value if the integer width is already correct.
52204// / Zero-extends if it is too narrow.
53205// / Truncates if the integer is too wide and the truncated part is zero, if it
@@ -549,6 +701,26 @@ static Value createZeroValue(Type type, Location loc,
549701 return rewriter.createOrFold <hw::BitcastOp>(loc, type, constZero);
550702}
551703
704+ struct ClassDeclOpConversion : public OpConversionPattern <ClassDeclOp> {
705+ ClassDeclOpConversion (TypeConverter &tc, MLIRContext *ctx,
706+ ClassTypeCache &cache)
707+ : OpConversionPattern<ClassDeclOp>(tc, ctx), cache(cache) {}
708+
709+ LogicalResult
710+ matchAndRewrite (ClassDeclOp op, OpAdaptor,
711+ ConversionPatternRewriter &rewriter) const override {
712+ if (failed (resolveClassStructBody (op, *typeConverter, cache)))
713+ return failure ();
714+
715+ // The declaration itself is a no-op
716+ rewriter.eraseOp (op);
717+ return success ();
718+ }
719+
720+ private:
721+ ClassTypeCache &cache; // shared, owned by the pass
722+ };
723+
552724struct VariableOpConversion : public OpConversionPattern <VariableOp> {
553725 using OpConversionPattern::OpConversionPattern;
554726
@@ -1876,6 +2048,11 @@ static void populateTypeConversion(TypeConverter &typeConverter) {
18762048 typeConverter.addConversion (
18772049 [](LLVM::LLVMPointerType t) -> std::optional<Type> { return t; });
18782050
2051+ // ClassHandleType -> !llvm.ptr
2052+ typeConverter.addConversion ([&](ClassHandleType type) -> std::optional<Type> {
2053+ return mlir::LLVM::LLVMPointerType::get (type.getContext ());
2054+ });
2055+
18792056 typeConverter.addConversion ([&](RefType type) -> std::optional<Type> {
18802057 if (auto innerType = typeConverter.convertType (type.getNestedType ()))
18812058 return llhd::RefType::get (innerType);
@@ -1936,7 +2113,12 @@ static void populateTypeConversion(TypeConverter &typeConverter) {
19362113}
19372114
19382115static void populateOpConversion (ConversionPatternSet &patterns,
1939- TypeConverter &typeConverter) {
2116+ TypeConverter &typeConverter,
2117+ ClassTypeCache &classCache) {
2118+
2119+ patterns.add <ClassDeclOpConversion>(typeConverter, patterns.getContext (),
2120+ classCache);
2121+
19402122 // clang-format off
19412123 patterns.add <
19422124 // Patterns of declaration operations.
@@ -2099,6 +2281,7 @@ std::unique_ptr<OperationPass<ModuleOp>> circt::createConvertMooreToCorePass() {
20992281void MooreToCorePass::runOnOperation () {
21002282 MLIRContext &context = getContext ();
21012283 ModuleOp module = getOperation ();
2284+ ClassTypeCache classCache;
21022285
21032286 IRRewriter rewriter (module );
21042287 (void )mlir::eraseUnreachableBlocks (rewriter, module ->getRegions ());
@@ -2110,7 +2293,7 @@ void MooreToCorePass::runOnOperation() {
21102293 populateLegality (target, typeConverter);
21112294
21122295 ConversionPatternSet patterns (&context, typeConverter);
2113- populateOpConversion (patterns, typeConverter);
2296+ populateOpConversion (patterns, typeConverter, classCache );
21142297
21152298 if (failed (applyFullConversion (module , target, std::move (patterns))))
21162299 signalPassFailure ();
0 commit comments