CIRCT 24.0.0git
Loading...
Searching...
No Matches
ProjectionUtils.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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#include "ProjectionUtils.h"
12#include "mlir/IR/Matchers.h"
13#include <cstdint>
14
15using namespace mlir;
16using namespace circt;
17using namespace llhd;
18
19/// Check if an op is one of the supported projection types.
20bool llhd::isProjection(const Operation *op) {
21 return isa_and_nonnull<SigArrayGetOp, SigExtractOp, SigStructExtractOp>(op);
22}
23
24/// Calculate a projection's range.
25/// Returns nullopt if the range could not be calculated for this projection.
26/// Asserts if the provided Operation is not a (supported) projection.
27std::optional<ProjectionRange> llhd::getProjectionRange(Operation *op) {
28 APInt index;
29 uint64_t offset = 0;
30 int64_t width = 0;
31 Value dynIndex;
32
33 TypeSwitch<Operation *>(op)
34 .Case<SigArrayGetOp>([&](auto op) {
35 if (!matchPattern(op.getIndex(), m_ConstantInt(&index))) {
36 dynIndex = op.getIndex();
37 } else {
38 width = hw::getBitWidth(getStoredType(op.getResult()));
39 offset = index.getZExtValue() * width;
40 }
41 })
42 .Case<SigExtractOp>([&](auto op) {
43 if (!matchPattern(op.getLowBit(), m_ConstantInt(&index))) {
44 dynIndex = op.getLowBit();
45 } else {
46 width = op.getResultWidth();
47 offset = index.getZExtValue();
48 }
49 })
50 .Case<SigStructExtractOp>([&](auto op) {
51 auto inputType = cast<RefType>(op.getInput().getType()).getNestedType();
52 if (auto unionType = dyn_cast<hw::UnionType>(inputType)) {
53 width = hw::getBitWidth(unionType);
54 } else {
55 auto structType = cast<hw::StructType>(inputType);
56 auto elements = structType.getElements();
57 auto fieldIdx = *structType.getFieldIndex(op.getFieldAttr());
58 bool fieldWidthsKnown = true;
59 for (auto field : elements.drop_front(fieldIdx + 1)) {
60 auto fieldWidth = hw::getBitWidth(field.type);
61 if (fieldWidth < 0) {
62 fieldWidthsKnown = false;
63 break;
64 }
65 offset += fieldWidth;
66 }
67 width =
68 fieldWidthsKnown ? hw::getBitWidth(elements[fieldIdx].type) : -1;
69 }
70 })
71 .Default([](auto) { llvm_unreachable("Unsupported projection type."); });
72
73 if (dynIndex)
74 return DynProjectionRange{dynIndex};
75
76 if (width < 0)
77 return std::nullopt;
78
79 return ConstProjectionRange{offset, static_cast<uint64_t>(width)};
80}
81
82/// Check whether two projections provably describe disjoint storage.
84 const ProjectionRange &b) {
85 auto *ca = std::get_if<ConstProjectionRange>(&a);
86 auto *cb = std::get_if<ConstProjectionRange>(&b);
87 // At least one side is dynamic: can't prove disjointness.
88 if (!ca || !cb)
89 return false;
90 // The ranges don't overlap: disjoint.
91 if (ca->offset + ca->width <= cb->offset ||
92 cb->offset + cb->width <= ca->offset)
93 return true;
94 return false;
95}
96
97Operation *llhd::getRootSignal(Value sig) {
98 if (!sig)
99 return nullptr;
100 Operation *op = sig.getDefiningOp();
101
102 while (isProjection(op)) {
103 op = op->getOperand(0).getDefiningOp();
104 if (!op)
105 return nullptr;
106 }
107 if (isa_and_nonnull<SignalOp>(op))
108 return op;
109 return nullptr;
110}
111
112std::pair<Operation *, std::optional<ProjectionRange>>
114 if (!sig)
115 return {};
116
117 // Initialize the range parameters.
118 bool isConst = true;
119 uint64_t offset = 0;
120 auto width = hw::getBitWidth(getStoredType(sig));
121 if (width < 0)
122 isConst = false;
123
124 // Walk up the projection chain.
125 Operation *op = sig.getDefiningOp();
126 while (isProjection(op)) {
127 auto projRange = getProjectionRange(op);
128 // Update the range only if all the encountered projections are constant.
129 if (isConst && projRange && !isDynamic(*projRange)) {
130 auto constRange = std::get<ConstProjectionRange>(*projRange);
131 offset += constRange.offset;
132 } else {
133 isConst = false;
134 }
135 op = op->getOperand(0).getDefiningOp();
136 }
137
138 // If the chain doesn't bottom out in a signal there's not value nor range
139 // to return.
140 if (!isa_and_nonnull<SignalOp>(op))
141 return {};
142
143 if (!isConst)
144 return {op, std::nullopt};
145
146 return {op, ConstProjectionRange{offset, static_cast<uint64_t>(width)}};
147}
int64_t getBitWidth(mlir::Type type)
Return the hardware bit width of a type.
Definition HWTypes.cpp:122
std::pair< Operation *, std::optional< ProjectionRange > > getRootSignalAndRange(Value sig)
Given a signal or a projection, find the root signal declaration by traversing the projection chain.
std::optional< ProjectionRange > getProjectionRange(Operation *op)
Calculate a projection's range.
std::variant< ConstProjectionRange, DynProjectionRange > ProjectionRange
Portion of the signal operand that is selected by a projection.
Type getStoredType(Value slot)
Return the type stored in a signal or projection.
Operation * getRootSignal(Value sig)
Given a signal or a projection, walk back the projection chain until you find the root signal declara...
bool isProjection(const Operation *op)
Check if an op is one of the supported projection types.
bool isDynamic(const ProjectionRange &range)
Helper to check if a projection range is dynamic or not.
bool isProvablyDisjoint(const ProjectionRange &a, const ProjectionRange &b)
Check whether two projections provably describe disjoint storage.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Portion of a signal selected by a projection, with a statically known offset and width in bits.
Portion of a signal selected by a projection with a dynamic index.