CIRCT  20.0.0git
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 
29 namespace circt {
30 namespace firrtl {
31 #define GEN_PASS_DEF_VBTOBV
32 #include "circt/Dialect/FIRRTL/Passes.h.inc"
33 } // namespace firrtl
34 } // namespace circt
35 
36 using namespace circt;
37 using namespace firrtl;
38 
39 //===----------------------------------------------------------------------===//
40 // Visitor
41 //===----------------------------------------------------------------------===//
42 
43 namespace {
44 class Visitor : public FIRRTLVisitor<Visitor, LogicalResult> {
45 public:
46  explicit Visitor(MLIRContext *);
47 
48  LogicalResult visit(FModuleOp);
49 
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 
138 Visitor::Visitor(MLIRContext *context) : context(context) {}
139 
140 //===----------------------------------------------------------------------===//
141 // Type Conversion
142 //===----------------------------------------------------------------------===//
143 
144 // NOLINTNEXTLINE(misc-no-recursion)
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 
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 
178 RefType 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 
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 
199 Type 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)
210 void 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 
224 void 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 
282 ArrayAttr 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 
293 Value 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 
304 Value 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 
315 Value 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 
324 Value 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)
339 void 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 
353 std::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)
416 void 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 
428 SmallVector<Value> Visitor::explode(Value value) {
429  auto output = SmallVector<Value>();
430  explode(value, output);
431  return output;
432 }
433 
434 // NOLINTNEXTLINE(misc-no-recursion)
435 std::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)
518 Value 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 
534 LogicalResult 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 
615 template <typename Op>
616 void 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 
639 Value 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 
655 template <typename Op>
656 void 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 
692 LogicalResult Visitor::visitStmt(ConnectOp op) {
693  handleConnect(op);
694  return success();
695 }
696 
697 LogicalResult Visitor::visitStmt(MatchingConnectOp op) {
698  handleConnect(op);
699  return success();
700 }
701 
702 //===----------------------------------------------------------------------===//
703 // Constant Conversion
704 //===----------------------------------------------------------------------===//
705 
706 Attribute 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)
726 Attribute 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)
746 Attribute 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)
756 Attribute 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 
766 LogicalResult 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)
793 Value 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 
817 LogicalResult 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 
865 LogicalResult Visitor::visitExpr(SubfieldOp op) {
866  toDelete.push_back(op);
867  return success();
868 }
869 
870 LogicalResult Visitor::visitExpr(SubindexOp op) {
871  toDelete.push_back(op);
872  return success();
873 }
874 
875 LogicalResult Visitor::visitExpr(SubaccessOp op) {
876  toDelete.push_back(op);
877  return success();
878 }
879 
880 LogicalResult Visitor::visitExpr(RefSubOp op) {
881  toDelete.push_back(op);
882  return success();
883 }
884 
885 //===----------------------------------------------------------------------===//
886 // Ref Ops
887 //===----------------------------------------------------------------------===//
888 
889 LogicalResult 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 
923 LogicalResult 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 
974 namespace {
975 class VBToBVPass : public circt::firrtl::impl::VBToBVBase<VBToBVPass> {
976  void runOnOperation() override;
977 };
978 } // end anonymous namespace
979 
980 void 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 
993 std::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.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
Direction flip(Direction direction)
Flip a port direction.
std::unique_ptr< mlir::Pass > createVBToBVPass()
Definition: VBToBV.cpp:993
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21