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