xref: /freebsd-src/contrib/llvm-project/llvm/include/llvm/IR/FixedPointBuilder.h (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
1e8d8bef9SDimitry Andric //===- llvm/FixedPointBuilder.h - Builder for fixed-point ops ---*- C++ -*-===//
2e8d8bef9SDimitry Andric //
3e8d8bef9SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e8d8bef9SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5e8d8bef9SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e8d8bef9SDimitry Andric //
7e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===//
8e8d8bef9SDimitry Andric //
9e8d8bef9SDimitry Andric // This file defines the FixedPointBuilder class, which is used as a convenient
10e8d8bef9SDimitry Andric // way to lower fixed-point arithmetic operations to LLVM IR.
11e8d8bef9SDimitry Andric //
12e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===//
13e8d8bef9SDimitry Andric 
14e8d8bef9SDimitry Andric #ifndef LLVM_IR_FIXEDPOINTBUILDER_H
15e8d8bef9SDimitry Andric #define LLVM_IR_FIXEDPOINTBUILDER_H
16e8d8bef9SDimitry Andric 
17e8d8bef9SDimitry Andric #include "llvm/ADT/APFixedPoint.h"
18e8d8bef9SDimitry Andric #include "llvm/IR/Constant.h"
19e8d8bef9SDimitry Andric #include "llvm/IR/Constants.h"
20e8d8bef9SDimitry Andric #include "llvm/IR/IRBuilder.h"
21e8d8bef9SDimitry Andric #include "llvm/IR/InstrTypes.h"
22e8d8bef9SDimitry Andric #include "llvm/IR/Instruction.h"
23e8d8bef9SDimitry Andric #include "llvm/IR/IntrinsicInst.h"
24e8d8bef9SDimitry Andric #include "llvm/IR/Intrinsics.h"
25e8d8bef9SDimitry Andric #include "llvm/IR/Type.h"
26e8d8bef9SDimitry Andric #include "llvm/IR/Value.h"
27e8d8bef9SDimitry Andric 
28*bdd1243dSDimitry Andric #include <cmath>
29*bdd1243dSDimitry Andric 
30e8d8bef9SDimitry Andric namespace llvm {
31e8d8bef9SDimitry Andric 
32e8d8bef9SDimitry Andric template <class IRBuilderTy> class FixedPointBuilder {
33e8d8bef9SDimitry Andric   IRBuilderTy &B;
34e8d8bef9SDimitry Andric 
Convert(Value * Src,const FixedPointSemantics & SrcSema,const FixedPointSemantics & DstSema,bool DstIsInteger)35e8d8bef9SDimitry Andric   Value *Convert(Value *Src, const FixedPointSemantics &SrcSema,
36e8d8bef9SDimitry Andric                  const FixedPointSemantics &DstSema, bool DstIsInteger) {
37e8d8bef9SDimitry Andric     unsigned SrcWidth = SrcSema.getWidth();
38e8d8bef9SDimitry Andric     unsigned DstWidth = DstSema.getWidth();
39e8d8bef9SDimitry Andric     unsigned SrcScale = SrcSema.getScale();
40e8d8bef9SDimitry Andric     unsigned DstScale = DstSema.getScale();
41e8d8bef9SDimitry Andric     bool SrcIsSigned = SrcSema.isSigned();
42e8d8bef9SDimitry Andric     bool DstIsSigned = DstSema.isSigned();
43e8d8bef9SDimitry Andric 
44e8d8bef9SDimitry Andric     Type *DstIntTy = B.getIntNTy(DstWidth);
45e8d8bef9SDimitry Andric 
46e8d8bef9SDimitry Andric     Value *Result = Src;
47e8d8bef9SDimitry Andric     unsigned ResultWidth = SrcWidth;
48e8d8bef9SDimitry Andric 
49e8d8bef9SDimitry Andric     // Downscale.
50e8d8bef9SDimitry Andric     if (DstScale < SrcScale) {
51e8d8bef9SDimitry Andric       // When converting to integers, we round towards zero. For negative
52e8d8bef9SDimitry Andric       // numbers, right shifting rounds towards negative infinity. In this case,
53e8d8bef9SDimitry Andric       // we can just round up before shifting.
54e8d8bef9SDimitry Andric       if (DstIsInteger && SrcIsSigned) {
55e8d8bef9SDimitry Andric         Value *Zero = Constant::getNullValue(Result->getType());
56e8d8bef9SDimitry Andric         Value *IsNegative = B.CreateICmpSLT(Result, Zero);
57e8d8bef9SDimitry Andric         Value *LowBits = ConstantInt::get(
58e8d8bef9SDimitry Andric             B.getContext(), APInt::getLowBitsSet(ResultWidth, SrcScale));
59e8d8bef9SDimitry Andric         Value *Rounded = B.CreateAdd(Result, LowBits);
60e8d8bef9SDimitry Andric         Result = B.CreateSelect(IsNegative, Rounded, Result);
61e8d8bef9SDimitry Andric       }
62e8d8bef9SDimitry Andric 
63e8d8bef9SDimitry Andric       Result = SrcIsSigned
64e8d8bef9SDimitry Andric                    ? B.CreateAShr(Result, SrcScale - DstScale, "downscale")
65e8d8bef9SDimitry Andric                    : B.CreateLShr(Result, SrcScale - DstScale, "downscale");
66e8d8bef9SDimitry Andric     }
67e8d8bef9SDimitry Andric 
68e8d8bef9SDimitry Andric     if (!DstSema.isSaturated()) {
69e8d8bef9SDimitry Andric       // Resize.
70e8d8bef9SDimitry Andric       Result = B.CreateIntCast(Result, DstIntTy, SrcIsSigned, "resize");
71e8d8bef9SDimitry Andric 
72e8d8bef9SDimitry Andric       // Upscale.
73e8d8bef9SDimitry Andric       if (DstScale > SrcScale)
74e8d8bef9SDimitry Andric         Result = B.CreateShl(Result, DstScale - SrcScale, "upscale");
75e8d8bef9SDimitry Andric     } else {
76e8d8bef9SDimitry Andric       // Adjust the number of fractional bits.
77e8d8bef9SDimitry Andric       if (DstScale > SrcScale) {
78e8d8bef9SDimitry Andric         // Compare to DstWidth to prevent resizing twice.
79e8d8bef9SDimitry Andric         ResultWidth = std::max(SrcWidth + DstScale - SrcScale, DstWidth);
80e8d8bef9SDimitry Andric         Type *UpscaledTy = B.getIntNTy(ResultWidth);
81e8d8bef9SDimitry Andric         Result = B.CreateIntCast(Result, UpscaledTy, SrcIsSigned, "resize");
82e8d8bef9SDimitry Andric         Result = B.CreateShl(Result, DstScale - SrcScale, "upscale");
83e8d8bef9SDimitry Andric       }
84e8d8bef9SDimitry Andric 
85e8d8bef9SDimitry Andric       // Handle saturation.
86e8d8bef9SDimitry Andric       bool LessIntBits = DstSema.getIntegralBits() < SrcSema.getIntegralBits();
87e8d8bef9SDimitry Andric       if (LessIntBits) {
88e8d8bef9SDimitry Andric         Value *Max = ConstantInt::get(
89e8d8bef9SDimitry Andric             B.getContext(),
90e8d8bef9SDimitry Andric             APFixedPoint::getMax(DstSema).getValue().extOrTrunc(ResultWidth));
91e8d8bef9SDimitry Andric         Value *TooHigh = SrcIsSigned ? B.CreateICmpSGT(Result, Max)
92e8d8bef9SDimitry Andric                                      : B.CreateICmpUGT(Result, Max);
93e8d8bef9SDimitry Andric         Result = B.CreateSelect(TooHigh, Max, Result, "satmax");
94e8d8bef9SDimitry Andric       }
95e8d8bef9SDimitry Andric       // Cannot overflow min to dest type if src is unsigned since all fixed
96e8d8bef9SDimitry Andric       // point types can cover the unsigned min of 0.
97e8d8bef9SDimitry Andric       if (SrcIsSigned && (LessIntBits || !DstIsSigned)) {
98e8d8bef9SDimitry Andric         Value *Min = ConstantInt::get(
99e8d8bef9SDimitry Andric             B.getContext(),
100e8d8bef9SDimitry Andric             APFixedPoint::getMin(DstSema).getValue().extOrTrunc(ResultWidth));
101e8d8bef9SDimitry Andric         Value *TooLow = B.CreateICmpSLT(Result, Min);
102e8d8bef9SDimitry Andric         Result = B.CreateSelect(TooLow, Min, Result, "satmin");
103e8d8bef9SDimitry Andric       }
104e8d8bef9SDimitry Andric 
105e8d8bef9SDimitry Andric       // Resize the integer part to get the final destination size.
106e8d8bef9SDimitry Andric       if (ResultWidth != DstWidth)
107e8d8bef9SDimitry Andric         Result = B.CreateIntCast(Result, DstIntTy, SrcIsSigned, "resize");
108e8d8bef9SDimitry Andric     }
109e8d8bef9SDimitry Andric     return Result;
110e8d8bef9SDimitry Andric   }
111e8d8bef9SDimitry Andric 
112e8d8bef9SDimitry Andric   /// Get the common semantic for two semantics, with the added imposition that
113e8d8bef9SDimitry Andric   /// saturated padded types retain the padding bit.
114e8d8bef9SDimitry Andric   FixedPointSemantics
getCommonBinopSemantic(const FixedPointSemantics & LHSSema,const FixedPointSemantics & RHSSema)115e8d8bef9SDimitry Andric   getCommonBinopSemantic(const FixedPointSemantics &LHSSema,
116e8d8bef9SDimitry Andric                          const FixedPointSemantics &RHSSema) {
117e8d8bef9SDimitry Andric     auto C = LHSSema.getCommonSemantics(RHSSema);
118e8d8bef9SDimitry Andric     bool BothPadded =
119e8d8bef9SDimitry Andric         LHSSema.hasUnsignedPadding() && RHSSema.hasUnsignedPadding();
120e8d8bef9SDimitry Andric     return FixedPointSemantics(
121e8d8bef9SDimitry Andric         C.getWidth() + (unsigned)(BothPadded && C.isSaturated()), C.getScale(),
122e8d8bef9SDimitry Andric         C.isSigned(), C.isSaturated(), BothPadded);
123e8d8bef9SDimitry Andric   }
124e8d8bef9SDimitry Andric 
125e8d8bef9SDimitry Andric   /// Given a floating point type and a fixed-point semantic, return a floating
126e8d8bef9SDimitry Andric   /// point type which can accommodate the fixed-point semantic. This is either
127e8d8bef9SDimitry Andric   /// \p Ty, or a floating point type with a larger exponent than Ty.
getAccommodatingFloatType(Type * Ty,const FixedPointSemantics & Sema)128e8d8bef9SDimitry Andric   Type *getAccommodatingFloatType(Type *Ty, const FixedPointSemantics &Sema) {
129e8d8bef9SDimitry Andric     const fltSemantics *FloatSema = &Ty->getFltSemantics();
130e8d8bef9SDimitry Andric     while (!Sema.fitsInFloatSemantics(*FloatSema))
131e8d8bef9SDimitry Andric       FloatSema = APFixedPoint::promoteFloatSemantics(FloatSema);
132e8d8bef9SDimitry Andric     return Type::getFloatingPointTy(Ty->getContext(), *FloatSema);
133e8d8bef9SDimitry Andric   }
134e8d8bef9SDimitry Andric 
135e8d8bef9SDimitry Andric public:
FixedPointBuilder(IRBuilderTy & Builder)136e8d8bef9SDimitry Andric   FixedPointBuilder(IRBuilderTy &Builder) : B(Builder) {}
137e8d8bef9SDimitry Andric 
138e8d8bef9SDimitry Andric   /// Convert an integer value representing a fixed-point number from one
139e8d8bef9SDimitry Andric   /// fixed-point semantic to another fixed-point semantic.
140e8d8bef9SDimitry Andric   /// \p Src     - The source value
141e8d8bef9SDimitry Andric   /// \p SrcSema - The fixed-point semantic of the source value
142e8d8bef9SDimitry Andric   /// \p DstSema - The resulting fixed-point semantic
CreateFixedToFixed(Value * Src,const FixedPointSemantics & SrcSema,const FixedPointSemantics & DstSema)143e8d8bef9SDimitry Andric   Value *CreateFixedToFixed(Value *Src, const FixedPointSemantics &SrcSema,
144e8d8bef9SDimitry Andric                             const FixedPointSemantics &DstSema) {
145e8d8bef9SDimitry Andric     return Convert(Src, SrcSema, DstSema, false);
146e8d8bef9SDimitry Andric   }
147e8d8bef9SDimitry Andric 
148e8d8bef9SDimitry Andric   /// Convert an integer value representing a fixed-point number to an integer
149e8d8bef9SDimitry Andric   /// with the given bit width and signedness.
150e8d8bef9SDimitry Andric   /// \p Src         - The source value
151e8d8bef9SDimitry Andric   /// \p SrcSema     - The fixed-point semantic of the source value
152e8d8bef9SDimitry Andric   /// \p DstWidth    - The bit width of the result value
153e8d8bef9SDimitry Andric   /// \p DstIsSigned - The signedness of the result value
CreateFixedToInteger(Value * Src,const FixedPointSemantics & SrcSema,unsigned DstWidth,bool DstIsSigned)154e8d8bef9SDimitry Andric   Value *CreateFixedToInteger(Value *Src, const FixedPointSemantics &SrcSema,
155e8d8bef9SDimitry Andric                               unsigned DstWidth, bool DstIsSigned) {
156e8d8bef9SDimitry Andric     return Convert(
157e8d8bef9SDimitry Andric         Src, SrcSema,
158e8d8bef9SDimitry Andric         FixedPointSemantics::GetIntegerSemantics(DstWidth, DstIsSigned), true);
159e8d8bef9SDimitry Andric   }
160e8d8bef9SDimitry Andric 
161e8d8bef9SDimitry Andric   /// Convert an integer value with the given signedness to an integer value
162e8d8bef9SDimitry Andric   /// representing the given fixed-point semantic.
163e8d8bef9SDimitry Andric   /// \p Src         - The source value
164e8d8bef9SDimitry Andric   /// \p SrcIsSigned - The signedness of the source value
165e8d8bef9SDimitry Andric   /// \p DstSema     - The resulting fixed-point semantic
CreateIntegerToFixed(Value * Src,unsigned SrcIsSigned,const FixedPointSemantics & DstSema)166e8d8bef9SDimitry Andric   Value *CreateIntegerToFixed(Value *Src, unsigned SrcIsSigned,
167e8d8bef9SDimitry Andric                               const FixedPointSemantics &DstSema) {
168e8d8bef9SDimitry Andric     return Convert(Src,
169e8d8bef9SDimitry Andric                    FixedPointSemantics::GetIntegerSemantics(
170e8d8bef9SDimitry Andric                        Src->getType()->getScalarSizeInBits(), SrcIsSigned),
171e8d8bef9SDimitry Andric                    DstSema, false);
172e8d8bef9SDimitry Andric   }
173e8d8bef9SDimitry Andric 
CreateFixedToFloating(Value * Src,const FixedPointSemantics & SrcSema,Type * DstTy)174e8d8bef9SDimitry Andric   Value *CreateFixedToFloating(Value *Src, const FixedPointSemantics &SrcSema,
175e8d8bef9SDimitry Andric                                Type *DstTy) {
176e8d8bef9SDimitry Andric     Value *Result;
177e8d8bef9SDimitry Andric     Type *OpTy = getAccommodatingFloatType(DstTy, SrcSema);
178e8d8bef9SDimitry Andric     // Convert the raw fixed-point value directly to floating point. If the
179e8d8bef9SDimitry Andric     // value is too large to fit, it will be rounded, not truncated.
180e8d8bef9SDimitry Andric     Result = SrcSema.isSigned() ? B.CreateSIToFP(Src, OpTy)
181e8d8bef9SDimitry Andric                                 : B.CreateUIToFP(Src, OpTy);
182e8d8bef9SDimitry Andric     // Rescale the integral-in-floating point by the scaling factor. This is
183e8d8bef9SDimitry Andric     // lossless, except for overflow to infinity which is unlikely.
184e8d8bef9SDimitry Andric     Result = B.CreateFMul(Result,
185e8d8bef9SDimitry Andric         ConstantFP::get(OpTy, std::pow(2, -(int)SrcSema.getScale())));
186e8d8bef9SDimitry Andric     if (OpTy != DstTy)
187e8d8bef9SDimitry Andric       Result = B.CreateFPTrunc(Result, DstTy);
188e8d8bef9SDimitry Andric     return Result;
189e8d8bef9SDimitry Andric   }
190e8d8bef9SDimitry Andric 
CreateFloatingToFixed(Value * Src,const FixedPointSemantics & DstSema)191e8d8bef9SDimitry Andric   Value *CreateFloatingToFixed(Value *Src, const FixedPointSemantics &DstSema) {
192e8d8bef9SDimitry Andric     bool UseSigned = DstSema.isSigned() || DstSema.hasUnsignedPadding();
193e8d8bef9SDimitry Andric     Value *Result = Src;
194e8d8bef9SDimitry Andric     Type *OpTy = getAccommodatingFloatType(Src->getType(), DstSema);
195e8d8bef9SDimitry Andric     if (OpTy != Src->getType())
196e8d8bef9SDimitry Andric       Result = B.CreateFPExt(Result, OpTy);
197e8d8bef9SDimitry Andric     // Rescale the floating point value so that its significant bits (for the
198e8d8bef9SDimitry Andric     // purposes of the conversion) are in the integral range.
199e8d8bef9SDimitry Andric     Result = B.CreateFMul(Result,
200e8d8bef9SDimitry Andric         ConstantFP::get(OpTy, std::pow(2, DstSema.getScale())));
201e8d8bef9SDimitry Andric 
202e8d8bef9SDimitry Andric     Type *ResultTy = B.getIntNTy(DstSema.getWidth());
203e8d8bef9SDimitry Andric     if (DstSema.isSaturated()) {
204e8d8bef9SDimitry Andric       Intrinsic::ID IID =
205e8d8bef9SDimitry Andric           UseSigned ? Intrinsic::fptosi_sat : Intrinsic::fptoui_sat;
206e8d8bef9SDimitry Andric       Result = B.CreateIntrinsic(IID, {ResultTy, OpTy}, {Result});
207e8d8bef9SDimitry Andric     } else {
208e8d8bef9SDimitry Andric       Result = UseSigned ? B.CreateFPToSI(Result, ResultTy)
209e8d8bef9SDimitry Andric                          : B.CreateFPToUI(Result, ResultTy);
210e8d8bef9SDimitry Andric     }
211e8d8bef9SDimitry Andric 
212e8d8bef9SDimitry Andric     // When saturating unsigned-with-padding using signed operations, we may
213e8d8bef9SDimitry Andric     // get negative values. Emit an extra clamp to zero.
214e8d8bef9SDimitry Andric     if (DstSema.isSaturated() && DstSema.hasUnsignedPadding()) {
215e8d8bef9SDimitry Andric       Constant *Zero = Constant::getNullValue(Result->getType());
216e8d8bef9SDimitry Andric       Result =
217e8d8bef9SDimitry Andric           B.CreateSelect(B.CreateICmpSLT(Result, Zero), Zero, Result, "satmin");
218e8d8bef9SDimitry Andric     }
219e8d8bef9SDimitry Andric 
220e8d8bef9SDimitry Andric     return Result;
221e8d8bef9SDimitry Andric   }
222e8d8bef9SDimitry Andric 
223e8d8bef9SDimitry Andric   /// Add two fixed-point values and return the result in their common semantic.
224e8d8bef9SDimitry Andric   /// \p LHS     - The left hand side
225e8d8bef9SDimitry Andric   /// \p LHSSema - The semantic of the left hand side
226e8d8bef9SDimitry Andric   /// \p RHS     - The right hand side
227e8d8bef9SDimitry Andric   /// \p RHSSema - The semantic of the right hand side
CreateAdd(Value * LHS,const FixedPointSemantics & LHSSema,Value * RHS,const FixedPointSemantics & RHSSema)228e8d8bef9SDimitry Andric   Value *CreateAdd(Value *LHS, const FixedPointSemantics &LHSSema,
229e8d8bef9SDimitry Andric                    Value *RHS, const FixedPointSemantics &RHSSema) {
230e8d8bef9SDimitry Andric     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
231e8d8bef9SDimitry Andric     bool UseSigned = CommonSema.isSigned() || CommonSema.hasUnsignedPadding();
232e8d8bef9SDimitry Andric 
233e8d8bef9SDimitry Andric     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
234e8d8bef9SDimitry Andric     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
235e8d8bef9SDimitry Andric 
236e8d8bef9SDimitry Andric     Value *Result;
237e8d8bef9SDimitry Andric     if (CommonSema.isSaturated()) {
238e8d8bef9SDimitry Andric       Intrinsic::ID IID = UseSigned ? Intrinsic::sadd_sat : Intrinsic::uadd_sat;
239e8d8bef9SDimitry Andric       Result = B.CreateBinaryIntrinsic(IID, WideLHS, WideRHS);
240e8d8bef9SDimitry Andric     } else {
241e8d8bef9SDimitry Andric       Result = B.CreateAdd(WideLHS, WideRHS);
242e8d8bef9SDimitry Andric     }
243e8d8bef9SDimitry Andric 
244e8d8bef9SDimitry Andric     return CreateFixedToFixed(Result, CommonSema,
245e8d8bef9SDimitry Andric                               LHSSema.getCommonSemantics(RHSSema));
246e8d8bef9SDimitry Andric   }
247e8d8bef9SDimitry Andric 
248e8d8bef9SDimitry Andric   /// Subtract two fixed-point values and return the result in their common
249e8d8bef9SDimitry Andric   /// semantic.
250e8d8bef9SDimitry Andric   /// \p LHS     - The left hand side
251e8d8bef9SDimitry Andric   /// \p LHSSema - The semantic of the left hand side
252e8d8bef9SDimitry Andric   /// \p RHS     - The right hand side
253e8d8bef9SDimitry Andric   /// \p RHSSema - The semantic of the right hand side
CreateSub(Value * LHS,const FixedPointSemantics & LHSSema,Value * RHS,const FixedPointSemantics & RHSSema)254e8d8bef9SDimitry Andric   Value *CreateSub(Value *LHS, const FixedPointSemantics &LHSSema,
255e8d8bef9SDimitry Andric                    Value *RHS, const FixedPointSemantics &RHSSema) {
256e8d8bef9SDimitry Andric     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
257e8d8bef9SDimitry Andric     bool UseSigned = CommonSema.isSigned() || CommonSema.hasUnsignedPadding();
258e8d8bef9SDimitry Andric 
259e8d8bef9SDimitry Andric     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
260e8d8bef9SDimitry Andric     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
261e8d8bef9SDimitry Andric 
262e8d8bef9SDimitry Andric     Value *Result;
263e8d8bef9SDimitry Andric     if (CommonSema.isSaturated()) {
264e8d8bef9SDimitry Andric       Intrinsic::ID IID = UseSigned ? Intrinsic::ssub_sat : Intrinsic::usub_sat;
265e8d8bef9SDimitry Andric       Result = B.CreateBinaryIntrinsic(IID, WideLHS, WideRHS);
266e8d8bef9SDimitry Andric     } else {
267e8d8bef9SDimitry Andric       Result = B.CreateSub(WideLHS, WideRHS);
268e8d8bef9SDimitry Andric     }
269e8d8bef9SDimitry Andric 
270e8d8bef9SDimitry Andric     // Subtraction can end up below 0 for padded unsigned operations, so emit
271e8d8bef9SDimitry Andric     // an extra clamp in that case.
272e8d8bef9SDimitry Andric     if (CommonSema.isSaturated() && CommonSema.hasUnsignedPadding()) {
273e8d8bef9SDimitry Andric       Constant *Zero = Constant::getNullValue(Result->getType());
274e8d8bef9SDimitry Andric       Result =
275e8d8bef9SDimitry Andric           B.CreateSelect(B.CreateICmpSLT(Result, Zero), Zero, Result, "satmin");
276e8d8bef9SDimitry Andric     }
277e8d8bef9SDimitry Andric 
278e8d8bef9SDimitry Andric     return CreateFixedToFixed(Result, CommonSema,
279e8d8bef9SDimitry Andric                               LHSSema.getCommonSemantics(RHSSema));
280e8d8bef9SDimitry Andric   }
281e8d8bef9SDimitry Andric 
282e8d8bef9SDimitry Andric   /// Multiply two fixed-point values and return the result in their common
283e8d8bef9SDimitry Andric   /// semantic.
284e8d8bef9SDimitry Andric   /// \p LHS     - The left hand side
285e8d8bef9SDimitry Andric   /// \p LHSSema - The semantic of the left hand side
286e8d8bef9SDimitry Andric   /// \p RHS     - The right hand side
287e8d8bef9SDimitry Andric   /// \p RHSSema - The semantic of the right hand side
CreateMul(Value * LHS,const FixedPointSemantics & LHSSema,Value * RHS,const FixedPointSemantics & RHSSema)288e8d8bef9SDimitry Andric   Value *CreateMul(Value *LHS, const FixedPointSemantics &LHSSema,
289e8d8bef9SDimitry Andric                    Value *RHS, const FixedPointSemantics &RHSSema) {
290e8d8bef9SDimitry Andric     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
291e8d8bef9SDimitry Andric     bool UseSigned = CommonSema.isSigned() || CommonSema.hasUnsignedPadding();
292e8d8bef9SDimitry Andric 
293e8d8bef9SDimitry Andric     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
294e8d8bef9SDimitry Andric     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
295e8d8bef9SDimitry Andric 
296e8d8bef9SDimitry Andric     Intrinsic::ID IID;
297e8d8bef9SDimitry Andric     if (CommonSema.isSaturated()) {
298e8d8bef9SDimitry Andric       IID = UseSigned ? Intrinsic::smul_fix_sat : Intrinsic::umul_fix_sat;
299e8d8bef9SDimitry Andric     } else {
300e8d8bef9SDimitry Andric       IID = UseSigned ? Intrinsic::smul_fix : Intrinsic::umul_fix;
301e8d8bef9SDimitry Andric     }
302e8d8bef9SDimitry Andric     Value *Result = B.CreateIntrinsic(
303e8d8bef9SDimitry Andric         IID, {WideLHS->getType()},
304e8d8bef9SDimitry Andric         {WideLHS, WideRHS, B.getInt32(CommonSema.getScale())});
305e8d8bef9SDimitry Andric 
306e8d8bef9SDimitry Andric     return CreateFixedToFixed(Result, CommonSema,
307e8d8bef9SDimitry Andric                               LHSSema.getCommonSemantics(RHSSema));
308e8d8bef9SDimitry Andric   }
309e8d8bef9SDimitry Andric 
310e8d8bef9SDimitry Andric   /// Divide two fixed-point values and return the result in their common
311e8d8bef9SDimitry Andric   /// semantic.
312e8d8bef9SDimitry Andric   /// \p LHS     - The left hand side
313e8d8bef9SDimitry Andric   /// \p LHSSema - The semantic of the left hand side
314e8d8bef9SDimitry Andric   /// \p RHS     - The right hand side
315e8d8bef9SDimitry Andric   /// \p RHSSema - The semantic of the right hand side
CreateDiv(Value * LHS,const FixedPointSemantics & LHSSema,Value * RHS,const FixedPointSemantics & RHSSema)316e8d8bef9SDimitry Andric   Value *CreateDiv(Value *LHS, const FixedPointSemantics &LHSSema,
317e8d8bef9SDimitry Andric                    Value *RHS, const FixedPointSemantics &RHSSema) {
318e8d8bef9SDimitry Andric     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
319e8d8bef9SDimitry Andric     bool UseSigned = CommonSema.isSigned() || CommonSema.hasUnsignedPadding();
320e8d8bef9SDimitry Andric 
321e8d8bef9SDimitry Andric     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
322e8d8bef9SDimitry Andric     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
323e8d8bef9SDimitry Andric 
324e8d8bef9SDimitry Andric     Intrinsic::ID IID;
325e8d8bef9SDimitry Andric     if (CommonSema.isSaturated()) {
326e8d8bef9SDimitry Andric       IID = UseSigned ? Intrinsic::sdiv_fix_sat : Intrinsic::udiv_fix_sat;
327e8d8bef9SDimitry Andric     } else {
328e8d8bef9SDimitry Andric       IID = UseSigned ? Intrinsic::sdiv_fix : Intrinsic::udiv_fix;
329e8d8bef9SDimitry Andric     }
330e8d8bef9SDimitry Andric     Value *Result = B.CreateIntrinsic(
331e8d8bef9SDimitry Andric         IID, {WideLHS->getType()},
332e8d8bef9SDimitry Andric         {WideLHS, WideRHS, B.getInt32(CommonSema.getScale())});
333e8d8bef9SDimitry Andric 
334e8d8bef9SDimitry Andric     return CreateFixedToFixed(Result, CommonSema,
335e8d8bef9SDimitry Andric                               LHSSema.getCommonSemantics(RHSSema));
336e8d8bef9SDimitry Andric   }
337e8d8bef9SDimitry Andric 
338e8d8bef9SDimitry Andric   /// Left shift a fixed-point value by an unsigned integer value. The integer
339e8d8bef9SDimitry Andric   /// value can be any bit width.
340e8d8bef9SDimitry Andric   /// \p LHS     - The left hand side
341e8d8bef9SDimitry Andric   /// \p LHSSema - The semantic of the left hand side
342e8d8bef9SDimitry Andric   /// \p RHS     - The right hand side
CreateShl(Value * LHS,const FixedPointSemantics & LHSSema,Value * RHS)343e8d8bef9SDimitry Andric   Value *CreateShl(Value *LHS, const FixedPointSemantics &LHSSema, Value *RHS) {
344e8d8bef9SDimitry Andric     bool UseSigned = LHSSema.isSigned() || LHSSema.hasUnsignedPadding();
345e8d8bef9SDimitry Andric 
346e8d8bef9SDimitry Andric     RHS = B.CreateIntCast(RHS, LHS->getType(), /*IsSigned=*/false);
347e8d8bef9SDimitry Andric 
348e8d8bef9SDimitry Andric     Value *Result;
349e8d8bef9SDimitry Andric     if (LHSSema.isSaturated()) {
350e8d8bef9SDimitry Andric       Intrinsic::ID IID = UseSigned ? Intrinsic::sshl_sat : Intrinsic::ushl_sat;
351e8d8bef9SDimitry Andric       Result = B.CreateBinaryIntrinsic(IID, LHS, RHS);
352e8d8bef9SDimitry Andric     } else {
353e8d8bef9SDimitry Andric       Result = B.CreateShl(LHS, RHS);
354e8d8bef9SDimitry Andric     }
355e8d8bef9SDimitry Andric 
356e8d8bef9SDimitry Andric     return Result;
357e8d8bef9SDimitry Andric   }
358e8d8bef9SDimitry Andric 
359e8d8bef9SDimitry Andric   /// Right shift a fixed-point value by an unsigned integer value. The integer
360e8d8bef9SDimitry Andric   /// value can be any bit width.
361e8d8bef9SDimitry Andric   /// \p LHS     - The left hand side
362e8d8bef9SDimitry Andric   /// \p LHSSema - The semantic of the left hand side
363e8d8bef9SDimitry Andric   /// \p RHS     - The right hand side
CreateShr(Value * LHS,const FixedPointSemantics & LHSSema,Value * RHS)364e8d8bef9SDimitry Andric   Value *CreateShr(Value *LHS, const FixedPointSemantics &LHSSema, Value *RHS) {
365e8d8bef9SDimitry Andric     RHS = B.CreateIntCast(RHS, LHS->getType(), false);
366e8d8bef9SDimitry Andric 
367e8d8bef9SDimitry Andric     return LHSSema.isSigned() ? B.CreateAShr(LHS, RHS) : B.CreateLShr(LHS, RHS);
368e8d8bef9SDimitry Andric   }
369e8d8bef9SDimitry Andric 
370e8d8bef9SDimitry Andric   /// Compare two fixed-point values for equality.
371e8d8bef9SDimitry Andric   /// \p LHS     - The left hand side
372e8d8bef9SDimitry Andric   /// \p LHSSema - The semantic of the left hand side
373e8d8bef9SDimitry Andric   /// \p RHS     - The right hand side
374e8d8bef9SDimitry Andric   /// \p RHSSema - The semantic of the right hand side
CreateEQ(Value * LHS,const FixedPointSemantics & LHSSema,Value * RHS,const FixedPointSemantics & RHSSema)375e8d8bef9SDimitry Andric   Value *CreateEQ(Value *LHS, const FixedPointSemantics &LHSSema,
376e8d8bef9SDimitry Andric                   Value *RHS, const FixedPointSemantics &RHSSema) {
377e8d8bef9SDimitry Andric     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
378e8d8bef9SDimitry Andric 
379e8d8bef9SDimitry Andric     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
380e8d8bef9SDimitry Andric     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
381e8d8bef9SDimitry Andric 
382e8d8bef9SDimitry Andric     return B.CreateICmpEQ(WideLHS, WideRHS);
383e8d8bef9SDimitry Andric   }
384e8d8bef9SDimitry Andric 
385e8d8bef9SDimitry Andric   /// Compare two fixed-point values for inequality.
386e8d8bef9SDimitry Andric   /// \p LHS     - The left hand side
387e8d8bef9SDimitry Andric   /// \p LHSSema - The semantic of the left hand side
388e8d8bef9SDimitry Andric   /// \p RHS     - The right hand side
389e8d8bef9SDimitry Andric   /// \p RHSSema - The semantic of the right hand side
CreateNE(Value * LHS,const FixedPointSemantics & LHSSema,Value * RHS,const FixedPointSemantics & RHSSema)390e8d8bef9SDimitry Andric   Value *CreateNE(Value *LHS, const FixedPointSemantics &LHSSema,
391e8d8bef9SDimitry Andric                   Value *RHS, const FixedPointSemantics &RHSSema) {
392e8d8bef9SDimitry Andric     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
393e8d8bef9SDimitry Andric 
394e8d8bef9SDimitry Andric     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
395e8d8bef9SDimitry Andric     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
396e8d8bef9SDimitry Andric 
397e8d8bef9SDimitry Andric     return B.CreateICmpNE(WideLHS, WideRHS);
398e8d8bef9SDimitry Andric   }
399e8d8bef9SDimitry Andric 
400e8d8bef9SDimitry Andric   /// Compare two fixed-point values as LHS < RHS.
401e8d8bef9SDimitry Andric   /// \p LHS     - The left hand side
402e8d8bef9SDimitry Andric   /// \p LHSSema - The semantic of the left hand side
403e8d8bef9SDimitry Andric   /// \p RHS     - The right hand side
404e8d8bef9SDimitry Andric   /// \p RHSSema - The semantic of the right hand side
CreateLT(Value * LHS,const FixedPointSemantics & LHSSema,Value * RHS,const FixedPointSemantics & RHSSema)405e8d8bef9SDimitry Andric   Value *CreateLT(Value *LHS, const FixedPointSemantics &LHSSema,
406e8d8bef9SDimitry Andric                   Value *RHS, const FixedPointSemantics &RHSSema) {
407e8d8bef9SDimitry Andric     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
408e8d8bef9SDimitry Andric 
409e8d8bef9SDimitry Andric     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
410e8d8bef9SDimitry Andric     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
411e8d8bef9SDimitry Andric 
412e8d8bef9SDimitry Andric     return CommonSema.isSigned() ? B.CreateICmpSLT(WideLHS, WideRHS)
413e8d8bef9SDimitry Andric                                  : B.CreateICmpULT(WideLHS, WideRHS);
414e8d8bef9SDimitry Andric   }
415e8d8bef9SDimitry Andric 
416e8d8bef9SDimitry Andric   /// Compare two fixed-point values as LHS <= RHS.
417e8d8bef9SDimitry Andric   /// \p LHS     - The left hand side
418e8d8bef9SDimitry Andric   /// \p LHSSema - The semantic of the left hand side
419e8d8bef9SDimitry Andric   /// \p RHS     - The right hand side
420e8d8bef9SDimitry Andric   /// \p RHSSema - The semantic of the right hand side
CreateLE(Value * LHS,const FixedPointSemantics & LHSSema,Value * RHS,const FixedPointSemantics & RHSSema)421e8d8bef9SDimitry Andric   Value *CreateLE(Value *LHS, const FixedPointSemantics &LHSSema,
422e8d8bef9SDimitry Andric                   Value *RHS, const FixedPointSemantics &RHSSema) {
423e8d8bef9SDimitry Andric     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
424e8d8bef9SDimitry Andric 
425e8d8bef9SDimitry Andric     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
426e8d8bef9SDimitry Andric     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
427e8d8bef9SDimitry Andric 
428e8d8bef9SDimitry Andric     return CommonSema.isSigned() ? B.CreateICmpSLE(WideLHS, WideRHS)
429e8d8bef9SDimitry Andric                                  : B.CreateICmpULE(WideLHS, WideRHS);
430e8d8bef9SDimitry Andric   }
431e8d8bef9SDimitry Andric 
432e8d8bef9SDimitry Andric   /// Compare two fixed-point values as LHS > RHS.
433e8d8bef9SDimitry Andric   /// \p LHS     - The left hand side
434e8d8bef9SDimitry Andric   /// \p LHSSema - The semantic of the left hand side
435e8d8bef9SDimitry Andric   /// \p RHS     - The right hand side
436e8d8bef9SDimitry Andric   /// \p RHSSema - The semantic of the right hand side
CreateGT(Value * LHS,const FixedPointSemantics & LHSSema,Value * RHS,const FixedPointSemantics & RHSSema)437e8d8bef9SDimitry Andric   Value *CreateGT(Value *LHS, const FixedPointSemantics &LHSSema,
438e8d8bef9SDimitry Andric                   Value *RHS, const FixedPointSemantics &RHSSema) {
439e8d8bef9SDimitry Andric     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
440e8d8bef9SDimitry Andric 
441e8d8bef9SDimitry Andric     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
442e8d8bef9SDimitry Andric     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
443e8d8bef9SDimitry Andric 
444e8d8bef9SDimitry Andric     return CommonSema.isSigned() ? B.CreateICmpSGT(WideLHS, WideRHS)
445e8d8bef9SDimitry Andric                                  : B.CreateICmpUGT(WideLHS, WideRHS);
446e8d8bef9SDimitry Andric   }
447e8d8bef9SDimitry Andric 
448e8d8bef9SDimitry Andric   /// Compare two fixed-point values as LHS >= RHS.
449e8d8bef9SDimitry Andric   /// \p LHS     - The left hand side
450e8d8bef9SDimitry Andric   /// \p LHSSema - The semantic of the left hand side
451e8d8bef9SDimitry Andric   /// \p RHS     - The right hand side
452e8d8bef9SDimitry Andric   /// \p RHSSema - The semantic of the right hand side
CreateGE(Value * LHS,const FixedPointSemantics & LHSSema,Value * RHS,const FixedPointSemantics & RHSSema)453e8d8bef9SDimitry Andric   Value *CreateGE(Value *LHS, const FixedPointSemantics &LHSSema,
454e8d8bef9SDimitry Andric                   Value *RHS, const FixedPointSemantics &RHSSema) {
455e8d8bef9SDimitry Andric     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
456e8d8bef9SDimitry Andric 
457e8d8bef9SDimitry Andric     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
458e8d8bef9SDimitry Andric     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
459e8d8bef9SDimitry Andric 
460e8d8bef9SDimitry Andric     return CommonSema.isSigned() ? B.CreateICmpSGE(WideLHS, WideRHS)
461e8d8bef9SDimitry Andric                                  : B.CreateICmpUGE(WideLHS, WideRHS);
462e8d8bef9SDimitry Andric   }
463e8d8bef9SDimitry Andric };
464e8d8bef9SDimitry Andric 
465e8d8bef9SDimitry Andric } // end namespace llvm
466e8d8bef9SDimitry Andric 
467e8d8bef9SDimitry Andric #endif // LLVM_IR_FIXEDPOINTBUILDER_H
468