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