CIRCT 23.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:
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:
91 using OpAdaptor = typename OpConversionPattern<comb::ICmpOp>::OpAdaptor;
92 using 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:
140 using OpAdaptor = typename OpConversionPattern<comb::ParityOp>::OpAdaptor;
141 using 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:
163 using OpAdaptor = typename OpConversionPattern<comb::ConcatOp>::OpAdaptor;
164 using 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:
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)
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