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