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 processListCreator(Operation *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::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.
281 LogicalResult 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 
301 LogicalResult 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 
338 namespace {
339 struct 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 
349 void 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 
360 std::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)
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