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