CIRCT 20.0.0git
Loading...
Searching...
No Matches
ProbesToSignals.cpp
Go to the documentation of this file.
1//===- ProbesToSignals.cpp - Probes to Signals ----------------------------===//
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 ProbesToSignals pass. This pass replaces probes with
10// signals of the same type. This is not considered a lowering but a
11// behavior-changing transformation that may break ABI compatibility anywhere
12// probes are used relevant to ABI.
13//
14// Pre-requisites for complete conversion:
15// * LowerOpenAggs
16// - Simplifies this pass, Probes are always separate.
17// * ExpandWhens
18// - ref.define is "static single connect", and FIRRTL does not have
19// an equivalent for hardware connections. As a result, probes sent out
20// from under a "when" cannot be represented currently.
21//
22// Suggested:
23// * Inference passes, especially width inference. Probes infer slightly
24// differently than non-probes do (must have same width along the chain).
25//
26// Colored probes are not supported.
27// Specialize layers on or off to remove colored probes first.
28//
29// Debug ports on FIRRTL memories are not currently supported,
30// but CHIRRTL debug ports are handled.
31//
32//===----------------------------------------------------------------------===//
33
40#include "circt/Support/Debug.h"
41#include "mlir/IR/Threading.h"
42#include "mlir/Transforms/DialectConversion.h"
43#include "llvm/ADT/STLExtras.h"
44
45#define DEBUG_TYPE "firrtl-probes-to-signals"
46
47namespace circt {
48namespace firrtl {
49#define GEN_PASS_DEF_PROBESTOSIGNALS
50#include "circt/Dialect/FIRRTL/Passes.h.inc"
51} // namespace firrtl
52} // namespace circt
53
54using namespace circt;
55using namespace firrtl;
56
57//===----------------------------------------------------------------------===//
58// Probes to Signals
59//===----------------------------------------------------------------------===//
60
61namespace {
62
63class ProbeVisitor : public FIRRTLVisitor<ProbeVisitor, LogicalResult> {
64public:
65 ProbeVisitor(hw::InnerRefNamespace &irn) : irn(irn) {}
66
67 /// Entrypoint.
68 LogicalResult visit(FModuleLike mod);
69
70 using FIRRTLVisitor<ProbeVisitor, LogicalResult>::visitDecl;
71 using FIRRTLVisitor<ProbeVisitor, LogicalResult>::visitExpr;
72 using FIRRTLVisitor<ProbeVisitor, LogicalResult>::visitStmt;
73
74 //===--------------------------------------------------------------------===//
75 // Type conversion
76 //===--------------------------------------------------------------------===//
77
78 /// Return the converted type, null if same, failure on error.
79 static FailureOr<Type> convertType(Type type, Location loc) {
80 auto err = [type, loc](const Twine &message) {
81 return mlir::emitError(loc, message) << ", cannot convert type " << type;
82 };
83 if (isa<OpenBundleType, OpenVectorType>(type))
84 return err("open aggregates not supported");
85
86 auto refType = dyn_cast<RefType>(type);
87 if (!refType)
88 return Type();
89
90 if (refType.getLayer())
91 return err("layer-colored probes not supported");
92
93 // Otherwise, this maps to the probed type.
94 return refType.getType();
95 }
96
97 /// Return "target" type, or failure on error.
98 static FailureOr<Type> mapType(Type type, Location loc) {
99 auto newType = convertType(type, loc);
100 if (failed(newType))
101 return failure();
102 return *newType ? *newType : type;
103 }
104
105 /// Map a range of types, return if changes needed.
106 template <typename R>
107 static FailureOr<bool> mapRange(R &&range, Location loc,
108 SmallVectorImpl<Type> &newTypes) {
109 newTypes.reserve(llvm::size(range));
110
111 bool anyConverted = false;
112 for (auto type : range) {
113 auto conv = mapType(type, loc);
114 if (failed(conv))
115 return failure();
116 newTypes.emplace_back(*conv);
117 anyConverted |= *conv != type;
118 }
119 return anyConverted;
120 }
121
122 // CHIRRTL
123 LogicalResult visitMemoryDebugPortOp(chirrtl::MemoryDebugPortOp op);
124
125 // Visitors
126
127 LogicalResult visitInvalidOp(Operation *op) {
128 if (auto dbgPortOp = dyn_cast<chirrtl::MemoryDebugPortOp>(op))
129 return visitMemoryDebugPortOp(dbgPortOp);
130
131 return visitUnhandledOp(op);
132 }
133 LogicalResult visitUnhandledOp(Operation *op);
134
135 /// Check declarations specifically before forwarding to unhandled.
136 LogicalResult visitUnhandledDecl(Operation *op) {
137 // Check for and handle active forceable declarations.
138 if (auto fop = dyn_cast<Forceable>(op); fop && fop.isForceable())
139 return visitActiveForceableDecl(fop);
140 return visitUnhandledOp(op);
141 }
142
143 // Declarations
144
145 LogicalResult visitDecl(MemOp op);
146 LogicalResult visitDecl(WireOp op);
147 LogicalResult visitActiveForceableDecl(Forceable fop);
148
149 LogicalResult visitInstanceLike(Operation *op);
150 LogicalResult visitDecl(InstanceOp op) { return visitInstanceLike(op); }
151 LogicalResult visitDecl(InstanceChoiceOp op) { return visitInstanceLike(op); }
152
153 // Probe operations.
154
155 LogicalResult visitExpr(RWProbeOp op);
156 LogicalResult visitExpr(RefCastOp op);
157 LogicalResult visitExpr(RefResolveOp op);
158 LogicalResult visitExpr(RefSendOp op);
159 LogicalResult visitExpr(RefSubOp op);
160
161 LogicalResult visitStmt(RefDefineOp op);
162
163 // Force and release operations: reject as unsupported.
164 LogicalResult visitStmt(RefForceOp op) {
165 return op.emitError("force not supported");
166 }
167 LogicalResult visitStmt(RefForceInitialOp op) {
168 return op.emitError("force_initial not supported");
169 }
170 LogicalResult visitStmt(RefReleaseOp op) {
171 return op.emitError("release not supported");
172 }
173 LogicalResult visitStmt(RefReleaseInitialOp op) {
174 return op.emitError("release_initial not supported");
175 }
176
177private:
178 /// Map from probe-typed Value's to their non-probe equivalent.
179 DenseMap<Value, Value> probeToHWMap;
180
181 /// Forceable operations to demote.
182 SmallVector<Forceable> forceables;
183
184 /// Operations to delete.
185 SmallVector<Operation *> toDelete;
186
187 /// Read-only copy of inner-ref namespace for resolving inner refs.
189};
190
191} // end namespace
192
193//===----------------------------------------------------------------------===//
194// Visitor: FModuleLike
195//===----------------------------------------------------------------------===//
196
197static Block *getBodyBlock(FModuleLike mod) {
198 // Safety check for below, presently all modules have a region.
199 assert(mod->getNumRegions() == 1);
200 auto &blocks = mod->getRegion(0).getBlocks();
201 return !blocks.empty() ? &blocks.front() : nullptr;
202}
203
204/// Visit a module, converting its ports and internals to use hardware signals
205/// instead of probes.
206LogicalResult ProbeVisitor::visit(FModuleLike mod) {
207 // If module has strings describing XMR suffixes for its ports, reject.
208 if (auto internalPaths = mod->getAttrOfType<ArrayAttr>("internalPaths"))
209 return mod.emitError("cannot convert module with internal path");
210
211 // Ports -> new ports without probe-ness.
212 // For all probe ports, insert non-probe duplex values to use
213 // as their replacement while rewriting. Only if has body.
214 SmallVector<std::pair<size_t, WireOp>> wires;
215
216 auto portTypes = mod.getPortTypes();
217 auto portLocs = mod.getPortLocationsAttr().getAsRange<Location>();
218 SmallVector<Attribute> newPortTypes;
219
220 wires.reserve(portTypes.size());
221 newPortTypes.reserve(portTypes.size());
222 auto *block = getBodyBlock(mod);
223 bool portsToChange = false;
224 for (auto [idx, typeAttr, loc] : llvm::enumerate(portTypes, portLocs)) {
225 auto type = cast<TypeAttr>(typeAttr);
226 auto conv = convertType(type.getValue(), loc);
227 if (failed(conv))
228 return failure();
229 auto newType = *conv;
230
231 if (newType) {
232 portsToChange = true;
233 newPortTypes.push_back(TypeAttr::get(newType));
234 if (block) {
235 auto builder = OpBuilder::atBlockBegin(block);
236 wires.emplace_back(idx, builder.create<WireOp>(loc, newType));
237 probeToHWMap[block->getArgument(idx)] = wires.back().second.getData();
238 }
239 } else
240 newPortTypes.push_back(type);
241 }
242
243 // Update body, if present.
244 if (block &&
245 block
246 ->walk<mlir::WalkOrder::PreOrder>(
247 [&](Operation *op) -> WalkResult { return dispatchVisitor(op); })
248 .wasInterrupted())
249 return failure();
250
251 // Update signature and argument types.
252 if (portsToChange) {
253 mod.setPortTypesAttr(ArrayAttr::get(mod->getContext(), newPortTypes));
254
255 if (block) {
256 // We may also need to update the types on the block arguments.
257 for (auto [arg, typeAttr] :
258 llvm::zip_equal(block->getArguments(), newPortTypes))
259 arg.setType(cast<TypeAttr>(typeAttr).getValue());
260
261 // Drop the port stand-ins and RAUW to the block arguments.
262 for (auto [idx, wire] : wires) {
263 auto arg = block->getArgument(idx);
264 wire.getData().replaceAllUsesWith(arg);
265 wire.erase();
266 }
267 }
268 }
269
270 // Delete operations that were converted.
271 for (auto *op : llvm::reverse(toDelete))
272 op->erase();
273
274 // Demote forceable's.
275 for (auto fop : forceables)
276 firrtl::detail::replaceWithNewForceability(fop, false);
277
278 return success();
279}
280
281//===----------------------------------------------------------------------===//
282// Visitor: Unhandled
283//===----------------------------------------------------------------------===//
284
285LogicalResult ProbeVisitor::visitUnhandledOp(Operation *op) {
286 auto checkType = [&](auto type) -> bool {
287 // Return if conversion needed (or if error).
288 auto newType = convertType(type, op->getLoc());
289 if (failed(newType))
290 return true;
291 if (!*newType)
292 return false;
293
294 // Type found that needs to be converted, diagnose.
295 op->emitError("unhandled operation needs conversion of type ")
296 << type << " to " << *newType;
297 return true;
298 };
299
300 return success(llvm::none_of(op->getOperandTypes(), checkType) &&
301 llvm::none_of(op->getResultTypes(), checkType));
302}
303
304//===----------------------------------------------------------------------===//
305// Visitor: CHIRRTL
306//===----------------------------------------------------------------------===//
307LogicalResult
308ProbeVisitor::visitMemoryDebugPortOp(chirrtl::MemoryDebugPortOp op) {
309 auto conv = convertType(op.getResult().getType(), op.getLoc());
310 if (failed(conv))
311 return failure();
312 auto type = *conv;
313 assert(type);
314
315 auto vectype = type_cast<FVectorType>(type);
316
317 // Just assert the chirrtl memory IR has the expected structure,
318 // if it didn't many things break.
319 // Must be defined in same module, tapped memory must be comb mem.
320 auto mem = op.getMemory().getDefiningOp<chirrtl::CombMemOp>();
321 assert(mem);
322
323 // The following is adapted from LowerAnnotations.
324 Value clock;
325 for (auto *portOp : mem.getResult().getUsers()) {
326 for (auto result : portOp->getResults()) {
327 for (auto *user : result.getUsers()) {
328 auto accessOp = dyn_cast<chirrtl::MemoryPortAccessOp>(user);
329 if (!accessOp)
330 continue;
331 auto newClock = accessOp.getClock();
332 if (clock && clock != newClock)
333 return mem.emitOpError(
334 "has different clocks on different ports (this is ambiguous "
335 "when compiling without reference types)");
336 clock = newClock;
337 }
338 }
339 }
340 if (!clock)
341 return mem->emitOpError(
342 "does not have an access port to determine a clock connection (this "
343 "is necessary when compiling without reference types)");
344
345 // Add one port per memory address.
346 SmallVector<Value> data;
347 ImplicitLocOpBuilder builder(op.getLoc(), op);
348
349 // Insert new ports as late as possible (end of block containing the memory).
350 // This is necessary to preserve ordering of existing ports.
351 builder.setInsertionPointToEnd(mem->getBlock());
352 Type uintType = builder.getType<UIntType>();
353 for (uint64_t i = 0, e = mem.getType().getNumElements(); i != e; ++i) {
354 auto port = builder.create<chirrtl::MemoryPortOp>(
355 mem.getType().getElementType(),
356 chirrtl::CMemoryPortType::get(builder.getContext()), mem.getResult(),
357 MemDirAttr::Read, builder.getStringAttr("memTap_" + Twine(i)),
358 builder.getArrayAttr({}));
359 builder.create<chirrtl::MemoryPortAccessOp>(
360 port.getPort(),
361 builder.create<ConstantOp>(uintType, APSInt::getUnsigned(i)), clock);
362 data.push_back(port.getData());
363 }
364
365 // Package up all the reads into a vector.
366 assert(vectype == FVectorType::get(mem.getType().getElementType(),
367 mem.getType().getNumElements()));
368 auto vecData = builder.create<VectorCreateOp>(vectype, data);
369
370 // While the new ports are added as late as possible, the debug port
371 // operation we're replacing likely has users and those are before
372 // the new ports. Add a wire at a point we know dominates this operation
373 // and the new port access operations added above. This will be used for
374 // the existing users of the debug port.
375 builder.setInsertionPoint(mem);
376 auto wire = builder.create<WireOp>(vectype);
377 builder.setInsertionPointToEnd(mem->getBlock());
378 emitConnect(builder, wire.getData(), vecData);
379 probeToHWMap[op.getResult()] = wire.getData();
380 toDelete.push_back(op);
381 return success();
382}
383
384//===----------------------------------------------------------------------===//
385// Visitor: Declarations
386//===----------------------------------------------------------------------===//
387
388LogicalResult ProbeVisitor::visitDecl(MemOp op) {
389 // Scan for debug ports. These are not supported presently, diagnose.
390 SmallVector<Type> newTypes;
391 auto needsConv = mapRange(op->getResultTypes(), op->getLoc(), newTypes);
392 if (failed(needsConv))
393 return failure();
394 if (!*needsConv)
395 return success();
396
397 return op.emitError("memory has unsupported debug port (memtap)");
398}
399
400LogicalResult ProbeVisitor::visitDecl(WireOp op) {
401 if (op.isForceable())
402 return visitActiveForceableDecl(op);
403
404 auto conv = convertType(op.getDataRaw().getType(), op.getLoc());
405 if (failed(conv))
406 return failure();
407 auto type = *conv;
408 if (!type) // No conversion needed.
409 return success();
410
411 // New Wire of converted type.
412 ImplicitLocOpBuilder builder(op.getLoc(), op);
413 auto cloned = cast<WireOp>(builder.clone(*op));
414 cloned->getOpResults().front().setType(type);
415 probeToHWMap[op.getDataRaw()] = cloned.getData();
416 toDelete.push_back(op);
417 return success();
418}
419
420LogicalResult ProbeVisitor::visitActiveForceableDecl(Forceable fop) {
421 assert(fop.isForceable() && "must be called on active forceables");
422 // Map rw ref result to normal result.
423 auto data = fop.getData();
424 auto conv = mapType(fop.getDataRef().getType(), fop.getLoc());
425 if (failed(conv))
426 return failure();
427 auto newType = *conv;
428 forceables.push_back(fop);
429
430 assert(newType == data.getType().getPassiveType());
431 if (newType != data.getType()) {
432 ImplicitLocOpBuilder builder(fop.getLoc(), fop);
433 builder.setInsertionPointAfterValue(data);
434 auto wire = builder.create<WireOp>(newType);
435 emitConnect(builder, wire.getData(), data);
436 data = wire.getData();
437 }
438 probeToHWMap[fop.getDataRef()] = data;
439 return success();
440}
441
442LogicalResult ProbeVisitor::visitInstanceLike(Operation *op) {
443 SmallVector<Type> newTypes;
444 auto needsConv = mapRange(op->getResultTypes(), op->getLoc(), newTypes);
445 if (failed(needsConv))
446 return failure();
447 if (!*needsConv)
448 return success();
449
450 // New instance with converted types.
451 // Move users of unconverted results to the new operation.
452 ImplicitLocOpBuilder builder(op->getLoc(), op);
453 auto *newInst = builder.clone(*op);
454 for (auto [oldResult, newResult, newType] :
455 llvm::zip_equal(op->getOpResults(), newInst->getOpResults(), newTypes)) {
456 if (newType == oldResult.getType()) {
457 oldResult.replaceAllUsesWith(newResult);
458 continue;
459 }
460
461 newResult.setType(newType);
462 probeToHWMap[oldResult] = newResult;
463 }
464
465 toDelete.push_back(op);
466 return success();
467}
468
469//===----------------------------------------------------------------------===//
470// Visitor: Probe operations
471//===----------------------------------------------------------------------===//
472
473LogicalResult ProbeVisitor::visitStmt(RefDefineOp op) {
474 // ref.define x, y -> connect map(x), map(y)
475 // Be mindful of connect semantics when considering
476 // placement.
477
478 auto newDest = probeToHWMap.at(op.getDest());
479 auto newSrc = probeToHWMap.at(op.getSrc());
480
481 // Source must be ancestor of destination block for a connect
482 // to behave the same (generally).
483 assert(!isa<BlockArgument>(newDest));
484 auto *destDefiningOp = newDest.getDefiningOp();
485 assert(destDefiningOp);
486 if (!newSrc.getParentBlock()->findAncestorOpInBlock(*destDefiningOp)) {
487 // Conditional or sending out of a layer...
488 auto diag = op.emitError("unable to convert to equivalent connect");
489 diag.attachNote(op.getDest().getLoc()) << "destination here";
490 diag.attachNote(op.getSrc().getLoc()) << "source here";
491 return diag;
492 }
493
494 auto *destBlock = newDest.getParentBlock();
495 auto builder = ImplicitLocOpBuilder::atBlockEnd(op.getLoc(), destBlock);
496 emitConnect(builder, newDest, newSrc);
497 toDelete.push_back(op);
498 return success();
499}
500
501LogicalResult ProbeVisitor::visitExpr(RWProbeOp op) {
502 // Handle similar to ref.send but lookup the target
503 // and materialize a value for it (indexing).
504 auto conv = mapType(op.getType(), op.getLoc());
505 if (failed(conv))
506 return failure();
507 auto newType = *conv;
508 toDelete.push_back(op);
509
510 auto ist = irn.lookup(op.getTarget());
511 assert(ist);
512 auto ref = getFieldRefForTarget(ist);
513
514 ImplicitLocOpBuilder builder(op.getLoc(), op);
515 builder.setInsertionPointAfterValue(ref.getValue());
516 auto data = getValueByFieldID(builder, ref.getValue(), ref.getFieldID());
517 assert(cast<FIRRTLBaseType>(data.getType()).getPassiveType() ==
518 op.getType().getType());
519 if (newType != data.getType()) {
520 auto wire = builder.create<WireOp>(newType);
521 emitConnect(builder, wire.getData(), data);
522 data = wire.getData();
523 }
524 probeToHWMap[op.getResult()] = data;
525 return success();
526}
527
528LogicalResult ProbeVisitor::visitExpr(RefCastOp op) {
529 auto input = probeToHWMap.at(op.getInput());
530 // Insert wire of the new type, and connect to it.
531
532 // y = ref.cast x : probe<t1> -> probe<t2>
533 // ->
534 // w = firrtl.wire : t2
535 // emitConnect(w : t2, map(x): t1)
536
537 auto conv = mapType(op.getResult().getType(), op.getLoc());
538 if (failed(conv))
539 return failure();
540 auto newType = *conv;
541
542 ImplicitLocOpBuilder builder(op.getLoc(), op);
543 builder.setInsertionPointAfterValue(input);
544 auto wire = builder.create<WireOp>(newType);
545 emitConnect(builder, wire.getData(), input);
546 probeToHWMap[op.getResult()] = wire.getData();
547 toDelete.push_back(op);
548 return success();
549}
550
551LogicalResult ProbeVisitor::visitExpr(RefSendOp op) {
552 auto conv = mapType(op.getResult().getType(), op.getLoc());
553 if (failed(conv))
554 return failure();
555 auto newType = *conv;
556 toDelete.push_back(op);
557
558 // If the mapped type is same as input, just use that.
559 if (newType == op.getBase().getType()) {
560 probeToHWMap[op.getResult()] = op.getBase();
561 return success();
562 }
563
564 // Otherwise, need to make this the probed type (passive).
565 // Insert wire of the new type, and connect to it.
566 assert(newType == op.getBase().getType().getPassiveType());
567 ImplicitLocOpBuilder builder(op.getLoc(), op);
568 builder.setInsertionPointAfterValue(op.getBase());
569 auto wire = builder.create<WireOp>(newType);
570 emitConnect(builder, wire.getData(), op.getBase());
571 probeToHWMap[op.getResult()] = wire.getData();
572 return success();
573}
574
575LogicalResult ProbeVisitor::visitExpr(RefResolveOp op) {
576 // ref.resolve x -> map(x)
577 auto val = probeToHWMap.at(op.getRef());
578 op.replaceAllUsesWith(val);
579 toDelete.push_back(op);
580 return success();
581}
582
583LogicalResult ProbeVisitor::visitExpr(RefSubOp op) {
584 // ref.sub x, fieldid -> index(map(x), fieldid)
585 auto val = probeToHWMap.at(op.getInput());
586 assert(val);
587 ImplicitLocOpBuilder builder(op.getLoc(), op);
588 builder.setInsertionPointAfterValue(op.getInput());
589 auto newVal =
590 getValueByFieldID(builder, val, op.getAccessedField().getFieldID());
591 probeToHWMap[op.getResult()] = newVal;
592 toDelete.push_back(op);
593 return success();
594}
595
596//===----------------------------------------------------------------------===//
597// Pass Infrastructure
598//===----------------------------------------------------------------------===//
599
600namespace {
601struct ProbesToSignalsPass
602 : public circt::firrtl::impl::ProbesToSignalsBase<ProbesToSignalsPass> {
603 ProbesToSignalsPass() = default;
604 void runOnOperation() override;
605};
606} // end anonymous namespace
607
608void ProbesToSignalsPass::runOnOperation() {
609 LLVM_DEBUG(debugPassHeader(this) << "\n");
610
611 SmallVector<Operation *, 0> ops(getOperation().getOps<FModuleLike>());
612
613 hw::InnerRefNamespace irn{getAnalysis<SymbolTable>(),
614 getAnalysis<hw::InnerSymbolTableCollection>()};
615
616 auto result = failableParallelForEach(&getContext(), ops, [&](Operation *op) {
617 ProbeVisitor visitor(irn);
618 return visitor.visit(cast<FModuleLike>(op));
619 });
620
621 if (result.failed())
622 signalPassFailure();
623}
624
625/// This is the pass constructor.
626std::unique_ptr<mlir::Pass> circt::firrtl::createProbesToSignalsPass() {
627 return std::make_unique<ProbesToSignalsPass>();
628}
assert(baseType &&"element must be base type")
static FIRRTLBaseType convertType(FIRRTLBaseType type)
Returns null type if no conversion is needed.
Definition DropConst.cpp:32
static Block * getBodyBlock(FModuleLike mod)
FIRRTLVisitor allows you to visit all of the expr/stmt/decls with one class declaration.
ResultType visitInvalidOp(Operation *op, ExtraArgs... args)
visitInvalidOp is an override point for non-FIRRTL dialect operations.
ResultType visitUnhandledOp(Operation *op, ExtraArgs... args)
visitUnhandledOp is an override point for FIRRTL dialect ops that the concrete visitor didn't bother ...
ResultType visitUnhandledDecl(Operation *op, ExtraArgs... args)
Forceable replaceWithNewForceability(Forceable op, bool forceable, ::mlir::PatternRewriter *rewriter=nullptr)
Replace a Forceable op with equivalent, changing whether forceable.
FieldRef getFieldRefForTarget(const hw::InnerSymTarget &ist)
Get FieldRef pointing to the specified inner symbol target, which must be valid.
Value getValueByFieldID(ImplicitLocOpBuilder builder, Value value, unsigned fieldID)
This gets the value targeted by a field id.
void emitConnect(OpBuilder &builder, Location loc, Value lhs, Value rhs)
Emit a connect between two values.
std::unique_ptr< mlir::Pass > createProbesToSignalsPass()
This is the pass constructor.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
llvm::raw_ostream & debugPassHeader(const mlir::Pass *pass, int width=80)
Write a boilerplate header for a pass to the debug stream.
Definition Debug.cpp:31
This class represents the namespace in which InnerRef's can be resolved.
InnerSymTarget lookup(hw::InnerRefAttr inner) const
Resolve the InnerRef to its target within this namespace, returning empty target if no such name exis...