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 namespace {
37 
38 class PruneTypeConverter : public mlir::TypeConverter {
39 public:
40  PruneTypeConverter() {
41  addConversion([&](Type type, SmallVectorImpl<Type> &results) {
43  results.push_back(type);
44  return success();
45  });
46  }
47 };
48 
49 template <typename TOp>
50 struct NoI0OperandsConversionPattern : public OpConversionPattern<TOp> {
51 public:
53  using OpAdaptor = typename OpConversionPattern<TOp>::OpAdaptor;
54 
55  LogicalResult
56  matchAndRewrite(TOp op, OpAdaptor adaptor,
57  ConversionPatternRewriter &rewriter) const override {
58  if (noI0TypedValue(adaptor.getOperands()))
59  return failure();
60 
61  // Part of i0-typed logic - prune it!
62  rewriter.eraseOp(op);
63  return success();
64  }
65 };
66 
67 template <typename... TOp>
68 static void addNoI0OperandsLegalizationPattern(ConversionTarget &target) {
69  target.addDynamicallyLegalOp<TOp...>(
70  [&](auto op) { return noI0TypedValue(op->getOperands()); });
71 }
72 
73 template <>
74 struct NoI0OperandsConversionPattern<comb::ICmpOp>
75  : public OpConversionPattern<comb::ICmpOp> {
76 public:
78  using OpAdaptor = typename OpConversionPattern<comb::ICmpOp>::OpAdaptor;
79 
80  // Returns the result of applying the predicate when the LHS and RHS are the
81  // exact same value.
82  static bool
83  applyCmpPredicateToEqualOperands(circt::comb::ICmpPredicate predicate) {
84  switch (predicate) {
85  case circt::comb::ICmpPredicate::eq:
86  case circt::comb::ICmpPredicate::sle:
87  case circt::comb::ICmpPredicate::sge:
88  case circt::comb::ICmpPredicate::ule:
89  case circt::comb::ICmpPredicate::uge:
90  case circt::comb::ICmpPredicate::ceq:
91  case circt::comb::ICmpPredicate::weq:
92  return true;
93  case circt::comb::ICmpPredicate::ne:
94  case circt::comb::ICmpPredicate::slt:
95  case circt::comb::ICmpPredicate::sgt:
96  case circt::comb::ICmpPredicate::ult:
97  case circt::comb::ICmpPredicate::ugt:
98  case circt::comb::ICmpPredicate::cne:
99  case circt::comb::ICmpPredicate::wne:
100  return false;
101  }
102  llvm_unreachable("unknown comparison predicate");
103  }
104 
105  LogicalResult
106  matchAndRewrite(comb::ICmpOp op, OpAdaptor adaptor,
107  ConversionPatternRewriter &rewriter) const override {
108  if (noI0TypedValue(adaptor.getOperands()))
109  return failure();
110 
111  // Caluculate the result of i0 value comparison.
112  bool result = applyCmpPredicateToEqualOperands(op.getPredicate());
113 
114  rewriter.replaceOpWithNewOp<hw::ConstantOp>(
115  op, APInt(1, result, /*isSigned=*/false));
116  return success();
117  }
118 };
119 
120 template <>
121 struct NoI0OperandsConversionPattern<comb::ParityOp>
122  : public OpConversionPattern<comb::ParityOp> {
123 public:
125  using OpAdaptor = typename OpConversionPattern<comb::ParityOp>::OpAdaptor;
126 
127  LogicalResult
128  matchAndRewrite(comb::ParityOp op, OpAdaptor adaptor,
129  ConversionPatternRewriter &rewriter) const override {
130  if (noI0TypedValue(adaptor.getOperands()))
131  return failure();
132 
133  // The value of "comb.parity i0" is 0.
134  rewriter.replaceOpWithNewOp<hw::ConstantOp>(
135  op, APInt(1, 0, /*isSigned=*/false));
136  return success();
137  }
138 };
139 
140 template <>
141 struct NoI0OperandsConversionPattern<comb::ConcatOp>
142  : public OpConversionPattern<comb::ConcatOp> {
143 public:
145  using OpAdaptor = typename OpConversionPattern<comb::ConcatOp>::OpAdaptor;
146 
147  LogicalResult
148  matchAndRewrite(comb::ConcatOp op, OpAdaptor adaptor,
149  ConversionPatternRewriter &rewriter) const override {
150  // Replace an i0 value with i0 constant.
151  if (op.getType().isInteger(0)) {
152  rewriter.replaceOpWithNewOp<hw::ConstantOp>(
153  op, APInt(1, 0, /*isSigned=*/false));
154  return success();
155  }
156 
157  if (noI0TypedValue(adaptor.getOperands()))
158  return failure();
159 
160  // Filter i0 operands and create a new concat op.
161  SmallVector<Value> newOperands;
162  llvm::copy_if(op.getOperands(), std::back_inserter(newOperands),
163  [](auto op) { return !op.getType().isInteger(0); });
164  rewriter.replaceOpWithNewOp<comb::ConcatOp>(op, newOperands);
165  return success();
166  }
167 };
168 
169 // A generic pruning pattern which prunes any operation which has an operand
170 // with an i0 typed value. Similarly, an operation is legal if all of its
171 // operands are not i0 typed.
172 template <typename TOp>
173 struct NoI0OperandPruningPattern {
174  using ConversionPattern = NoI0OperandsConversionPattern<TOp>;
175  static void addLegalizer(ConversionTarget &target) {
176  addNoI0OperandsLegalizationPattern<TOp>(target);
177  }
178 };
179 
180 // The NoI0ResultsConversionPattern will aggressively remove any operation
181 // which has a zero-width result. Furthermore, it will recursively erase any
182 // downstream users of the operation.
183 template <typename TOp>
184 struct NoI0ResultsConversionPattern : public OpConversionPattern<TOp> {
185 public:
187  using OpAdaptor = typename OpConversionPattern<TOp>::OpAdaptor;
188 
189  LogicalResult
190  matchAndRewrite(TOp op, OpAdaptor adaptor,
191  ConversionPatternRewriter &rewriter) const override {
192  if (noI0TypedValue(op->getResults()))
193  return failure();
194 
195  // Part of i0-typed logic - prune!
196  assert(op->getNumResults() == 1 &&
197  "expected single result if using rewriter.replaceOpWith");
198  rewriter.replaceOpWithNewOp<hw::ConstantOp>(
199  op, APInt(0, 0, /*isSigned=*/false));
200  return success();
201  }
202 };
203 
204 template <typename... TOp>
205 static void addNoI0ResultsLegalizationPattern(ConversionTarget &target) {
206  target.addDynamicallyLegalOp<TOp...>(
207  [&](auto op) { return noI0TypedValue(op->getResults()); });
208 }
209 
210 // A generic pruning pattern which prunes any operation that returns an i0
211 // value.
212 template <typename TOp>
213 struct NoI0ResultPruningPattern {
214  using ConversionPattern = NoI0ResultsConversionPattern<TOp>;
215  static void addLegalizer(ConversionTarget &target) {
216  addNoI0ResultsLegalizationPattern<TOp>(target);
217  }
218 };
219 
220 // Adds a pruning pattern to the conversion target. TPattern is expected to
221 // provides ConversionPattern definition and an addLegalizer function.
222 template <typename... TPattern>
223 static void addPruningPattern(ConversionTarget &target,
224  RewritePatternSet &patterns,
225  PruneTypeConverter &typeConverter) {
226  (patterns.add<typename TPattern::ConversionPattern>(typeConverter,
227  patterns.getContext()),
228  ...);
229  (TPattern::addLegalizer(target), ...);
230 }
231 
232 template <typename... TOp>
233 static void addNoI0ResultPruningPattern(ConversionTarget &target,
234  RewritePatternSet &patterns,
235  PruneTypeConverter &typeConverter) {
236  (patterns.add<typename NoI0ResultPruningPattern<TOp>::ConversionPattern>(
237  typeConverter, patterns.getContext()),
238  ...);
239  (NoI0ResultPruningPattern<TOp>::addLegalizer(target), ...);
240 }
241 
242 } // namespace
243 
244 void ExportVerilog::pruneZeroValuedLogic(HWEmittableModuleLike module) {
245  ConversionTarget target(*module->getContext());
246  RewritePatternSet patterns(module->getContext());
247  PruneTypeConverter typeConverter;
248 
249  target.addLegalDialect<sv::SVDialect, comb::CombDialect, hw::HWDialect>();
250  addPruningPattern<NoI0OperandPruningPattern<sv::PAssignOp>,
251  NoI0OperandPruningPattern<sv::BPAssignOp>,
252  NoI0OperandPruningPattern<sv::AssignOp>,
253  NoI0OperandPruningPattern<comb::ICmpOp>,
254  NoI0OperandPruningPattern<comb::ParityOp>,
255  NoI0OperandPruningPattern<comb::ConcatOp>>(target, patterns,
256  typeConverter);
257 
258  addNoI0ResultPruningPattern<
259  // SV ops
261  // Prune all zero-width combinational logic.
264  comb::ReplicateOp, comb::ShlOp, comb::ShrSOp, comb::ShrUOp, comb::SubOp,
265  comb::XorOp>(target, patterns, typeConverter);
266 
267  (void)applyPartialConversion(module, target, std::move(patterns));
268 }
assert(baseType &&"element must be base type")
static bool applyCmpPredicateToEqualOperands(ICmpPredicate predicate)
Definition: CombFolds.cpp:2839
static bool noI0TypedValue(ValueRange 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