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 
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 listCreateOp);
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 // Check if the element type of a potentially nested list includes path types.
73 static bool hasPathType(Type type) {
74  bool isPathType = false;
75  type.walk([&](Type innerType) {
76  if (isa<BasePathType, PathType>(innerType))
77  isPathType = true;
78  });
79 
80  return isPathType;
81 }
82 
83 // Convert potentially nested lists of PathType or BasePathType to frozen lists.
84 static Type processType(Type type) {
85  mlir::AttrTypeReplacer replacer;
86  replacer.addReplacement([](BasePathType innerType) {
87  return FrozenBasePathType::get(innerType.getContext());
88  });
89  replacer.addReplacement([](PathType innerType) {
90  return FrozenPathType::get(innerType.getContext());
91  });
92 
93  return replacer.replace(type);
94 }
95 
96 LogicalResult PathVisitor::processPath(Location loc, hw::HierPathOp hierPathOp,
97  PathAttr &targetPath,
98  StringAttr &bottomModule,
99  StringAttr &component,
100  StringAttr &field) {
101  auto *context = hierPathOp->getContext();
102  // Look up the associated HierPathOp.
103  auto namepath = hierPathOp.getNamepathAttr().getValue();
104  SmallVector<PathElement> modules;
105 
106  // Process the of the hierarchical path.
107  for (auto attr : namepath.drop_back()) {
108  auto innerRef = cast<hw::InnerRefAttr>(attr);
109  auto target = irn.lookup(innerRef);
110  assert(target.isOpOnly() && "can't target a port the middle of a namepath");
111  auto *op = target.getOp();
112  // Get the verilog name of the target.
113  auto verilogName = op->getAttrOfType<StringAttr>("hw.verilogName");
114  if (!verilogName) {
115  auto diag = emitError(loc, "component does not have verilog name");
116  diag.attachNote(op->getLoc()) << "component here";
117  return diag;
118  }
119  modules.emplace_back(innerRef.getModule(), verilogName);
120  }
121 
122  // Process the final target.
123  auto &end = namepath.back();
124  if (auto innerRef = dyn_cast<hw::InnerRefAttr>(end)) {
125  auto target = irn.lookup(innerRef);
126  if (target.isPort()) {
127  // We are targeting the port of a module.
128  auto module = cast<hw::HWModuleLike>(target.getOp());
129  auto index = target.getPort();
130  bottomModule = module.getModuleNameAttr();
131  component = StringAttr::get(context, module.getPortName(index));
132  auto loc = module.getPortLoc(index);
133  auto type = module.getPortTypes()[index];
134  if (failed(getAccessPath(loc, type, target.getField(), field)))
135  return failure();
136  } else {
137  auto *op = target.getOp();
138  assert(op && "innerRef should be targeting something");
139  // Get the current module.
140  auto currentModule = innerRef.getModule();
141  // Get the verilog name of the target.
142  auto verilogName = op->getAttrOfType<StringAttr>("hw.verilogName");
143  if (!verilogName) {
144  auto diag = emitError(loc, "component does not have verilog name");
145  diag.attachNote(op->getLoc()) << "component here";
146  return diag;
147  }
148  // If this is our inner ref pair: [Foo::bar]
149  // if "bar" is an instance, modules = [Foo::bar], bottomModule = Bar.
150  // if "bar" is a wire, modules = [], bottomModule = Foo, component = bar.
151  if (isa<hw::HWInstanceLike>(op)) {
152  // TODO: add support for instance choices.
153  auto inst = dyn_cast<hw::InstanceOp>(op);
154  if (!inst)
155  return op->emitError("unsupported instance operation");
156  // We are targeting an instance.
157  modules.emplace_back(currentModule, verilogName);
158  bottomModule = inst.getReferencedModuleNameAttr();
159  component = StringAttr::get(context, "");
160  field = StringAttr::get(context, "");
161  } else {
162  // We are targeting a regular component.
163  bottomModule = currentModule;
164  component = verilogName;
165  auto innerSym = cast<hw::InnerSymbolOpInterface>(op);
166  auto value = innerSym.getTargetResult();
167  if (failed(getAccessPath(value.getLoc(), value.getType(),
168  target.getField(), field)))
169  return failure();
170  }
171  }
172  } else {
173  // We are targeting a module.
174  auto symbolRef = cast<FlatSymbolRefAttr>(end);
175  bottomModule = symbolRef.getAttr();
176  component = StringAttr::get(context, "");
177  field = StringAttr::get(context, "");
178  }
179 
180  // Create the target path.
181  targetPath = PathAttr::get(context, modules);
182  return success();
183 }
184 
185 LogicalResult PathVisitor::process(PathCreateOp path) {
186  auto hierPathOp =
187  irn.symTable.lookup<hw::HierPathOp>(path.getTargetAttr().getAttr());
188  PathAttr targetPath;
189  StringAttr bottomModule;
190  StringAttr ref;
191  StringAttr field;
192  if (failed(processPath(path.getLoc(), hierPathOp, targetPath, bottomModule,
193  ref, field)))
194  return failure();
195 
196  // Replace the old path operation.
197  OpBuilder builder(path);
198  auto frozenPath = builder.create<FrozenPathCreateOp>(
199  path.getLoc(), path.getTargetKindAttr(), path->getOperand(0), targetPath,
200  bottomModule, ref, field);
201  path.replaceAllUsesWith(frozenPath.getResult());
202  path->erase();
203 
204  return success();
205 }
206 
207 LogicalResult PathVisitor::process(BasePathCreateOp path) {
208  auto hierPathOp =
209  irn.symTable.lookup<hw::HierPathOp>(path.getTargetAttr().getAttr());
210  PathAttr targetPath;
211  StringAttr bottomModule;
212  StringAttr ref;
213  StringAttr field;
214  if (failed(processPath(path.getLoc(), hierPathOp, targetPath, bottomModule,
215  ref, field)))
216  return failure();
217 
218  if (!ref.empty())
219  return path->emitError("basepath must target an instance");
220  if (!field.empty())
221  return path->emitError("basepath must not target a field");
222 
223  // Replace the old path operation.
224  OpBuilder builder(path);
225  auto frozenPath = builder.create<FrozenBasePathCreateOp>(
226  path.getLoc(), path->getOperand(0), targetPath);
227  path.replaceAllUsesWith(frozenPath.getResult());
228  path->erase();
229 
230  return success();
231 }
232 
233 LogicalResult PathVisitor::process(EmptyPathOp path) {
234  OpBuilder builder(path);
235  auto frozenPath = builder.create<FrozenEmptyPathOp>(path.getLoc());
236  path.replaceAllUsesWith(frozenPath.getResult());
237  path->erase();
238  return success();
239 }
240 
241 /// Replace a ListCreateOp of path types with frozen path types.
242 LogicalResult PathVisitor::process(ListCreateOp listCreateOp) {
243  ListType listType = listCreateOp.getResult().getType();
244 
245  // Check if there are any path types in the list(s).
246  if (!hasPathType(listType))
247  return success();
248 
249  // Create a new ListType with frozen path types.
250  auto newListType = processType(listType);
251 
252  // Create a new op with the result type updated to replace path types.
253  OpBuilder builder(listCreateOp);
254  auto newListCreateOp = builder.create<ListCreateOp>(
255  listCreateOp.getLoc(), newListType, listCreateOp.getOperands());
256  listCreateOp.replaceAllUsesWith(newListCreateOp.getResult());
257  listCreateOp->erase();
258  return success();
259 }
260 
261 LogicalResult PathVisitor::run(ModuleOp module) {
262  auto updatePathType = [&](Value value) {
263  if (hasPathType(value.getType()))
264  value.setType(processType(value.getType()));
265  };
266  for (auto classLike : module.getOps<ClassLike>()) {
267  // Transform PathType block argument to FrozenPathType.
268  for (auto arg : classLike.getBodyBlock()->getArguments())
269  updatePathType(arg);
270  auto result = classLike->walk([&](Operation *op) -> WalkResult {
271  if (auto basePath = dyn_cast<BasePathCreateOp>(op)) {
272  if (failed(process(basePath)))
273  return WalkResult::interrupt();
274  } else if (auto path = dyn_cast<PathCreateOp>(op)) {
275  if (failed(process(path)))
276  return WalkResult::interrupt();
277  } else if (auto path = dyn_cast<EmptyPathOp>(op)) {
278  if (failed(process(path)))
279  return WalkResult::interrupt();
280  } else if (auto listCreate = dyn_cast<ListCreateOp>(op)) {
281  if (failed(process(listCreate)))
282  return WalkResult::interrupt();
283  }
284  return WalkResult::advance();
285  });
286  if (result.wasInterrupted())
287  return failure();
288  }
289  return success();
290 }
291 
292 namespace {
293 struct FreezePathsPass : public FreezePathsBase<FreezePathsPass> {
294  void runOnOperation() override;
295 };
296 } // namespace
297 
298 void FreezePathsPass::runOnOperation() {
299  auto module = getOperation();
300  auto &instanceGraph = getAnalysis<hw::InstanceGraph>();
301  mlir::SymbolTableCollection symbolTableCollection;
302  auto &symbolTable = getAnalysis<SymbolTable>();
303  hw::InnerSymbolTableCollection collection(module);
304  hw::InnerRefNamespace irn{symbolTable, collection};
305  if (failed(PathVisitor(instanceGraph, irn).run(module)))
306  signalPassFailure();
307 }
308 
309 std::unique_ptr<mlir::Pass> circt::om::createFreezePathsPass() {
310  return std::make_unique<FreezePathsPass>();
311 }
assert(baseType &&"element must be base type")
static bool hasPathType(Type type)
Definition: FreezePaths.cpp:73
static LogicalResult getAccessPath(Location loc, Type type, size_t fieldId, StringAttr &result)
Definition: FreezePaths.cpp:43
static Type processType(Type type)
Definition: FreezePaths.cpp:84
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