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