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