CIRCT 23.0.0git
Loading...
Searching...
No Matches
SimplifyRefs.cpp
Go to the documentation of this file.
1//===- SimplifyRefs.cpp - moore.concat_ref and queue reference lowering -- ===//
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 the SimplifyRefs pass.
10// It has three purposes:
11// - To disassemble the moore.concat_ref. Which is tricky to lower
12// directly. For example, disassemble "{a, b} = c" onto "a = c[7:3]"
13// and "b = c[2:0]".
14// - To eliminate moore.dyn_queue_ref_element in the case where the reference
15// immediately has a value assigned via blocking assignment, replacing it with
16// moore.queue.set. Queue element references are tricky to lower into LLHD,
17// so it's best to get rid of them.
18// - To rewrite assignments to ExtractOp expressions on a packed struct,
19// e.g. "s[7:0] = v", into an assignment to a concatenation of the struct's
20// (possibly further sliced) fields.
21//
22//===----------------------------------------------------------------------===//
23
26#include "mlir/IR/IRMapping.h"
27#include "mlir/Transforms/DialectConversion.h"
28
29namespace circt {
30namespace moore {
31#define GEN_PASS_DEF_SIMPLIFYREFS
32#include "circt/Dialect/Moore/MoorePasses.h.inc"
33} // namespace moore
34} // namespace circt
35
36using namespace circt;
37using namespace moore;
38using namespace mlir;
39
40namespace {
41
42// A helper function for collecting the non-concatRef operands of concatRef.
43static void collectOperands(Value operand, SmallVectorImpl<Value> &operands,
44 ConversionPatternRewriter &rewriter) {
45 if (auto concatRefOp = operand.getDefiningOp<ConcatRefOp>()) {
46 // Assuming the assignment is the only user, erase the op now.
47 if (std::distance(concatRefOp->getUsers().begin(),
48 concatRefOp->getUsers().end()) == 1) {
49 rewriter.eraseOp(concatRefOp);
50 }
51 for (auto nestedOperand : concatRefOp.getValues())
52 collectOperands(nestedOperand, operands, rewriter);
53
54 } else
55 operands.push_back(operand);
56}
57
58template <typename OpTy>
59struct ConcatRefLowering : public OpConversionPattern<OpTy> {
61 using OpAdaptor = typename OpTy::Adaptor;
62
63 LogicalResult
64 matchAndRewrite(OpTy op, OpAdaptor adaptor,
65 ConversionPatternRewriter &rewriter) const override {
66 // Use to collect the operands of concatRef.
67 SmallVector<Value, 4> operands;
68 collectOperands(op.getDst(), operands, rewriter);
69 auto srcWidth =
70 cast<UnpackedType>(op.getSrc().getType()).getBitSize().value();
71
72 // Disassemble assignments with the LHS is concatRef. And create new
73 // corresponding assignments using non-concatRef LHS.
74 for (auto operand : operands) {
75 auto type = cast<RefType>(operand.getType()).getNestedType();
76 auto width = type.getBitSize().value();
77
78 rewriter.setInsertionPoint(op);
79 // FIXME: Need to estimate whether the bits range is from large to
80 // small or vice versa. Like "logic [7:0] or [0:7]".
81
82 // Only able to correctly handle the situation like "[7:0]" now.
83 auto extract = ExtractOp::create(rewriter, op.getLoc(), type, op.getSrc(),
84 srcWidth - width);
85
86 // Update the real bit width of RHS of assignment. Like "c" the above
87 // description mentioned.
88 srcWidth = srcWidth - width;
89
90 // Clone the original op (preserving any extra operand, e.g. a delay on
91 // the delayed assign variants) and remap dst/src to the leaf ref and its
92 // extracted slice.
93 IRMapping mapping;
94 mapping.map(op.getDst(), operand);
95 mapping.map(op.getSrc(), Value(extract));
96 rewriter.clone(*op.getOperation(), mapping);
97 }
98 rewriter.eraseOp(op);
99 return success();
100 }
101};
102
103struct FieldInfo {
104 Value field = nullptr;
105 uint32_t size = 0;
106 uint32_t offset = 0;
107};
108
109// A helper function that recursively collects the members of a struct.
110static LogicalResult collectFields(Value structRef,
111 SmallVector<FieldInfo> &fields,
112 ConversionPatternRewriter &rewriter,
113 uint32_t initialOffset = 0) {
114 auto structType =
115 cast<StructType>(cast<RefType>(structRef.getType()).getNestedType());
116 uint32_t offset = initialOffset;
117 // Visit fields in reverse order (declaration order is MSB-first)
118 for (auto &member : llvm::reverse(structType.getMembers())) {
119 auto fieldRef = StructExtractRefOp::create(rewriter, structRef.getLoc(),
120 RefType::get(member.type),
121 member.name, structRef);
122
123 auto fieldSize = member.type.getBitSize();
124 if (!fieldSize)
125 return mlir::emitError(structRef.getLoc())
126 << "unsupported: field with unknown size in struct flattening";
127
128 if (isa<StructType>(member.type)) {
129 auto result = collectFields(fieldRef, fields, rewriter, offset);
130 if (failed(result))
131 return result;
132 } else if (isa<UnionType>(member.type)) {
133 return mlir::emitError(structRef.getLoc())
134 << "unsupported: union member in struct flattening";
135 } else {
136 fields.push_back({fieldRef, *fieldSize, offset});
137 }
138 offset += *fieldSize;
139 }
140 return success();
141}
142
143template <typename OpTy>
144struct StructExtractLowering : public OpConversionPattern<OpTy> {
146 using OpAdaptor = typename OpTy::Adaptor;
147
148 LogicalResult
149 matchAndRewrite(OpTy op, OpAdaptor adaptor,
150 ConversionPatternRewriter &rewriter) const override {
151 Value dst = op.getDst();
152
153 // We are specifically matching cases in which the LHS of the assignment is
154 // an ExtractRef operation on a struct.
155 auto extractRefOp = dyn_cast<ExtractRefOp>(dst.getDefiningOp());
156 auto baseStructType = dyn_cast_if_present<StructType>(
157 cast<RefType>(extractRefOp.getInput().getType()).getNestedType());
158 if (!baseStructType)
159 return success();
160
161 // Get the boundaries of the ExtractOp.
162 uint32_t extractedLow = extractRefOp.getLowBit();
163 auto targetWidth =
164 cast<RefType>(dst.getType()).getNestedType().getBitSize();
165 if (!targetWidth)
166 return mlir::emitError(op.getLoc())
167 << "unsupported: found field with unknown size in struct "
168 "flattening";
169
170 rewriter.setInsertionPoint(op);
171 Location loc = op.getLoc();
172
173 // Collect all the fields of the struct.
174 SmallVector<FieldInfo> fields;
175 if (failed(collectFields(extractRefOp.getInput(), fields, rewriter))) {
176 return failure();
177 }
178
179 // Select only the fields revelant to the ExtractOp
180 SmallVector<Value> relevantFields;
181 uint32_t offsetInStruct = extractedLow;
182 uint32_t remaining = *targetWidth;
183 for (auto &field : fields) {
184 if (remaining == 0)
185 break;
186
187 if (field.offset <= offsetInStruct &&
188 field.offset + field.size > offsetInStruct) {
189 uint32_t offsetInField = offsetInStruct - field.offset;
190 uint32_t remainingInField = field.size - offsetInField;
191 uint32_t sizeToExtract = std::min(remaining, remainingInField);
192
193 auto fieldType = cast<RefType>(field.field.getType()).getNestedType();
194 auto extractType =
195 IntType::get(rewriter.getContext(), sizeToExtract,
196 cast<PackedType>(fieldType).getDomain());
197 auto newLhs =
198 ExtractRefOp::create(rewriter, loc, RefType::get(extractType),
199 field.field, offsetInField);
200 relevantFields.push_back(newLhs);
201
202 offsetInStruct += sizeToExtract;
203 remaining -= sizeToExtract;
204 }
205 }
206
207 if (relevantFields.size() == 0)
208 return mlir::emitError(op.getLoc()) << "Struct extract is out of range";
209
210 // Reverse the fields order to match Concat order
211 std::reverse(relevantFields.begin(), relevantFields.end());
212
213 Value finalRef =
214 relevantFields.size() == 1
215 ? relevantFields.front()
216 : Value(ConcatRefOp::create(rewriter, loc, relevantFields));
217
218 IRMapping mapping;
219 mapping.map(op.getDst(), finalRef);
220 rewriter.clone(*op.getOperation(), mapping);
221
222 rewriter.eraseOp(op);
223 rewriter.eraseOp(extractRefOp);
224 return success();
225 }
226};
227
228struct QueueRefLowering : public OpConversionPattern<DynQueueRefElementOp> {
229 using OpConversionPattern<DynQueueRefElementOp>::OpConversionPattern;
230
231 LogicalResult
232 matchAndRewrite(DynQueueRefElementOp op, OpAdaptor adaptor,
233 ConversionPatternRewriter &rewriter) const override {
234 // For now, we only support using a queue reference in the LHS of a blocking
235 // assignment op.
236 for (auto *consumer : op->getUsers()) {
237 if (isa<BlockingAssignOp>(consumer)) {
238
239 auto assignOp = cast<BlockingAssignOp>(consumer);
240 // Replace BlockingAssignOp with a queue.set operation to the index.
241 rewriter.setInsertionPoint(consumer);
242 moore::QueueSetOp::create(rewriter, op->getLoc(), op.getInput(),
243 op.getIndex(), assignOp.getSrc());
244
245 rewriter.eraseOp(assignOp);
246 } else {
247 return mlir::emitError(op.getLoc())
248 << "Queue element reference couldn't be reduced to setting the "
249 "value at an index: consuming op "
250 << consumer << " is not supported";
251 }
252 }
253
254 rewriter.eraseOp(op);
255
256 return success();
257 }
258};
259
260struct AssocArrayRefLowering
261 : public OpConversionPattern<AssocArrayExtractRefOp> {
262 using OpConversionPattern<AssocArrayExtractRefOp>::OpConversionPattern;
263
264 LogicalResult
265 matchAndRewrite(AssocArrayExtractRefOp op, OpAdaptor adaptor,
266 ConversionPatternRewriter &rewriter) const override {
267 for (auto *consumer : op->getUsers()) {
268 if (isa<BlockingAssignOp>(consumer)) {
269 auto assignOp = cast<BlockingAssignOp>(consumer);
270 rewriter.setInsertionPoint(consumer);
271 moore::AssocArraySetOp::create(rewriter, op->getLoc(), op.getInput(),
272 op.getIndex(), assignOp.getSrc());
273 rewriter.eraseOp(assignOp);
274 } else {
275 return mlir::emitError(op.getLoc())
276 << "Associative array element reference couldn't be reduced "
277 "to setting the value at an index: consuming op "
278 << consumer << " is not supported";
279 }
280 }
281
282 rewriter.eraseOp(op);
283 return success();
284 }
285};
286
287struct SimplifyRefsPass
288 : public circt::moore::impl::SimplifyRefsBase<SimplifyRefsPass> {
289 void runOnOperation() override;
290};
291
292} // namespace
293
294std::unique_ptr<mlir::Pass> circt::moore::createSimplifyRefsPass() {
295 return std::make_unique<SimplifyRefsPass>();
296}
297
298void SimplifyRefsPass::runOnOperation() {
299 MLIRContext &context = getContext();
300 ConversionTarget target(context);
301
302 target.addDynamicallyLegalOp<ContinuousAssignOp, BlockingAssignOp,
303 NonBlockingAssignOp, DelayedContinuousAssignOp,
304 DelayedNonBlockingAssignOp>([](auto op) {
305 auto extractRefOp =
306 op->getOperand(0).template getDefiningOp<ExtractRefOp>();
307 if (!extractRefOp)
308 return true;
309 auto nestedType =
310 cast<RefType>(extractRefOp.getInput().getType()).getNestedType();
311 return !isa<StructType>(nestedType);
312 });
313
314 target.markUnknownOpDynamicallyLegal([](Operation *) { return true; });
315
316 RewritePatternSet extractRefOnStructPatterns(&context);
317 extractRefOnStructPatterns
318 .add<StructExtractLowering<ContinuousAssignOp>,
319 StructExtractLowering<BlockingAssignOp>,
320 StructExtractLowering<NonBlockingAssignOp>,
321 StructExtractLowering<DelayedContinuousAssignOp>,
322 StructExtractLowering<DelayedNonBlockingAssignOp>>(&context);
323
324 if (failed(applyFullConversion(getOperation(), target,
325 std::move(extractRefOnStructPatterns)))) {
326 signalPassFailure();
327 return;
328 }
329
330 target.addDynamicallyLegalOp<ContinuousAssignOp, BlockingAssignOp,
331 NonBlockingAssignOp, DelayedContinuousAssignOp,
332 DelayedNonBlockingAssignOp>([](auto op) {
333 return !op->getOperand(0).template getDefiningOp<ConcatRefOp>();
334 });
335
336 RewritePatternSet concatRefPatterns(&context);
337 concatRefPatterns.add<ConcatRefLowering<ContinuousAssignOp>,
338 ConcatRefLowering<BlockingAssignOp>,
339 ConcatRefLowering<NonBlockingAssignOp>,
340 ConcatRefLowering<DelayedContinuousAssignOp>,
341 ConcatRefLowering<DelayedNonBlockingAssignOp>>(
342 &context);
343
344 if (failed(applyFullConversion(getOperation(), target,
345 std::move(concatRefPatterns)))) {
346 signalPassFailure();
347 return;
348 }
349
350 // Once we have removed ConcatRefOps, attempt to rewrite any queue element
351 // references to queue.set
352 RewritePatternSet queueRefPatterns(&context);
353 target.addIllegalOp<DynQueueRefElementOp>();
354 queueRefPatterns.add<QueueRefLowering>(&context);
355 if (failed(applyPartialConversion(getOperation(), target,
356 std::move(queueRefPatterns))))
357 signalPassFailure();
358
359 // Once we have removed AssocArrayExtractRefOps, attempt to rewrite any
360 // associative array element references to assoc_array.set
361 RewritePatternSet assocArrayRefPatterns(&context);
362 target.addIllegalOp<AssocArrayExtractRefOp>();
363 assocArrayRefPatterns.add<AssocArrayRefLowering>(&context);
364 if (failed(applyPartialConversion(getOperation(), target,
365 std::move(assocArrayRefPatterns))))
366 signalPassFailure();
367}
static std::unique_ptr< Context > context
static Attribute collectFields(MLIRContext *context, ArrayRef< Attribute > operands)
std::unique_ptr< mlir::Pass > createSimplifyRefsPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.