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