CIRCT  19.0.0git
SpecializeLayers.cpp
Go to the documentation of this file.
1 //===- SpecializeLayers.cpp -------------------------------------*- 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 
15 #include "circt/Dialect/HW/HWOps.h"
16 #include "mlir/IR/ImplicitLocOpBuilder.h"
17 #include "mlir/IR/Threading.h"
18 #include "llvm/ADT/STLExtras.h"
19 #include <optional>
20 #include <type_traits>
21 
22 namespace circt {
23 namespace firrtl {
24 #define GEN_PASS_DEF_SPECIALIZELAYERS
25 #include "circt/Dialect/FIRRTL/Passes.h.inc"
26 } // namespace firrtl
27 } // namespace circt
28 
29 using namespace mlir;
30 using namespace circt;
31 using namespace firrtl;
32 
33 // TODO: this should be upstreamed.
34 namespace llvm {
35 template <>
36 struct PointerLikeTypeTraits<mlir::ArrayAttr>
37  : public PointerLikeTypeTraits<mlir::Attribute> {
38  static inline mlir::ArrayAttr getFromVoidPointer(void *ptr) {
39  return mlir::ArrayAttr::getFromOpaquePointer(ptr);
40  }
41 };
42 } // namespace llvm
43 
44 namespace {
45 /// Removes non-local annotations whose path is no longer viable, due to
46 /// the removal of module instances.
47 struct AnnotationCleaner {
48  /// Create an AnnotationCleaner which removes any annotation which contains a
49  /// reference to a symbol in removedPaths.
50  AnnotationCleaner(const DenseSet<StringAttr> &removedPaths)
51  : removedPaths(removedPaths) {}
52 
53  AnnotationSet cleanAnnotations(AnnotationSet annos) {
54  annos.removeAnnotations([&](Annotation anno) {
55  if (auto nla = anno.getMember<FlatSymbolRefAttr>("circt.nonlocal"))
56  return removedPaths.contains(nla.getAttr());
57  return false;
58  });
59  return annos;
60  }
61 
62  void cleanAnnotations(Operation *op) {
63  AnnotationSet oldAnnotations(op);
64  // We want to avoid attaching an empty annotation array on to an op that
65  // never had an annotation array in the first place.
66  if (!oldAnnotations.empty()) {
67  auto newAnnotations = cleanAnnotations(oldAnnotations);
68  if (oldAnnotations != newAnnotations)
69  newAnnotations.applyToOperation(op);
70  }
71  }
72 
73  void operator()(FModuleLike module) {
74  // Clean the regular annotations.
75  cleanAnnotations(module);
76 
77  // Clean all port annotations.
78  for (size_t i = 0, e = module.getNumPorts(); i < e; ++i) {
79  auto oldAnnotations = AnnotationSet::forPort(module, i);
80  if (!oldAnnotations.empty()) {
81  auto newAnnotations = cleanAnnotations(oldAnnotations);
82  if (oldAnnotations != newAnnotations)
83  newAnnotations.applyToPort(module, i);
84  }
85  }
86 
87  // Clean all annotations in body.
88  module->walk([&](Operation *op) {
89  // Clean regular annotations.
90  cleanAnnotations(op);
91 
92  if (auto mem = dyn_cast<MemOp>(op)) {
93  // Update all annotations on ports.
94  for (size_t i = 0, e = mem.getNumResults(); i < e; ++i) {
95  auto oldAnnotations = AnnotationSet::forPort(mem, i);
96  if (!oldAnnotations.empty()) {
97  auto newAnnotations = cleanAnnotations(oldAnnotations);
98  if (oldAnnotations != newAnnotations)
99  newAnnotations.applyToPort(mem, i);
100  }
101  }
102  }
103  });
104  }
105 
106  /// A set of symbols of removed paths.
107  const DenseSet<StringAttr> &removedPaths;
108 };
109 
110 /// Helper to keep track of an insertion point and move operations around.
111 struct InsertionPoint {
112  /// Create an insertion point at the end of a block.
113  static InsertionPoint atBlockEnd(Block *block) {
114  return InsertionPoint{block, block->end()};
115  }
116 
117  /// Move the target operation before the current insertion point and update
118  /// the insertion point to point to the op.
119  void moveOpBefore(Operation *op) {
120  op->moveBefore(block, it);
121  it = Block::iterator(op);
122  }
123 
124 private:
125  InsertionPoint(Block *block, Block::iterator it) : block(block), it(it) {}
126 
127  Block *block;
128  Block::iterator it;
129 };
130 
131 /// The specialization mode of a layer.
132 enum class Specialization { Enable, Disable };
133 
134 /// A specialized value. If the value is colored such that it is disabled,
135 /// it will not contain an underlying value. Otherwise, contains the
136 /// specialized version of the value.
137 template <typename T>
138 struct Specialized {
139  /// Create a disabled specialized value.
140  Specialized() : Specialized(Specialization::Disable, nullptr) {}
141 
142  /// Create an enabled specialized value.
143  Specialized(T value) : Specialized(Specialization::Enable, value) {}
144 
145  /// Returns true if the value was specialized away.
146  bool isDisabled() const { return value.getInt() == Specialization::Disable; }
147 
148  /// Returns the specialized value if it still exists.
149  T getValue() const {
150  assert(!isDisabled());
151  return value.getPointer();
152  }
153 
154  operator bool() const { return !isDisabled(); }
155 
156 private:
157  Specialized(Specialization specialization, T value)
158  : value(value, specialization) {}
159  llvm::PointerIntPair<T, 1, Specialization> value;
160 };
161 
162 struct SpecializeLayers {
163  SpecializeLayers(
164  CircuitOp circuit,
165  const DenseMap<SymbolRefAttr, Specialization> &specializations)
166  : context(circuit->getContext()), circuit(circuit),
167  specializations(specializations) {}
168 
169  /// Create a reference to every field in the inner symbol, and record it in
170  /// the list of removed symbols.
171  static void recordRemovedInnerSym(DenseSet<Attribute> &removedSyms,
172  StringAttr moduleName,
173  hw::InnerSymAttr innerSym) {
174  for (auto field : innerSym)
175  removedSyms.insert(hw::InnerRefAttr::get(moduleName, field.getName()));
176  }
177 
178  /// Create a reference to every field in the inner symbol, and record it in
179  /// the list of removed symbols.
180  static void recordRemovedInnerSyms(DenseSet<Attribute> &removedSyms,
181  StringAttr moduleName, Block *block) {
182  block->walk([&](hw::InnerSymbolOpInterface op) {
183  if (auto innerSym = op.getInnerSymAttr())
184  recordRemovedInnerSym(removedSyms, moduleName, innerSym);
185  });
186  }
187 
188  /// If this layer reference is being specialized, returns the specialization
189  /// mode. Otherwise, it returns disabled value.
190  std::optional<Specialization> getSpecialization(SymbolRefAttr layerRef) {
191  auto it = specializations.find(layerRef);
192  if (it != specializations.end())
193  return it->getSecond();
194  return std::nullopt;
195  }
196 
197  /// Forms a symbol reference to a layer using head as the root reference, and
198  /// nestedRefs as the path to the specific layer. If this layer reference is
199  /// being specialized, returns the specialization mode. Otherwise, it returns
200  /// nullopt.
201  std::optional<Specialization>
202  getSpecialization(StringAttr head, ArrayRef<FlatSymbolRefAttr> nestedRefs) {
203  return getSpecialization(SymbolRefAttr::get(head, nestedRefs));
204  }
205 
206  /// Specialize a layer reference by removing enabled layers, mangling the
207  /// names of inlined layers, and returning a disabled value if the layer was
208  /// disabled. This can return nullptr if all layers were enabled.
209  Specialized<SymbolRefAttr> specializeLayerRef(SymbolRefAttr layerRef) {
210  // Walk the layer reference from root to leaf, checking if each outer layer
211  // was specialized or not. If an outer layer was disabled, then this
212  // specific layer is implicitly disabled as well. If an outer layer was
213  // enabled, we need to use its name to mangle the name of inner layers. If
214  // the layer is not specialized, we may need to mangle its name, otherwise
215  // we leave it alone.
216 
217  auto oldRoot = layerRef.getRootReference();
218  SmallVector<FlatSymbolRefAttr> oldNestedRefs;
219 
220  // A prefix to be used to track how to mangle the next non-inlined layer.
221  SmallString<64> prefix;
222  SmallVector<FlatSymbolRefAttr> newRef;
223 
224  // Side-effecting helper which returns false if the currently examined layer
225  // is disabled, true otherwise. If the current layer is being enabled, add
226  // its name to the prefix. If the layer is not specialized, we mangle the
227  // name with the prefix which is then reset to the empty string, and copy it
228  // into the new specialize layer reference.
229  auto helper = [&](StringAttr ref) -> bool {
230  if (auto specialization = getSpecialization(oldRoot, oldNestedRefs)) {
231  if (*specialization == Specialization::Enable) {
232  // We are enabling this layer, the next non-enabled layer should
233  // include this layer's name as a prefix.
234  prefix.append(ref.getValue());
235  prefix.append("_");
236  return true;
237  }
238  // We are disabling this layer.
239  return false;
240  }
241  // We are not specializing this layer. Mangle the name with the current
242  // prefix.
243  newRef.push_back(FlatSymbolRefAttr::get(
244  StringAttr::get(ref.getContext(), prefix + ref.getValue())));
245  prefix.clear();
246  return true;
247  };
248 
249  if (!helper(oldRoot))
250  return {};
251 
252  for (auto ref : layerRef.getNestedReferences()) {
253  oldNestedRefs.push_back(ref);
254  if (!helper(ref.getAttr()))
255  return {};
256  }
257 
258  if (newRef.empty())
259  return {SymbolRefAttr()};
260 
261  // Root references need to be handled differently than nested references,
262  // but since we don't know before hand which layer will form the new root
263  // layer, we copy all layers to the same array, at the cost of unnecessarily
264  // wrapping the new root reference into a FlatSymbolRefAttr and having to
265  // unpack it again.
266  auto newRoot = newRef.front().getAttr();
267  return {SymbolRefAttr::get(newRoot, ArrayRef(newRef).drop_front())};
268  }
269 
270  /// Specialize a RefType by specializing the layer color. If the RefType is
271  /// colored with a disabled layer, this will return nullptr.
272  RefType specializeRefType(RefType refType) {
273  if (auto oldLayer = refType.getLayer()) {
274  if (auto newLayer = specializeLayerRef(oldLayer))
275  return RefType::get(refType.getType(), refType.getForceable(),
276  newLayer.getValue());
277  return nullptr;
278  }
279  return refType;
280  }
281 
282  Type specializeType(Type type) {
283  if (auto refType = dyn_cast<RefType>(type))
284  return specializeRefType(refType);
285  return type;
286  }
287 
288  /// Specialize a value by modifying its type. Returns nullptr if this value
289  /// should be disabled, and the original value otherwise.
290  Value specializeValue(Value value) {
291  if (auto newType = specializeType(value.getType())) {
292  value.setType(newType);
293  return value;
294  }
295  return nullptr;
296  }
297 
298  /// Specialize a layerblock. If the layerblock is disabled, it and all of its
299  /// contents will be erased, and all removed inner symbols will be recorded
300  /// so that we can later clean up hierarchical paths. If the layer is
301  /// enabled, then we will inline the contents of the layer to the same
302  /// position and delete the layerblock.
303  void specializeOp(LayerBlockOp layerBlock, InsertionPoint &insertionPoint,
304  DenseSet<Attribute> &removedSyms) {
305  auto oldLayerRef = layerBlock.getLayerNameAttr();
306 
307  // Get the specialization for the current layerblock, not taking into
308  // account if an outer layerblock was specialized. We should not have
309  // recursed inside of the disabled layerblock anyways, as it just gets
310  // erased.
311  auto specialization = getSpecialization(oldLayerRef);
312 
313  // We are not specializing this layerblock.
314  if (!specialization) {
315  // We must update the name of this layerblock to reflect
316  // specializations of any outer layers.
317  auto newLayerRef = specializeLayerRef(oldLayerRef).getValue();
318  if (oldLayerRef != newLayerRef)
319  layerBlock.setLayerNameAttr(newLayerRef);
320  // Specialize inner operations, but keep them in their original
321  // location.
322  auto *block = layerBlock.getBody();
323  auto bodyIP = InsertionPoint::atBlockEnd(block);
324  specializeBlock(block, bodyIP, removedSyms);
325  insertionPoint.moveOpBefore(layerBlock);
326  return;
327  }
328 
329  // We are enabling this layer, and all contents of this layer need to be
330  // moved (inlined) to the insertion point.
331  if (*specialization == Specialization::Enable) {
332  // Move all contents to the insertion point and specialize them.
333  specializeBlock(layerBlock.getBody(), insertionPoint, removedSyms);
334  // Erase the now empty layerblock.
335  layerBlock->erase();
336  return;
337  }
338 
339  // We are disabling this layerblock, so we can just erase the layerblock. We
340  // need to record all the objects with symbols which are being deleted.
341  auto moduleName = layerBlock->getParentOfType<FModuleOp>().getNameAttr();
342  recordRemovedInnerSyms(removedSyms, moduleName, layerBlock.getBody());
343  layerBlock->erase();
344  }
345 
346  void specializeOp(WhenOp when, InsertionPoint &insertionPoint,
347  DenseSet<Attribute> &removedSyms) {
348  // We need to specialize both arms of the when, but the inner ops should
349  // be left where they are (and not inlined to the insertion point).
350  auto *thenBlock = &when.getThenBlock();
351  auto thenIP = InsertionPoint::atBlockEnd(thenBlock);
352  specializeBlock(thenBlock, thenIP, removedSyms);
353  if (when.hasElseRegion()) {
354  auto *elseBlock = &when.getElseBlock();
355  auto elseIP = InsertionPoint::atBlockEnd(elseBlock);
356  specializeBlock(elseBlock, elseIP, removedSyms);
357  }
358  insertionPoint.moveOpBefore(when);
359  }
360 
361  void specializeOp(MatchOp match, InsertionPoint &insertionPoint,
362  DenseSet<Attribute> &removedSyms) {
363  for (size_t i = 0, e = match.getNumRegions(); i < e; ++i) {
364  auto *caseBlock = &match.getRegion(i).front();
365  auto caseIP = InsertionPoint::atBlockEnd(caseBlock);
366  specializeBlock(caseBlock, caseIP, removedSyms);
367  }
368  insertionPoint.moveOpBefore(match);
369  }
370 
371  void specializeOp(InstanceOp instance, InsertionPoint &insertionPoint,
372  DenseSet<Attribute> &removedSyms) {
373  /// Update the types of any probe ports on the instance, and delete any
374  /// probe port that is permanently disabled.
375  llvm::BitVector disabledPorts(instance->getNumResults());
376  for (auto result : instance->getResults())
377  if (!specializeValue(result))
378  disabledPorts.set(result.getResultNumber());
379  if (disabledPorts.any()) {
380  OpBuilder builder(instance);
381  auto newInstance = instance.erasePorts(builder, disabledPorts);
382  instance->erase();
383  instance = newInstance;
384  }
385 
386  // Specialize the required enable layers. Due to the layer verifiers, there
387  // should not be any disabled layer in this instance and this should be
388  // infallible.
389  auto newLayers = specializeEnableLayers(instance.getLayersAttr());
390  instance.setLayersAttr(newLayers.getValue());
391 
392  insertionPoint.moveOpBefore(instance);
393  }
394 
395  void specializeOp(InstanceChoiceOp instanceChoice,
396  InsertionPoint &insertionPoint,
397  DenseSet<Attribute> &removedSyms) {
398  /// Update the types of any probe ports on the instanceChoice, and delete
399  /// any probe port that is permanently disabled.
400  llvm::BitVector disabledPorts(instanceChoice->getNumResults());
401  for (auto result : instanceChoice->getResults())
402  if (!specializeValue(result))
403  disabledPorts.set(result.getResultNumber());
404  if (disabledPorts.any()) {
405  OpBuilder builder(instanceChoice);
406  auto newInstanceChoice =
407  instanceChoice.erasePorts(builder, disabledPorts);
408  instanceChoice->erase();
409  instanceChoice = newInstanceChoice;
410  }
411 
412  // Specialize the required enable layers. Due to the layer verifiers, there
413  // should not be any disabled layer in this instanceChoice and this should
414  // be infallible.
415  auto newLayers = specializeEnableLayers(instanceChoice.getLayersAttr());
416  instanceChoice.setLayersAttr(newLayers.getValue());
417 
418  insertionPoint.moveOpBefore(instanceChoice);
419  }
420 
421  void specializeOp(WireOp wire, InsertionPoint &insertionPoint,
422  DenseSet<Attribute> &removedSyms) {
423  if (specializeValue(wire.getResult())) {
424  insertionPoint.moveOpBefore(wire);
425  } else {
426  if (auto innerSym = wire.getInnerSymAttr())
427  recordRemovedInnerSym(removedSyms,
428  wire->getParentOfType<FModuleOp>().getNameAttr(),
429  innerSym);
430  wire.erase();
431  }
432  }
433 
434  void specializeOp(RefDefineOp refDefine, InsertionPoint &insertionPoint,
435  DenseSet<Attribute> &removedSyms) {
436  // If this is connected disabled probes, erase the refdefine op.
437  if (auto layerRef = refDefine.getDest().getType().getLayer())
438  if (!specializeLayerRef(layerRef)) {
439  refDefine->erase();
440  return;
441  }
442  insertionPoint.moveOpBefore(refDefine);
443  }
444 
445  void specializeOp(RefSubOp refSub, InsertionPoint &insertionPoint,
446  DenseSet<Attribute> &removedSyms) {
447  if (specializeValue(refSub->getResult(0)))
448  insertionPoint.moveOpBefore(refSub);
449  else
450 
451  refSub.erase();
452  }
453 
454  void specializeOp(RefCastOp refCast, InsertionPoint &insertionPoint,
455  DenseSet<Attribute> &removedSyms) {
456  if (specializeValue(refCast->getResult(0)))
457  insertionPoint.moveOpBefore(refCast);
458  else
459  refCast.erase();
460  }
461 
462  /// Specialize a block of operations, removing any probes which are
463  /// disabled, and moving all operations to the insertion point.
464  void specializeBlock(Block *block, InsertionPoint &insertionPoint,
465  DenseSet<Attribute> &removedSyms) {
466  // Since this can erase operations that deal with disabled probes, we walk
467  // the block in reverse to make sure that we erase uses before defs.
468  for (auto &op : llvm::make_early_inc_range(llvm::reverse(*block))) {
469  TypeSwitch<Operation *>(&op)
470  .Case<LayerBlockOp, WhenOp, MatchOp, InstanceOp, InstanceChoiceOp,
471  WireOp, RefDefineOp, RefSubOp, RefCastOp>(
472  [&](auto op) { specializeOp(op, insertionPoint, removedSyms); })
473  .Default([&](Operation *op) {
474  // By default all operations should be inlined from an enabled
475  // layer.
476  insertionPoint.moveOpBefore(op);
477  });
478  }
479  }
480 
481  /// Specialize the list of enabled layers for a module. Return a disabled
482  /// layer if one of the required layers has been disabled.
483  Specialized<ArrayAttr> specializeEnableLayers(ArrayAttr layers) {
484  SmallVector<Attribute> newLayers;
485  for (auto layer : layers.getAsRange<SymbolRefAttr>()) {
486  auto newLayer = specializeLayerRef(layer);
487  if (!newLayer)
488  return {};
489  if (newLayer.getValue())
490  newLayers.push_back(newLayer.getValue());
491  }
492  return ArrayAttr::get(context, newLayers);
493  }
494 
495  void specializeModulePorts(FModuleLike moduleLike,
496  DenseSet<Attribute> &removedSyms) {
497  auto oldTypeAttrs = moduleLike.getPortTypesAttr();
498 
499  // The list of new port types.
500  SmallVector<Attribute> newTypeAttrs;
501  newTypeAttrs.reserve(oldTypeAttrs.size());
502 
503  // This is the list of port indices which need to be removed because they
504  // have been specialized away.
505  llvm::BitVector disabledPorts(oldTypeAttrs.size());
506 
507  auto moduleName = moduleLike.getNameAttr();
508  for (auto [index, typeAttr] :
509  llvm::enumerate(oldTypeAttrs.getAsRange<TypeAttr>())) {
510  // Specialize the type fo the port.
511  if (auto type = specializeType(typeAttr.getValue())) {
512  newTypeAttrs.push_back(TypeAttr::get(type));
513  } else {
514  // The port is being disabled, and should be removed.
515  if (auto portSym = moduleLike.getPortSymbolAttr(index))
516  recordRemovedInnerSym(removedSyms, moduleName, portSym);
517  disabledPorts.set(index);
518  }
519  }
520 
521  // Erase the disabled ports.
522  moduleLike.erasePorts(disabledPorts);
523 
524  // Update the rest of the port types.
525  moduleLike->setAttr(FModuleLike::getPortTypesAttrName(),
526  ArrayAttr::get(moduleLike.getContext(), newTypeAttrs));
527 
528  // We may also need to update the types on the block arguments.
529  if (auto moduleOp = dyn_cast<FModuleOp>(moduleLike.getOperation()))
530  for (auto [arg, typeAttr] :
531  llvm::zip(moduleOp.getArguments(), newTypeAttrs))
532  arg.setType(cast<TypeAttr>(typeAttr).getValue());
533  }
534 
535  template <typename T>
536  DenseSet<Attribute> specializeModuleLike(T op) {
537  DenseSet<Attribute> removedSyms;
538 
539  // Specialize all operations in the body of the module. This must be done
540  // before specializing the module ports so that we don't try to erase values
541  // that still have uses.
542  if constexpr (std::is_same_v<T, FModuleOp>) {
543  auto *block = cast<FModuleOp>(op).getBodyBlock();
544  auto bodyIP = InsertionPoint::atBlockEnd(block);
545  specializeBlock(block, bodyIP, removedSyms);
546  }
547 
548  // Specialize the ports of this module.
549  specializeModulePorts(op, removedSyms);
550 
551  return removedSyms;
552  }
553 
554  template <typename T>
555  T specializeEnableLayers(T module, DenseSet<Attribute> &removedSyms) {
556  // Update the required layers on the module.
557  if (auto newLayers = specializeEnableLayers(module.getLayersAttr())) {
558  module.setLayersAttr(newLayers.getValue());
559  return module;
560  }
561 
562  // If we disabled a layer which this module requires, we must delete the
563  // whole module.
564  auto moduleName = module.getNameAttr();
565  removedSyms.insert(FlatSymbolRefAttr::get(moduleName));
566  if constexpr (std::is_same_v<T, FModuleOp>)
567  recordRemovedInnerSyms(removedSyms, moduleName,
568  cast<FModuleOp>(module).getBodyBlock());
569 
570  module->erase();
571  return nullptr;
572  }
573 
574  /// Specialize a layer operation, by removing enabled layers and inlining
575  /// their contents, deleting disabled layers and all nested layers, and
576  /// mangling the names of any inlined layers.
577  void specializeLayer(LayerOp layer) {
578  StringAttr head = layer.getSymNameAttr();
579  SmallVector<FlatSymbolRefAttr> nestedRefs;
580 
581  std::function<void(LayerOp, Block::iterator, const Twine &)> handleLayer =
582  [&](LayerOp layer, Block::iterator insertionPoint,
583  const Twine &prefix) {
584  auto *block = &layer.getBody().getBlocks().front();
585  auto specialization = getSpecialization(head, nestedRefs);
586 
587  // If we are not specializing the current layer, visit the inner
588  // layers.
589  if (!specialization) {
590  // We only mangle the name and move the layer if the prefix is
591  // non-empty, which indicates that we are enabling the parent
592  // layer.
593  if (!prefix.isTriviallyEmpty()) {
594  layer.setSymNameAttr(
595  StringAttr::get(context, prefix + layer.getSymName()));
596  auto *parentBlock = insertionPoint->getBlock();
597  layer->moveBefore(parentBlock, insertionPoint);
598  }
599  for (auto nested :
600  llvm::make_early_inc_range(block->getOps<LayerOp>())) {
601  nestedRefs.push_back(SymbolRefAttr::get(nested));
602  handleLayer(nested, Block::iterator(nested), "");
603  }
604  return;
605  }
606 
607  if (*specialization == Specialization::Enable) {
608  for (auto nested :
609  llvm::make_early_inc_range(block->getOps<LayerOp>())) {
610  nestedRefs.push_back(SymbolRefAttr::get(nested));
611  handleLayer(nested, insertionPoint,
612  prefix + layer.getSymName() + "_");
613  }
614  // Erase the now empty layer.
615  layer->erase();
616  return;
617  }
618 
619  // If we are disabling this layer, then we can just fully delete
620  // it.
621  layer->erase();
622  };
623 
624  handleLayer(layer, Block::iterator(layer), "");
625  }
626 
627  void operator()() {
628  // Gather all operations we need to specialize, and all the ops that we
629  // need to clean. We specialize layers and module's enable layers here
630  // because that can delete the operations and must be done serially.
631  SmallVector<Operation *> specialize;
632  DenseSet<Attribute> removedSyms;
633  for (auto &op : llvm::make_early_inc_range(*circuit.getBodyBlock())) {
634  TypeSwitch<Operation *>(&op)
635  .Case<FModuleOp, FExtModuleOp>([&](auto module) {
636  if (specializeEnableLayers(module, removedSyms))
637  specialize.push_back(module);
638  })
639  .Case<LayerOp>([&](LayerOp layer) { specializeLayer(layer); });
640  }
641 
642  // Function to merge two sets together.
643  auto mergeSets = [](auto &&a, auto &&b) {
644  a.insert(b.begin(), b.end());
645  return std::forward<decltype(a)>(a);
646  };
647 
648  // Specialize all modules in parallel. The result is a set of all inner
649  // symbol references which are no longer valid due to disabling layers.
650  removedSyms = transformReduce(
651  context, specialize, removedSyms, mergeSets,
652  [&](Operation *op) -> DenseSet<Attribute> {
653  return TypeSwitch<Operation *, DenseSet<Attribute>>(op)
654  .Case<FModuleOp, FExtModuleOp>(
655  [&](auto op) { return specializeModuleLike(op); });
656  });
657 
658  // Remove all hierarchical path operations which reference deleted symbols,
659  // and create a set of the removed paths operations. We will have to remove
660  // all annotations which use these paths.
661  DenseSet<StringAttr> removedPaths;
662  for (auto hierPath : llvm::make_early_inc_range(
663  circuit.getBody().getOps<hw::HierPathOp>())) {
664  auto namepath = hierPath.getNamepath().getValue();
665  auto shouldDelete = [&](Attribute ref) {
666  return removedSyms.contains(ref);
667  };
668  if (llvm::any_of(namepath.drop_back(), shouldDelete)) {
669  removedPaths.insert(SymbolTable::getSymbolName(hierPath));
670  hierPath->erase();
671  continue;
672  }
673  // If we deleted the target of the hierpath, we don't need to add it to
674  // the list of removedPaths, since no annotation will be left around to
675  // reference this path.
676  if (shouldDelete(namepath.back()))
677  hierPath->erase();
678  }
679 
680  // Walk all annotations in the circuit and remove the ones that have a
681  // path which traverses or targets a removed instantiation.
682  SmallVector<FModuleLike> clean;
683  for (auto &op : *circuit.getBodyBlock())
684  if (isa<FModuleOp, FExtModuleOp, FIntModuleOp, FMemModuleOp>(op))
685  clean.push_back(cast<FModuleLike>(op));
686 
687  parallelForEach(context, clean, [&](FModuleLike module) {
688  (AnnotationCleaner(removedPaths))(module);
689  });
690  }
691 
692  MLIRContext *context;
693  CircuitOp circuit;
694  const DenseMap<SymbolRefAttr, Specialization> &specializations;
695 };
696 
697 struct SpecializeLayersPass
698  : public circt::firrtl::impl::SpecializeLayersBase<SpecializeLayersPass> {
699 
700  void runOnOperation() override {
701  auto circuit = getOperation();
702  SymbolTableCollection stc;
703 
704  // Get the list of enabled and disabled layers, and remove them from the
705  // circuit operation.
706  auto enabledLayers = circuit.getEnableLayersAttr();
707  circuit.removeEnableLayersAttr();
708  auto disabledLayers = circuit.getDisableLayersAttr();
709  circuit.removeDisableLayersAttr();
710 
711  // If we did not transform the circuit the circuit, return early.
712  // TODO: if both arrays are empty we could preserve specific analyses, but
713  // not all analyses since we have modified the circuit op.
714  if (!enabledLayers && !disabledLayers)
715  return markAllAnalysesPreserved();
716 
717  // Set of layers to enable or disable.
718  DenseMap<SymbolRefAttr, Specialization> specializations;
719 
720  // Record all the layers which are being enabled.
721  if (enabledLayers) {
722  for (auto enabledLayer : enabledLayers.getAsRange<SymbolRefAttr>()) {
723  // Verify that this is a real layer.
724  if (!stc.lookupSymbolIn(circuit, enabledLayer)) {
725  mlir::emitError(circuit.getLoc()) << "unknown layer " << enabledLayer;
726  signalPassFailure();
727  return;
728  }
729  specializations[enabledLayer] = Specialization::Enable;
730  }
731  }
732 
733  // Record all of the layers which are being disabled.
734  if (disabledLayers) {
735  for (auto disabledLayer : disabledLayers.getAsRange<SymbolRefAttr>()) {
736  // Verify that this is a real layer.
737  if (!stc.lookupSymbolIn(circuit, disabledLayer)) {
738  mlir::emitError(circuit.getLoc())
739  << "unknown layer " << disabledLayer;
740  signalPassFailure();
741  return;
742  }
743 
744  // Verify that we are not both enabling and disabling this layer.
745  auto [it, inserted] =
746  specializations.try_emplace(disabledLayer, Specialization::Disable);
747  if (!inserted && it->getSecond() == Specialization::Enable) {
748  mlir::emitError(circuit.getLoc())
749  << "layer " << disabledLayer << " both enabled and disabled";
750  signalPassFailure();
751  return;
752  }
753  }
754  }
755 
756  // Run specialization on our circuit.
757  SpecializeLayers(circuit, specializations)();
758  }
759 };
760 } // end anonymous namespace
761 
762 std::unique_ptr<Pass> firrtl::createSpecializeLayersPass() {
763  return std::make_unique<SpecializeLayersPass>();
764 }
assert(baseType &&"element must be base type")
static AnnotationSet forPort(Operation *op, size_t portNo)
Builder builder
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
This class provides a read-only projection of an annotation.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
std::unique_ptr< mlir::Pass > createSpecializeLayersPass()
static ResultTy transformReduce(MLIRContext *context, IterTy begin, IterTy end, ResultTy init, ReduceFuncTy reduce, TransformFuncTy transform)
Wrapper for llvm::parallelTransformReduce that performs the transform_reduce serially when MLIR multi...
Definition: FIRRTLUtils.h:295
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
static mlir::ArrayAttr getFromVoidPointer(void *ptr)