• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===- llvm/FixedPointBuilder.h - Builder for fixed-point ops ---*- C++ -*-===//
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 defines the FixedPointBuilder class, which is used as a convenient
10 // way to lower fixed-point arithmetic operations to LLVM IR.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef LLVM_IR_FIXEDPOINTBUILDER_H
15 #define LLVM_IR_FIXEDPOINTBUILDER_H
16 
17 #include "llvm/ADT/APFixedPoint.h"
18 #include "llvm/IR/Constant.h"
19 #include "llvm/IR/Constants.h"
20 #include "llvm/IR/IRBuilder.h"
21 #include "llvm/IR/InstrTypes.h"
22 #include "llvm/IR/Instruction.h"
23 #include "llvm/IR/IntrinsicInst.h"
24 #include "llvm/IR/Intrinsics.h"
25 #include "llvm/IR/Type.h"
26 #include "llvm/IR/Value.h"
27 
28 namespace llvm {
29 
30 template <class IRBuilderTy> class FixedPointBuilder {
31   IRBuilderTy &B;
32 
Convert(Value * Src,const FixedPointSemantics & SrcSema,const FixedPointSemantics & DstSema,bool DstIsInteger)33   Value *Convert(Value *Src, const FixedPointSemantics &SrcSema,
34                  const FixedPointSemantics &DstSema, bool DstIsInteger) {
35     unsigned SrcWidth = SrcSema.getWidth();
36     unsigned DstWidth = DstSema.getWidth();
37     unsigned SrcScale = SrcSema.getScale();
38     unsigned DstScale = DstSema.getScale();
39     bool SrcIsSigned = SrcSema.isSigned();
40     bool DstIsSigned = DstSema.isSigned();
41 
42     Type *DstIntTy = B.getIntNTy(DstWidth);
43 
44     Value *Result = Src;
45     unsigned ResultWidth = SrcWidth;
46 
47     // Downscale.
48     if (DstScale < SrcScale) {
49       // When converting to integers, we round towards zero. For negative
50       // numbers, right shifting rounds towards negative infinity. In this case,
51       // we can just round up before shifting.
52       if (DstIsInteger && SrcIsSigned) {
53         Value *Zero = Constant::getNullValue(Result->getType());
54         Value *IsNegative = B.CreateICmpSLT(Result, Zero);
55         Value *LowBits = ConstantInt::get(
56             B.getContext(), APInt::getLowBitsSet(ResultWidth, SrcScale));
57         Value *Rounded = B.CreateAdd(Result, LowBits);
58         Result = B.CreateSelect(IsNegative, Rounded, Result);
59       }
60 
61       Result = SrcIsSigned
62                    ? B.CreateAShr(Result, SrcScale - DstScale, "downscale")
63                    : B.CreateLShr(Result, SrcScale - DstScale, "downscale");
64     }
65 
66     if (!DstSema.isSaturated()) {
67       // Resize.
68       Result = B.CreateIntCast(Result, DstIntTy, SrcIsSigned, "resize");
69 
70       // Upscale.
71       if (DstScale > SrcScale)
72         Result = B.CreateShl(Result, DstScale - SrcScale, "upscale");
73     } else {
74       // Adjust the number of fractional bits.
75       if (DstScale > SrcScale) {
76         // Compare to DstWidth to prevent resizing twice.
77         ResultWidth = std::max(SrcWidth + DstScale - SrcScale, DstWidth);
78         Type *UpscaledTy = B.getIntNTy(ResultWidth);
79         Result = B.CreateIntCast(Result, UpscaledTy, SrcIsSigned, "resize");
80         Result = B.CreateShl(Result, DstScale - SrcScale, "upscale");
81       }
82 
83       // Handle saturation.
84       bool LessIntBits = DstSema.getIntegralBits() < SrcSema.getIntegralBits();
85       if (LessIntBits) {
86         Value *Max = ConstantInt::get(
87             B.getContext(),
88             APFixedPoint::getMax(DstSema).getValue().extOrTrunc(ResultWidth));
89         Value *TooHigh = SrcIsSigned ? B.CreateICmpSGT(Result, Max)
90                                      : B.CreateICmpUGT(Result, Max);
91         Result = B.CreateSelect(TooHigh, Max, Result, "satmax");
92       }
93       // Cannot overflow min to dest type if src is unsigned since all fixed
94       // point types can cover the unsigned min of 0.
95       if (SrcIsSigned && (LessIntBits || !DstIsSigned)) {
96         Value *Min = ConstantInt::get(
97             B.getContext(),
98             APFixedPoint::getMin(DstSema).getValue().extOrTrunc(ResultWidth));
99         Value *TooLow = B.CreateICmpSLT(Result, Min);
100         Result = B.CreateSelect(TooLow, Min, Result, "satmin");
101       }
102 
103       // Resize the integer part to get the final destination size.
104       if (ResultWidth != DstWidth)
105         Result = B.CreateIntCast(Result, DstIntTy, SrcIsSigned, "resize");
106     }
107     return Result;
108   }
109 
110   /// Get the common semantic for two semantics, with the added imposition that
111   /// saturated padded types retain the padding bit.
112   FixedPointSemantics
getCommonBinopSemantic(const FixedPointSemantics & LHSSema,const FixedPointSemantics & RHSSema)113   getCommonBinopSemantic(const FixedPointSemantics &LHSSema,
114                          const FixedPointSemantics &RHSSema) {
115     auto C = LHSSema.getCommonSemantics(RHSSema);
116     bool BothPadded =
117         LHSSema.hasUnsignedPadding() && RHSSema.hasUnsignedPadding();
118     return FixedPointSemantics(
119         C.getWidth() + (unsigned)(BothPadded && C.isSaturated()), C.getScale(),
120         C.isSigned(), C.isSaturated(), BothPadded);
121   }
122 
123 public:
FixedPointBuilder(IRBuilderTy & Builder)124   FixedPointBuilder(IRBuilderTy &Builder) : B(Builder) {}
125 
126   /// Convert an integer value representing a fixed-point number from one
127   /// fixed-point semantic to another fixed-point semantic.
128   /// \p Src     - The source value
129   /// \p SrcSema - The fixed-point semantic of the source value
130   /// \p DstSema - The resulting fixed-point semantic
CreateFixedToFixed(Value * Src,const FixedPointSemantics & SrcSema,const FixedPointSemantics & DstSema)131   Value *CreateFixedToFixed(Value *Src, const FixedPointSemantics &SrcSema,
132                             const FixedPointSemantics &DstSema) {
133     return Convert(Src, SrcSema, DstSema, false);
134   }
135 
136   /// Convert an integer value representing a fixed-point number to an integer
137   /// with the given bit width and signedness.
138   /// \p Src         - The source value
139   /// \p SrcSema     - The fixed-point semantic of the source value
140   /// \p DstWidth    - The bit width of the result value
141   /// \p DstIsSigned - The signedness of the result value
CreateFixedToInteger(Value * Src,const FixedPointSemantics & SrcSema,unsigned DstWidth,bool DstIsSigned)142   Value *CreateFixedToInteger(Value *Src, const FixedPointSemantics &SrcSema,
143                               unsigned DstWidth, bool DstIsSigned) {
144     return Convert(
145         Src, SrcSema,
146         FixedPointSemantics::GetIntegerSemantics(DstWidth, DstIsSigned), true);
147   }
148 
149   /// Convert an integer value with the given signedness to an integer value
150   /// representing the given fixed-point semantic.
151   /// \p Src         - The source value
152   /// \p SrcIsSigned - The signedness of the source value
153   /// \p DstSema     - The resulting fixed-point semantic
CreateIntegerToFixed(Value * Src,unsigned SrcIsSigned,const FixedPointSemantics & DstSema)154   Value *CreateIntegerToFixed(Value *Src, unsigned SrcIsSigned,
155                               const FixedPointSemantics &DstSema) {
156     return Convert(Src,
157                    FixedPointSemantics::GetIntegerSemantics(
158                        Src->getType()->getScalarSizeInBits(), SrcIsSigned),
159                    DstSema, false);
160   }
161 
162   /// Add two fixed-point values and return the result in their common semantic.
163   /// \p LHS     - The left hand side
164   /// \p LHSSema - The semantic of the left hand side
165   /// \p RHS     - The right hand side
166   /// \p RHSSema - The semantic of the right hand side
CreateAdd(Value * LHS,const FixedPointSemantics & LHSSema,Value * RHS,const FixedPointSemantics & RHSSema)167   Value *CreateAdd(Value *LHS, const FixedPointSemantics &LHSSema,
168                    Value *RHS, const FixedPointSemantics &RHSSema) {
169     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
170     bool UseSigned = CommonSema.isSigned() || CommonSema.hasUnsignedPadding();
171 
172     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
173     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
174 
175     Value *Result;
176     if (CommonSema.isSaturated()) {
177       Intrinsic::ID IID = UseSigned ? Intrinsic::sadd_sat : Intrinsic::uadd_sat;
178       Result = B.CreateBinaryIntrinsic(IID, WideLHS, WideRHS);
179     } else {
180       Result = B.CreateAdd(WideLHS, WideRHS);
181     }
182 
183     return CreateFixedToFixed(Result, CommonSema,
184                               LHSSema.getCommonSemantics(RHSSema));
185   }
186 
187   /// Subtract two fixed-point values and return the result in their common
188   /// semantic.
189   /// \p LHS     - The left hand side
190   /// \p LHSSema - The semantic of the left hand side
191   /// \p RHS     - The right hand side
192   /// \p RHSSema - The semantic of the right hand side
CreateSub(Value * LHS,const FixedPointSemantics & LHSSema,Value * RHS,const FixedPointSemantics & RHSSema)193   Value *CreateSub(Value *LHS, const FixedPointSemantics &LHSSema,
194                    Value *RHS, const FixedPointSemantics &RHSSema) {
195     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
196     bool UseSigned = CommonSema.isSigned() || CommonSema.hasUnsignedPadding();
197 
198     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
199     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
200 
201     Value *Result;
202     if (CommonSema.isSaturated()) {
203       Intrinsic::ID IID = UseSigned ? Intrinsic::ssub_sat : Intrinsic::usub_sat;
204       Result = B.CreateBinaryIntrinsic(IID, WideLHS, WideRHS);
205     } else {
206       Result = B.CreateSub(WideLHS, WideRHS);
207     }
208 
209     // Subtraction can end up below 0 for padded unsigned operations, so emit
210     // an extra clamp in that case.
211     if (CommonSema.isSaturated() && CommonSema.hasUnsignedPadding()) {
212       Constant *Zero = Constant::getNullValue(Result->getType());
213       Result =
214           B.CreateSelect(B.CreateICmpSLT(Result, Zero), Zero, Result, "satmin");
215     }
216 
217     return CreateFixedToFixed(Result, CommonSema,
218                               LHSSema.getCommonSemantics(RHSSema));
219   }
220 
221   /// Multiply two fixed-point values and return the result in their common
222   /// semantic.
223   /// \p LHS     - The left hand side
224   /// \p LHSSema - The semantic of the left hand side
225   /// \p RHS     - The right hand side
226   /// \p RHSSema - The semantic of the right hand side
CreateMul(Value * LHS,const FixedPointSemantics & LHSSema,Value * RHS,const FixedPointSemantics & RHSSema)227   Value *CreateMul(Value *LHS, const FixedPointSemantics &LHSSema,
228                    Value *RHS, const FixedPointSemantics &RHSSema) {
229     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
230     bool UseSigned = CommonSema.isSigned() || CommonSema.hasUnsignedPadding();
231 
232     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
233     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
234 
235     Intrinsic::ID IID;
236     if (CommonSema.isSaturated()) {
237       IID = UseSigned ? Intrinsic::smul_fix_sat : Intrinsic::umul_fix_sat;
238     } else {
239       IID = UseSigned ? Intrinsic::smul_fix : Intrinsic::umul_fix;
240     }
241     Value *Result = B.CreateIntrinsic(
242         IID, {WideLHS->getType()},
243         {WideLHS, WideRHS, B.getInt32(CommonSema.getScale())});
244 
245     return CreateFixedToFixed(Result, CommonSema,
246                               LHSSema.getCommonSemantics(RHSSema));
247   }
248 
249   /// Divide two fixed-point values and return the result in their common
250   /// semantic.
251   /// \p LHS     - The left hand side
252   /// \p LHSSema - The semantic of the left hand side
253   /// \p RHS     - The right hand side
254   /// \p RHSSema - The semantic of the right hand side
CreateDiv(Value * LHS,const FixedPointSemantics & LHSSema,Value * RHS,const FixedPointSemantics & RHSSema)255   Value *CreateDiv(Value *LHS, const FixedPointSemantics &LHSSema,
256                    Value *RHS, const FixedPointSemantics &RHSSema) {
257     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
258     bool UseSigned = CommonSema.isSigned() || CommonSema.hasUnsignedPadding();
259 
260     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
261     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
262 
263     Intrinsic::ID IID;
264     if (CommonSema.isSaturated()) {
265       IID = UseSigned ? Intrinsic::sdiv_fix_sat : Intrinsic::udiv_fix_sat;
266     } else {
267       IID = UseSigned ? Intrinsic::sdiv_fix : Intrinsic::udiv_fix;
268     }
269     Value *Result = B.CreateIntrinsic(
270         IID, {WideLHS->getType()},
271         {WideLHS, WideRHS, B.getInt32(CommonSema.getScale())});
272 
273     return CreateFixedToFixed(Result, CommonSema,
274                               LHSSema.getCommonSemantics(RHSSema));
275   }
276 
277   /// Left shift a fixed-point value by an unsigned integer value. The integer
278   /// value can be any bit width.
279   /// \p LHS     - The left hand side
280   /// \p LHSSema - The semantic of the left hand side
281   /// \p RHS     - The right hand side
CreateShl(Value * LHS,const FixedPointSemantics & LHSSema,Value * RHS)282   Value *CreateShl(Value *LHS, const FixedPointSemantics &LHSSema, Value *RHS) {
283     bool UseSigned = LHSSema.isSigned() || LHSSema.hasUnsignedPadding();
284 
285     RHS = B.CreateIntCast(RHS, LHS->getType(), /*IsSigned=*/false);
286 
287     Value *Result;
288     if (LHSSema.isSaturated()) {
289       Intrinsic::ID IID = UseSigned ? Intrinsic::sshl_sat : Intrinsic::ushl_sat;
290       Result = B.CreateBinaryIntrinsic(IID, LHS, RHS);
291     } else {
292       Result = B.CreateShl(LHS, RHS);
293     }
294 
295     return Result;
296   }
297 
298   /// Right shift a fixed-point value by an unsigned integer value. The integer
299   /// value can be any bit width.
300   /// \p LHS     - The left hand side
301   /// \p LHSSema - The semantic of the left hand side
302   /// \p RHS     - The right hand side
CreateShr(Value * LHS,const FixedPointSemantics & LHSSema,Value * RHS)303   Value *CreateShr(Value *LHS, const FixedPointSemantics &LHSSema, Value *RHS) {
304     RHS = B.CreateIntCast(RHS, LHS->getType(), false);
305 
306     return LHSSema.isSigned() ? B.CreateAShr(LHS, RHS) : B.CreateLShr(LHS, RHS);
307   }
308 
309   /// Compare two fixed-point values for equality.
310   /// \p LHS     - The left hand side
311   /// \p LHSSema - The semantic of the left hand side
312   /// \p RHS     - The right hand side
313   /// \p RHSSema - The semantic of the right hand side
CreateEQ(Value * LHS,const FixedPointSemantics & LHSSema,Value * RHS,const FixedPointSemantics & RHSSema)314   Value *CreateEQ(Value *LHS, const FixedPointSemantics &LHSSema,
315                   Value *RHS, const FixedPointSemantics &RHSSema) {
316     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
317 
318     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
319     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
320 
321     return B.CreateICmpEQ(WideLHS, WideRHS);
322   }
323 
324   /// Compare two fixed-point values for inequality.
325   /// \p LHS     - The left hand side
326   /// \p LHSSema - The semantic of the left hand side
327   /// \p RHS     - The right hand side
328   /// \p RHSSema - The semantic of the right hand side
CreateNE(Value * LHS,const FixedPointSemantics & LHSSema,Value * RHS,const FixedPointSemantics & RHSSema)329   Value *CreateNE(Value *LHS, const FixedPointSemantics &LHSSema,
330                   Value *RHS, const FixedPointSemantics &RHSSema) {
331     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
332 
333     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
334     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
335 
336     return B.CreateICmpNE(WideLHS, WideRHS);
337   }
338 
339   /// Compare two fixed-point values as LHS < RHS.
340   /// \p LHS     - The left hand side
341   /// \p LHSSema - The semantic of the left hand side
342   /// \p RHS     - The right hand side
343   /// \p RHSSema - The semantic of the right hand side
CreateLT(Value * LHS,const FixedPointSemantics & LHSSema,Value * RHS,const FixedPointSemantics & RHSSema)344   Value *CreateLT(Value *LHS, const FixedPointSemantics &LHSSema,
345                   Value *RHS, const FixedPointSemantics &RHSSema) {
346     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
347 
348     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
349     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
350 
351     return CommonSema.isSigned() ? B.CreateICmpSLT(WideLHS, WideRHS)
352                                  : B.CreateICmpULT(WideLHS, WideRHS);
353   }
354 
355   /// Compare two fixed-point values as LHS <= RHS.
356   /// \p LHS     - The left hand side
357   /// \p LHSSema - The semantic of the left hand side
358   /// \p RHS     - The right hand side
359   /// \p RHSSema - The semantic of the right hand side
CreateLE(Value * LHS,const FixedPointSemantics & LHSSema,Value * RHS,const FixedPointSemantics & RHSSema)360   Value *CreateLE(Value *LHS, const FixedPointSemantics &LHSSema,
361                   Value *RHS, const FixedPointSemantics &RHSSema) {
362     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
363 
364     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
365     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
366 
367     return CommonSema.isSigned() ? B.CreateICmpSLE(WideLHS, WideRHS)
368                                  : B.CreateICmpULE(WideLHS, WideRHS);
369   }
370 
371   /// Compare two fixed-point values as LHS > RHS.
372   /// \p LHS     - The left hand side
373   /// \p LHSSema - The semantic of the left hand side
374   /// \p RHS     - The right hand side
375   /// \p RHSSema - The semantic of the right hand side
CreateGT(Value * LHS,const FixedPointSemantics & LHSSema,Value * RHS,const FixedPointSemantics & RHSSema)376   Value *CreateGT(Value *LHS, const FixedPointSemantics &LHSSema,
377                   Value *RHS, const FixedPointSemantics &RHSSema) {
378     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
379 
380     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
381     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
382 
383     return CommonSema.isSigned() ? B.CreateICmpSGT(WideLHS, WideRHS)
384                                  : B.CreateICmpUGT(WideLHS, WideRHS);
385   }
386 
387   /// Compare two fixed-point values as LHS >= RHS.
388   /// \p LHS     - The left hand side
389   /// \p LHSSema - The semantic of the left hand side
390   /// \p RHS     - The right hand side
391   /// \p RHSSema - The semantic of the right hand side
CreateGE(Value * LHS,const FixedPointSemantics & LHSSema,Value * RHS,const FixedPointSemantics & RHSSema)392   Value *CreateGE(Value *LHS, const FixedPointSemantics &LHSSema,
393                   Value *RHS, const FixedPointSemantics &RHSSema) {
394     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
395 
396     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
397     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
398 
399     return CommonSema.isSigned() ? B.CreateICmpSGE(WideLHS, WideRHS)
400                                  : B.CreateICmpUGE(WideLHS, WideRHS);
401   }
402 };
403 
404 } // end namespace llvm
405 
406 #endif // LLVM_IR_FIXEDPOINTBUILDER_H
407