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