clang 24.0.0git
CIRGenFixedPointBuilder.h
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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// Helper for Fixed point code generation.
10//
11//===----------------------------------------------------------------------===//
12
13#include "CIRGenBuilder.h"
14
15#include "mlir/IR/Value.h"
16
17namespace clang::CIRGen {
18// A CIR-specific generating version of the llvm::FixedPointBuilder.
20 CIRGenFixedPointBuilder(CIRGenBuilderTy &builder, mlir::Location loc)
21 : builder(builder), loc(loc) {}
22
23 mlir::Value createFixedToFloating(mlir::Value src,
24 const llvm::FixedPointSemantics &srcSema,
25 mlir::Type dstTy) {
26 mlir::Type opTy = getAccommodatingFloatType(dstTy, srcSema);
27 // Convert the raw fixed-point value directly to floating point. If the
28 // value is too large to fit, it will be rounded, not truncated.
29 mlir::Value result =
30 builder.createCast(loc, cir::CastKind::int_to_float, src, opTy);
31 // Rescale the integral-in-floating point by the scaling factor. This is
32 // lossless, except for overflow to infinity which is unlikely.
33 const llvm::fltSemantics &opSemantics =
34 mlir::cast<cir::FPTypeInterface>(opTy).getFloatSemantics();
35 llvm::APFloat scaleVal(
36 std::pow(2.0, -static_cast<int>(srcSema.getScale())));
37 bool losesInfo;
38 scaleVal.convert(opSemantics, llvm::APFloat::rmNearestTiesToEven,
39 &losesInfo);
40 (void)losesInfo;
41
42 cir::ConstantOp fpConst = builder.getConstFP(loc, opTy, scaleVal);
43 result = builder.createFMul(loc, result, fpConst);
44
45 if (opTy != dstTy)
46 result = builder.createFloatingCast(result, dstTy);
47 return result;
48 }
49
50 mlir::Value createFloatingToFixed(mlir::Value src,
51 const llvm::FixedPointSemantics &dstSema) {
52
53 bool useSigned = dstSema.isSigned() || dstSema.hasUnsignedPadding();
54 mlir::Value result = src;
55 mlir::Type opTy = getAccommodatingFloatType(src.getType(), dstSema);
56
57 if (opTy != src.getType())
58 result = builder.createFloatingCast(result, opTy);
59
60 // Rescale the floating point value so that its significant bits (for the
61 // purposes of the conversion) are in the integral range.
62 const llvm::fltSemantics &opSemantics =
63 mlir::cast<cir::FPTypeInterface>(opTy).getFloatSemantics();
64 llvm::APFloat scaleVal(std::pow(2.0, dstSema.getScale()));
65 bool losesInfo;
66 scaleVal.convert(opSemantics, llvm::APFloat::rmNearestTiesToEven,
67 &losesInfo);
68 (void)losesInfo;
69
70 cir::ConstantOp fpConst = builder.getConstFP(loc, opTy, scaleVal);
71 result = builder.createFMul(loc, result, fpConst);
72
73 cir::IntType resultTy = cir::IntType::get(
74 builder.getContext(), dstSema.getWidth(), dstSema.isSigned());
75
76 if (dstSema.isSaturated()) {
77 result = builder.emitIntrinsicCallOp(
78 loc, useSigned ? "fptosi.sat" : "fptoui.sat", resultTy, result);
79 } else {
80 result = builder.createCast(loc, cir::CastKind::float_to_int, result,
81 resultTy);
82 }
83
84 // When saturating unsigned-with-padding using signed operations, we may
85 // get negative values. Emit an extra clamp to zero.
86 if (dstSema.isSaturated() && dstSema.hasUnsignedPadding()) {
87 mlir::Value zero = builder.getNullValue(result.getType(), loc);
88 mlir::Value isNeg =
89 builder.createCompare(loc, cir::CmpOpKind::lt, result, zero);
90 result = builder.createSelect(loc, isNeg, zero, result);
91 }
92
93 return result;
94 }
95
96 mlir::Value createFixedToInteger(mlir::Value src,
97 const llvm::FixedPointSemantics &srcSema,
98 unsigned dstWidth, bool dstIsSigned) {
99 return convert(
100 src, srcSema,
101 llvm::FixedPointSemantics::GetIntegerSemantics(dstWidth, dstIsSigned),
102 /*dstIsInteger=*/true);
103 }
104
105 mlir::Value createIntegerToFixed(mlir::Value src, unsigned srcIsSigned,
106 const llvm::FixedPointSemantics &dstSema) {
107 unsigned srcWidth;
108 if (mlir::isa<cir::BoolType>(src.getType())) {
109 assert(!srcIsSigned);
110 srcWidth = 1;
111 src = builder.createBoolToInt(
112 src, cir::IntType::get(builder.getContext(), 1, /*isSigned=*/false));
113 } else {
114 srcWidth = mlir::cast<cir::IntType>(src.getType()).getWidth();
115 }
116 return convert(
117 src,
118 llvm::FixedPointSemantics::GetIntegerSemantics(srcWidth, srcIsSigned),
119 dstSema, /*dstIsInteger=*/false);
120 }
121
122 mlir::Value createFixedToFixed(mlir::Value src,
123 const llvm::FixedPointSemantics &srcSema,
124 const llvm::FixedPointSemantics &dstSema) {
125 return convert(src, srcSema, dstSema, /*dstIsInteger=*/false);
126 }
127
128 mlir::Value createAdd(mlir::Value lhs,
129 const llvm::FixedPointSemantics &lhsSema,
130 mlir::Value rhs,
131 const llvm::FixedPointSemantics &rhsSema) {
132 auto commonSema = getCommonBinopSemantic(lhsSema, rhsSema);
133
134 mlir::Value wideLhs = createFixedToFixed(lhs, lhsSema, commonSema);
135 mlir::Value wideRhs = createFixedToFixed(rhs, rhsSema, commonSema);
136
137 mlir::Value result;
138 if (commonSema.isSaturated()) {
139 result = builder.createAdd(loc, wideLhs, wideRhs,
141 } else {
142 result = builder.createAdd(loc, wideLhs, wideRhs);
143 }
144
145 return createFixedToFixed(result, commonSema,
146 lhsSema.getCommonSemantics(rhsSema));
147 }
148
149 mlir::Value createSub(mlir::Value lhs,
150 const llvm::FixedPointSemantics &lhsSema,
151 mlir::Value rhs,
152 const llvm::FixedPointSemantics &rhsSema) {
153 auto commonSema = getCommonBinopSemantic(lhsSema, rhsSema);
154
155 mlir::Value wideLhs = createFixedToFixed(lhs, lhsSema, commonSema);
156 mlir::Value wideRhs = createFixedToFixed(rhs, rhsSema, commonSema);
157
158 mlir::Value result;
159 if (commonSema.isSaturated()) {
160 result = builder.createSub(loc, wideLhs, wideRhs,
162 } else {
163 result = builder.createSub(loc, wideLhs, wideRhs);
164 }
165
166 // Subtraction can end up below 0 for padded unsigned operations, so emit
167 // an extra clamp in that case.
168 if (commonSema.isSaturated() && commonSema.hasUnsignedPadding()) {
169 mlir::Value zero = builder.getNullValue(result.getType(), loc);
170 mlir::Value ltZero =
171 builder.createCompare(loc, cir::CmpOpKind::lt, result, zero);
172 result = builder.createSelect(loc, ltZero, zero, result);
173 }
174
175 return createFixedToFixed(result, commonSema,
176 lhsSema.getCommonSemantics(rhsSema));
177 }
178
179 mlir::Value createMul(mlir::Value lhs,
180 const llvm::FixedPointSemantics &lhsSema,
181 mlir::Value rhs,
182 const llvm::FixedPointSemantics &rhsSema) {
183 auto commonSema = getCommonBinopSemantic(lhsSema, rhsSema);
184 bool useSigned = commonSema.isSigned() || commonSema.hasUnsignedPadding();
185
186 mlir::Value wideLhs = createFixedToFixed(lhs, lhsSema, commonSema);
187 mlir::Value wideRhs = createFixedToFixed(rhs, rhsSema, commonSema);
188
189 llvm::SmallString<13> intrinId;
190 cir::ConstantOp scale;
191
192 if (useSigned) {
193 intrinId = "smul.fix";
194 scale = builder.getSInt32(commonSema.getScale(), loc);
195 } else {
196 intrinId = "umul.fix";
197 scale = builder.getUInt32(commonSema.getScale(), loc);
198 }
199
200 if (commonSema.isSaturated())
201 intrinId += ".sat";
202
203 mlir::Value result =
204 builder.emitIntrinsicCallOp(loc, intrinId, wideLhs.getType(),
205 mlir::ValueRange{wideLhs, wideRhs, scale});
206
207 return createFixedToFixed(result, commonSema,
208 lhsSema.getCommonSemantics(rhsSema));
209 }
210
211 mlir::Value createDiv(mlir::Value lhs,
212 const llvm::FixedPointSemantics &lhsSema,
213 mlir::Value rhs,
214 const llvm::FixedPointSemantics &rhsSema) {
215 auto commonSema = getCommonBinopSemantic(lhsSema, rhsSema);
216 bool useSigned = commonSema.isSigned() || commonSema.hasUnsignedPadding();
217
218 mlir::Value wideLhs = createFixedToFixed(lhs, lhsSema, commonSema);
219 mlir::Value wideRhs = createFixedToFixed(rhs, rhsSema, commonSema);
220
221 llvm::SmallString<13> intrinId;
222 cir::ConstantOp scale;
223
224 if (useSigned) {
225 intrinId = "sdiv.fix";
226 scale = builder.getSInt32(commonSema.getScale(), loc);
227 } else {
228 intrinId = "udiv.fix";
229 scale = builder.getUInt32(commonSema.getScale(), loc);
230 }
231
232 if (commonSema.isSaturated())
233 intrinId += ".sat";
234
235 mlir::Value result =
236 builder.emitIntrinsicCallOp(loc, intrinId, wideLhs.getType(),
237 mlir::ValueRange{wideLhs, wideRhs, scale});
238
239 return createFixedToFixed(result, commonSema,
240 lhsSema.getCommonSemantics(rhsSema));
241 }
242
243 mlir::Value createCmp(mlir::Value lhs,
244 const llvm::FixedPointSemantics &lhsSema,
245 mlir::Value rhs,
246 const llvm::FixedPointSemantics &rhsSema,
247 cir::CmpOpKind kind) {
248 auto commonSema = getCommonBinopSemantic(lhsSema, rhsSema);
249
250 mlir::Value wideLhs = createFixedToFixed(lhs, lhsSema, commonSema);
251 mlir::Value wideRhs = createFixedToFixed(rhs, rhsSema, commonSema);
252
253 return builder.createCompare(loc, kind, wideLhs, wideRhs);
254 }
255
256 mlir::Value createShl(mlir::Value lhs,
257 const llvm::FixedPointSemantics &lhsSema,
258 mlir::Value rhs) {
259 mlir::Value result;
260 if (lhsSema.isSaturated()) {
261 // We have to cast the RHS to the matching int type, but we have to do so
262 // through unsigned so we can ensure we get zext.
263 auto rhsIntTy = mlir::cast<cir::IntType>(rhs.getType());
264 auto rhsUnsignedTy = cir::IntType::get(
265 builder.getContext(), rhsIntTy.getWidth(), /*isSigned=*/false);
266
267 mlir::Value rhsUnsigned =
268 builder.createCast(cir::CastKind::integral, rhs, rhsUnsignedTy);
269 mlir::Value rhsResized = builder.createCast(cir::CastKind::integral,
270 rhsUnsigned, lhs.getType());
271
272 bool useSigned = lhsSema.isSigned() || lhsSema.hasUnsignedPadding();
273 result = builder.emitIntrinsicCallOp(
274 loc, useSigned ? "sshl.sat" : "ushl.sat", lhs.getType(),
275 mlir::ValueRange{lhs, rhsResized});
276 } else {
277 result = builder.createShiftLeft(loc, lhs, rhs);
278 }
279
280 return result;
281 }
282
283 mlir::Value createShr(mlir::Value lhs, mlir::Value rhs) {
284 return builder.createShiftRight(loc, lhs, rhs);
285 }
286
287private:
288 mlir::Value convert(mlir::Value src, const llvm::FixedPointSemantics &srcSema,
289 const llvm::FixedPointSemantics &dstSema,
290 bool dstIsInteger) {
291 unsigned srcWidth = srcSema.getWidth();
292 unsigned dstWidth = dstSema.getWidth();
293 unsigned srcScale = srcSema.getScale();
294 unsigned dstScale = dstSema.getScale();
295 bool srcIsSigned = srcSema.isSigned();
296 bool dstIsSigned = dstSema.isSigned();
297
298 mlir::Value result = src;
299 unsigned resultWidth = srcWidth;
300
301 // Downscale.
302 if (dstScale < srcScale) {
303 // When converting to integers, we round towards zero. For negative
304 // numbers, right shifting rounds towards negative infinity. In this case,
305 // we can just round up before shifting.
306 if (dstIsInteger && srcIsSigned) {
307 mlir::Value zero = builder.getNullValue(result.getType(), loc);
308 mlir::Value isNegative =
309 builder.createCompare(loc, cir::CmpOpKind::lt, result, zero);
310 mlir::Value lowBits = builder.getConstAPInt(
311 loc, result.getType(),
312 llvm::APInt::getLowBitsSet(srcWidth, srcScale));
313 mlir::Value rounded = builder.createAdd(loc, result, lowBits);
314 result = builder.createSelect(loc, isNegative, rounded, result);
315 }
316 result = builder.createShiftRight(loc, result, srcScale - dstScale);
317 }
318
319 cir::IntType dstIntTy =
320 cir::IntType::get(builder.getContext(), dstWidth, dstSema.isSigned());
321
322 if (!dstSema.isSaturated()) {
323 // Resize.
324 result = builder.createIntCast(result, dstIntTy);
325 // Upscale.
326 if (dstScale > srcScale)
327 result = builder.createShiftLeft(loc, result, dstScale - srcScale);
328 } else {
329 // Adjust the number of fractional bits.
330 if (dstScale > srcScale) {
331 // Compare to DstWidth to prevent resizing twice.
332 resultWidth = std::max(srcWidth + dstScale - srcScale, dstWidth);
333 cir::IntType upscaledTy =
334 cir::IntType::get(builder.getContext(), resultWidth, srcIsSigned);
335 result = builder.createIntCast(result, upscaledTy);
336 result = builder.createShiftLeft(loc, result, dstScale - srcScale);
337 }
338
339 // Handle saturation.
340 bool fewerIntBits = dstSema.getIntegralBits() < srcSema.getIntegralBits();
341 if (fewerIntBits) {
342 mlir::Value max = builder.getConstAPInt(
343 loc, result.getType(),
344 llvm::APFixedPoint::getMax(dstSema).getValue().extOrTrunc(
345 resultWidth));
346
347 mlir::Value tooHigh =
348 builder.createCompare(loc, cir::CmpOpKind::gt, result, max);
349 result = builder.createSelect(loc, tooHigh, max, result);
350 }
351
352 // Cannot overflow min to dest type if src is unsigned since all fixed
353 // point types can cover the unsigned min of 0.
354 if (srcIsSigned && (fewerIntBits || !dstIsSigned)) {
355 mlir::Value min = builder.getConstAPInt(
356 loc, result.getType(),
357 llvm::APFixedPoint::getMin(dstSema).getValue().extOrTrunc(
358 resultWidth));
359 mlir::Value tooLow =
360 builder.createCompare(loc, cir::CmpOpKind::lt, result, min);
361 result = builder.createSelect(loc, tooLow, min, result);
362 }
363
364 // Resize the integer part to get the final destination size.
365 if (resultWidth != dstWidth)
366 result = builder.createIntCast(result, dstIntTy);
367 }
368 return result;
369 }
370
371 mlir::Type getAccommodatingFloatType(mlir::Type ty,
372 const llvm::FixedPointSemantics &sema) {
373 const llvm::fltSemantics *floatSema =
374 &mlir::cast<cir::FPTypeInterface>(ty).getFloatSemantics();
375 while (!sema.fitsInFloatSemantics(*floatSema))
376 floatSema = llvm::APFixedPoint::promoteFloatSemantics(floatSema);
377 cir::FPTypeInterface accommodating =
378 cir::getFloatingPointType(*floatSema, builder.getContext());
379 assert(accommodating && "no float type for semantics?");
380 return accommodating;
381 }
382 /// Get the common semantic for two semantics, with the added imposition that
383 /// saturated padded types retain the padding bit.
384 llvm::FixedPointSemantics
385 getCommonBinopSemantic(const llvm::FixedPointSemantics &lhsSema,
386 const llvm::FixedPointSemantics &rhsSema) {
387 auto c = lhsSema.getCommonSemantics(rhsSema);
388 bool bothPadded =
389 lhsSema.hasUnsignedPadding() && rhsSema.hasUnsignedPadding();
390 return llvm::FixedPointSemantics(
391 c.getWidth() + static_cast<unsigned>(bothPadded && c.isSaturated()),
392 c.getScale(), c.isSigned(), c.isSaturated(), bothPadded);
393 }
394
395 CIRGenBuilderTy &builder;
396 mlir::Location loc;
397};
398} // namespace clang::CIRGen
static bool isNegative(SValBuilder &SVB, ProgramStateRef State, NonLoc Value)
*collection of selector each with an associated kind and an ordered *collection of selectors A selector has a kind
__DEVICE__ int min(int __a, int __b)
__DEVICE__ int max(int __a, int __b)
mlir::Value getConstAPInt(mlir::Location loc, mlir::Type typ, const llvm::APInt &val)
cir::ConstantOp getNullValue(mlir::Type ty, mlir::Location loc)
mlir::Value createAdd(mlir::Location loc, mlir::Value lhs, mlir::Value rhs, OverflowBehavior ob=OverflowBehavior::None)
mlir::Value createShiftLeft(mlir::Location loc, mlir::Value lhs, unsigned bits)
mlir::Value createIntCast(mlir::Value src, mlir::Type newTy)
cir::CmpOp createCompare(mlir::Location loc, cir::CmpOpKind kind, mlir::Value lhs, mlir::Value rhs)
mlir::Value createSelect(mlir::Location loc, mlir::Value condition, mlir::Value trueValue, mlir::Value falseValue)
mlir::Value createShiftRight(mlir::Location loc, mlir::Value lhs, unsigned bits)
cir::FPTypeInterface getFloatingPointType(const llvm::fltSemantics &sem, mlir::MLIRContext *ctx)
Returns the CIR floating-point type for the given semantics, or a null type if CIR has no type for it...
Definition CIRTypes.cpp:42
CIRGenFixedPointBuilder(CIRGenBuilderTy &builder, mlir::Location loc)
mlir::Value createFixedToFloating(mlir::Value src, const llvm::FixedPointSemantics &srcSema, mlir::Type dstTy)
mlir::Value createFloatingToFixed(mlir::Value src, const llvm::FixedPointSemantics &dstSema)
mlir::Value createFixedToFixed(mlir::Value src, const llvm::FixedPointSemantics &srcSema, const llvm::FixedPointSemantics &dstSema)
mlir::Value createAdd(mlir::Value lhs, const llvm::FixedPointSemantics &lhsSema, mlir::Value rhs, const llvm::FixedPointSemantics &rhsSema)
mlir::Value createShl(mlir::Value lhs, const llvm::FixedPointSemantics &lhsSema, mlir::Value rhs)
mlir::Value createDiv(mlir::Value lhs, const llvm::FixedPointSemantics &lhsSema, mlir::Value rhs, const llvm::FixedPointSemantics &rhsSema)
mlir::Value createMul(mlir::Value lhs, const llvm::FixedPointSemantics &lhsSema, mlir::Value rhs, const llvm::FixedPointSemantics &rhsSema)
mlir::Value createFixedToInteger(mlir::Value src, const llvm::FixedPointSemantics &srcSema, unsigned dstWidth, bool dstIsSigned)
mlir::Value createCmp(mlir::Value lhs, const llvm::FixedPointSemantics &lhsSema, mlir::Value rhs, const llvm::FixedPointSemantics &rhsSema, cir::CmpOpKind kind)
mlir::Value createIntegerToFixed(mlir::Value src, unsigned srcIsSigned, const llvm::FixedPointSemantics &dstSema)
mlir::Value createShr(mlir::Value lhs, mlir::Value rhs)
mlir::Value createSub(mlir::Value lhs, const llvm::FixedPointSemantics &lhsSema, mlir::Value rhs, const llvm::FixedPointSemantics &rhsSema)
#define pow(__x, __y)
Definition tgmath.h:490