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 result = clampNegativeToZero(result);
88
89 return result;
90 }
91
92 mlir::Value createFixedToInteger(mlir::Value src,
93 const llvm::FixedPointSemantics &srcSema,
94 unsigned dstWidth, bool dstIsSigned) {
95 return convert(
96 src, srcSema,
97 llvm::FixedPointSemantics::GetIntegerSemantics(dstWidth, dstIsSigned),
98 /*dstIsInteger=*/true);
99 }
100
101 mlir::Value createIntegerToFixed(mlir::Value src, unsigned srcIsSigned,
102 const llvm::FixedPointSemantics &dstSema) {
103 unsigned srcWidth;
104 if (mlir::isa<cir::BoolType>(src.getType())) {
105 assert(!srcIsSigned);
106 srcWidth = 1;
107 src = builder.createBoolToInt(
108 src, cir::IntType::get(builder.getContext(), 1, /*isSigned=*/false));
109 } else {
110 srcWidth = mlir::cast<cir::IntType>(src.getType()).getWidth();
111 }
112 return convert(
113 src,
114 llvm::FixedPointSemantics::GetIntegerSemantics(srcWidth, srcIsSigned),
115 dstSema, /*dstIsInteger=*/false);
116 }
117
118 mlir::Value createFixedToFixed(mlir::Value src,
119 const llvm::FixedPointSemantics &srcSema,
120 const llvm::FixedPointSemantics &dstSema) {
121 return convert(src, srcSema, dstSema, /*dstIsInteger=*/false);
122 }
123
124 mlir::Value createAdd(mlir::Value lhs,
125 const llvm::FixedPointSemantics &lhsSema,
126 mlir::Value rhs,
127 const llvm::FixedPointSemantics &rhsSema) {
128 auto commonSema = getCommonBinopSemantic(lhsSema, rhsSema);
129
130 mlir::Value wideLhs = createFixedToFixed(lhs, lhsSema, commonSema);
131 mlir::Value wideRhs = createFixedToFixed(rhs, rhsSema, commonSema);
132
133 mlir::Value result =
134 commonSema.isSaturated()
135 ? createSaturatingAddSub(wideLhs, wideRhs, commonSema,
136 /*isSub=*/false)
137 : builder.createAdd(loc, wideLhs, wideRhs);
138
139 return createFixedToFixed(result, commonSema,
140 lhsSema.getCommonSemantics(rhsSema));
141 }
142
143 mlir::Value createSub(mlir::Value lhs,
144 const llvm::FixedPointSemantics &lhsSema,
145 mlir::Value rhs,
146 const llvm::FixedPointSemantics &rhsSema) {
147 auto commonSema = getCommonBinopSemantic(lhsSema, rhsSema);
148
149 mlir::Value wideLhs = createFixedToFixed(lhs, lhsSema, commonSema);
150 mlir::Value wideRhs = createFixedToFixed(rhs, rhsSema, commonSema);
151
152 mlir::Value result =
153 commonSema.isSaturated()
154 ? createSaturatingAddSub(wideLhs, wideRhs, commonSema,
155 /*isSub=*/true)
156 : builder.createSub(loc, wideLhs, wideRhs);
157
158 return createFixedToFixed(result, commonSema,
159 lhsSema.getCommonSemantics(rhsSema));
160 }
161
162 mlir::Value createMul(mlir::Value lhs,
163 const llvm::FixedPointSemantics &lhsSema,
164 mlir::Value rhs,
165 const llvm::FixedPointSemantics &rhsSema) {
166 auto commonSema = getCommonBinopSemantic(lhsSema, rhsSema);
167 bool useSigned = commonSema.isSigned() || commonSema.hasUnsignedPadding();
168
169 mlir::Value wideLhs = createFixedToFixed(lhs, lhsSema, commonSema);
170 mlir::Value wideRhs = createFixedToFixed(rhs, rhsSema, commonSema);
171
172 llvm::SmallString<13> intrinId;
173 cir::ConstantOp scale;
174
175 if (useSigned) {
176 intrinId = "smul.fix";
177 scale = builder.getSInt32(commonSema.getScale(), loc);
178 } else {
179 intrinId = "umul.fix";
180 scale = builder.getUInt32(commonSema.getScale(), loc);
181 }
182
183 if (commonSema.isSaturated())
184 intrinId += ".sat";
185
186 mlir::Value result =
187 builder.emitIntrinsicCallOp(loc, intrinId, wideLhs.getType(),
188 mlir::ValueRange{wideLhs, wideRhs, scale});
189
190 return createFixedToFixed(result, commonSema,
191 lhsSema.getCommonSemantics(rhsSema));
192 }
193
194 mlir::Value createDiv(mlir::Value lhs,
195 const llvm::FixedPointSemantics &lhsSema,
196 mlir::Value rhs,
197 const llvm::FixedPointSemantics &rhsSema) {
198 auto commonSema = getCommonBinopSemantic(lhsSema, rhsSema);
199 bool useSigned = commonSema.isSigned() || commonSema.hasUnsignedPadding();
200
201 mlir::Value wideLhs = createFixedToFixed(lhs, lhsSema, commonSema);
202 mlir::Value wideRhs = createFixedToFixed(rhs, rhsSema, commonSema);
203
204 llvm::SmallString<13> intrinId;
205 cir::ConstantOp scale;
206
207 if (useSigned) {
208 intrinId = "sdiv.fix";
209 scale = builder.getSInt32(commonSema.getScale(), loc);
210 } else {
211 intrinId = "udiv.fix";
212 scale = builder.getUInt32(commonSema.getScale(), loc);
213 }
214
215 if (commonSema.isSaturated())
216 intrinId += ".sat";
217
218 mlir::Value result =
219 builder.emitIntrinsicCallOp(loc, intrinId, wideLhs.getType(),
220 mlir::ValueRange{wideLhs, wideRhs, scale});
221
222 return createFixedToFixed(result, commonSema,
223 lhsSema.getCommonSemantics(rhsSema));
224 }
225
226 mlir::Value createCmp(mlir::Value lhs,
227 const llvm::FixedPointSemantics &lhsSema,
228 mlir::Value rhs,
229 const llvm::FixedPointSemantics &rhsSema,
230 cir::CmpOpKind kind) {
231 auto commonSema = getCommonBinopSemantic(lhsSema, rhsSema);
232
233 mlir::Value wideLhs = createFixedToFixed(lhs, lhsSema, commonSema);
234 mlir::Value wideRhs = createFixedToFixed(rhs, rhsSema, commonSema);
235
236 return builder.createCompare(loc, kind, wideLhs, wideRhs);
237 }
238
239 mlir::Value createShl(mlir::Value lhs,
240 const llvm::FixedPointSemantics &lhsSema,
241 mlir::Value rhs) {
242 mlir::Value result;
243 if (lhsSema.isSaturated()) {
244 // We have to cast the RHS to the matching int type, but we have to do so
245 // through unsigned so we can ensure we get zext.
246 auto rhsIntTy = mlir::cast<cir::IntType>(rhs.getType());
247 auto rhsUnsignedTy = cir::IntType::get(
248 builder.getContext(), rhsIntTy.getWidth(), /*isSigned=*/false);
249
250 mlir::Value rhsUnsigned =
251 builder.createCast(cir::CastKind::integral, rhs, rhsUnsignedTy);
252 mlir::Value rhsResized = builder.createCast(cir::CastKind::integral,
253 rhsUnsigned, lhs.getType());
254
255 bool useSigned = lhsSema.isSigned() || lhsSema.hasUnsignedPadding();
256 result = builder.emitIntrinsicCallOp(
257 loc, useSigned ? "sshl.sat" : "ushl.sat", lhs.getType(),
258 mlir::ValueRange{lhs, rhsResized});
259 } else {
260 result = builder.createShiftLeft(loc, lhs, rhs);
261 }
262
263 return result;
264 }
265
266 mlir::Value createShr(mlir::Value lhs, mlir::Value rhs) {
267 return builder.createShiftRight(loc, lhs, rhs);
268 }
269
270private:
271 mlir::Value convert(mlir::Value src, const llvm::FixedPointSemantics &srcSema,
272 const llvm::FixedPointSemantics &dstSema,
273 bool dstIsInteger) {
274 unsigned srcWidth = srcSema.getWidth();
275 unsigned dstWidth = dstSema.getWidth();
276 unsigned srcScale = srcSema.getScale();
277 unsigned dstScale = dstSema.getScale();
278 bool srcIsSigned = srcSema.isSigned();
279 bool dstIsSigned = dstSema.isSigned();
280
281 mlir::Value result = src;
282 unsigned resultWidth = srcWidth;
283
284 // Downscale.
285 if (dstScale < srcScale) {
286 // When converting to integers, we round towards zero. For negative
287 // numbers, right shifting rounds towards negative infinity. In this case,
288 // we can just round up before shifting.
289 if (dstIsInteger && srcIsSigned) {
290 mlir::Value zero = builder.getNullValue(result.getType(), loc);
291 mlir::Value isNegative =
292 builder.createCompare(loc, cir::CmpOpKind::lt, result, zero);
293 mlir::Value lowBits = builder.getConstAPInt(
294 loc, result.getType(),
295 llvm::APInt::getLowBitsSet(srcWidth, srcScale));
296 mlir::Value rounded = builder.createAdd(loc, result, lowBits);
297 result = builder.createSelect(loc, isNegative, rounded, result);
298 }
299 result = builder.createShiftRight(loc, result, srcScale - dstScale);
300 }
301
302 cir::IntType dstIntTy =
303 cir::IntType::get(builder.getContext(), dstWidth, dstSema.isSigned());
304
305 if (!dstSema.isSaturated()) {
306 // Resize.
307 result = builder.createIntCast(result, dstIntTy);
308 // Upscale.
309 if (dstScale > srcScale)
310 result = builder.createShiftLeft(loc, result, dstScale - srcScale);
311 } else {
312 // Adjust the number of fractional bits.
313 if (dstScale > srcScale) {
314 // Compare to DstWidth to prevent resizing twice.
315 resultWidth = std::max(srcWidth + dstScale - srcScale, dstWidth);
316 cir::IntType upscaledTy =
317 cir::IntType::get(builder.getContext(), resultWidth, srcIsSigned);
318 result = builder.createIntCast(result, upscaledTy);
319 result = builder.createShiftLeft(loc, result, dstScale - srcScale);
320 }
321
322 // Handle saturation.
323 bool fewerIntBits = dstSema.getIntegralBits() < srcSema.getIntegralBits();
324 if (fewerIntBits) {
325 mlir::Value max = builder.getConstAPInt(
326 loc, result.getType(),
327 llvm::APFixedPoint::getMax(dstSema).getValue().extOrTrunc(
328 resultWidth));
329
330 mlir::Value tooHigh =
331 builder.createCompare(loc, cir::CmpOpKind::gt, result, max);
332 result = builder.createSelect(loc, tooHigh, max, result);
333 }
334
335 // Cannot overflow min to dest type if src is unsigned since all fixed
336 // point types can cover the unsigned min of 0.
337 if (srcIsSigned && (fewerIntBits || !dstIsSigned)) {
338 mlir::Value min = builder.getConstAPInt(
339 loc, result.getType(),
340 llvm::APFixedPoint::getMin(dstSema).getValue().extOrTrunc(
341 resultWidth));
342 mlir::Value tooLow =
343 builder.createCompare(loc, cir::CmpOpKind::lt, result, min);
344 result = builder.createSelect(loc, tooLow, min, result);
345 }
346
347 // Resize the integer part to get the final destination size.
348 if (resultWidth != dstWidth)
349 result = builder.createIntCast(result, dstIntTy);
350 }
351 return result;
352 }
353
354 mlir::Type getAccommodatingFloatType(mlir::Type ty,
355 const llvm::FixedPointSemantics &sema) {
356 const llvm::fltSemantics *floatSema =
357 &mlir::cast<cir::FPTypeInterface>(ty).getFloatSemantics();
358 while (!sema.fitsInFloatSemantics(*floatSema))
359 floatSema = llvm::APFixedPoint::promoteFloatSemantics(floatSema);
360 cir::FPTypeInterface accommodating =
361 cir::getFloatingPointType(*floatSema, builder.getContext());
362 assert(accommodating && "no float type for semantics?");
363 return accommodating;
364 }
365 /// Get the common semantic for two semantics, with the added imposition that
366 /// saturated padded types retain the padding bit.
367 llvm::FixedPointSemantics
368 getCommonBinopSemantic(const llvm::FixedPointSemantics &lhsSema,
369 const llvm::FixedPointSemantics &rhsSema) {
370 auto c = lhsSema.getCommonSemantics(rhsSema);
371 bool bothPadded =
372 lhsSema.hasUnsignedPadding() && rhsSema.hasUnsignedPadding();
373 return llvm::FixedPointSemantics(
374 c.getWidth() + static_cast<unsigned>(bothPadded && c.isSaturated()),
375 c.getScale(), c.isSigned(), c.isSaturated(), bothPadded);
376 }
377
378 /// Padded unsigned semantics compute as signed, as llvm::FixedPointBuilder
379 /// does: the padding bit keeps values at or below the signed maximum. CIR
380 /// integers carry signedness, so operands are cast to this type and back.
381 cir::IntType getOpIntType(const llvm::FixedPointSemantics &sema) {
382 return cir::IntType::get(builder.getContext(), sema.getWidth(),
383 sema.isSigned() || sema.hasUnsignedPadding());
384 }
385
386 /// A padded unsigned result computed as signed may have gone negative.
387 mlir::Value clampNegativeToZero(mlir::Value value) {
388 mlir::Value zero = builder.getNullValue(value.getType(), loc);
389 mlir::Value isNeg =
390 builder.createCompare(loc, cir::CmpOpKind::lt, value, zero);
391 return builder.createSelect(loc, isNeg, zero, value);
392 }
393
394 mlir::Value createSaturatingAddSub(mlir::Value lhs, mlir::Value rhs,
395 const llvm::FixedPointSemantics &sema,
396 bool isSub) {
397 cir::IntType opTy = getOpIntType(sema);
398 mlir::Value opLhs = builder.createIntCast(lhs, opTy);
399 mlir::Value opRhs = builder.createIntCast(rhs, opTy);
400 mlir::Value result = isSub ? builder.createSubSat(loc, opLhs, opRhs)
401 : builder.createAddSat(loc, opLhs, opRhs);
402 if (isSub && sema.hasUnsignedPadding())
403 result = clampNegativeToZero(result);
404 return builder.createIntCast(result, lhs.getType());
405 }
406
407 CIRGenBuilderTy &builder;
408 mlir::Location loc;
409};
410} // 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 createAdd(mlir::Location loc, mlir::Value lhs, mlir::Value rhs, OverflowFlags flags=OverflowFlags::None)
mlir::Value getConstAPInt(mlir::Location loc, mlir::Type typ, const llvm::APInt &val)
cir::ConstantOp getNullValue(mlir::Type ty, mlir::Location loc)
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:43
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