Skip to content

Commit b4b774e

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 feb38f6 commit b4b774e

File tree

2 files changed

+215
-2
lines changed

2 files changed

+215
-2
lines changed

lib/Conversion/MooreToCore/MooreToCore.cpp

Lines changed: 185 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,158 @@ 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+
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+
552724
struct 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

19382115
static 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() {
20992281
void 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();
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// RUN: circt-opt %s --convert-moore-to-core --verify-diagnostics | FileCheck %s
2+
3+
/// Check that a classdecl gets translated to a llvm struct type
4+
5+
// CHECK-LABEL: module {
6+
// CHECK: llvm.mlir.global internal @__type_PropertyCombo() {addr_space = 0 : i32}
7+
// CHECK-SAME: : !llvm.struct<"moore.class.PropertyCombo", (i32, i18, i32)> {
8+
// CHECK: [[U:%.*]] = llvm.mlir.undef : !llvm.struct<"moore.class.PropertyCombo", (i32, i18, i32)>
9+
// CHECK: llvm.return [[U]] : !llvm.struct<"moore.class.PropertyCombo", (i32, i18, i32)>
10+
// CHECK: }
11+
// CHECK: }
12+
13+
// (Optional sanity: the class decl should be gone after conversion.)
14+
// CHECK-NOT: moore.class.classdecl
15+
16+
module {
17+
moore.class.classdecl @PropertyCombo {
18+
moore.class.propertydecl @pubAutoI32 : !moore.i32
19+
moore.class.propertydecl @protStatL18 : !moore.l18
20+
moore.class.propertydecl @localAutoI32 : !moore.i32
21+
}
22+
23+
llvm.mlir.global internal @__type_PropertyCombo()
24+
: !llvm.struct<"moore.class.PropertyCombo", (i32, i18, i32)> {
25+
%u = llvm.mlir.undef
26+
: !llvm.struct<"moore.class.PropertyCombo", (i32, i18, i32)>
27+
llvm.return %u
28+
: !llvm.struct<"moore.class.PropertyCombo", (i32, i18, i32)>
29+
}
30+
}

0 commit comments

Comments
 (0)