CIRCT 20.0.0git
Loading...
Searching...
No Matches
FIRRTLUtils.cpp
Go to the documentation of this file.
1//===- FIRRTLUtils.cpp - FIRRTL IR Utilities --------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file defines various utilties to help generate and process FIRRTL IR.
10//
11//===----------------------------------------------------------------------===//
12
18#include "mlir/IR/ImplicitLocOpBuilder.h"
19#include "llvm/ADT/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 = builder.create<RefCastOp>(dstFType, src);
46 builder.create<RefDefineOp>(dst, src);
47 } else if (type_isa<PropertyType>(dstFType) &&
48 type_isa<PropertyType>(srcFType)) {
49 // Properties use propassign.
50 builder.create<PropAssignOp>(dst, src);
51 } else {
52 // Other types, give up and leave a connect
53 builder.create<ConnectOp>(dst, src);
54 }
55 return;
56 }
57
58 // More special connects
59 if (isa<AnalogType>(dstType)) {
60 builder.create<AttachOp>(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 builder.create<MatchingConnectOp>(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 builder.create<ConnectOp>(dst, src);
79 return;
80 }
81 for (size_t i = 0; i < numElements; ++i) {
82 auto dstField = builder.create<SubfieldOp>(dst, i);
83 auto srcField = builder.create<SubfieldOp>(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 builder.create<ConnectOp>(dst, src);
99 return;
100 }
101 for (size_t i = 0; i < numElements; ++i) {
102 auto dstField = builder.create<SubindexOp>(dst, i);
103 auto srcField = builder.create<SubindexOp>(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 = builder.create<UninferredResetCastOp>(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 = builder.create<ConstCastOp>(dstType.getWidthlessType(), src);
128 }
129
130 builder.create<ConnectOp>(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 = builder.create<TailPrimOp>(tmpType, src, srcWidth - dstWidth);
144 // Insert the cast back to signed if needed.
145 if (isSignedDest)
146 src = builder.create<AsSIntPrimOp>(
147 dstType.getConstType(tmpType.isConst()), src);
148 } else if (srcWidth < dstWidth) {
149 // Need to extend arg.
150 src = builder.create<PadPrimOp>(src, dstWidth);
151 }
152
153 if (auto srcType = type_cast<FIRRTLBaseType>(src.getType());
154 srcType && dstType != srcType &&
155 areTypesConstCastable(dstType, srcType)) {
156 src = builder.create<ConstCastOp>(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 builder.create<MatchingConnectOp>(dst, src);
164 } else
165 builder.create<ConnectOp>(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 enumType = type_dyn_cast<FEnumType>(type)) {
652 auto index = enumType.getIndexForFieldID(localID);
653 auto &element = enumType.getElements()[index];
654 name += nameSafe ? "_" : ".";
655 name += element.name.getValue();
656 type = element.type;
657 localID = localID - enumType.getFieldID(index);
658 } else if (auto classType = type_dyn_cast<ClassType>(type)) {
659 auto index = classType.getIndexForFieldID(localID);
660 auto &element = classType.getElement(index);
661 name += nameSafe ? "_" : ".";
662 name += element.name.getValue();
663 type = element.type;
664 localID = localID - classType.getFieldID(index);
665 } else {
666 // If we reach here, the field ref is pointing inside some aggregate type
667 // that isn't a bundle or a vector. If the type is a ground type, then the
668 // localID should be 0 at this point, and we should have broken from the
669 // loop.
670 llvm_unreachable("unsupported type");
671 }
672 }
673
674 return {name.str().str(), rootKnown};
675}
676
677/// This gets the value targeted by a field id. If the field id is targeting
678/// the value itself, it returns it unchanged. If it is targeting a single field
679/// in a aggregate value, such as a bundle or vector, this will create the
680/// necessary subaccesses to get the value.
681Value circt::firrtl::getValueByFieldID(ImplicitLocOpBuilder builder,
682 Value value, unsigned fieldID) {
683 // When the fieldID hits 0, we've found the target value.
684 while (fieldID != 0) {
685 FIRRTLTypeSwitch<Type, void>(value.getType())
686 .Case<BundleType, OpenBundleType>([&](auto bundle) {
687 auto index = bundle.getIndexForFieldID(fieldID);
688 value = builder.create<SubfieldOp>(value, index);
689 fieldID -= bundle.getFieldID(index);
690 })
691 .Case<FVectorType, OpenVectorType>([&](auto vector) {
692 auto index = vector.getIndexForFieldID(fieldID);
693 value = builder.create<SubindexOp>(value, index);
694 fieldID -= vector.getFieldID(index);
695 })
696 .Case<RefType>([&](auto reftype) {
698 .template Case<BundleType, FVectorType>([&](auto type) {
699 auto index = type.getIndexForFieldID(fieldID);
700 value = builder.create<RefSubOp>(value, index);
701 fieldID -= type.getFieldID(index);
702 })
703 .Default([&](auto _) {
704 llvm::report_fatal_error(
705 "unrecognized type for indexing through with fieldID");
706 });
707 })
708 // TODO: Plumb error case out and handle in callers.
709 .Default([&](auto _) {
710 llvm::report_fatal_error(
711 "unrecognized type for indexing through with fieldID");
712 });
713 }
714 return value;
715}
716
717/// Walk leaf ground types in the `firrtlType` and apply the function `fn`.
718/// The first argument of `fn` is field ID, and the second argument is a
719/// leaf ground type and the third argument is a bool to indicate flip.
721 FIRRTLType firrtlType,
722 llvm::function_ref<void(uint64_t, FIRRTLBaseType, bool)> fn) {
723 auto type = getBaseType(firrtlType);
724
725 // If this is not a base type, return.
726 if (!type)
727 return;
728
729 // If this is a ground type, don't call recursive functions.
730 if (type.isGround())
731 return fn(0, type, false);
732
733 uint64_t fieldID = 0;
734 auto recurse = [&](auto &&f, FIRRTLBaseType type, bool isFlip) -> void {
736 .Case<BundleType>([&](BundleType bundle) {
737 for (size_t i = 0, e = bundle.getNumElements(); i < e; ++i) {
738 fieldID++;
739 f(f, bundle.getElementType(i),
740 isFlip ^ bundle.getElement(i).isFlip);
741 }
742 })
743 .template Case<FVectorType>([&](FVectorType vector) {
744 for (size_t i = 0, e = vector.getNumElements(); i < e; ++i) {
745 fieldID++;
746 f(f, vector.getElementType(), isFlip);
747 }
748 })
749 .template Case<FEnumType>([&](FEnumType fenum) {
750 for (size_t i = 0, e = fenum.getNumElements(); i < e; ++i) {
751 fieldID++;
752 f(f, fenum.getElementType(i), isFlip);
753 }
754 })
755 .Default([&](FIRRTLBaseType groundType) {
756 assert(groundType.isGround() &&
757 "only ground types are expected here");
758 fn(fieldID, groundType, isFlip);
759 });
760 };
761 recurse(recurse, type, false);
762}
763
764/// Return the inner sym target for the specified value and fieldID.
765/// If root is a blockargument, this must be FModuleLike.
767 auto root = ref.getValue();
768 if (auto arg = dyn_cast<BlockArgument>(root)) {
769 auto mod = cast<FModuleLike>(arg.getOwner()->getParentOp());
770 return hw::InnerSymTarget(arg.getArgNumber(), mod, ref.getFieldID());
771 }
772 return hw::InnerSymTarget(root.getDefiningOp(), ref.getFieldID());
773}
774
775/// Get FieldRef pointing to the specified inner symbol target, which must be
776/// valid. Returns null FieldRef if target points to something with no value,
777/// such as a port of an external module.
779 if (ist.isPort()) {
780 return TypeSwitch<Operation *, FieldRef>(ist.getOp())
781 .Case<FModuleOp>([&](auto fmod) {
782 return FieldRef(fmod.getArgument(ist.getPort()), ist.getField());
783 })
784 .Default({});
785 }
786
787 auto symOp = dyn_cast<hw::InnerSymbolOpInterface>(ist.getOp());
788 assert(symOp && symOp.getTargetResultIndex() &&
789 (symOp.supportsPerFieldSymbols() || ist.getField() == 0));
790 return FieldRef(symOp.getTargetResult(), ist.getField());
791}
792
793// Return InnerSymAttr with sym on specified fieldID.
794std::pair<hw::InnerSymAttr, StringAttr> circt::firrtl::getOrAddInnerSym(
795 MLIRContext *context, hw::InnerSymAttr attr, uint64_t fieldID,
796 llvm::function_ref<hw::InnerSymbolNamespace &()> getNamespace) {
797 SmallVector<hw::InnerSymPropertiesAttr> props;
798 if (attr) {
799 // If already present, return it.
800 if (auto sym = attr.getSymIfExists(fieldID))
801 return {attr, sym};
802 llvm::append_range(props, attr.getProps());
803 }
804
805 // Otherwise, create symbol and add to list.
806 auto sym = StringAttr::get(context, getNamespace().newName("sym"));
807 props.push_back(hw::InnerSymPropertiesAttr::get(
808 context, sym, fieldID, StringAttr::get(context, "public")));
809 // TODO: store/ensure always sorted, insert directly, faster search.
810 // For now, just be good and sort by fieldID.
811 llvm::sort(props,
812 [](auto &p, auto &q) { return p.getFieldID() < q.getFieldID(); });
813 return {hw::InnerSymAttr::get(context, props), sym};
814}
815
817 const hw::InnerSymTarget &target,
818 llvm::function_ref<hw::InnerSymbolNamespace &()> getNamespace) {
819 if (target.isPort()) {
820 if (auto mod = dyn_cast<FModuleOp>(target.getOp())) {
821 auto portIdx = target.getPort();
822 assert(portIdx < mod.getNumPorts());
823 auto [attr, sym] =
824 getOrAddInnerSym(mod.getContext(), mod.getPortSymbolAttr(portIdx),
825 target.getField(), getNamespace);
826 mod.setPortSymbolAttr(portIdx, attr);
827 return sym;
828 }
829 } else {
830 // InnerSymbols only supported if op implements the interface.
831 if (auto symOp = dyn_cast<hw::InnerSymbolOpInterface>(target.getOp())) {
832 auto [attr, sym] =
833 getOrAddInnerSym(symOp.getContext(), symOp.getInnerSymAttr(),
834 target.getField(), getNamespace);
835 symOp.setInnerSymbolAttr(attr);
836 return sym;
837 }
838 }
839
840 assert(0 && "target must be port of FModuleOp or InnerSymbol");
841 return {};
842}
843
845 GetNamespaceCallback getNamespace) {
846 FModuleLike module;
847 if (target.isPort())
848 module = cast<FModuleLike>(target.getOp());
849 else
850 module = target.getOp()->getParentOfType<FModuleOp>();
851 assert(module);
852
853 return getOrAddInnerSym(target, [&]() -> hw::InnerSymbolNamespace & {
854 return getNamespace(module);
855 });
856}
857
858/// Obtain an inner reference to an operation, possibly adding an `inner_sym`
859/// to that operation.
860hw::InnerRefAttr
862 GetNamespaceCallback getNamespace) {
863 auto mod = target.isPort() ? dyn_cast<FModuleOp>(target.getOp())
864 : target.getOp()->getParentOfType<FModuleOp>();
865 assert(mod &&
866 "must be an operation inside an FModuleOp or port of FModuleOp");
867 return hw::InnerRefAttr::get(SymbolTable::getSymbolName(mod),
868 getOrAddInnerSym(target, getNamespace));
869}
870
871/// Parse a string that may encode a FIRRTL location into a LocationAttr.
872std::pair<bool, std::optional<mlir::LocationAttr>>
873circt::firrtl::maybeStringToLocation(StringRef spelling, bool skipParsing,
874 StringAttr &locatorFilenameCache,
875 FileLineColLoc &fileLineColLocCache,
876 MLIRContext *context) {
877 // The spelling of the token looks something like "@[Decoupled.scala 221:8]".
878 if (!spelling.starts_with("@[") || !spelling.ends_with("]"))
879 return {false, std::nullopt};
880
881 spelling = spelling.drop_front(2).drop_back(1);
882
883 // Decode the locator in "spelling", returning the filename and filling in
884 // lineNo and colNo on success. On failure, this returns an empty filename.
885 auto decodeLocator = [&](StringRef input, unsigned &resultLineNo,
886 unsigned &resultColNo) -> StringRef {
887 // Split at the last space.
888 auto spaceLoc = input.find_last_of(' ');
889 if (spaceLoc == StringRef::npos)
890 return {};
891
892 auto filename = input.take_front(spaceLoc);
893 auto lineAndColumn = input.drop_front(spaceLoc + 1);
894
895 // Decode the line/column. If the colon is missing, then it will be empty
896 // here.
897 StringRef lineStr, colStr;
898 std::tie(lineStr, colStr) = lineAndColumn.split(':');
899
900 // Decode the line number and the column number if present.
901 if (lineStr.getAsInteger(10, resultLineNo))
902 return {};
903 if (!colStr.empty()) {
904 if (colStr.front() != '{') {
905 if (colStr.getAsInteger(10, resultColNo))
906 return {};
907 } else {
908 // compound locator, just parse the first part for now
909 if (colStr.drop_front().split(',').first.getAsInteger(10, resultColNo))
910 return {};
911 }
912 }
913 return filename;
914 };
915
916 // Decode the locator spelling, reporting an error if it is malformed.
917 unsigned lineNo = 0, columnNo = 0;
918 StringRef filename = decodeLocator(spelling, lineNo, columnNo);
919 if (filename.empty())
920 return {false, std::nullopt};
921
922 // If info locators are ignored, don't actually apply them. We still do all
923 // the verification above though.
924 if (skipParsing)
925 return {true, std::nullopt};
926
927 /// Return an FileLineColLoc for the specified location, but use a bit of
928 /// caching to reduce thrasing the MLIRContext.
929 auto getFileLineColLoc = [&](StringRef filename, unsigned lineNo,
930 unsigned columnNo) -> FileLineColLoc {
931 // Check our single-entry cache for this filename.
932 StringAttr filenameId = locatorFilenameCache;
933 if (filenameId.str() != filename) {
934 // We missed! Get the right identifier.
935 locatorFilenameCache = filenameId = StringAttr::get(context, filename);
936
937 // If we miss in the filename cache, we also miss in the FileLineColLoc
938 // cache.
939 return fileLineColLocCache =
940 FileLineColLoc::get(filenameId, lineNo, columnNo);
941 }
942
943 // If we hit the filename cache, check the FileLineColLoc cache.
944 auto result = fileLineColLocCache;
945 if (result && result.getLine() == lineNo && result.getColumn() == columnNo)
946 return result;
947
948 return fileLineColLocCache =
949 FileLineColLoc::get(filenameId, lineNo, columnNo);
950 };
951
952 // Compound locators will be combined with spaces, like:
953 // @[Foo.scala 123:4 Bar.scala 309:14]
954 // and at this point will be parsed as a-long-string-with-two-spaces at
955 // 309:14. We'd like to parse this into two things and represent it as an
956 // MLIR fused locator, but we want to be conservatively safe for filenames
957 // that have a space in it. As such, we are careful to make sure we can
958 // decode the filename/loc of the result. If so, we accumulate results,
959 // backward, in this vector.
960 SmallVector<Location> extraLocs;
961 auto spaceLoc = filename.find_last_of(' ');
962 while (spaceLoc != StringRef::npos) {
963 // Try decoding the thing before the space. Validates that there is another
964 // space and that the file/line can be decoded in that substring.
965 unsigned nextLineNo = 0, nextColumnNo = 0;
966 auto nextFilename =
967 decodeLocator(filename.take_front(spaceLoc), nextLineNo, nextColumnNo);
968
969 // On failure we didn't have a joined locator.
970 if (nextFilename.empty())
971 break;
972
973 // On success, remember what we already parsed (Bar.Scala / 309:14), and
974 // move on to the next chunk.
975 auto loc =
976 getFileLineColLoc(filename.drop_front(spaceLoc + 1), lineNo, columnNo);
977 extraLocs.push_back(loc);
978 filename = nextFilename;
979 lineNo = nextLineNo;
980 columnNo = nextColumnNo;
981 spaceLoc = filename.find_last_of(' ');
982 }
983
984 mlir::LocationAttr result = getFileLineColLoc(filename, lineNo, columnNo);
985 if (!extraLocs.empty()) {
986 extraLocs.push_back(result);
987 std::reverse(extraLocs.begin(), extraLocs.end());
988 result = FusedLoc::get(context, extraLocs);
989 }
990 return {true, result};
991}
992
993/// Given a type, return the corresponding lowered type for the HW dialect.
994/// Non-FIRRTL types are simply passed through. This returns a null type if it
995/// cannot be lowered.
997 Type type, std::optional<Location> loc,
998 llvm::function_ref<hw::TypeAliasType(Type, BaseTypeAliasType, Location)>
999 getTypeDeclFn) {
1000 auto firType = type_dyn_cast<FIRRTLBaseType>(type);
1001 if (!firType)
1002 return type;
1003
1004 // If not known how to lower alias types, then ignore the alias.
1005 if (getTypeDeclFn)
1006 if (BaseTypeAliasType aliasType = dyn_cast<BaseTypeAliasType>(firType)) {
1007 if (!loc)
1008 loc = UnknownLoc::get(type.getContext());
1009 type = lowerType(aliasType.getInnerType(), loc, getTypeDeclFn);
1010 return getTypeDeclFn(type, aliasType, *loc);
1011 }
1012 // Ignore flip types.
1013 firType = firType.getPassiveType();
1014
1015 if (auto bundle = type_dyn_cast<BundleType>(firType)) {
1016 mlir::SmallVector<hw::StructType::FieldInfo, 8> hwfields;
1017 for (auto element : bundle) {
1018 Type etype = lowerType(element.type, loc, getTypeDeclFn);
1019 if (!etype)
1020 return {};
1021 hwfields.push_back(hw::StructType::FieldInfo{element.name, etype});
1022 }
1023 return hw::StructType::get(type.getContext(), hwfields);
1024 }
1025 if (auto vec = type_dyn_cast<FVectorType>(firType)) {
1026 auto elemTy = lowerType(vec.getElementType(), loc, getTypeDeclFn);
1027 if (!elemTy)
1028 return {};
1029 return hw::ArrayType::get(elemTy, vec.getNumElements());
1030 }
1031 if (auto fenum = type_dyn_cast<FEnumType>(firType)) {
1032 mlir::SmallVector<hw::UnionType::FieldInfo, 8> hwfields;
1033 SmallVector<Attribute> names;
1034 bool simple = true;
1035 for (auto element : fenum) {
1036 Type etype = lowerType(element.type, loc, getTypeDeclFn);
1037 if (!etype)
1038 return {};
1039 hwfields.push_back(hw::UnionType::FieldInfo{element.name, etype, 0});
1040 names.push_back(element.name);
1041 if (!isa<UIntType>(element.type) ||
1042 element.type.getBitWidthOrSentinel() != 0)
1043 simple = false;
1044 }
1045 auto tagTy = hw::EnumType::get(type.getContext(),
1046 ArrayAttr::get(type.getContext(), names));
1047 if (simple)
1048 return tagTy;
1049 auto bodyTy = hw::UnionType::get(type.getContext(), hwfields);
1050 hw::StructType::FieldInfo fields[2] = {
1051 {StringAttr::get(type.getContext(), "tag"), tagTy},
1052 {StringAttr::get(type.getContext(), "body"), bodyTy}};
1053 return hw::StructType::get(type.getContext(), fields);
1054 }
1055 if (type_isa<ClockType>(firType))
1056 return seq::ClockType::get(firType.getContext());
1057
1058 auto width = firType.getBitWidthOrSentinel();
1059 if (width >= 0) // IntType, analog with known width, clock, etc.
1060 return IntegerType::get(type.getContext(), width);
1061
1062 return {};
1063}
1064
1065void circt::firrtl::makeCommonPrefix(SmallString<64> &a, StringRef b) {
1066 // truncate 'a' to the common prefix of 'a' and 'b'.
1067 size_t i = 0;
1068 size_t e = std::min(a.size(), b.size());
1069 for (; i < e; ++i)
1070 if (a[i] != b[i])
1071 break;
1072 a.resize(i);
1073
1074 // truncate 'a' so it ends on a directory seperator.
1075 auto sep = llvm::sys::path::get_separator();
1076 while (!a.empty() && !a.ends_with(sep))
1077 a.pop_back();
1078}
1079
1080PathOp circt::firrtl::createPathRef(Operation *op, hw::HierPathOp nla,
1081 mlir::ImplicitLocOpBuilder &builderOM) {
1082
1083 auto *context = op->getContext();
1084 auto id = DistinctAttr::create(UnitAttr::get(context));
1085 TargetKind kind = TargetKind::Reference;
1086 // If op is null, then create an empty path.
1087 if (op) {
1088 NamedAttrList fields;
1089 fields.append("id", id);
1090 fields.append("class", StringAttr::get(context, "circt.tracker"));
1091 if (nla)
1092 fields.append("circt.nonlocal", mlir::FlatSymbolRefAttr::get(nla));
1093 AnnotationSet annos(op);
1094 annos.addAnnotations(DictionaryAttr::get(context, fields));
1095 annos.applyToOperation(op);
1096 if (isa<InstanceOp, FModuleLike>(op))
1097 kind = TargetKind::Instance;
1098 }
1099
1100 // Create the path operation.
1101 return builderOM.create<PathOp>(kind, id);
1102}
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()
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)
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:80
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.