CIRCT 20.0.0git
Loading...
Searching...
No Matches
VBToBV.cpp
Go to the documentation of this file.
1//===- VBToBV.cpp - "Vector of Bundle" to "Bundle of Vector" ----*- C++ -*-===//
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 VBToBV pass, which takes any bundle embedded in
10// a vector, and converts it to a vector embedded within a bundle.
11//
12//===----------------------------------------------------------------------===//
13
19#include "mlir/IR/BuiltinAttributes.h"
20#include "mlir/IR/ImplicitLocOpBuilder.h"
21#include "mlir/IR/Threading.h"
22#include "mlir/IR/Visitors.h"
23#include "mlir/Pass/Pass.h"
24#include "llvm/ADT/ArrayRef.h"
25#include "llvm/ADT/STLExtras.h"
26#include "llvm/ADT/SmallVector.h"
27#include "llvm/Support/ErrorHandling.h"
28
29namespace circt {
30namespace firrtl {
31#define GEN_PASS_DEF_VBTOBV
32#include "circt/Dialect/FIRRTL/Passes.h.inc"
33} // namespace firrtl
34} // namespace circt
35
36using namespace circt;
37using namespace firrtl;
38
39//===----------------------------------------------------------------------===//
40// Visitor
41//===----------------------------------------------------------------------===//
42
43namespace {
44class Visitor : public FIRRTLVisitor<Visitor, LogicalResult> {
45public:
46 explicit Visitor(MLIRContext *);
47
48 LogicalResult visit(FModuleOp);
49
50 using FIRRTLVisitor<Visitor, LogicalResult>::visitDecl;
51 using FIRRTLVisitor<Visitor, LogicalResult>::visitExpr;
52 using FIRRTLVisitor<Visitor, LogicalResult>::visitStmt;
53
54 LogicalResult visitUnhandledOp(Operation *);
55
56 LogicalResult visitInvalidOp(Operation *op) {
57 return op->emitError("invalid operation operation");
58 }
59
60 template <typename Op>
61 void emitExplodedConnect(ImplicitLocOpBuilder &, Type, ArrayRef<Value>,
62 ArrayRef<Value>);
63 Value emitBundleCreate(ImplicitLocOpBuilder &, Type, ArrayRef<Value>);
64 template <typename Op>
65 void handleConnect(Op);
66
67 LogicalResult visitStmt(ConnectOp);
68 LogicalResult visitStmt(MatchingConnectOp);
69
70 LogicalResult visitExpr(AggregateConstantOp);
71 LogicalResult visitExpr(VectorCreateOp);
72 LogicalResult visitExpr(SubfieldOp);
73 LogicalResult visitExpr(SubindexOp);
74 LogicalResult visitExpr(SubaccessOp);
75 LogicalResult visitExpr(RefSubOp);
76 LogicalResult visitExpr(RefResolveOp);
77
78 Type convertType(Type);
80 RefType convertType(RefType);
82 FIRRTLBaseType convertType(FIRRTLBaseType, SmallVector<unsigned> &);
83
84 Attribute convertConstant(Type, Attribute);
85 Attribute convertVectorConstant(FVectorType, ArrayAttr);
86 Attribute convertBundleConstant(BundleType, ArrayAttr);
87 Attribute convertBundleInVectorConstant(BundleType, ArrayRef<Attribute>);
88
89 /// Blow out an annotation target to it's leaf targets.
90 void explodeFieldID(Type, uint64_t,
91 SmallVectorImpl<std::pair<Type, uint64_t>> &);
92 void fixAnnotation(Type, Type, DictionaryAttr, SmallVectorImpl<Attribute> &);
93 ArrayAttr fixAnnotations(Type, Type, ArrayAttr);
94
95 /// Blow out a value into it's leaf-values.
96 void explode(Value, SmallVectorImpl<Value> &);
97 SmallVector<Value> explode(Value);
98 /// Blow out a ref into it's leaf-refs.
99 void explodeRef(Value, SmallVectorImpl<Value> &);
100 /// Fix an operand.
101 std::pair<SmallVector<Value>, bool> fixOperand(Value);
102 /// Fix a read-only/rhs operand. This operand may be rematerialized as a
103 /// bundle using an intermediate bundle-create op, which means it is not
104 /// possible to write to the converted value.
105 Value fixROperand(Value);
106 /// Fix a ref-typed operand.
107 std::pair<SmallVector<Value>, bool> fixRefOperand(Value);
108
109 /// Bundle/Vector Create Ops
110 Value sinkVecDimIntoOperands(ImplicitLocOpBuilder &, FIRRTLBaseType,
111 const SmallVectorImpl<Value> &);
112
113 /// pull an access op from the cache if available, create the op if needed.
114 Value getSubfield(Value, unsigned);
115 Value getSubindex(Value, unsigned);
116 Value getSubaccess(Operation *, Value, Value);
117 Value getRefSub(Value, unsigned);
118
119 MLIRContext *context;
120 SmallVector<Operation *> toDelete;
121
122 /// A mapping from old values to their fixed up values. If a value is
123 /// unchanged, it will be mapped to itself. If a value is present in the map,
124 /// and does not map to itself, then it must be deleted.
125 DenseMap<Value, Value> valueMap;
126
127 /// A cache mapping unconverted types to their bv-converted equivalents.
128 DenseMap<FIRRTLType, FIRRTLType> typeMap;
129
130 /// A cache of generated subfield/index/access operations.
131 DenseMap<std::tuple<Value, unsigned>, Value> subfieldCache;
132 DenseMap<std::tuple<Value, unsigned>, Value> subindexCache;
133 DenseMap<std::tuple<Operation *, Value, Value>, Value> subaccessCache;
134 DenseMap<std::tuple<Value, unsigned>, Value> refSubCache;
135};
136} // end anonymous namespace
137
138Visitor::Visitor(MLIRContext *context) : context(context) {}
139
140//===----------------------------------------------------------------------===//
141// Type Conversion
142//===----------------------------------------------------------------------===//
143
144// NOLINTNEXTLINE(misc-no-recursion)
145FIRRTLBaseType Visitor::convertType(FIRRTLBaseType type,
146 SmallVector<unsigned> &dimensions) {
147 if (auto vectorType = type_dyn_cast<FVectorType>(type); vectorType) {
148 dimensions.push_back(vectorType.getNumElements());
149 auto converted = convertType(vectorType.getElementType(), dimensions);
150 dimensions.pop_back();
151 return converted;
152 }
153 if (auto bundleType = type_dyn_cast<BundleType>(type); bundleType) {
154 SmallVector<BundleType::BundleElement> elements;
155 for (auto element : bundleType.getElements()) {
156 elements.push_back(BundleType::BundleElement(
157 element.name, element.isFlip, convertType(element.type, dimensions)));
158 }
159 return BundleType::get(context, elements);
160 }
161 for (auto size : llvm::reverse(dimensions))
162 type = FVectorType::get(type, size);
163 return type;
164}
165
166FIRRTLBaseType Visitor::convertType(FIRRTLBaseType type) {
167 auto cached = typeMap.lookup(type);
168 if (cached)
169 return type_cast<FIRRTLBaseType>(cached);
170
171 SmallVector<unsigned> dimensions;
172 auto converted = convertType(type, dimensions);
173
174 typeMap.insert({type, converted});
175 return converted;
176}
177
178RefType Visitor::convertType(RefType type) {
179 auto cached = typeMap.lookup(type);
180 if (cached)
181 return type_cast<RefType>(cached);
182 auto converted = RefType::get(convertType(type.getType()),
183 type.getForceable(), type.getLayer());
184 typeMap.insert({type, converted});
185 return converted;
186}
187
188FIRRTLType Visitor::convertType(FIRRTLType type) {
189 auto cached = typeMap.lookup(type);
190 if (cached)
191 return type_cast<FIRRTLType>(cached);
192 if (auto baseType = type_dyn_cast<FIRRTLBaseType>(type))
193 return convertType(baseType);
194 if (auto refType = type_dyn_cast<RefType>(type))
195 return convertType(refType);
196 return type;
197}
198
199Type Visitor::convertType(Type type) {
200 if (auto firrtlType = type_dyn_cast<FIRRTLType>(type))
201 return convertType(firrtlType);
202 return type;
203}
204
205//===----------------------------------------------------------------------===//
206// Annotations
207//===----------------------------------------------------------------------===//
208
209// NOLINTNEXTLINE(misc-no-recursion)
210void Visitor::explodeFieldID(
211 Type type, uint64_t fieldID,
212 SmallVectorImpl<std::pair<Type, uint64_t>> &fields) {
213 if (auto bundleType = type_dyn_cast<BundleType>(type)) {
214 for (size_t i = 0, e = bundleType.getNumElements(); i < e; ++i) {
215 auto eltType = bundleType.getElementType(i);
216 auto eltID = fieldID + bundleType.getFieldID(i);
217 explodeFieldID(eltType, eltID, fields);
218 }
219 return;
220 }
221 fields.emplace_back(type, fieldID);
222}
223
224void Visitor::fixAnnotation(Type oldType, Type newType, DictionaryAttr annoAttr,
225 SmallVectorImpl<Attribute> &newAnnos) {
226 Annotation anno(annoAttr);
227 auto fieldID = anno.getFieldID();
228
229 // If the field ID targets the entire structure, we don't need to make a
230 // change.
231 if (fieldID == 0) {
232 newAnnos.push_back(anno.getAttr());
233 return;
234 }
235
236 SmallVector<uint32_t> bundleAccesses;
237 SmallVector<uint32_t> vectorAccesses;
238 while (fieldID != 0) {
239 if (auto bundleType = type_dyn_cast<BundleType>(oldType)) {
240 auto [index, subID] = bundleType.getIndexAndSubfieldID(fieldID);
241 bundleAccesses.push_back(index);
242 oldType = bundleType.getElementType(index);
243 fieldID = subID;
244 continue;
245 }
246 if (auto vectorType = type_dyn_cast<FVectorType>(oldType)) {
247 auto [index, subID] = vectorType.getIndexAndSubfieldID(fieldID);
248 vectorAccesses.push_back(index);
249 oldType = vectorType.getElementType();
250 fieldID = subID;
251 continue;
252 }
253 llvm_unreachable("non-zero field ID can only be used on aggregate types");
254 }
255
256 uint64_t newID = 0;
257 for (auto index : bundleAccesses) {
258 auto bundleType = type_cast<BundleType>(newType);
259 newID += bundleType.getFieldID(index);
260 newType = bundleType.getElementType(index);
261 }
262
263 SmallVector<std::pair<Type, uint64_t>> fields;
264 if (type_isa<BundleType>(newType) && !vectorAccesses.empty()) {
265 explodeFieldID(newType, newID, fields);
266 } else {
267 fields.emplace_back(newType, newID);
268 }
269
270 auto i64Type = IntegerType::get(context, 64);
271 for (auto [type, fieldID] : fields) {
272 for (auto index : vectorAccesses) {
273 auto vectorType = type_cast<FVectorType>(type);
274 type = vectorType.getElementType();
275 fieldID += vectorType.getFieldID(index);
276 }
277 anno.setMember("circt.fieldID", IntegerAttr::get(i64Type, fieldID));
278 newAnnos.push_back(anno.getAttr());
279 }
280}
281
282ArrayAttr Visitor::fixAnnotations(Type oldType, Type newType, ArrayAttr annos) {
283 SmallVector<Attribute> newAnnos;
284 for (auto anno : cast<ArrayAttr>(annos).getAsRange<DictionaryAttr>())
285 fixAnnotation(oldType, newType, anno, newAnnos);
286 return ArrayAttr::get(context, newAnnos);
287}
288
289//===----------------------------------------------------------------------===//
290// Path Building and Caching
291//===----------------------------------------------------------------------===//
292
293Value Visitor::getSubfield(Value input, unsigned index) {
294 Value &result = subfieldCache[{input, index}];
295 if (result)
296 return result;
297
298 OpBuilder builder(context);
299 builder.setInsertionPointAfterValue(input);
300 result = builder.create<SubfieldOp>(input.getLoc(), input, index);
301 return result;
302}
303
304Value Visitor::getSubindex(Value input, unsigned index) {
305 auto &result = subindexCache[{input, index}];
306 if (result)
307 return result;
308
309 OpBuilder builder(context);
310 builder.setInsertionPointAfterValue(input);
311 result = builder.create<SubindexOp>(input.getLoc(), input, index);
312 return result;
313}
314
315Value Visitor::getSubaccess(Operation *place, Value input, Value index) {
316 auto &result = subaccessCache[{place, input, index}];
317 if (result)
318 return result;
319 OpBuilder builder(place);
320 result = builder.create<SubaccessOp>(input.getLoc(), input, index);
321 return result;
322}
323
324Value Visitor::getRefSub(Value input, unsigned index) {
325 auto &result = refSubCache[{input, index}];
326 if (result)
327 return result;
328 OpBuilder builder(context);
329 builder.setInsertionPointAfterValue(input);
330 result = builder.create<RefSubOp>(input.getLoc(), input, index);
331 return result;
332}
333
334//===----------------------------------------------------------------------===//
335// Ref Operand Fixup
336//===----------------------------------------------------------------------===//
337
338// NOLINTNEXTLINE(misc-no-recursion)
339void Visitor::explodeRef(Value value, SmallVectorImpl<Value> &output) {
340 auto underlyingType = type_cast<RefType>(value.getType()).getType();
341 if (auto bundleType = type_dyn_cast<BundleType>(underlyingType)) {
342 for (size_t i = 0, e = bundleType.getNumElements(); i < e; ++i) {
343 OpBuilder builder(context);
344 builder.setInsertionPointAfterValue(value);
345 auto field = builder.create<RefSubOp>(value.getLoc(), value, i);
346 explodeRef(field, output);
347 }
348 return;
349 }
350 output.push_back(value);
351}
352
353std::pair<SmallVector<Value>, bool> Visitor::fixRefOperand(Value value) {
354 SmallVector<unsigned> bundleAccesses;
355 SmallVector<unsigned> vectorAccesses;
356
357 // Sort subref operations into either bundle or vector accesses.
358 while (value) {
359 Operation *op = value.getDefiningOp();
360 if (!op)
361 break;
362 if (auto refSubOp = dyn_cast<RefSubOp>(op)) {
363 value = refSubOp.getInput();
364 auto type = type_cast<RefType>(value.getType()).getType();
365 if (type_isa<BundleType>(type))
366 bundleAccesses.push_back(refSubOp.getIndex());
367 else if (type_isa<FVectorType>(type))
368 vectorAccesses.push_back(refSubOp.getIndex());
369 else
370 refSubOp->emitError("unknown aggregate type");
371 continue;
372 }
373 break;
374 }
375
376 // value now points at canonical storage location of the ref operand.
377 // get the corresponding converted object.
378 value = valueMap[value];
379 assert(value);
380
381 // replay the bundle accesses first.
382 for (auto index : llvm::reverse(bundleAccesses)) {
383 value = getRefSub(value, index);
384 }
385
386 // If the current value is a bundle type, but we need to replay vector access,
387 // that indicates an AOS->SOA conversion occurred, and a vector was sunk into
388 // a bundle. Explode the bundle object to it's leaves, all of which must be
389 // an arm of the sunken vector.
390 SmallVector<Value> values;
391 bool exploded = false;
392 if (type_isa<BundleType>(type_cast<RefType>(value.getType()).getType()) &&
393 !vectorAccesses.empty()) {
394 explodeRef(value, values);
395 exploded = true;
396 } else {
397 values.push_back(value);
398 exploded = false;
399 }
400
401 // Finally, replay any vector access operations on each of the output values.
402 for (auto &value : values) {
403 for (auto index : llvm::reverse(vectorAccesses)) {
404 value = getRefSub(value, index);
405 }
406 }
407
408 return {values, exploded};
409}
410
411//===----------------------------------------------------------------------===//
412// Operand Fixup
413//===----------------------------------------------------------------------===//
414
415// NOLINTNEXTLINE(misc-no-recursion)
416void Visitor::explode(Value value, SmallVectorImpl<Value> &output) {
417 auto type = value.getType();
418 if (auto bundleType = type_dyn_cast<BundleType>(type)) {
419 for (size_t i = 0, e = bundleType.getNumElements(); i < e; ++i) {
420 auto field = getSubfield(value, i);
421 explode(field, output);
422 }
423 return;
424 }
425 output.push_back(value);
426}
427
428SmallVector<Value> Visitor::explode(Value value) {
429 auto output = SmallVector<Value>();
430 explode(value, output);
431 return output;
432}
433
434// NOLINTNEXTLINE(misc-no-recursion)
435std::pair<SmallVector<Value>, bool> Visitor::fixOperand(Value value) {
436 auto type = value.getType();
437
438 // If the operand is a ref-type, we have a different mechanism for repairing
439 // the path.
440 if (type_isa<RefType>(type))
441 return fixRefOperand(value);
442
443 SmallVector<SubfieldOp> bundleAccesses;
444 SmallVector<Operation *> vectorAccesses;
445
446 // Walk back through the subaccess ops to the canonical storage location.
447 // Collect the path according to the type of access, splitting bundle
448 // accesses ops from vector accesses ops.
449 while (value) {
450 Operation *op = value.getDefiningOp();
451 if (!op)
452 break;
453 if (auto subfieldOp = dyn_cast<SubfieldOp>(op)) {
454 value = subfieldOp.getInput();
455 bundleAccesses.push_back(subfieldOp);
456 continue;
457 }
458 if (auto subindexOp = dyn_cast<SubindexOp>(op)) {
459 value = subindexOp.getInput();
460 vectorAccesses.push_back(subindexOp);
461 continue;
462 }
463 if (auto subaccessOp = dyn_cast<SubaccessOp>(op)) {
464 value = subaccessOp.getInput();
465 vectorAccesses.push_back(subaccessOp);
466 continue;
467 }
468 break;
469 }
470
471 // Value now points at the original canonical storage location.
472 // Get the converted equivalent object.
473 value = valueMap[value];
474 assert(value && "canonical storage location must have been converted");
475
476 // Replay the subaccess operations, in the converted order;
477 // bundle accesses first, then vector accesses.
478 for (auto subfieldOp : llvm::reverse(bundleAccesses))
479 value = getSubfield(value, subfieldOp.getFieldIndex());
480
481 // If the current value is a bundle, but we have vector accesses to replay,
482 // we must explode the bundle and apply the vector accesses to each leaf of
483 // the bundle. The leaves will be vectors corresponding to the sunken vector.
484 SmallVector<Value> values;
485 bool exploded = false;
486
487 if (type_isa<BundleType>(value.getType()) && !vectorAccesses.empty()) {
488 explode(value, values);
489 exploded = true;
490 } else {
491 values.push_back(value);
492 exploded = false;
493 }
494
495 // Finally, replay the vector access operations.
496 for (auto &value : values) {
497 for (auto *op : llvm::reverse(vectorAccesses)) {
498 if (auto subindexOp = dyn_cast<SubindexOp>(op)) {
499 value = getSubindex(value, subindexOp.getIndex());
500 continue;
501 }
502 if (auto subaccessOp = dyn_cast<SubaccessOp>(op)) {
503 auto index = fixROperand(subaccessOp.getIndex());
504 value = getSubaccess(subaccessOp, value, index);
505 continue;
506 }
507 }
508 }
509
510 return {values, exploded};
511}
512
513//===----------------------------------------------------------------------===//
514// Read-Only / RHS Operand Fixup
515//===----------------------------------------------------------------------===//
516
517// NOLINTNEXTLINE(misc-no-recursion)
518Value Visitor::fixROperand(Value operand) {
519 auto [values, exploded] = fixOperand(operand);
520 if (!exploded)
521 return values.front();
522
523 // The operand must be materialized into a single read-only bundle.
524 auto newType = convertType(operand.getType());
525 ImplicitLocOpBuilder builder(operand.getLoc(), context);
526 builder.setInsertionPointAfterValue(operand);
527 return emitBundleCreate(builder, newType, values);
528}
529
530//===----------------------------------------------------------------------===//
531// Base Case -- Any Regular Operation
532//===----------------------------------------------------------------------===//
533
534LogicalResult Visitor::visitUnhandledOp(Operation *op) {
535 ImplicitLocOpBuilder builder(op->getLoc(), op);
536 bool changed = false;
537
538 // Typical operations read from passive operands, only.
539 // We can materialize any passive operand into a single value, potentially
540 // with fresh intermediate bundle create ops in between.
541 SmallVector<Value> newOperands;
542 for (auto oldOperand : op->getOperands()) {
543 auto newOperand = fixROperand(oldOperand);
544 changed |= (oldOperand != newOperand);
545 newOperands.push_back(newOperand);
546 }
547
548 // We can rewrite the type of any result, but if any result type changes,
549 // then the operation will be cloned.
550 SmallVector<Type> newTypes;
551 for (auto oldResult : op->getResults()) {
552 auto oldType = oldResult.getType();
553 auto newType = convertType(oldType);
554 changed |= oldType != newType;
555 newTypes.push_back(newType);
556 }
557
558 if (changed) {
559 auto *newOp = builder.clone(*op);
560 newOp->setOperands(newOperands);
561 for (size_t i = 0, e = op->getNumResults(); i < e; ++i) {
562 auto newResult = newOp->getResult(i);
563 newResult.setType(newTypes[i]);
564 valueMap[op->getResult(i)] = newResult;
565 }
566
567 // Annotation updates.
568 if (auto portAnnos = op->getAttrOfType<ArrayAttr>("portAnnotations")) {
569 // Update port annotations. We make a hard assumption that there is one
570 // operation result per set of port annotations.
571 SmallVector<Attribute> newPortAnnos;
572 for (unsigned i = 0, e = portAnnos.size(); i < e; ++i) {
573 auto oldType = op->getResult(i).getType();
574 auto newType = newTypes[i];
575 newPortAnnos.push_back(
576 fixAnnotations(oldType, newType, cast<ArrayAttr>(portAnnos[i])));
577 }
578 newOp->setAttr("portAnnotations", ArrayAttr::get(context, newPortAnnos));
579 } else if (newOp->getNumResults() == 1) {
580 // Update annotations. If the operation does not have exactly 1 result,
581 // then we have no type change with which to understand how to transform
582 // the annotations. We do not update the regular annotations if the
583 // operation had port annotations.
584 if (auto annos = newOp->getAttrOfType<ArrayAttr>("annotations")) {
585 auto oldType = op->getResult(0).getType();
586 auto newType = newTypes[0];
587 auto newAnnos = fixAnnotations(oldType, newType, annos);
588 AnnotationSet(newAnnos, context).applyToOperation(newOp);
589 }
590 }
591
592 toDelete.push_back(op);
593 op = newOp;
594
595 } else {
596 // As a safety precaution, all unchanged "canonical storage locations"
597 // must be mapped to themselves.
598 for (auto result : op->getResults())
599 valueMap[result] = result;
600 }
601
602 for (auto &region : op->getRegions())
603 for (auto &block : region.getBlocks())
604 for (auto &op : block)
605 if (failed(dispatchVisitor(&op)))
606 return failure();
607
608 return success();
609}
610
611//===----------------------------------------------------------------------===//
612// Statements
613//===----------------------------------------------------------------------===//
614
615template <typename Op>
616void Visitor::emitExplodedConnect(ImplicitLocOpBuilder &builder, Type type,
617 ArrayRef<Value> lhs, ArrayRef<Value> rhs) {
618 assert(lhs.size() == rhs.size() &&
619 "Something went wrong exploding the elements");
620 const auto *lhsIt = lhs.begin();
621 const auto *rhsIt = rhs.begin();
622
623 auto explodeConnect = [&](auto self, Type type, bool flip = false) -> void {
624 if (auto bundleType = type_dyn_cast<BundleType>(type)) {
625 for (auto &element : bundleType) {
626 self(self, element.type, flip ^ element.isFlip);
627 }
628 return;
629 }
630 auto lhs = *lhsIt++;
631 auto rhs = *rhsIt++;
632 if (flip)
633 std::swap(lhs, rhs);
634 builder.create<Op>(lhs, rhs);
635 };
636 explodeConnect(explodeConnect, type);
637}
638
639Value Visitor::emitBundleCreate(ImplicitLocOpBuilder &builder, Type type,
640 ArrayRef<Value> values) {
641 auto *it = values.begin();
642 auto convert = [&](auto self, Type type) -> Value {
643 if (auto bundleType = type_dyn_cast<BundleType>(type)) {
644 SmallVector<Value> fields;
645 for (auto element : bundleType.getElements()) {
646 fields.push_back(self(self, element.type));
647 }
648 return builder.create<BundleCreateOp>(type, fields);
649 }
650 return *(it++);
651 };
652 return convert(convert, type);
653}
654
655template <typename Op>
656void Visitor::handleConnect(Op op) {
657 ImplicitLocOpBuilder builder(op.getLoc(), op);
658 auto oldLhs = op.getDest();
659 auto oldRhs = op.getSrc();
660
661 auto oldType = type_cast<FIRRTLType>(oldLhs.getType());
662 auto type = convertType(oldType);
663
664 auto [lhs, lhsExploded] = fixOperand(oldLhs);
665 auto [rhs, rhsExploded] = fixOperand(oldRhs);
666
667 if (!lhsExploded && !rhsExploded && oldLhs == lhs[0] && oldRhs == rhs[0])
668 return;
669
670 if (lhsExploded) {
671 if (rhsExploded) {
672 emitExplodedConnect<Op>(builder, type, lhs, rhs);
673 } else {
674 emitExplodedConnect<Op>(builder, type, lhs, explode(rhs[0]));
675 }
676 } else {
677 if (rhsExploded) {
678 if (auto baseType = type_dyn_cast<FIRRTLBaseType>(type);
679 baseType && baseType.isPassive()) {
680 builder.create<Op>(lhs[0], emitBundleCreate(builder, type, rhs));
681 } else {
682 emitExplodedConnect<Op>(builder, type, explode(lhs[0]), rhs);
683 }
684 } else {
685 builder.create<Op>(lhs[0], rhs[0]);
686 }
687 }
688
689 toDelete.push_back(op);
690}
691
692LogicalResult Visitor::visitStmt(ConnectOp op) {
693 handleConnect(op);
694 return success();
695}
696
697LogicalResult Visitor::visitStmt(MatchingConnectOp op) {
698 handleConnect(op);
699 return success();
700}
701
702//===----------------------------------------------------------------------===//
703// Constant Conversion
704//===----------------------------------------------------------------------===//
705
706Attribute Visitor::convertBundleInVectorConstant(BundleType type,
707 ArrayRef<Attribute> fields) {
708 auto numBundleFields = type.getNumElements();
709 SmallVector<SmallVector<Attribute>> newBundleFields;
710 newBundleFields.resize(numBundleFields);
711 for (auto bundle : fields) {
712 auto subfields = cast<ArrayAttr>(bundle);
713 for (size_t i = 0; i < numBundleFields; ++i) {
714 newBundleFields[i].push_back(subfields[i]);
715 }
716 }
717
718 SmallVector<Attribute> newFieldAttrs;
719 for (auto &newBundleField : newBundleFields) {
720 newFieldAttrs.push_back(ArrayAttr::get(context, newBundleField));
721 }
722 return ArrayAttr::get(context, newFieldAttrs);
723}
724
725// NOLINTNEXTLINE(misc-no-recursion)
726Attribute Visitor::convertVectorConstant(FVectorType oldType,
727 ArrayAttr oldElements) {
728 auto oldElementType = oldType.getElementType();
729 auto newElementType = convertType(oldElementType);
730
731 if (oldElementType == newElementType)
732 if (auto bundleElementType = type_dyn_cast<BundleType>(oldElementType))
733 return convertBundleInVectorConstant(bundleElementType,
734 oldElements.getValue());
735
736 SmallVector<Attribute> newElements;
737 for (auto oldElement : oldElements) {
738 newElements.push_back(convertConstant(oldElementType, oldElement));
739 }
740
741 auto bundleType = type_cast<BundleType>(newElementType);
742 return convertBundleInVectorConstant(bundleType, newElements);
743}
744
745// NOLINTNEXTLINE(misc-no-recursion)
746Attribute Visitor::convertBundleConstant(BundleType type, ArrayAttr fields) {
747 SmallVector<Attribute> converted;
748 auto elements = type.getElements();
749 for (size_t i = 0, e = elements.size(); i < e; ++i) {
750 converted.push_back(convertConstant(elements[i].type, fields[i]));
751 }
752 return ArrayAttr::get(context, converted);
753}
754
755// NOLINTNEXTLINE(misc-no-recursion)
756Attribute Visitor::convertConstant(Type type, Attribute value) {
757 if (auto bundleType = type_dyn_cast<BundleType>(type))
758 return convertBundleConstant(bundleType, cast<ArrayAttr>(value));
759
760 if (auto vectorType = type_dyn_cast<FVectorType>(type))
761 return convertVectorConstant(vectorType, cast<ArrayAttr>(value));
762
763 return value;
764}
765
766LogicalResult Visitor::visitExpr(AggregateConstantOp op) {
767 auto oldValue = op.getResult();
768
769 auto oldType = oldValue.getType();
770 auto newType = convertType(oldType);
771 if (oldType == newType) {
772 valueMap[oldValue] = oldValue;
773 return success();
774 }
775
776 auto fields = cast<ArrayAttr>(convertConstant(oldType, op.getFields()));
777
778 OpBuilder builder(op);
779 auto newOp =
780 builder.create<AggregateConstantOp>(op.getLoc(), newType, fields);
781
782 valueMap[oldValue] = newOp.getResult();
783 toDelete.push_back(op);
784
785 return success();
786}
787
788//===----------------------------------------------------------------------===//
789// Aggregate Create Ops
790//===----------------------------------------------------------------------===//
791
792// NOLINTNEXTLINE(misc-no-recursion)
793Value Visitor::sinkVecDimIntoOperands(ImplicitLocOpBuilder &builder,
794 FIRRTLBaseType type,
795 const SmallVectorImpl<Value> &values) {
796 auto length = values.size();
797 if (auto bundleType = type_dyn_cast<BundleType>(type)) {
798 SmallVector<Value> newFields;
799 SmallVector<BundleType::BundleElement> newElements;
800 for (auto [i, elt] : llvm::enumerate(bundleType)) {
801 SmallVector<Value> subValues;
802 for (auto v : values)
803 subValues.push_back(getSubfield(v, i));
804 auto newField = sinkVecDimIntoOperands(builder, elt.type, subValues);
805 newFields.push_back(newField);
806 newElements.emplace_back(elt.name, /*isFlip=*/false,
807 type_cast<FIRRTLBaseType>(newField.getType()));
808 }
809 auto newType = BundleType::get(builder.getContext(), newElements);
810 auto newBundle = builder.create<BundleCreateOp>(newType, newFields);
811 return newBundle;
812 }
813 auto newType = FVectorType::get(type, length);
814 return builder.create<VectorCreateOp>(newType, values);
815}
816
817LogicalResult Visitor::visitExpr(VectorCreateOp op) {
818 ImplicitLocOpBuilder builder(op.getLoc(), op);
819
820 auto oldType = op.getType();
821 auto newType = convertType(oldType);
822
823 if (oldType == newType) {
824 auto changed = false;
825 SmallVector<Value> newFields;
826 for (auto oldField : op.getFields()) {
827 auto newField = fixROperand(oldField);
828 if (oldField != newField)
829 changed = true;
830 newFields.push_back(newField);
831 }
832
833 if (!changed) {
834 auto result = op.getResult();
835 valueMap[result] = result;
836 return success();
837 }
838
839 auto newOp =
840 builder.create<VectorCreateOp>(op.getLoc(), newType, newFields);
841 valueMap[op.getResult()] = newOp.getResult();
842 toDelete.push_back(op);
843 return success();
844 }
845
846 // OK, We are in for some pain!
847 SmallVector<Value> convertedOldFields;
848 for (auto oldField : op.getFields()) {
849 auto convertedField = fixROperand(oldField);
850 convertedOldFields.push_back(convertedField);
851 }
852
853 auto value = sinkVecDimIntoOperands(
854 builder, convertType(oldType.base().getElementType()),
855 convertedOldFields);
856 valueMap[op.getResult()] = value;
857 toDelete.push_back(op);
858 return success();
859}
860
861//===----------------------------------------------------------------------===//
862// Pathing Ops
863//===----------------------------------------------------------------------===//
864
865LogicalResult Visitor::visitExpr(SubfieldOp op) {
866 toDelete.push_back(op);
867 return success();
868}
869
870LogicalResult Visitor::visitExpr(SubindexOp op) {
871 toDelete.push_back(op);
872 return success();
873}
874
875LogicalResult Visitor::visitExpr(SubaccessOp op) {
876 toDelete.push_back(op);
877 return success();
878}
879
880LogicalResult Visitor::visitExpr(RefSubOp op) {
881 toDelete.push_back(op);
882 return success();
883}
884
885//===----------------------------------------------------------------------===//
886// Ref Ops
887//===----------------------------------------------------------------------===//
888
889LogicalResult Visitor::visitExpr(RefResolveOp op) {
890 ImplicitLocOpBuilder builder(op.getLoc(), op);
891 auto [refs, exploded] = fixRefOperand(op.getRef());
892 if (!exploded) {
893 auto ref = refs[0];
894 if (ref == op.getRef()) {
895 valueMap[op.getResult()] = op.getResult();
896 return success();
897 }
898 auto value = builder.create<RefResolveOp>(convertType(op.getType()), ref)
899 .getResult();
900 valueMap[op.getResult()] = value;
901 toDelete.push_back(op);
902 return success();
903 }
904
905 auto type = convertType(op.getType());
906 SmallVector<Value> values;
907 for (auto ref : refs) {
908 values.push_back(builder
909 .create<RefResolveOp>(
910 type_cast<RefType>(ref.getType()).getType(), ref)
911 .getResult());
912 }
913 auto value = emitBundleCreate(builder, type, values);
914 valueMap[op.getResult()] = value;
915 toDelete.push_back(op);
916 return success();
917}
918
919//===----------------------------------------------------------------------===//
920// Visitor Entrypoint
921//===----------------------------------------------------------------------===//
922
923LogicalResult Visitor::visit(FModuleOp op) {
924 BitVector portsToErase(op.getNumPorts() * 2);
925 {
926 SmallVector<std::pair<unsigned, PortInfo>> newPorts;
927 auto ports = op.getPorts();
928 auto count = 0;
929 for (auto [index, port] : llvm::enumerate(ports)) {
930 auto oldType = port.type;
931 auto newType = convertType(oldType);
932 if (newType == oldType)
933 continue;
934 auto newPort = port;
935 newPort.type = newType;
936 newPort.annotations = AnnotationSet(
937 fixAnnotations(oldType, newType, port.annotations.getArrayAttr()));
938 portsToErase[count + index] = true;
939 newPorts.push_back({index + 1, newPort});
940
941 ++count;
942 }
943 op.insertPorts(newPorts);
944 }
945
946 auto *body = op.getBodyBlock();
947 for (unsigned i = 0, e = body->getNumArguments(); i < e; ++i) {
948 if (portsToErase[i]) {
949 auto oldArg = body->getArgument(i);
950 auto newArg = body->getArgument(i + 1);
951 valueMap[oldArg] = newArg;
952 } else {
953 auto oldArg = body->getArgument(i);
954 valueMap[oldArg] = oldArg;
955 }
956 }
957
958 for (auto &op : *body) {
959 if (failed(dispatchVisitor(&op)))
960 return failure();
961 }
962
963 while (!toDelete.empty())
964 toDelete.pop_back_val()->erase();
965 op.erasePorts(portsToErase);
966
967 return success();
968}
969
970//===----------------------------------------------------------------------===//
971// Pass Infrastructure
972//===----------------------------------------------------------------------===//
973
974namespace {
975class VBToBVPass : public circt::firrtl::impl::VBToBVBase<VBToBVPass> {
976 void runOnOperation() override;
977};
978} // end anonymous namespace
979
980void VBToBVPass::runOnOperation() {
981 std::vector<FModuleOp> modules;
982 llvm::append_range(modules, getOperation().getBody().getOps<FModuleOp>());
983 auto result =
984 failableParallelForEach(&getContext(), modules, [&](FModuleOp module) {
985 Visitor visitor(&getContext());
986 return visitor.visit(module);
987 });
988
989 if (result.failed())
990 signalPassFailure();
991}
992
993std::unique_ptr<mlir::Pass> circt::firrtl::createVBToBVPass() {
994 return std::make_unique<VBToBVPass>();
995}
assert(baseType &&"element must be base type")
static FIRRTLBaseType convertType(FIRRTLBaseType type)
Returns null type if no conversion is needed.
Definition DropConst.cpp:32
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.
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 ...
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 > createVBToBVPass()
Definition VBToBV.cpp:993
ModulePort::Direction flip(ModulePort::Direction direction)
Flip a port direction.
Definition HWOps.cpp:36
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.