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 typeMap.insert({type, converted});
183 auto cached = typeMap.lookup(type);
185 return type_cast<FIRRTLType>(cached);
186 if (
auto baseType = type_dyn_cast<FIRRTLBaseType>(type))
188 if (
auto refType = type_dyn_cast<RefType>(type))
194 if (
auto firrtlType = type_dyn_cast<FIRRTLType>(type))
204 void Visitor::explodeFieldID(
205 Type type, uint64_t fieldID,
206 SmallVectorImpl<std::pair<Type, uint64_t>> &fields) {
207 if (
auto bundleType = type_dyn_cast<BundleType>(type)) {
208 for (
size_t i = 0, e = bundleType.getNumElements(); i < e; ++i) {
209 auto eltType = bundleType.getElementType(i);
210 auto eltID = fieldID + bundleType.getFieldID(i);
211 explodeFieldID(eltType, eltID, fields);
215 fields.emplace_back(type, fieldID);
218 void Visitor::fixAnnotation(Type oldType, Type newType, DictionaryAttr annoAttr,
219 SmallVectorImpl<Attribute> &newAnnos) {
221 auto fieldID = anno.getFieldID();
226 newAnnos.push_back(anno.getAttr());
230 SmallVector<uint32_t> bundleAccesses;
231 SmallVector<uint32_t> vectorAccesses;
232 while (fieldID != 0) {
233 if (
auto bundleType = type_dyn_cast<BundleType>(oldType)) {
234 auto [index, subID] = bundleType.getIndexAndSubfieldID(fieldID);
235 bundleAccesses.push_back(index);
236 oldType = bundleType.getElementType(index);
240 if (
auto vectorType = type_dyn_cast<FVectorType>(oldType)) {
241 auto [index, subID] = vectorType.getIndexAndSubfieldID(fieldID);
242 vectorAccesses.push_back(index);
243 oldType = vectorType.getElementType();
247 llvm_unreachable(
"non-zero field ID can only be used on aggregate types");
251 for (
auto index : bundleAccesses) {
252 auto bundleType = type_cast<BundleType>(newType);
253 newID += bundleType.getFieldID(index);
254 newType = bundleType.getElementType(index);
257 SmallVector<std::pair<Type, uint64_t>> fields;
258 if (type_isa<BundleType>(newType) && !vectorAccesses.empty()) {
259 explodeFieldID(newType, newID, fields);
261 fields.emplace_back(newType, newID);
265 for (
auto [type, fieldID] : fields) {
266 for (
auto index : vectorAccesses) {
267 auto vectorType = type_cast<FVectorType>(type);
268 type = vectorType.getElementType();
269 fieldID += vectorType.getFieldID(index);
272 newAnnos.push_back(anno.getAttr());
276 ArrayAttr Visitor::fixAnnotations(Type oldType, Type newType, ArrayAttr annos) {
277 SmallVector<Attribute> newAnnos;
278 for (
auto anno : cast<ArrayAttr>(annos).getAsRange<DictionaryAttr>())
279 fixAnnotation(oldType, newType, anno, newAnnos);
287 Value Visitor::getSubfield(Value input,
unsigned index) {
288 Value &result = subfieldCache[{input, index}];
293 builder.setInsertionPointAfterValue(input);
294 result =
builder.create<SubfieldOp>(input.getLoc(), input, index);
298 Value Visitor::getSubindex(Value input,
unsigned index) {
299 auto &result = subindexCache[{input, index}];
304 builder.setInsertionPointAfterValue(input);
305 result =
builder.create<SubindexOp>(input.getLoc(), input, index);
309 Value Visitor::getSubaccess(Operation *place, Value input, Value index) {
310 auto &result = subaccessCache[{place, input, index}];
314 result =
builder.create<SubaccessOp>(input.getLoc(), input, index);
318 Value Visitor::getRefSub(Value input,
unsigned index) {
319 auto &result = refSubCache[{input, index}];
323 builder.setInsertionPointAfterValue(input);
324 result =
builder.create<RefSubOp>(input.getLoc(), input, index);
333 void Visitor::explodeRef(Value
value, SmallVectorImpl<Value> &output) {
334 auto underlyingType = type_cast<RefType>(
value.getType()).getType();
335 if (
auto bundleType = type_dyn_cast<BundleType>(underlyingType)) {
336 for (
size_t i = 0, e = bundleType.getNumElements(); i < e; ++i) {
340 explodeRef(field, output);
344 output.push_back(
value);
347 std::pair<SmallVector<Value>,
bool> Visitor::fixRefOperand(Value
value) {
348 SmallVector<unsigned> bundleAccesses;
349 SmallVector<unsigned> vectorAccesses;
353 Operation *op =
value.getDefiningOp();
356 if (
auto refSubOp = dyn_cast<RefSubOp>(op)) {
357 value = refSubOp.getInput();
358 auto type = type_cast<RefType>(
value.getType()).getType();
359 if (type_isa<BundleType>(type))
360 bundleAccesses.push_back(refSubOp.getIndex());
361 else if (type_isa<FVectorType>(type))
362 vectorAccesses.push_back(refSubOp.getIndex());
364 refSubOp->emitError(
"unknown aggregate type");
376 for (
auto index : llvm::reverse(bundleAccesses)) {
384 SmallVector<Value> values;
385 bool exploded =
false;
386 if (type_isa<BundleType>(type_cast<RefType>(
value.getType()).getType()) &&
387 !vectorAccesses.empty()) {
388 explodeRef(
value, values);
391 values.push_back(
value);
396 for (
auto &
value : values) {
397 for (
auto index : llvm::reverse(vectorAccesses)) {
402 return {values, exploded};
410 void Visitor::explode(Value
value, SmallVectorImpl<Value> &output) {
411 auto type =
value.getType();
412 if (
auto bundleType = type_dyn_cast<BundleType>(type)) {
413 for (
size_t i = 0, e = bundleType.getNumElements(); i < e; ++i) {
414 auto field = getSubfield(
value, i);
415 explode(field, output);
419 output.push_back(
value);
422 SmallVector<Value> Visitor::explode(Value
value) {
423 auto output = SmallVector<Value>();
424 explode(
value, output);
429 std::pair<SmallVector<Value>,
bool> Visitor::fixOperand(Value
value) {
430 auto type =
value.getType();
434 if (type_isa<RefType>(type))
435 return fixRefOperand(
value);
437 SmallVector<SubfieldOp> bundleAccesses;
438 SmallVector<Operation *> vectorAccesses;
444 Operation *op =
value.getDefiningOp();
447 if (
auto subfieldOp = dyn_cast<SubfieldOp>(op)) {
448 value = subfieldOp.getInput();
449 bundleAccesses.push_back(subfieldOp);
452 if (
auto subindexOp = dyn_cast<SubindexOp>(op)) {
453 value = subindexOp.getInput();
454 vectorAccesses.push_back(subindexOp);
457 if (
auto subaccessOp = dyn_cast<SubaccessOp>(op)) {
458 value = subaccessOp.getInput();
459 vectorAccesses.push_back(subaccessOp);
468 assert(
value &&
"canonical storage location must have been converted");
472 for (
auto subfieldOp : llvm::reverse(bundleAccesses))
473 value = getSubfield(
value, subfieldOp.getFieldIndex());
478 SmallVector<Value> values;
479 bool exploded =
false;
481 if (type_isa<BundleType>(
value.getType()) && !vectorAccesses.empty()) {
482 explode(
value, values);
485 values.push_back(
value);
490 for (
auto &
value : values) {
491 for (
auto *op : llvm::reverse(vectorAccesses)) {
492 if (
auto subindexOp = dyn_cast<SubindexOp>(op)) {
493 value = getSubindex(
value, subindexOp.getIndex());
496 if (
auto subaccessOp = dyn_cast<SubaccessOp>(op)) {
497 auto index = fixROperand(subaccessOp.getIndex());
498 value = getSubaccess(subaccessOp,
value, index);
504 return {values, exploded};
512 Value Visitor::fixROperand(Value operand) {
513 auto [values, exploded] = fixOperand(operand);
515 return values.front();
519 ImplicitLocOpBuilder
builder(operand.getLoc(), context);
520 builder.setInsertionPointAfterValue(operand);
521 return emitBundleCreate(
builder, newType, values);
528 LogicalResult Visitor::visitUnhandledOp(Operation *op) {
529 ImplicitLocOpBuilder
builder(op->getLoc(), op);
530 bool changed =
false;
535 SmallVector<Value> newOperands;
536 for (
auto oldOperand : op->getOperands()) {
537 auto newOperand = fixROperand(oldOperand);
538 changed |= (oldOperand != newOperand);
539 newOperands.push_back(newOperand);
544 SmallVector<Type> newTypes;
545 for (
auto oldResult : op->getResults()) {
546 auto oldType = oldResult.getType();
548 changed |= oldType != newType;
549 newTypes.push_back(newType);
553 auto *newOp =
builder.clone(*op);
554 newOp->setOperands(newOperands);
555 for (
size_t i = 0, e = op->getNumResults(); i < e; ++i) {
556 auto newResult = newOp->getResult(i);
557 newResult.setType(newTypes[i]);
558 valueMap[op->getResult(i)] = newResult;
562 if (
auto portAnnos = op->getAttrOfType<ArrayAttr>(
"portAnnotations")) {
565 SmallVector<Attribute> newPortAnnos;
566 for (
unsigned i = 0, e = portAnnos.size(); i < e; ++i) {
567 auto oldType = op->getResult(i).getType();
568 auto newType = newTypes[i];
569 newPortAnnos.push_back(
570 fixAnnotations(oldType, newType, cast<ArrayAttr>(portAnnos[i])));
572 newOp->setAttr(
"portAnnotations",
ArrayAttr::get(context, newPortAnnos));
573 }
else if (newOp->getNumResults() == 1) {
578 if (
auto annos = newOp->getAttrOfType<ArrayAttr>(
"annotations")) {
579 auto oldType = op->getResult(0).getType();
580 auto newType = newTypes[0];
581 auto newAnnos = fixAnnotations(oldType, newType, annos);
586 toDelete.push_back(op);
592 for (
auto result : op->getResults())
593 valueMap[result] = result;
596 for (
auto ®ion : op->getRegions())
597 for (
auto &block : region.getBlocks())
598 for (
auto &op : block)
599 if (failed(dispatchVisitor(&op)))
609 template <
typename Op>
610 void Visitor::emitExplodedConnect(ImplicitLocOpBuilder &
builder, Type type,
611 ArrayRef<Value> lhs, ArrayRef<Value> rhs) {
612 assert(lhs.size() == rhs.size() &&
613 "Something went wrong exploding the elements");
614 const auto *lhsIt = lhs.begin();
615 const auto *rhsIt = rhs.begin();
617 auto explodeConnect = [&](
auto self, Type type,
bool flip =
false) ->
void {
618 if (
auto bundleType = type_dyn_cast<BundleType>(type)) {
619 for (
auto &element : bundleType) {
620 self(
self, element.type,
flip ^ element.isFlip);
630 explodeConnect(explodeConnect, type);
633 Value Visitor::emitBundleCreate(ImplicitLocOpBuilder &
builder, Type type,
634 ArrayRef<Value> values) {
635 auto *it = values.begin();
636 auto convert = [&](
auto self, Type type) -> Value {
637 if (
auto bundleType = type_dyn_cast<BundleType>(type)) {
638 SmallVector<Value> fields;
639 for (
auto element : bundleType.getElements()) {
640 fields.push_back(
self(
self, element.type));
642 return builder.create<BundleCreateOp>(type, fields);
646 return convert(convert, type);
649 template <
typename Op>
650 void Visitor::handleConnect(Op op) {
651 ImplicitLocOpBuilder
builder(op.getLoc(), op);
652 auto oldLhs = op.getDest();
653 auto oldRhs = op.getSrc();
655 auto oldType = type_cast<FIRRTLType>(oldLhs.getType());
658 auto [lhs, lhsExploded] = fixOperand(oldLhs);
659 auto [rhs, rhsExploded] = fixOperand(oldRhs);
661 if (!lhsExploded && !rhsExploded && oldLhs == lhs[0] && oldRhs == rhs[0])
666 emitExplodedConnect<Op>(
builder, type, lhs, rhs);
668 emitExplodedConnect<Op>(
builder, type, lhs, explode(rhs[0]));
672 if (
auto baseType = type_dyn_cast<FIRRTLBaseType>(type);
673 baseType && baseType.isPassive()) {
676 emitExplodedConnect<Op>(
builder, type, explode(lhs[0]), rhs);
679 builder.create<Op>(lhs[0], rhs[0]);
683 toDelete.push_back(op);
686 LogicalResult Visitor::visitStmt(ConnectOp op) {
691 LogicalResult Visitor::visitStmt(StrictConnectOp op) {
700 Attribute Visitor::convertBundleInVectorConstant(BundleType type,
701 ArrayRef<Attribute> fields) {
702 auto numBundleFields = type.getNumElements();
703 SmallVector<SmallVector<Attribute>> newBundleFields;
704 newBundleFields.resize(numBundleFields);
705 for (
auto bundle : fields) {
706 auto subfields = cast<ArrayAttr>(bundle);
707 for (
size_t i = 0; i < numBundleFields; ++i) {
708 newBundleFields[i].push_back(subfields[i]);
712 SmallVector<Attribute> newFieldAttrs;
713 for (
auto &newBundleField : newBundleFields) {
720 Attribute Visitor::convertVectorConstant(FVectorType oldType,
721 ArrayAttr oldElements) {
722 auto oldElementType = oldType.getElementType();
725 if (oldElementType == newElementType)
726 if (
auto bundleElementType = type_dyn_cast<BundleType>(oldElementType))
727 return convertBundleInVectorConstant(bundleElementType,
728 oldElements.getValue());
730 SmallVector<Attribute> newElements;
731 for (
auto oldElement : oldElements) {
732 newElements.push_back(convertConstant(oldElementType, oldElement));
735 auto bundleType = type_cast<BundleType>(newElementType);
736 return convertBundleInVectorConstant(bundleType, newElements);
740 Attribute Visitor::convertBundleConstant(BundleType type, ArrayAttr fields) {
741 SmallVector<Attribute> converted;
742 auto elements = type.getElements();
743 for (
size_t i = 0, e = elements.size(); i < e; ++i) {
744 converted.push_back(convertConstant(elements[i].type, fields[i]));
750 Attribute Visitor::convertConstant(Type type, Attribute
value) {
751 if (
auto bundleType = type_dyn_cast<BundleType>(type))
752 return convertBundleConstant(bundleType, cast<ArrayAttr>(
value));
754 if (
auto vectorType = type_dyn_cast<FVectorType>(type))
755 return convertVectorConstant(vectorType, cast<ArrayAttr>(
value));
760 LogicalResult Visitor::visitExpr(AggregateConstantOp op) {
761 auto oldValue = op.getResult();
763 auto oldType = oldValue.getType();
765 if (oldType == newType) {
766 valueMap[oldValue] = oldValue;
770 auto fields = cast<ArrayAttr>(convertConstant(oldType, op.getFields()));
774 builder.create<AggregateConstantOp>(op.getLoc(), newType, fields);
776 valueMap[oldValue] = newOp.getResult();
777 toDelete.push_back(op);
787 Value Visitor::sinkVecDimIntoOperands(ImplicitLocOpBuilder &
builder,
789 const SmallVectorImpl<Value> &values) {
790 auto length = values.size();
791 if (
auto bundleType = type_dyn_cast<BundleType>(type)) {
792 SmallVector<Value> newFields;
793 SmallVector<BundleType::BundleElement> newElements;
794 for (
auto [i, elt] : llvm::enumerate(bundleType)) {
795 SmallVector<Value> subValues;
796 for (
auto v : values)
797 subValues.push_back(getSubfield(v, i));
798 auto newField = sinkVecDimIntoOperands(
builder, elt.type, subValues);
799 newFields.push_back(newField);
800 newElements.emplace_back(elt.name,
false,
801 type_cast<FIRRTLBaseType>(newField.getType()));
804 auto newBundle =
builder.create<BundleCreateOp>(newType, newFields);
808 return builder.create<VectorCreateOp>(newType, values);
811 LogicalResult Visitor::visitExpr(VectorCreateOp op) {
812 ImplicitLocOpBuilder
builder(op.getLoc(), op);
814 auto oldType = op.getType();
817 if (oldType == newType) {
818 auto changed =
false;
819 SmallVector<Value> newFields;
820 for (
auto oldField : op.getFields()) {
821 auto newField = fixROperand(oldField);
822 if (oldField != newField)
824 newFields.push_back(newField);
828 auto result = op.getResult();
829 valueMap[result] = result;
834 builder.create<VectorCreateOp>(op.getLoc(), newType, newFields);
835 valueMap[op.getResult()] = newOp.getResult();
836 toDelete.push_back(op);
841 SmallVector<Value> convertedOldFields;
842 for (
auto oldField : op.getFields()) {
843 auto convertedField = fixROperand(oldField);
844 convertedOldFields.push_back(convertedField);
847 auto value = sinkVecDimIntoOperands(
849 valueMap[op.getResult()] =
value;
850 toDelete.push_back(op);
858 LogicalResult Visitor::visitExpr(SubfieldOp op) {
859 toDelete.push_back(op);
863 LogicalResult Visitor::visitExpr(SubindexOp op) {
864 toDelete.push_back(op);
868 LogicalResult Visitor::visitExpr(SubaccessOp op) {
869 toDelete.push_back(op);
873 LogicalResult Visitor::visitExpr(RefSubOp op) {
874 toDelete.push_back(op);
882 LogicalResult Visitor::visitExpr(RefResolveOp op) {
883 ImplicitLocOpBuilder
builder(op.getLoc(), op);
884 auto [refs, exploded] = fixRefOperand(op.getRef());
887 if (ref == op.getRef()) {
888 valueMap[op.getResult()] = op.getResult();
893 valueMap[op.getResult()] =
value;
894 toDelete.push_back(op);
899 SmallVector<Value> values;
900 for (
auto ref : refs) {
902 .create<RefResolveOp>(
903 type_cast<RefType>(ref.getType()).getType(), ref)
907 valueMap[op.getResult()] =
value;
908 toDelete.push_back(op);
916 LogicalResult Visitor::visit(FModuleOp op) {
917 BitVector portsToErase(op.getNumPorts() * 2);
919 SmallVector<std::pair<unsigned, PortInfo>> newPorts;
920 auto ports = op.getPorts();
922 for (
auto [index, port] : llvm::enumerate(ports)) {
923 auto oldType = port.type;
925 if (newType == oldType)
928 newPort.type = newType;
930 fixAnnotations(oldType, newType, port.annotations.getArrayAttr()));
931 portsToErase[count + index] =
true;
932 newPorts.push_back({index + 1, newPort});
936 op.insertPorts(newPorts);
939 auto *body = op.getBodyBlock();
940 for (
unsigned i = 0, e = body->getNumArguments(); i < e; ++i) {
941 if (portsToErase[i]) {
942 auto oldArg = body->getArgument(i);
943 auto newArg = body->getArgument(i + 1);
944 valueMap[oldArg] = newArg;
946 auto oldArg = body->getArgument(i);
947 valueMap[oldArg] = oldArg;
951 for (
auto &op : *body) {
952 if (failed(dispatchVisitor(&op)))
956 while (!toDelete.empty())
957 toDelete.pop_back_val()->erase();
958 op.erasePorts(portsToErase);
968 class VBToBVPass :
public VBToBVBase<VBToBVPass> {
969 void runOnOperation()
override;
973 void VBToBVPass::runOnOperation() {
974 std::vector<FModuleOp> modules;
975 llvm::append_range(modules, getOperation().getBody().getOps<FModuleOp>());
977 failableParallelForEach(&getContext(), modules, [&](FModuleOp module) {
978 Visitor visitor(&getContext());
979 return visitor.visit(module);
987 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()
This file defines an intermediate representation for circuits acting as an abstraction for constraint...