From ad18a0d69778c7a02e398967aafd92318fc50a58 Mon Sep 17 00:00:00 2001 From: Leonard Chan Date: Wed, 16 Jan 2019 18:13:59 +0000 Subject: [PATCH] [Fixed Point Arithmetic] Fixed Point Addition This patch covers addition between fixed point types and other fixed point types or integers, using the conversion rules described in 4.1.4 of N1169. Usual arithmetic rules do not apply to binary operations when one of the operands is a fixed point type, and the result of the operation must be calculated with the full precision of the operands, so we should not perform any casting to a common type. This patch does not include constant expression evaluation for addition of fixed point types. That will be addressed in another patch since I think this one is already big enough. Differential Revision: https://reviews.llvm.org/D53738 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@351364 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/ASTContext.h | 6 + include/clang/AST/Type.h | 7 + include/clang/Basic/FixedPoint.h | 21 ++ lib/AST/ASTContext.cpp | 43 ++- lib/Basic/FixedPoint.cpp | 25 ++ lib/CodeGen/CGExprScalar.cpp | 82 ++++- lib/Sema/SemaExpr.cpp | 91 +++++- test/Frontend/fixed_point_add.c | 390 ++++++++++++++++++++++++ test/Frontend/fixed_point_conversions.c | 22 +- 9 files changed, 667 insertions(+), 20 deletions(-) create mode 100644 test/Frontend/fixed_point_add.c diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 13870116c7..aadc3acb19 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -2624,6 +2624,12 @@ public: // corresponding saturated type for a given fixed point type. QualType getCorrespondingSaturatedType(QualType Ty) const; + // This method accepts fixed point types and returns the corresponding signed + // type. Unlike getCorrespondingUnsignedType(), this only accepts unsigned + // fixed point types because there are unsigned integer types like bool and + // char8_t that don't have signed equivalents. + QualType getCorrespondingSignedFixedPointType(QualType Ty) const; + //===--------------------------------------------------------------------===// // Integer Values //===--------------------------------------------------------------------===// diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index d4c97b1b5e..4c48473d2e 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -2269,6 +2269,9 @@ public: /// ISO/IEC JTC1 SC22 WG14 N1169. bool isFixedPointType() const; + /// Return true if this is a fixed point or integer type. + bool isFixedPointOrIntegerType() const; + /// Return true if this is a saturated fixed point type according to /// ISO/IEC JTC1 SC22 WG14 N1169. This type can be signed or unsigned. bool isSaturatedFixedPointType() const; @@ -6596,6 +6599,10 @@ inline bool Type::isFixedPointType() const { return false; } +inline bool Type::isFixedPointOrIntegerType() const { + return isFixedPointType() || isIntegerType(); +} + inline bool Type::isSaturatedFixedPointType() const { if (const auto *BT = dyn_cast(CanonicalType)) { return BT->getKind() >= BuiltinType::SatShortAccum && diff --git a/include/clang/Basic/FixedPoint.h b/include/clang/Basic/FixedPoint.h index 9a9b7cc9c1..bfba55a041 100644 --- a/include/clang/Basic/FixedPoint.h +++ b/include/clang/Basic/FixedPoint.h @@ -18,6 +18,7 @@ #define LLVM_CLANG_BASIC_FIXEDPOINT_H #include "llvm/ADT/APSInt.h" +#include "llvm/Support/raw_ostream.h" namespace clang { @@ -36,6 +37,8 @@ public: : Width(Width), Scale(Scale), IsSigned(IsSigned), IsSaturated(IsSaturated), HasUnsignedPadding(HasUnsignedPadding) { assert(Width >= Scale && "Not enough room for the scale"); + assert(!(IsSigned && HasUnsignedPadding) && + "Cannot have unsigned padding on a signed type."); } unsigned getWidth() const { return Width; } @@ -46,6 +49,9 @@ public: void setSaturated(bool Saturated) { IsSaturated = Saturated; } + /// Return the number of integral bits represented by these semantics. These + /// are separate from the fractional bits and do not include the sign or + /// padding bit. unsigned getIntegralBits() const { if (IsSigned || (!IsSigned && HasUnsignedPadding)) return Width - Scale - 1; @@ -53,6 +59,21 @@ public: return Width - Scale; } + /// Return the FixedPointSemantics that allows for calculating the full + /// precision semantic that can precisely represent the precision and ranges + /// of both input values. This does not compute the resulting semantics for a + /// given binary operation. + FixedPointSemantics + getCommonSemantics(const FixedPointSemantics &Other) const; + + /// Return the FixedPointSemantics for an integer type. + static FixedPointSemantics GetIntegerSemantics(unsigned Width, + bool IsSigned) { + return FixedPointSemantics(Width, /*Scale=*/0, IsSigned, + /*IsSaturated=*/false, + /*HasUnsignedPadding=*/false); + } + private: unsigned Width; unsigned Scale; diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 21b6f36e9a..ed203a331d 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -10485,7 +10485,13 @@ unsigned char ASTContext::getFixedPointIBits(QualType Ty) const { } FixedPointSemantics ASTContext::getFixedPointSemantics(QualType Ty) const { - assert(Ty->isFixedPointType()); + assert(Ty->isFixedPointType() || + Ty->isIntegerType() && "Can only get the fixed point semantics for a " + "fixed point or integer type."); + if (Ty->isIntegerType()) + return FixedPointSemantics::GetIntegerSemantics(getIntWidth(Ty), + Ty->isSignedIntegerType()); + bool isSigned = Ty->isSignedFixedPointType(); return FixedPointSemantics( static_cast(getTypeSize(Ty)), getFixedPointScale(Ty), isSigned, @@ -10502,3 +10508,38 @@ APFixedPoint ASTContext::getFixedPointMin(QualType Ty) const { assert(Ty->isFixedPointType()); return APFixedPoint::getMin(getFixedPointSemantics(Ty)); } + +QualType ASTContext::getCorrespondingSignedFixedPointType(QualType Ty) const { + assert(Ty->isUnsignedFixedPointType() && + "Expected unsigned fixed point type"); + const auto *BTy = Ty->getAs(); + + switch (BTy->getKind()) { + case BuiltinType::UShortAccum: + return ShortAccumTy; + case BuiltinType::UAccum: + return AccumTy; + case BuiltinType::ULongAccum: + return LongAccumTy; + case BuiltinType::SatUShortAccum: + return SatShortAccumTy; + case BuiltinType::SatUAccum: + return SatAccumTy; + case BuiltinType::SatULongAccum: + return SatLongAccumTy; + case BuiltinType::UShortFract: + return ShortFractTy; + case BuiltinType::UFract: + return FractTy; + case BuiltinType::ULongFract: + return LongFractTy; + case BuiltinType::SatUShortFract: + return SatShortFractTy; + case BuiltinType::SatUFract: + return SatFractTy; + case BuiltinType::SatULongFract: + return SatLongFractTy; + default: + llvm_unreachable("Unexpected unsigned fixed point type"); + } +} diff --git a/lib/Basic/FixedPoint.cpp b/lib/Basic/FixedPoint.cpp index bfff0fc212..0aaa9af191 100644 --- a/lib/Basic/FixedPoint.cpp +++ b/lib/Basic/FixedPoint.cpp @@ -112,4 +112,29 @@ APFixedPoint APFixedPoint::getMin(const FixedPointSemantics &Sema) { return APFixedPoint(Val, Sema); } +FixedPointSemantics FixedPointSemantics::getCommonSemantics( + const FixedPointSemantics &Other) const { + unsigned CommonScale = std::max(getScale(), Other.getScale()); + unsigned CommonWidth = + std::max(getIntegralBits(), Other.getIntegralBits()) + CommonScale; + + bool ResultIsSigned = isSigned() || Other.isSigned(); + bool ResultIsSaturated = isSaturated() || Other.isSaturated(); + bool ResultHasUnsignedPadding = false; + if (!ResultIsSigned) { + // Both are unsigned. + ResultHasUnsignedPadding = hasUnsignedPadding() && + Other.hasUnsignedPadding() && !ResultIsSaturated; + } + + // If the result is signed, add an extra bit for the sign. Otherwise, if it is + // unsigned and has unsigned padding, we only need to add the extra padding + // bit back if we are not saturating. + if (ResultIsSigned || ResultHasUnsignedPadding) + CommonWidth++; + + return FixedPointSemantics(CommonWidth, CommonScale, ResultIsSigned, + ResultIsSaturated, ResultHasUnsignedPadding); +} + } // namespace clang diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp index 1c14d4c99a..6b3c78121b 100644 --- a/lib/CodeGen/CGExprScalar.cpp +++ b/lib/CodeGen/CGExprScalar.cpp @@ -125,6 +125,13 @@ struct BinOpInfo { return CFP->isZero(); return true; } + + /// Check if either operand is a fixed point type, in which case, this + /// operation did not follow usual arithmetic conversion and both operands may + /// not be the same. + bool isFixedPointBinOp() const { + return isa(E) && Ty->isFixedPointType(); + } }; static bool MustVisitNullValue(const Expr *E) { @@ -351,6 +358,9 @@ public: Value *EmitFixedPointConversion(Value *Src, QualType SrcTy, QualType DstTy, SourceLocation Loc); + Value *EmitFixedPointConversion(Value *Src, FixedPointSemantics &SrcFixedSema, + FixedPointSemantics &DstFixedSema, + SourceLocation Loc); /// Emit a conversion from the specified complex type to the specified /// destination type, where the destination type is an LLVM scalar type. @@ -729,6 +739,9 @@ public: return Builder.CreateOr(Ops.LHS, Ops.RHS, "or"); } + // Helper functions for fixed point binary operations. + Value *EmitFixedPointAdd(const BinOpInfo &Ops); + BinOpInfo EmitBinOps(const BinaryOperator *E); LValue EmitCompoundAssignLValue(const CompoundAssignOperator *E, Value *(ScalarExprEmitter::*F)(const BinOpInfo &), @@ -1423,10 +1436,6 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType, Value *ScalarExprEmitter::EmitFixedPointConversion(Value *Src, QualType SrcTy, QualType DstTy, SourceLocation Loc) { - using llvm::APInt; - using llvm::ConstantInt; - using llvm::Value; - assert(SrcTy->isFixedPointType()); assert(DstTy->isFixedPointType()); @@ -1434,6 +1443,16 @@ Value *ScalarExprEmitter::EmitFixedPointConversion(Value *Src, QualType SrcTy, CGF.getContext().getFixedPointSemantics(SrcTy); FixedPointSemantics DstFPSema = CGF.getContext().getFixedPointSemantics(DstTy); + return EmitFixedPointConversion(Src, SrcFPSema, DstFPSema, Loc); +} + +Value *ScalarExprEmitter::EmitFixedPointConversion( + Value *Src, FixedPointSemantics &SrcFPSema, FixedPointSemantics &DstFPSema, + SourceLocation Loc) { + using llvm::APInt; + using llvm::ConstantInt; + using llvm::Value; + unsigned SrcWidth = SrcFPSema.getWidth(); unsigned DstWidth = DstFPSema.getWidth(); unsigned SrcScale = SrcFPSema.getScale(); @@ -1462,7 +1481,8 @@ Value *ScalarExprEmitter::EmitFixedPointConversion(Value *Src, QualType SrcTy, } else { // Adjust the number of fractional bits. if (DstScale > SrcScale) { - ResultWidth = SrcWidth + 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"); @@ -1493,7 +1513,8 @@ Value *ScalarExprEmitter::EmitFixedPointConversion(Value *Src, QualType SrcTy, } // Resize the integer part to get the final destination size. - Result = Builder.CreateIntCast(Result, DstIntTy, SrcIsSigned, "resize"); + if (ResultWidth != DstWidth) + Result = Builder.CreateIntCast(Result, DstIntTy, SrcIsSigned, "resize"); } return Result; } @@ -3338,9 +3359,58 @@ Value *ScalarExprEmitter::EmitAdd(const BinOpInfo &op) { return propagateFMFlags(V, op); } + if (op.isFixedPointBinOp()) + return EmitFixedPointAdd(op); + return Builder.CreateAdd(op.LHS, op.RHS, "add"); } +/// The resulting value must be calculated with exact precision, so the operands +/// may not be the same type. +Value *ScalarExprEmitter::EmitFixedPointAdd(const BinOpInfo &op) { + using llvm::APSInt; + using llvm::ConstantInt; + + const auto *BinOp = cast(op.E); + assert(BinOp->getOpcode() == BO_Add && "Expected operation to be addition"); + + // The result is a fixed point type and at least one of the operands is fixed + // point while the other is either fixed point or an int. This resulting type + // should be determined by Sema::handleFixedPointConversions(). + QualType ResultTy = op.Ty; + QualType LHSTy = BinOp->getLHS()->getType(); + QualType RHSTy = BinOp->getRHS()->getType(); + ASTContext &Ctx = CGF.getContext(); + Value *LHS = op.LHS; + Value *RHS = op.RHS; + + auto LHSFixedSema = Ctx.getFixedPointSemantics(LHSTy); + auto RHSFixedSema = Ctx.getFixedPointSemantics(RHSTy); + auto ResultFixedSema = Ctx.getFixedPointSemantics(ResultTy); + auto CommonFixedSema = LHSFixedSema.getCommonSemantics(RHSFixedSema); + + // Convert the operands to the full precision type. + Value *FullLHS = EmitFixedPointConversion(LHS, LHSFixedSema, CommonFixedSema, + BinOp->getExprLoc()); + Value *FullRHS = EmitFixedPointConversion(RHS, RHSFixedSema, CommonFixedSema, + BinOp->getExprLoc()); + + // Perform the actual addition. + Value *Result; + if (ResultFixedSema.isSaturated()) { + llvm::Intrinsic::ID IID = ResultFixedSema.isSigned() + ? llvm::Intrinsic::sadd_sat + : llvm::Intrinsic::uadd_sat; + Result = Builder.CreateBinaryIntrinsic(IID, FullLHS, FullRHS); + } else { + Result = Builder.CreateAdd(FullLHS, FullRHS); + } + + // Convert to the result type. + return EmitFixedPointConversion(Result, CommonFixedSema, ResultFixedSema, + BinOp->getExprLoc()); +} + Value *ScalarExprEmitter::EmitSub(const BinOpInfo &op) { // The LHS is always a pointer if either side is. if (!op.LHS->getType()->isPointerTy()) { diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index d5416d4d05..533b3b7da9 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -1250,6 +1250,93 @@ static QualType handleComplexIntConversion(Sema &S, ExprResult &LHS, return ComplexType; } +/// Return the rank of a given fixed point or integer type. The value itself +/// doesn't matter, but the values must be increasing with proper increasing +/// rank as described in N1169 4.1.1. +static unsigned GetFixedPointRank(QualType Ty) { + const auto *BTy = Ty->getAs(); + assert(BTy && "Expected a builtin type."); + + switch (BTy->getKind()) { + case BuiltinType::ShortFract: + case BuiltinType::UShortFract: + case BuiltinType::SatShortFract: + case BuiltinType::SatUShortFract: + return 1; + case BuiltinType::Fract: + case BuiltinType::UFract: + case BuiltinType::SatFract: + case BuiltinType::SatUFract: + return 2; + case BuiltinType::LongFract: + case BuiltinType::ULongFract: + case BuiltinType::SatLongFract: + case BuiltinType::SatULongFract: + return 3; + case BuiltinType::ShortAccum: + case BuiltinType::UShortAccum: + case BuiltinType::SatShortAccum: + case BuiltinType::SatUShortAccum: + return 4; + case BuiltinType::Accum: + case BuiltinType::UAccum: + case BuiltinType::SatAccum: + case BuiltinType::SatUAccum: + return 5; + case BuiltinType::LongAccum: + case BuiltinType::ULongAccum: + case BuiltinType::SatLongAccum: + case BuiltinType::SatULongAccum: + return 6; + default: + if (BTy->isInteger()) + return 0; + llvm_unreachable("Unexpected fixed point or integer type"); + } +} + +/// handleFixedPointConversion - Fixed point operations between fixed +/// point types and integers or other fixed point types do not fall under +/// usual arithmetic conversion since these conversions could result in loss +/// of precsision (N1169 4.1.4). These operations should be calculated with +/// the full precision of their result type (N1169 4.1.6.2.1). +static QualType handleFixedPointConversion(Sema &S, QualType LHSTy, + QualType RHSTy) { + assert((LHSTy->isFixedPointType() || RHSTy->isFixedPointType()) && + "Expected at least one of the operands to be a fixed point type"); + assert((LHSTy->isFixedPointOrIntegerType() || + RHSTy->isFixedPointOrIntegerType()) && + "Special fixed point arithmetic operation conversions are only " + "applied to ints or other fixed point types"); + + // If one operand has signed fixed-point type and the other operand has + // unsigned fixed-point type, then the unsigned fixed-point operand is + // converted to its corresponding signed fixed-point type and the resulting + // type is the type of the converted operand. + if (RHSTy->isSignedFixedPointType() && LHSTy->isUnsignedFixedPointType()) + LHSTy = S.Context.getCorrespondingSignedFixedPointType(LHSTy); + else if (RHSTy->isUnsignedFixedPointType() && LHSTy->isSignedFixedPointType()) + RHSTy = S.Context.getCorrespondingSignedFixedPointType(RHSTy); + + // The result type is the type with the highest rank, whereby a fixed-point + // conversion rank is always greater than an integer conversion rank; if the + // type of either of the operands is a saturating fixedpoint type, the result + // type shall be the saturating fixed-point type corresponding to the type + // with the highest rank; the resulting value is converted (taking into + // account rounding and overflow) to the precision of the resulting type. + // Same ranks between signed and unsigned types are resolved earlier, so both + // types are either signed or both unsigned at this point. + unsigned LHSTyRank = GetFixedPointRank(LHSTy); + unsigned RHSTyRank = GetFixedPointRank(RHSTy); + + QualType ResultTy = LHSTyRank > RHSTyRank ? LHSTy : RHSTy; + + if (LHSTy->isSaturatedFixedPointType() || RHSTy->isSaturatedFixedPointType()) + ResultTy = S.Context.getCorrespondingSaturatedType(ResultTy); + + return ResultTy; +} + /// UsualArithmeticConversions - Performs various conversions that are common to /// binary operators (C99 6.3.1.8). If both operands aren't arithmetic, this /// routine returns the first non-arithmetic type found. The client is @@ -1322,12 +1409,14 @@ QualType Sema::UsualArithmeticConversions(ExprResult &LHS, ExprResult &RHS, return handleComplexIntConversion(*this, LHS, RHS, LHSType, RHSType, IsCompAssign); + if (LHSType->isFixedPointType() || RHSType->isFixedPointType()) + return handleFixedPointConversion(*this, LHSType, RHSType); + // Finally, we have two differing integer types. return handleIntegerConversion (*this, LHS, RHS, LHSType, RHSType, IsCompAssign); } - //===----------------------------------------------------------------------===// // Semantic Analysis for various Expression Types //===----------------------------------------------------------------------===// diff --git a/test/Frontend/fixed_point_add.c b/test/Frontend/fixed_point_add.c new file mode 100644 index 0000000000..ff3cdb9d4e --- /dev/null +++ b/test/Frontend/fixed_point_add.c @@ -0,0 +1,390 @@ +// RUN: %clang_cc1 -ffixed-point -S -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,SIGNED +// RUN: %clang_cc1 -ffixed-point -fpadding-on-unsigned-fixed-point -S -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,UNSIGNED + +void SignedAddition() { + // CHECK-LABEL: SignedAddition + short _Accum sa; + _Accum a, b, c, d; + long _Accum la; + unsigned short _Accum usa; + unsigned _Accum ua; + unsigned long _Accum ula; + + short _Fract sf; + _Fract f; + long _Fract lf; + unsigned short _Fract usf; + unsigned _Fract uf; + unsigned long _Fract ulf; + + // Same type + // CHECK: [[SA:%[0-9]+]] = load i16, i16* %sa, align 2 + // CHECK-NEXT: [[SA2:%[0-9]+]] = load i16, i16* %sa, align 2 + // CHECK-NEXT: [[SUM:%[0-9]+]] = add i16 [[SA]], [[SA2]] + // CHECK-NEXT: store i16 [[SUM]], i16* %sa, align 2 + sa = sa + sa; + + // To larger scale and larger width + // CHECK: [[SA:%[0-9]+]] = load i16, i16* %sa, align 2 + // CHECK-NEXT: [[A:%[0-9]+]] = load i32, i32* %a, align 4 + // CHECK-NEXT: [[EXT_SA:%[a-z0-9]+]] = sext i16 [[SA]] to i32 + // CHECK-NEXT: [[SA:%[a-z0-9]+]] = shl i32 [[EXT_SA]], 8 + // CHECK-NEXT: [[SUM:%[0-9]+]] = add i32 [[SA]], [[A]] + // CHECK-NEXT: store i32 [[SUM]], i32* %a, align 4 + a = sa + a; + + // To same scale and smaller width + // CHECK: [[SA:%[0-9]+]] = load i16, i16* %sa, align 2 + // CHECK-NEXT: [[SF:%[0-9]+]] = load i8, i8* %sf, align 1 + // CHECK-NEXT: [[EXT_SF:%[a-z0-9]+]] = sext i8 [[SF]] to i16 + // CHECK-NEXT: [[SUM:%[0-9]+]] = add i16 [[SA]], [[EXT_SF]] + // CHECK-NEXT: store i16 [[SUM]], i16* %sa, align 2 + sa = sa + sf; + + // To smaller scale and same width. + // CHECK: [[SA:%[0-9]+]] = load i16, i16* %sa, align 2 + // CHECK-NEXT: [[F:%[0-9]+]] = load i16, i16* %f, align 2 + // CHECK-NEXT: [[EXT_SA:%[a-z0-9]+]] = sext i16 [[SA]] to i24 + // CHECK-NEXT: [[SA:%[a-z0-9]+]] = shl i24 [[EXT_SA]], 8 + // CHECK-NEXT: [[EXT_F:%[a-z0-9]+]] = sext i16 [[F]] to i24 + // CHECK-NEXT: [[SUM:%[0-9]+]] = add i24 [[SA]], [[EXT_F]] + // CHECK-NEXT: [[RES:%[a-z0-9]+]] = ashr i24 [[SUM]], 8 + // CHECK-NEXT: [[TRUNC_RES:%[a-z0-9]+]] = trunc i24 [[RES]] to i16 + // CHECK-NEXT: store i16 [[TRUNC_RES]], i16* %sa, align 2 + sa = sa + f; + + // To smaller scale and smaller width + // CHECK: [[A:%[0-9]+]] = load i32, i32* %a, align 4 + // CHECK-NEXT: [[SF:%[0-9]+]] = load i8, i8* %sf, align 1 + // CHECK-NEXT: [[EXT_SF:%[a-z0-9]+]] = sext i8 [[SF]] to i32 + // CHECK-NEXT: [[SF:%[a-z0-9]+]] = shl i32 [[EXT_SF]], 8 + // CHECK-NEXT: [[SUM:%[0-9]+]] = add i32 [[A]], [[SF]] + // CHECK-NEXT: store i32 [[SUM]], i32* %a, align 4 + a = a + sf; + + // To larger scale and same width + // CHECK: [[A:%[0-9]+]] = load i32, i32* %a, align 4 + // CHECK-NEXT: [[LF:%[0-9]+]] = load i32, i32* %lf, align 4 + // CHECK-NEXT: [[EXT_A:%[a-z0-9]+]] = sext i32 [[A]] to i48 + // CHECK-NEXT: [[A:%[a-z0-9]+]] = shl i48 [[EXT_A]], 16 + // CHECK-NEXT: [[EXT_LF:%[a-z0-9]+]] = sext i32 [[LF]] to i48 + // CHECK-NEXT: [[SUM:%[0-9]+]] = add i48 [[A]], [[EXT_LF]] + // CHECK-NEXT: [[RES:%[a-z0-9]+]] = ashr i48 [[SUM]], 16 + // CHECK-NEXT: [[TRUNC_RES:%[a-z0-9]+]] = trunc i48 [[RES]] to i32 + // CHECK-NEXT: store i32 [[TRUNC_RES]], i32* %a, align 4 + a = a + lf; + + // With corresponding unsigned type + // CHECK: [[SA:%[0-9]+]] = load i16, i16* %sa, align 2 + // CHECK-NEXT: [[USA:%[0-9]+]] = load i16, i16* %usa, align 2 + // SIGNED-NEXT: [[SA_EXT:%[a-z0-9]+]] = sext i16 [[SA]] to i17 + // SIGNED-NEXT: [[SA:%[a-z0-9]+]] = shl i17 [[SA_EXT]], 1 + // SIGNED-NEXT: [[USA_EXT:%[a-z0-9]+]] = zext i16 [[USA]] to i17 + // SIGNED-NEXT: [[SUM:%[0-9]+]] = add i17 [[SA]], [[USA_EXT]] + // SIGNED-NEXT: [[RESULT:%[a-z0-9]+]] = ashr i17 [[SUM]], 1 + // SIGNED-NEXT: [[SUM:%[a-z0-9]+]] = trunc i17 [[RESULT]] to i16 + // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = add i16 [[SA]], [[USA]] + // CHECK-NEXT: store i16 [[SUM]], i16* %sa, align 2 + sa = sa + usa; + + // With unsigned of larger scale + // CHECK: [[SA:%[0-9]+]] = load i16, i16* %sa, align 2 + // CHECK-NEXT: [[USA:%[0-9]+]] = load i32, i32* %ua, align 4 + // SIGNED-NEXT: [[SA_EXT:%[a-z0-9]+]] = sext i16 [[SA]] to i33 + // SIGNED-NEXT: [[SA:%[a-z0-9]+]] = shl i33 [[SA_EXT]], 9 + // SIGNED-NEXT: [[USA_EXT:%[a-z0-9]+]] = zext i32 [[USA]] to i33 + // SIGNED-NEXT: [[SUM:%[0-9]+]] = add i33 [[SA]], [[USA_EXT]] + // SIGNED-NEXT: [[RESULT:%[a-z0-9]+]] = ashr i33 [[SUM]], 1 + // SIGNED-NEXT: [[SUM:%[a-z0-9]+]] = trunc i33 [[RESULT]] to i32 + // UNSIGNED-NEXT: [[EXT_SA:%[a-z0-9]+]] = sext i16 [[SA]] to i32 + // UNSIGNED-NEXT: [[SA:%[a-z0-9]+]] = shl i32 [[EXT_SA]], 8 + // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = add i32 [[SA]], [[USA]] + // CHECK-NEXT: store i32 [[SUM]], i32* %a, align 4 + a = sa + ua; + + // With unsigned of smaller width + // CHECK: [[SA:%[0-9]+]] = load i16, i16* %sa, align 2 + // CHECK-NEXT: [[USF:%[0-9]+]] = load i8, i8* %usf, align 1 + // SIGNED-NEXT: [[SA_EXT:%[a-z0-9]+]] = sext i16 [[SA]] to i17 + // SIGNED-NEXT: [[SA:%[a-z0-9]+]] = shl i17 [[SA_EXT]], 1 + // SIGNED-NEXT: [[USF_EXT:%[a-z0-9]+]] = zext i8 [[USF]] to i17 + // SIGNED-NEXT: [[SUM:%[0-9]+]] = add i17 [[SA]], [[USF_EXT]] + // SIGNED-NEXT: [[RESULT:%[a-z0-9]+]] = ashr i17 [[SUM]], 1 + // SIGNED-NEXT: [[SUM:%[a-z0-9]+]] = trunc i17 [[RESULT]] to i16 + // UNSIGNED-NEXT: [[EXT_USF:%[a-z0-9]+]] = zext i8 [[USF]] to i16 + // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = add i16 [[SA]], [[EXT_USF]] + // CHECK-NEXT: store i16 [[SUM]], i16* %sa, align 2 + sa = sa + usf; + + // With unsigned of larger width and smaller scale + // CHECK: [[SA:%[0-9]+]] = load i16, i16* %sa, align 2 + // CHECK-NEXT: [[ULF:%[0-9]+]] = load i32, i32* %ulf, align 4 + // SIGNED-NEXT: [[SA_EXT:%[a-z0-9]+]] = sext i16 [[SA]] to i41 + // SIGNED-NEXT: [[SA:%[a-z0-9]+]] = shl i41 [[SA_EXT]], 25 + // SIGNED-NEXT: [[ULF_EXT:%[a-z0-9]+]] = zext i32 [[ULF]] to i41 + // SIGNED-NEXT: [[SUM:%[0-9]+]] = add i41 [[SA]], [[ULF_EXT]] + // SIGNED-NEXT: [[RESULT:%[a-z0-9]+]] = ashr i41 [[SUM]], 25 + // SIGNED-NEXT: [[RES_TRUNC:%[a-z0-9]+]] = trunc i41 [[RESULT]] to i16 + // UNSIGNED-NEXT: [[EXT_SA:%[a-z0-9]+]] = sext i16 [[SA]] to i40 + // UNSIGNED-NEXT: [[SA:%[a-z0-9]+]] = shl i40 [[EXT_SA]], 24 + // UNSIGNED-NEXT: [[EXT_ULF:%[a-z0-9]+]] = zext i32 [[ULF]] to i40 + // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = add i40 [[SA]], [[EXT_ULF]] + // UNSIGNED-NEXT: [[RES:%[a-z0-9]+]] = ashr i40 [[SUM]], 24 + // UNSIGNED-NEXT: [[RES_TRUNC:%[a-z0-9]+]] = trunc i40 [[RES]] to i16 + // CHECK-NEXT: store i16 [[RES_TRUNC]], i16* %sa, align 2 + sa = sa + ulf; + + // Chained additions of the same signed type should result in the same + // semantics width. + // CHECK: [[A:%[0-9]+]] = load i32, i32* %a, align 4 + // CHECK-NEXT: [[B:%[0-9]+]] = load i32, i32* %b, align 4 + // CHECK-NEXT: [[SUM:%[0-9]+]] = add i32 [[A]], [[B]] + // CHECK-NEXT: [[C:%[0-9]+]] = load i32, i32* %c, align 4 + // CHECK-NEXT: [[SUM2:%[0-9]+]] = add i32 [[SUM]], [[C]] + // CHECK-NEXT: [[D:%[0-9]+]] = load i32, i32* %d, align 4 + // CHECK-NEXT: [[SUM3:%[0-9]+]] = add i32 [[SUM2]], [[D]] + // CHECK-NEXT: store i32 [[SUM3]], i32* %a, align 4 + a = a + b + c + d; +} + +void UnsignedAddition() { + // CHECK-LABEL: UnsignedAddition + unsigned short _Accum usa; + unsigned _Accum ua; + unsigned long _Accum ula; + + unsigned short _Fract usf; + unsigned _Fract uf; + unsigned long _Fract ulf; + + // CHECK: [[USA:%[0-9]+]] = load i16, i16* %usa, align 2 + // CHECK-NEXT: [[USA2:%[0-9]+]] = load i16, i16* %usa, align 2 + // CHECK-NEXT: [[SUM:%[0-9]+]] = add i16 [[USA]], [[USA2]] + // CHECK-NEXT: store i16 [[SUM]], i16* %usa, align 2 + usa = usa + usa; + + // CHECK: [[USA:%[0-9]+]] = load i16, i16* %usa, align 2 + // CHECK-NEXT: [[UA:%[0-9]+]] = load i32, i32* %ua, align 4 + // CHECK-NEXT: [[EXT_USA:%[a-z0-9]+]] = zext i16 [[USA]] to i32 + // CHECK-NEXT: [[USA:%[a-z0-9]+]] = shl i32 [[EXT_USA]], 8 + // CHECK-NEXT: [[SUM:%[0-9]+]] = add i32 [[USA]], [[UA]] + // CHECK-NEXT: store i32 [[SUM]], i32* %ua, align 4 + ua = usa + ua; + + // CHECK: [[USA:%[0-9]+]] = load i16, i16* %usa, align 2 + // CHECK-NEXT: [[USF:%[0-9]+]] = load i8, i8* %usf, align 1 + // CHECK-NEXT: [[EXT_USF:%[a-z0-9]+]] = zext i8 [[USF]] to i16 + // CHECK-NEXT: [[SUM:%[0-9]+]] = add i16 [[USA]], [[EXT_USF]] + // CHECK-NEXT: store i16 [[SUM]], i16* %usa, align 2 + usa = usa + usf; + + // CHECK: [[USA:%[0-9]+]] = load i16, i16* %usa, align 2 + // CHECK-NEXT: [[UF:%[0-9]+]] = load i16, i16* %uf, align 2 + // CHECK-NEXT: [[USA_EXT:%[a-z0-9]+]] = zext i16 [[USA]] to i24 + // CHECK-NEXT: [[USA:%[a-z0-9]+]] = shl i24 [[USA_EXT]], 8 + // CHECK-NEXT: [[UF_EXT:%[a-z0-9]+]] = zext i16 [[UF]] to i24 + // CHECK-NEXT: [[SUM:%[0-9]+]] = add i24 [[USA]], [[UF_EXT]] + // CHECK-NEXT: [[RES:%[a-z0-9]+]] = lshr i24 [[SUM]], 8 + // CHECK-NEXT: [[RES_TRUNC:%[a-z0-9]+]] = trunc i24 [[RES]] to i16 + // CHECK-NEXT: store i16 [[RES_TRUNC]], i16* %usa, align 2 + usa = usa + uf; +} + +void IntAddition() { + // CHECK-LABEL: IntAddition + short _Accum sa; + _Accum a; + unsigned short _Accum usa; + _Sat short _Accum sa_sat; + int i; + unsigned int ui; + long _Fract lf; + _Bool b; + + // CHECK: [[SA:%[0-9]+]] = load i16, i16* %sa, align 2 + // CHECK-NEXT: [[I:%[0-9]+]] = load i32, i32* %i, align 4 + // CHECK-NEXT: [[SA_EXT:%[a-z0-9]+]] = sext i16 [[SA]] to i39 + // CHECK-NEXT: [[I_EXT:%[a-z0-9]+]] = sext i32 [[I]] to i39 + // CHECK-NEXT: [[I:%[a-z0-9]+]] = shl i39 [[I_EXT]], 7 + // CHECK-NEXT: [[SUM:%[0-9]+]] = add i39 [[SA_EXT]], [[I]] + // CHECK-NEXT: [[RES:%[a-z0-9]+]] = trunc i39 [[SUM]] to i16 + // CHECK-NEXT: store i16 [[RES]], i16* %sa, align 2 + sa = sa + i; + + // CHECK: [[SA:%[0-9]+]] = load i16, i16* %sa, align 2 + // CHECK-NEXT: [[UI:%[0-9]+]] = load i32, i32* %ui, align 4 + // CHECK-NEXT: [[SA_EXT:%[a-z0-9]+]] = sext i16 [[SA]] to i40 + // CHECK-NEXT: [[UI_EXT:%[a-z0-9]+]] = zext i32 [[UI]] to i40 + // CHECK-NEXT: [[UI:%[a-z0-9]+]] = shl i40 [[UI_EXT]], 7 + // CHECK-NEXT: [[SUM:%[0-9]+]] = add i40 [[SA_EXT]], [[UI]] + // CHECK-NEXT: [[RES:%[a-z0-9]+]] = trunc i40 [[SUM]] to i16 + // CHECK-NEXT: store i16 [[RES]], i16* %sa, align 2 + sa = sa + ui; + + // CHECK: [[USA:%[0-9]+]] = load i16, i16* %usa, align 2 + // CHECK-NEXT: [[I:%[0-9]+]] = load i32, i32* %i, align 4 + // SIGNED-NEXT: [[USA_EXT:%[a-z0-9]+]] = zext i16 [[USA]] to i40 + // SIGNED-NEXT: [[I_EXT:%[a-z0-9]+]] = sext i32 [[I]] to i40 + // SIGNED-NEXT: [[I:%[a-z0-9]+]] = shl i40 [[I_EXT]], 8 + // SIGNED-NEXT: [[SUM:%[0-9]+]] = add i40 [[USA_EXT]], [[I]] + // SIGNED-NEXT: [[RES:%[a-z0-9]+]] = trunc i40 [[SUM]] to i16 + // UNSIGNED-NEXT: [[USA_EXT:%[a-z0-9]+]] = zext i16 [[USA]] to i39 + // UNSIGNED-NEXT: [[I_EXT:%[a-z0-9]+]] = sext i32 [[I]] to i39 + // UNSIGNED-NEXT: [[I:%[a-z0-9]+]] = shl i39 [[I_EXT]], 7 + // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = add i39 [[USA_EXT]], [[I]] + // UNSIGNED-NEXT: [[RES:%[a-z0-9]+]] = trunc i39 [[SUM]] to i16 + // CHECK-NEXT: store i16 [[RES]], i16* %usa, align 2 + usa = usa + i; + + // CHECK: [[USA:%[0-9]+]] = load i16, i16* %usa, align 2 + // CHECK-NEXT: [[I:%[0-9]+]] = load i32, i32* %ui, align 4 + // SIGNED-NEXT: [[USA_EXT:%[a-z0-9]+]] = zext i16 [[USA]] to i40 + // SIGNED-NEXT: [[I_EXT:%[a-z0-9]+]] = zext i32 [[I]] to i40 + // SIGNED-NEXT: [[I:%[a-z0-9]+]] = shl i40 [[I_EXT]], 8 + // SIGNED-NEXT: [[SUM:%[0-9]+]] = add i40 [[USA_EXT]], [[I]] + // SIGNED-NEXT: [[RES:%[a-z0-9]+]] = trunc i40 [[SUM]] to i16 + // UNSIGNED-NEXT: [[USA_EXT:%[a-z0-9]+]] = zext i16 [[USA]] to i39 + // UNSIGNED-NEXT: [[I_EXT:%[a-z0-9]+]] = zext i32 [[I]] to i39 + // UNSIGNED-NEXT: [[I:%[a-z0-9]+]] = shl i39 [[I_EXT]], 7 + // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = add i39 [[USA_EXT]], [[I]] + // UNSIGNED-NEXT: [[RES:%[a-z0-9]+]] = trunc i39 [[SUM]] to i16 + // CHECK-NEXT: store i16 [[RES]], i16* %usa, align 2 + usa = usa + ui; + + // CHECK: [[LF:%[0-9]+]] = load i32, i32* %lf, align 4 + // CHECK-NEXT: [[UI:%[0-9]+]] = load i32, i32* %ui, align 4 + // CHECK-NEXT: [[LF_EXT:%[a-z0-9]+]] = sext i32 [[LF]] to i64 + // CHECK-NEXT: [[UI_EXT:%[a-z0-9]+]] = zext i32 [[UI]] to i64 + // CHECK-NEXT: [[UI:%[a-z0-9]+]] = shl i64 [[UI_EXT]], 31 + // CHECK-NEXT: [[SUM:%[0-9]+]] = add i64 [[LF_EXT]], [[UI]] + // CHECK-NEXT: [[RES:%[a-z0-9]+]] = trunc i64 [[SUM]] to i32 + // CHECK-NEXT: store i32 [[RES]], i32* %lf, align 4 + lf = lf + ui; + + // CHECK: [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4 + // CHECK-NEXT: [[BOOL:%[0-9]+]] = load i8, i8* %b, align 1 + // CHECK-NEXT: [[AS_BOOL:%[a-z0-9]+]] = trunc i8 [[BOOL]] to i1 + // CHECK-NEXT: [[BOOL_EXT:%[a-z0-9]+]] = zext i1 [[AS_BOOL]] to i32 + // CHECK-NEXT: [[ACCUM_EXT:%[a-z0-9]+]] = sext i32 [[ACCUM]] to i47 + // CHECK-NEXT: [[BOOL:%[a-z0-9]+]] = sext i32 [[BOOL_EXT]] to i47 + // CHECK-NEXT: [[BOOL_EXT:%[a-z0-9]+]] = shl i47 [[BOOL]], 15 + // CHECK-NEXT: [[SUM:%[0-9]+]] = add i47 [[ACCUM_EXT]], [[BOOL_EXT]] + // CHECK-NEXT: [[RESULT:%[a-z0-9]+]] = trunc i47 [[SUM]] to i32 + // CHECK-NEXT: store i32 [[RESULT]], i32* %a, align 4 + a = a + b; +} + +void SaturatedAddition() { + // CHECK-LABEL: SaturatedAddition + short _Accum sa; + _Accum a; + long _Accum la; + unsigned short _Accum usa; + unsigned _Accum ua; + unsigned long _Accum ula; + + _Sat short _Accum sa_sat; + _Sat _Accum a_sat; + _Sat long _Accum la_sat; + _Sat unsigned short _Accum usa_sat; + _Sat unsigned _Accum ua_sat; + _Sat unsigned long _Accum ula_sat; + _Sat unsigned _Fract uf_sat; + + int i; + unsigned int ui; + + // CHECK: [[SA:%[0-9]+]] = load i16, i16* %sa, align 2 + // CHECK-NEXT: [[SA_SAT:%[0-9]+]] = load i16, i16* %sa_sat, align 2 + // CHECK-NEXT: [[SUM:%[0-9]+]] = call i16 @llvm.sadd.sat.i16(i16 [[SA]], i16 + // [[SA_SAT]]) + // CHECK-NEXT: store i16 [[SUM]], i16* %sa_sat, align 2 + sa_sat = sa + sa_sat; + + // CHECK: [[USA:%[0-9]+]] = load i16, i16* %usa, align 2 + // CHECK-NEXT: [[USA_SAT:%[0-9]+]] = load i16, i16* %usa_sat, align 2 + // SIGNED-NEXT: [[SUM:%[0-9]+]] = call i16 @llvm.uadd.sat.i16(i16 [[USA]], i16 [[USA_SAT]]) + // SIGNED-NEXT: store i16 [[SUM]], i16* %usa_sat, align 2 + // UNSIGNED-NEXT: [[USA_TRUNC:%[a-z0-9]+]] = trunc i16 [[USA]] to i15 + // UNSIGNED-NEXT: [[USA_SAT_TRUNC:%[a-z0-9]+]] = trunc i16 [[USA_SAT]] to i15 + // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = call i15 @llvm.uadd.sat.i15(i15 [[USA_TRUNC]], i15 [[USA_SAT_TRUNC]]) + // UNSIGNED-NEXT: [[SUM_EXT:%[a-z0-9]+]] = zext i15 [[SUM]] to i16 + // UNSIGNED-NEXT: store i16 [[SUM_EXT]], i16* %usa_sat, align 2 + usa_sat = usa + usa_sat; + + // CHECK: [[UA:%[0-9]+]] = load i32, i32* %ua, align 4 + // CHECK-NEXT: [[USA:%[0-9]+]] = load i16, i16* %usa_sat, align 2 + // SIGNED-NEXT: [[USA_EXT:%[a-z0-9]+]] = zext i16 [[USA]] to i32 + // SIGNED-NEXT: [[USA:%[a-z0-9]+]] = shl i32 [[USA_EXT]], 8 + // SIGNED-NEXT: [[SUM:%[0-9]+]] = call i32 @llvm.uadd.sat.i32(i32 [[UA]], i32 [[USA]]) + // SIGNED-NEXT: store i32 [[SUM]], i32* %ua_sat, align 4 + // UNSIGNED-NEXT: [[UA_TRUNC:%[a-z0-9]+]] = trunc i32 [[UA]] to i31 + // UNSIGNED-NEXT: [[USA_EXT:%[a-z0-9]+]] = zext i16 [[USA]] to i31 + // UNSIGNED-NEXT: [[USA:%[a-z0-9]+]] = shl i31 [[USA_EXT]], 8 + // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = call i31 @llvm.uadd.sat.i31(i31 [[UA_TRUNC]], i31 [[USA]]) + // UNSIGNED-NEXT: [[SUM_EXT:%[a-z0-9]+]] = zext i31 [[SUM]] to i32 + // UNSIGNED-NEXT: store i32 [[SUM_EXT]], i32* %ua_sat, align 4 + ua_sat = ua + usa_sat; + + // CHECK: [[SA_SAT:%[0-9]+]] = load i16, i16* %sa_sat, align 2 + // CHECK-NEXT: [[I:%[0-9]+]] = load i32, i32* %i, align 4 + // CHECK-NEXT: [[SA_SAT_EXT:%[a-z0-9]+]] = sext i16 [[SA_SAT]] to i39 + // CHECK-NEXT: [[I_EXT:%[a-z0-9]+]] = sext i32 [[I]] to i39 + // CHECK-NEXT: [[I:%[a-z0-9]+]] = shl i39 [[I_EXT]], 7 + // CHECK-NEXT: [[SUM:%[0-9]+]] = call i39 @llvm.sadd.sat.i39(i39 [[SA_SAT_EXT]], i39 [[I]]) + // CHECK-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i39 [[SUM]], 32767 + // CHECK-NEXT: [[RES:%[a-z0-9]+]] = select i1 [[USE_MAX]], i39 32767, i39 [[SUM]] + // CHECK-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i39 [[RES]], -32768 + // CHECK-NEXT: [[RES2:%[a-z0-9]+]] = select i1 [[USE_MIN]], i39 -32768, i39 [[RES]] + // CHECK-NEXT: [[RES3:%[a-z0-9]+]] = trunc i39 [[RES2]] to i16 + // CHECK-NEXT: store i16 [[RES3]], i16* %sa_sat, align 2 + sa_sat = sa_sat + i; + + // CHECK: [[SA_SAT:%[0-9]+]] = load i16, i16* %sa_sat, align 2 + // CHECK-NEXT: [[I:%[0-9]+]] = load i32, i32* %ui, align 4 + // CHECK-NEXT: [[SA_SAT_EXT:%[a-z0-9]+]] = sext i16 [[SA_SAT]] to i40 + // CHECK-NEXT: [[I_EXT:%[a-z0-9]+]] = zext i32 [[I]] to i40 + // CHECK-NEXT: [[I:%[a-z0-9]+]] = shl i40 [[I_EXT]], 7 + // CHECK-NEXT: [[SUM:%[0-9]+]] = call i40 @llvm.sadd.sat.i40(i40 [[SA_SAT_EXT]], i40 [[I]]) + // CHECK-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i40 [[SUM]], 32767 + // CHECK-NEXT: [[RES:%[a-z0-9]+]] = select i1 [[USE_MAX]], i40 32767, i40 [[SUM]] + // CHECK-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i40 [[RES]], -32768 + // CHECK-NEXT: [[RES2:%[a-z0-9]+]] = select i1 [[USE_MIN]], i40 -32768, i40 [[RES]] + // CHECK-NEXT: [[RES3:%[a-z0-9]+]] = trunc i40 [[RES2]] to i16 + // CHECK-NEXT: store i16 [[RES3]], i16* %sa_sat, align 2 + sa_sat = sa_sat + ui; + + // CHECK: [[UF_SAT:%[0-9]+]] = load i16, i16* %uf_sat, align 2 + // CHECK-NEXT: [[UF_SAT2:%[0-9]+]] = load i16, i16* %uf_sat, align 2 + // SIGNED-NEXT: [[SUM:%[0-9]+]] = call i16 @llvm.uadd.sat.i16(i16 [[UF_SAT]], i16 [[UF_SAT2]]) + // SIGNED-NEXT: store i16 [[SUM]], i16* %uf_sat, align 2 + // UNSIGNED-NEXT: [[UF_SAT_TRUNC:%[a-z0-9]+]] = trunc i16 [[UF_SAT]] to i15 + // UNSIGNED-NEXT: [[UF_SAT_TRUNC2:%[a-z0-9]+]] = trunc i16 [[UF_SAT2]] to i15 + // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = call i15 @llvm.uadd.sat.i15(i15 [[UF_SAT_TRUNC]], i15 [[UF_SAT_TRUNC2]]) + // UNSIGNED-NEXT: [[SUM_EXT:%[a-z0-9]+]] = zext i15 [[SUM]] to i16 + // UNSIGNED-NEXT: store i16 [[SUM_EXT]], i16* %uf_sat, align 2 + uf_sat = uf_sat + uf_sat; + + // CHECK: [[USA_SAT:%[0-9]+]] = load i16, i16* %usa_sat, align 2 + // CHECK-NEXT: [[I:%[0-9]+]] = load i32, i32* %i, align 4 + // SIGNED-NEXT: [[USA_SAT_RESIZE:%[a-z0-9]+]] = zext i16 [[USA_SAT]] to i40 + // SIGNED-NEXT: [[I_RESIZE:%[a-z0-9]+]] = sext i32 [[I]] to i40 + // SIGNED-NEXT: [[I_UPSCALE:%[a-z0-9]+]] = shl i40 [[I_RESIZE]], 8 + // SIGNED-NEXT: [[SUM:%[0-9]+]] = call i40 @llvm.uadd.sat.i40(i40 [[USA_SAT_RESIZE]], i40 [[I_UPSCALE]]) + // SIGNED-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i40 [[SUM]], 65535 + // SIGNED-NEXT: [[RESULT:%[a-z0-9]+]] = select i1 [[USE_MAX]], i40 65535, i40 [[SUM]] + // SIGNED-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i40 [[RESULT]], 0 + // SIGNED-NEXT: [[RESULT2:%[a-z0-9]+]] = select i1 [[USE_MIN]], i40 0, i40 [[RESULT]] + // SIGNED-NEXT: [[RESULT:%[a-z0-9]+]] = trunc i40 [[RESULT2]] to i16 + // UNSIGNED-NEXT: [[USA_SAT_RESIZE:%[a-z0-9]+]] = zext i16 [[USA_SAT]] to i39 + // UNSIGNED-NEXT: [[I_RESIZE:%[a-z0-9]+]] = sext i32 [[I]] to i39 + // UNSIGNED-NEXT: [[I_UPSCALE:%[a-z0-9]+]] = shl i39 [[I_RESIZE]], 7 + // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = call i39 @llvm.uadd.sat.i39(i39 [[USA_SAT_RESIZE]], i39 [[I_UPSCALE]]) + // UNSIGNED-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i39 [[SUM]], 32767 + // UNSIGNED-NEXT: [[RESULT:%[a-z0-9]+]] = select i1 [[USE_MAX]], i39 32767, i39 [[SUM]] + // UNSIGNED-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i39 [[RESULT]], 0 + // UNSIGNED-NEXT: [[RESULT2:%[a-z0-9]+]] = select i1 [[USE_MIN]], i39 0, i39 [[RESULT]] + // UNSIGNED-NEXT: [[RESULT:%[a-z0-9]+]] = trunc i39 [[RESULT2]] to i16 + // CHECK-NEXT: store i16 [[RESULT]], i16* %usa_sat, align 2 + usa_sat = usa_sat + i; +} diff --git a/test/Frontend/fixed_point_conversions.c b/test/Frontend/fixed_point_conversions.c index 7e98fb1e13..22cde0eb69 100644 --- a/test/Frontend/fixed_point_conversions.c +++ b/test/Frontend/fixed_point_conversions.c @@ -214,19 +214,17 @@ void TestFixedPointCastSaturation() { // Only get overflow checking if signed fract to unsigned accum sat_ua = sat_sf; // DEFAULT: [[FRACT:%[0-9a-z]+]] = load i8, i8* %sat_sf, align 1 - // DEFAULT-NEXT: [[FRACT_EXT:%[0-9a-z]+]] = sext i8 [[FRACT]] to i17 - // DEFAULT-NEXT: [[ACCUM:%[0-9a-z]+]] = shl i17 [[FRACT_EXT]], 9 - // DEFAULT-NEXT: [[IS_NEG:%[0-9a-z]+]] = icmp slt i17 [[ACCUM]], 0 - // DEFAULT-NEXT: [[RESULT:%[0-9a-z]+]] = select i1 [[IS_NEG]], i17 0, i17 [[ACCUM]] - // DEFAULT-NEXT: [[RESULT_EXT:%[0-9a-z]+]] = sext i17 [[RESULT]] to i32 - // DEFAULT-NEXT: store i32 [[RESULT_EXT]], i32* %sat_ua, align 4 + // DEFAULT-NEXT: [[FRACT_EXT:%[0-9a-z]+]] = sext i8 [[FRACT]] to i32 + // DEFAULT-NEXT: [[ACCUM:%[0-9a-z]+]] = shl i32 [[FRACT_EXT]], 9 + // DEFAULT-NEXT: [[IS_NEG:%[0-9a-z]+]] = icmp slt i32 [[ACCUM]], 0 + // DEFAULT-NEXT: [[RESULT:%[0-9a-z]+]] = select i1 [[IS_NEG]], i32 0, i32 [[ACCUM]] + // DEFAULT-NEXT: store i32 [[RESULT]], i32* %sat_ua, align 4 // SAME: [[FRACT:%[0-9a-z]+]] = load i8, i8* %sat_sf, align 1 - // SAME-NEXT: [[FRACT_EXT:%[0-9a-z]+]] = sext i8 [[FRACT]] to i16 - // SAME-NEXT: [[ACCUM:%[0-9a-z]+]] = shl i16 [[FRACT_EXT]], 8 - // SAME-NEXT: [[IS_NEG:%[0-9a-z]+]] = icmp slt i16 [[ACCUM]], 0 - // SAME-NEXT: [[RESULT:%[0-9a-z]+]] = select i1 [[IS_NEG]], i16 0, i16 [[ACCUM]] - // SAME-NEXT: [[RESULT_EXT:%[0-9a-z]+]] = sext i16 [[RESULT]] to i32 - // SAME-NEXT: store i32 [[RESULT_EXT]], i32* %sat_ua, align 4 + // SAME-NEXT: [[FRACT_EXT:%[0-9a-z]+]] = sext i8 [[FRACT]] to i32 + // SAME-NEXT: [[ACCUM:%[0-9a-z]+]] = shl i32 [[FRACT_EXT]], 8 + // SAME-NEXT: [[IS_NEG:%[0-9a-z]+]] = icmp slt i32 [[ACCUM]], 0 + // SAME-NEXT: [[RESULT:%[0-9a-z]+]] = select i1 [[IS_NEG]], i32 0, i32 [[ACCUM]] + // SAME-NEXT: store i32 [[RESULT]], i32* %sat_ua, align 4 } void TestFixedPointCastBetFractAccum() { -- 2.40.0