#include "llvm/IR/CFG.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/FixedPointBuilder.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GetElementPtrTypeIterator.h"
#include "llvm/IR/GlobalVariable.h"
/// and an integer.
Value *EmitFixedPointConversion(Value *Src, QualType SrcTy, QualType DstTy,
SourceLocation Loc);
- Value *EmitFixedPointConversion(Value *Src,
- llvm::FixedPointSemantics &SrcFixedSema,
- llvm::FixedPointSemantics &DstFixedSema,
- SourceLocation Loc,
- bool DstIsInteger = false);
/// Emit a conversion from the specified complex type to the specified
/// destination type, where the destination type is an LLVM scalar type.
SourceLocation Loc) {
auto SrcFPSema = CGF.getContext().getFixedPointSemantics(SrcTy);
auto DstFPSema = CGF.getContext().getFixedPointSemantics(DstTy);
- return EmitFixedPointConversion(Src, SrcFPSema, DstFPSema, Loc,
- DstTy->isIntegerType());
-}
-
-Value *ScalarExprEmitter::EmitFixedPointConversion(
- Value *Src, llvm::FixedPointSemantics &SrcFPSema,
- llvm::FixedPointSemantics &DstFPSema,
- SourceLocation Loc, bool DstIsInteger) {
- using llvm::APFixedPoint;
- using llvm::APInt;
- using llvm::ConstantInt;
- using llvm::Value;
-
- unsigned SrcWidth = SrcFPSema.getWidth();
- unsigned DstWidth = DstFPSema.getWidth();
- unsigned SrcScale = SrcFPSema.getScale();
- unsigned DstScale = DstFPSema.getScale();
- bool SrcIsSigned = SrcFPSema.isSigned();
- bool DstIsSigned = DstFPSema.isSigned();
-
- llvm::Type *DstIntTy = Builder.getIntNTy(DstWidth);
-
- Value *Result = Src;
- unsigned ResultWidth = SrcWidth;
-
- // Downscale.
- if (DstScale < SrcScale) {
- // When converting to integers, we round towards zero. For negative numbers,
- // right shifting rounds towards negative infinity. In this case, we can
- // just round up before shifting.
- if (DstIsInteger && SrcIsSigned) {
- Value *Zero = llvm::Constant::getNullValue(Result->getType());
- Value *IsNegative = Builder.CreateICmpSLT(Result, Zero);
- Value *LowBits = ConstantInt::get(
- CGF.getLLVMContext(), APInt::getLowBitsSet(ResultWidth, SrcScale));
- Value *Rounded = Builder.CreateAdd(Result, LowBits);
- Result = Builder.CreateSelect(IsNegative, Rounded, Result);
- }
-
- Result = SrcIsSigned
- ? Builder.CreateAShr(Result, SrcScale - DstScale, "downscale")
- : Builder.CreateLShr(Result, SrcScale - DstScale, "downscale");
- }
-
- if (!DstFPSema.isSaturated()) {
- // Resize.
- Result = Builder.CreateIntCast(Result, DstIntTy, SrcIsSigned, "resize");
-
- // Upscale.
- if (DstScale > SrcScale)
- Result = Builder.CreateShl(Result, DstScale - SrcScale, "upscale");
- } else {
- // Adjust the number of fractional bits.
- if (DstScale > SrcScale) {
- // Compare to DstWidth to prevent resizing twice.
- ResultWidth = std::max(SrcWidth + DstScale - SrcScale, DstWidth);
- llvm::Type *UpscaledTy = Builder.getIntNTy(ResultWidth);
- Result = Builder.CreateIntCast(Result, UpscaledTy, SrcIsSigned, "resize");
- Result = Builder.CreateShl(Result, DstScale - SrcScale, "upscale");
- }
-
- // Handle saturation.
- bool LessIntBits = DstFPSema.getIntegralBits() < SrcFPSema.getIntegralBits();
- if (LessIntBits) {
- Value *Max = ConstantInt::get(
- CGF.getLLVMContext(),
- APFixedPoint::getMax(DstFPSema).getValue().extOrTrunc(ResultWidth));
- Value *TooHigh = SrcIsSigned ? Builder.CreateICmpSGT(Result, Max)
- : Builder.CreateICmpUGT(Result, Max);
- Result = Builder.CreateSelect(TooHigh, Max, Result, "satmax");
- }
- // Cannot overflow min to dest type if src is unsigned since all fixed
- // point types can cover the unsigned min of 0.
- if (SrcIsSigned && (LessIntBits || !DstIsSigned)) {
- Value *Min = ConstantInt::get(
- CGF.getLLVMContext(),
- APFixedPoint::getMin(DstFPSema).getValue().extOrTrunc(ResultWidth));
- Value *TooLow = Builder.CreateICmpSLT(Result, Min);
- Result = Builder.CreateSelect(TooLow, Min, Result, "satmin");
- }
-
- // Resize the integer part to get the final destination size.
- if (ResultWidth != DstWidth)
- Result = Builder.CreateIntCast(Result, DstIntTy, SrcIsSigned, "resize");
- }
+ llvm::FixedPointBuilder<CGBuilderTy> FPBuilder(Builder);
+ llvm::Value *Result;
+ if (DstTy->isIntegerType())
+ Result = FPBuilder.CreateFixedToInteger(Src, SrcFPSema,
+ DstFPSema.getWidth(),
+ DstFPSema.isSigned());
+ else if (SrcTy->isIntegerType())
+ Result = FPBuilder.CreateIntegerToFixed(Src, SrcFPSema.isSigned(),
+ DstFPSema);
+ else
+ Result = FPBuilder.CreateFixedToFixed(Src, SrcFPSema, DstFPSema);
return Result;
}
// Now, convert from our invented integer literal to the type of the unary
// op. This will upscale and saturate if necessary. This value can become
// undef in some cases.
- auto SrcSema =
- llvm::FixedPointSemantics::GetIntegerSemantics(
- value->getType()->getScalarSizeInBits(), /*IsSigned=*/true);
+ llvm::FixedPointBuilder<CGBuilderTy> FPBuilder(Builder);
auto DstSema = CGF.getContext().getFixedPointSemantics(Info.Ty);
- Info.RHS = EmitFixedPointConversion(Info.RHS, SrcSema, DstSema,
- E->getExprLoc());
+ Info.RHS = FPBuilder.CreateIntegerToFixed(Info.RHS, true, DstSema);
value = EmitFixedPointBinOp(Info);
// Objective-C pointer types.
auto ResultFixedSema = Ctx.getFixedPointSemantics(ResultTy);
auto CommonFixedSema = LHSFixedSema.getCommonSemantics(RHSFixedSema);
- // Convert the operands to the full precision type.
- Value *FullLHS = EmitFixedPointConversion(LHS, LHSFixedSema, CommonFixedSema,
- op.E->getExprLoc());
- Value *FullRHS = EmitFixedPointConversion(RHS, RHSFixedSema, CommonFixedSema,
- op.E->getExprLoc());
-
// Perform the actual operation.
Value *Result;
+ llvm::FixedPointBuilder<CGBuilderTy> FPBuilder(Builder);
switch (op.Opcode) {
case BO_AddAssign:
- case BO_Add: {
- if (CommonFixedSema.isSaturated()) {
- llvm::Intrinsic::ID IID = CommonFixedSema.isSigned()
- ? llvm::Intrinsic::sadd_sat
- : llvm::Intrinsic::uadd_sat;
- Result = Builder.CreateBinaryIntrinsic(IID, FullLHS, FullRHS);
- } else {
- Result = Builder.CreateAdd(FullLHS, FullRHS);
- }
+ case BO_Add:
+ Result = FPBuilder.CreateAdd(LHS, LHSFixedSema, RHS, RHSFixedSema);
break;
- }
case BO_SubAssign:
- case BO_Sub: {
- if (CommonFixedSema.isSaturated()) {
- llvm::Intrinsic::ID IID = CommonFixedSema.isSigned()
- ? llvm::Intrinsic::ssub_sat
- : llvm::Intrinsic::usub_sat;
- Result = Builder.CreateBinaryIntrinsic(IID, FullLHS, FullRHS);
- } else {
- Result = Builder.CreateSub(FullLHS, FullRHS);
- }
+ case BO_Sub:
+ Result = FPBuilder.CreateSub(LHS, LHSFixedSema, RHS, RHSFixedSema);
break;
- }
case BO_MulAssign:
- case BO_Mul: {
- llvm::Intrinsic::ID IID;
- if (CommonFixedSema.isSaturated())
- IID = CommonFixedSema.isSigned() ? llvm::Intrinsic::smul_fix_sat
- : llvm::Intrinsic::umul_fix_sat;
- else
- IID = CommonFixedSema.isSigned() ? llvm::Intrinsic::smul_fix
- : llvm::Intrinsic::umul_fix;
- Result = Builder.CreateIntrinsic(IID, {FullLHS->getType()},
- {FullLHS, FullRHS, Builder.getInt32(CommonFixedSema.getScale())});
+ case BO_Mul:
+ Result = FPBuilder.CreateMul(LHS, LHSFixedSema, RHS, RHSFixedSema);
break;
- }
case BO_DivAssign:
- case BO_Div: {
- llvm::Intrinsic::ID IID;
- if (CommonFixedSema.isSaturated())
- IID = CommonFixedSema.isSigned() ? llvm::Intrinsic::sdiv_fix_sat
- : llvm::Intrinsic::udiv_fix_sat;
- else
- IID = CommonFixedSema.isSigned() ? llvm::Intrinsic::sdiv_fix
- : llvm::Intrinsic::udiv_fix;
- Result = Builder.CreateIntrinsic(IID, {FullLHS->getType()},
- {FullLHS, FullRHS, Builder.getInt32(CommonFixedSema.getScale())});
- break;
- }
+ case BO_Div:
+ Result = FPBuilder.CreateDiv(LHS, LHSFixedSema, RHS, RHSFixedSema);
+ break;
case BO_LT:
- return CommonFixedSema.isSigned() ? Builder.CreateICmpSLT(FullLHS, FullRHS)
- : Builder.CreateICmpULT(FullLHS, FullRHS);
+ return FPBuilder.CreateLT(LHS, LHSFixedSema, RHS, RHSFixedSema);
case BO_GT:
- return CommonFixedSema.isSigned() ? Builder.CreateICmpSGT(FullLHS, FullRHS)
- : Builder.CreateICmpUGT(FullLHS, FullRHS);
+ return FPBuilder.CreateGT(LHS, LHSFixedSema, RHS, RHSFixedSema);
case BO_LE:
- return CommonFixedSema.isSigned() ? Builder.CreateICmpSLE(FullLHS, FullRHS)
- : Builder.CreateICmpULE(FullLHS, FullRHS);
+ return FPBuilder.CreateLE(LHS, LHSFixedSema, RHS, RHSFixedSema);
case BO_GE:
- return CommonFixedSema.isSigned() ? Builder.CreateICmpSGE(FullLHS, FullRHS)
- : Builder.CreateICmpUGE(FullLHS, FullRHS);
+ return FPBuilder.CreateGE(LHS, LHSFixedSema, RHS, RHSFixedSema);
case BO_EQ:
// For equality operations, we assume any padding bits on unsigned types are
// zero'd out. They could be overwritten through non-saturating operations
// that cause overflow, but this leads to undefined behavior.
- return Builder.CreateICmpEQ(FullLHS, FullRHS);
+ return FPBuilder.CreateEQ(LHS, LHSFixedSema, RHS, RHSFixedSema);
case BO_NE:
- return Builder.CreateICmpNE(FullLHS, FullRHS);
+ return FPBuilder.CreateNE(LHS, LHSFixedSema, RHS, RHSFixedSema);
case BO_Shl:
case BO_Shr:
case BO_Cmp:
}
// Convert to the result type.
- return EmitFixedPointConversion(Result, CommonFixedSema, ResultFixedSema,
- op.E->getExprLoc());
+ return FPBuilder.CreateFixedToFixed(Result, CommonFixedSema, ResultFixedSema);
}
Value *ScalarExprEmitter::EmitSub(const BinOpInfo &op) {
// UNSIGNED-NEXT: entry:
// UNSIGNED-NEXT: [[TMP0:%.*]] = load i16, i16* @usa, align 2
// UNSIGNED-NEXT: [[TMP1:%.*]] = load i16, i16* @usa_sat, align 2
-// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i16 [[TMP0]] to i15
-// UNSIGNED-NEXT: [[RESIZE1:%.*]] = trunc i16 [[TMP1]] to i15
-// UNSIGNED-NEXT: [[TMP2:%.*]] = call i15 @llvm.uadd.sat.i15(i15 [[RESIZE]], i15 [[RESIZE1]])
-// UNSIGNED-NEXT: [[RESIZE2:%.*]] = zext i15 [[TMP2]] to i16
-// UNSIGNED-NEXT: store i16 [[RESIZE2]], i16* @usa_sat, align 2
+// UNSIGNED-NEXT: [[TMP2:%.*]] = call i16 @llvm.sadd.sat.i16(i16 [[TMP0]], i16 [[TMP1]])
+// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i16 [[TMP2]] to i15
+// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i15 [[RESIZE]] to i16
+// UNSIGNED-NEXT: store i16 [[RESIZE1]], i16* @usa_sat, align 2
// UNSIGNED-NEXT: ret void
//
void sat_usasusausas() {
// UNSIGNED-NEXT: entry:
// UNSIGNED-NEXT: [[TMP0:%.*]] = load i32, i32* @ua, align 4
// UNSIGNED-NEXT: [[TMP1:%.*]] = load i16, i16* @usa_sat, align 2
-// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i32 [[TMP0]] to i31
-// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i16 [[TMP1]] to i31
-// UNSIGNED-NEXT: [[UPSCALE:%.*]] = shl i31 [[RESIZE1]], 8
-// UNSIGNED-NEXT: [[TMP2:%.*]] = call i31 @llvm.uadd.sat.i31(i31 [[RESIZE]], i31 [[UPSCALE]])
-// UNSIGNED-NEXT: [[RESIZE2:%.*]] = zext i31 [[TMP2]] to i32
+// UNSIGNED-NEXT: [[RESIZE:%.*]] = zext i16 [[TMP1]] to i32
+// UNSIGNED-NEXT: [[UPSCALE:%.*]] = shl i32 [[RESIZE]], 8
+// UNSIGNED-NEXT: [[TMP2:%.*]] = call i32 @llvm.sadd.sat.i32(i32 [[TMP0]], i32 [[UPSCALE]])
+// UNSIGNED-NEXT: [[RESIZE1:%.*]] = trunc i32 [[TMP2]] to i31
+// UNSIGNED-NEXT: [[RESIZE2:%.*]] = zext i31 [[RESIZE1]] to i32
// UNSIGNED-NEXT: store i32 [[RESIZE2]], i32* @ua_sat, align 4
// UNSIGNED-NEXT: ret void
//
// UNSIGNED-NEXT: entry:
// UNSIGNED-NEXT: [[TMP0:%.*]] = load i16, i16* @uf_sat, align 2
// UNSIGNED-NEXT: [[TMP1:%.*]] = load i16, i16* @uf_sat, align 2
-// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i16 [[TMP0]] to i15
-// UNSIGNED-NEXT: [[RESIZE1:%.*]] = trunc i16 [[TMP1]] to i15
-// UNSIGNED-NEXT: [[TMP2:%.*]] = call i15 @llvm.uadd.sat.i15(i15 [[RESIZE]], i15 [[RESIZE1]])
-// UNSIGNED-NEXT: [[RESIZE2:%.*]] = zext i15 [[TMP2]] to i16
-// UNSIGNED-NEXT: store i16 [[RESIZE2]], i16* @uf_sat, align 2
+// UNSIGNED-NEXT: [[TMP2:%.*]] = call i16 @llvm.sadd.sat.i16(i16 [[TMP0]], i16 [[TMP1]])
+// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i16 [[TMP2]] to i15
+// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i15 [[RESIZE]] to i16
+// UNSIGNED-NEXT: store i16 [[RESIZE1]], i16* @uf_sat, align 2
// UNSIGNED-NEXT: ret void
//
void sat_ufsufsufs() {
// UNSIGNED-NEXT: entry:
// UNSIGNED-NEXT: [[TMP0:%.*]] = load i16, i16* @usa, align 2
// UNSIGNED-NEXT: [[TMP1:%.*]] = load i16, i16* @usa, align 2
-// UNSIGNED-NEXT: [[TMP2:%.*]] = call i16 @llvm.udiv.fix.i16(i16 [[TMP0]], i16 [[TMP1]], i32 7)
+// UNSIGNED-NEXT: [[TMP2:%.*]] = call i16 @llvm.sdiv.fix.i16(i16 [[TMP0]], i16 [[TMP1]], i32 7)
// UNSIGNED-NEXT: store i16 [[TMP2]], i16* @usa, align 2
// UNSIGNED-NEXT: ret void
//
// UNSIGNED-NEXT: [[TMP1:%.*]] = load i32, i32* @ua, align 4
// UNSIGNED-NEXT: [[RESIZE:%.*]] = zext i16 [[TMP0]] to i32
// UNSIGNED-NEXT: [[UPSCALE:%.*]] = shl i32 [[RESIZE]], 8
-// UNSIGNED-NEXT: [[TMP2:%.*]] = call i32 @llvm.udiv.fix.i32(i32 [[UPSCALE]], i32 [[TMP1]], i32 15)
+// UNSIGNED-NEXT: [[TMP2:%.*]] = call i32 @llvm.sdiv.fix.i32(i32 [[UPSCALE]], i32 [[TMP1]], i32 15)
// UNSIGNED-NEXT: store i32 [[TMP2]], i32* @ua, align 4
// UNSIGNED-NEXT: ret void
//
// UNSIGNED-NEXT: [[TMP0:%.*]] = load i16, i16* @usa, align 2
// UNSIGNED-NEXT: [[TMP1:%.*]] = load i8, i8* @usf, align 1
// UNSIGNED-NEXT: [[RESIZE:%.*]] = zext i8 [[TMP1]] to i16
-// UNSIGNED-NEXT: [[TMP2:%.*]] = call i16 @llvm.udiv.fix.i16(i16 [[TMP0]], i16 [[RESIZE]], i32 7)
+// UNSIGNED-NEXT: [[TMP2:%.*]] = call i16 @llvm.sdiv.fix.i16(i16 [[TMP0]], i16 [[RESIZE]], i32 7)
// UNSIGNED-NEXT: store i16 [[TMP2]], i16* @usa, align 2
// UNSIGNED-NEXT: ret void
//
// UNSIGNED-NEXT: [[RESIZE:%.*]] = zext i16 [[TMP0]] to i24
// UNSIGNED-NEXT: [[UPSCALE:%.*]] = shl i24 [[RESIZE]], 8
// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i16 [[TMP1]] to i24
-// UNSIGNED-NEXT: [[TMP2:%.*]] = call i24 @llvm.udiv.fix.i24(i24 [[UPSCALE]], i24 [[RESIZE1]], i32 15)
+// UNSIGNED-NEXT: [[TMP2:%.*]] = call i24 @llvm.sdiv.fix.i24(i24 [[UPSCALE]], i24 [[RESIZE1]], i32 15)
// UNSIGNED-NEXT: [[DOWNSCALE:%.*]] = lshr i24 [[TMP2]], 8
// UNSIGNED-NEXT: [[RESIZE2:%.*]] = trunc i24 [[DOWNSCALE]] to i16
// UNSIGNED-NEXT: store i16 [[RESIZE2]], i16* @usa, align 2
// UNSIGNED-NEXT: entry:
// UNSIGNED-NEXT: [[TMP0:%.*]] = load i16, i16* @usa, align 2
// UNSIGNED-NEXT: [[TMP1:%.*]] = load i16, i16* @usa_sat, align 2
-// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i16 [[TMP0]] to i15
-// UNSIGNED-NEXT: [[RESIZE1:%.*]] = trunc i16 [[TMP1]] to i15
-// UNSIGNED-NEXT: [[TMP2:%.*]] = call i15 @llvm.udiv.fix.sat.i15(i15 [[RESIZE]], i15 [[RESIZE1]], i32 7)
-// UNSIGNED-NEXT: [[RESIZE2:%.*]] = zext i15 [[TMP2]] to i16
-// UNSIGNED-NEXT: store i16 [[RESIZE2]], i16* @usa_sat, align 2
+// UNSIGNED-NEXT: [[TMP2:%.*]] = call i16 @llvm.sdiv.fix.sat.i16(i16 [[TMP0]], i16 [[TMP1]], i32 7)
+// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i16 [[TMP2]] to i15
+// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i15 [[RESIZE]] to i16
+// UNSIGNED-NEXT: store i16 [[RESIZE1]], i16* @usa_sat, align 2
// UNSIGNED-NEXT: ret void
//
void sat_usasusausas() {
// UNSIGNED-NEXT: entry:
// UNSIGNED-NEXT: [[TMP0:%.*]] = load i32, i32* @ua, align 4
// UNSIGNED-NEXT: [[TMP1:%.*]] = load i16, i16* @usa_sat, align 2
-// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i32 [[TMP0]] to i31
-// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i16 [[TMP1]] to i31
-// UNSIGNED-NEXT: [[UPSCALE:%.*]] = shl i31 [[RESIZE1]], 8
-// UNSIGNED-NEXT: [[TMP2:%.*]] = call i31 @llvm.udiv.fix.sat.i31(i31 [[RESIZE]], i31 [[UPSCALE]], i32 15)
-// UNSIGNED-NEXT: [[RESIZE2:%.*]] = zext i31 [[TMP2]] to i32
+// UNSIGNED-NEXT: [[RESIZE:%.*]] = zext i16 [[TMP1]] to i32
+// UNSIGNED-NEXT: [[UPSCALE:%.*]] = shl i32 [[RESIZE]], 8
+// UNSIGNED-NEXT: [[TMP2:%.*]] = call i32 @llvm.sdiv.fix.sat.i32(i32 [[TMP0]], i32 [[UPSCALE]], i32 15)
+// UNSIGNED-NEXT: [[RESIZE1:%.*]] = trunc i32 [[TMP2]] to i31
+// UNSIGNED-NEXT: [[RESIZE2:%.*]] = zext i31 [[RESIZE1]] to i32
// UNSIGNED-NEXT: store i32 [[RESIZE2]], i32* @ua_sat, align 4
// UNSIGNED-NEXT: ret void
//
// UNSIGNED-NEXT: entry:
// UNSIGNED-NEXT: [[TMP0:%.*]] = load i16, i16* @uf_sat, align 2
// UNSIGNED-NEXT: [[TMP1:%.*]] = load i16, i16* @uf_sat, align 2
-// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i16 [[TMP0]] to i15
-// UNSIGNED-NEXT: [[RESIZE1:%.*]] = trunc i16 [[TMP1]] to i15
-// UNSIGNED-NEXT: [[TMP2:%.*]] = call i15 @llvm.udiv.fix.sat.i15(i15 [[RESIZE]], i15 [[RESIZE1]], i32 15)
-// UNSIGNED-NEXT: [[RESIZE2:%.*]] = zext i15 [[TMP2]] to i16
-// UNSIGNED-NEXT: store i16 [[RESIZE2]], i16* @uf_sat, align 2
+// UNSIGNED-NEXT: [[TMP2:%.*]] = call i16 @llvm.sdiv.fix.sat.i16(i16 [[TMP0]], i16 [[TMP1]], i32 15)
+// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i16 [[TMP2]] to i15
+// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i15 [[RESIZE]] to i16
+// UNSIGNED-NEXT: store i16 [[RESIZE1]], i16* @uf_sat, align 2
// UNSIGNED-NEXT: ret void
//
void sat_ufsufsufs() {
// UNSIGNED-NEXT: entry:
// UNSIGNED-NEXT: [[TMP0:%.*]] = load i16, i16* @usa, align 2
// UNSIGNED-NEXT: [[TMP1:%.*]] = load i16, i16* @usa, align 2
-// UNSIGNED-NEXT: [[TMP2:%.*]] = call i16 @llvm.umul.fix.i16(i16 [[TMP0]], i16 [[TMP1]], i32 7)
+// UNSIGNED-NEXT: [[TMP2:%.*]] = call i16 @llvm.smul.fix.i16(i16 [[TMP0]], i16 [[TMP1]], i32 7)
// UNSIGNED-NEXT: store i16 [[TMP2]], i16* @usa, align 2
// UNSIGNED-NEXT: ret void
//
// UNSIGNED-NEXT: [[TMP1:%.*]] = load i32, i32* @ua, align 4
// UNSIGNED-NEXT: [[RESIZE:%.*]] = zext i16 [[TMP0]] to i32
// UNSIGNED-NEXT: [[UPSCALE:%.*]] = shl i32 [[RESIZE]], 8
-// UNSIGNED-NEXT: [[TMP2:%.*]] = call i32 @llvm.umul.fix.i32(i32 [[UPSCALE]], i32 [[TMP1]], i32 15)
+// UNSIGNED-NEXT: [[TMP2:%.*]] = call i32 @llvm.smul.fix.i32(i32 [[UPSCALE]], i32 [[TMP1]], i32 15)
// UNSIGNED-NEXT: store i32 [[TMP2]], i32* @ua, align 4
// UNSIGNED-NEXT: ret void
//
// UNSIGNED-NEXT: [[TMP0:%.*]] = load i16, i16* @usa, align 2
// UNSIGNED-NEXT: [[TMP1:%.*]] = load i8, i8* @usf, align 1
// UNSIGNED-NEXT: [[RESIZE:%.*]] = zext i8 [[TMP1]] to i16
-// UNSIGNED-NEXT: [[TMP2:%.*]] = call i16 @llvm.umul.fix.i16(i16 [[TMP0]], i16 [[RESIZE]], i32 7)
+// UNSIGNED-NEXT: [[TMP2:%.*]] = call i16 @llvm.smul.fix.i16(i16 [[TMP0]], i16 [[RESIZE]], i32 7)
// UNSIGNED-NEXT: store i16 [[TMP2]], i16* @usa, align 2
// UNSIGNED-NEXT: ret void
//
// UNSIGNED-NEXT: [[RESIZE:%.*]] = zext i16 [[TMP0]] to i24
// UNSIGNED-NEXT: [[UPSCALE:%.*]] = shl i24 [[RESIZE]], 8
// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i16 [[TMP1]] to i24
-// UNSIGNED-NEXT: [[TMP2:%.*]] = call i24 @llvm.umul.fix.i24(i24 [[UPSCALE]], i24 [[RESIZE1]], i32 15)
+// UNSIGNED-NEXT: [[TMP2:%.*]] = call i24 @llvm.smul.fix.i24(i24 [[UPSCALE]], i24 [[RESIZE1]], i32 15)
// UNSIGNED-NEXT: [[DOWNSCALE:%.*]] = lshr i24 [[TMP2]], 8
// UNSIGNED-NEXT: [[RESIZE2:%.*]] = trunc i24 [[DOWNSCALE]] to i16
// UNSIGNED-NEXT: store i16 [[RESIZE2]], i16* @usa, align 2
// UNSIGNED-NEXT: entry:
// UNSIGNED-NEXT: [[TMP0:%.*]] = load i16, i16* @usa, align 2
// UNSIGNED-NEXT: [[TMP1:%.*]] = load i16, i16* @usa_sat, align 2
-// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i16 [[TMP0]] to i15
-// UNSIGNED-NEXT: [[RESIZE1:%.*]] = trunc i16 [[TMP1]] to i15
-// UNSIGNED-NEXT: [[TMP2:%.*]] = call i15 @llvm.umul.fix.sat.i15(i15 [[RESIZE]], i15 [[RESIZE1]], i32 7)
-// UNSIGNED-NEXT: [[RESIZE2:%.*]] = zext i15 [[TMP2]] to i16
-// UNSIGNED-NEXT: store i16 [[RESIZE2]], i16* @usa_sat, align 2
+// UNSIGNED-NEXT: [[TMP2:%.*]] = call i16 @llvm.smul.fix.sat.i16(i16 [[TMP0]], i16 [[TMP1]], i32 7)
+// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i16 [[TMP2]] to i15
+// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i15 [[RESIZE]] to i16
+// UNSIGNED-NEXT: store i16 [[RESIZE1]], i16* @usa_sat, align 2
// UNSIGNED-NEXT: ret void
//
void sat_usasusausas() {
// UNSIGNED-NEXT: entry:
// UNSIGNED-NEXT: [[TMP0:%.*]] = load i32, i32* @ua, align 4
// UNSIGNED-NEXT: [[TMP1:%.*]] = load i16, i16* @usa_sat, align 2
-// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i32 [[TMP0]] to i31
-// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i16 [[TMP1]] to i31
-// UNSIGNED-NEXT: [[UPSCALE:%.*]] = shl i31 [[RESIZE1]], 8
-// UNSIGNED-NEXT: [[TMP2:%.*]] = call i31 @llvm.umul.fix.sat.i31(i31 [[RESIZE]], i31 [[UPSCALE]], i32 15)
-// UNSIGNED-NEXT: [[RESIZE2:%.*]] = zext i31 [[TMP2]] to i32
+// UNSIGNED-NEXT: [[RESIZE:%.*]] = zext i16 [[TMP1]] to i32
+// UNSIGNED-NEXT: [[UPSCALE:%.*]] = shl i32 [[RESIZE]], 8
+// UNSIGNED-NEXT: [[TMP2:%.*]] = call i32 @llvm.smul.fix.sat.i32(i32 [[TMP0]], i32 [[UPSCALE]], i32 15)
+// UNSIGNED-NEXT: [[RESIZE1:%.*]] = trunc i32 [[TMP2]] to i31
+// UNSIGNED-NEXT: [[RESIZE2:%.*]] = zext i31 [[RESIZE1]] to i32
// UNSIGNED-NEXT: store i32 [[RESIZE2]], i32* @ua_sat, align 4
// UNSIGNED-NEXT: ret void
//
// UNSIGNED-NEXT: entry:
// UNSIGNED-NEXT: [[TMP0:%.*]] = load i16, i16* @uf_sat, align 2
// UNSIGNED-NEXT: [[TMP1:%.*]] = load i16, i16* @uf_sat, align 2
-// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i16 [[TMP0]] to i15
-// UNSIGNED-NEXT: [[RESIZE1:%.*]] = trunc i16 [[TMP1]] to i15
-// UNSIGNED-NEXT: [[TMP2:%.*]] = call i15 @llvm.umul.fix.sat.i15(i15 [[RESIZE]], i15 [[RESIZE1]], i32 15)
-// UNSIGNED-NEXT: [[RESIZE2:%.*]] = zext i15 [[TMP2]] to i16
-// UNSIGNED-NEXT: store i16 [[RESIZE2]], i16* @uf_sat, align 2
+// UNSIGNED-NEXT: [[TMP2:%.*]] = call i16 @llvm.smul.fix.sat.i16(i16 [[TMP0]], i16 [[TMP1]], i32 15)
+// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i16 [[TMP2]] to i15
+// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i15 [[RESIZE]] to i16
+// UNSIGNED-NEXT: store i16 [[RESIZE1]], i16* @uf_sat, align 2
// UNSIGNED-NEXT: ret void
//
void sat_ufsufsufs() {
// UNSIGNED-NEXT: entry:
// UNSIGNED-NEXT: [[TMP0:%.*]] = load i16, i16* @usa, align 2
// UNSIGNED-NEXT: [[TMP1:%.*]] = load i16, i16* @usa_sat, align 2
-// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i16 [[TMP0]] to i15
-// UNSIGNED-NEXT: [[RESIZE1:%.*]] = trunc i16 [[TMP1]] to i15
-// UNSIGNED-NEXT: [[TMP2:%.*]] = call i15 @llvm.usub.sat.i15(i15 [[RESIZE]], i15 [[RESIZE1]])
-// UNSIGNED-NEXT: [[RESIZE2:%.*]] = zext i15 [[TMP2]] to i16
-// UNSIGNED-NEXT: store i16 [[RESIZE2]], i16* @usa_sat, align 2
+// UNSIGNED-NEXT: [[TMP2:%.*]] = call i16 @llvm.ssub.sat.i16(i16 [[TMP0]], i16 [[TMP1]])
+// UNSIGNED-NEXT: [[TMP3:%.*]] = icmp slt i16 [[TMP2]], 0
+// UNSIGNED-NEXT: [[SATMIN:%.*]] = select i1 [[TMP3]], i16 0, i16 [[TMP2]]
+// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i16 [[SATMIN]] to i15
+// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i15 [[RESIZE]] to i16
+// UNSIGNED-NEXT: store i16 [[RESIZE1]], i16* @usa_sat, align 2
// UNSIGNED-NEXT: ret void
//
void sat_usasusausas() {
// UNSIGNED-NEXT: entry:
// UNSIGNED-NEXT: [[TMP0:%.*]] = load i32, i32* @ua, align 4
// UNSIGNED-NEXT: [[TMP1:%.*]] = load i16, i16* @usa_sat, align 2
-// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i32 [[TMP0]] to i31
-// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i16 [[TMP1]] to i31
-// UNSIGNED-NEXT: [[UPSCALE:%.*]] = shl i31 [[RESIZE1]], 8
-// UNSIGNED-NEXT: [[TMP2:%.*]] = call i31 @llvm.usub.sat.i31(i31 [[RESIZE]], i31 [[UPSCALE]])
-// UNSIGNED-NEXT: [[RESIZE2:%.*]] = zext i31 [[TMP2]] to i32
+// UNSIGNED-NEXT: [[RESIZE:%.*]] = zext i16 [[TMP1]] to i32
+// UNSIGNED-NEXT: [[UPSCALE:%.*]] = shl i32 [[RESIZE]], 8
+// UNSIGNED-NEXT: [[TMP2:%.*]] = call i32 @llvm.ssub.sat.i32(i32 [[TMP0]], i32 [[UPSCALE]])
+// UNSIGNED-NEXT: [[TMP3:%.*]] = icmp slt i32 [[TMP2]], 0
+// UNSIGNED-NEXT: [[SATMIN:%.*]] = select i1 [[TMP3]], i32 0, i32 [[TMP2]]
+// UNSIGNED-NEXT: [[RESIZE1:%.*]] = trunc i32 [[SATMIN]] to i31
+// UNSIGNED-NEXT: [[RESIZE2:%.*]] = zext i31 [[RESIZE1]] to i32
// UNSIGNED-NEXT: store i32 [[RESIZE2]], i32* @ua_sat, align 4
// UNSIGNED-NEXT: ret void
//
// UNSIGNED-NEXT: entry:
// UNSIGNED-NEXT: [[TMP0:%.*]] = load i16, i16* @uf_sat, align 2
// UNSIGNED-NEXT: [[TMP1:%.*]] = load i16, i16* @uf_sat, align 2
-// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i16 [[TMP0]] to i15
-// UNSIGNED-NEXT: [[RESIZE1:%.*]] = trunc i16 [[TMP1]] to i15
-// UNSIGNED-NEXT: [[TMP2:%.*]] = call i15 @llvm.usub.sat.i15(i15 [[RESIZE]], i15 [[RESIZE1]])
-// UNSIGNED-NEXT: [[RESIZE2:%.*]] = zext i15 [[TMP2]] to i16
-// UNSIGNED-NEXT: store i16 [[RESIZE2]], i16* @uf_sat, align 2
+// UNSIGNED-NEXT: [[TMP2:%.*]] = call i16 @llvm.ssub.sat.i16(i16 [[TMP0]], i16 [[TMP1]])
+// UNSIGNED-NEXT: [[TMP3:%.*]] = icmp slt i16 [[TMP2]], 0
+// UNSIGNED-NEXT: [[SATMIN:%.*]] = select i1 [[TMP3]], i16 0, i16 [[TMP2]]
+// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i16 [[SATMIN]] to i15
+// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i15 [[RESIZE]] to i16
+// UNSIGNED-NEXT: store i16 [[RESIZE1]], i16* @uf_sat, align 2
// UNSIGNED-NEXT: ret void
//
void sat_ufsufsufs() {
// UNSIGNED-LABEL: @inc_sua(
// UNSIGNED-NEXT: entry:
// UNSIGNED-NEXT: [[TMP0:%.*]] = load i32, i32* @sua, align 4
-// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i32 [[TMP0]] to i31
-// UNSIGNED-NEXT: [[TMP1:%.*]] = call i31 @llvm.uadd.sat.i31(i31 [[RESIZE]], i31 32768)
-// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i31 [[TMP1]] to i32
+// UNSIGNED-NEXT: [[TMP1:%.*]] = call i32 @llvm.sadd.sat.i32(i32 [[TMP0]], i32 32768)
+// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i32 [[TMP1]] to i31
+// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i31 [[RESIZE]] to i32
// UNSIGNED-NEXT: store i32 [[RESIZE1]], i32* @sua, align 4
// UNSIGNED-NEXT: ret void
//
// UNSIGNED-LABEL: @inc_susa(
// UNSIGNED-NEXT: entry:
// UNSIGNED-NEXT: [[TMP0:%.*]] = load i16, i16* @susa, align 2
-// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i16 [[TMP0]] to i15
-// UNSIGNED-NEXT: [[TMP1:%.*]] = call i15 @llvm.uadd.sat.i15(i15 [[RESIZE]], i15 128)
-// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i15 [[TMP1]] to i16
+// UNSIGNED-NEXT: [[TMP1:%.*]] = call i16 @llvm.sadd.sat.i16(i16 [[TMP0]], i16 128)
+// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i16 [[TMP1]] to i15
+// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i15 [[RESIZE]] to i16
// UNSIGNED-NEXT: store i16 [[RESIZE1]], i16* @susa, align 2
// UNSIGNED-NEXT: ret void
//
// UNSIGNED-LABEL: @inc_suf(
// UNSIGNED-NEXT: entry:
// UNSIGNED-NEXT: [[TMP0:%.*]] = load i16, i16* @suf, align 2
-// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i16 [[TMP0]] to i15
-// UNSIGNED-NEXT: [[TMP1:%.*]] = call i15 @llvm.uadd.sat.i15(i15 [[RESIZE]], i15 -1)
-// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i15 [[TMP1]] to i16
+// UNSIGNED-NEXT: [[TMP1:%.*]] = call i16 @llvm.sadd.sat.i16(i16 [[TMP0]], i16 32767)
+// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i16 [[TMP1]] to i15
+// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i15 [[RESIZE]] to i16
// UNSIGNED-NEXT: store i16 [[RESIZE1]], i16* @suf, align 2
// UNSIGNED-NEXT: ret void
//
// UNSIGNED-LABEL: @dec_sua(
// UNSIGNED-NEXT: entry:
// UNSIGNED-NEXT: [[TMP0:%.*]] = load i32, i32* @sua, align 4
-// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i32 [[TMP0]] to i31
-// UNSIGNED-NEXT: [[TMP1:%.*]] = call i31 @llvm.usub.sat.i31(i31 [[RESIZE]], i31 32768)
-// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i31 [[TMP1]] to i32
+// UNSIGNED-NEXT: [[TMP1:%.*]] = call i32 @llvm.ssub.sat.i32(i32 [[TMP0]], i32 32768)
+// UNSIGNED-NEXT: [[TMP2:%.*]] = icmp slt i32 [[TMP1]], 0
+// UNSIGNED-NEXT: [[SATMIN:%.*]] = select i1 [[TMP2]], i32 0, i32 [[TMP1]]
+// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i32 [[SATMIN]] to i31
+// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i31 [[RESIZE]] to i32
// UNSIGNED-NEXT: store i32 [[RESIZE1]], i32* @sua, align 4
// UNSIGNED-NEXT: ret void
//
// UNSIGNED-LABEL: @dec_susa(
// UNSIGNED-NEXT: entry:
// UNSIGNED-NEXT: [[TMP0:%.*]] = load i16, i16* @susa, align 2
-// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i16 [[TMP0]] to i15
-// UNSIGNED-NEXT: [[TMP1:%.*]] = call i15 @llvm.usub.sat.i15(i15 [[RESIZE]], i15 128)
-// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i15 [[TMP1]] to i16
+// UNSIGNED-NEXT: [[TMP1:%.*]] = call i16 @llvm.ssub.sat.i16(i16 [[TMP0]], i16 128)
+// UNSIGNED-NEXT: [[TMP2:%.*]] = icmp slt i16 [[TMP1]], 0
+// UNSIGNED-NEXT: [[SATMIN:%.*]] = select i1 [[TMP2]], i16 0, i16 [[TMP1]]
+// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i16 [[SATMIN]] to i15
+// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i15 [[RESIZE]] to i16
// UNSIGNED-NEXT: store i16 [[RESIZE1]], i16* @susa, align 2
// UNSIGNED-NEXT: ret void
//
// UNSIGNED-LABEL: @dec_suf(
// UNSIGNED-NEXT: entry:
// UNSIGNED-NEXT: [[TMP0:%.*]] = load i16, i16* @suf, align 2
-// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i16 [[TMP0]] to i15
-// UNSIGNED-NEXT: [[TMP1:%.*]] = call i15 @llvm.usub.sat.i15(i15 [[RESIZE]], i15 -1)
-// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i15 [[TMP1]] to i16
+// UNSIGNED-NEXT: [[TMP1:%.*]] = call i16 @llvm.ssub.sat.i16(i16 [[TMP0]], i16 32767)
+// UNSIGNED-NEXT: [[TMP2:%.*]] = icmp slt i16 [[TMP1]], 0
+// UNSIGNED-NEXT: [[SATMIN:%.*]] = select i1 [[TMP2]], i16 0, i16 [[TMP1]]
+// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i16 [[SATMIN]] to i15
+// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i15 [[RESIZE]] to i16
// UNSIGNED-NEXT: store i16 [[RESIZE1]], i16* @suf, align 2
// UNSIGNED-NEXT: ret void
//
// UNSIGNED-LABEL: @neg_susa(
// UNSIGNED-NEXT: entry:
// UNSIGNED-NEXT: [[TMP0:%.*]] = load i16, i16* @susa, align 2
-// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i16 [[TMP0]] to i15
-// UNSIGNED-NEXT: [[TMP1:%.*]] = call i15 @llvm.usub.sat.i15(i15 0, i15 [[RESIZE]])
-// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i15 [[TMP1]] to i16
+// UNSIGNED-NEXT: [[TMP1:%.*]] = call i16 @llvm.ssub.sat.i16(i16 0, i16 [[TMP0]])
+// UNSIGNED-NEXT: [[TMP2:%.*]] = icmp slt i16 [[TMP1]], 0
+// UNSIGNED-NEXT: [[SATMIN:%.*]] = select i1 [[TMP2]], i16 0, i16 [[TMP1]]
+// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i16 [[SATMIN]] to i15
+// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i15 [[RESIZE]] to i16
// UNSIGNED-NEXT: store i16 [[RESIZE1]], i16* @susa, align 2
// UNSIGNED-NEXT: ret void
//
// UNSIGNED-LABEL: @neg_suf(
// UNSIGNED-NEXT: entry:
// UNSIGNED-NEXT: [[TMP0:%.*]] = load i16, i16* @suf, align 2
-// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i16 [[TMP0]] to i15
-// UNSIGNED-NEXT: [[TMP1:%.*]] = call i15 @llvm.usub.sat.i15(i15 0, i15 [[RESIZE]])
-// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i15 [[TMP1]] to i16
+// UNSIGNED-NEXT: [[TMP1:%.*]] = call i16 @llvm.ssub.sat.i16(i16 0, i16 [[TMP0]])
+// UNSIGNED-NEXT: [[TMP2:%.*]] = icmp slt i16 [[TMP1]], 0
+// UNSIGNED-NEXT: [[SATMIN:%.*]] = select i1 [[TMP2]], i16 0, i16 [[TMP1]]
+// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i16 [[SATMIN]] to i15
+// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i15 [[RESIZE]] to i16
// UNSIGNED-NEXT: store i16 [[RESIZE1]], i16* @suf, align 2
// UNSIGNED-NEXT: ret void
//