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