CIRCT  20.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  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 process(ListCreateOp listCreateOp);
45  LogicalResult process(ObjectFieldOp objectFieldOp);
46  LogicalResult run(ModuleOp module);
47  hw::InstanceGraph &instanceGraph;
48  hw::InnerRefNamespace &irn;
49  std::function<StringAttr(Operation *)> getOpNameFallback;
50 };
51 } // namespace
52 
53 static 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.
83 static 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 
93 mlir::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.
105 static Type processType(Type type) {
106  mlir::AttrTypeReplacer replacer = makeReplacer();
107  return replacer.replace(type);
108 }
109 
110 LogicalResult 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 
203 LogicalResult 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 
225 LogicalResult 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 
251 LogicalResult 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.
260 LogicalResult PathVisitor::process(ListCreateOp listCreateOp) {
261  ListType listType = listCreateOp.getResult().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<ListCreateOp>(
273  listCreateOp.getLoc(), newListType, listCreateOp.getOperands());
274  listCreateOp.replaceAllUsesWith(newListCreateOp.getResult());
275  listCreateOp->erase();
276  return success();
277 }
278 
279 /// Replace an ObjectFieldOp of path types with frozen path types.
280 LogicalResult PathVisitor::process(ObjectFieldOp objectFieldOp) {
281  Type resultType = objectFieldOp.getResult().getType();
282 
283  // Check if there are any path types in the field.
284  if (!hasPathType(resultType))
285  return success();
286 
287  // Create a new result Type with frozen path types.
288  auto newResultType = processType(resultType);
289 
290  // Create a new op with the result type updated to replace path types.
291  OpBuilder builder(objectFieldOp);
292  auto newObjectFieldOp = builder.create<ObjectFieldOp>(
293  objectFieldOp.getLoc(), newResultType, objectFieldOp.getObject(),
294  objectFieldOp.getFieldPath());
295  objectFieldOp.replaceAllUsesWith(newObjectFieldOp.getResult());
296  objectFieldOp->erase();
297  return success();
298 }
299 
300 LogicalResult PathVisitor::run(ModuleOp module) {
301  auto updatePathType = [&](Value value) {
302  if (hasPathType(value.getType()))
303  value.setType(processType(value.getType()));
304  };
305  for (auto classLike : module.getOps<ClassLike>()) {
306  // Transform PathType block argument to FrozenPathType.
307  for (auto arg : classLike.getBodyBlock()->getArguments())
308  updatePathType(arg);
309  auto result = classLike->walk([&](Operation *op) -> WalkResult {
310  if (auto basePath = dyn_cast<BasePathCreateOp>(op)) {
311  if (failed(process(basePath)))
312  return WalkResult::interrupt();
313  } else if (auto path = dyn_cast<PathCreateOp>(op)) {
314  if (failed(process(path)))
315  return WalkResult::interrupt();
316  } else if (auto path = dyn_cast<EmptyPathOp>(op)) {
317  if (failed(process(path)))
318  return WalkResult::interrupt();
319  } else if (auto listCreate = dyn_cast<ListCreateOp>(op)) {
320  if (failed(process(listCreate)))
321  return WalkResult::interrupt();
322  } else if (auto objectField = dyn_cast<ObjectFieldOp>(op)) {
323  if (failed(process(objectField)))
324  return WalkResult::interrupt();
325  }
326  return WalkResult::advance();
327  });
328  if (result.wasInterrupted())
329  return failure();
330 
331  // Transform field types
332  classLike.replaceFieldTypes(makeReplacer());
333  }
334  return success();
335 }
336 
337 namespace {
338 struct FreezePathsPass
339  : public circt::om::impl::FreezePathsBase<FreezePathsPass> {
340  FreezePathsPass(std::function<StringAttr(Operation *)> getOpName)
341  : getOpName(std::move(getOpName)) {}
342  void runOnOperation() override;
343 
344  std::function<StringAttr(Operation *)> getOpName;
345 };
346 } // namespace
347 
348 void FreezePathsPass::runOnOperation() {
349  auto module = getOperation();
350  auto &instanceGraph = getAnalysis<hw::InstanceGraph>();
351  mlir::SymbolTableCollection symbolTableCollection;
352  auto &symbolTable = getAnalysis<SymbolTable>();
353  hw::InnerSymbolTableCollection collection(module);
354  hw::InnerRefNamespace irn{symbolTable, collection};
355  if (failed(PathVisitor(instanceGraph, irn, getOpName).run(module)))
356  signalPassFailure();
357 }
358 
359 std::unique_ptr<mlir::Pass> circt::om::createFreezePathsPass(
360  std::function<StringAttr(Operation *)> getOpName) {
361  return std::make_unique<FreezePathsPass>(getOpName);
362 }
assert(baseType &&"element must be base type")
static bool hasPathType(Type type)
Definition: FreezePaths.cpp:83
static LogicalResult getAccessPath(Location loc, Type type, size_t fieldId, StringAttr &result)
Definition: FreezePaths.cpp:53
mlir::AttrTypeReplacer makeReplacer()
Definition: FreezePaths.cpp:93
static Type processType(Type type)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
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(std::function< StringAttr(Operation *)> getOpNameFallback={})
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
int run(Type[Generator] generator=CppGenerator, cmdline_args=sys.argv)
Definition: codegen.py:121
Definition: om.py:1