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