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