CIRCT 24.0.0git
Loading...
Searching...
No Matches
PruneZeroValuedLogic.cpp
Go to the documentation of this file.
1//===- PruneZeroValuedLogic.cpp - Prune zero-valued logic -----------------===//
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 transform removes zero-valued logic from a `hw.module`.
10//
11//===----------------------------------------------------------------------===//
12
18#include "circt/Support/LLVM.h"
19#include "mlir/IR/Builders.h"
20#include "mlir/IR/PatternMatch.h"
21#include "mlir/Transforms/DialectConversion.h"
22
23using namespace circt;
24using namespace hw;
25
26static bool noI0Type(TypeRange types) {
27 return llvm::none_of(
28 types, [](Type type) { return ExportVerilog::isZeroBitType(type); });
29}
30
31static bool noI0TypedValue(ValueRange values) {
32 return noI0Type(values.getTypes());
33}
34
35/// Flatten the given value ranges into a single vector of values.
36static SmallVector<Value> flattenValues(ArrayRef<ValueRange> values) {
37 SmallVector<Value> result;
38 for (const auto &vals : values)
39 llvm::append_range(result, vals);
40 return result;
41}
42
43namespace {
44
45class PruneTypeConverter : public mlir::TypeConverter {
46public:
47 PruneTypeConverter() {
48 addConversion([&](Type type, SmallVectorImpl<Type> &results) {
50 results.push_back(type);
51 return success();
52 });
53 }
54};
55
56template <typename TOp>
57struct NoI0OperandsConversionPattern : public OpConversionPattern<TOp> {
58public:
59 using OpConversionPattern<TOp>::OpConversionPattern;
60 using OpAdaptor = typename OpConversionPattern<TOp>::OpAdaptor;
61 using OneToNOpAdaptor = typename OpConversionPattern<TOp>::OneToNOpAdaptor;
62
63 LogicalResult
64 matchAndRewrite(TOp op, OneToNOpAdaptor adaptor,
65 ConversionPatternRewriter &rewriter) const override {
66 SmallVector<Value> flattenedOperands = flattenValues(adaptor.getOperands());
67
68 // flattenedOperands may be empty (in case all operands are i0 typed and
69 // have already been pruned. Then the 1:N adaptor will reflect this as no
70 // operands).
71 if (!flattenedOperands.empty() && noI0TypedValue(flattenedOperands))
72 return failure();
73
74 // Part of i0-typed logic - prune it!
75 rewriter.eraseOp(op);
76 return success();
77 }
78};
79
80template <typename... TOp>
81static void addNoI0OperandsLegalizationPattern(ConversionTarget &target) {
82 target.addDynamicallyLegalOp<TOp...>(
83 [&](auto op) { return noI0TypedValue(op->getOperands()); });
84}
85
86template <>
87struct NoI0OperandsConversionPattern<comb::ICmpOp>
88 : public OpConversionPattern<comb::ICmpOp> {
89public:
90 using OpConversionPattern<comb::ICmpOp>::OpConversionPattern;
91 using OpAdaptor = typename OpConversionPattern<comb::ICmpOp>::OpAdaptor;
92 using OneToNOpAdaptor =
93 typename OpConversionPattern<comb::ICmpOp>::OneToNOpAdaptor;
94
95 // Returns the result of applying the predicate when the LHS and RHS are the
96 // exact same value.
97 static bool
98 applyCmpPredicateToEqualOperands(circt::comb::ICmpPredicate predicate) {
99 switch (predicate) {
100 case circt::comb::ICmpPredicate::eq:
101 case circt::comb::ICmpPredicate::sle:
102 case circt::comb::ICmpPredicate::sge:
103 case circt::comb::ICmpPredicate::ule:
104 case circt::comb::ICmpPredicate::uge:
105 case circt::comb::ICmpPredicate::ceq:
106 case circt::comb::ICmpPredicate::weq:
107 return true;
108 case circt::comb::ICmpPredicate::ne:
109 case circt::comb::ICmpPredicate::slt:
110 case circt::comb::ICmpPredicate::sgt:
111 case circt::comb::ICmpPredicate::ult:
112 case circt::comb::ICmpPredicate::ugt:
113 case circt::comb::ICmpPredicate::cne:
114 case circt::comb::ICmpPredicate::wne:
115 return false;
116 }
117 llvm_unreachable("unknown comparison predicate");
118 }
119
120 LogicalResult
121 matchAndRewrite(comb::ICmpOp op, OneToNOpAdaptor adaptor,
122 ConversionPatternRewriter &rewriter) const override {
123 if (noI0TypedValue(flattenValues(adaptor.getOperands())))
124 return failure();
125
126 // Caluculate the result of i0 value comparison.
127 bool result = applyCmpPredicateToEqualOperands(op.getPredicate());
128
129 rewriter.replaceOpWithNewOp<hw::ConstantOp>(
130 op, APInt(1, result, /*isSigned=*/false));
131 return success();
132 }
133};
134
135template <>
136struct NoI0OperandsConversionPattern<comb::ParityOp>
137 : public OpConversionPattern<comb::ParityOp> {
138public:
139 using OpConversionPattern<comb::ParityOp>::OpConversionPattern;
140 using OpAdaptor = typename OpConversionPattern<comb::ParityOp>::OpAdaptor;
141 using OneToNOpAdaptor =
142 typename OpConversionPattern<comb::ParityOp>::OneToNOpAdaptor;
143
144 LogicalResult
145 matchAndRewrite(comb::ParityOp op, OneToNOpAdaptor adaptor,
146 ConversionPatternRewriter &rewriter) const override {
147 SmallVector<Value> flattenedOperands = flattenValues(adaptor.getOperands());
148 if (!flattenedOperands.empty() && noI0TypedValue(flattenedOperands))
149 return failure();
150
151 // The value of "comb.parity i0" is 0.
152 rewriter.replaceOpWithNewOp<hw::ConstantOp>(
153 op, APInt(1, 0, /*isSigned=*/false));
154 return success();
155 }
156};
157
158template <>
159struct NoI0OperandsConversionPattern<comb::ConcatOp>
160 : public OpConversionPattern<comb::ConcatOp> {
161public:
162 using OpConversionPattern<comb::ConcatOp>::OpConversionPattern;
163 using OpAdaptor = typename OpConversionPattern<comb::ConcatOp>::OpAdaptor;
164 using OneToNOpAdaptor =
165 typename OpConversionPattern<comb::ConcatOp>::OneToNOpAdaptor;
166
167 LogicalResult
168 matchAndRewrite(comb::ConcatOp op, OneToNOpAdaptor adaptor,
169 ConversionPatternRewriter &rewriter) const override {
170 // Replace an i0 value with i0 constant.
171 if (op.getType().isInteger(0)) {
172 rewriter.replaceOpWithNewOp<hw::ConstantOp>(
173 op, APInt(1, 0, /*isSigned=*/false));
174 return success();
175 }
176
177 SmallVector<Value> materializedOperands =
178 flattenValues(adaptor.getOperands());
179
180 // If the materializedOperands are the same as the original operands, then
181 // there were no i0 operands. If not, then that means that this op used to
182 // have an i0 operand but materialization removed that operand, as reflect
183 // in the 1:N operand adaptor.
184 if (materializedOperands.size() == adaptor.getOperands().size())
185 return failure();
186
187 // Create a new concat op with the materialized operands.
188 rewriter.replaceOpWithNewOp<comb::ConcatOp>(op, materializedOperands);
189 return success();
190 }
191};
192
193// A generic pruning pattern which prunes any operation which has an operand
194// with an i0 typed value. Similarly, an operation is legal if all of its
195// operands are not i0 typed.
196template <typename TOp>
197struct NoI0OperandPruningPattern {
198 using ConversionPattern = NoI0OperandsConversionPattern<TOp>;
199 static void addLegalizer(ConversionTarget &target) {
200 addNoI0OperandsLegalizationPattern<TOp>(target);
201 }
202};
203
204// The NoI0ResultsConversionPattern will aggressively remove any operation
205// which has a zero-width result. Furthermore, it will recursively erase any
206// downstream users of the operation.
207template <typename TOp>
208struct NoI0ResultsConversionPattern : public OpConversionPattern<TOp> {
209public:
210 using OpConversionPattern<TOp>::OpConversionPattern;
211 using OneToNOpAdaptor = typename OpConversionPattern<TOp>::OneToNOpAdaptor;
212
213 LogicalResult
214 matchAndRewrite(TOp op, OneToNOpAdaptor adaptor,
215 ConversionPatternRewriter &rewriter) const override {
216 if (noI0TypedValue(op->getResults()))
217 return failure();
218
219 // Part of i0-typed logic - prune!
220 assert(op->getNumResults() == 1 &&
221 "expected single result if using rewriter.replaceOpWith");
222 rewriter.replaceOpWithNewOp<hw::ConstantOp>(
223 op, APInt(0, 0, /*isSigned=*/false));
224 return success();
225 }
226};
227
228template <typename... TOp>
229static void addNoI0ResultsLegalizationPattern(ConversionTarget &target) {
230 target.addDynamicallyLegalOp<TOp...>(
231 [&](auto op) { return noI0TypedValue(op->getResults()); });
232}
233
234// A generic pruning pattern which prunes any operation that returns an i0
235// value.
236template <typename TOp>
237struct NoI0ResultPruningPattern {
238 using ConversionPattern = NoI0ResultsConversionPattern<TOp>;
239 static void addLegalizer(ConversionTarget &target) {
240 addNoI0ResultsLegalizationPattern<TOp>(target);
241 }
242};
243
244// Adds a pruning pattern to the conversion target. TPattern is expected to
245// provides ConversionPattern definition and an addLegalizer function.
246template <typename... TPattern>
247static void addPruningPattern(ConversionTarget &target,
248 RewritePatternSet &patterns,
249 PruneTypeConverter &typeConverter) {
250 (patterns.add<typename TPattern::ConversionPattern>(typeConverter,
251 patterns.getContext()),
252 ...);
253 (TPattern::addLegalizer(target), ...);
254}
255
256template <typename... TOp>
257static void addNoI0ResultPruningPattern(ConversionTarget &target,
258 RewritePatternSet &patterns,
259 PruneTypeConverter &typeConverter) {
260 (patterns.add<typename NoI0ResultPruningPattern<TOp>::ConversionPattern>(
261 typeConverter, patterns.getContext()),
262 ...);
263 (NoI0ResultPruningPattern<TOp>::addLegalizer(target), ...);
264}
265
266} // namespace
267
268void ExportVerilog::pruneZeroValuedLogic(HWEmittableModuleLike module) {
269 ConversionTarget target(*module->getContext());
270 RewritePatternSet patterns(module->getContext());
271 PruneTypeConverter typeConverter;
272
273 target.addLegalDialect<sv::SVDialect, comb::CombDialect, hw::HWDialect>();
274 addPruningPattern<NoI0OperandPruningPattern<sv::PAssignOp>,
275 NoI0OperandPruningPattern<sv::BPAssignOp>,
276 NoI0OperandPruningPattern<sv::AssignOp>,
277 NoI0OperandPruningPattern<comb::ICmpOp>,
278 NoI0OperandPruningPattern<comb::ParityOp>,
279 NoI0OperandPruningPattern<comb::ConcatOp>>(target, patterns,
280 typeConverter);
281
282 addNoI0ResultPruningPattern<
283 // SV ops
285 // Prune all zero-width combinational logic.
288 comb::ReplicateOp, comb::ShlOp, comb::ShrSOp, comb::ShrUOp, comb::SubOp,
289 comb::XorOp>(target, patterns, typeConverter);
290
291 (void)applyPartialConversion(module, target, std::move(patterns));
292}
assert(baseType &&"element must be base type")
static bool applyCmpPredicateToEqualOperands(ICmpPredicate predicate)
false return failure()
Parse an optional keyword or string and set instance into 'result'.
return success()
static bool noI0TypedValue(ValueRange values)
static bool noI0Type(TypeRange types)
static SmallVector< Value > flattenValues(ArrayRef< ValueRange > values)
Flatten the given value ranges into a single vector of values.
Definition sv.py:70
void pruneZeroValuedLogic(hw::HWEmittableModuleLike module)
bool isZeroBitType(Type type)
Return true if this is a zero bit type, e.g.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition comb.py:1
Definition hw.py:1