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