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