CIRCT  20.0.0git
HWArithOps.cpp
Go to the documentation of this file.
1 //===- HWArithOps.cpp - Implement the HW arithmetic 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 implement the HW arithmetic ops.
10 //
11 //===----------------------------------------------------------------------===//
12 
15 #include "mlir/IR/Builders.h"
16 #include "mlir/IR/PatternMatch.h"
17 #include "llvm/ADT/APSInt.h"
18 
19 using namespace circt;
20 using namespace hwarith;
21 
22 namespace circt {
23 namespace hwarith {
24 #include "circt/Dialect/HWArith/HWArithCanonicalizations.h.inc"
25 
26 }
27 } // namespace circt
28 
29 //===----------------------------------------------------------------------===//
30 // CastOp
31 //===----------------------------------------------------------------------===//
32 
33 LogicalResult CastOp::verify() {
34  auto inType = getIn().getType();
35  auto outType = getOut().getType();
36  bool isInSignless = !isHWArithIntegerType(inType);
37  bool isOutSignless = !isHWArithIntegerType(outType);
38 
39  if (isInSignless && isOutSignless)
40  return emitError("at least one type needs to carry sign semantics (ui/si)");
41 
42  if (isInSignless) {
43  unsigned inBitWidth = inType.getIntOrFloatBitWidth();
44  unsigned outBitWidth = outType.getIntOrFloatBitWidth();
45  if (inBitWidth < outBitWidth)
46  return emitError("bit extension is undefined for a signless type");
47  }
48 
49  return success();
50 }
51 
52 void CastOp::getCanonicalizationPatterns(RewritePatternSet &results,
53  MLIRContext *context) {
54  results.insert<EliminateCast>(context);
55 }
56 
57 //===----------------------------------------------------------------------===//
58 // ConstantOp
59 //===----------------------------------------------------------------------===//
60 
61 APSInt ConstantOp::getConstantValue() { return getRawValueAttr().getAPSInt(); }
62 
63 OpFoldResult ConstantOp::fold(FoldAdaptor adaptor) {
64  assert(adaptor.getOperands().empty() && "constant has no operands");
65  return getRawValueAttr();
66 }
67 
68 void ConstantOp::print(OpAsmPrinter &p) {
69  p << " ";
70  p.printAttribute(getRawValueAttr());
71  p.printOptionalAttrDict(getOperation()->getAttrs(),
72  /*elidedAttrs=*/{getRawValueAttrName()});
73 }
74 
75 ParseResult ConstantOp::parse(OpAsmParser &parser, OperationState &result) {
76  IntegerAttr valueAttr;
77 
78  if (parser.parseAttribute(valueAttr, getRawValueAttrName(result.name),
79  result.attributes) ||
80  parser.parseOptionalAttrDict(result.attributes))
81  return failure();
82 
83  result.addTypes(valueAttr.getType());
84  return success();
85 }
86 
87 //===----------------------------------------------------------------------===//
88 // AddOp
89 //===----------------------------------------------------------------------===//
90 
91 LogicalResult AddOp::inferReturnTypes(MLIRContext *context,
92  std::optional<Location> loc,
93  ValueRange operands, DictionaryAttr attrs,
94  mlir::OpaqueProperties properties,
95  mlir::RegionRange regions,
96  SmallVectorImpl<Type> &results) {
97  auto lhs = cast<IntegerType>(operands[0].getType());
98  auto rhs = cast<IntegerType>(operands[1].getType());
99  IntegerType::SignednessSemantics signedness;
100  unsigned resultWidth = inferAddResultType(signedness, lhs, rhs);
101 
102  results.push_back(IntegerType::get(context, resultWidth, signedness));
103  return success();
104 }
105 
106 //===----------------------------------------------------------------------===//
107 // SubOp
108 //===----------------------------------------------------------------------===//
109 
110 LogicalResult SubOp::inferReturnTypes(MLIRContext *context,
111  std::optional<Location> loc,
112  ValueRange operands, DictionaryAttr attrs,
113  mlir::OpaqueProperties properties,
114  mlir::RegionRange regions,
115  SmallVectorImpl<Type> &results) {
116  auto lhs = cast<IntegerType>(operands[0].getType());
117  auto rhs = cast<IntegerType>(operands[1].getType());
118  // The result type rules are identical to the ones for an addition
119  // With one exception: all results are signed!
120  IntegerType::SignednessSemantics signedness;
121  unsigned resultWidth = inferAddResultType(signedness, lhs, rhs);
122  signedness = IntegerType::Signed;
123 
124  results.push_back(IntegerType::get(context, resultWidth, signedness));
125  return success();
126 }
127 
128 //===----------------------------------------------------------------------===//
129 // MulOp
130 //===----------------------------------------------------------------------===//
131 
132 static IntegerType::SignednessSemantics
133 getSignedInheritedSignedness(IntegerType lhs, IntegerType rhs) {
134  // Signed operands are dominant and enforce a signed result
135  if (lhs.getSignedness() == rhs.getSignedness()) {
136  // the signedness is also identical to the operands
137  return lhs.getSignedness();
138  } else {
139  // For mixed signedness the result is always signed
140  return IntegerType::Signed;
141  }
142 }
143 
144 LogicalResult MulOp::inferReturnTypes(MLIRContext *context,
145  std::optional<Location> loc,
146  ValueRange operands, DictionaryAttr attrs,
147  mlir::OpaqueProperties properties,
148  mlir::RegionRange regions,
149  SmallVectorImpl<Type> &results) {
150  auto lhs = cast<IntegerType>(operands[0].getType());
151  auto rhs = cast<IntegerType>(operands[1].getType());
152  // the result width stays the same no matter the signedness
153  unsigned resultWidth = lhs.getWidth() + rhs.getWidth();
154  IntegerType::SignednessSemantics signedness =
156 
157  results.push_back(IntegerType::get(context, resultWidth, signedness));
158  return success();
159 }
160 
161 //===----------------------------------------------------------------------===//
162 // DivOp
163 //===----------------------------------------------------------------------===//
164 
165 LogicalResult DivOp::inferReturnTypes(MLIRContext *context,
166  std::optional<Location> loc,
167  ValueRange operands, DictionaryAttr attrs,
168  mlir::OpaqueProperties properties,
169  mlir::RegionRange regions,
170  SmallVectorImpl<Type> &results) {
171  auto lhs = cast<IntegerType>(operands[0].getType());
172  auto rhs = cast<IntegerType>(operands[1].getType());
173  // The result width is always at least as large as the bit width of lhs
174  unsigned resultWidth = lhs.getWidth();
175 
176  // if the divisor is signed, then the result width needs to be extended by 1
177  if (rhs.isSigned())
178  ++resultWidth;
179 
180  IntegerType::SignednessSemantics signedness =
182 
183  results.push_back(IntegerType::get(context, resultWidth, signedness));
184  return success();
185 }
186 
187 //===----------------------------------------------------------------------===//
188 // Utility
189 //===----------------------------------------------------------------------===//
190 
191 namespace circt {
192 namespace hwarith {
193 
194 unsigned inferAddResultType(IntegerType::SignednessSemantics &signedness,
195  IntegerType lhs, IntegerType rhs) {
196  // the result width is never less than max(w1, w2) + 1
197  unsigned resultWidth = std::max(lhs.getWidth(), rhs.getWidth()) + 1;
198 
199  if (lhs.getSignedness() == rhs.getSignedness()) {
200  // max(w1, w2) + 1 in case both operands use the same signedness
201  // the signedness is also identical to the operands
202  signedness = lhs.getSignedness();
203  } else {
204  // For mixed signedness the result is always signed
205  signedness = IntegerType::Signed;
206 
207  // Regarding the result width two case need to be considered:
208  if ((lhs.isUnsigned() && lhs.getWidth() >= rhs.getWidth()) ||
209  (rhs.isUnsigned() && rhs.getWidth() >= lhs.getWidth())) {
210  // 1. the unsigned width is >= the signed width,
211  // then the width needs to be increased by 1
212  ++resultWidth;
213  }
214  // 2. the unsigned width is < the signed width,
215  // then no further adjustment is needed
216  }
217  return resultWidth;
218 }
219 
220 static LogicalResult verifyBinOp(Operation *binOp) {
221  auto ops = binOp->getOperands();
222  if (ops.size() != 2)
223  return binOp->emitError() << "expected 2 operands but got " << ops.size();
224 
225  return success();
226 }
227 
228 } // namespace hwarith
229 } // namespace circt
230 
231 //===----------------------------------------------------------------------===//
232 // TableGen generated logic.
233 //===----------------------------------------------------------------------===//
234 
235 // Provide the autogenerated implementation guts for the Op classes.
236 #define GET_OP_CLASSES
237 #include "circt/Dialect/HWArith/HWArith.cpp.inc"
assert(baseType &&"element must be base type")
static std::optional< APInt > getConstantValue(Value value)
static IntegerType::SignednessSemantics getSignedInheritedSignedness(IntegerType lhs, IntegerType rhs)
Definition: HWArithOps.cpp:133
static LogicalResult verify(Value clock, bool eventExists, mlir::Location loc)
Definition: SVOps.cpp:2467
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
static LogicalResult verifyBinOp(Operation *binOp)
Definition: HWArithOps.cpp:220
bool isHWArithIntegerType(::mlir::Type type)
unsigned inferAddResultType(IntegerType::SignednessSemantics &signedness, IntegerType lhs, IntegerType rhs)
Definition: HWArithOps.cpp:194
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21