22 #include "mlir/IR/BuiltinAttributes.h"
23 #include "mlir/IR/ImplicitLocOpBuilder.h"
24 #include "mlir/IR/Threading.h"
25 #include "mlir/IR/Visitors.h"
26 #include "llvm/ADT/ArrayRef.h"
27 #include "llvm/ADT/STLExtras.h"
28 #include "llvm/ADT/SmallVector.h"
29 #include "llvm/Support/ErrorHandling.h"
31 using namespace circt;
32 using namespace firrtl;
39 class Visitor :
public FIRRTLVisitor<Visitor, LogicalResult> {
41 explicit Visitor(MLIRContext *);
43 LogicalResult visit(FModuleOp);
49 LogicalResult visitUnhandledOp(Operation *);
51 LogicalResult visitInvalidOp(Operation *op) {
52 return op->emitError(
"invalid operation operation");
55 template <
typename Op>
56 void emitExplodedConnect(ImplicitLocOpBuilder &, Type, ArrayRef<Value>,
58 Value emitBundleCreate(ImplicitLocOpBuilder &, Type, ArrayRef<Value>);
59 template <
typename Op>
60 void handleConnect(Op);
62 LogicalResult visitStmt(ConnectOp);
63 LogicalResult visitStmt(StrictConnectOp);
65 LogicalResult visitExpr(AggregateConstantOp);
66 LogicalResult visitExpr(VectorCreateOp);
67 LogicalResult visitExpr(SubfieldOp);
68 LogicalResult visitExpr(SubindexOp);
69 LogicalResult visitExpr(SubaccessOp);
70 LogicalResult visitExpr(RefSubOp);
71 LogicalResult visitExpr(RefResolveOp);
79 Attribute convertConstant(Type, Attribute);
80 Attribute convertVectorConstant(FVectorType, ArrayAttr);
81 Attribute convertBundleConstant(BundleType, ArrayAttr);
82 Attribute convertBundleInVectorConstant(BundleType, ArrayRef<Attribute>);
85 void explodeFieldID(Type, uint64_t,
86 SmallVectorImpl<std::pair<Type, uint64_t>> &);
87 void fixAnnotation(Type, Type, DictionaryAttr, SmallVectorImpl<Attribute> &);
88 ArrayAttr fixAnnotations(Type, Type, ArrayAttr);
91 void explode(Value, SmallVectorImpl<Value> &);
92 SmallVector<Value> explode(Value);
94 void explodeRef(Value, SmallVectorImpl<Value> &);
96 std::pair<SmallVector<Value>,
bool> fixOperand(Value);
100 Value fixROperand(Value);
102 std::pair<SmallVector<Value>,
bool> fixRefOperand(Value);
105 Value sinkVecDimIntoOperands(ImplicitLocOpBuilder &,
FIRRTLBaseType,
106 const SmallVectorImpl<Value> &);
109 Value getSubfield(Value,
unsigned);
110 Value getSubindex(Value,
unsigned);
111 Value getSubaccess(Operation *, Value, Value);
112 Value getRefSub(Value,
unsigned);
114 MLIRContext *context;
115 SmallVector<Operation *> toDelete;
120 DenseMap<Value, Value> valueMap;
123 DenseMap<FIRRTLType, FIRRTLType> typeMap;
126 DenseMap<std::tuple<Value, unsigned>, Value> subfieldCache;
127 DenseMap<std::tuple<Value, unsigned>, Value> subindexCache;
128 DenseMap<std::tuple<Operation *, Value, Value>, Value> subaccessCache;
129 DenseMap<std::tuple<Value, unsigned>, Value> refSubCache;
133 Visitor::Visitor(MLIRContext *context) : context(context) {}
141 SmallVector<unsigned> &dimensions) {
142 if (
auto vectorType = type_dyn_cast<FVectorType>(type); vectorType) {
143 dimensions.push_back(vectorType.getNumElements());
144 auto converted =
convertType(vectorType.getElementType(), dimensions);
145 dimensions.pop_back();
148 if (
auto bundleType = type_dyn_cast<BundleType>(type); bundleType) {
149 SmallVector<BundleType::BundleElement> elements;
150 for (
auto element : bundleType.getElements()) {
151 elements.push_back(BundleType::BundleElement(
152 element.name, element.isFlip,
convertType(element.type, dimensions)));
156 for (
auto size : llvm::reverse(dimensions))
162 auto cached = typeMap.lookup(type);
164 return type_cast<FIRRTLBaseType>(cached);
166 SmallVector<unsigned> dimensions;
169 typeMap.insert({type, converted});
174 auto cached = typeMap.lookup(type);
176 return type_cast<RefType>(cached);
178 type.getForceable(), type.getLayer());
179 typeMap.insert({type, converted});
184 auto cached = typeMap.lookup(type);
186 return type_cast<FIRRTLType>(cached);
187 if (
auto baseType = type_dyn_cast<FIRRTLBaseType>(type))
189 if (
auto refType = type_dyn_cast<RefType>(type))
195 if (
auto firrtlType = type_dyn_cast<FIRRTLType>(type))
205 void Visitor::explodeFieldID(
206 Type type, uint64_t fieldID,
207 SmallVectorImpl<std::pair<Type, uint64_t>> &fields) {
208 if (
auto bundleType = type_dyn_cast<BundleType>(type)) {
209 for (
size_t i = 0, e = bundleType.getNumElements(); i < e; ++i) {
210 auto eltType = bundleType.getElementType(i);
211 auto eltID = fieldID + bundleType.getFieldID(i);
212 explodeFieldID(eltType, eltID, fields);
216 fields.emplace_back(type, fieldID);
219 void Visitor::fixAnnotation(Type oldType, Type newType, DictionaryAttr annoAttr,
220 SmallVectorImpl<Attribute> &newAnnos) {
222 auto fieldID = anno.getFieldID();
227 newAnnos.push_back(anno.getAttr());
231 SmallVector<uint32_t> bundleAccesses;
232 SmallVector<uint32_t> vectorAccesses;
233 while (fieldID != 0) {
234 if (
auto bundleType = type_dyn_cast<BundleType>(oldType)) {
235 auto [index, subID] = bundleType.getIndexAndSubfieldID(fieldID);
236 bundleAccesses.push_back(index);
237 oldType = bundleType.getElementType(index);
241 if (
auto vectorType = type_dyn_cast<FVectorType>(oldType)) {
242 auto [index, subID] = vectorType.getIndexAndSubfieldID(fieldID);
243 vectorAccesses.push_back(index);
244 oldType = vectorType.getElementType();
248 llvm_unreachable(
"non-zero field ID can only be used on aggregate types");
252 for (
auto index : bundleAccesses) {
253 auto bundleType = type_cast<BundleType>(newType);
254 newID += bundleType.getFieldID(index);
255 newType = bundleType.getElementType(index);
258 SmallVector<std::pair<Type, uint64_t>> fields;
259 if (type_isa<BundleType>(newType) && !vectorAccesses.empty()) {
260 explodeFieldID(newType, newID, fields);
262 fields.emplace_back(newType, newID);
266 for (
auto [type, fieldID] : fields) {
267 for (
auto index : vectorAccesses) {
268 auto vectorType = type_cast<FVectorType>(type);
269 type = vectorType.getElementType();
270 fieldID += vectorType.getFieldID(index);
273 newAnnos.push_back(anno.getAttr());
277 ArrayAttr Visitor::fixAnnotations(Type oldType, Type newType, ArrayAttr annos) {
278 SmallVector<Attribute> newAnnos;
279 for (
auto anno : cast<ArrayAttr>(annos).getAsRange<DictionaryAttr>())
280 fixAnnotation(oldType, newType, anno, newAnnos);
288 Value Visitor::getSubfield(Value input,
unsigned index) {
289 Value &result = subfieldCache[{input, index}];
294 builder.setInsertionPointAfterValue(input);
295 result =
builder.create<SubfieldOp>(input.getLoc(), input, index);
299 Value Visitor::getSubindex(Value input,
unsigned index) {
300 auto &result = subindexCache[{input, index}];
305 builder.setInsertionPointAfterValue(input);
306 result =
builder.create<SubindexOp>(input.getLoc(), input, index);
310 Value Visitor::getSubaccess(Operation *place, Value input, Value index) {
311 auto &result = subaccessCache[{place, input, index}];
315 result =
builder.create<SubaccessOp>(input.getLoc(), input, index);
319 Value Visitor::getRefSub(Value input,
unsigned index) {
320 auto &result = refSubCache[{input, index}];
324 builder.setInsertionPointAfterValue(input);
325 result =
builder.create<RefSubOp>(input.getLoc(), input, index);
334 void Visitor::explodeRef(Value value, SmallVectorImpl<Value> &output) {
335 auto underlyingType = type_cast<RefType>(value.getType()).getType();
336 if (
auto bundleType = type_dyn_cast<BundleType>(underlyingType)) {
337 for (
size_t i = 0, e = bundleType.getNumElements(); i < e; ++i) {
339 builder.setInsertionPointAfterValue(value);
340 auto field =
builder.create<RefSubOp>(value.getLoc(), value, i);
341 explodeRef(field, output);
345 output.push_back(value);
348 std::pair<SmallVector<Value>,
bool> Visitor::fixRefOperand(Value value) {
349 SmallVector<unsigned> bundleAccesses;
350 SmallVector<unsigned> vectorAccesses;
354 Operation *op = value.getDefiningOp();
357 if (
auto refSubOp = dyn_cast<RefSubOp>(op)) {
358 value = refSubOp.getInput();
359 auto type = type_cast<RefType>(value.getType()).getType();
360 if (type_isa<BundleType>(type))
361 bundleAccesses.push_back(refSubOp.getIndex());
362 else if (type_isa<FVectorType>(type))
363 vectorAccesses.push_back(refSubOp.getIndex());
365 refSubOp->emitError(
"unknown aggregate type");
373 value = valueMap[value];
377 for (
auto index : llvm::reverse(bundleAccesses)) {
378 value = getRefSub(value, index);
385 SmallVector<Value> values;
386 bool exploded =
false;
387 if (type_isa<BundleType>(type_cast<RefType>(value.getType()).getType()) &&
388 !vectorAccesses.empty()) {
389 explodeRef(value, values);
392 values.push_back(value);
397 for (
auto &value : values) {
398 for (
auto index : llvm::reverse(vectorAccesses)) {
399 value = getRefSub(value, index);
403 return {values, exploded};
411 void Visitor::explode(Value value, SmallVectorImpl<Value> &output) {
412 auto type = value.getType();
413 if (
auto bundleType = type_dyn_cast<BundleType>(type)) {
414 for (
size_t i = 0, e = bundleType.getNumElements(); i < e; ++i) {
415 auto field = getSubfield(value, i);
416 explode(field, output);
420 output.push_back(value);
423 SmallVector<Value> Visitor::explode(Value value) {
424 auto output = SmallVector<Value>();
425 explode(value, output);
430 std::pair<SmallVector<Value>,
bool> Visitor::fixOperand(Value value) {
431 auto type = value.getType();
435 if (type_isa<RefType>(type))
436 return fixRefOperand(value);
438 SmallVector<SubfieldOp> bundleAccesses;
439 SmallVector<Operation *> vectorAccesses;
445 Operation *op = value.getDefiningOp();
448 if (
auto subfieldOp = dyn_cast<SubfieldOp>(op)) {
449 value = subfieldOp.getInput();
450 bundleAccesses.push_back(subfieldOp);
453 if (
auto subindexOp = dyn_cast<SubindexOp>(op)) {
454 value = subindexOp.getInput();
455 vectorAccesses.push_back(subindexOp);
458 if (
auto subaccessOp = dyn_cast<SubaccessOp>(op)) {
459 value = subaccessOp.getInput();
460 vectorAccesses.push_back(subaccessOp);
468 value = valueMap[value];
469 assert(value &&
"canonical storage location must have been converted");
473 for (
auto subfieldOp : llvm::reverse(bundleAccesses))
474 value = getSubfield(value, subfieldOp.getFieldIndex());
479 SmallVector<Value> values;
480 bool exploded =
false;
482 if (type_isa<BundleType>(value.getType()) && !vectorAccesses.empty()) {
483 explode(value, values);
486 values.push_back(value);
491 for (
auto &value : values) {
492 for (
auto *op : llvm::reverse(vectorAccesses)) {
493 if (
auto subindexOp = dyn_cast<SubindexOp>(op)) {
494 value = getSubindex(value, subindexOp.getIndex());
497 if (
auto subaccessOp = dyn_cast<SubaccessOp>(op)) {
498 auto index = fixROperand(subaccessOp.getIndex());
499 value = getSubaccess(subaccessOp, value, index);
505 return {values, exploded};
513 Value Visitor::fixROperand(Value operand) {
514 auto [values, exploded] = fixOperand(operand);
516 return values.front();
520 ImplicitLocOpBuilder
builder(operand.getLoc(), context);
521 builder.setInsertionPointAfterValue(operand);
522 return emitBundleCreate(
builder, newType, values);
529 LogicalResult Visitor::visitUnhandledOp(Operation *op) {
530 ImplicitLocOpBuilder
builder(op->getLoc(), op);
531 bool changed =
false;
536 SmallVector<Value> newOperands;
537 for (
auto oldOperand : op->getOperands()) {
538 auto newOperand = fixROperand(oldOperand);
539 changed |= (oldOperand != newOperand);
540 newOperands.push_back(newOperand);
545 SmallVector<Type> newTypes;
546 for (
auto oldResult : op->getResults()) {
547 auto oldType = oldResult.getType();
549 changed |= oldType != newType;
550 newTypes.push_back(newType);
554 auto *newOp =
builder.clone(*op);
555 newOp->setOperands(newOperands);
556 for (
size_t i = 0, e = op->getNumResults(); i < e; ++i) {
557 auto newResult = newOp->getResult(i);
558 newResult.setType(newTypes[i]);
559 valueMap[op->getResult(i)] = newResult;
563 if (
auto portAnnos = op->getAttrOfType<ArrayAttr>(
"portAnnotations")) {
566 SmallVector<Attribute> newPortAnnos;
567 for (
unsigned i = 0, e = portAnnos.size(); i < e; ++i) {
568 auto oldType = op->getResult(i).getType();
569 auto newType = newTypes[i];
570 newPortAnnos.push_back(
571 fixAnnotations(oldType, newType, cast<ArrayAttr>(portAnnos[i])));
573 newOp->setAttr(
"portAnnotations",
ArrayAttr::get(context, newPortAnnos));
574 }
else if (newOp->getNumResults() == 1) {
579 if (
auto annos = newOp->getAttrOfType<ArrayAttr>(
"annotations")) {
580 auto oldType = op->getResult(0).getType();
581 auto newType = newTypes[0];
582 auto newAnnos = fixAnnotations(oldType, newType, annos);
587 toDelete.push_back(op);
593 for (
auto result : op->getResults())
594 valueMap[result] = result;
597 for (
auto ®ion : op->getRegions())
598 for (
auto &block : region.getBlocks())
599 for (
auto &op : block)
600 if (failed(dispatchVisitor(&op)))
610 template <
typename Op>
611 void Visitor::emitExplodedConnect(ImplicitLocOpBuilder &
builder, Type type,
612 ArrayRef<Value> lhs, ArrayRef<Value> rhs) {
613 assert(lhs.size() == rhs.size() &&
614 "Something went wrong exploding the elements");
615 const auto *lhsIt = lhs.begin();
616 const auto *rhsIt = rhs.begin();
618 auto explodeConnect = [&](
auto self, Type type,
bool flip =
false) ->
void {
619 if (
auto bundleType = type_dyn_cast<BundleType>(type)) {
620 for (
auto &element : bundleType) {
621 self(
self, element.type,
flip ^ element.isFlip);
631 explodeConnect(explodeConnect, type);
634 Value Visitor::emitBundleCreate(ImplicitLocOpBuilder &
builder, Type type,
635 ArrayRef<Value> values) {
636 auto *it = values.begin();
637 auto convert = [&](
auto self, Type type) -> Value {
638 if (
auto bundleType = type_dyn_cast<BundleType>(type)) {
639 SmallVector<Value> fields;
640 for (
auto element : bundleType.getElements()) {
641 fields.push_back(
self(
self, element.type));
643 return builder.create<BundleCreateOp>(type, fields);
647 return convert(convert, type);
650 template <
typename Op>
651 void Visitor::handleConnect(Op op) {
652 ImplicitLocOpBuilder
builder(op.getLoc(), op);
653 auto oldLhs = op.getDest();
654 auto oldRhs = op.getSrc();
656 auto oldType = type_cast<FIRRTLType>(oldLhs.getType());
659 auto [lhs, lhsExploded] = fixOperand(oldLhs);
660 auto [rhs, rhsExploded] = fixOperand(oldRhs);
662 if (!lhsExploded && !rhsExploded && oldLhs == lhs[0] && oldRhs == rhs[0])
667 emitExplodedConnect<Op>(
builder, type, lhs, rhs);
669 emitExplodedConnect<Op>(
builder, type, lhs, explode(rhs[0]));
673 if (
auto baseType = type_dyn_cast<FIRRTLBaseType>(type);
674 baseType && baseType.isPassive()) {
677 emitExplodedConnect<Op>(
builder, type, explode(lhs[0]), rhs);
680 builder.create<Op>(lhs[0], rhs[0]);
684 toDelete.push_back(op);
687 LogicalResult Visitor::visitStmt(ConnectOp op) {
692 LogicalResult Visitor::visitStmt(StrictConnectOp op) {
701 Attribute Visitor::convertBundleInVectorConstant(BundleType type,
702 ArrayRef<Attribute> fields) {
703 auto numBundleFields = type.getNumElements();
704 SmallVector<SmallVector<Attribute>> newBundleFields;
705 newBundleFields.resize(numBundleFields);
706 for (
auto bundle : fields) {
707 auto subfields = cast<ArrayAttr>(bundle);
708 for (
size_t i = 0; i < numBundleFields; ++i) {
709 newBundleFields[i].push_back(subfields[i]);
713 SmallVector<Attribute> newFieldAttrs;
714 for (
auto &newBundleField : newBundleFields) {
721 Attribute Visitor::convertVectorConstant(FVectorType oldType,
722 ArrayAttr oldElements) {
723 auto oldElementType = oldType.getElementType();
726 if (oldElementType == newElementType)
727 if (
auto bundleElementType = type_dyn_cast<BundleType>(oldElementType))
728 return convertBundleInVectorConstant(bundleElementType,
729 oldElements.getValue());
731 SmallVector<Attribute> newElements;
732 for (
auto oldElement : oldElements) {
733 newElements.push_back(convertConstant(oldElementType, oldElement));
736 auto bundleType = type_cast<BundleType>(newElementType);
737 return convertBundleInVectorConstant(bundleType, newElements);
741 Attribute Visitor::convertBundleConstant(BundleType type, ArrayAttr fields) {
742 SmallVector<Attribute> converted;
743 auto elements = type.getElements();
744 for (
size_t i = 0, e = elements.size(); i < e; ++i) {
745 converted.push_back(convertConstant(elements[i].type, fields[i]));
751 Attribute Visitor::convertConstant(Type type, Attribute value) {
752 if (
auto bundleType = type_dyn_cast<BundleType>(type))
753 return convertBundleConstant(bundleType, cast<ArrayAttr>(value));
755 if (
auto vectorType = type_dyn_cast<FVectorType>(type))
756 return convertVectorConstant(vectorType, cast<ArrayAttr>(value));
761 LogicalResult Visitor::visitExpr(AggregateConstantOp op) {
762 auto oldValue = op.getResult();
764 auto oldType = oldValue.getType();
766 if (oldType == newType) {
767 valueMap[oldValue] = oldValue;
771 auto fields = cast<ArrayAttr>(convertConstant(oldType, op.getFields()));
775 builder.create<AggregateConstantOp>(op.getLoc(), newType, fields);
777 valueMap[oldValue] = newOp.getResult();
778 toDelete.push_back(op);
788 Value Visitor::sinkVecDimIntoOperands(ImplicitLocOpBuilder &
builder,
790 const SmallVectorImpl<Value> &values) {
791 auto length = values.size();
792 if (
auto bundleType = type_dyn_cast<BundleType>(type)) {
793 SmallVector<Value> newFields;
794 SmallVector<BundleType::BundleElement> newElements;
795 for (
auto [i, elt] : llvm::enumerate(bundleType)) {
796 SmallVector<Value> subValues;
797 for (
auto v : values)
798 subValues.push_back(getSubfield(v, i));
799 auto newField = sinkVecDimIntoOperands(
builder, elt.type, subValues);
800 newFields.push_back(newField);
801 newElements.emplace_back(elt.name,
false,
802 type_cast<FIRRTLBaseType>(newField.getType()));
805 auto newBundle =
builder.create<BundleCreateOp>(newType, newFields);
809 return builder.create<VectorCreateOp>(newType, values);
812 LogicalResult Visitor::visitExpr(VectorCreateOp op) {
813 ImplicitLocOpBuilder
builder(op.getLoc(), op);
815 auto oldType = op.getType();
818 if (oldType == newType) {
819 auto changed =
false;
820 SmallVector<Value> newFields;
821 for (
auto oldField : op.getFields()) {
822 auto newField = fixROperand(oldField);
823 if (oldField != newField)
825 newFields.push_back(newField);
829 auto result = op.getResult();
830 valueMap[result] = result;
835 builder.create<VectorCreateOp>(op.getLoc(), newType, newFields);
836 valueMap[op.getResult()] = newOp.getResult();
837 toDelete.push_back(op);
842 SmallVector<Value> convertedOldFields;
843 for (
auto oldField : op.getFields()) {
844 auto convertedField = fixROperand(oldField);
845 convertedOldFields.push_back(convertedField);
848 auto value = sinkVecDimIntoOperands(
851 valueMap[op.getResult()] = value;
852 toDelete.push_back(op);
860 LogicalResult Visitor::visitExpr(SubfieldOp op) {
861 toDelete.push_back(op);
865 LogicalResult Visitor::visitExpr(SubindexOp op) {
866 toDelete.push_back(op);
870 LogicalResult Visitor::visitExpr(SubaccessOp op) {
871 toDelete.push_back(op);
875 LogicalResult Visitor::visitExpr(RefSubOp op) {
876 toDelete.push_back(op);
884 LogicalResult Visitor::visitExpr(RefResolveOp op) {
885 ImplicitLocOpBuilder
builder(op.getLoc(), op);
886 auto [refs, exploded] = fixRefOperand(op.getRef());
889 if (ref == op.getRef()) {
890 valueMap[op.getResult()] = op.getResult();
895 valueMap[op.getResult()] = value;
896 toDelete.push_back(op);
901 SmallVector<Value> values;
902 for (
auto ref : refs) {
904 .create<RefResolveOp>(
905 type_cast<RefType>(ref.getType()).getType(), ref)
908 auto value = emitBundleCreate(
builder, type, values);
909 valueMap[op.getResult()] = value;
910 toDelete.push_back(op);
918 LogicalResult Visitor::visit(FModuleOp op) {
919 BitVector portsToErase(op.getNumPorts() * 2);
921 SmallVector<std::pair<unsigned, PortInfo>> newPorts;
922 auto ports = op.getPorts();
924 for (
auto [index, port] : llvm::enumerate(ports)) {
925 auto oldType = port.type;
927 if (newType == oldType)
930 newPort.type = newType;
932 fixAnnotations(oldType, newType, port.annotations.getArrayAttr()));
933 portsToErase[count + index] =
true;
934 newPorts.push_back({index + 1, newPort});
938 op.insertPorts(newPorts);
941 auto *body = op.getBodyBlock();
942 for (
unsigned i = 0, e = body->getNumArguments(); i < e; ++i) {
943 if (portsToErase[i]) {
944 auto oldArg = body->getArgument(i);
945 auto newArg = body->getArgument(i + 1);
946 valueMap[oldArg] = newArg;
948 auto oldArg = body->getArgument(i);
949 valueMap[oldArg] = oldArg;
953 for (
auto &op : *body) {
954 if (failed(dispatchVisitor(&op)))
958 while (!toDelete.empty())
959 toDelete.pop_back_val()->erase();
960 op.erasePorts(portsToErase);
970 class VBToBVPass :
public VBToBVBase<VBToBVPass> {
971 void runOnOperation()
override;
975 void VBToBVPass::runOnOperation() {
976 std::vector<FModuleOp> modules;
977 llvm::append_range(modules, getOperation().getBody().getOps<FModuleOp>());
979 failableParallelForEach(&getContext(), modules, [&](FModuleOp module) {
980 Visitor visitor(&getContext());
981 return visitor.visit(module);
989 return std::make_unique<VBToBVPass>();
assert(baseType &&"element must be base type")
static FIRRTLBaseType convertType(FIRRTLBaseType type)
Returns null type if no conversion is needed.
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool applyToOperation(Operation *op) const
Store the annotations in this set in an operation's annotations attribute, overwriting any existing a...
This class provides a read-only projection of an annotation.
FIRRTLVisitor allows you to visit all of the expr/stmt/decls with one class declaration.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Direction flip(Direction direction)
Flip a port direction.
std::unique_ptr< mlir::Pass > createVBToBVPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.