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