CIRCT  18.0.0git
LowerClasses.cpp
Go to the documentation of this file.
1 //===- LowerClasses.cpp - Lower to OM classes and objects -----------------===//
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 // This file defines the LowerClasses pass.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "PassDetails.h"
20 #include "circt/Dialect/OM/OMOps.h"
21 #include "mlir/IR/BuiltinOps.h"
22 #include "mlir/IR/IRMapping.h"
23 #include "mlir/IR/PatternMatch.h"
24 #include "mlir/IR/Threading.h"
25 #include "mlir/Support/LogicalResult.h"
26 #include "mlir/Transforms/DialectConversion.h"
27 #include "llvm/ADT/STLExtras.h"
28 #include "llvm/Support/raw_ostream.h"
29 
30 using namespace mlir;
31 using namespace circt;
32 using namespace circt::firrtl;
33 using namespace circt::om;
34 
35 namespace {
36 
37 /// Helper class which holds a hierarchical path op reference and a pointer to
38 /// to the targeted operation.
39 struct PathInfo {
40  PathInfo() = default;
41  PathInfo(Operation *op, FlatSymbolRefAttr symRef) : op(op), symRef(symRef) {
42  assert(op && "op must not be null");
43  assert(symRef && "symRef must not be null");
44  }
45 
46  operator bool() const { return op != nullptr; }
47 
48  Operation *op = nullptr;
49  FlatSymbolRefAttr symRef = nullptr;
50 };
51 
52 /// Maps a FIRRTL path id to the lowered PathInfo.
53 using PathInfoTable = DenseMap<DistinctAttr, PathInfo>;
54 
55 /// The suffix to append to lowered module names.
56 static constexpr StringRef kClassNameSuffix = "_Class";
57 
58 /// Helper class to capture details about a property.
59 struct Property {
60  size_t index;
61  StringRef name;
62  Type type;
63  Location loc;
64 };
65 
66 /// Helper class to capture state about a Class being lowered.
67 struct ClassLoweringState {
68  FModuleLike moduleLike;
69  om::ClassLike classLike;
70 };
71 
72 struct LowerClassesPass : public LowerClassesBase<LowerClassesPass> {
73  void runOnOperation() override;
74 
75 private:
76  LogicalResult lowerPaths(PathInfoTable &pathInfoTable,
77  SymbolTable &symbolTable);
78 
79  // Predicate to check if a module-like needs a Class to be created.
80  bool shouldCreateClass(FModuleLike moduleLike);
81 
82  // Create an OM Class op from a FIRRTL Class op.
83  ClassLoweringState createClass(FModuleLike moduleLike);
84 
85  // Lower the FIRRTL Class to OM Class.
86  void lowerClassLike(ClassLoweringState state);
87  void lowerClass(om::ClassOp classOp, FModuleLike moduleLike);
88  void lowerClassExtern(ClassExternOp classExternOp, FModuleLike moduleLike);
89 
90  // Update Object instantiations in a FIRRTL Module or OM Class.
91  LogicalResult updateInstances(Operation *op, InstanceGraph &instanceGraph,
92  SymbolTable &symbolTable);
93 
94  // Convert to OM ops and types in Classes or Modules.
95  LogicalResult dialectConversion(
96  Operation *op, const PathInfoTable &pathInfoTable,
97  const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable);
98 };
99 
100 } // namespace
101 
102 /// This pass removes the OMIR tracker annotations from operations, and ensures
103 /// that each thing that was targeted has a hierarchical path targeting it. It
104 /// builds a table which maps the OMIR tracker annotation IDs to the
105 /// corresponding hierarchical paths. We use this table to convert FIRRTL path
106 /// ops to OM. FIRRTL paths refer to their target using a target ID, while OM
107 /// paths refer to their target using hierarchical paths.
108 LogicalResult LowerClassesPass::lowerPaths(PathInfoTable &pathInfoTable,
109  SymbolTable &symbolTable) {
110  auto *context = &getContext();
111  auto circuit = getOperation();
112  hw::InnerSymbolNamespaceCollection namespaces;
113  HierPathCache cache(circuit, symbolTable);
114 
115  auto processPathTrackers = [&](AnnoTarget target) -> LogicalResult {
116  auto error = false;
117  auto annotations = target.getAnnotations();
118  auto *op = target.getOp();
119  FModuleLike module;
120  annotations.removeAnnotations([&](Annotation anno) {
121  // If there has been an error, just skip this annotation.
122  if (error)
123  return false;
124 
125  // We are looking for OMIR tracker annotations.
126  if (!anno.isClass("circt.tracker"))
127  return false;
128 
129  // The token must have a valid ID.
130  auto id = anno.getMember<DistinctAttr>("id");
131  if (!id) {
132  op->emitError("circt.tracker annotation missing id field");
133  error = true;
134  return false;
135  }
136 
137  // Get the fieldID. If there is none, it is assumed to be 0.
138  uint64_t fieldID = anno.getFieldID();
139 
140  // Attach an inner sym to the operation.
141  Attribute targetSym;
142  if (auto portTarget = target.dyn_cast<PortAnnoTarget>()) {
143  targetSym = getInnerRefTo(
144  {portTarget.getPortNo(), portTarget.getOp(), fieldID},
145  [&](FModuleLike module) -> hw::InnerSymbolNamespace & {
146  return namespaces[module];
147  });
148  } else if (auto module = dyn_cast<FModuleLike>(op)) {
149  assert(!fieldID && "field not valid for modules");
150  targetSym = FlatSymbolRefAttr::get(module.getModuleNameAttr());
151  } else {
152  targetSym = getInnerRefTo(
153  {target.getOp(), fieldID},
154  [&](FModuleLike module) -> hw::InnerSymbolNamespace & {
155  return namespaces[module];
156  });
157  }
158 
159  // Create the hierarchical path.
160  SmallVector<Attribute> path;
161  if (auto hierName = anno.getMember<FlatSymbolRefAttr>("circt.nonlocal")) {
162  auto hierPathOp =
163  dyn_cast<hw::HierPathOp>(symbolTable.lookup(hierName.getAttr()));
164  if (!hierPathOp) {
165  op->emitError("annotation does not point at a HierPathOp");
166  error = true;
167  return false;
168  }
169  // Copy the old path, dropping the module name.
170  auto oldPath = hierPathOp.getNamepath().getValue();
171  llvm::append_range(path, oldPath.drop_back());
172  }
173  path.push_back(targetSym);
174 
175  // Create the HierPathOp.
176  auto pathAttr = ArrayAttr::get(context, path);
177  auto &pathInfo = pathInfoTable[id];
178  if (pathInfo) {
179  auto diag =
180  emitError(pathInfo.op->getLoc(), "duplicate identifier found");
181  diag.attachNote(op->getLoc()) << "other identifier here";
182  error = true;
183  return false;
184  }
185 
186  // Record the path operation associated with the path op.
187  pathInfo = {op, cache.getRefFor(pathAttr)};
188 
189  // Remove this annotation from the operation.
190  return true;
191  });
192 
193  if (error)
194  return failure();
195  target.setAnnotations(annotations);
196  return success();
197  };
198 
199  for (auto module : circuit.getOps<FModuleLike>()) {
200  // Process the module annotations.
201  if (failed(processPathTrackers(OpAnnoTarget(module))))
202  return failure();
203  // Process module port annotations.
204  for (unsigned i = 0, e = module.getNumPorts(); i < e; ++i)
205  if (failed(processPathTrackers(PortAnnoTarget(module, i))))
206  return failure();
207  // Process ops in the module body.
208  auto result = module.walk([&](hw::InnerSymbolOpInterface op) {
209  if (failed(processPathTrackers(OpAnnoTarget(op))))
210  return WalkResult::interrupt();
211  return WalkResult::advance();
212  });
213  if (result.wasInterrupted())
214  return failure();
215  }
216  return success();
217 }
218 
219 /// Lower FIRRTL Class and Object ops to OM Class and Object ops
220 void LowerClassesPass::runOnOperation() {
221  MLIRContext *ctx = &getContext();
222 
223  // Get the CircuitOp.
224  CircuitOp circuit = getOperation();
225 
226  // Get the InstanceGraph and SymbolTable.
227  InstanceGraph &instanceGraph = getAnalysis<InstanceGraph>();
228  SymbolTable &symbolTable = getAnalysis<SymbolTable>();
229 
230  // Rewrite all path annotations into inner symbol targets.
231  PathInfoTable pathInfoTable;
232  if (failed(lowerPaths(pathInfoTable, symbolTable))) {
233  signalPassFailure();
234  return;
235  }
236 
237  // Create new OM Class ops serially.
238  DenseMap<StringAttr, firrtl::ClassType> classTypeTable;
239  SmallVector<ClassLoweringState> loweringState;
240  for (auto moduleLike : circuit.getOps<FModuleLike>()) {
241  if (shouldCreateClass(moduleLike)) {
242  loweringState.push_back(createClass(moduleLike));
243  if (auto classLike =
244  dyn_cast<firrtl::ClassLike>(moduleLike.getOperation()))
245  classTypeTable[classLike.getNameAttr()] = classLike.getInstanceType();
246  }
247  }
248 
249  // Move ops from FIRRTL Class to OM Class in parallel.
250  mlir::parallelForEach(ctx, loweringState,
251  [this](auto state) { lowerClassLike(state); });
252 
253  // Completely erase Class module-likes
254  for (auto state : loweringState) {
255  if (isa<firrtl::ClassLike>(state.moduleLike.getOperation()))
256  state.moduleLike.erase();
257  }
258 
259  // Collect ops where Objects can be instantiated.
260  SmallVector<Operation *> objectContainers;
261  for (auto &op : circuit.getOps())
262  if (isa<FModuleOp, om::ClassLike>(op))
263  objectContainers.push_back(&op);
264 
265  // Update Object creation ops in Classes or Modules in parallel.
266  if (failed(mlir::failableParallelForEach(
267  ctx, objectContainers,
268  [this, &instanceGraph, &symbolTable](auto *op) {
269  return updateInstances(op, instanceGraph, symbolTable);
270  })))
271  return signalPassFailure();
272 
273  // Convert to OM ops and types in Classes or Modules in parallel.
274  if (failed(
275  mlir::failableParallelForEach(ctx, objectContainers, [&](auto *op) {
276  return dialectConversion(op, pathInfoTable, classTypeTable);
277  })))
278  return signalPassFailure();
279 
280  // We keep the instance graph up to date, so mark that analysis preserved.
281  markAnalysesPreserved<InstanceGraph>();
282 }
283 
284 std::unique_ptr<mlir::Pass> circt::firrtl::createLowerClassesPass() {
285  return std::make_unique<LowerClassesPass>();
286 }
287 
288 // Predicate to check if a module-like needs a Class to be created.
289 bool LowerClassesPass::shouldCreateClass(FModuleLike moduleLike) {
290  if (isa<firrtl::ClassLike>(moduleLike.getOperation()))
291  return true;
292 
293  // Always create a class for public modules.
294  if (moduleLike.isPublic())
295  return true;
296 
297  return llvm::any_of(moduleLike.getPorts(), [](PortInfo port) {
298  return isa<PropertyType>(port.type);
299  });
300 }
301 
302 // Create an OM Class op from a FIRRTL Class op or Module op with properties.
303 ClassLoweringState LowerClassesPass::createClass(FModuleLike moduleLike) {
304  // Collect the parameter names from input properties.
305  SmallVector<StringRef> formalParamNames;
306  // Every class gets a base path as its first parameter.
307  formalParamNames.emplace_back("basepath");
308  for (auto [index, port] : llvm::enumerate(moduleLike.getPorts()))
309  if (port.isInput() && isa<PropertyType>(port.type))
310  formalParamNames.push_back(port.name);
311 
312  OpBuilder builder = OpBuilder::atBlockEnd(getOperation().getBodyBlock());
313 
314  // Take the name from the FIRRTL Class or Module to create the OM Class name.
315  StringRef className = moduleLike.getName();
316 
317  // Use the defname for external modules.
318  if (auto externMod = dyn_cast<FExtModuleOp>(moduleLike.getOperation()))
319  if (auto defname = externMod.getDefname())
320  className = defname.value();
321 
322  // If the op is a Module or ExtModule, the OM Class would conflict with the HW
323  // Module, so give it a suffix. There is no formal ABI for this yet.
324  StringRef suffix =
325  isa<FModuleOp, FExtModuleOp>(moduleLike) ? kClassNameSuffix : "";
326 
327  // Construct the OM Class with the FIRRTL Class name and parameter names.
328  om::ClassLike loweredClassOp;
329  if (isa<firrtl::ExtClassOp, firrtl::FExtModuleOp>(moduleLike.getOperation()))
330  loweredClassOp = builder.create<om::ClassExternOp>(
331  moduleLike.getLoc(), className + suffix, formalParamNames);
332  else
333  loweredClassOp = builder.create<om::ClassOp>(
334  moduleLike.getLoc(), className + suffix, formalParamNames);
335 
336  return {moduleLike, loweredClassOp};
337 }
338 
339 void LowerClassesPass::lowerClassLike(ClassLoweringState state) {
340  auto moduleLike = state.moduleLike;
341  auto classLike = state.classLike;
342 
343  if (auto classOp = dyn_cast<om::ClassOp>(classLike.getOperation())) {
344  return lowerClass(classOp, moduleLike);
345  }
346  if (auto classExternOp =
347  dyn_cast<om::ClassExternOp>(classLike.getOperation())) {
348  return lowerClassExtern(classExternOp, moduleLike);
349  }
350  llvm_unreachable("unhandled class-like op");
351 }
352 
353 void LowerClassesPass::lowerClass(om::ClassOp classOp, FModuleLike moduleLike) {
354  // Map from Values in the FIRRTL Class to Values in the OM Class.
355  IRMapping mapping;
356 
357  // Collect information about property ports.
358  SmallVector<Property> inputProperties;
359  BitVector portsToErase(moduleLike.getNumPorts());
360  for (auto [index, port] : llvm::enumerate(moduleLike.getPorts())) {
361  // For Module ports that aren't property types, move along.
362  if (!isa<PropertyType>(port.type))
363  continue;
364 
365  // Remember input properties to create the OM Class formal parameters.
366  if (port.isInput())
367  inputProperties.push_back({index, port.name, port.type, port.loc});
368 
369  // In case this is a Module, remember to erase this port.
370  portsToErase.set(index);
371  }
372 
373  // Construct the OM Class body with block arguments for each input property,
374  // updating the mapping to map from the input property to the block argument.
375  Block *classBody = &classOp->getRegion(0).emplaceBlock();
376  // Every class created from a module gets a base path as its first parameter.
377  classBody->addArgument(BasePathType::get(&getContext()),
378  UnknownLoc::get(&getContext()));
379  for (auto inputProperty : inputProperties) {
380  BlockArgument parameterValue =
381  classBody->addArgument(inputProperty.type, inputProperty.loc);
382  BlockArgument inputValue =
383  moduleLike->getRegion(0).getArgument(inputProperty.index);
384  mapping.map(inputValue, parameterValue);
385  }
386 
387  // Clone the property ops from the FIRRTL Class or Module to the OM Class.
388  SmallVector<Operation *> opsToErase;
389  OpBuilder builder = OpBuilder::atBlockBegin(classOp.getBodyBlock());
390  for (auto &op : moduleLike->getRegion(0).getOps()) {
391  // Check if any operand is a property.
392  auto propertyOperands = llvm::any_of(op.getOperandTypes(), [](Type type) {
393  return isa<PropertyType>(type);
394  });
395 
396  // Check if any result is a property.
397  auto propertyResults = llvm::any_of(
398  op.getResultTypes(), [](Type type) { return isa<PropertyType>(type); });
399 
400  // If there are no properties here, move along.
401  if (!propertyOperands && !propertyResults)
402  continue;
403 
404  // Actually clone the op over to the OM Class.
405  builder.clone(op, mapping);
406 
407  // In case this is a Module, remember to erase this op, unless it is an
408  // instance. Instances are handled later in updateInstances.
409  if (!isa<InstanceOp>(op))
410  opsToErase.push_back(&op);
411  }
412 
413  // Convert any output property assignments to Field ops.
414  for (auto op : llvm::make_early_inc_range(classOp.getOps<PropAssignOp>())) {
415  // Property assignments will currently be pointing back to the original
416  // FIRRTL Class for output ports.
417  auto outputPort = dyn_cast<BlockArgument>(op.getDest());
418  if (!outputPort)
419  continue;
420 
421  // Get the original port name, create a Field, and erase the propassign.
422  auto name = moduleLike.getPortName(outputPort.getArgNumber());
423  builder.create<ClassFieldOp>(op.getLoc(), name, op.getSrc());
424  op.erase();
425  }
426 
427  // If the module-like is a Class, it will be completely erased later.
428  // Otherwise, erase just the property ports and ops.
429  if (!isa<firrtl::ClassLike>(moduleLike.getOperation())) {
430  // Erase ops in use before def order, thanks to FIRRTL's SSA regions.
431  for (auto *op : llvm::reverse(opsToErase))
432  op->erase();
433 
434  // Erase property typed ports.
435  moduleLike.erasePorts(portsToErase);
436  }
437 }
438 
439 void LowerClassesPass::lowerClassExtern(ClassExternOp classExternOp,
440  FModuleLike moduleLike) {
441  // Construct the OM Class body.
442  // Add a block arguments for each input property.
443  // Add a class.extern.field op for each output.
444  BitVector portsToErase(moduleLike.getNumPorts());
445  Block *classBody = &classExternOp.getRegion().emplaceBlock();
446  OpBuilder builder = OpBuilder::atBlockBegin(classBody);
447 
448  // Every class gets a base path as its first parameter.
449  classBody->addArgument(BasePathType::get(&getContext()),
450  UnknownLoc::get(&getContext()));
451 
452  for (unsigned i = 0, e = moduleLike.getNumPorts(); i < e; ++i) {
453  auto type = moduleLike.getPortType(i);
454  if (!isa<PropertyType>(type))
455  continue;
456 
457  auto loc = moduleLike.getPortLocation(i);
458  auto direction = moduleLike.getPortDirection(i);
459  if (direction == Direction::In)
460  classBody->addArgument(type, loc);
461  else {
462  auto name = moduleLike.getPortNameAttr(i);
463  builder.create<om::ClassExternFieldOp>(loc, name, type);
464  }
465 
466  // In case this is a Module, remember to erase this port.
467  portsToErase.set(i);
468  }
469 
470  // If the module-like is a Class, it will be completely erased later.
471  // Otherwise, erase just the property ports and ops.
472  if (!isa<firrtl::ClassLike>(moduleLike.getOperation())) {
473  // Erase property typed ports.
474  moduleLike.erasePorts(portsToErase);
475  }
476 }
477 
478 // Helper to update an Object instantiation. FIRRTL Object instances are
479 // converted to OM Object instances.
480 static LogicalResult
481 updateObjectInstance(firrtl::ObjectOp firrtlObject, OpBuilder &builder,
482  SmallVectorImpl<Operation *> &opsToErase) {
483  // The 0'th argument is the base path.
484  auto basePath = firrtlObject->getBlock()->getArgument(0);
485  // build a table mapping the indices of input ports to their position in the
486  // om class's parameter list.
487  auto firrtlClassType = firrtlObject.getType();
488  auto numElements = firrtlClassType.getNumElements();
489  llvm::SmallVector<unsigned> argIndexTable;
490  argIndexTable.resize(numElements);
491 
492  unsigned nextArgIndex = 1;
493 
494  for (unsigned i = 0; i < numElements; ++i) {
495  auto direction = firrtlClassType.getElement(i).direction;
496  if (direction == Direction::In)
497  argIndexTable[i] = nextArgIndex++;
498  }
499 
500  // Collect its input actual parameters by finding any subfield ops that are
501  // assigned to. Take the source of the assignment as the actual parameter.
502 
503  llvm::SmallVector<Value> args;
504  args.resize(nextArgIndex);
505  args[0] = basePath;
506 
507  for (auto *user : llvm::make_early_inc_range(firrtlObject->getUsers())) {
508  if (auto subfield = dyn_cast<ObjectSubfieldOp>(user)) {
509  auto index = subfield.getIndex();
510  auto direction = firrtlClassType.getElement(index).direction;
511 
512  // We only lower "writes to input ports" here. Reads from output
513  // ports will be handled using the conversion framework.
514  if (direction == Direction::Out)
515  continue;
516 
517  for (auto *subfieldUser :
518  llvm::make_early_inc_range(subfield->getUsers())) {
519  if (auto propassign = dyn_cast<PropAssignOp>(subfieldUser)) {
520  // the operands of the propassign may have already been converted to
521  // om. Use the generic operand getters to get the operands as
522  // untyped values.
523  auto dst = propassign.getOperand(0);
524  auto src = propassign.getOperand(1);
525  if (dst == subfield.getResult()) {
526  args[argIndexTable[index]] = src;
527  opsToErase.push_back(propassign);
528  }
529  }
530  }
531 
532  opsToErase.push_back(subfield);
533  }
534  }
535 
536  // Check that all input ports have been initialized.
537  for (unsigned i = 0; i < numElements; ++i) {
538  auto element = firrtlClassType.getElement(i);
539  if (element.direction == Direction::Out)
540  continue;
541 
542  auto argIndex = argIndexTable[i];
543  if (!args[argIndex])
544  return emitError(firrtlObject.getLoc())
545  << "uninitialized input port " << element.name;
546  }
547 
548  // Convert the FIRRTL Class type to an OM Class type.
549  auto className = firrtlObject.getType().getNameAttr();
550  auto classType = om::ClassType::get(firrtlObject->getContext(), className);
551 
552  // Create the new Object op.
553  builder.setInsertionPoint(firrtlObject);
554  auto object = builder.create<om::ObjectOp>(
555  firrtlObject.getLoc(), classType, firrtlObject.getClassNameAttr(), args);
556 
557  // Replace uses of the FIRRTL Object with the OM Object. The later dialect
558  // conversion will take care of converting the types.
559  firrtlObject.replaceAllUsesWith(object.getResult());
560 
561  // Erase the original Object, now that we're done with it.
562  opsToErase.push_back(firrtlObject);
563 
564  return success();
565 }
566 
567 // Helper to update a Module instantiation in a Class. Module instances within a
568 // Class are converted to OM Object instances of the Class derived from the
569 // Module.
570 static LogicalResult
571 updateModuleInstanceClass(InstanceOp firrtlInstance, OpBuilder &builder,
572  SmallVectorImpl<Operation *> &opsToErase,
573  SymbolTable &symbolTable) {
574  // Collect the FIRRTL instance inputs to form the Object instance actual
575  // parameters. The order of the SmallVector needs to match the order the
576  // formal parameters are declared on the corresponding Class.
577  SmallVector<Value> actualParameters;
578  // The 0'th argument is the base path.
579  actualParameters.push_back(firrtlInstance->getBlock()->getArgument(0));
580  for (auto result : firrtlInstance.getResults()) {
581  // If the port is an output, continue.
582  if (firrtlInstance.getPortDirection(result.getResultNumber()) ==
583  Direction::Out)
584  continue;
585 
586  // If the port is not a property type, continue.
587  auto propertyResult = dyn_cast<FIRRTLPropertyValue>(result);
588  if (!propertyResult)
589  continue;
590 
591  // Get the property assignment to the input, and track the assigned
592  // Value as an actual parameter to the Object instance.
593  auto propertyAssignment = getPropertyAssignment(propertyResult);
594  assert(propertyAssignment && "properties require single assignment");
595  actualParameters.push_back(propertyAssignment.getSrc());
596 
597  // Erase the property assignment.
598  opsToErase.push_back(propertyAssignment);
599  }
600 
601  // Get the referenced module to get its name.
602  auto referencedModule =
603  dyn_cast<FModuleLike>(firrtlInstance.getReferencedModule(symbolTable));
604 
605  StringRef moduleName = referencedModule.getName();
606 
607  // Use the defname for external modules.
608  if (auto externMod = dyn_cast<FExtModuleOp>(referencedModule.getOperation()))
609  if (auto defname = externMod.getDefname())
610  moduleName = defname.value();
611 
612  // Convert the FIRRTL Module name to an OM Class type.
613  auto className = FlatSymbolRefAttr::get(
614  builder.getStringAttr(moduleName + kClassNameSuffix));
615 
616  auto classType = om::ClassType::get(firrtlInstance->getContext(), className);
617 
618  // Create the new Object op.
619  builder.setInsertionPoint(firrtlInstance);
620  auto object =
621  builder.create<om::ObjectOp>(firrtlInstance.getLoc(), classType,
622  className.getAttr(), actualParameters);
623 
624  // Replace uses of the FIRRTL instance outputs with field access into
625  // the OM Object. The later dialect conversion will take care of
626  // converting the types.
627  for (auto result : firrtlInstance.getResults()) {
628  // If the port isn't an output, continue.
629  if (firrtlInstance.getPortDirection(result.getResultNumber()) !=
630  Direction::Out)
631  continue;
632 
633  // If the port is not a property type, continue.
634  if (!isa<PropertyType>(result.getType()))
635  continue;
636 
637  // The path to the field is just this output's name.
638  auto objectFieldPath = builder.getArrayAttr({FlatSymbolRefAttr::get(
639  firrtlInstance.getPortName(result.getResultNumber()))});
640 
641  // Create the field access.
642  auto objectField = builder.create<ObjectFieldOp>(
643  object.getLoc(), result.getType(), object, objectFieldPath);
644 
645  result.replaceAllUsesWith(objectField);
646  }
647 
648  // Erase the original instance, now that we're done with it.
649  opsToErase.push_back(firrtlInstance);
650 
651  return success();
652 }
653 
654 // Helper to update a Module instantiation in a Module. Module instances within
655 // a Module are updated to remove the property typed ports.
656 static LogicalResult
657 updateModuleInstanceModule(InstanceOp firrtlInstance, OpBuilder &builder,
658  SmallVectorImpl<Operation *> &opsToErase,
659  InstanceGraph &instanceGraph) {
660  // Collect property typed ports to erase.
661  BitVector portsToErase(firrtlInstance.getNumResults());
662  for (auto result : firrtlInstance.getResults())
663  if (isa<PropertyType>(result.getType()))
664  portsToErase.set(result.getResultNumber());
665 
666  // If there are none, nothing to do.
667  if (portsToErase.none())
668  return success();
669 
670  // Create a new instance with the property ports removed.
671  builder.setInsertionPoint(firrtlInstance);
672  InstanceOp newInstance = firrtlInstance.erasePorts(builder, portsToErase);
673 
674  // Replace the instance in the instance graph. This is called from multiple
675  // threads, but because the instance graph data structure is not mutated, and
676  // only one thread ever sets the instance pointer for a given instance, this
677  // should be safe.
678  instanceGraph.replaceInstance(firrtlInstance, newInstance);
679 
680  // Erase the original instance, which is now replaced.
681  opsToErase.push_back(firrtlInstance);
682 
683  return success();
684 }
685 
686 // Update Object or Module instantiations in a FIRRTL Module or OM Class.
687 LogicalResult LowerClassesPass::updateInstances(Operation *op,
688  InstanceGraph &instanceGraph,
689  SymbolTable &symbolTable) {
690  OpBuilder builder(op);
691 
692  // Track ops to erase at the end. We can't do this eagerly, since we want to
693  // loop over each op in the container's body, and we may end up removing some
694  // ops later in the body when we visit instances earlier in the body.
695  SmallVector<Operation *> opsToErase;
696 
697  // Dispatch on each Object or Module instance.
698  for (auto &instance : llvm::make_early_inc_range(op->getRegion(0).getOps())) {
699  LogicalResult result =
700  TypeSwitch<Operation *, LogicalResult>(&instance)
701  .Case([&](firrtl::ObjectOp firrtlObject) {
702  // Convert FIRRTL Object instance to OM Object instance.
703  return updateObjectInstance(firrtlObject, builder, opsToErase);
704  })
705  .Case([&](InstanceOp firrtlInstance) {
706  return TypeSwitch<Operation *, LogicalResult>(op)
707  .Case([&](om::ClassLike) {
708  // Convert FIRRTL Module instance within a Class to OM
709  // Object instance.
710  return updateModuleInstanceClass(firrtlInstance, builder,
711  opsToErase, symbolTable);
712  })
713  .Case([&](FModuleOp) {
714  // Convert FIRRTL Module instance within a Module to remove
715  // property ports if necessary.
717  firrtlInstance, builder, opsToErase, instanceGraph);
718  })
719  .Default([](auto *op) { return success(); });
720  })
721  .Default([](auto *op) { return success(); });
722 
723  if (failed(result))
724  return result;
725  }
726 
727  // Erase the ops marked to be erased.
728  for (auto *op : opsToErase)
729  op->erase();
730 
731  return success();
732 }
733 
734 // Pattern rewriters for dialect conversion.
735 
737  : public OpConversionPattern<FIntegerConstantOp> {
738  using OpConversionPattern::OpConversionPattern;
739 
740  LogicalResult
741  matchAndRewrite(FIntegerConstantOp op, OpAdaptor adaptor,
742  ConversionPatternRewriter &rewriter) const override {
743  rewriter.replaceOpWithNewOp<om::ConstantOp>(
744  op, om::OMIntegerType::get(op.getContext()),
745  om::IntegerAttr::get(op.getContext(), adaptor.getValueAttr()));
746  return success();
747  }
748 };
749 
750 struct BoolConstantOpConversion : public OpConversionPattern<BoolConstantOp> {
751  using OpConversionPattern::OpConversionPattern;
752 
753  LogicalResult
754  matchAndRewrite(BoolConstantOp op, OpAdaptor adaptor,
755  ConversionPatternRewriter &rewriter) const override {
756  rewriter.replaceOpWithNewOp<om::ConstantOp>(
757  op, rewriter.getBoolAttr(adaptor.getValue()));
758  return success();
759  }
760 };
761 
763  : public OpConversionPattern<DoubleConstantOp> {
764  using OpConversionPattern::OpConversionPattern;
765 
766  LogicalResult
767  matchAndRewrite(DoubleConstantOp op, OpAdaptor adaptor,
768  ConversionPatternRewriter &rewriter) const override {
769  rewriter.replaceOpWithNewOp<om::ConstantOp>(op, adaptor.getValue());
770  return success();
771  }
772 };
773 
775  : public OpConversionPattern<StringConstantOp> {
776  using OpConversionPattern::OpConversionPattern;
777 
778  LogicalResult
779  matchAndRewrite(StringConstantOp op, OpAdaptor adaptor,
780  ConversionPatternRewriter &rewriter) const override {
781  auto stringType = om::StringType::get(op.getContext());
782  rewriter.replaceOpWithNewOp<om::ConstantOp>(
783  op, stringType, StringAttr::get(op.getValue(), stringType));
784  return success();
785  }
786 };
787 
789  : public OpConversionPattern<firrtl::ListCreateOp> {
790  using OpConversionPattern::OpConversionPattern;
791 
792  LogicalResult
793  matchAndRewrite(firrtl::ListCreateOp op, OpAdaptor adaptor,
794  ConversionPatternRewriter &rewriter) const override {
795  auto listType = getTypeConverter()->convertType<om::ListType>(op.getType());
796  if (!listType)
797  return failure();
798  rewriter.replaceOpWithNewOp<om::ListCreateOp>(op, listType,
799  adaptor.getElements());
800  return success();
801  }
802 };
803 
804 struct PathOpConversion : public OpConversionPattern<firrtl::PathOp> {
805 
806  PathOpConversion(TypeConverter &typeConverter, MLIRContext *context,
807  const PathInfoTable &pathInfoTable,
808  PatternBenefit benefit = 1)
809  : OpConversionPattern(typeConverter, context, benefit),
810  pathInfoTable(pathInfoTable) {}
811 
812  LogicalResult
813  matchAndRewrite(firrtl::PathOp op, OpAdaptor adaptor,
814  ConversionPatternRewriter &rewriter) const override {
815  auto *context = op->getContext();
816  auto pathType = om::PathType::get(context);
817  auto pathInfo = pathInfoTable.lookup(op.getTarget());
818 
819  // The 0'th argument is the base path.
820  auto basePath = op->getBlock()->getArgument(0);
821 
822  // If the target was optimized away, then replace the path operation with
823  // a deleted path.
824  if (!pathInfo) {
825  if (op.getTargetKind() == firrtl::TargetKind::DontTouch)
826  return emitError(op.getLoc(), "DontTouch target was deleted");
827  if (op.getTargetKind() == firrtl::TargetKind::Instance)
828  return emitError(op.getLoc(), "Instance target was deleted");
829  rewriter.replaceOpWithNewOp<om::EmptyPathOp>(op);
830  return success();
831  }
832 
833  auto symbol = pathInfo.symRef;
834 
835  // Convert the target kind to an OMIR target. Member references are updated
836  // to reflect the current kind of reference.
837  om::TargetKind targetKind;
838  switch (op.getTargetKind()) {
839  case firrtl::TargetKind::DontTouch:
840  targetKind = om::TargetKind::DontTouch;
841  break;
842  case firrtl::TargetKind::Reference:
843  targetKind = om::TargetKind::Reference;
844  break;
845  case firrtl::TargetKind::Instance:
846  if (!isa<InstanceOp, FModuleLike>(pathInfo.op))
847  return emitError(op.getLoc(), "invalid target for instance path")
848  .attachNote(pathInfo.op->getLoc())
849  << "target not instance or module";
850  targetKind = om::TargetKind::Instance;
851  break;
852  case firrtl::TargetKind::MemberInstance:
853  case firrtl::TargetKind::MemberReference:
854  if (isa<InstanceOp, FModuleLike>(pathInfo.op))
855  targetKind = om::TargetKind::MemberInstance;
856  else
857  targetKind = om::TargetKind::MemberReference;
858  break;
859  }
860 
861  rewriter.replaceOpWithNewOp<om::PathCreateOp>(
862  op, pathType, om::TargetKindAttr::get(op.getContext(), targetKind),
863  basePath, symbol);
864  return success();
865  }
866 
867  const PathInfoTable &pathInfoTable;
868 };
869 
870 struct WireOpConversion : public OpConversionPattern<WireOp> {
871  using OpConversionPattern::OpConversionPattern;
872 
873  LogicalResult
874  matchAndRewrite(WireOp wireOp, OpAdaptor adaptor,
875  ConversionPatternRewriter &rewriter) const override {
876  auto wireValue = dyn_cast<FIRRTLPropertyValue>(wireOp.getResult());
877 
878  // If the wire isn't a Property, not much we can do here.
879  if (!wireValue)
880  return failure();
881 
882  // If the wire isn't inside a graph region, we can't trivially remove it. In
883  // practice, this pattern does run for wires in graph regions, so this check
884  // should pass and we can proceed with the trivial rewrite.
885  auto regionKindInterface = wireOp->getParentOfType<RegionKindInterface>();
886  if (!regionKindInterface)
887  return failure();
888  if (regionKindInterface.getRegionKind(0) != RegionKind::Graph)
889  return failure();
890 
891  // Find the assignment to the wire.
892  PropAssignOp propAssign = getPropertyAssignment(wireValue);
893 
894  // Use the source of the assignment instead of the wire.
895  rewriter.replaceOp(wireOp, propAssign.getSrc());
896 
897  // Erase the source of the assignment.
898  rewriter.eraseOp(propAssign);
899 
900  return success();
901  }
902 };
903 
904 struct AnyCastOpConversion : public OpConversionPattern<ObjectAnyRefCastOp> {
905  using OpConversionPattern::OpConversionPattern;
906 
907  LogicalResult
908  matchAndRewrite(ObjectAnyRefCastOp op, OpAdaptor adaptor,
909  ConversionPatternRewriter &rewriter) const override {
910  rewriter.replaceOpWithNewOp<AnyCastOp>(op, adaptor.getInput());
911  return success();
912  }
913 };
914 
916  : public OpConversionPattern<firrtl::ObjectSubfieldOp> {
917  using OpConversionPattern::OpConversionPattern;
918 
920  const TypeConverter &typeConverter, MLIRContext *context,
921  const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable)
922  : OpConversionPattern(typeConverter, context),
923  classTypeTable(classTypeTable) {}
924 
925  LogicalResult
926  matchAndRewrite(firrtl::ObjectSubfieldOp op, OpAdaptor adaptor,
927  ConversionPatternRewriter &rewriter) const override {
928  auto omClassType = dyn_cast<om::ClassType>(adaptor.getInput().getType());
929  if (!omClassType)
930  return failure();
931 
932  // Convert the field-index used by the firrtl implementation, to a symbol,
933  // as used by the om implementation.
934  auto firrtlClassType =
935  classTypeTable.lookup(omClassType.getClassName().getAttr());
936  if (!firrtlClassType)
937  return failure();
938 
939  const auto &element = firrtlClassType.getElement(op.getIndex());
940  // We cannot convert input ports to fields.
941  if (element.direction == Direction::In)
942  return failure();
943 
944  auto field = FlatSymbolRefAttr::get(element.name);
945  auto path = rewriter.getArrayAttr({field});
946  auto type = typeConverter->convertType(element.type);
947  rewriter.replaceOpWithNewOp<om::ObjectFieldOp>(op, type, adaptor.getInput(),
948  path);
949  return success();
950  }
951 
952  const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable;
953 };
954 
955 struct ClassFieldOpConversion : public OpConversionPattern<ClassFieldOp> {
956  using OpConversionPattern::OpConversionPattern;
957 
958  LogicalResult
959  matchAndRewrite(ClassFieldOp op, OpAdaptor adaptor,
960  ConversionPatternRewriter &rewriter) const override {
961  rewriter.replaceOpWithNewOp<ClassFieldOp>(op, adaptor.getSymNameAttr(),
962  adaptor.getValue());
963  return success();
964  }
965 };
966 
968  : public OpConversionPattern<ClassExternFieldOp> {
969  using OpConversionPattern::OpConversionPattern;
970 
971  LogicalResult
972  matchAndRewrite(ClassExternFieldOp op, OpAdaptor adaptor,
973  ConversionPatternRewriter &rewriter) const override {
974  auto type = typeConverter->convertType(adaptor.getType());
975  if (!type)
976  return failure();
977  rewriter.replaceOpWithNewOp<ClassExternFieldOp>(
978  op, adaptor.getSymNameAttr(), type);
979  return success();
980  }
981 };
982 
983 struct ObjectOpConversion : public OpConversionPattern<om::ObjectOp> {
984  using OpConversionPattern::OpConversionPattern;
985 
986  LogicalResult
987  matchAndRewrite(om::ObjectOp objectOp, OpAdaptor adaptor,
988  ConversionPatternRewriter &rewriter) const override {
989  // Replace the object with a new object using the converted actual parameter
990  // types from the adaptor.
991  rewriter.replaceOpWithNewOp<om::ObjectOp>(objectOp, objectOp.getType(),
992  adaptor.getClassNameAttr(),
993  adaptor.getActualParams());
994  return success();
995  }
996 };
997 
998 struct ClassOpSignatureConversion : public OpConversionPattern<om::ClassOp> {
999  using OpConversionPattern::OpConversionPattern;
1000 
1001  LogicalResult
1002  matchAndRewrite(om::ClassOp classOp, OpAdaptor adaptor,
1003  ConversionPatternRewriter &rewriter) const override {
1004  Block *body = classOp.getBodyBlock();
1005  TypeConverter::SignatureConversion result(body->getNumArguments());
1006 
1007  // Convert block argument types.
1008  if (failed(typeConverter->convertSignatureArgs(body->getArgumentTypes(),
1009  result)))
1010  return failure();
1011 
1012  // Convert the body.
1013  if (failed(rewriter.convertRegionTypes(body->getParent(), *typeConverter,
1014  &result)))
1015  return failure();
1016 
1017  rewriter.updateRootInPlace(classOp, []() {});
1018 
1019  return success();
1020  }
1021 };
1022 
1024  : public OpConversionPattern<om::ClassExternOp> {
1025  using OpConversionPattern::OpConversionPattern;
1026 
1027  LogicalResult
1028  matchAndRewrite(om::ClassExternOp classOp, OpAdaptor adaptor,
1029  ConversionPatternRewriter &rewriter) const override {
1030  Block *body = classOp.getBodyBlock();
1031  TypeConverter::SignatureConversion result(body->getNumArguments());
1032 
1033  // Convert block argument types.
1034  if (failed(typeConverter->convertSignatureArgs(body->getArgumentTypes(),
1035  result)))
1036  return failure();
1037 
1038  // Convert the body.
1039  if (failed(rewriter.convertRegionTypes(body->getParent(), *typeConverter,
1040  &result)))
1041  return failure();
1042 
1043  rewriter.updateRootInPlace(classOp, []() {});
1044 
1045  return success();
1046  }
1047 };
1048 
1049 struct ObjectFieldOpConversion : public OpConversionPattern<ObjectFieldOp> {
1050  using OpConversionPattern::OpConversionPattern;
1051 
1052  LogicalResult
1053  matchAndRewrite(ObjectFieldOp op, OpAdaptor adaptor,
1054  ConversionPatternRewriter &rewriter) const override {
1055  // Replace the object field with a new object field of the appropriate
1056  // result type based on the type converter.
1057  auto type = typeConverter->convertType(op.getType());
1058  if (!type)
1059  return failure();
1060 
1061  rewriter.replaceOpWithNewOp<ObjectFieldOp>(op, type, adaptor.getObject(),
1062  adaptor.getFieldPathAttr());
1063 
1064  return success();
1065  }
1066 };
1067 
1068 // Helpers for dialect conversion setup.
1069 
1070 static void populateConversionTarget(ConversionTarget &target) {
1071  // FIRRTL dialect operations inside ClassOps or not using only OM types must
1072  // be legalized.
1073  target.addDynamicallyLegalDialect<FIRRTLDialect>(
1074  [](Operation *op) { return !op->getParentOfType<om::ClassLike>(); });
1075 
1076  // OM dialect operations are legal if they don't use FIRRTL types.
1077  target.addDynamicallyLegalDialect<OMDialect>([](Operation *op) {
1078  auto containsFIRRTLType = [](Type type) {
1079  return type
1080  .walk([](Type type) {
1081  return failure(isa<FIRRTLDialect>(type.getDialect()));
1082  })
1083  .wasInterrupted();
1084  };
1085  auto noFIRRTLOperands =
1086  llvm::none_of(op->getOperandTypes(), [&containsFIRRTLType](Type type) {
1087  return containsFIRRTLType(type);
1088  });
1089  auto noFIRRTLResults =
1090  llvm::none_of(op->getResultTypes(), [&containsFIRRTLType](Type type) {
1091  return containsFIRRTLType(type);
1092  });
1093  return noFIRRTLOperands && noFIRRTLResults;
1094  });
1095 
1096  // the OM op class.extern.field doesn't have operands or results, so we must
1097  // check it's type for a firrtl dialect.
1098  target.addDynamicallyLegalOp<ClassExternFieldOp>(
1099  [](ClassExternFieldOp op) { return !isa<FIRRTLType>(op.getType()); });
1100 
1101  // OM Class ops are legal if they don't use FIRRTL types for block arguments.
1102  target.addDynamicallyLegalOp<om::ClassOp, om::ClassExternOp>(
1103  [](Operation *op) -> std::optional<bool> {
1104  auto classLike = dyn_cast<om::ClassLike>(op);
1105  if (!classLike)
1106  return std::nullopt;
1107 
1108  return llvm::none_of(
1109  classLike.getBodyBlock()->getArgumentTypes(),
1110  [](Type type) { return isa<FIRRTLDialect>(type.getDialect()); });
1111  });
1112 }
1113 
1114 static void populateTypeConverter(TypeConverter &converter) {
1115  // Convert FIntegerType to IntegerType.
1116  converter.addConversion(
1117  [](IntegerType type) { return OMIntegerType::get(type.getContext()); });
1118  converter.addConversion([](FIntegerType type) {
1119  // The actual width of the IntegerType doesn't actually get used; it will be
1120  // folded away by the dialect conversion infrastructure to the type of the
1121  // APSIntAttr used in the FIntegerConstantOp.
1122  return OMIntegerType::get(type.getContext());
1123  });
1124 
1125  // Convert FIRRTL StringType to OM StringType.
1126  converter.addConversion([](om::StringType type) { return type; });
1127  converter.addConversion([](firrtl::StringType type) {
1128  return om::StringType::get(type.getContext());
1129  });
1130 
1131  // Convert FIRRTL PathType to OM PathType.
1132  converter.addConversion([](om::PathType type) { return type; });
1133  converter.addConversion([](om::BasePathType type) { return type; });
1134  converter.addConversion([](om::FrozenPathType type) { return type; });
1135  converter.addConversion([](om::FrozenBasePathType type) { return type; });
1136  converter.addConversion([](firrtl::PathType type) {
1137  return om::PathType::get(type.getContext());
1138  });
1139 
1140  // Convert FIRRTL Class type to OM Class type.
1141  converter.addConversion([](om::ClassType type) { return type; });
1142  converter.addConversion([](firrtl::ClassType type) {
1143  return om::ClassType::get(type.getContext(), type.getNameAttr());
1144  });
1145 
1146  // Convert FIRRTL AnyRef type to OM Any type.
1147  converter.addConversion([](om::AnyType type) { return type; });
1148  converter.addConversion([](firrtl::AnyRefType type) {
1149  return om::AnyType::get(type.getContext());
1150  });
1151 
1152  // Convert FIRRTL List type to OM List type.
1153  auto convertListType = [&converter](auto type) -> std::optional<mlir::Type> {
1154  auto elementType = converter.convertType(type.getElementType());
1155  if (!elementType)
1156  return {};
1158  };
1159 
1160  converter.addConversion(
1161  [convertListType](om::ListType type) -> std::optional<mlir::Type> {
1162  // Convert any om.list<firrtl> -> om.list<om>
1163  return convertListType(type);
1164  });
1165 
1166  converter.addConversion(
1167  [convertListType](firrtl::ListType type) -> std::optional<mlir::Type> {
1168  // Convert any firrtl.list<firrtl> -> om.list<om>
1169  return convertListType(type);
1170  });
1171 
1172  // Convert FIRRTL Bool type to OM
1173  converter.addConversion(
1174  [](BoolType type) { return IntegerType::get(type.getContext(), 1); });
1175 
1176  // Convert FIRRTL double type to OM.
1177  converter.addConversion(
1178  [](DoubleType type) { return FloatType::getF64(type.getContext()); });
1179 
1180  // Add a target materialization to fold away unrealized conversion casts.
1181  converter.addTargetMaterialization(
1182  [](OpBuilder &builder, Type type, ValueRange values, Location loc) {
1183  assert(values.size() == 1);
1184  return values[0];
1185  });
1186 
1187  // Add a source materialization to fold away unrealized conversion casts.
1188  converter.addSourceMaterialization(
1189  [](OpBuilder &builder, Type type, ValueRange values, Location loc) {
1190  assert(values.size() == 1);
1191  return values[0];
1192  });
1193 }
1194 
1196  RewritePatternSet &patterns, TypeConverter &converter,
1197  const PathInfoTable &pathInfoTable,
1198  const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable) {
1199  patterns.add<FIntegerConstantOpConversion>(converter, patterns.getContext());
1200  patterns.add<StringConstantOpConversion>(converter, patterns.getContext());
1201  patterns.add<PathOpConversion>(converter, patterns.getContext(),
1202  pathInfoTable);
1203  patterns.add<WireOpConversion>(converter, patterns.getContext());
1204  patterns.add<AnyCastOpConversion>(converter, patterns.getContext());
1205  patterns.add<ObjectSubfieldOpConversion>(converter, patterns.getContext(),
1206  classTypeTable);
1207  patterns.add<ClassFieldOpConversion>(converter, patterns.getContext());
1208  patterns.add<ClassExternFieldOpConversion>(converter, patterns.getContext());
1209  patterns.add<ClassOpSignatureConversion>(converter, patterns.getContext());
1211  patterns.getContext());
1212  patterns.add<ObjectOpConversion>(converter, patterns.getContext());
1213  patterns.add<ObjectFieldOpConversion>(converter, patterns.getContext());
1214  patterns.add<ListCreateOpConversion>(converter, patterns.getContext());
1215  patterns.add<BoolConstantOpConversion>(converter, patterns.getContext());
1216  patterns.add<DoubleConstantOpConversion>(converter, patterns.getContext());
1217 }
1218 
1219 // Convert to OM ops and types in Classes or Modules.
1220 LogicalResult LowerClassesPass::dialectConversion(
1221  Operation *op, const PathInfoTable &pathInfoTable,
1222  const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable) {
1223  ConversionTarget target(getContext());
1224  populateConversionTarget(target);
1225 
1226  TypeConverter typeConverter;
1227  populateTypeConverter(typeConverter);
1228 
1229  RewritePatternSet patterns(&getContext());
1230  populateRewritePatterns(patterns, typeConverter, pathInfoTable,
1231  classTypeTable);
1232 
1233  return applyPartialConversion(op, target, std::move(patterns));
1234 }
assert(baseType &&"element must be base type")
MlirType uint64_t numElements
Definition: CHIRRTL.cpp:26
MlirType elementType
Definition: CHIRRTL.cpp:25
static void populateConversionTarget(ConversionTarget &target)
static LogicalResult updateModuleInstanceModule(InstanceOp firrtlInstance, OpBuilder &builder, SmallVectorImpl< Operation * > &opsToErase, InstanceGraph &instanceGraph)
static void populateTypeConverter(TypeConverter &converter)
static LogicalResult updateObjectInstance(firrtl::ObjectOp firrtlObject, OpBuilder &builder, SmallVectorImpl< Operation * > &opsToErase)
static LogicalResult updateModuleInstanceClass(InstanceOp firrtlInstance, OpBuilder &builder, SmallVectorImpl< Operation * > &opsToErase, SymbolTable &symbolTable)
static void populateRewritePatterns(RewritePatternSet &patterns, TypeConverter &converter, const PathInfoTable &pathInfoTable, const DenseMap< StringAttr, firrtl::ClassType > &classTypeTable)
Builder builder
This class provides a read-only projection of an annotation.
unsigned getFieldID() const
Get the field id this attribute targets.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
This graph tracks modules and where they are instantiated.
virtual void replaceInstance(InstanceOpInterface inst, InstanceOpInterface newInst)
Replaces an instance of a module with another instance.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:53
hw::InnerRefAttr getInnerRefTo(const hw::InnerSymTarget &target, GetNamespaceCallback getNamespace)
Obtain an inner reference to the target (operation or port), adding an inner symbol as necessary.
PropAssignOp getPropertyAssignment(FIRRTLPropertyValue value)
Return the single assignment to a Property value.
std::unique_ptr< mlir::Pass > createLowerClassesPass()
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
Definition: DebugAnalysis.h:21
LogicalResult matchAndRewrite(ObjectAnyRefCastOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(BoolConstantOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(ClassExternFieldOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(om::ClassExternOp classOp, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(ClassFieldOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(om::ClassOp classOp, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(DoubleConstantOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(FIntegerConstantOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(firrtl::ListCreateOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(ObjectFieldOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(om::ObjectOp objectOp, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
ObjectSubfieldOpConversion(const TypeConverter &typeConverter, MLIRContext *context, const DenseMap< StringAttr, firrtl::ClassType > &classTypeTable)
const DenseMap< StringAttr, firrtl::ClassType > & classTypeTable
LogicalResult matchAndRewrite(firrtl::ObjectSubfieldOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
PathOpConversion(TypeConverter &typeConverter, MLIRContext *context, const PathInfoTable &pathInfoTable, PatternBenefit benefit=1)
const PathInfoTable & pathInfoTable
LogicalResult matchAndRewrite(firrtl::PathOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(StringConstantOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(WireOp wireOp, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
An annotation target is used to keep track of something that is targeted by an Annotation.
A cache of existing HierPathOps, mostly used to facilitate HierPathOp reuse.
This represents an annotation targeting a specific operation.
This represents an annotation targeting a specific port of a module, memory, or instance.
This holds the name and type that describes the module's ports.