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