CIRCT 24.0.0git
Loading...
Searching...
No Matches
HWConvertBitcasts.cpp
Go to the documentation of this file.
1//===- ConvertBitcasts.cpp ------------------------------------------------===//
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
12#include "mlir/IR/AttrTypeSubElements.h"
13#include "mlir/IR/Iterators.h"
14#include "mlir/Pass/Pass.h"
15
16#define DEBUG_TYPE "hw-convert-bitcasts"
17
18namespace circt {
19namespace hw {
20#define GEN_PASS_DEF_HWCONVERTBITCASTS
21#include "circt/Dialect/HW/Passes.h.inc"
22} // namespace hw
23} // namespace circt
24
25using namespace mlir;
26using namespace circt;
27using namespace hw;
28
29namespace {
30struct HWConvertBitcastsPass
31 : circt::hw::impl::HWConvertBitcastsBase<HWConvertBitcastsPass> {
32 using circt::hw::impl::HWConvertBitcastsBase<
33 HWConvertBitcastsPass>::HWConvertBitcastsBase;
34
35 void runOnOperation() override;
36 static bool isTypeSupported(Type ty);
37 LogicalResult convertBitcastOp(OpBuilder builder, BitcastOp bitcastOp);
38};
39} // namespace
40
41// Array conversion: Lower bits correspond to lower array index
42// Struct conversion: Higher bits correspond to lower field index
43
44// NOLINTNEXTLINE(misc-no-recursion)
45bool HWConvertBitcastsPass::isTypeSupported(Type ty) {
46 if (isa<IntegerType>(ty))
47 return true;
48 if (auto arrayTy = hw::type_dyn_cast<hw::ArrayType>(ty))
49 return isTypeSupported(arrayTy.getElementType());
50 if (auto structTy = hw::type_dyn_cast<hw::StructType>(ty))
51 return llvm::all_of(structTy.getElements(),
52 [](StructType::FieldInfo field) {
53 return isTypeSupported(field.type);
54 });
55 // TODO: Add support for: union, packed array, enum
56 return false;
57}
58
59// Collect integer fields of aggregates for concatenation
60// NOLINTNEXTLINE(misc-no-recursion)
61static void collectIntegersRecursively(OpBuilder builder, Location loc,
62 Value inputVal,
63 SmallVectorImpl<Value> &accumulator) {
64 // If input is defined by BitcastOp, unwrap it:
65 if (auto bitcast = inputVal.getDefiningOp<BitcastOp>();
66 bitcast &&
67 HWConvertBitcastsPass::isTypeSupported(bitcast.getInput().getType())) {
68 collectIntegersRecursively(builder, loc, bitcast.getInput(), accumulator);
69 return;
70 }
71
72 // If input is defined by ConcatOp, unwrap its operands:
73 if (auto concat = inputVal.getDefiningOp<comb::ConcatOp>()) {
74 for (Value op : concat.getInputs())
75 collectIntegersRecursively(builder, loc, op, accumulator);
76 return;
77 }
78
79 // End of recursion: Integer value
80 if (isa<IntegerType>(inputVal.getType())) {
81 accumulator.push_back(inputVal);
82 return;
83 }
84
85 auto numBits = getBitWidth(inputVal.getType());
86 assert(numBits >= 0 && "Bitwidth of input must be known");
87
88 // Array Type
89 if (auto arrayTy = dyn_cast<ArrayType>(inputVal.getType())) {
90 unsigned numElements = arrayTy.getNumElements();
91 // Avoid creating zero-width indices to dodge lowering issues
92 auto indexType =
93 builder.getIntegerType(std::max(1u, llvm::Log2_64_Ceil(numElements)));
94 for (unsigned i = 0; i < numElements; ++i) {
95 // Process from high to low array index
96 auto indexCst = ConstantOp::create(
97 builder, loc, builder.getIntegerAttr(indexType, numElements - i - 1));
98 auto getOp = ArrayGetOp::create(builder, loc, inputVal, indexCst);
99 collectIntegersRecursively(builder, loc, getOp.getResult(), accumulator);
100 }
101 return;
102 }
103
104 // Struct Type
105 if (auto structTy = dyn_cast<StructType>(inputVal.getType())) {
106 // Process from low to high field index
107 auto explodeOp = StructExplodeOp::create(builder, loc, inputVal);
108 for (auto elt : explodeOp.getResults())
109 collectIntegersRecursively(builder, loc, elt, accumulator);
110 return;
111 }
112
113 assert(false && "Unsupported type");
114}
115
116// Convert collected integer chunks (MSB to LSB) to aggregate
117// NOLINTNEXTLINE(misc-no-recursion)
118static Value constructAggregateRecursively(OpBuilder builder, Location loc,
119 ArrayRef<Value> integers,
120 size_t &idx, size_t &bitOffset,
121 Type targetType) {
122 assert(getBitWidth(targetType) >= 0 && "Bitwidth of target must be known");
123
124 // End of recursion: Integer value
125 if (auto intTy = dyn_cast<IntegerType>(targetType)) {
126 size_t remaining = intTy.getWidth();
127 if (remaining == 0)
128 return ConstantOp::create(builder, loc, builder.getIntegerAttr(intTy, 0));
129
130 SmallVector<Value> pieces;
131 while (remaining > 0) {
132 assert(idx < integers.size() && "Not enough bits in input");
133 Value cur = integers[idx];
134 size_t curWidth = cur.getType().getIntOrFloatBitWidth();
135 if (curWidth == 0) {
136 ++idx;
137 continue;
138 }
139 size_t take = std::min(remaining, curWidth - bitOffset);
140 if (bitOffset == 0 && take == curWidth) {
141 pieces.push_back(cur);
142 } else {
143 auto sliceTy = builder.getIntegerType(take);
144 pieces.push_back(comb::ExtractOp::create(builder, loc, sliceTy, cur,
145 curWidth - bitOffset - take));
146 }
147 remaining -= take;
148 if ((bitOffset += take) == curWidth) {
149 ++idx;
150 bitOffset = 0;
151 }
152 }
153 if (pieces.size() == 1)
154 return pieces.front();
155 return comb::ConcatOp::create(builder, loc, pieces);
156 }
157
158 SmallVector<Value> elements;
159
160 // Array Type
161 if (auto arrayTy = type_dyn_cast<ArrayType>(targetType)) {
162 auto numElements = arrayTy.getNumElements();
163 elements.reserve(numElements);
164 for (unsigned i = 0; i < numElements; ++i) {
165 // Process bits from MSB to LSB
166 elements.push_back(constructAggregateRecursively(
167 builder, loc, integers, idx, bitOffset, arrayTy.getElementType()));
168 }
169 // Array create reverses order:
170 // Higher bits are added first and become high indices.
171 return ArrayCreateOp::create(builder, loc, targetType, elements);
172 }
173
174 // Struct Type
175 if (auto structTy = type_dyn_cast<StructType>(targetType)) {
176 auto fields = structTy.getElements();
177 elements.reserve(fields.size());
178 for (const auto &field : fields) {
179 // Process bits from MSB to LSB
180 elements.push_back(constructAggregateRecursively(
181 builder, loc, integers, idx, bitOffset, field.type));
182 }
183 // Struct create does not reverse order:
184 // Higher bits are added first and become low indices.
185 return StructCreateOp::create(builder, loc, targetType, elements);
186 }
187
188 assert(false && "Unsupported type");
189 return {};
190}
191
192LogicalResult HWConvertBitcastsPass::convertBitcastOp(OpBuilder builder,
193 BitcastOp bitcastOp) {
194 bool inputSupported = isTypeSupported(bitcastOp.getInput().getType());
195 bool outputSupported = isTypeSupported(bitcastOp.getType());
196 if (!allowPartialConversion) {
197 if (!inputSupported)
198 bitcastOp.emitOpError("has unsupported input type");
199 if (!outputSupported)
200 bitcastOp.emitOpError("has unsupported output type");
201 }
202 if (!(inputSupported && outputSupported))
203 return failure();
204
205 builder.setInsertionPoint(bitcastOp);
206
207 // Collect integer chunks from input value (MSB to LSB)
208 SmallVector<Value> integers;
209 collectIntegersRecursively(builder, bitcastOp.getLoc(), bitcastOp.getInput(),
210 integers);
211
212 // Convert collected integer chunks to the target type
213 size_t idx = 0, bitOffset = 0;
214 Value result =
215 constructAggregateRecursively(builder, bitcastOp.getLoc(), integers, idx,
216 bitOffset, bitcastOp.getType());
217
218 // Replace operation
219 bitcastOp.getResult().replaceAllUsesWith(result);
220 bitcastOp.erase();
221 return success();
222}
223
224void HWConvertBitcastsPass::runOnOperation() {
225 OpBuilder builder(getOperation());
226 bool anyFailed = false;
227 bool anyChanged = false;
228 getOperation().getBody()->walk<WalkOrder::PostOrder, ReverseIterator>(
229 [&](BitcastOp bitcastOp) {
230 anyChanged = true;
231 if (failed(convertBitcastOp(builder, bitcastOp)))
232 anyFailed = true;
233 });
234
235 if (!anyChanged) {
236 markAllAnalysesPreserved();
237 return;
238 }
239
240 if (anyFailed && !allowPartialConversion)
241 signalPassFailure();
242}
assert(baseType &&"element must be base type")
MlirType uint64_t numElements
Definition CHIRRTL.cpp:30
static SmallVector< T > concat(const SmallVectorImpl< T > &a, const SmallVectorImpl< T > &b)
Returns a new vector containing the concatenation of vectors a and b.
Definition CalyxOps.cpp:555
static void collectIntegersRecursively(OpBuilder builder, Location loc, Value inputVal, SmallVectorImpl< Value > &accumulator)
static Value constructAggregateRecursively(OpBuilder builder, Location loc, ArrayRef< Value > integers, size_t &idx, size_t &bitOffset, Type targetType)
false return failure()
Parse an optional keyword or string and set instance into 'result'.
return success()
create(low_bit, result_type, input=None)
Definition comb.py:187
create(elements, Type result_type=None)
Definition hw.py:486
create(array_value, idx)
Definition hw.py:453
create(data_type, value)
Definition hw.py:433
create(elements, Type result_type=None)
Definition hw.py:547
BaseTy type_dyn_cast(Type type)
Definition HWTypes.h:234
int64_t getBitWidth(mlir::Type type)
Return the hardware bit width of a type.
Definition HWTypes.cpp:122
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition hw.py:1