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<StrictConnectOp>(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<StrictConnectOp>(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 // Return InnerSymAttr with sym on specified fieldID.
762 std::pair<hw::InnerSymAttr, StringAttr> circt::firrtl::getOrAddInnerSym(
763  MLIRContext *context, hw::InnerSymAttr attr, uint64_t fieldID,
764  llvm::function_ref<hw::InnerSymbolNamespace &()> getNamespace) {
765  SmallVector<hw::InnerSymPropertiesAttr> props;
766  if (attr) {
767  // If already present, return it.
768  if (auto sym = attr.getSymIfExists(fieldID))
769  return {attr, sym};
770  llvm::append_range(props, attr.getProps());
771  }
772 
773  // Otherwise, create symbol and add to list.
774  auto sym = StringAttr::get(context, getNamespace().newName("sym"));
775  props.push_back(hw::InnerSymPropertiesAttr::get(
776  context, sym, fieldID, StringAttr::get(context, "public")));
777  // TODO: store/ensure always sorted, insert directly, faster search.
778  // For now, just be good and sort by fieldID.
779  llvm::sort(props,
780  [](auto &p, auto &q) { return p.getFieldID() < q.getFieldID(); });
781  return {hw::InnerSymAttr::get(context, props), sym};
782 }
783 
785  const hw::InnerSymTarget &target,
786  llvm::function_ref<hw::InnerSymbolNamespace &()> getNamespace) {
787  if (target.isPort()) {
788  if (auto mod = dyn_cast<FModuleOp>(target.getOp())) {
789  auto portIdx = target.getPort();
790  assert(portIdx < mod.getNumPorts());
791  auto [attr, sym] =
792  getOrAddInnerSym(mod.getContext(), mod.getPortSymbolAttr(portIdx),
793  target.getField(), getNamespace);
794  mod.setPortSymbolsAttr(portIdx, attr);
795  return sym;
796  }
797  } else {
798  // InnerSymbols only supported if op implements the interface.
799  if (auto symOp = dyn_cast<hw::InnerSymbolOpInterface>(target.getOp())) {
800  auto [attr, sym] =
801  getOrAddInnerSym(symOp.getContext(), symOp.getInnerSymAttr(),
802  target.getField(), getNamespace);
803  symOp.setInnerSymbolAttr(attr);
804  return sym;
805  }
806  }
807 
808  assert(0 && "target must be port of FModuleOp or InnerSymbol");
809  return {};
810 }
811 
813  GetNamespaceCallback getNamespace) {
814  FModuleLike module;
815  if (target.isPort())
816  module = cast<FModuleLike>(target.getOp());
817  else
818  module = target.getOp()->getParentOfType<FModuleOp>();
819  assert(module);
820 
821  return getOrAddInnerSym(target, [&]() -> hw::InnerSymbolNamespace & {
822  return getNamespace(module);
823  });
824 }
825 
826 /// Obtain an inner reference to an operation, possibly adding an `inner_sym`
827 /// to that operation.
828 hw::InnerRefAttr
830  GetNamespaceCallback getNamespace) {
831  auto mod = target.isPort() ? dyn_cast<FModuleOp>(target.getOp())
832  : target.getOp()->getParentOfType<FModuleOp>();
833  assert(mod &&
834  "must be an operation inside an FModuleOp or port of FModuleOp");
835  return hw::InnerRefAttr::get(SymbolTable::getSymbolName(mod),
836  getOrAddInnerSym(target, getNamespace));
837 }
838 
839 /// Parse a string that may encode a FIRRTL location into a LocationAttr.
840 std::pair<bool, std::optional<mlir::LocationAttr>>
841 circt::firrtl::maybeStringToLocation(StringRef spelling, bool skipParsing,
842  StringAttr &locatorFilenameCache,
843  FileLineColLoc &fileLineColLocCache,
844  MLIRContext *context) {
845  // The spelling of the token looks something like "@[Decoupled.scala 221:8]".
846  if (!spelling.starts_with("@[") || !spelling.ends_with("]"))
847  return {false, std::nullopt};
848 
849  spelling = spelling.drop_front(2).drop_back(1);
850 
851  // Decode the locator in "spelling", returning the filename and filling in
852  // lineNo and colNo on success. On failure, this returns an empty filename.
853  auto decodeLocator = [&](StringRef input, unsigned &resultLineNo,
854  unsigned &resultColNo) -> StringRef {
855  // Split at the last space.
856  auto spaceLoc = input.find_last_of(' ');
857  if (spaceLoc == StringRef::npos)
858  return {};
859 
860  auto filename = input.take_front(spaceLoc);
861  auto lineAndColumn = input.drop_front(spaceLoc + 1);
862 
863  // Decode the line/column. If the colon is missing, then it will be empty
864  // here.
865  StringRef lineStr, colStr;
866  std::tie(lineStr, colStr) = lineAndColumn.split(':');
867 
868  // Decode the line number and the column number if present.
869  if (lineStr.getAsInteger(10, resultLineNo))
870  return {};
871  if (!colStr.empty()) {
872  if (colStr.front() != '{') {
873  if (colStr.getAsInteger(10, resultColNo))
874  return {};
875  } else {
876  // compound locator, just parse the first part for now
877  if (colStr.drop_front().split(',').first.getAsInteger(10, resultColNo))
878  return {};
879  }
880  }
881  return filename;
882  };
883 
884  // Decode the locator spelling, reporting an error if it is malformed.
885  unsigned lineNo = 0, columnNo = 0;
886  StringRef filename = decodeLocator(spelling, lineNo, columnNo);
887  if (filename.empty())
888  return {false, std::nullopt};
889 
890  // If info locators are ignored, don't actually apply them. We still do all
891  // the verification above though.
892  if (skipParsing)
893  return {true, std::nullopt};
894 
895  /// Return an FileLineColLoc for the specified location, but use a bit of
896  /// caching to reduce thrasing the MLIRContext.
897  auto getFileLineColLoc = [&](StringRef filename, unsigned lineNo,
898  unsigned columnNo) -> FileLineColLoc {
899  // Check our single-entry cache for this filename.
900  StringAttr filenameId = locatorFilenameCache;
901  if (filenameId.str() != filename) {
902  // We missed! Get the right identifier.
903  locatorFilenameCache = filenameId = StringAttr::get(context, filename);
904 
905  // If we miss in the filename cache, we also miss in the FileLineColLoc
906  // cache.
907  return fileLineColLocCache =
908  FileLineColLoc::get(filenameId, lineNo, columnNo);
909  }
910 
911  // If we hit the filename cache, check the FileLineColLoc cache.
912  auto result = fileLineColLocCache;
913  if (result && result.getLine() == lineNo && result.getColumn() == columnNo)
914  return result;
915 
916  return fileLineColLocCache =
917  FileLineColLoc::get(filenameId, lineNo, columnNo);
918  };
919 
920  // Compound locators will be combined with spaces, like:
921  // @[Foo.scala 123:4 Bar.scala 309:14]
922  // and at this point will be parsed as a-long-string-with-two-spaces at
923  // 309:14. We'd like to parse this into two things and represent it as an
924  // MLIR fused locator, but we want to be conservatively safe for filenames
925  // that have a space in it. As such, we are careful to make sure we can
926  // decode the filename/loc of the result. If so, we accumulate results,
927  // backward, in this vector.
928  SmallVector<Location> extraLocs;
929  auto spaceLoc = filename.find_last_of(' ');
930  while (spaceLoc != StringRef::npos) {
931  // Try decoding the thing before the space. Validates that there is another
932  // space and that the file/line can be decoded in that substring.
933  unsigned nextLineNo = 0, nextColumnNo = 0;
934  auto nextFilename =
935  decodeLocator(filename.take_front(spaceLoc), nextLineNo, nextColumnNo);
936 
937  // On failure we didn't have a joined locator.
938  if (nextFilename.empty())
939  break;
940 
941  // On success, remember what we already parsed (Bar.Scala / 309:14), and
942  // move on to the next chunk.
943  auto loc =
944  getFileLineColLoc(filename.drop_front(spaceLoc + 1), lineNo, columnNo);
945  extraLocs.push_back(loc);
946  filename = nextFilename;
947  lineNo = nextLineNo;
948  columnNo = nextColumnNo;
949  spaceLoc = filename.find_last_of(' ');
950  }
951 
952  mlir::LocationAttr result = getFileLineColLoc(filename, lineNo, columnNo);
953  if (!extraLocs.empty()) {
954  extraLocs.push_back(result);
955  std::reverse(extraLocs.begin(), extraLocs.end());
956  result = FusedLoc::get(context, extraLocs);
957  }
958  return {true, result};
959 }
960 
961 /// Given a type, return the corresponding lowered type for the HW dialect.
962 /// Non-FIRRTL types are simply passed through. This returns a null type if it
963 /// cannot be lowered.
965  Type type, std::optional<Location> loc,
966  llvm::function_ref<hw::TypeAliasType(Type, BaseTypeAliasType, Location)>
967  getTypeDeclFn) {
968  auto firType = type_dyn_cast<FIRRTLBaseType>(type);
969  if (!firType)
970  return type;
971 
972  // If not known how to lower alias types, then ignore the alias.
973  if (getTypeDeclFn)
974  if (BaseTypeAliasType aliasType = dyn_cast<BaseTypeAliasType>(firType)) {
975  if (!loc)
976  loc = UnknownLoc::get(type.getContext());
977  type = lowerType(aliasType.getInnerType(), loc, getTypeDeclFn);
978  return getTypeDeclFn(type, aliasType, *loc);
979  }
980  // Ignore flip types.
981  firType = firType.getPassiveType();
982 
983  if (auto bundle = type_dyn_cast<BundleType>(firType)) {
984  mlir::SmallVector<hw::StructType::FieldInfo, 8> hwfields;
985  for (auto element : bundle) {
986  Type etype = lowerType(element.type, loc, getTypeDeclFn);
987  if (!etype)
988  return {};
989  hwfields.push_back(hw::StructType::FieldInfo{element.name, etype});
990  }
991  return hw::StructType::get(type.getContext(), hwfields);
992  }
993  if (auto vec = type_dyn_cast<FVectorType>(firType)) {
994  auto elemTy = lowerType(vec.getElementType(), loc, getTypeDeclFn);
995  if (!elemTy)
996  return {};
997  return hw::ArrayType::get(elemTy, vec.getNumElements());
998  }
999  if (auto fenum = type_dyn_cast<FEnumType>(firType)) {
1000  mlir::SmallVector<hw::UnionType::FieldInfo, 8> hwfields;
1001  SmallVector<Attribute> names;
1002  bool simple = true;
1003  for (auto element : fenum) {
1004  Type etype = lowerType(element.type, loc, getTypeDeclFn);
1005  if (!etype)
1006  return {};
1007  hwfields.push_back(hw::UnionType::FieldInfo{element.name, etype, 0});
1008  names.push_back(element.name);
1009  if (!isa<UIntType>(element.type) ||
1010  element.type.getBitWidthOrSentinel() != 0)
1011  simple = false;
1012  }
1013  auto tagTy = hw::EnumType::get(type.getContext(),
1014  ArrayAttr::get(type.getContext(), names));
1015  if (simple)
1016  return tagTy;
1017  auto bodyTy = hw::UnionType::get(type.getContext(), hwfields);
1018  hw::StructType::FieldInfo fields[2] = {
1019  {StringAttr::get(type.getContext(), "tag"), tagTy},
1020  {StringAttr::get(type.getContext(), "body"), bodyTy}};
1021  return hw::StructType::get(type.getContext(), fields);
1022  }
1023  if (type_isa<ClockType>(firType))
1024  return seq::ClockType::get(firType.getContext());
1025 
1026  auto width = firType.getBitWidthOrSentinel();
1027  if (width >= 0) // IntType, analog with known width, clock, etc.
1028  return IntegerType::get(type.getContext(), width);
1029 
1030  return {};
1031 }
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.
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:518
FIRRTLTypeSwitch< T, ResultT > & Case(CallableT &&caseFn)
Add a case on the given type.
Definition: FIRRTLTypes.h:528
This is the common base class between SIntType and UIntType.
Definition: FIRRTLTypes.h:294
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: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:392
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:393
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