CIRCT  20.0.0git
InferReadWrite.cpp
Go to the documentation of this file.
1 //===- InferReadWrite.cpp - Infer Read Write Memory -----------------------===//
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 InferReadWrite pass.
10 //
11 //===----------------------------------------------------------------------===//
12 
18 #include "mlir/IR/ImplicitLocOpBuilder.h"
19 #include "mlir/Pass/Pass.h"
20 #include "llvm/ADT/APSInt.h"
21 #include "llvm/ADT/StringSwitch.h"
22 #include "llvm/ADT/TypeSwitch.h"
23 #include "llvm/Support/Debug.h"
24 
25 #define DEBUG_TYPE "firrtl-infer-read-write"
26 
27 namespace circt {
28 namespace firrtl {
29 #define GEN_PASS_DEF_INFERREADWRITE
30 #include "circt/Dialect/FIRRTL/Passes.h.inc"
31 } // namespace firrtl
32 } // namespace circt
33 
34 using namespace circt;
35 using namespace firrtl;
36 
37 namespace {
38 struct InferReadWritePass
39  : public circt::firrtl::impl::InferReadWriteBase<InferReadWritePass> {
40 
41  /// This pass performs two memory transformations:
42  /// 1. If the multi-bit enable port is connected to a constant 1,
43  /// then, replace with a single bit mask. Create a new memory with a
44  /// 1 bit mask, and replace the old memory with it. The single bit mask
45  /// memory is always lowered to an unmasked memory.
46  /// 2. If the read and write enable ports are trivially mutually exclusive,
47  /// then create a new memory with a single read/write port, and replace
48  /// the old memory with it.
49  void runOnOperation() override {
50  LLVM_DEBUG(llvm::dbgs() << "\n Running Infer Read Write on module:"
51  << getOperation().getName());
52  SmallVector<Operation *> opsToErase;
53 
54  auto result = getOperation().walk([&](Operation *op) {
55  // This pass is going to have problems if it tries to determine signal
56  // drivers in the presence of WhenOps. Conservatively error out if we see
57  // any WhenOps.
58  //
59  // TODO: This would be better handled if WhenOps were moved into the
60  // CHIRRTL dialect so that this pass could more strongly specify that it
61  // only works on FIRRTL as opposed to a subset of FIRRTL.
62  if (isa<WhenOp>(op)) {
63  op->emitOpError()
64  << "is unsupported by InferReadWrite as this pass cannot trace "
65  "signal drivers in their presence. Please run `ExpandWhens` to "
66  "remove these operations before running this pass.";
67  return WalkResult::interrupt();
68  }
69 
70  MemOp memOp = dyn_cast<MemOp>(op);
71  if (!memOp)
72  return WalkResult::advance();
73 
74  inferUnmasked(memOp, opsToErase);
75  simplifyWmode(memOp);
76  size_t nReads, nWrites, nRWs, nDbgs;
77  memOp.getNumPorts(nReads, nWrites, nRWs, nDbgs);
78  // Run the analysis only for Seq memories (latency=1) and a single read
79  // and write ports.
80  if (!(nReads == 1 && nWrites == 1 && nRWs == 0) ||
81  !(memOp.getReadLatency() == 1 && memOp.getWriteLatency() == 1))
82  return WalkResult::skip();
83  SmallVector<Attribute, 4> resultNames;
84  SmallVector<Type, 4> resultTypes;
85  SmallVector<Attribute> portAtts;
86  SmallVector<Attribute, 4> portAnnotations;
87  Value rClock, wClock;
88  // The memory has exactly two ports.
89  SmallVector<Value> readTerms, writeTerms;
90  for (const auto &portIt : llvm::enumerate(memOp.getResults())) {
91  Attribute portAnno;
92  portAnno = memOp.getPortAnnotation(portIt.index());
93  if (memOp.getPortKind(portIt.index()) == MemOp::PortKind::Debug) {
94  resultNames.push_back(memOp.getPortName(portIt.index()));
95  resultTypes.push_back(memOp.getResult(portIt.index()).getType());
96  portAnnotations.push_back(portAnno);
97  continue;
98  }
99  // Append the annotations from the two ports.
100  if (!cast<ArrayAttr>(portAnno).empty())
101  portAtts.push_back(memOp.getPortAnnotation(portIt.index()));
102  // Get the port value.
103  Value portVal = portIt.value();
104  // Get the port kind.
105  bool isReadPort =
106  memOp.getPortKind(portIt.index()) == MemOp::PortKind::Read;
107  // Iterate over all users of the port.
108  for (Operation *u : portVal.getUsers())
109  if (auto sf = dyn_cast<SubfieldOp>(u)) {
110  // Get the field name.
111  auto fName = sf.getInput().getType().base().getElementName(
112  sf.getFieldIndex());
113  // If this is the enable field, record the product terms(the And
114  // expression tree).
115  if (fName == "en")
116  getProductTerms(sf, isReadPort ? readTerms : writeTerms);
117 
118  else if (fName == "clk") {
119  if (isReadPort)
120  rClock = getConnectSrc(sf);
121  else
122  wClock = getConnectSrc(sf);
123  }
124  }
125  // End of loop for getting MemOp port users.
126  }
127  if (!sameDriver(rClock, wClock))
128  return WalkResult::skip();
129 
130  rClock = wClock;
131  LLVM_DEBUG(
132  llvm::dbgs() << "\n read clock:" << rClock
133  << " --- write clock:" << wClock;
134  llvm::dbgs() << "\n Read terms==>"; for (auto t
135  : readTerms) llvm::dbgs()
136  << "\n term::" << t;
137 
138  llvm::dbgs() << "\n Write terms==>"; for (auto t
139  : writeTerms) llvm::dbgs()
140  << "\n term::" << t;
141 
142  );
143  // If the read and write clocks are the same, and if any of the write
144  // enable product terms are a complement of the read enable, then return
145  // the write enable term.
146  auto complementTerm = checkComplement(readTerms, writeTerms);
147  if (!complementTerm)
148  return WalkResult::skip();
149 
150  // Create the merged rw port for the new memory.
151  resultNames.push_back(StringAttr::get(memOp.getContext(), "rw"));
152  // Set the type of the rw port.
153  resultTypes.push_back(MemOp::getTypeForPort(
154  memOp.getDepth(), memOp.getDataType(), MemOp::PortKind::ReadWrite,
155  memOp.getMaskBits()));
156  ImplicitLocOpBuilder builder(memOp.getLoc(), memOp);
157  portAnnotations.push_back(builder.getArrayAttr(portAtts));
158  // Create the new rw memory.
159  auto rwMem = builder.create<MemOp>(
160  resultTypes, memOp.getReadLatency(), memOp.getWriteLatency(),
161  memOp.getDepth(), RUWAttr::Undefined,
162  builder.getArrayAttr(resultNames), memOp.getNameAttr(),
163  memOp.getNameKind(), memOp.getAnnotations(),
164  builder.getArrayAttr(portAnnotations), memOp.getInnerSymAttr(),
165  memOp.getInitAttr(), memOp.getPrefixAttr());
166  ++numRWPortMemoriesInferred;
167  auto rwPort = rwMem->getResult(nDbgs);
168  // Create the subfield access to all fields of the port.
169  // The addr should be connected to read/write address depending on the
170  // read/write mode.
171  auto addr = builder.create<SubfieldOp>(rwPort, "addr");
172  // Enable is high whenever the memory is written or read.
173  auto enb = builder.create<SubfieldOp>(rwPort, "en");
174  // Read/Write clock.
175  auto clk = builder.create<SubfieldOp>(rwPort, "clk");
176  auto readData = builder.create<SubfieldOp>(rwPort, "rdata");
177  // wmode is high when the port is in write mode. That is this can be
178  // connected to the write enable.
179  auto wmode = builder.create<SubfieldOp>(rwPort, "wmode");
180  auto writeData = builder.create<SubfieldOp>(rwPort, "wdata");
181  auto mask = builder.create<SubfieldOp>(rwPort, "wmask");
182  // Temp wires to replace the original memory connects.
183  auto rAddr =
184  builder.create<WireOp>(addr.getType(), "readAddr").getResult();
185  auto wAddr =
186  builder.create<WireOp>(addr.getType(), "writeAddr").getResult();
187  auto wEnWire =
188  builder.create<WireOp>(enb.getType(), "writeEnable").getResult();
189  auto rEnWire =
190  builder.create<WireOp>(enb.getType(), "readEnable").getResult();
191  auto writeClock =
192  builder.create<WireOp>(ClockType::get(enb.getContext())).getResult();
193  // addr = Mux(WriteEnable, WriteAddress, ReadAddress).
194  builder.create<MatchingConnectOp>(
195  addr, builder.create<MuxPrimOp>(wEnWire, wAddr, rAddr));
196  // Enable = Or(WriteEnable, ReadEnable).
197  builder.create<MatchingConnectOp>(
198  enb, builder.create<OrPrimOp>(rEnWire, wEnWire));
199  builder.setInsertionPointToEnd(wmode->getBlock());
200  builder.create<MatchingConnectOp>(wmode, complementTerm);
201  // Now iterate over the original memory read and write ports.
202  size_t dbgsIndex = 0;
203  for (const auto &portIt : llvm::enumerate(memOp.getResults())) {
204  // Get the port value.
205  Value portVal = portIt.value();
206  if (memOp.getPortKind(portIt.index()) == MemOp::PortKind::Debug) {
207  memOp.getResult(portIt.index())
208  .replaceAllUsesWith(rwMem.getResult(dbgsIndex));
209  dbgsIndex++;
210  continue;
211  }
212  // Get the port kind.
213  bool isReadPort =
214  memOp.getPortKind(portIt.index()) == MemOp::PortKind::Read;
215  // Iterate over all users of the port, which are the subfield ops, and
216  // replace them.
217  for (Operation *u : portVal.getUsers())
218  if (auto sf = dyn_cast<SubfieldOp>(u)) {
219  StringRef fName = sf.getInput().getType().base().getElementName(
220  sf.getFieldIndex());
221  Value repl;
222  if (isReadPort)
223  repl = llvm::StringSwitch<Value>(fName)
224  .Case("en", rEnWire)
225  .Case("clk", clk)
226  .Case("addr", rAddr)
227  .Case("data", readData);
228  else
229  repl = llvm::StringSwitch<Value>(fName)
230  .Case("en", wEnWire)
231  .Case("clk", writeClock)
232  .Case("addr", wAddr)
233  .Case("data", writeData)
234  .Case("mask", mask);
235  sf.replaceAllUsesWith(repl);
236  // Once all the uses of the subfield op replaced, delete it.
237  opsToErase.push_back(sf);
238  }
239  }
240  simplifyWmode(rwMem);
241  // All uses for all results of mem removed, now erase the memOp.
242  opsToErase.push_back(memOp);
243  return WalkResult::advance();
244  });
245 
246  if (result.wasInterrupted())
247  return signalPassFailure();
248 
249  for (auto *o : opsToErase)
250  o->erase();
251  }
252 
253 private:
254  // Get the source value which is connected to the dst.
255  Value getConnectSrc(Value dst) {
256  for (auto *c : dst.getUsers())
257  if (auto connect = dyn_cast<FConnectLike>(c))
258  if (connect.getDest() == dst)
259  return connect.getSrc();
260 
261  return nullptr;
262  }
263 
264  /// If the ports are not directly connected to the same clock, then check
265  /// if indirectly connected to the same clock.
266  bool sameDriver(Value rClock, Value wClock) {
267  if (rClock == wClock)
268  return true;
269  DenseSet<Value> rClocks, wClocks;
270  while (rClock) {
271  // Record all the values which are indirectly connected to the clock
272  // port.
273  rClocks.insert(rClock);
274  rClock = getConnectSrc(rClock);
275  }
276 
277  bool sameClock = false;
278  // Now check all the indirect connections to the write memory clock
279  // port.
280  while (wClock) {
281  if (rClocks.find(wClock) != rClocks.end()) {
282  sameClock = true;
283  break;
284  }
285  wClock = getConnectSrc(wClock);
286  }
287  return sameClock;
288  }
289 
290  void getProductTerms(Value enValue, SmallVector<Value> &terms) {
291  if (!enValue)
292  return;
293  SmallVector<Value> worklist;
294  worklist.push_back(enValue);
295  while (!worklist.empty()) {
296  auto term = worklist.back();
297  worklist.pop_back();
298  terms.push_back(term);
299  if (isa<BlockArgument>(term))
300  continue;
301  TypeSwitch<Operation *>(term.getDefiningOp())
302  .Case<NodeOp>([&](auto n) { worklist.push_back(n.getInput()); })
303  .Case<AndPrimOp>([&](AndPrimOp andOp) {
304  worklist.push_back(andOp.getOperand(0));
305  worklist.push_back(andOp.getOperand(1));
306  })
307  .Case<MuxPrimOp>([&](auto muxOp) {
308  // Check for the pattern when low is 0, which is equivalent to (sel
309  // & high)
310  // term = mux (sel, high, 0) => term = sel & high
311  if (ConstantOp cLow = dyn_cast_or_null<ConstantOp>(
312  muxOp.getLow().getDefiningOp()))
313  if (cLow.getValue().isZero()) {
314  worklist.push_back(muxOp.getSel());
315  worklist.push_back(muxOp.getHigh());
316  }
317  })
318  .Default([&](auto) {
319  if (auto src = getConnectSrc(term))
320  worklist.push_back(src);
321  });
322  }
323  }
324 
325  /// If any of the terms in the read enable, prodTerms[0] is a complement of
326  /// any of the terms in the write enable prodTerms[1], return the
327  /// corresponding write enable term. prodTerms[0], prodTerms[1] is a vector of
328  /// Value, each of which correspond to the two product terms of read and write
329  /// enable respectively.
330  Value checkComplement(const SmallVector<Value> &readTerms,
331  const SmallVector<Value> &writeTerms) {
332  // Foreach Value in first term, check if it is the complement of any of the
333  // Value in second term.
334  for (auto t1 : readTerms)
335  for (auto t2 : writeTerms) {
336  // Return t2, t1 is a Not of t2.
337  if (!isa<BlockArgument>(t1) && isa<NotPrimOp>(t1.getDefiningOp()))
338  if (cast<NotPrimOp>(t1.getDefiningOp()).getInput() == t2)
339  return t2;
340  // Else Return t2, if t2 is a Not of t1.
341  if (!isa<BlockArgument>(t2) && isa<NotPrimOp>(t2.getDefiningOp()))
342  if (cast<NotPrimOp>(t2.getDefiningOp()).getInput() == t1)
343  return t2;
344  }
345 
346  return {};
347  }
348 
349  void handleCatPrimOp(CatPrimOp defOp, SmallVectorImpl<Value> &bits) {
350 
351  long lastSize = 0;
352  // Cat the bits of both the operands.
353  for (auto operand : defOp->getOperands()) {
354  SmallVectorImpl<Value> &opBits = valueBitsSrc[operand];
355  size_t s =
356  getBitWidth(type_cast<FIRRTLBaseType>(operand.getType())).value();
357  assert(opBits.size() == s);
358  for (long i = lastSize, e = lastSize + s; i != e; ++i)
359  bits[i] = opBits[i - lastSize];
360  lastSize = s;
361  }
362  }
363 
364  void handleBitsPrimOp(BitsPrimOp bitsPrim, SmallVectorImpl<Value> &bits) {
365 
366  SmallVectorImpl<Value> &opBits = valueBitsSrc[bitsPrim.getInput()];
367  for (size_t srcIndex = bitsPrim.getLo(), e = bitsPrim.getHi(), i = 0;
368  srcIndex <= e; ++srcIndex, ++i)
369  bits[i] = opBits[srcIndex];
370  }
371 
372  // Try to extract the value assigned to each bit of `val`. This is a heuristic
373  // to determine if each bit of the `val` is assigned the same value.
374  // Common pattern that this heuristic detects,
375  // mask = {{w1,w1},{w2,w2}}}
376  // w1 = w[0]
377  // w2 = w[0]
378  bool areBitsDrivenBySameSource(Value val) {
379  SmallVector<Value> stack;
380  stack.push_back(val);
381 
382  while (!stack.empty()) {
383  auto val = stack.back();
384  if (valueBitsSrc.contains(val)) {
385  stack.pop_back();
386  continue;
387  }
388 
389  auto size = getBitWidth(type_cast<FIRRTLBaseType>(val.getType()));
390  // Cannot analyze aggregate types.
391  if (!size.has_value())
392  return false;
393 
394  auto bitsSize = size.value();
395  if (auto *defOp = val.getDefiningOp()) {
396  if (isa<CatPrimOp>(defOp)) {
397  bool operandsDone = true;
398  // If the value is a cat of other values, compute the bits of the
399  // operands.
400  for (auto operand : defOp->getOperands()) {
401  if (valueBitsSrc.contains(operand))
402  continue;
403  stack.push_back(operand);
404  operandsDone = false;
405  }
406  if (!operandsDone)
407  continue;
408 
409  valueBitsSrc[val].resize_for_overwrite(bitsSize);
410  handleCatPrimOp(cast<CatPrimOp>(defOp), valueBitsSrc[val]);
411  } else if (auto bitsPrim = dyn_cast<BitsPrimOp>(defOp)) {
412  auto input = bitsPrim.getInput();
413  if (!valueBitsSrc.contains(input)) {
414  stack.push_back(input);
415  continue;
416  }
417  valueBitsSrc[val].resize_for_overwrite(bitsSize);
418  handleBitsPrimOp(bitsPrim, valueBitsSrc[val]);
419  } else if (auto constOp = dyn_cast<ConstantOp>(defOp)) {
420  auto constVal = constOp.getValue();
421  valueBitsSrc[val].resize_for_overwrite(bitsSize);
422  if (constVal.isAllOnes() || constVal.isZero()) {
423  for (auto &b : valueBitsSrc[val])
424  b = constOp;
425  } else
426  return false;
427  } else if (auto wireOp = dyn_cast<WireOp>(defOp)) {
428  if (bitsSize != 1)
429  return false;
430  valueBitsSrc[val].resize_for_overwrite(bitsSize);
431  if (auto src = getConnectSrc(wireOp.getResult())) {
432  valueBitsSrc[val][0] = src;
433  } else
434  valueBitsSrc[val][0] = wireOp.getResult();
435  } else
436  return false;
437  } else
438  return false;
439  stack.pop_back();
440  }
441  if (!valueBitsSrc.contains(val))
442  return false;
443  return llvm::all_equal(valueBitsSrc[val]);
444  }
445 
446  // Remove redundant dependence of wmode on the enable signal. wmode can assume
447  // the enable signal be true.
448  void simplifyWmode(MemOp &memOp) {
449 
450  // Iterate over all results, and find the enable and wmode fields of the RW
451  // port.
452  for (const auto &portIt : llvm::enumerate(memOp.getResults())) {
453  auto portKind = memOp.getPortKind(portIt.index());
454  if (portKind != MemOp::PortKind::ReadWrite)
455  continue;
456  Value enableDriver, wmodeDriver;
457  Value portVal = portIt.value();
458  // Iterate over all users of the rw port.
459  for (Operation *u : portVal.getUsers())
460  if (auto sf = dyn_cast<SubfieldOp>(u)) {
461  // Get the field name.
462  auto fName =
463  sf.getInput().getType().base().getElementName(sf.getFieldIndex());
464  // Record the enable and wmode fields.
465  if (fName.contains("en"))
466  enableDriver = getConnectSrc(sf.getResult());
467  if (fName.contains("wmode"))
468  wmodeDriver = getConnectSrc(sf.getResult());
469  }
470 
471  if (enableDriver && wmodeDriver) {
472  ImplicitLocOpBuilder builder(memOp.getLoc(), memOp);
473  builder.setInsertionPointToStart(
474  memOp->getParentOfType<FModuleOp>().getBodyBlock());
475  auto constOne = builder.create<ConstantOp>(
476  UIntType::get(builder.getContext(), 1), APInt(1, 1));
477  setEnable(enableDriver, wmodeDriver, constOne);
478  }
479  }
480  }
481 
482  // Replace any occurence of enable on the expression tree of wmode with a
483  // constant one.
484  void setEnable(Value enableDriver, Value wmodeDriver, Value constOne) {
485  auto getDriverOp = [&](Value dst) -> Operation * {
486  // Look through one level of wire to get the driver op.
487  auto *defOp = dst.getDefiningOp();
488  if (defOp) {
489  if (isa<WireOp>(defOp))
490  dst = getConnectSrc(dst);
491  if (dst)
492  defOp = dst.getDefiningOp();
493  }
494  return defOp;
495  };
496  SmallVector<Value> stack;
497  llvm::SmallDenseSet<Value> visited;
498  stack.push_back(wmodeDriver);
499  while (!stack.empty()) {
500  auto driver = stack.pop_back_val();
501  if (!visited.insert(driver).second)
502  continue;
503  auto *defOp = getDriverOp(driver);
504  if (!defOp)
505  continue;
506  for (auto operand : llvm::enumerate(defOp->getOperands())) {
507  if (operand.value() == enableDriver)
508  defOp->setOperand(operand.index(), constOne);
509  else
510  stack.push_back(operand.value());
511  }
512  }
513  }
514 
515  void inferUnmasked(MemOp &memOp, SmallVector<Operation *> &opsToErase) {
516  bool isMasked = true;
517 
518  // Iterate over all results, and check if the mask field of the result is
519  // connected to a multi-bit constant 1.
520  for (const auto &portIt : llvm::enumerate(memOp.getResults())) {
521  // Read ports donot have the mask field.
522  if (memOp.getPortKind(portIt.index()) == MemOp::PortKind::Read ||
523  memOp.getPortKind(portIt.index()) == MemOp::PortKind::Debug)
524  continue;
525  Value portVal = portIt.value();
526  // Iterate over all users of the write/rw port.
527  for (Operation *u : portVal.getUsers())
528  if (auto sf = dyn_cast<SubfieldOp>(u)) {
529  // Get the field name.
530  auto fName =
531  sf.getInput().getType().base().getElementName(sf.getFieldIndex());
532  // Check if this is the mask field.
533  if (fName.contains("mask")) {
534  // Already 1 bit, nothing to do.
535  if (sf.getResult().getType().getBitWidthOrSentinel() == 1)
536  continue;
537  // Check what is the mask field directly connected to.
538  // If we can infer that all the bits of the mask are always assigned
539  // the same value, then the memory is unmasked.
540  if (auto maskVal = getConnectSrc(sf))
541  if (areBitsDrivenBySameSource(maskVal))
542  isMasked = false;
543  }
544  }
545  }
546 
547  if (!isMasked) {
548  // Replace with a new memory of 1 bit mask.
549  ImplicitLocOpBuilder builder(memOp.getLoc(), memOp);
550  // Copy the result type, except the mask bits.
551  SmallVector<Type, 4> resultTypes;
552  for (size_t i = 0, e = memOp.getNumResults(); i != e; ++i)
553  resultTypes.push_back(
554  MemOp::getTypeForPort(memOp.getDepth(), memOp.getDataType(),
555  memOp.getPortKind(i), /*maskBits=*/1));
556 
557  // Copy everything from old memory, except the result type.
558  auto newMem = builder.create<MemOp>(
559  resultTypes, memOp.getReadLatencyAttr(), memOp.getWriteLatencyAttr(),
560  memOp.getDepthAttr(), memOp.getRuwAttr(), memOp.getPortNamesAttr(),
561  memOp.getNameAttr(), memOp.getNameKindAttr(),
562  memOp.getAnnotationsAttr(), memOp.getPortAnnotationsAttr(),
563  memOp.getInnerSymAttr(), memOp.getInitAttr(), memOp.getPrefixAttr());
564  // Now replace the result of old memory with the new one.
565  for (const auto &portIt : llvm::enumerate(memOp.getResults())) {
566  // Old result.
567  Value oldPort = portIt.value();
568  // New result.
569  auto newPortVal = newMem->getResult(portIt.index());
570  // If read port, then blindly replace.
571  if (memOp.getPortKind(portIt.index()) == MemOp::PortKind::Read ||
572  memOp.getPortKind(portIt.index()) == MemOp::PortKind::Debug) {
573  oldPort.replaceAllUsesWith(newPortVal);
574  continue;
575  }
576  // Otherwise, all fields can be blindly replaced, except mask field.
577  for (Operation *u : oldPort.getUsers()) {
578  auto oldRes = dyn_cast<SubfieldOp>(u);
579  auto sf =
580  builder.create<SubfieldOp>(newPortVal, oldRes.getFieldIndex());
581  auto fName =
582  sf.getInput().getType().base().getElementName(sf.getFieldIndex());
583  // Replace all mask fields with a one bit constant 1.
584  // Replace all other fields with the new port.
585  if (fName.contains("mask")) {
586  WireOp dummy = builder.create<WireOp>(oldRes.getType());
587  oldRes->replaceAllUsesWith(dummy);
588  builder.create<MatchingConnectOp>(
589  sf, builder.create<ConstantOp>(
590  UIntType::get(builder.getContext(), 1), APInt(1, 1)));
591  } else
592  oldRes->replaceAllUsesWith(sf);
593 
594  opsToErase.push_back(oldRes);
595  }
596  }
597  opsToErase.push_back(memOp);
598  memOp = newMem;
599  }
600  }
601 
602  // Record of what are the source values that drive each bit of a value. Used
603  // to check if each bit of a value is being driven by the same source.
604  llvm::DenseMap<Value, SmallVector<Value>> valueBitsSrc;
605 };
606 } // end anonymous namespace
607 
608 std::unique_ptr<mlir::Pass> circt::firrtl::createInferReadWritePass() {
609  return std::make_unique<InferReadWritePass>();
610 }
assert(baseType &&"element must be base type")
static InstancePath empty
def connect(destination, source)
Definition: support.py:39
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
std::unique_ptr< mlir::Pass > createInferReadWritePass()
std::optional< int64_t > getBitWidth(FIRRTLBaseType type, bool ignoreFlip=false)
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