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