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