Index: cfe/trunk/include/clang/AST/ASTContext.h =================================================================== --- cfe/trunk/include/clang/AST/ASTContext.h +++ cfe/trunk/include/clang/AST/ASTContext.h @@ -2624,6 +2624,12 @@ // 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 //===--------------------------------------------------------------------===// Index: cfe/trunk/include/clang/AST/Type.h =================================================================== --- cfe/trunk/include/clang/AST/Type.h +++ cfe/trunk/include/clang/AST/Type.h @@ -2269,6 +2269,9 @@ /// 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 @@ 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 && Index: cfe/trunk/include/clang/Basic/FixedPoint.h =================================================================== --- cfe/trunk/include/clang/Basic/FixedPoint.h +++ cfe/trunk/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 @@ : 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 @@ 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 @@ 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; Index: cfe/trunk/lib/AST/ASTContext.cpp =================================================================== --- cfe/trunk/lib/AST/ASTContext.cpp +++ cfe/trunk/lib/AST/ASTContext.cpp @@ -10485,7 +10485,13 @@ } 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 @@ 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"); + } +} Index: cfe/trunk/lib/Basic/FixedPoint.cpp =================================================================== --- cfe/trunk/lib/Basic/FixedPoint.cpp +++ cfe/trunk/lib/Basic/FixedPoint.cpp @@ -112,4 +112,29 @@ 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 Index: cfe/trunk/lib/CodeGen/CGExprScalar.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGExprScalar.cpp +++ cfe/trunk/lib/CodeGen/CGExprScalar.cpp @@ -125,6 +125,13 @@ 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 @@ 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 @@ 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::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 @@ 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 @@ } 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 @@ } // 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 @@ 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()) { Index: cfe/trunk/lib/Sema/SemaExpr.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaExpr.cpp +++ cfe/trunk/lib/Sema/SemaExpr.cpp @@ -1250,6 +1250,93 @@ 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 @@ 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 //===----------------------------------------------------------------------===// Index: cfe/trunk/test/Frontend/fixed_point_add.c =================================================================== --- cfe/trunk/test/Frontend/fixed_point_add.c +++ cfe/trunk/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; +} Index: cfe/trunk/test/Frontend/fixed_point_conversions.c =================================================================== --- cfe/trunk/test/Frontend/fixed_point_conversions.c +++ cfe/trunk/test/Frontend/fixed_point_conversions.c @@ -214,19 +214,17 @@ // 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() {