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