CIRCT 24.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);
79 FIRRTLType convertType(FIRRTLType);
80 RefType convertType(RefType);
81 FIRRTLBaseType convertType(FIRRTLBaseType);
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 = SubfieldOp::create(builder, 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 = SubindexOp::create(builder, 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 = SubaccessOp::create(builder, 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 = RefSubOp::create(builder, 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 = RefSubOp::create(builder, 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 Op::create(builder, 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 BundleCreateOp::create(builder, 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 Op::create(builder, lhs[0], emitBundleCreate(builder, type, rhs));
681 } else {
682 emitExplodedConnect<Op>(builder, type, explode(lhs[0]), rhs);
683 }
684 } else {
685 Op::create(builder, 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 AggregateConstantOp::create(builder, 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 = BundleCreateOp::create(builder, newType, newFields);
811 return newBundle;
812 }
813 auto newType = FVectorType::get(type, length);
814 return VectorCreateOp::create(builder, 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 VectorCreateOp::create(builder, 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 = RefResolveOp::create(builder, 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(
909 RefResolveOp::create(builder,
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}
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static FIRRTLBaseType convertType(FIRRTLBaseType type)
Returns null type if no conversion is needed.
Definition DropConst.cpp:32
static LogicalResult convert(arc::ExecuteOp op, arc::ExecuteOp::Adaptor adaptor, ConversionPatternRewriter &rewriter, const TypeConverter &converter)
false return failure()
Parse an optional keyword or string and set instance into 'result'.
return success()
FIRRTLVisitor allows you to visit all of the expr/stmt/decls with one class declaration.
BaseTy type_cast(Type type)
BaseTy type_dyn_cast(Type type)
bool type_isa(Type type)
ModulePort::Direction flip(ModulePort::Direction direction)
Flip a port direction.
Definition HWOps.cpp:36
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.