CIRCT  19.0.0git
SFCCompat.cpp
Go to the documentation of this file.
1 //===- SFCCompat.cpp - SFC Compatible 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 // This pass makes a number of updates to the circuit that are required to match
9 // the behavior of the Scala FIRRTL Compiler (SFC). This pass removes invalid
10 // values from the circuit. This is a combination of the Scala FIRRTL
11 // Compiler's RemoveRests pass and RemoveValidIf. This is done to remove two
12 // "interpretations" of invalid. Namely: (1) registers that are initialized to
13 // an invalid value (module scoped and looking through wires and connects only)
14 // are converted to an unitialized register and (2) invalid values are converted
15 // to zero (after rule 1 is applied). Additionally, this pass checks and
16 // disallows async reset registers that are not driven with a constant when
17 // looking through wires, connects, and nodes.
18 //
19 //===----------------------------------------------------------------------===//
20 
25 #include "mlir/IR/ImplicitLocOpBuilder.h"
26 #include "mlir/Pass/Pass.h"
27 #include "llvm/ADT/APSInt.h"
28 #include "llvm/ADT/TypeSwitch.h"
29 #include "llvm/Support/Debug.h"
30 
31 #define DEBUG_TYPE "firrtl-remove-resets"
32 
33 namespace circt {
34 namespace firrtl {
35 #define GEN_PASS_DEF_SFCCOMPAT
36 #include "circt/Dialect/FIRRTL/Passes.h.inc"
37 } // namespace firrtl
38 } // namespace circt
39 
40 using namespace circt;
41 using namespace firrtl;
42 
44  : public circt::firrtl::impl::SFCCompatBase<SFCCompatPass> {
45  void runOnOperation() override;
46 };
47 
49  LLVM_DEBUG(
50  llvm::dbgs() << "==----- Running SFCCompat "
51  "---------------------------------------------------===\n"
52  << "Module: '" << getOperation().getName() << "'\n";);
53 
54  bool madeModifications = false;
55  SmallVector<InvalidValueOp> invalidOps;
56 
57  auto fullAsyncResetAttr =
59  auto isFullAsyncResetAnno = [fullAsyncResetAttr](Annotation anno) {
60  return anno.getClassAttr() == fullAsyncResetAttr;
61  };
62  bool fullAsyncResetExists = AnnotationSet::removePortAnnotations(
63  getOperation(), [&](unsigned argNum, Annotation anno) {
64  return isFullAsyncResetAnno(anno);
65  });
66  getOperation()->walk(
67  [isFullAsyncResetAnno, &fullAsyncResetExists](Operation *op) {
68  fullAsyncResetExists |=
69  AnnotationSet::removeAnnotations(op, isFullAsyncResetAnno);
70  });
71  madeModifications |= fullAsyncResetExists;
72 
73  auto result = getOperation()->walk([&](Operation *op) {
74  // Populate invalidOps for later handling.
75  if (auto inv = dyn_cast<InvalidValueOp>(op)) {
76  invalidOps.push_back(inv);
77  return WalkResult::advance();
78  }
79  auto reg = dyn_cast<RegResetOp>(op);
80  if (!reg)
81  return WalkResult::advance();
82 
83  // If the `RegResetOp` has an invalidated initialization and we
84  // are not running FART, then replace it with a `RegOp`.
85  if (!fullAsyncResetExists &&
86  walkDrivers(reg.getResetValue(), true, true, false,
87  [](FieldRef dst, FieldRef src) {
88  return src.isa<InvalidValueOp>();
89  })) {
90  ImplicitLocOpBuilder builder(reg.getLoc(), reg);
91  RegOp newReg = builder.create<RegOp>(
92  reg.getResult().getType(), reg.getClockVal(), reg.getNameAttr(),
93  reg.getNameKindAttr(), reg.getAnnotationsAttr(),
94  reg.getInnerSymAttr(), reg.getForceableAttr());
95  reg.replaceAllUsesWith(newReg);
96  reg.erase();
97  madeModifications = true;
98  return WalkResult::advance();
99  }
100 
101  // If the `RegResetOp` has an asynchronous reset and the reset value is not
102  // a module-scoped constant when looking through wires and nodes, then
103  // generate an error. This implements the SFC's CheckResets pass.
104  if (!isa<AsyncResetType>(reg.getResetSignal().getType()))
105  return WalkResult::advance();
106  if (walkDrivers(
107  reg.getResetValue(), true, true, true,
108  [&](FieldRef dst, FieldRef src) {
109  if (src.isa<ConstantOp, InvalidValueOp, SpecialConstantOp,
110  AggregateConstantOp>())
111  return true;
112  auto diag = emitError(reg.getLoc());
113  auto [fieldName, rootKnown] = getFieldName(dst);
114  diag << "register " << reg.getNameAttr()
115  << " has an async reset, but its reset value";
116  if (rootKnown)
117  diag << " \"" << fieldName << "\"";
118  diag << " is not driven with a constant value through wires, "
119  "nodes, or connects";
120  std::tie(fieldName, rootKnown) = getFieldName(src);
121  diag.attachNote(src.getLoc())
122  << "reset driver is "
123  << (rootKnown ? ("\"" + fieldName + "\"") : "here");
124  return false;
125  }))
126  return WalkResult::advance();
127  return WalkResult::interrupt();
128  });
129 
130  if (result.wasInterrupted())
131  return signalPassFailure();
132 
133  // Convert all invalid values to zero.
134  for (auto inv : invalidOps) {
135  // Delete invalids which have no uses.
136  if (inv->getUses().empty()) {
137  inv->erase();
138  madeModifications = true;
139  continue;
140  }
141  ImplicitLocOpBuilder builder(inv.getLoc(), inv);
142  Value replacement =
144  .Case<ClockType, AsyncResetType, ResetType>(
145  [&](auto type) -> Value {
146  return builder.create<SpecialConstantOp>(
147  type, builder.getBoolAttr(false));
148  })
149  .Case<IntType>([&](IntType type) -> Value {
150  return builder.create<ConstantOp>(type, getIntZerosAttr(type));
151  })
152  .Case<BundleType, FVectorType>([&](auto type) -> Value {
153  auto width = circt::firrtl::getBitWidth(type);
154  assert(width && "width must be inferred");
155  auto zero = builder.create<ConstantOp>(APSInt(*width));
156  return builder.create<BitCastOp>(type, zero);
157  })
158  .Default([&](auto) {
159  llvm_unreachable("all types are supported");
160  return Value();
161  });
162  inv.replaceAllUsesWith(replacement);
163  inv.erase();
164  madeModifications = true;
165  }
166 
167  if (!madeModifications)
168  return markAllAnalysesPreserved();
169 }
170 
171 std::unique_ptr<mlir::Pass> circt::firrtl::createSFCCompatPass() {
172  return std::make_unique<SFCCompatPass>();
173 }
assert(baseType &&"element must be base type")
int32_t width
Definition: FIRRTL.cpp:36
This class represents a reference to a specific field or element of an aggregate value.
Definition: FieldRef.h:28
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
static bool removePortAnnotations(Operation *module, llvm::function_ref< bool(unsigned, Annotation)> predicate)
Remove all port annotations from a module or extmodule for which predicate returns true.
This class provides a read-only projection of an annotation.
This class implements the same functionality as TypeSwitch except that it uses firrtl::type_dyn_cast ...
Definition: FIRRTLTypes.h:520
FIRRTLTypeSwitch< T, ResultT > & Case(CallableT &&caseFn)
Add a case on the given type.
Definition: FIRRTLTypes.h:530
This is the common base class between SIntType and UIntType.
Definition: FIRRTLTypes.h:296
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
constexpr const char * fullAsyncResetAnnoClass
Annotation that marks a reset (port or wire) and domain.
std::unique_ptr< mlir::Pass > createSFCCompatPass()
Definition: SFCCompat.cpp:171
bool walkDrivers(FIRRTLBaseValue value, bool lookThroughWires, bool lookThroughNodes, bool lookThroughCasts, WalkDriverCallback callback)
std::optional< int64_t > getBitWidth(FIRRTLBaseType type, bool ignoreFlip=false)
IntegerAttr getIntZerosAttr(Type type)
Utility for generating a constant zero attribute.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
Definition: seq.py:20
void runOnOperation() override
Definition: SFCCompat.cpp:48