Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-10 08:44:00

0001 //===- llvm/FixedPointBuilder.h - Builder for fixed-point ops ---*- C++ -*-===//
0002 //
0003 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
0004 // See https://llvm.org/LICENSE.txt for license information.
0005 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
0006 //
0007 //===----------------------------------------------------------------------===//
0008 //
0009 // This file defines the FixedPointBuilder class, which is used as a convenient
0010 // way to lower fixed-point arithmetic operations to LLVM IR.
0011 //
0012 //===----------------------------------------------------------------------===//
0013 
0014 #ifndef LLVM_IR_FIXEDPOINTBUILDER_H
0015 #define LLVM_IR_FIXEDPOINTBUILDER_H
0016 
0017 #include "llvm/ADT/APFixedPoint.h"
0018 #include "llvm/IR/Constant.h"
0019 #include "llvm/IR/Constants.h"
0020 #include "llvm/IR/IRBuilder.h"
0021 #include "llvm/IR/InstrTypes.h"
0022 #include "llvm/IR/Instruction.h"
0023 #include "llvm/IR/IntrinsicInst.h"
0024 #include "llvm/IR/Intrinsics.h"
0025 #include "llvm/IR/Type.h"
0026 #include "llvm/IR/Value.h"
0027 
0028 #include <cmath>
0029 
0030 namespace llvm {
0031 
0032 template <class IRBuilderTy> class FixedPointBuilder {
0033   IRBuilderTy &B;
0034 
0035   Value *Convert(Value *Src, const FixedPointSemantics &SrcSema,
0036                  const FixedPointSemantics &DstSema, bool DstIsInteger) {
0037     unsigned SrcWidth = SrcSema.getWidth();
0038     unsigned DstWidth = DstSema.getWidth();
0039     unsigned SrcScale = SrcSema.getScale();
0040     unsigned DstScale = DstSema.getScale();
0041     bool SrcIsSigned = SrcSema.isSigned();
0042     bool DstIsSigned = DstSema.isSigned();
0043 
0044     Type *DstIntTy = B.getIntNTy(DstWidth);
0045 
0046     Value *Result = Src;
0047     unsigned ResultWidth = SrcWidth;
0048 
0049     // Downscale.
0050     if (DstScale < SrcScale) {
0051       // When converting to integers, we round towards zero. For negative
0052       // numbers, right shifting rounds towards negative infinity. In this case,
0053       // we can just round up before shifting.
0054       if (DstIsInteger && SrcIsSigned) {
0055         Value *Zero = Constant::getNullValue(Result->getType());
0056         Value *IsNegative = B.CreateICmpSLT(Result, Zero);
0057         Value *LowBits = ConstantInt::get(
0058             B.getContext(), APInt::getLowBitsSet(ResultWidth, SrcScale));
0059         Value *Rounded = B.CreateAdd(Result, LowBits);
0060         Result = B.CreateSelect(IsNegative, Rounded, Result);
0061       }
0062 
0063       Result = SrcIsSigned
0064                    ? B.CreateAShr(Result, SrcScale - DstScale, "downscale")
0065                    : B.CreateLShr(Result, SrcScale - DstScale, "downscale");
0066     }
0067 
0068     if (!DstSema.isSaturated()) {
0069       // Resize.
0070       Result = B.CreateIntCast(Result, DstIntTy, SrcIsSigned, "resize");
0071 
0072       // Upscale.
0073       if (DstScale > SrcScale)
0074         Result = B.CreateShl(Result, DstScale - SrcScale, "upscale");
0075     } else {
0076       // Adjust the number of fractional bits.
0077       if (DstScale > SrcScale) {
0078         // Compare to DstWidth to prevent resizing twice.
0079         ResultWidth = std::max(SrcWidth + DstScale - SrcScale, DstWidth);
0080         Type *UpscaledTy = B.getIntNTy(ResultWidth);
0081         Result = B.CreateIntCast(Result, UpscaledTy, SrcIsSigned, "resize");
0082         Result = B.CreateShl(Result, DstScale - SrcScale, "upscale");
0083       }
0084 
0085       // Handle saturation.
0086       bool LessIntBits = DstSema.getIntegralBits() < SrcSema.getIntegralBits();
0087       if (LessIntBits) {
0088         Value *Max = ConstantInt::get(
0089             B.getContext(),
0090             APFixedPoint::getMax(DstSema).getValue().extOrTrunc(ResultWidth));
0091         Value *TooHigh = SrcIsSigned ? B.CreateICmpSGT(Result, Max)
0092                                      : B.CreateICmpUGT(Result, Max);
0093         Result = B.CreateSelect(TooHigh, Max, Result, "satmax");
0094       }
0095       // Cannot overflow min to dest type if src is unsigned since all fixed
0096       // point types can cover the unsigned min of 0.
0097       if (SrcIsSigned && (LessIntBits || !DstIsSigned)) {
0098         Value *Min = ConstantInt::get(
0099             B.getContext(),
0100             APFixedPoint::getMin(DstSema).getValue().extOrTrunc(ResultWidth));
0101         Value *TooLow = B.CreateICmpSLT(Result, Min);
0102         Result = B.CreateSelect(TooLow, Min, Result, "satmin");
0103       }
0104 
0105       // Resize the integer part to get the final destination size.
0106       if (ResultWidth != DstWidth)
0107         Result = B.CreateIntCast(Result, DstIntTy, SrcIsSigned, "resize");
0108     }
0109     return Result;
0110   }
0111 
0112   /// Get the common semantic for two semantics, with the added imposition that
0113   /// saturated padded types retain the padding bit.
0114   FixedPointSemantics
0115   getCommonBinopSemantic(const FixedPointSemantics &LHSSema,
0116                          const FixedPointSemantics &RHSSema) {
0117     auto C = LHSSema.getCommonSemantics(RHSSema);
0118     bool BothPadded =
0119         LHSSema.hasUnsignedPadding() && RHSSema.hasUnsignedPadding();
0120     return FixedPointSemantics(
0121         C.getWidth() + (unsigned)(BothPadded && C.isSaturated()), C.getScale(),
0122         C.isSigned(), C.isSaturated(), BothPadded);
0123   }
0124 
0125   /// Given a floating point type and a fixed-point semantic, return a floating
0126   /// point type which can accommodate the fixed-point semantic. This is either
0127   /// \p Ty, or a floating point type with a larger exponent than Ty.
0128   Type *getAccommodatingFloatType(Type *Ty, const FixedPointSemantics &Sema) {
0129     const fltSemantics *FloatSema = &Ty->getFltSemantics();
0130     while (!Sema.fitsInFloatSemantics(*FloatSema))
0131       FloatSema = APFixedPoint::promoteFloatSemantics(FloatSema);
0132     return Type::getFloatingPointTy(Ty->getContext(), *FloatSema);
0133   }
0134 
0135 public:
0136   FixedPointBuilder(IRBuilderTy &Builder) : B(Builder) {}
0137 
0138   /// Convert an integer value representing a fixed-point number from one
0139   /// fixed-point semantic to another fixed-point semantic.
0140   /// \p Src     - The source value
0141   /// \p SrcSema - The fixed-point semantic of the source value
0142   /// \p DstSema - The resulting fixed-point semantic
0143   Value *CreateFixedToFixed(Value *Src, const FixedPointSemantics &SrcSema,
0144                             const FixedPointSemantics &DstSema) {
0145     return Convert(Src, SrcSema, DstSema, false);
0146   }
0147 
0148   /// Convert an integer value representing a fixed-point number to an integer
0149   /// with the given bit width and signedness.
0150   /// \p Src         - The source value
0151   /// \p SrcSema     - The fixed-point semantic of the source value
0152   /// \p DstWidth    - The bit width of the result value
0153   /// \p DstIsSigned - The signedness of the result value
0154   Value *CreateFixedToInteger(Value *Src, const FixedPointSemantics &SrcSema,
0155                               unsigned DstWidth, bool DstIsSigned) {
0156     return Convert(
0157         Src, SrcSema,
0158         FixedPointSemantics::GetIntegerSemantics(DstWidth, DstIsSigned), true);
0159   }
0160 
0161   /// Convert an integer value with the given signedness to an integer value
0162   /// representing the given fixed-point semantic.
0163   /// \p Src         - The source value
0164   /// \p SrcIsSigned - The signedness of the source value
0165   /// \p DstSema     - The resulting fixed-point semantic
0166   Value *CreateIntegerToFixed(Value *Src, unsigned SrcIsSigned,
0167                               const FixedPointSemantics &DstSema) {
0168     return Convert(Src,
0169                    FixedPointSemantics::GetIntegerSemantics(
0170                        Src->getType()->getScalarSizeInBits(), SrcIsSigned),
0171                    DstSema, false);
0172   }
0173 
0174   Value *CreateFixedToFloating(Value *Src, const FixedPointSemantics &SrcSema,
0175                                Type *DstTy) {
0176     Value *Result;
0177     Type *OpTy = getAccommodatingFloatType(DstTy, SrcSema);
0178     // Convert the raw fixed-point value directly to floating point. If the
0179     // value is too large to fit, it will be rounded, not truncated.
0180     Result = SrcSema.isSigned() ? B.CreateSIToFP(Src, OpTy)
0181                                 : B.CreateUIToFP(Src, OpTy);
0182     // Rescale the integral-in-floating point by the scaling factor. This is
0183     // lossless, except for overflow to infinity which is unlikely.
0184     Result = B.CreateFMul(Result,
0185         ConstantFP::get(OpTy, std::pow(2, -(int)SrcSema.getScale())));
0186     if (OpTy != DstTy)
0187       Result = B.CreateFPTrunc(Result, DstTy);
0188     return Result;
0189   }
0190 
0191   Value *CreateFloatingToFixed(Value *Src, const FixedPointSemantics &DstSema) {
0192     bool UseSigned = DstSema.isSigned() || DstSema.hasUnsignedPadding();
0193     Value *Result = Src;
0194     Type *OpTy = getAccommodatingFloatType(Src->getType(), DstSema);
0195     if (OpTy != Src->getType())
0196       Result = B.CreateFPExt(Result, OpTy);
0197     // Rescale the floating point value so that its significant bits (for the
0198     // purposes of the conversion) are in the integral range.
0199     Result = B.CreateFMul(Result,
0200         ConstantFP::get(OpTy, std::pow(2, DstSema.getScale())));
0201 
0202     Type *ResultTy = B.getIntNTy(DstSema.getWidth());
0203     if (DstSema.isSaturated()) {
0204       Intrinsic::ID IID =
0205           UseSigned ? Intrinsic::fptosi_sat : Intrinsic::fptoui_sat;
0206       Result = B.CreateIntrinsic(IID, {ResultTy, OpTy}, {Result});
0207     } else {
0208       Result = UseSigned ? B.CreateFPToSI(Result, ResultTy)
0209                          : B.CreateFPToUI(Result, ResultTy);
0210     }
0211 
0212     // When saturating unsigned-with-padding using signed operations, we may
0213     // get negative values. Emit an extra clamp to zero.
0214     if (DstSema.isSaturated() && DstSema.hasUnsignedPadding()) {
0215       Constant *Zero = Constant::getNullValue(Result->getType());
0216       Result =
0217           B.CreateSelect(B.CreateICmpSLT(Result, Zero), Zero, Result, "satmin");
0218     }
0219 
0220     return Result;
0221   }
0222 
0223   /// Add two fixed-point values and return the result in their common semantic.
0224   /// \p LHS     - The left hand side
0225   /// \p LHSSema - The semantic of the left hand side
0226   /// \p RHS     - The right hand side
0227   /// \p RHSSema - The semantic of the right hand side
0228   Value *CreateAdd(Value *LHS, const FixedPointSemantics &LHSSema,
0229                    Value *RHS, const FixedPointSemantics &RHSSema) {
0230     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
0231     bool UseSigned = CommonSema.isSigned() || CommonSema.hasUnsignedPadding();
0232 
0233     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
0234     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
0235 
0236     Value *Result;
0237     if (CommonSema.isSaturated()) {
0238       Intrinsic::ID IID = UseSigned ? Intrinsic::sadd_sat : Intrinsic::uadd_sat;
0239       Result = B.CreateBinaryIntrinsic(IID, WideLHS, WideRHS);
0240     } else {
0241       Result = B.CreateAdd(WideLHS, WideRHS);
0242     }
0243 
0244     return CreateFixedToFixed(Result, CommonSema,
0245                               LHSSema.getCommonSemantics(RHSSema));
0246   }
0247 
0248   /// Subtract two fixed-point values and return the result in their common
0249   /// semantic.
0250   /// \p LHS     - The left hand side
0251   /// \p LHSSema - The semantic of the left hand side
0252   /// \p RHS     - The right hand side
0253   /// \p RHSSema - The semantic of the right hand side
0254   Value *CreateSub(Value *LHS, const FixedPointSemantics &LHSSema,
0255                    Value *RHS, const FixedPointSemantics &RHSSema) {
0256     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
0257     bool UseSigned = CommonSema.isSigned() || CommonSema.hasUnsignedPadding();
0258 
0259     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
0260     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
0261 
0262     Value *Result;
0263     if (CommonSema.isSaturated()) {
0264       Intrinsic::ID IID = UseSigned ? Intrinsic::ssub_sat : Intrinsic::usub_sat;
0265       Result = B.CreateBinaryIntrinsic(IID, WideLHS, WideRHS);
0266     } else {
0267       Result = B.CreateSub(WideLHS, WideRHS);
0268     }
0269 
0270     // Subtraction can end up below 0 for padded unsigned operations, so emit
0271     // an extra clamp in that case.
0272     if (CommonSema.isSaturated() && CommonSema.hasUnsignedPadding()) {
0273       Constant *Zero = Constant::getNullValue(Result->getType());
0274       Result =
0275           B.CreateSelect(B.CreateICmpSLT(Result, Zero), Zero, Result, "satmin");
0276     }
0277 
0278     return CreateFixedToFixed(Result, CommonSema,
0279                               LHSSema.getCommonSemantics(RHSSema));
0280   }
0281 
0282   /// Multiply two fixed-point values and return the result in their common
0283   /// semantic.
0284   /// \p LHS     - The left hand side
0285   /// \p LHSSema - The semantic of the left hand side
0286   /// \p RHS     - The right hand side
0287   /// \p RHSSema - The semantic of the right hand side
0288   Value *CreateMul(Value *LHS, const FixedPointSemantics &LHSSema,
0289                    Value *RHS, const FixedPointSemantics &RHSSema) {
0290     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
0291     bool UseSigned = CommonSema.isSigned() || CommonSema.hasUnsignedPadding();
0292 
0293     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
0294     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
0295 
0296     Intrinsic::ID IID;
0297     if (CommonSema.isSaturated()) {
0298       IID = UseSigned ? Intrinsic::smul_fix_sat : Intrinsic::umul_fix_sat;
0299     } else {
0300       IID = UseSigned ? Intrinsic::smul_fix : Intrinsic::umul_fix;
0301     }
0302     Value *Result = B.CreateIntrinsic(
0303         IID, {WideLHS->getType()},
0304         {WideLHS, WideRHS, B.getInt32(CommonSema.getScale())});
0305 
0306     return CreateFixedToFixed(Result, CommonSema,
0307                               LHSSema.getCommonSemantics(RHSSema));
0308   }
0309 
0310   /// Divide two fixed-point values and return the result in their common
0311   /// semantic.
0312   /// \p LHS     - The left hand side
0313   /// \p LHSSema - The semantic of the left hand side
0314   /// \p RHS     - The right hand side
0315   /// \p RHSSema - The semantic of the right hand side
0316   Value *CreateDiv(Value *LHS, const FixedPointSemantics &LHSSema,
0317                    Value *RHS, const FixedPointSemantics &RHSSema) {
0318     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
0319     bool UseSigned = CommonSema.isSigned() || CommonSema.hasUnsignedPadding();
0320 
0321     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
0322     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
0323 
0324     Intrinsic::ID IID;
0325     if (CommonSema.isSaturated()) {
0326       IID = UseSigned ? Intrinsic::sdiv_fix_sat : Intrinsic::udiv_fix_sat;
0327     } else {
0328       IID = UseSigned ? Intrinsic::sdiv_fix : Intrinsic::udiv_fix;
0329     }
0330     Value *Result = B.CreateIntrinsic(
0331         IID, {WideLHS->getType()},
0332         {WideLHS, WideRHS, B.getInt32(CommonSema.getScale())});
0333 
0334     return CreateFixedToFixed(Result, CommonSema,
0335                               LHSSema.getCommonSemantics(RHSSema));
0336   }
0337 
0338   /// Left shift a fixed-point value by an unsigned integer value. The integer
0339   /// value can be any bit width.
0340   /// \p LHS     - The left hand side
0341   /// \p LHSSema - The semantic of the left hand side
0342   /// \p RHS     - The right hand side
0343   Value *CreateShl(Value *LHS, const FixedPointSemantics &LHSSema, Value *RHS) {
0344     bool UseSigned = LHSSema.isSigned() || LHSSema.hasUnsignedPadding();
0345 
0346     RHS = B.CreateIntCast(RHS, LHS->getType(), /*IsSigned=*/false);
0347 
0348     Value *Result;
0349     if (LHSSema.isSaturated()) {
0350       Intrinsic::ID IID = UseSigned ? Intrinsic::sshl_sat : Intrinsic::ushl_sat;
0351       Result = B.CreateBinaryIntrinsic(IID, LHS, RHS);
0352     } else {
0353       Result = B.CreateShl(LHS, RHS);
0354     }
0355 
0356     return Result;
0357   }
0358 
0359   /// Right shift a fixed-point value by an unsigned integer value. The integer
0360   /// value can be any bit width.
0361   /// \p LHS     - The left hand side
0362   /// \p LHSSema - The semantic of the left hand side
0363   /// \p RHS     - The right hand side
0364   Value *CreateShr(Value *LHS, const FixedPointSemantics &LHSSema, Value *RHS) {
0365     RHS = B.CreateIntCast(RHS, LHS->getType(), false);
0366 
0367     return LHSSema.isSigned() ? B.CreateAShr(LHS, RHS) : B.CreateLShr(LHS, RHS);
0368   }
0369 
0370   /// Compare two fixed-point values for equality.
0371   /// \p LHS     - The left hand side
0372   /// \p LHSSema - The semantic of the left hand side
0373   /// \p RHS     - The right hand side
0374   /// \p RHSSema - The semantic of the right hand side
0375   Value *CreateEQ(Value *LHS, const FixedPointSemantics &LHSSema,
0376                   Value *RHS, const FixedPointSemantics &RHSSema) {
0377     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
0378 
0379     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
0380     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
0381 
0382     return B.CreateICmpEQ(WideLHS, WideRHS);
0383   }
0384 
0385   /// Compare two fixed-point values for inequality.
0386   /// \p LHS     - The left hand side
0387   /// \p LHSSema - The semantic of the left hand side
0388   /// \p RHS     - The right hand side
0389   /// \p RHSSema - The semantic of the right hand side
0390   Value *CreateNE(Value *LHS, const FixedPointSemantics &LHSSema,
0391                   Value *RHS, const FixedPointSemantics &RHSSema) {
0392     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
0393 
0394     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
0395     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
0396 
0397     return B.CreateICmpNE(WideLHS, WideRHS);
0398   }
0399 
0400   /// Compare two fixed-point values as LHS < RHS.
0401   /// \p LHS     - The left hand side
0402   /// \p LHSSema - The semantic of the left hand side
0403   /// \p RHS     - The right hand side
0404   /// \p RHSSema - The semantic of the right hand side
0405   Value *CreateLT(Value *LHS, const FixedPointSemantics &LHSSema,
0406                   Value *RHS, const FixedPointSemantics &RHSSema) {
0407     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
0408 
0409     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
0410     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
0411 
0412     return CommonSema.isSigned() ? B.CreateICmpSLT(WideLHS, WideRHS)
0413                                  : B.CreateICmpULT(WideLHS, WideRHS);
0414   }
0415 
0416   /// Compare two fixed-point values as LHS <= RHS.
0417   /// \p LHS     - The left hand side
0418   /// \p LHSSema - The semantic of the left hand side
0419   /// \p RHS     - The right hand side
0420   /// \p RHSSema - The semantic of the right hand side
0421   Value *CreateLE(Value *LHS, const FixedPointSemantics &LHSSema,
0422                   Value *RHS, const FixedPointSemantics &RHSSema) {
0423     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
0424 
0425     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
0426     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
0427 
0428     return CommonSema.isSigned() ? B.CreateICmpSLE(WideLHS, WideRHS)
0429                                  : B.CreateICmpULE(WideLHS, WideRHS);
0430   }
0431 
0432   /// Compare two fixed-point values as LHS > RHS.
0433   /// \p LHS     - The left hand side
0434   /// \p LHSSema - The semantic of the left hand side
0435   /// \p RHS     - The right hand side
0436   /// \p RHSSema - The semantic of the right hand side
0437   Value *CreateGT(Value *LHS, const FixedPointSemantics &LHSSema,
0438                   Value *RHS, const FixedPointSemantics &RHSSema) {
0439     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
0440 
0441     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
0442     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
0443 
0444     return CommonSema.isSigned() ? B.CreateICmpSGT(WideLHS, WideRHS)
0445                                  : B.CreateICmpUGT(WideLHS, WideRHS);
0446   }
0447 
0448   /// Compare two fixed-point values as LHS >= RHS.
0449   /// \p LHS     - The left hand side
0450   /// \p LHSSema - The semantic of the left hand side
0451   /// \p RHS     - The right hand side
0452   /// \p RHSSema - The semantic of the right hand side
0453   Value *CreateGE(Value *LHS, const FixedPointSemantics &LHSSema,
0454                   Value *RHS, const FixedPointSemantics &RHSSema) {
0455     auto CommonSema = getCommonBinopSemantic(LHSSema, RHSSema);
0456 
0457     Value *WideLHS = CreateFixedToFixed(LHS, LHSSema, CommonSema);
0458     Value *WideRHS = CreateFixedToFixed(RHS, RHSSema, CommonSema);
0459 
0460     return CommonSema.isSigned() ? B.CreateICmpSGE(WideLHS, WideRHS)
0461                                  : B.CreateICmpUGE(WideLHS, WideRHS);
0462   }
0463 };
0464 
0465 } // end namespace llvm
0466 
0467 #endif // LLVM_IR_FIXEDPOINTBUILDER_H