CIRCT 24.0.0git
Loading...
Searching...
No Matches
CreateVTables.cpp
Go to the documentation of this file.
1//===- CreateVTables.cpp - Create VTables from ClassDeclOps --------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file defines the CreateVTables pass.
10//
11//===----------------------------------------------------------------------===//
12
15
16namespace circt {
17namespace moore {
18#define GEN_PASS_DEF_CREATEVTABLES
19#include "circt/Dialect/Moore/MoorePasses.h.inc"
20} // namespace moore
21} // namespace circt
22
23using namespace circt;
24using namespace moore;
25using namespace mlir;
26
27namespace {
28using MethodMap = llvm::DenseMap<StringRef, std::optional<SymbolRefAttr>>;
29using ClassMap = llvm::DenseMap<ClassDeclOp, MethodMap>;
30
31struct CreateVTablesPass
32 : public circt::moore::impl::CreateVTablesBase<CreateVTablesPass> {
33 void runOnOperation() override;
34
35private:
36 /// Cache that stores the most derived virtual method for each class
37 ClassMap classToMethodMap;
38 /// Helper function to collect vtable info for every top-level classdecl
39 void collectClasses(ModuleOp mod, SymbolTable &symTab);
40 /// Recursive helper function to determine most derived virtual method impl
41 /// per class
42 void collectClassDependencies(ModuleOp mod, SymbolTable &symTab,
43 ClassDeclOp &clsDecl,
44 SymbolRefAttr dependencyName);
45
46 /// Function to emit VTable computed in classToMethodMap
47 void emitVTablePerClass(ModuleOp mod, SymbolTable &symTab,
48 OpBuilder &builder);
49 void emitVTablePerDependencyClass(ModuleOp mod, SymbolTable &symTab,
50 OpBuilder &builder, ClassDeclOp &clsDecl,
51 SymbolRefAttr dependencyName);
52};
53} // namespace
54
55std::unique_ptr<mlir::Pass> circt::moore::createVTablesPass() {
56 return std::make_unique<CreateVTablesPass>();
57}
58
59void CreateVTablesPass::collectClassDependencies(ModuleOp mod,
60 SymbolTable &symTab,
61 ClassDeclOp &clsDecl,
62 SymbolRefAttr dependencyName) {
63 auto dependencyDecl =
64 symTab.lookupNearestSymbolFrom<ClassDeclOp>(mod, dependencyName);
65
66 // Due to skipping GenericClassDefSymbol now, the parameterized interface
67 // class(@Bar) will not be collected in the symbol table, such as:
68 // `interface class Bar #(parameter N); endclass` extracted from Chipsalliance
69 // class_test_26.
70 if (!dependencyDecl) {
71 return;
72 }
73
74 auto &clsMap = classToMethodMap[clsDecl];
75 for (auto methodDecl : dependencyDecl.getBody().getOps<ClassMethodDeclOp>()) {
76
77 // If a derived class already maps this method, continue
78 auto &mapEntry = clsMap[methodDecl.getSymName()];
79 if (mapEntry)
80 continue;
81
82 std::optional<SymbolRefAttr> impl;
83 if (methodDecl.getImpl().has_value()) {
84 impl = methodDecl.getImpl().value();
85 } else {
86 impl = std::nullopt;
87 }
88
89 mapEntry = impl;
90 }
91 if (dependencyDecl.getBase().has_value())
92 collectClassDependencies(mod, symTab, clsDecl,
93 dependencyDecl.getBase().value());
94 if (dependencyDecl.getImplementedInterfaces().has_value())
95 for (auto intf : dependencyDecl.getImplementedInterfacesAttr())
96 collectClassDependencies(mod, symTab, clsDecl, cast<SymbolRefAttr>(intf));
97}
98
99void CreateVTablesPass::collectClasses(ModuleOp mod, SymbolTable &symTab) {
100 for (auto clsDecl : mod.getBodyRegion().getOps<ClassDeclOp>()) {
101 auto &clsMap = classToMethodMap[clsDecl];
102 // Don't override if already filled
103 if (!clsMap.empty())
104 continue;
105 collectClassDependencies(mod, symTab, clsDecl, SymbolRefAttr::get(clsDecl));
106 }
107}
108
109static bool noneHaveImpl(const MethodMap &methods) {
110 return llvm::all_of(methods,
111 [](const auto &kv) { return !kv.second.has_value(); });
112}
113
114static bool allHaveImpl(const MethodMap &methods) {
115 return llvm::all_of(methods,
116 [](const auto &kv) { return kv.second.has_value(); });
117}
118
119static inline SymbolRefAttr getVTableName(ClassDeclOp &clsDecl) {
120
121 auto base = SymbolRefAttr::get(clsDecl); // e.g. @MyClass
122 auto suffix = mlir::FlatSymbolRefAttr::get(clsDecl.getContext(), "vtable");
123 auto vTableName = mlir::SymbolRefAttr::get(base.getRootReference(), {suffix});
124 return vTableName;
125}
126
127void CreateVTablesPass::emitVTablePerDependencyClass(
128 ModuleOp mod, SymbolTable &symTab, OpBuilder &builder, ClassDeclOp &clsDecl,
129 SymbolRefAttr dependencyName) {
130
131 auto dependencyDecl =
132 symTab.lookupNearestSymbolFrom<ClassDeclOp>(mod, dependencyName);
133
134 auto clsMethodMap = classToMethodMap[clsDecl];
135 auto depMethodMap = classToMethodMap[dependencyDecl];
136
137 // If the VTable would be empty, don't emit it.
138 if (depMethodMap.empty())
139 return;
140
141 auto vTableName = getVTableName(dependencyDecl);
142
143 auto clsVTable =
144 VTableOp::create(builder, dependencyDecl.getLoc(), vTableName);
145 auto &region = clsVTable.getRegion();
146 auto &block = region.emplaceBlock();
147
148 OpBuilder::InsertionGuard g(builder);
149 builder.setInsertionPointToEnd(&block);
150
151 // First emit base class if available
152 if (dependencyDecl.getBase().has_value())
153 emitVTablePerDependencyClass(mod, symTab, builder, clsDecl,
154 dependencyDecl.getBase().value());
155
156 // Next emit interface classes if available
157 if (dependencyDecl.getImplementedInterfaces().has_value())
158 for (auto intf : dependencyDecl.getImplementedInterfacesAttr())
159 emitVTablePerDependencyClass(mod, symTab, builder, clsDecl,
160 cast<SymbolRefAttr>(intf));
161
162 // Last, emit any own method symbol entries
163 for (auto methodDecl : dependencyDecl.getBody().getOps<ClassMethodDeclOp>()) {
164 auto methodName = methodDecl.getSymName();
165 VTableEntryOp::create(builder, methodDecl.getLoc(), methodName,
166 clsMethodMap[methodName].value());
167 }
168}
169
170void CreateVTablesPass::emitVTablePerClass(ModuleOp mod, SymbolTable &symTab,
171 OpBuilder &builder) {
172 // Check emission for every top-level class decl
173 for (auto [clsDecl, methodMap] : classToMethodMap) {
174
175 // Sanity check that either all methods are implemented or none are.
176 if (!(allHaveImpl(methodMap) || noneHaveImpl(methodMap))) {
177 clsDecl.emitError()
178 << "Class declaration " << clsDecl.getSymName()
179 << " is malformed; some methods are abstract and some are concrete, "
180 "which is not legal in System Verilog.";
181 return;
182 }
183
184 // Skip abstract classes
185 if (noneHaveImpl(methodMap))
186 continue;
187
188 auto vTableName = getVTableName(clsDecl);
189 // Don't try to emit a vtable if it already exists.
190 if (symTab.lookupNearestSymbolFrom<VTableOp>(mod, vTableName))
191 continue;
192
193 builder.setInsertionPointAfter(clsDecl);
194
195 emitVTablePerDependencyClass(mod, symTab, builder, clsDecl,
196 SymbolRefAttr::get(clsDecl));
197 }
198}
199
200void CreateVTablesPass::runOnOperation() {
201 ModuleOp mod = getOperation();
202 SymbolTable symTab(mod);
203 collectClasses(mod, symTab);
204 mlir::OpBuilder builder = mlir::OpBuilder::atBlockBegin(mod.getBody());
205 emitVTablePerClass(mod, symTab, builder);
206}
static bool noneHaveImpl(const MethodMap &methods)
static bool allHaveImpl(const MethodMap &methods)
static SymbolRefAttr getVTableName(ClassDeclOp &clsDecl)
std::unique_ptr< mlir::Pass > createVTablesPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.