CIRCT 22.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 auto &clsMap = classToMethodMap[clsDecl];
68 for (auto methodDecl : dependencyDecl.getBody().getOps<ClassMethodDeclOp>()) {
69
70 // If a derived class already maps this method, continue
71 auto &mapEntry = clsMap[methodDecl.getSymName()];
72 if (mapEntry)
73 continue;
74
75 std::optional<SymbolRefAttr> impl;
76 if (methodDecl.getImpl().has_value()) {
77 impl = methodDecl.getImpl().value();
78 } else {
79 impl = std::nullopt;
80 }
81
82 mapEntry = impl;
83 }
84 if (dependencyDecl.getBase().has_value())
85 collectClassDependencies(mod, symTab, clsDecl,
86 dependencyDecl.getBase().value());
87 if (dependencyDecl.getImplementedInterfaces().has_value())
88 for (auto intf : dependencyDecl.getImplementedInterfacesAttr())
89 collectClassDependencies(mod, symTab, clsDecl, cast<SymbolRefAttr>(intf));
90}
91
92void CreateVTablesPass::collectClasses(ModuleOp mod, SymbolTable &symTab) {
93 for (auto clsDecl : mod.getBodyRegion().getOps<ClassDeclOp>()) {
94 auto &clsMap = classToMethodMap[clsDecl];
95 // Don't override if already filled
96 if (!clsMap.empty())
97 continue;
98 collectClassDependencies(mod, symTab, clsDecl, SymbolRefAttr::get(clsDecl));
99 }
100}
101
102static bool noneHaveImpl(const MethodMap &methods) {
103 return llvm::all_of(methods,
104 [](const auto &kv) { return !kv.second.has_value(); });
105}
106
107static bool allHaveImpl(const MethodMap &methods) {
108 return llvm::all_of(methods,
109 [](const auto &kv) { return kv.second.has_value(); });
110}
111
112static inline SymbolRefAttr getVTableName(ClassDeclOp &clsDecl) {
113
114 auto base = SymbolRefAttr::get(clsDecl); // e.g. @MyClass
115 auto suffix = mlir::FlatSymbolRefAttr::get(clsDecl.getContext(), "vtable");
116 auto vTableName = mlir::SymbolRefAttr::get(base.getRootReference(), {suffix});
117 return vTableName;
118}
119
120void CreateVTablesPass::emitVTablePerDependencyClass(
121 ModuleOp mod, SymbolTable &symTab, OpBuilder &builder, ClassDeclOp &clsDecl,
122 SymbolRefAttr dependencyName) {
123
124 auto dependencyDecl =
125 symTab.lookupNearestSymbolFrom<ClassDeclOp>(mod, dependencyName);
126
127 auto clsMethodMap = classToMethodMap[clsDecl];
128 auto depMethodMap = classToMethodMap[dependencyDecl];
129
130 // If the VTable would be empty, don't emit it.
131 if (depMethodMap.empty())
132 return;
133
134 auto vTableName = getVTableName(dependencyDecl);
135
136 auto clsVTable =
137 VTableOp::create(builder, dependencyDecl.getLoc(), vTableName);
138 auto &region = clsVTable.getRegion();
139 auto &block = region.emplaceBlock();
140
141 OpBuilder::InsertionGuard g(builder);
142 builder.setInsertionPointToEnd(&block);
143
144 // First emit base class if available
145 if (dependencyDecl.getBase().has_value())
146 emitVTablePerDependencyClass(mod, symTab, builder, clsDecl,
147 dependencyDecl.getBase().value());
148
149 // Next emit interface classes if available
150 if (dependencyDecl.getImplementedInterfaces().has_value())
151 for (auto intf : dependencyDecl.getImplementedInterfacesAttr())
152 emitVTablePerDependencyClass(mod, symTab, builder, clsDecl,
153 cast<SymbolRefAttr>(intf));
154
155 // Last, emit any own method symbol entries
156 for (auto methodDecl : dependencyDecl.getBody().getOps<ClassMethodDeclOp>()) {
157 auto methodName = methodDecl.getSymName();
158 VTableEntryOp::create(builder, methodDecl.getLoc(), methodName,
159 clsMethodMap[methodName].value());
160 }
161}
162
163void CreateVTablesPass::emitVTablePerClass(ModuleOp mod, SymbolTable &symTab,
164 OpBuilder &builder) {
165 // Check emission for every top-level class decl
166 for (auto [clsDecl, methodMap] : classToMethodMap) {
167
168 // Sanity check that either all methods are implemented or none are.
169 if (!(allHaveImpl(methodMap) || noneHaveImpl(methodMap))) {
170 clsDecl.emitError()
171 << "Class declaration " << clsDecl.getSymName()
172 << " is malformed; some methods are abstract and some are concrete, "
173 "which is not legal in System Verilog.";
174 return;
175 }
176
177 // Skip abstract classes
178 if (noneHaveImpl(methodMap))
179 continue;
180
181 auto vTableName = getVTableName(clsDecl);
182 // Don't try to emit a vtable if it already exists.
183 if (symTab.lookupNearestSymbolFrom<VTableOp>(mod, vTableName))
184 continue;
185
186 builder.setInsertionPointAfter(clsDecl);
187
188 emitVTablePerDependencyClass(mod, symTab, builder, clsDecl,
189 SymbolRefAttr::get(clsDecl));
190 }
191}
192
193void CreateVTablesPass::runOnOperation() {
194 ModuleOp mod = getOperation();
195 SymbolTable symTab(mod);
196 collectClasses(mod, symTab);
197 mlir::OpBuilder builder = mlir::OpBuilder::atBlockBegin(mod.getBody());
198 emitVTablePerClass(mod, symTab, builder);
199}
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.