CIRCT  19.0.0git
CombOps.cpp
Go to the documentation of this file.
1 //===- CombOps.cpp - Implement the Comb operations ------------------------===//
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 file implements combinational ops.
10 //
11 //===----------------------------------------------------------------------===//
12 
14 #include "circt/Dialect/HW/HWOps.h"
15 #include "mlir/IR/Builders.h"
16 #include "mlir/IR/ImplicitLocOpBuilder.h"
17 #include "mlir/IR/PatternMatch.h"
18 #include "llvm/Support/FormatVariadic.h"
19 
20 using namespace circt;
21 using namespace comb;
22 
23 /// Create a sign extension operation from a value of integer type to an equal
24 /// or larger integer type.
25 Value comb::createOrFoldSExt(Location loc, Value value, Type destTy,
26  OpBuilder &builder) {
27  IntegerType valueType = dyn_cast<IntegerType>(value.getType());
28  assert(valueType && isa<IntegerType>(destTy) &&
29  valueType.getWidth() <= destTy.getIntOrFloatBitWidth() &&
30  valueType.getWidth() != 0 && "invalid sext operands");
31  // If already the right size, we are done.
32  if (valueType == destTy)
33  return value;
34 
35  // sext is concat with a replicate of the sign bits and the bottom part.
36  auto signBit =
37  builder.createOrFold<ExtractOp>(loc, value, valueType.getWidth() - 1, 1);
38  auto signBits = builder.createOrFold<ReplicateOp>(
39  loc, signBit, destTy.getIntOrFloatBitWidth() - valueType.getWidth());
40  return builder.createOrFold<ConcatOp>(loc, signBits, value);
41 }
42 
43 Value comb::createOrFoldSExt(Value value, Type destTy,
44  ImplicitLocOpBuilder &builder) {
45  return createOrFoldSExt(builder.getLoc(), value, destTy, builder);
46 }
47 
48 Value comb::createOrFoldNot(Location loc, Value value, OpBuilder &builder,
49  bool twoState) {
50  auto allOnes = builder.create<hw::ConstantOp>(loc, value.getType(), -1);
51  return builder.createOrFold<XorOp>(loc, value, allOnes, twoState);
52 }
53 
54 Value comb::createOrFoldNot(Value value, ImplicitLocOpBuilder &builder,
55  bool twoState) {
56  return createOrFoldNot(builder.getLoc(), value, builder, twoState);
57 }
58 
59 //===----------------------------------------------------------------------===//
60 // ICmpOp
61 //===----------------------------------------------------------------------===//
62 
63 ICmpPredicate ICmpOp::getFlippedPredicate(ICmpPredicate predicate) {
64  switch (predicate) {
65  case ICmpPredicate::eq:
66  return ICmpPredicate::eq;
67  case ICmpPredicate::ne:
68  return ICmpPredicate::ne;
69  case ICmpPredicate::slt:
70  return ICmpPredicate::sgt;
71  case ICmpPredicate::sle:
72  return ICmpPredicate::sge;
73  case ICmpPredicate::sgt:
74  return ICmpPredicate::slt;
75  case ICmpPredicate::sge:
76  return ICmpPredicate::sle;
77  case ICmpPredicate::ult:
78  return ICmpPredicate::ugt;
79  case ICmpPredicate::ule:
80  return ICmpPredicate::uge;
81  case ICmpPredicate::ugt:
82  return ICmpPredicate::ult;
83  case ICmpPredicate::uge:
84  return ICmpPredicate::ule;
85  case ICmpPredicate::ceq:
86  return ICmpPredicate::ceq;
87  case ICmpPredicate::cne:
88  return ICmpPredicate::cne;
89  case ICmpPredicate::weq:
90  return ICmpPredicate::weq;
91  case ICmpPredicate::wne:
92  return ICmpPredicate::wne;
93  }
94  llvm_unreachable("unknown comparison predicate");
95 }
96 
97 bool ICmpOp::isPredicateSigned(ICmpPredicate predicate) {
98  switch (predicate) {
99  case ICmpPredicate::ult:
100  case ICmpPredicate::ugt:
101  case ICmpPredicate::ule:
102  case ICmpPredicate::uge:
103  case ICmpPredicate::ne:
104  case ICmpPredicate::eq:
105  case ICmpPredicate::cne:
106  case ICmpPredicate::ceq:
107  case ICmpPredicate::wne:
108  case ICmpPredicate::weq:
109  return false;
110  case ICmpPredicate::slt:
111  case ICmpPredicate::sgt:
112  case ICmpPredicate::sle:
113  case ICmpPredicate::sge:
114  return true;
115  }
116  llvm_unreachable("unknown comparison predicate");
117 }
118 
119 /// Returns the predicate for a logically negated comparison, e.g. mapping
120 /// EQ => NE and SLE => SGT.
121 ICmpPredicate ICmpOp::getNegatedPredicate(ICmpPredicate predicate) {
122  switch (predicate) {
123  case ICmpPredicate::eq:
124  return ICmpPredicate::ne;
125  case ICmpPredicate::ne:
126  return ICmpPredicate::eq;
127  case ICmpPredicate::slt:
128  return ICmpPredicate::sge;
129  case ICmpPredicate::sle:
130  return ICmpPredicate::sgt;
131  case ICmpPredicate::sgt:
132  return ICmpPredicate::sle;
133  case ICmpPredicate::sge:
134  return ICmpPredicate::slt;
135  case ICmpPredicate::ult:
136  return ICmpPredicate::uge;
137  case ICmpPredicate::ule:
138  return ICmpPredicate::ugt;
139  case ICmpPredicate::ugt:
140  return ICmpPredicate::ule;
141  case ICmpPredicate::uge:
142  return ICmpPredicate::ult;
143  case ICmpPredicate::ceq:
144  return ICmpPredicate::cne;
145  case ICmpPredicate::cne:
146  return ICmpPredicate::ceq;
147  case ICmpPredicate::weq:
148  return ICmpPredicate::wne;
149  case ICmpPredicate::wne:
150  return ICmpPredicate::weq;
151  }
152  llvm_unreachable("unknown comparison predicate");
153 }
154 
155 /// Return true if this is an equality test with -1, which is a "reduction
156 /// and" operation in Verilog.
157 bool ICmpOp::isEqualAllOnes() {
158  if (getPredicate() != ICmpPredicate::eq)
159  return false;
160 
161  if (auto op1 =
162  dyn_cast_or_null<hw::ConstantOp>(getOperand(1).getDefiningOp()))
163  return op1.getValue().isAllOnes();
164  return false;
165 }
166 
167 /// Return true if this is a not equal test with 0, which is a "reduction
168 /// or" operation in Verilog.
169 bool ICmpOp::isNotEqualZero() {
170  if (getPredicate() != ICmpPredicate::ne)
171  return false;
172 
173  if (auto op1 =
174  dyn_cast_or_null<hw::ConstantOp>(getOperand(1).getDefiningOp()))
175  return op1.getValue().isZero();
176  return false;
177 }
178 
179 //===----------------------------------------------------------------------===//
180 // Unary Operations
181 //===----------------------------------------------------------------------===//
182 
183 LogicalResult ReplicateOp::verify() {
184  // The source must be equal or smaller than the dest type, and an even
185  // multiple of it. Both are already known to be signless integers.
186  auto srcWidth = cast<IntegerType>(getOperand().getType()).getWidth();
187  auto dstWidth = cast<IntegerType>(getType()).getWidth();
188  if (srcWidth == 0)
189  return emitOpError("replicate does not take zero bit integer");
190 
191  if (srcWidth > dstWidth)
192  return emitOpError("replicate cannot shrink bitwidth of operand"),
193  failure();
194 
195  if (dstWidth % srcWidth)
196  return emitOpError("replicate must produce integer multiple of operand"),
197  failure();
198 
199  return success();
200 }
201 
202 //===----------------------------------------------------------------------===//
203 // Variadic operations
204 //===----------------------------------------------------------------------===//
205 
206 static LogicalResult verifyUTBinOp(Operation *op) {
207  if (op->getOperands().empty())
208  return op->emitOpError("requires 1 or more args");
209  return success();
210 }
211 
212 LogicalResult AddOp::verify() { return verifyUTBinOp(*this); }
213 
214 LogicalResult MulOp::verify() { return verifyUTBinOp(*this); }
215 
216 LogicalResult AndOp::verify() { return verifyUTBinOp(*this); }
217 
218 LogicalResult OrOp::verify() { return verifyUTBinOp(*this); }
219 
220 LogicalResult XorOp::verify() { return verifyUTBinOp(*this); }
221 
222 /// Return true if this is a two operand xor with an all ones constant as its
223 /// RHS operand.
224 bool XorOp::isBinaryNot() {
225  if (getNumOperands() != 2)
226  return false;
227  if (auto cst = getOperand(1).getDefiningOp<hw::ConstantOp>())
228  if (cst.getValue().isAllOnes())
229  return true;
230  return false;
231 }
232 
233 //===----------------------------------------------------------------------===//
234 // ConcatOp
235 //===----------------------------------------------------------------------===//
236 
237 static unsigned getTotalWidth(ValueRange inputs) {
238  unsigned resultWidth = 0;
239  for (auto input : inputs) {
240  resultWidth += hw::type_cast<IntegerType>(input.getType()).getWidth();
241  }
242  return resultWidth;
243 }
244 
245 LogicalResult ConcatOp::verify() {
246  unsigned tyWidth = cast<IntegerType>(getType()).getWidth();
247  unsigned operandsTotalWidth = getTotalWidth(getInputs());
248  if (tyWidth != operandsTotalWidth)
249  return emitOpError("ConcatOp requires operands total width to "
250  "match type width. operands "
251  "totalWidth is")
252  << operandsTotalWidth << ", but concatOp type width is " << tyWidth;
253 
254  return success();
255 }
256 
257 void ConcatOp::build(OpBuilder &builder, OperationState &result, Value hd,
258  ValueRange tl) {
259  result.addOperands(ValueRange{hd});
260  result.addOperands(tl);
261  unsigned hdWidth = cast<IntegerType>(hd.getType()).getWidth();
262  result.addTypes(builder.getIntegerType(getTotalWidth(tl) + hdWidth));
263 }
264 
265 LogicalResult ConcatOp::inferReturnTypes(
266  MLIRContext *context, std::optional<Location> loc, ValueRange operands,
267  DictionaryAttr attrs, mlir::OpaqueProperties properties,
268  mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
269  unsigned resultWidth = getTotalWidth(operands);
270  results.push_back(IntegerType::get(context, resultWidth));
271  return success();
272 }
273 
274 //===----------------------------------------------------------------------===//
275 // Other Operations
276 //===----------------------------------------------------------------------===//
277 
278 LogicalResult ExtractOp::verify() {
279  unsigned srcWidth = cast<IntegerType>(getInput().getType()).getWidth();
280  unsigned dstWidth = cast<IntegerType>(getType()).getWidth();
281  if (getLowBit() >= srcWidth || srcWidth - getLowBit() < dstWidth)
282  return emitOpError("from bit too large for input"), failure();
283 
284  return success();
285 }
286 
287 LogicalResult TruthTableOp::verify() {
288  size_t numInputs = getInputs().size();
289  if (numInputs >= sizeof(size_t) * 8)
290  return emitOpError("Truth tables support a maximum of ")
291  << sizeof(size_t) * 8 - 1 << " inputs on your platform";
292 
293  ArrayAttr table = getLookupTable();
294  if (table.size() != (1ull << numInputs))
295  return emitOpError("Expected lookup table of 2^n length");
296  return success();
297 }
298 
299 //===----------------------------------------------------------------------===//
300 // TableGen generated logic.
301 //===----------------------------------------------------------------------===//
302 
303 // Provide the autogenerated implementation guts for the Op classes.
304 #define GET_OP_CLASSES
305 #include "circt/Dialect/Comb/Comb.cpp.inc"
assert(baseType &&"element must be base type")
static unsigned getTotalWidth(ValueRange inputs)
Definition: CombOps.cpp:237
static LogicalResult verifyUTBinOp(Operation *op)
Definition: CombOps.cpp:206
llvm::SmallVector< StringAttr > inputs
Builder builder
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
Value createOrFoldNot(Location loc, Value value, OpBuilder &builder, bool twoState=false)
Create a `‘Not’' gate on a value.
Definition: CombOps.cpp:48
Value createOrFoldSExt(Location loc, Value value, Type destTy, OpBuilder &builder)
Create a sign extension operation from a value of integer type to an equal or larger integer type.
Definition: CombOps.cpp:25
LogicalResult inferReturnTypes(MLIRContext *context, std::optional< Location > loc, ValueRange operands, DictionaryAttr attrs, mlir::OpaqueProperties properties, mlir::RegionRange regions, SmallVectorImpl< Type > &results, llvm::function_ref< FIRRTLType(ValueRange, ArrayRef< NamedAttribute >, std::optional< Location >)> callback)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
Definition: comb.py:1