CIRCT 20.0.0git
Loading...
Searching...
No Matches
FreezePaths.cpp
Go to the documentation of this file.
1//===- FreezePaths.cpp - Freeze Paths pass ----------------------*- C++ -*-===//
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// Contains the definitions of the OM freeze paths pass.
10//
11//===----------------------------------------------------------------------===//
12
19#include "mlir/Pass/Pass.h"
20
21namespace circt {
22namespace om {
23#define GEN_PASS_DEF_FREEZEPATHS
24#include "circt/Dialect/OM/OMPasses.h.inc"
25} // namespace om
26} // namespace circt
27
28using namespace circt;
29using namespace om;
30
31namespace {
32struct PathVisitor {
33 PathVisitor(hw::InstanceGraph &instanceGraph, hw::InnerRefNamespace &irn,
34 std::function<StringAttr(Operation *)> &getOpName)
35 : instanceGraph(instanceGraph), irn(irn), getOpNameFallback(getOpName) {}
36
37 StringAttr field;
38 LogicalResult processPath(Location loc, hw::HierPathOp hierPathOp,
39 PathAttr &targetPath, StringAttr &bottomModule,
40 StringAttr &component, StringAttr &field);
41 LogicalResult process(BasePathCreateOp pathOp);
42 LogicalResult process(PathCreateOp pathOp);
43 LogicalResult process(EmptyPathOp pathOp);
44 LogicalResult processListCreator(Operation *listCreateOp);
45 LogicalResult process(ObjectFieldOp objectFieldOp);
46 LogicalResult run(ModuleOp module);
47 hw::InstanceGraph &instanceGraph;
49 std::function<StringAttr(Operation *)> getOpNameFallback;
50};
51} // namespace
52
53static LogicalResult getAccessPath(Location loc, Type type, size_t fieldId,
54 StringAttr &result) {
55 SmallString<64> field;
56 while (fieldId) {
57 if (auto aliasType = dyn_cast<hw::TypeAliasType>(type))
58 type = aliasType.getCanonicalType();
59 if (auto structType = dyn_cast<hw::StructType>(type)) {
60 auto index = structType.getIndexForFieldID(fieldId);
61 auto &element = structType.getElements()[index];
62 field.push_back('.');
63 llvm::append_range(field, element.name.getValue());
64 type = element.type;
65 fieldId -= structType.getFieldID(index);
66 } else if (auto arrayType = dyn_cast<hw::ArrayType>(type)) {
67 auto index = arrayType.getIndexForFieldID(fieldId);
68 field.push_back('[');
69 Twine(index).toVector(field);
70 field.push_back(']');
71 type = arrayType.getElementType();
72 fieldId -= arrayType.getFieldID(index);
73 } else {
74 return emitError(loc) << "can't create access path with fieldID "
75 << fieldId << " in type " << type;
76 }
77 }
78 result = StringAttr::get(loc->getContext(), field);
79 return success();
80}
81
82// Check if the element type of a potentially nested list includes path types.
83static bool hasPathType(Type type) {
84 bool isPathType = false;
85 type.walk([&](Type innerType) {
86 if (isa<BasePathType, PathType>(innerType))
87 isPathType = true;
88 });
89
90 return isPathType;
91}
92
93mlir::AttrTypeReplacer makeReplacer() {
94 mlir::AttrTypeReplacer replacer;
95 replacer.addReplacement([](BasePathType innerType) {
96 return FrozenBasePathType::get(innerType.getContext());
97 });
98 replacer.addReplacement([](PathType innerType) {
99 return FrozenPathType::get(innerType.getContext());
100 });
101 return replacer;
102}
103
104// Convert potentially nested lists of PathType or BasePathType to frozen lists.
105static Type processType(Type type) {
106 mlir::AttrTypeReplacer replacer = makeReplacer();
107 return replacer.replace(type);
108}
109
110LogicalResult PathVisitor::processPath(Location loc, hw::HierPathOp hierPathOp,
111 PathAttr &targetPath,
112 StringAttr &bottomModule,
113 StringAttr &component,
114 StringAttr &field) {
115 auto *context = hierPathOp->getContext();
116 // Look up the associated HierPathOp.
117 auto namepath = hierPathOp.getNamepathAttr().getValue();
118 SmallVector<PathElement> modules;
119
120 // Process the of the hierarchical path.
121 for (auto attr : namepath.drop_back()) {
122 auto innerRef = cast<hw::InnerRefAttr>(attr);
123 auto target = irn.lookup(innerRef);
124 assert(target.isOpOnly() && "can't target a port the middle of a namepath");
125 auto *op = target.getOp();
126 // Get the verilog name of the target.
127 auto verilogName = op->getAttrOfType<StringAttr>("hw.verilogName");
128 if (!verilogName && getOpNameFallback)
129 verilogName = getOpNameFallback(op);
130 if (!verilogName) {
131 auto diag = emitError(loc, "component does not have verilog name");
132 diag.attachNote(op->getLoc()) << "component here";
133 return diag;
134 }
135 modules.emplace_back(innerRef.getModule(), verilogName);
136 }
137
138 // Process the final target.
139 auto &end = namepath.back();
140 if (auto innerRef = dyn_cast<hw::InnerRefAttr>(end)) {
141 auto target = irn.lookup(innerRef);
142 if (target.isPort()) {
143 // We are targeting the port of a module.
144 auto module = cast<hw::HWModuleLike>(target.getOp());
145 auto index = target.getPort();
146 bottomModule = module.getModuleNameAttr();
147 component = StringAttr::get(context, module.getPortName(index));
148 auto loc = module.getPortLoc(index);
149 auto type = module.getPortTypes()[index];
150 if (failed(getAccessPath(loc, type, target.getField(), field)))
151 return failure();
152 } else {
153 auto *op = target.getOp();
154 assert(op && "innerRef should be targeting something");
155 // Get the current module.
156 auto currentModule = innerRef.getModule();
157 // Get the verilog name of the target.
158 auto verilogName = op->getAttrOfType<StringAttr>("hw.verilogName");
159 if (!verilogName && getOpNameFallback)
160 verilogName = getOpNameFallback(op);
161 if (!verilogName) {
162 auto diag = emitError(loc, "component does not have verilog name");
163 diag.attachNote(op->getLoc()) << "component here";
164 return diag;
165 }
166 // If this is our inner ref pair: [Foo::bar]
167 // if "bar" is an instance, modules = [Foo::bar], bottomModule = Bar.
168 // if "bar" is a wire, modules = [], bottomModule = Foo, component = bar.
169 if (auto inst = dyn_cast<hw::HWInstanceLike>(op)) {
170 // TODO: add support for instance choices.
171 auto mods = inst.getReferencedModuleNamesAttr();
172 if (mods.size() > 1)
173 return op->emitError("unsupported instance operation");
174 // We are targeting an instance.
175 modules.emplace_back(currentModule, verilogName);
176 bottomModule = cast<StringAttr>(mods[0]);
177 component = StringAttr::get(context, "");
178 field = StringAttr::get(context, "");
179 } else {
180 // We are targeting a regular component.
181 bottomModule = currentModule;
182 component = verilogName;
183 auto innerSym = cast<hw::InnerSymbolOpInterface>(op);
184 auto value = innerSym.getTargetResult();
185 if (failed(getAccessPath(value.getLoc(), value.getType(),
186 target.getField(), field)))
187 return failure();
188 }
189 }
190 } else {
191 // We are targeting a module.
192 auto symbolRef = cast<FlatSymbolRefAttr>(end);
193 bottomModule = symbolRef.getAttr();
194 component = StringAttr::get(context, "");
195 field = StringAttr::get(context, "");
196 }
197
198 // Create the target path.
199 targetPath = PathAttr::get(context, modules);
200 return success();
201}
202
203LogicalResult PathVisitor::process(PathCreateOp path) {
204 auto hierPathOp =
205 irn.symTable.lookup<hw::HierPathOp>(path.getTargetAttr().getAttr());
206 PathAttr targetPath;
207 StringAttr bottomModule;
208 StringAttr ref;
209 StringAttr field;
210 if (failed(processPath(path.getLoc(), hierPathOp, targetPath, bottomModule,
211 ref, field)))
212 return failure();
213
214 // Replace the old path operation.
215 OpBuilder builder(path);
216 auto frozenPath = builder.create<FrozenPathCreateOp>(
217 path.getLoc(), path.getTargetKindAttr(), path->getOperand(0), targetPath,
218 bottomModule, ref, field);
219 path.replaceAllUsesWith(frozenPath.getResult());
220 path->erase();
221
222 return success();
223}
224
225LogicalResult PathVisitor::process(BasePathCreateOp path) {
226 auto hierPathOp =
227 irn.symTable.lookup<hw::HierPathOp>(path.getTargetAttr().getAttr());
228 PathAttr targetPath;
229 StringAttr bottomModule;
230 StringAttr ref;
231 StringAttr field;
232 if (failed(processPath(path.getLoc(), hierPathOp, targetPath, bottomModule,
233 ref, field)))
234 return failure();
235
236 if (!ref.empty())
237 return path->emitError("basepath must target an instance");
238 if (!field.empty())
239 return path->emitError("basepath must not target a field");
240
241 // Replace the old path operation.
242 OpBuilder builder(path);
243 auto frozenPath = builder.create<FrozenBasePathCreateOp>(
244 path.getLoc(), path->getOperand(0), targetPath);
245 path.replaceAllUsesWith(frozenPath.getResult());
246 path->erase();
247
248 return success();
249}
250
251LogicalResult PathVisitor::process(EmptyPathOp path) {
252 OpBuilder builder(path);
253 auto frozenPath = builder.create<FrozenEmptyPathOp>(path.getLoc());
254 path.replaceAllUsesWith(frozenPath.getResult());
255 path->erase();
256 return success();
257}
258
259/// Replace a ListCreateOp of path types with frozen path types.
260LogicalResult PathVisitor::processListCreator(Operation *listCreateOp) {
261 ListType listType = cast<ListType>(listCreateOp->getResult(0).getType());
262
263 // Check if there are any path types in the list(s).
264 if (!hasPathType(listType))
265 return success();
266
267 // Create a new ListType with frozen path types.
268 auto newListType = processType(listType);
269
270 // Create a new op with the result type updated to replace path types.
271 OpBuilder builder(listCreateOp);
272 auto *newListCreateOp = builder.create(
273 listCreateOp->getLoc(), listCreateOp->getName().getIdentifier(),
274 listCreateOp->getOperands(), {newListType});
275 listCreateOp->getResult(0).replaceAllUsesWith(newListCreateOp->getResult(0));
276 listCreateOp->erase();
277 return success();
278}
279
280/// Replace an ObjectFieldOp of path types with frozen path types.
281LogicalResult PathVisitor::process(ObjectFieldOp objectFieldOp) {
282 Type resultType = objectFieldOp.getResult().getType();
283
284 // Check if there are any path types in the field.
285 if (!hasPathType(resultType))
286 return success();
287
288 // Create a new result Type with frozen path types.
289 auto newResultType = processType(resultType);
290
291 // Create a new op with the result type updated to replace path types.
292 OpBuilder builder(objectFieldOp);
293 auto newObjectFieldOp = builder.create<ObjectFieldOp>(
294 objectFieldOp.getLoc(), newResultType, objectFieldOp.getObject(),
295 objectFieldOp.getFieldPath());
296 objectFieldOp.replaceAllUsesWith(newObjectFieldOp.getResult());
297 objectFieldOp->erase();
298 return success();
299}
300
301LogicalResult PathVisitor::run(ModuleOp module) {
302 auto updatePathType = [&](Value value) {
303 if (hasPathType(value.getType()))
304 value.setType(processType(value.getType()));
305 };
306 for (auto classLike : module.getOps<ClassLike>()) {
307 // Transform PathType block argument to FrozenPathType.
308 for (auto arg : classLike.getBodyBlock()->getArguments())
309 updatePathType(arg);
310 auto result = classLike->walk([&](Operation *op) -> WalkResult {
311 if (auto basePath = dyn_cast<BasePathCreateOp>(op)) {
312 if (failed(process(basePath)))
313 return WalkResult::interrupt();
314 } else if (auto path = dyn_cast<PathCreateOp>(op)) {
315 if (failed(process(path)))
316 return WalkResult::interrupt();
317 } else if (auto path = dyn_cast<EmptyPathOp>(op)) {
318 if (failed(process(path)))
319 return WalkResult::interrupt();
320 } else if (isa<ListCreateOp, ListConcatOp>(op)) {
321 if (failed(processListCreator(op)))
322 return WalkResult::interrupt();
323 } else if (auto objectField = dyn_cast<ObjectFieldOp>(op)) {
324 if (failed(process(objectField)))
325 return WalkResult::interrupt();
326 }
327 return WalkResult::advance();
328 });
329 if (result.wasInterrupted())
330 return failure();
331
332 // Transform field types
333 classLike.replaceFieldTypes(makeReplacer());
334 }
335 return success();
336}
337
338namespace {
339struct FreezePathsPass
340 : public circt::om::impl::FreezePathsBase<FreezePathsPass> {
341 FreezePathsPass(std::function<StringAttr(Operation *)> getOpName)
342 : getOpName(std::move(getOpName)) {}
343 void runOnOperation() override;
344
345 std::function<StringAttr(Operation *)> getOpName;
346};
347} // namespace
348
349void FreezePathsPass::runOnOperation() {
350 auto module = getOperation();
351 auto &instanceGraph = getAnalysis<hw::InstanceGraph>();
352 mlir::SymbolTableCollection symbolTableCollection;
353 auto &symbolTable = getAnalysis<SymbolTable>();
354 hw::InnerSymbolTableCollection collection(module);
355 hw::InnerRefNamespace irn{symbolTable, collection};
356 if (failed(PathVisitor(instanceGraph, irn, getOpName).run(module)))
357 signalPassFailure();
358}
359
360std::unique_ptr<mlir::Pass> circt::om::createFreezePathsPass(
361 std::function<StringAttr(Operation *)> getOpName) {
362 return std::make_unique<FreezePathsPass>(getOpName);
363}
assert(baseType &&"element must be base type")
static bool hasPathType(Type type)
static LogicalResult getAccessPath(Location loc, Type type, size_t fieldId, StringAttr &result)
mlir::AttrTypeReplacer makeReplacer()
static Type processType(Type type)
static Block * getBodyBlock(FModuleLike mod)
This class represents a collection of InnerSymbolTable's.
HW-specific instance graph with a virtual entry node linking to all publicly visible modules.
std::unique_ptr< mlir::Pass > createFreezePathsPass(std::function< StringAttr(Operation *)> getOpNameFallback={})
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
int run(Type[Generator] generator=CppGenerator, cmdline_args=sys.argv)
Definition codegen.py:121
Definition om.py:1
This class represents the namespace in which InnerRef's can be resolved.