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