CIRCT 20.0.0git
Loading...
Searching...
No Matches
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
25namespace circt {
26namespace firrtl {
27#define GEN_PASS_DEF_INFERREADWRITE
28#include "circt/Dialect/FIRRTL/Passes.h.inc"
29} // namespace firrtl
30} // namespace circt
31
32using namespace circt;
33using namespace firrtl;
34
35namespace {
36struct 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
251private:
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
606std::unique_ptr<mlir::Pass> circt::firrtl::createInferReadWritePass() {
607 return std::make_unique<InferReadWritePass>();
608}
assert(baseType &&"element must be base type")
connect(destination, source)
Definition support.py:39
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.