CIRCT 24.0.0git
Loading...
Searching...
No Matches
HWAggregateToComb.cpp
Go to the documentation of this file.
1//===- HWAggregateToComb.cpp - HW aggregate to comb -------------*- 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
12#include "mlir/Pass/Pass.h"
13#include "mlir/Transforms/DialectConversion.h"
14#include "llvm/ADT/APInt.h"
15
16namespace circt {
17namespace hw {
18#define GEN_PASS_DEF_HWAGGREGATETOCOMB
19#include "circt/Dialect/HW/Passes.h.inc"
20} // namespace hw
21} // namespace circt
22
23using namespace mlir;
24using namespace circt;
25
26namespace {
27
28// Lower hw.array_create and hw.array_concat to comb.concat.
29template <typename OpTy>
30struct HWArrayCreateLikeOpConversion : OpConversionPattern<OpTy> {
32 using OpAdaptor = typename OpConversionPattern<OpTy>::OpAdaptor;
33 LogicalResult
34 matchAndRewrite(OpTy op, OpAdaptor adaptor,
35 ConversionPatternRewriter &rewriter) const override {
36 rewriter.replaceOpWithNewOp<comb::ConcatOp>(op, adaptor.getInputs());
37 return success();
38 }
39};
40
41struct HWAggregateConstantOpConversion
42 : OpConversionPattern<hw::AggregateConstantOp> {
43 using OpConversionPattern<hw::AggregateConstantOp>::OpConversionPattern;
44
45 LogicalResult
46 matchAndRewrite(hw::AggregateConstantOp op, OpAdaptor adaptor,
47 ConversionPatternRewriter &rewriter) const override {
48 // Lower to concat.
49 APInt intVal;
50 if (failed(hw::aggregateAttrToAPInt(op.getType(), adaptor.getFieldsAttr(),
51 intVal)))
52 return failure();
53 rewriter.replaceOpWithNewOp<hw::ConstantOp>(op, intVal);
54 return success();
55 }
56};
57
58struct HWArrayGetOpConversion : OpConversionPattern<hw::ArrayGetOp> {
60
61 LogicalResult
62 matchAndRewrite(hw::ArrayGetOp op, OpAdaptor adaptor,
63 ConversionPatternRewriter &rewriter) const override {
64 SmallVector<Value> results;
65 auto arrayType = cast<hw::ArrayType>(op.getInput().getType());
66 auto elemType = arrayType.getElementType();
67 auto numElements = arrayType.getNumElements();
68 auto elemWidth = hw::getBitWidth(elemType);
69 if (elemWidth < 0)
70 return rewriter.notifyMatchFailure(op.getLoc(), "unknown element width");
71
72 auto lowered = adaptor.getInput();
73 auto index = adaptor.getIndex();
74 APInt constantIndex;
75 if (matchPattern(index, m_ConstantInt(&constantIndex))) {
76 int64_t maxIndex = std::numeric_limits<int32_t>::max() / elemWidth;
77 if (constantIndex.isSingleWord() &&
78 constantIndex.getZExtValue() <= static_cast<uint64_t>(maxIndex)) {
79 rewriter.replaceOpWithNewOp<comb::ExtractOp>(
80 op, lowered, constantIndex.getZExtValue() * elemWidth, elemWidth);
81 return success();
82 }
83 }
84
85 for (size_t i = 0; i < numElements; ++i)
86 results.push_back(rewriter.createOrFold<comb::ExtractOp>(
87 op.getLoc(), lowered, i * elemWidth, elemWidth));
88
89 SmallVector<Value> bits;
90 comb::extractBits(rewriter, index, bits);
91 auto result = comb::constructMuxTree(rewriter, op.getLoc(), bits, results,
92 results.back());
93
94 rewriter.replaceOp(op, result);
95 return success();
96 }
97};
98
99struct HWArraySliceOpConversion : OpConversionPattern<hw::ArraySliceOp> {
101
102 LogicalResult
103 matchAndRewrite(hw::ArraySliceOp op, OpAdaptor adaptor,
104 ConversionPatternRewriter &rewriter) const override {
105 SmallVector<Value> results;
106 auto arrayType = cast<hw::ArrayType>(op.getInput().getType());
107 auto elemType = arrayType.getElementType();
108 auto numElements = arrayType.getNumElements();
109 auto elemWidth = hw::getBitWidth(elemType);
110 if (elemWidth < 0)
111 return rewriter.notifyMatchFailure(op.getLoc(), "unknown element width");
112 auto resultArrayType = cast<hw::ArrayType>(op.getResult().getType());
113 auto resultNumElements = resultArrayType.getNumElements();
114
115 auto lowered = adaptor.getInput();
116 auto index = adaptor.getLowIndex();
117 APInt constantIndex;
118 if (matchPattern(index, m_ConstantInt(&constantIndex))) {
119 int64_t maxIndex = std::numeric_limits<int32_t>::max() / elemWidth;
120 if (constantIndex.isSingleWord() &&
121 constantIndex.getZExtValue() <= static_cast<uint64_t>(maxIndex)) {
122 rewriter.replaceOpWithNewOp<comb::ExtractOp>(
123 op, lowered, constantIndex.getZExtValue() * elemWidth,
124 resultNumElements * elemWidth);
125 return success();
126 }
127 }
128
129 for (size_t i = 0; i <= numElements - resultNumElements; ++i)
130 results.push_back(rewriter.createOrFold<comb::ExtractOp>(
131 op.getLoc(), lowered, i * elemWidth, resultNumElements * elemWidth));
132
133 SmallVector<Value> bits;
134 comb::extractBits(rewriter, index, bits);
135 auto result = comb::constructMuxTree(rewriter, op.getLoc(), bits, results,
136 results.back());
137
138 rewriter.replaceOp(op, result);
139 return success();
140 }
141};
142
143struct HWArrayInjectOpConversion : OpConversionPattern<hw::ArrayInjectOp> {
144 using OpConversionPattern<hw::ArrayInjectOp>::OpConversionPattern;
145
146 LogicalResult
147 matchAndRewrite(hw::ArrayInjectOp op, OpAdaptor adaptor,
148 ConversionPatternRewriter &rewriter) const override {
149 auto arrayType = cast<hw::ArrayType>(op.getInput().getType());
150 auto elemType = arrayType.getElementType();
151 auto numElements = arrayType.getNumElements();
152 auto elemWidth = hw::getBitWidth(elemType);
153 if (elemWidth < 0)
154 return rewriter.notifyMatchFailure(op.getLoc(), "unknown element width");
155
156 Location loc = op.getLoc();
157
158 // Extract all elements from the input array
159 SmallVector<Value> originalElements;
160 auto inputArray = adaptor.getInput();
161 for (size_t i = 0; i < numElements; ++i) {
162 originalElements.push_back(rewriter.createOrFold<comb::ExtractOp>(
163 loc, inputArray, i * elemWidth, elemWidth));
164 }
165
166 // Create 2D array: each row represents what the array would look like
167 // if injection happened at that specific index
168 SmallVector<Value> arrayRows;
169 arrayRows.reserve(numElements);
170 for (int injectIdx = numElements - 1; injectIdx >= 0; --injectIdx) {
171 SmallVector<Value> rowElements;
172 rowElements.reserve(numElements);
173
174 // Build the row: array[n-1], array[n-2], ..., but replace element at
175 // injectIdx with newVal
176 for (int originalIdx = numElements - 1; originalIdx >= 0; --originalIdx) {
177 if (originalIdx == injectIdx) {
178 rowElements.push_back(adaptor.getElement());
179 } else {
180 rowElements.push_back(originalElements[originalIdx]);
181 }
182 }
183
184 // Concatenate elements to form this row
185 Value row = hw::ArrayCreateOp::create(rewriter, loc, rowElements);
186 arrayRows.push_back(row);
187 }
188
189 // Create the 2D array by concatenating all rows
190 // arrayRows[0] corresponds to injection at index 0
191 // arrayRows[1] corresponds to injection at index 1, etc.
192 Value array2D = hw::ArrayCreateOp::create(rewriter, loc, arrayRows);
193
194 // Create array_get operation to select the row
195 auto arrayGetOp =
196 hw::ArrayGetOp::create(rewriter, loc, array2D, adaptor.getIndex());
197
198 rewriter.replaceOp(op, arrayGetOp);
199 return success();
200 }
201};
202
203struct HWStructCreateOpConversion : OpConversionPattern<hw::StructCreateOp> {
205
206 LogicalResult
207 matchAndRewrite(hw::StructCreateOp op, OpAdaptor adaptor,
208 ConversionPatternRewriter &rewriter) const override {
209 // Lower struct_create to comb.concat. The first field occupies the MSBs, so
210 // we concatenate fields in order (comb.concat places first operand at MSB).
211 rewriter.replaceOpWithNewOp<comb::ConcatOp>(op, adaptor.getInput());
212 return success();
213 }
214};
215
216struct HWStructExtractOpConversion : OpConversionPattern<hw::StructExtractOp> {
218
219 LogicalResult
220 matchAndRewrite(hw::StructExtractOp op, OpAdaptor adaptor,
221 ConversionPatternRewriter &rewriter) const override {
222 auto structType = cast<hw::StructType>(op.getInput().getType());
223 auto fieldIndex = op.getFieldIndex();
224 auto elements = structType.getElements();
225
226 int64_t totalBitWidth = hw::getBitWidth(structType);
227 if (totalBitWidth < 0)
228 return rewriter.notifyMatchFailure(op.getLoc(), "unknown struct width");
229
230 // Compute the bit offset from the MSB by summing the widths of all
231 // preceding fields. The first field occupies the MSBs.
232 int64_t consumedBits = 0;
233 for (size_t i = 0; i < fieldIndex; ++i) {
234 int64_t fieldWidth = hw::getBitWidth(elements[i].type);
235 assert(fieldWidth >= 0 &&
236 "must be failed before if field width is unknown");
237 consumedBits += fieldWidth;
238 }
239
240 int64_t fieldWidth = hw::getBitWidth(elements[fieldIndex].type);
241 assert(fieldWidth >= 0 &&
242 "must be failed before if field width is unknown");
243
244 // Extract the field using comb.extract. Offset is from LSB.
245 int64_t bitOffset = totalBitWidth - consumedBits - fieldWidth;
246 rewriter.replaceOpWithNewOp<comb::ExtractOp>(op, adaptor.getInput(),
247 bitOffset, fieldWidth);
248 return success();
249 }
250};
251
252struct MuxOpConversion : OpConversionPattern<comb::MuxOp> {
254
255 LogicalResult
256 matchAndRewrite(comb::MuxOp op, OpAdaptor adaptor,
257 ConversionPatternRewriter &rewriter) const override {
258 // Re-create Mux with legalized types.
259 rewriter.replaceOpWithNewOp<comb::MuxOp>(
260 op, adaptor.getCond(), adaptor.getTrueValue(), adaptor.getFalseValue());
261 return success();
262 }
263};
264
265/// A type converter is needed to perform the in-flight materialization of
266/// aggregate types to integer types.
267class AggregateTypeConverter : public TypeConverter {
268public:
269 AggregateTypeConverter() {
270 addConversion([](Type type) -> Type { return type; });
271 addConversion([](hw::ArrayType t) -> Type {
272 return IntegerType::get(t.getContext(), hw::getBitWidth(t));
273 });
274 addConversion([](hw::StructType t) -> Type {
275 return IntegerType::get(t.getContext(), hw::getBitWidth(t));
276 });
277 addTargetMaterialization([](mlir::OpBuilder &builder, mlir::Type resultType,
278 mlir::ValueRange inputs,
279 mlir::Location loc) -> mlir::Value {
280 if (inputs.size() != 1)
281 return Value();
282
283 return hw::BitcastOp::create(builder, loc, resultType, inputs[0])
284 ->getResult(0);
285 });
286
287 addSourceMaterialization([](mlir::OpBuilder &builder, mlir::Type resultType,
288 mlir::ValueRange inputs,
289 mlir::Location loc) -> mlir::Value {
290 if (inputs.size() != 1)
291 return Value();
292
293 return hw::BitcastOp::create(builder, loc, resultType, inputs[0])
294 ->getResult(0);
295 });
296 }
297};
298} // namespace
299
301 RewritePatternSet &patterns, AggregateTypeConverter &typeConverter) {
302 patterns.add<HWArrayGetOpConversion,
303 HWArrayCreateLikeOpConversion<hw::ArrayCreateOp>,
304 HWArrayCreateLikeOpConversion<hw::ArrayConcatOp>,
305 HWAggregateConstantOpConversion, HWArraySliceOpConversion,
306 HWArrayInjectOpConversion, HWStructCreateOpConversion,
307 HWStructExtractOpConversion, MuxOpConversion>(
308 typeConverter, patterns.getContext());
309}
310
311namespace {
312struct HWAggregateToCombPass
313 : public hw::impl::HWAggregateToCombBase<HWAggregateToCombPass> {
314 void runOnOperation() override;
315 using HWAggregateToCombBase<HWAggregateToCombPass>::HWAggregateToCombBase;
316};
317} // namespace
318
319void HWAggregateToCombPass::runOnOperation() {
320 ConversionTarget target(getContext());
321
323 hw::AggregateConstantOp, hw::ArrayInjectOp,
326 target.addDynamicallyLegalOp<comb::MuxOp>(
327 [](comb::MuxOp op) { return hw::type_isa<IntegerType>(op.getType()); });
328 target.addLegalDialect<hw::HWDialect, comb::CombDialect>();
329
330 RewritePatternSet patterns(&getContext());
331 AggregateTypeConverter typeConverter;
333
334 if (failed(mlir::applyPartialConversion(getOperation(), target,
335 std::move(patterns))))
336 return signalPassFailure();
337}
assert(baseType &&"element must be base type")
MlirType uint64_t numElements
Definition CHIRRTL.cpp:30
static void populateHWAggregateToCombOpConversionPatterns(RewritePatternSet &patterns, AggregateTypeConverter &typeConverter)
create(elements, Type result_type=None)
Definition hw.py:483
create(array_value, idx)
Definition hw.py:450
create(data_type, value)
Definition hw.py:441
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition hw.py:1