CIRCT  20.0.0git
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 
13 #include "ExportVerilogInternals.h"
15 #include "circt/Dialect/HW/HWOps.h"
18 #include "mlir/IR/Builders.h"
19 #include "mlir/IR/PatternMatch.h"
20 #include "mlir/Transforms/DialectConversion.h"
21 
22 using namespace llvm;
23 using namespace mlir;
24 using namespace circt;
25 using namespace hw;
26 
27 static bool noI0Type(TypeRange types) {
28  return llvm::none_of(
29  types, [](Type type) { return ExportVerilog::isZeroBitType(type); });
30 }
31 
32 static bool noI0TypedValue(ValueRange values) {
33  return noI0Type(values.getTypes());
34 }
35 
36 /// Flatten the given value ranges into a single vector of values.
37 static 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 
44 namespace {
45 
46 class PruneTypeConverter : public mlir::TypeConverter {
47 public:
48  PruneTypeConverter() {
49  addConversion([&](Type type, SmallVectorImpl<Type> &results) {
51  results.push_back(type);
52  return success();
53  });
54  }
55 };
56 
57 template <typename TOp>
58 struct NoI0OperandsConversionPattern : public OpConversionPattern<TOp> {
59 public:
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  ValueRange 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 
81 template <typename... TOp>
82 static void addNoI0OperandsLegalizationPattern(ConversionTarget &target) {
83  target.addDynamicallyLegalOp<TOp...>(
84  [&](auto op) { return noI0TypedValue(op->getOperands()); });
85 }
86 
87 template <>
88 struct NoI0OperandsConversionPattern<comb::ICmpOp>
89  : public OpConversionPattern<comb::ICmpOp> {
90 public:
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 
136 template <>
137 struct NoI0OperandsConversionPattern<comb::ParityOp>
138  : public OpConversionPattern<comb::ParityOp> {
139 public:
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  ValueRange 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 
159 template <>
160 struct NoI0OperandsConversionPattern<comb::ConcatOp>
161  : public OpConversionPattern<comb::ConcatOp> {
162 public:
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.
197 template <typename TOp>
198 struct 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.
208 template <typename TOp>
209 struct NoI0ResultsConversionPattern : public OpConversionPattern<TOp> {
210 public:
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 
229 template <typename... TOp>
230 static 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.
237 template <typename TOp>
238 struct 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.
247 template <typename... TPattern>
248 static 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 
257 template <typename... TOp>
258 static 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 
269 void 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)
Definition: CombFolds.cpp:2728
static bool noI0TypedValue(ValueRange values)
static SmallVector< Value > flattenValues(ArrayRef< ValueRange > values)
Flatten the given value ranges into a single vector of values.
static bool noI0Type(TypeRange types)
Definition: sv.py:68
Definition: sv.py:35
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: DebugAnalysis.h:21
Definition: comb.py:1
Definition: hw.py:1