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