CIRCT  18.0.0git
FIRRTLUtils.cpp
Go to the documentation of this file.
1 //===- FIRRTLUtils.cpp - FIRRTL IR Utilities --------------------*- 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 various utilties to help generate and process FIRRTL IR.
10 //
11 //===----------------------------------------------------------------------===//
12 
14 #include "circt/Dialect/HW/HWOps.h"
17 #include "mlir/IR/ImplicitLocOpBuilder.h"
18 #include "llvm/ADT/TypeSwitch.h"
19 
20 using namespace circt;
21 using namespace firrtl;
22 
23 void circt::firrtl::emitConnect(OpBuilder &builder, Location loc, Value dst,
24  Value src) {
25  ImplicitLocOpBuilder locBuilder(loc, builder.getInsertionBlock(),
26  builder.getInsertionPoint());
27  emitConnect(locBuilder, dst, src);
28  builder.restoreInsertionPoint(locBuilder.saveInsertionPoint());
29 }
30 
31 /// Emit a connect between two values.
32 void circt::firrtl::emitConnect(ImplicitLocOpBuilder &builder, Value dst,
33  Value src) {
34  auto dstFType = type_cast<FIRRTLType>(dst.getType());
35  auto srcFType = type_cast<FIRRTLType>(src.getType());
36  auto dstType = type_dyn_cast<FIRRTLBaseType>(dstFType);
37  auto srcType = type_dyn_cast<FIRRTLBaseType>(srcFType);
38  // Special Connects (non-base, foreign):
39  if (!dstType) {
40  // References use ref.define. Add cast if types don't match.
41  if (type_isa<RefType>(dstFType)) {
42  if (dstFType != srcFType)
43  src = builder.create<RefCastOp>(dstFType, src);
44  builder.create<RefDefineOp>(dst, src);
45  } else // Other types, give up and leave a connect
46  builder.create<ConnectOp>(dst, src);
47  return;
48  }
49 
50  // If the types are the exact same we can just connect them.
51  if (dstType == srcType && dstType.isPassive() &&
52  !dstType.hasUninferredWidth()) {
53  builder.create<StrictConnectOp>(dst, src);
54  return;
55  }
56 
57  if (auto dstBundle = type_dyn_cast<BundleType>(dstType)) {
58  // Connect all the bundle elements pairwise.
59  auto numElements = dstBundle.getNumElements();
60  // Check if we are trying to create an illegal connect - just create the
61  // connect and let the verifier catch it.
62  auto srcBundle = type_dyn_cast<BundleType>(srcType);
63  if (!srcBundle || numElements != srcBundle.getNumElements()) {
64  builder.create<ConnectOp>(dst, src);
65  return;
66  }
67  for (size_t i = 0; i < numElements; ++i) {
68  auto dstField = builder.create<SubfieldOp>(dst, i);
69  auto srcField = builder.create<SubfieldOp>(src, i);
70  if (dstBundle.getElement(i).isFlip)
71  std::swap(dstField, srcField);
72  emitConnect(builder, dstField, srcField);
73  }
74  return;
75  }
76 
77  if (auto dstVector = type_dyn_cast<FVectorType>(dstType)) {
78  // Connect all the vector elements pairwise.
79  auto numElements = dstVector.getNumElements();
80  // Check if we are trying to create an illegal connect - just create the
81  // connect and let the verifier catch it.
82  auto srcVector = type_dyn_cast<FVectorType>(srcType);
83  if (!srcVector || numElements != srcVector.getNumElements()) {
84  builder.create<ConnectOp>(dst, src);
85  return;
86  }
87  for (size_t i = 0; i < numElements; ++i) {
88  auto dstField = builder.create<SubindexOp>(dst, i);
89  auto srcField = builder.create<SubindexOp>(src, i);
90  emitConnect(builder, dstField, srcField);
91  }
92  return;
93  }
94 
95  if ((dstType.hasUninferredReset() || srcType.hasUninferredReset()) &&
96  dstType != srcType) {
97  srcType = dstType.getConstType(srcType.isConst());
98  src = builder.create<UninferredResetCastOp>(srcType, src);
99  }
100 
101  // Handle passive types with possibly uninferred widths.
102  auto dstWidth = dstType.getBitWidthOrSentinel();
103  auto srcWidth = srcType.getBitWidthOrSentinel();
104  if (dstWidth < 0 || srcWidth < 0) {
105  // If one of these types has an uninferred width, we connect them with a
106  // regular connect operation.
107 
108  // Const-cast as needed, using widthless version of dest.
109  // (dest is either widthless already, or source is and if the types
110  // can be const-cast'd, do so)
111  if (dstType != srcType && dstType.getWidthlessType() != srcType &&
112  areTypesConstCastable(dstType.getWidthlessType(), srcType)) {
113  src = builder.create<ConstCastOp>(dstType.getWidthlessType(), src);
114  }
115 
116  builder.create<ConnectOp>(dst, src);
117  return;
118  }
119 
120  // The source must be extended or truncated.
121  if (dstWidth < srcWidth) {
122  // firrtl.tail always returns uint even for sint operands.
123  IntType tmpType =
124  type_cast<IntType>(dstType).getConstType(srcType.isConst());
125  bool isSignedDest = tmpType.isSigned();
126  if (isSignedDest)
127  tmpType =
128  UIntType::get(dstType.getContext(), dstWidth, srcType.isConst());
129  src = builder.create<TailPrimOp>(tmpType, src, srcWidth - dstWidth);
130  // Insert the cast back to signed if needed.
131  if (isSignedDest)
132  src = builder.create<AsSIntPrimOp>(
133  dstType.getConstType(tmpType.isConst()), src);
134  } else if (srcWidth < dstWidth) {
135  // Need to extend arg.
136  src = builder.create<PadPrimOp>(src, dstWidth);
137  }
138 
139  if (auto srcType = type_cast<FIRRTLBaseType>(src.getType());
140  srcType && dstType != srcType &&
141  areTypesConstCastable(dstType, srcType)) {
142  src = builder.create<ConstCastOp>(dstType, src);
143  }
144 
145  // Strict connect requires the types to be completely equal, including
146  // connecting uint<1> to abstract reset types.
147  if (dstType == src.getType() && dstType.isPassive() &&
148  !dstType.hasUninferredWidth()) {
149  builder.create<StrictConnectOp>(dst, src);
150  } else
151  builder.create<ConnectOp>(dst, src);
152 }
153 
154 IntegerAttr circt::firrtl::getIntAttr(Type type, const APInt &value) {
155  auto intType = type_cast<IntType>(type);
156  assert((!intType.hasWidth() ||
157  (unsigned)intType.getWidthOrSentinel() == value.getBitWidth()) &&
158  "value / type width mismatch");
159  auto intSign =
160  intType.isSigned() ? IntegerType::Signed : IntegerType::Unsigned;
161  auto attrType =
162  IntegerType::get(type.getContext(), value.getBitWidth(), intSign);
163  return IntegerAttr::get(attrType, value);
164 }
165 
166 /// Return an IntegerAttr filled with zeros for the specified FIRRTL integer
167 /// type. This handles both the known width and unknown width case.
168 IntegerAttr circt::firrtl::getIntZerosAttr(Type type) {
169  int32_t width = abs(type_cast<IntType>(type).getWidthOrSentinel());
170  return getIntAttr(type, APInt(width, 0));
171 }
172 
173 /// Return an IntegerAttr filled with ones for the specified FIRRTL integer
174 /// type. This handles both the known width and unknown width case.
175 IntegerAttr circt::firrtl::getIntOnesAttr(Type type) {
176  int32_t width = abs(type_cast<IntType>(type).getWidthOrSentinel());
177  return getIntAttr(type, APInt(width, -1));
178 }
179 
180 /// Return the single assignment to a Property value. It is assumed that the
181 /// single assigment invariant is enforced elsewhere.
183  for (auto *user : value.getUsers())
184  if (auto propassign = dyn_cast<PropAssignOp>(user))
185  if (propassign.getDest() == value)
186  return propassign;
187 
188  // The invariant that there is a single assignment should be enforced
189  // elsewhere. If for some reason a user called this on a Property value that
190  // is not assigned (like a module input port), just return null.
191  return nullptr;
192 }
193 
194 /// Return the value that drives another FIRRTL value within module scope. Only
195 /// look backwards through one connection. This is intended to be used in
196 /// situations where you only need to look at the most recent connect, e.g., to
197 /// know if a wire has been driven to a constant. Return null if no driver via
198 /// a connect was found.
200  for (auto *user : val.getUsers()) {
201  if (auto connect = dyn_cast<FConnectLike>(user)) {
202  if (connect.getDest() != val)
203  continue;
204  return connect.getSrc();
205  }
206  }
207  return nullptr;
208 }
209 
211  bool lookThroughNodes,
212  bool lookThroughCasts) {
213  // Update `val` to the source of the connection driving `thisVal`. This walks
214  // backwards across users to find the first connection and updates `val` to
215  // the source. This assumes that only one connect is driving `thisVal`, i.e.,
216  // this pass runs after `ExpandWhens`.
217  auto updateVal = [&](Value thisVal) {
218  for (auto *user : thisVal.getUsers()) {
219  if (auto connect = dyn_cast<FConnectLike>(user)) {
220  if (connect.getDest() != val)
221  continue;
222  val = connect.getSrc();
223  return;
224  }
225  }
226  val = nullptr;
227  return;
228  };
229 
230  while (val) {
231  // The value is a port.
232  if (auto blockArg = dyn_cast<BlockArgument>(val)) {
233  FModuleOp op = cast<FModuleOp>(val.getParentBlock()->getParentOp());
234  auto direction = op.getPortDirection(blockArg.getArgNumber());
235  // Base case: this is one of the module's input ports.
236  if (direction == Direction::In)
237  return blockArg;
238  updateVal(blockArg);
239  continue;
240  }
241 
242  auto *op = val.getDefiningOp();
243 
244  // The value is an instance port.
245  if (auto inst = dyn_cast<InstanceOp>(op)) {
246  auto resultNo = cast<OpResult>(val).getResultNumber();
247  // Base case: this is an instance's output port.
248  if (inst.getPortDirection(resultNo) == Direction::Out)
249  return inst.getResult(resultNo);
250  updateVal(val);
251  continue;
252  }
253 
254  // If told to look through wires, continue from the driver of the wire.
255  if (lookThroughWires && isa<WireOp>(op)) {
256  updateVal(op->getResult(0));
257  continue;
258  }
259 
260  // If told to look through nodes, continue from the node input.
261  if (lookThroughNodes && isa<NodeOp>(op)) {
262  val = cast<NodeOp>(op).getInput();
263  continue;
264  }
265 
266  if (lookThroughCasts &&
267  isa<AsUIntPrimOp, AsSIntPrimOp, AsClockPrimOp, AsAsyncResetPrimOp>(
268  op)) {
269  val = op->getOperand(0);
270  continue;
271  }
272 
273  // Look through unary ops generated by emitConnect
274  if (isa<PadPrimOp, TailPrimOp>(op)) {
275  val = op->getOperand(0);
276  continue;
277  }
278 
279  // Base case: this is a constant/invalid or primop.
280  //
281  // TODO: If needed, this could be modified to look through unary ops which
282  // have an unambiguous single driver. This should only be added if a need
283  // arises for it.
284  break;
285  };
286  return val;
287 }
288 
290  bool lookThroughNodes, bool lookThroughCasts,
291  WalkDriverCallback callback) {
292  // TODO: what do we want to happen when there are flips in the type? Do we
293  // want to filter out fields which have reverse flow?
294  assert(value.getType().isPassive() && "this code was not tested with flips");
295 
296  // This method keeps a stack of wires (or ports) and subfields of those that
297  // it still has to process. It keeps track of which fields in the
298  // destination are attached to which fields of the source, as well as which
299  // subfield of the source we are currently investigating. The fieldID is
300  // used to filter which subfields of the current operation which we should
301  // visit. As an example, the src might be an aggregate wire, but the current
302  // value might be a subfield of that wire. The `src` FieldRef will represent
303  // all subaccesses to the target, but `fieldID` for the current op only needs
304  // to represent the all subaccesses between the current op and the target.
305  struct StackElement {
306  StackElement(FieldRef dst, FieldRef src, Value current, unsigned fieldID)
307  : dst(dst), src(src), current(current), it(current.user_begin()),
308  fieldID(fieldID) {}
309  // The elements of the destination that this refers to.
310  FieldRef dst;
311  // The elements of the source that this refers to.
312  FieldRef src;
313 
314  // These next fields are tied to the value we are currently iterating. This
315  // is used so we can check if a connect op is reading or driving from this
316  // value.
317  Value current;
318  // An iterator of the users of the current value. An end() iterator can be
319  // constructed from the `current` value.
320  Value::user_iterator it;
321  // A filter for which fields of the current value we care about.
322  unsigned fieldID;
323  };
324  SmallVector<StackElement> workStack;
325 
326  // Helper to add record a new wire to be processed in the worklist. This will
327  // add the wire itself to the worklist, which will lead to all subaccesses
328  // being eventually processed as well.
329  auto addToWorklist = [&](FieldRef dst, FieldRef src) {
330  auto value = src.getValue();
331  workStack.emplace_back(dst, src, value, src.getFieldID());
332  };
333 
334  // Create an initial fieldRef from the input value. As a starting state, the
335  // dst and src are the same value.
336  auto original = getFieldRefFromValue(value);
337  auto fieldRef = original;
338 
339  // This loop wraps the worklist, which processes wires. Initially the worklist
340  // is empty.
341  while (true) {
342  // This loop looks through simple operations like casts and nodes. If it
343  // encounters a wire it will stop and add the wire to the worklist.
344  while (true) {
345  auto val = fieldRef.getValue();
346 
347  // The value is a port.
348  if (auto blockArg = dyn_cast<BlockArgument>(val)) {
349  auto *parent = val.getParentBlock()->getParentOp();
350  auto module = cast<FModuleLike>(parent);
351  auto direction = module.getPortDirection(blockArg.getArgNumber());
352  // Base case: this is one of the module's input ports.
353  if (direction == Direction::In) {
354  if (!callback(original, fieldRef))
355  return false;
356  break;
357  }
358  addToWorklist(original, fieldRef);
359  break;
360  }
361 
362  auto *op = val.getDefiningOp();
363 
364  // The value is an instance port.
365  if (auto inst = dyn_cast<InstanceOp>(op)) {
366  auto resultNo = cast<OpResult>(val).getResultNumber();
367  // Base case: this is an instance's output port.
368  if (inst.getPortDirection(resultNo) == Direction::Out) {
369  if (!callback(original, fieldRef))
370  return false;
371  break;
372  }
373  addToWorklist(original, fieldRef);
374  break;
375  }
376 
377  // If told to look through wires, continue from the driver of the wire.
378  if (lookThroughWires && isa<WireOp>(op)) {
379  addToWorklist(original, fieldRef);
380  break;
381  }
382 
383  // If told to look through nodes, continue from the node input.
384  if (lookThroughNodes && isa<NodeOp>(op)) {
385  auto input = cast<NodeOp>(op).getInput();
386  auto next = getFieldRefFromValue(input);
387  fieldRef = next.getSubField(fieldRef.getFieldID());
388  continue;
389  }
390 
391  // If told to look through casts, continue from the cast input.
392  if (lookThroughCasts &&
393  isa<AsUIntPrimOp, AsSIntPrimOp, AsClockPrimOp, AsAsyncResetPrimOp>(
394  op)) {
395  auto input = op->getOperand(0);
396  auto next = getFieldRefFromValue(input);
397  fieldRef = next.getSubField(fieldRef.getFieldID());
398  continue;
399  }
400 
401  // Look through unary ops generated by emitConnect.
402  if (isa<PadPrimOp, TailPrimOp>(op)) {
403  auto input = op->getOperand(0);
404  auto next = getFieldRefFromValue(input);
405  fieldRef = next.getSubField(fieldRef.getFieldID());
406  continue;
407  }
408 
409  // Base case: this is a constant/invalid or primop.
410  //
411  // TODO: If needed, this could be modified to look through unary ops which
412  // have an unambiguous single driver. This should only be added if a need
413  // arises for it.
414  if (!callback(original, fieldRef))
415  return false;
416  break;
417  }
418 
419  // Process the next element on the stack.
420  while (true) {
421  // If there is nothing left in the workstack, we are done.
422  if (workStack.empty())
423  return true;
424  auto &back = workStack.back();
425  auto current = back.current;
426  // Pop the current element if we have processed all users.
427  if (back.it == current.user_end()) {
428  workStack.pop_back();
429  continue;
430  }
431 
432  original = back.dst;
433  fieldRef = back.src;
434  auto *user = *back.it++;
435  auto fieldID = back.fieldID;
436 
437  if (auto subfield = dyn_cast<SubfieldOp>(user)) {
438  BundleType bundleType = subfield.getInput().getType();
439  auto index = subfield.getFieldIndex();
440  auto subID = bundleType.getFieldID(index);
441  // If the index of this operation doesn't match the target, skip it.
442  if (fieldID && index != bundleType.getIndexForFieldID(fieldID))
443  continue;
444  auto subRef = fieldRef.getSubField(subID);
445  auto subOriginal = original.getSubField(subID);
446  auto value = subfield.getResult();
447  workStack.emplace_back(subOriginal, subRef, value, fieldID - subID);
448  } else if (auto subindex = dyn_cast<SubindexOp>(user)) {
449  FVectorType vectorType = subindex.getInput().getType();
450  auto index = subindex.getIndex();
451  auto subID = vectorType.getFieldID(index);
452  // If the index of this operation doesn't match the target, skip it.
453  if (fieldID && index != vectorType.getIndexForFieldID(fieldID))
454  continue;
455  auto subRef = fieldRef.getSubField(subID);
456  auto subOriginal = original.getSubField(subID);
457  auto value = subindex.getResult();
458  workStack.emplace_back(subOriginal, subRef, value, fieldID - subID);
459  } else if (auto connect = dyn_cast<FConnectLike>(user)) {
460  // Make sure that this connect is driving the value.
461  if (connect.getDest() != current)
462  continue;
463  // If the value is driven by a connect, we don't have to recurse,
464  // just update the current value.
465  fieldRef = getFieldRefFromValue(connect.getSrc());
466  break;
467  }
468  }
469  }
470 }
471 
472 //===----------------------------------------------------------------------===//
473 // FieldRef helpers
474 //===----------------------------------------------------------------------===//
475 
476 /// Get the delta indexing from a value, as a FieldRef.
477 FieldRef circt::firrtl::getDeltaRef(Value value, bool lookThroughCasts) {
478  // Handle bad input.
479  if (LLVM_UNLIKELY(!value))
480  return FieldRef();
481 
482  // Block arguments are not index results, empty delta.
483  auto *op = value.getDefiningOp();
484  if (!op)
485  return FieldRef();
486 
487  // Otherwise, optionally look through casts (delta of 0),
488  // dispatch to index operations' getAccesssedField(),
489  // or return no delta.
490  return TypeSwitch<Operation *, FieldRef>(op)
491  .Case<RefCastOp, ConstCastOp, UninferredResetCastOp>(
492  [lookThroughCasts](auto op) {
493  if (!lookThroughCasts)
494  return FieldRef();
495  return FieldRef(op.getInput(), 0);
496  })
497  .Case<SubfieldOp, OpenSubfieldOp, SubindexOp, OpenSubindexOp, RefSubOp,
498  ObjectSubfieldOp>(
499  [](auto subOp) { return subOp.getAccessedField(); })
500  .Default(FieldRef());
501 }
502 
504  bool lookThroughCasts) {
505  if (LLVM_UNLIKELY(!value))
506  return {value, 0};
507 
508  // Walk through indexing operations, and optionally through casts.
509  unsigned id = 0;
510  while (true) {
511  auto deltaRef = getDeltaRef(value, lookThroughCasts);
512  if (!deltaRef)
513  return {value, id};
514  // Update total fieldID.
515  id = deltaRef.getSubField(id).getFieldID();
516  // Chase to next value.
517  value = deltaRef.getValue();
518  }
519 }
520 
521 /// Get the string name of a value which is a direct child of a declaration op.
522 static void getDeclName(Value value, SmallString<64> &string, bool nameSafe) {
523  // Treat the value as a worklist to allow for recursion.
524  while (value) {
525  if (auto arg = dyn_cast<BlockArgument>(value)) {
526  // Get the module ports and get the name.
527  auto *op = arg.getOwner()->getParentOp();
528  TypeSwitch<Operation *>(op).Case<FModuleOp, ClassOp>([&](auto op) {
529  auto name = cast<StringAttr>(op.getPortNames()[arg.getArgNumber()]);
530  string += name.getValue();
531  });
532  return;
533  }
534 
535  auto *op = value.getDefiningOp();
536  TypeSwitch<Operation *>(op)
537  .Case<ObjectOp>([&](ObjectOp op) {
538  string += op.getInstanceName();
539  value = nullptr;
540  })
541  .Case<InstanceOp, MemOp>([&](auto op) {
542  string += op.getName();
543  string += nameSafe ? "_" : ".";
544  string += op.getPortName(cast<OpResult>(value).getResultNumber())
545  .getValue();
546  value = nullptr;
547  })
548  .Case<FNamableOp>([&](auto op) {
549  string += op.getName();
550  value = nullptr;
551  })
552  .Case<mlir::UnrealizedConversionCastOp>(
553  [&](mlir::UnrealizedConversionCastOp cast) {
554  // Forward through 1:1 conversion cast ops.
555  if (cast.getNumResults() == 1 && cast.getNumOperands() == 1 &&
556  cast.getResult(0).getType() == cast.getOperand(0).getType()) {
557  value = cast.getInputs()[0];
558  } else {
559  // Can't name this.
560  string.clear();
561  value = nullptr;
562  }
563  })
564  .Default([&](auto) {
565  // Can't name this.
566  string.clear();
567  value = nullptr;
568  });
569  }
570 }
571 
572 std::pair<std::string, bool>
573 circt::firrtl::getFieldName(const FieldRef &fieldRef, bool nameSafe) {
574  SmallString<64> name;
575  auto value = fieldRef.getValue();
576  getDeclName(value, name, nameSafe);
577  bool rootKnown = !name.empty();
578 
579  auto type = value.getType();
580  auto localID = fieldRef.getFieldID();
581  while (localID) {
582  // Index directly into ref inner type.
583  if (auto refTy = type_dyn_cast<RefType>(type))
584  type = refTy.getType();
585 
586  if (auto bundleType = type_dyn_cast<BundleType>(type)) {
587  auto index = bundleType.getIndexForFieldID(localID);
588  // Add the current field string, and recurse into a subfield.
589  auto &element = bundleType.getElements()[index];
590  if (!name.empty())
591  name += nameSafe ? "_" : ".";
592  name += element.name.getValue();
593  // Recurse in to the element type.
594  type = element.type;
595  localID = localID - bundleType.getFieldID(index);
596  } else if (auto bundleType = type_dyn_cast<OpenBundleType>(type)) {
597  auto index = bundleType.getIndexForFieldID(localID);
598  // Add the current field string, and recurse into a subfield.
599  auto &element = bundleType.getElements()[index];
600  if (!name.empty())
601  name += nameSafe ? "_" : ".";
602  name += element.name.getValue();
603  // Recurse in to the element type.
604  type = element.type;
605  localID = localID - bundleType.getFieldID(index);
606  } else if (auto vecType = type_dyn_cast<FVectorType>(type)) {
607  auto index = vecType.getIndexForFieldID(localID);
608  name += nameSafe ? "_" : "[";
609  name += std::to_string(index);
610  if (!nameSafe)
611  name += "]";
612  // Recurse in to the element type.
613  type = vecType.getElementType();
614  localID = localID - vecType.getFieldID(index);
615  } else if (auto vecType = type_dyn_cast<OpenVectorType>(type)) {
616  auto index = vecType.getIndexForFieldID(localID);
617  name += nameSafe ? "_" : "[";
618  name += std::to_string(index);
619  if (!nameSafe)
620  name += "]";
621  // Recurse in to the element type.
622  type = vecType.getElementType();
623  localID = localID - vecType.getFieldID(index);
624  } else if (auto enumType = type_dyn_cast<FEnumType>(type)) {
625  auto index = enumType.getIndexForFieldID(localID);
626  auto &element = enumType.getElements()[index];
627  name += nameSafe ? "_" : ".";
628  name += element.name.getValue();
629  type = element.type;
630  localID = localID - enumType.getFieldID(index);
631  } else if (auto classType = type_dyn_cast<ClassType>(type)) {
632  auto index = classType.getIndexForFieldID(localID);
633  auto &element = classType.getElement(index);
634  name += nameSafe ? "_" : ".";
635  name += element.name.getValue();
636  type = element.type;
637  localID = localID - classType.getFieldID(index);
638  } else {
639  // If we reach here, the field ref is pointing inside some aggregate type
640  // that isn't a bundle or a vector. If the type is a ground type, then the
641  // localID should be 0 at this point, and we should have broken from the
642  // loop.
643  llvm_unreachable("unsupported type");
644  }
645  }
646 
647  return {name.str().str(), rootKnown};
648 }
649 
650 /// This gets the value targeted by a field id. If the field id is targeting
651 /// the value itself, it returns it unchanged. If it is targeting a single field
652 /// in a aggregate value, such as a bundle or vector, this will create the
653 /// necessary subaccesses to get the value.
654 Value circt::firrtl::getValueByFieldID(ImplicitLocOpBuilder builder,
655  Value value, unsigned fieldID) {
656  // When the fieldID hits 0, we've found the target value.
657  while (fieldID != 0) {
659  .Case<BundleType, OpenBundleType>([&](auto bundle) {
660  auto index = bundle.getIndexForFieldID(fieldID);
661  value = builder.create<SubfieldOp>(value, index);
662  fieldID -= bundle.getFieldID(index);
663  })
664  .Case<FVectorType, OpenVectorType>([&](auto vector) {
665  auto index = vector.getIndexForFieldID(fieldID);
666  value = builder.create<SubindexOp>(value, index);
667  fieldID -= vector.getFieldID(index);
668  })
669  .Case<RefType>([&](auto reftype) {
670  FIRRTLTypeSwitch<FIRRTLBaseType, void>(reftype.getType())
671  .template Case<BundleType, FVectorType>([&](auto type) {
672  auto index = type.getIndexForFieldID(fieldID);
673  value = builder.create<RefSubOp>(value, index);
674  fieldID -= type.getFieldID(index);
675  })
676  .Default([&](auto _) {
677  llvm::report_fatal_error(
678  "unrecognized type for indexing through with fieldID");
679  });
680  })
681  // TODO: Plumb error case out and handle in callers.
682  .Default([&](auto _) {
683  llvm::report_fatal_error(
684  "unrecognized type for indexing through with fieldID");
685  });
686  }
687  return value;
688 }
689 
690 /// Walk leaf ground types in the `firrtlType` and apply the function `fn`.
691 /// The first argument of `fn` is field ID, and the second argument is a
692 /// leaf ground type and the third argument is a bool to indicate flip.
694  FIRRTLType firrtlType,
695  llvm::function_ref<void(uint64_t, FIRRTLBaseType, bool)> fn) {
696  auto type = getBaseType(firrtlType);
697 
698  // If this is not a base type, return.
699  if (!type)
700  return;
701 
702  // If this is a ground type, don't call recursive functions.
703  if (type.isGround())
704  return fn(0, type, false);
705 
706  uint64_t fieldID = 0;
707  auto recurse = [&](auto &&f, FIRRTLBaseType type, bool isFlip) -> void {
709  .Case<BundleType>([&](BundleType bundle) {
710  for (size_t i = 0, e = bundle.getNumElements(); i < e; ++i) {
711  fieldID++;
712  f(f, bundle.getElementType(i),
713  isFlip ^ bundle.getElement(i).isFlip);
714  }
715  })
716  .template Case<FVectorType>([&](FVectorType vector) {
717  for (size_t i = 0, e = vector.getNumElements(); i < e; ++i) {
718  fieldID++;
719  f(f, vector.getElementType(), isFlip);
720  }
721  })
722  .template Case<FEnumType>([&](FEnumType fenum) {
723  for (size_t i = 0, e = fenum.getNumElements(); i < e; ++i) {
724  fieldID++;
725  f(f, fenum.getElementType(i), isFlip);
726  }
727  })
728  .Default([&](FIRRTLBaseType groundType) {
729  assert(groundType.isGround() &&
730  "only ground types are expected here");
731  fn(fieldID, groundType, isFlip);
732  });
733  };
734  recurse(recurse, type, false);
735 }
736 
737 /// Return the inner sym target for the specified value and fieldID.
738 /// If root is a blockargument, this must be FModuleLike.
740  auto root = ref.getValue();
741  if (auto arg = dyn_cast<BlockArgument>(root)) {
742  auto mod = cast<FModuleLike>(arg.getOwner()->getParentOp());
743  return hw::InnerSymTarget(arg.getArgNumber(), mod, ref.getFieldID());
744  }
745  return hw::InnerSymTarget(root.getDefiningOp(), ref.getFieldID());
746 }
747 
748 // Return InnerSymAttr with sym on specified fieldID.
749 std::pair<hw::InnerSymAttr, StringAttr> circt::firrtl::getOrAddInnerSym(
750  MLIRContext *context, hw::InnerSymAttr attr, uint64_t fieldID,
751  llvm::function_ref<hw::InnerSymbolNamespace &()> getNamespace) {
752  SmallVector<hw::InnerSymPropertiesAttr> props;
753  if (attr) {
754  // If already present, return it.
755  if (auto sym = attr.getSymIfExists(fieldID))
756  return {attr, sym};
757  llvm::append_range(props, attr.getProps());
758  }
759 
760  // Otherwise, create symbol and add to list.
761  auto sym = StringAttr::get(context, getNamespace().newName("sym"));
762  props.push_back(hw::InnerSymPropertiesAttr::get(
763  context, sym, fieldID, StringAttr::get(context, "public")));
764  // TODO: store/ensure always sorted, insert directly, faster search.
765  // For now, just be good and sort by fieldID.
766  llvm::sort(props,
767  [](auto &p, auto &q) { return p.getFieldID() < q.getFieldID(); });
768  return {hw::InnerSymAttr::get(context, props), sym};
769 }
770 
772  const hw::InnerSymTarget &target,
773  llvm::function_ref<hw::InnerSymbolNamespace &()> getNamespace) {
774  if (target.isPort()) {
775  if (auto mod = dyn_cast<FModuleOp>(target.getOp())) {
776  auto portIdx = target.getPort();
777  assert(portIdx < mod.getNumPorts());
778  auto [attr, sym] =
779  getOrAddInnerSym(mod.getContext(), mod.getPortSymbolAttr(portIdx),
780  target.getField(), getNamespace);
781  mod.setPortSymbolsAttr(portIdx, attr);
782  return sym;
783  }
784  } else {
785  // InnerSymbols only supported if op implements the interface.
786  if (auto symOp = dyn_cast<hw::InnerSymbolOpInterface>(target.getOp())) {
787  auto [attr, sym] =
788  getOrAddInnerSym(symOp.getContext(), symOp.getInnerSymAttr(),
789  target.getField(), getNamespace);
790  symOp.setInnerSymbolAttr(attr);
791  return sym;
792  }
793  }
794 
795  assert(0 && "target must be port of FModuleOp or InnerSymbol");
796  return {};
797 }
798 
800  GetNamespaceCallback getNamespace) {
801  FModuleLike module;
802  if (target.isPort())
803  module = cast<FModuleLike>(target.getOp());
804  else
805  module = target.getOp()->getParentOfType<FModuleOp>();
806  assert(module);
807 
808  return getOrAddInnerSym(target, [&]() -> hw::InnerSymbolNamespace & {
809  return getNamespace(module);
810  });
811 }
812 
813 /// Obtain an inner reference to an operation, possibly adding an `inner_sym`
814 /// to that operation.
815 hw::InnerRefAttr
817  GetNamespaceCallback getNamespace) {
818  auto mod = target.isPort() ? dyn_cast<FModuleOp>(target.getOp())
819  : target.getOp()->getParentOfType<FModuleOp>();
820  assert(mod &&
821  "must be an operation inside an FModuleOp or port of FModuleOp");
822  return hw::InnerRefAttr::get(SymbolTable::getSymbolName(mod),
823  getOrAddInnerSym(target, getNamespace));
824 }
825 
826 /// Parse a string that may encode a FIRRTL location into a LocationAttr.
827 std::pair<bool, std::optional<mlir::LocationAttr>>
828 circt::firrtl::maybeStringToLocation(StringRef spelling, bool skipParsing,
829  StringAttr &locatorFilenameCache,
830  FileLineColLoc &fileLineColLocCache,
831  MLIRContext *context) {
832  // The spelling of the token looks something like "@[Decoupled.scala 221:8]".
833  if (!spelling.startswith("@[") || !spelling.endswith("]"))
834  return {false, std::nullopt};
835 
836  spelling = spelling.drop_front(2).drop_back(1);
837 
838  // Decode the locator in "spelling", returning the filename and filling in
839  // lineNo and colNo on success. On failure, this returns an empty filename.
840  auto decodeLocator = [&](StringRef input, unsigned &resultLineNo,
841  unsigned &resultColNo) -> StringRef {
842  // Split at the last space.
843  auto spaceLoc = input.find_last_of(' ');
844  if (spaceLoc == StringRef::npos)
845  return {};
846 
847  auto filename = input.take_front(spaceLoc);
848  auto lineAndColumn = input.drop_front(spaceLoc + 1);
849 
850  // Decode the line/column. If the colon is missing, then it will be empty
851  // here.
852  StringRef lineStr, colStr;
853  std::tie(lineStr, colStr) = lineAndColumn.split(':');
854 
855  // Decode the line number and the column number if present.
856  if (lineStr.getAsInteger(10, resultLineNo))
857  return {};
858  if (!colStr.empty()) {
859  if (colStr.front() != '{') {
860  if (colStr.getAsInteger(10, resultColNo))
861  return {};
862  } else {
863  // compound locator, just parse the first part for now
864  if (colStr.drop_front().split(',').first.getAsInteger(10, resultColNo))
865  return {};
866  }
867  }
868  return filename;
869  };
870 
871  // Decode the locator spelling, reporting an error if it is malformed.
872  unsigned lineNo = 0, columnNo = 0;
873  StringRef filename = decodeLocator(spelling, lineNo, columnNo);
874  if (filename.empty())
875  return {false, std::nullopt};
876 
877  // If info locators are ignored, don't actually apply them. We still do all
878  // the verification above though.
879  if (skipParsing)
880  return {true, std::nullopt};
881 
882  /// Return an FileLineColLoc for the specified location, but use a bit of
883  /// caching to reduce thrasing the MLIRContext.
884  auto getFileLineColLoc = [&](StringRef filename, unsigned lineNo,
885  unsigned columnNo) -> FileLineColLoc {
886  // Check our single-entry cache for this filename.
887  StringAttr filenameId = locatorFilenameCache;
888  if (filenameId.str() != filename) {
889  // We missed! Get the right identifier.
890  locatorFilenameCache = filenameId = StringAttr::get(context, filename);
891 
892  // If we miss in the filename cache, we also miss in the FileLineColLoc
893  // cache.
894  return fileLineColLocCache =
895  FileLineColLoc::get(filenameId, lineNo, columnNo);
896  }
897 
898  // If we hit the filename cache, check the FileLineColLoc cache.
899  auto result = fileLineColLocCache;
900  if (result && result.getLine() == lineNo && result.getColumn() == columnNo)
901  return result;
902 
903  return fileLineColLocCache =
904  FileLineColLoc::get(filenameId, lineNo, columnNo);
905  };
906 
907  // Compound locators will be combined with spaces, like:
908  // @[Foo.scala 123:4 Bar.scala 309:14]
909  // and at this point will be parsed as a-long-string-with-two-spaces at
910  // 309:14. We'd like to parse this into two things and represent it as an
911  // MLIR fused locator, but we want to be conservatively safe for filenames
912  // that have a space in it. As such, we are careful to make sure we can
913  // decode the filename/loc of the result. If so, we accumulate results,
914  // backward, in this vector.
915  SmallVector<Location> extraLocs;
916  auto spaceLoc = filename.find_last_of(' ');
917  while (spaceLoc != StringRef::npos) {
918  // Try decoding the thing before the space. Validates that there is another
919  // space and that the file/line can be decoded in that substring.
920  unsigned nextLineNo = 0, nextColumnNo = 0;
921  auto nextFilename =
922  decodeLocator(filename.take_front(spaceLoc), nextLineNo, nextColumnNo);
923 
924  // On failure we didn't have a joined locator.
925  if (nextFilename.empty())
926  break;
927 
928  // On success, remember what we already parsed (Bar.Scala / 309:14), and
929  // move on to the next chunk.
930  auto loc =
931  getFileLineColLoc(filename.drop_front(spaceLoc + 1), lineNo, columnNo);
932  extraLocs.push_back(loc);
933  filename = nextFilename;
934  lineNo = nextLineNo;
935  columnNo = nextColumnNo;
936  spaceLoc = filename.find_last_of(' ');
937  }
938 
939  mlir::LocationAttr result = getFileLineColLoc(filename, lineNo, columnNo);
940  if (!extraLocs.empty()) {
941  extraLocs.push_back(result);
942  std::reverse(extraLocs.begin(), extraLocs.end());
943  result = FusedLoc::get(context, extraLocs);
944  }
945  return {true, result};
946 }
947 
948 /// Given a type, return the corresponding lowered type for the HW dialect.
949 /// Non-FIRRTL types are simply passed through. This returns a null type if it
950 /// cannot be lowered.
952  Type type, std::optional<Location> loc,
953  llvm::function_ref<hw::TypeAliasType(Type, BaseTypeAliasType, Location)>
954  getTypeDeclFn) {
955  auto firType = type_dyn_cast<FIRRTLBaseType>(type);
956  if (!firType)
957  return type;
958 
959  // If not known how to lower alias types, then ignore the alias.
960  if (getTypeDeclFn)
961  if (BaseTypeAliasType aliasType = dyn_cast<BaseTypeAliasType>(firType)) {
962  if (!loc)
963  loc = UnknownLoc::get(type.getContext());
964  type = lowerType(aliasType.getInnerType(), loc, getTypeDeclFn);
965  return getTypeDeclFn(type, aliasType, *loc);
966  }
967  // Ignore flip types.
968  firType = firType.getPassiveType();
969 
970  if (auto bundle = type_dyn_cast<BundleType>(firType)) {
971  mlir::SmallVector<hw::StructType::FieldInfo, 8> hwfields;
972  for (auto element : bundle) {
973  Type etype = lowerType(element.type, loc, getTypeDeclFn);
974  if (!etype)
975  return {};
976  hwfields.push_back(hw::StructType::FieldInfo{element.name, etype});
977  }
978  return hw::StructType::get(type.getContext(), hwfields);
979  }
980  if (auto vec = type_dyn_cast<FVectorType>(firType)) {
981  auto elemTy = lowerType(vec.getElementType(), loc, getTypeDeclFn);
982  if (!elemTy)
983  return {};
984  return hw::ArrayType::get(elemTy, vec.getNumElements());
985  }
986  if (auto fenum = type_dyn_cast<FEnumType>(firType)) {
987  mlir::SmallVector<hw::UnionType::FieldInfo, 8> hwfields;
988  SmallVector<Attribute> names;
989  bool simple = true;
990  for (auto element : fenum) {
991  Type etype = lowerType(element.type, loc, getTypeDeclFn);
992  if (!etype)
993  return {};
994  hwfields.push_back(hw::UnionType::FieldInfo{element.name, etype, 0});
995  names.push_back(element.name);
996  if (!isa<UIntType>(element.type) ||
997  element.type.getBitWidthOrSentinel() != 0)
998  simple = false;
999  }
1000  auto tagTy = hw::EnumType::get(type.getContext(),
1001  ArrayAttr::get(type.getContext(), names));
1002  if (simple)
1003  return tagTy;
1004  auto bodyTy = hw::UnionType::get(type.getContext(), hwfields);
1005  hw::StructType::FieldInfo fields[2] = {
1006  {StringAttr::get(type.getContext(), "tag"), tagTy},
1007  {StringAttr::get(type.getContext(), "body"), bodyTy}};
1008  return hw::StructType::get(type.getContext(), fields);
1009  }
1010  if (type_isa<ClockType>(firType))
1011  return seq::ClockType::get(firType.getContext());
1012 
1013  auto width = firType.getBitWidthOrSentinel();
1014  if (width >= 0) // IntType, analog with known width, clock, etc.
1015  return IntegerType::get(type.getContext(), width);
1016 
1017  return {};
1018 }
lowerAnnotationsNoRefTypePorts FirtoolPreserveValuesMode value
Definition: Firtool.cpp:95
assert(baseType &&"element must be base type")
MlirType uint64_t numElements
Definition: CHIRRTL.cpp:26
static void getDeclName(Value value, SmallString< 64 > &string, bool nameSafe)
Get the string name of a value which is a direct child of a declaration op.
int32_t width
Definition: FIRRTL.cpp:27
static Value lookThroughWires(Value value)
Trace a value through wires to its original definition.
Builder builder
This class represents a reference to a specific field or element of an aggregate value.
Definition: FieldRef.h:28
unsigned getFieldID() const
Get the field ID of this FieldRef, which is a unique identifier mapped to a specific field in a bundl...
Definition: FieldRef.h:59
Value getValue() const
Get the Value which created this location.
Definition: FieldRef.h:37
bool isConst()
Returns true if this is a 'const' type that can only hold compile-time constant values.
This class implements the same functionality as TypeSwitch except that it uses firrtl::type_dyn_cast ...
Definition: FIRRTLTypes.h:515
FIRRTLTypeSwitch< T, ResultT > & Case(CallableT &&caseFn)
Add a case on the given type.
Definition: FIRRTLTypes.h:525
This is the common base class between SIntType and UIntType.
Definition: FIRRTLTypes.h:291
IntType getConstType(bool isConst)
Return a 'const' or non-'const' version of this type.
The target of an inner symbol, the entity the symbol is a handle for.
Operation * getOp() const
Return the target's base operation. For ports, this is the module.
auto getField() const
Return the target's fieldID.
auto getPort() const
Return the target's port, if valid. Check "isPort()".
bool isPort() const
Return if this targets a port.
def connect(destination, source)
Definition: support.py:37
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:53
llvm::function_ref< hw::InnerSymbolNamespace &(FModuleLike mod)> GetNamespaceCallback
Definition: FIRRTLUtils.h:141
FieldRef getDeltaRef(Value value, bool lookThroughCasts=false)
Get the delta indexing from a value, as a FieldRef.
FIRRTLBaseType getBaseType(Type type)
If it is a base type, return it as is.
Definition: FIRRTLUtils.h:217
FieldRef getFieldRefFromValue(Value value, bool lookThroughCasts=false)
Get the FieldRef from a value.
mlir::TypedValue< FIRRTLBaseType > FIRRTLBaseValue
Definition: FIRRTLTypes.h:389
void walkGroundTypes(FIRRTLType firrtlType, llvm::function_ref< void(uint64_t, FIRRTLBaseType, bool)> fn)
Walk leaf ground types in the firrtlType and apply the function fn.
IntegerAttr getIntAttr(Type type, const APInt &value)
Utiility for generating a constant attribute.
std::pair< bool, std::optional< mlir::LocationAttr > > maybeStringToLocation(llvm::StringRef spelling, bool skipParsing, mlir::StringAttr &locatorFilenameCache, FileLineColLoc &fileLineColLocCache, MLIRContext *context)
std::pair< hw::InnerSymAttr, StringAttr > getOrAddInnerSym(MLIRContext *context, hw::InnerSymAttr attr, uint64_t fieldID, llvm::function_ref< hw::InnerSymbolNamespace &()> getNamespace)
Ensure that the the InnerSymAttr has a symbol on the field specified.
hw::InnerRefAttr getInnerRefTo(const hw::InnerSymTarget &target, GetNamespaceCallback getNamespace)
Obtain an inner reference to the target (operation or port), adding an inner symbol as necessary.
PropAssignOp getPropertyAssignment(FIRRTLPropertyValue value)
Return the single assignment to a Property value.
Value getModuleScopedDriver(Value val, bool lookThroughWires, bool lookThroughNodes, bool lookThroughCasts)
Return the value that drives another FIRRTL value within module scope.
Value getDriverFromConnect(Value val)
Return the module-scoped driver of a value only looking through one connect.
Value getValueByFieldID(ImplicitLocOpBuilder builder, Value value, unsigned fieldID)
This gets the value targeted by a field id.
std::pair< std::string, bool > getFieldName(const FieldRef &fieldRef, bool nameSafe=false)
Get a string identifier representing the FieldRef.
llvm::function_ref< bool(const FieldRef &dst, const FieldRef &src)> WalkDriverCallback
Walk all the drivers of a value, passing in the connect operations drive the value.
Definition: FIRRTLUtils.h:80
mlir::TypedValue< PropertyType > FIRRTLPropertyValue
Definition: FIRRTLTypes.h:390
Type lowerType(Type type, std::optional< Location > loc={}, llvm::function_ref< hw::TypeAliasType(Type, BaseTypeAliasType, Location)> getTypeDeclFn={})
Given a type, return the corresponding lowered type for the HW dialect.
hw::InnerSymTarget getTargetFor(FieldRef ref)
Return the inner sym target for the specified value and fieldID.
bool areTypesConstCastable(FIRRTLType destType, FIRRTLType srcType, bool srcOuterTypeIsConst=false)
Returns whether the srcType can be const-casted to the destType.
bool walkDrivers(FIRRTLBaseValue value, bool lookThroughWires, bool lookThroughNodes, bool lookThroughCasts, WalkDriverCallback callback)
IntegerAttr getIntOnesAttr(Type type)
Utility for generating a constant all ones attribute.
void emitConnect(OpBuilder &builder, Location loc, Value lhs, Value rhs)
Emit a connect between two values.
Definition: FIRRTLUtils.cpp:23
IntegerAttr getIntZerosAttr(Type type)
Utility for generating a constant zero attribute.
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
Definition: DebugAnalysis.h:21