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