Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -9460,6 +9460,14 @@ QualType UsualArithmeticConversions(ExprResult &LHS, ExprResult &RHS, bool IsCompAssign = false); + /// FixedPointConversions - 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). + QualType FixedPointConversions(ExprResult &FixedExpr, ExprResult &OtherExpr, + bool IsCompAssign = false); + /// AssignConvertType - All of the 'assignment' semantic checks return this /// enum to indicate whether the assignment was allowed. These checks are /// done for simple assignments, as well as initialization, return from Index: clang/lib/CodeGen/CGExprScalar.cpp =================================================================== --- clang/lib/CodeGen/CGExprScalar.cpp +++ clang/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) { @@ -714,6 +721,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 &), @@ -3135,9 +3145,128 @@ return propagateFMFlags(V, op); } + if (op.isFixedPointBinOp()) + return EmitFixedPointAdd(op); + return Builder.CreateAdd(op.LHS, op.RHS, "add"); } +/// Extract the width, scale, and sign from a fixed point or integer type. If +/// the type is an integer, the scale is zero. +static void GetFixedPointAttributes(ASTContext &Ctx, QualType Ty, + unsigned &Width, unsigned &Scale, + bool &Sign) { + if (Ty->isFixedPointType()) { + auto FixedSema = Ctx.getFixedPointSemantics(Ty); + Width = FixedSema.getWidth(); + Scale = FixedSema.getScale(); + Sign = Ty->isSignedFixedPointType(); + } else if (Ty->isIntegerType()) { + Width = Ctx.getIntWidth(Ty); + Scale = 0; + Sign = Ty->isSignedIntegerType(); + } else { + llvm_unreachable("Expected the type to be a fixed point or integer type"); + } +} + +/// 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 = dyn_cast(op.E); + assert(BinOp && "Expected the operator to be a binary operator"); + 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::FixedPointConversions(). + 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; + + unsigned LHSWidth, LHSScale, RHSWidth, RHSScale, ResultWidth, ResultScale; + bool LHSSign, RHSSign, ResultSign; + GetFixedPointAttributes(Ctx, LHSTy, LHSWidth, LHSScale, LHSSign); + GetFixedPointAttributes(Ctx, RHSTy, RHSWidth, RHSScale, RHSSign); + GetFixedPointAttributes(Ctx, ResultTy, ResultWidth, ResultScale, ResultSign); + + // Cast both to a common type than can hold the full precision of the + // resulting value. + unsigned LHSIntegralBits = LHSWidth - LHSScale; + unsigned RHSIntegralBits = RHSWidth - RHSScale; + unsigned CommonWidth = + std::max(LHSIntegralBits, RHSIntegralBits) + std::max(LHSScale, RHSScale); + llvm::Type *CommonTy = Builder.getIntNTy(CommonWidth); + + if (LHSWidth < CommonWidth) + LHS = LHSSign ? Builder.CreateSExt(LHS, CommonTy) + : Builder.CreateZExt(LHS, CommonTy); + if (RHSWidth < CommonWidth) + RHS = RHSSign ? Builder.CreateSExt(RHS, CommonTy) + : Builder.CreateZExt(RHS, CommonTy); + + // Align scales + unsigned CommonScale = std::max({LHSScale, RHSScale, ResultScale}); + if (CommonScale > LHSScale) + LHS = Builder.CreateShl(LHS, CommonScale - LHSScale); + if (CommonScale > RHSScale) + RHS = Builder.CreateShl(RHS, CommonScale - RHSScale); + + Value *Result; + if (ResultTy->isSaturatedFixedPointType()) { + if (ResultWidth < CommonWidth) { + // In the event we extended the sign of both operands, the intrinsic will + // not saturate to the initial bit width of the result type. In this case, + // we can default to use min/max clamping. This can arise from adding an + // operand of an int type whose width is larger than the width of the + // other fixed point operand. + // If we end up implementing the intrinsics for saturating ints to + // specified witdths, this section could be replaced to a call to those + // intrinsics. + APSInt MaxVal = + Ctx.getFixedPointMax(ResultTy).getValue().extend(CommonWidth); + APSInt MinVal = + Ctx.getFixedPointMin(ResultTy).getValue().extend(CommonWidth); + auto MaxValConst = ConstantInt::get(CommonTy, MaxVal); + auto MinValConst = ConstantInt::get(CommonTy, MinVal); + + Result = Builder.CreateAdd(LHS, RHS); + Value *UseMax = ResultSign ? Builder.CreateICmpSGT(Result, MaxValConst) + : Builder.CreateICmpUGT(Result, MaxValConst); + Value *UseMin = ResultSign ? Builder.CreateICmpSLT(Result, MinValConst) + : Builder.CreateICmpULT(Result, MinValConst); + + Result = Builder.CreateSelect( + UseMax, MaxValConst, + Builder.CreateSelect(UseMin, MinValConst, Result)); + } else { + unsigned IID = + ResultSign ? llvm::Intrinsic::sadd_sat : llvm::Intrinsic::uadd_sat; + llvm::Function *intrinsic = CGF.CGM.getIntrinsic(IID, CommonTy); + Result = Builder.CreateCall(intrinsic, {LHS, RHS}); + } + } else { + Result = Builder.CreateAdd(LHS, RHS); + } + + // Align to result scale + if (ResultScale < CommonScale) { + Result = ResultSign ? Builder.CreateAShr(Result, CommonScale - ResultScale) + : Builder.CreateLShr(Result, CommonScale - ResultScale); + } + + if (ResultWidth < CommonWidth) + Result = Builder.CreateTrunc(Result, Builder.getIntNTy(ResultWidth)); + + return Result; +} + Value *ScalarExprEmitter::EmitSub(const BinOpInfo &op) { // The LHS is always a pointer if either side is. if (!op.LHS->getType()->isPointerTy()) { Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -1306,6 +1306,148 @@ (*this, LHS, RHS, LHSType, RHSType, IsCompAssign); } +/// For a given fixed point type, return it's signed equivalent. +static QualType GetCorrespondingSignedFixedPointType(ASTContext &Ctx, + QualType Ty) { + assert(Ty->isUnsignedFixedPointType() && + "Expected unsigned fixed point type"); + const auto *BTy = Ty->getAs(); + + switch (BTy->getKind()) { + case BuiltinType::UShortAccum: + return Ctx.ShortAccumTy; + case BuiltinType::UAccum: + return Ctx.AccumTy; + case BuiltinType::ULongAccum: + return Ctx.LongAccumTy; + case BuiltinType::SatUShortAccum: + return Ctx.SatShortAccumTy; + case BuiltinType::SatUAccum: + return Ctx.SatAccumTy; + case BuiltinType::SatULongAccum: + return Ctx.SatLongAccumTy; + case BuiltinType::UShortFract: + return Ctx.ShortFractTy; + case BuiltinType::UFract: + return Ctx.FractTy; + case BuiltinType::ULongFract: + return Ctx.LongFractTy; + case BuiltinType::SatUShortFract: + return Ctx.SatShortFractTy; + case BuiltinType::SatUFract: + return Ctx.SatFractTy; + case BuiltinType::SatULongFract: + return Ctx.SatLongFractTy; + default: + llvm_unreachable("Unexpected unsigned fixed point type"); + } +} + +/// Return the rank of a given fixed point 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) { + assert(Ty->isFixedPointType() && "Expected fixed point type"); + const auto *BTy = Ty->getAs(); + + switch (BTy->getKind()) { + case BuiltinType::ShortFract: + case BuiltinType::UShortFract: + case BuiltinType::SatShortFract: + case BuiltinType::SatUShortFract: + return 0; + case BuiltinType::Fract: + case BuiltinType::UFract: + case BuiltinType::SatFract: + case BuiltinType::SatUFract: + return 1; + case BuiltinType::LongFract: + case BuiltinType::ULongFract: + case BuiltinType::SatLongFract: + case BuiltinType::SatULongFract: + return 2; + case BuiltinType::ShortAccum: + case BuiltinType::UShortAccum: + case BuiltinType::SatShortAccum: + case BuiltinType::SatUShortAccum: + return 3; + case BuiltinType::Accum: + case BuiltinType::UAccum: + case BuiltinType::SatAccum: + case BuiltinType::SatUAccum: + return 4; + case BuiltinType::LongAccum: + case BuiltinType::ULongAccum: + case BuiltinType::SatLongAccum: + case BuiltinType::SatULongAccum: + return 5; + default: + llvm_unreachable("Unexpected fixed point type"); + } +} + +/// If an operand in a binary operation is a fixed point type, do not perform +/// implicit conversions on it (with the exception of unsigned to signed +// conversion). Instead the resulting type is determined by the +/// rules in N1169 4.1.4. +QualType Sema::FixedPointConversions(ExprResult &FixedExpr, + ExprResult &OtherExpr, bool IsCompAssign) { + QualType FixedTy = FixedExpr.get()->getType(); + QualType OtherTy = OtherExpr.get()->getType(); + assert(FixedTy->isFixedPointType() && + "Expected FixedTy to be a fixed point type"); + assert((OtherTy->isFixedPointType() || OtherTy->isIntegerType()) && + "Special fixed point arithmetic operation conversions are only " + "applied to ints or other fixed point types"); + + // LValue to RValue conversions + if (!IsCompAssign) { + FixedExpr = UsualUnaryConversions(FixedExpr.get()); + if (FixedExpr.isInvalid()) + return QualType(); + } + OtherExpr = UsualUnaryConversions(OtherExpr.get()); + if (OtherExpr.isInvalid()) + return QualType(); + + // 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 (OtherTy->isSignedFixedPointType() && + FixedTy->isUnsignedFixedPointType()) { + QualType ResultTy = GetCorrespondingSignedFixedPointType(Context, FixedTy); + FixedExpr = ImpCastExprToType(FixedExpr.get(), ResultTy, CK_FixedPointCast); + FixedTy = ResultTy; + } else if (OtherTy->isUnsignedFixedPointType() && + FixedTy->isSignedFixedPointType()) { + QualType ResultTy = GetCorrespondingSignedFixedPointType(Context, OtherTy); + OtherExpr = ImpCastExprToType(OtherExpr.get(), ResultTy, CK_FixedPointCast); + OtherTy = ResultTy; + } + + // 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. + if (OtherTy->isIntegerType()) + return FixedTy; + + // Same ranks between signed and unsigned types are resolved earlier, so both + // types are either signed or both unsigned at this point. + unsigned FixedTyRank = GetFixedPointRank(FixedTy); + unsigned OtherTyRank = GetFixedPointRank(OtherTy); + + QualType ResultTy = FixedTyRank > OtherTyRank ? FixedTy : OtherTy; + + if (FixedTy->isSaturatedFixedPointType() || + OtherTy->isSaturatedFixedPointType()) + ResultTy = Context.getCorrespondingSaturatedType(ResultTy); + + return ResultTy; +} //===----------------------------------------------------------------------===// // Semantic Analysis for various Expression Types @@ -9070,7 +9212,13 @@ return compType; } - QualType compType = UsualArithmeticConversions(LHS, RHS, CompLHSTy); + QualType compType; + if (LHS.get()->getType()->isFixedPointType()) + compType = FixedPointConversions(LHS, RHS, CompLHSTy); + else if (RHS.get()->getType()->isFixedPointType()) + compType = FixedPointConversions(RHS, LHS, CompLHSTy); + else + compType = UsualArithmeticConversions(LHS, RHS, CompLHSTy); if (LHS.isInvalid() || RHS.isInvalid()) return QualType(); Index: clang/test/Frontend/fixed_point_add.c =================================================================== --- /dev/null +++ clang/test/Frontend/fixed_point_add.c @@ -0,0 +1,280 @@ +// 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; + 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:%[0-9]+]] = sext i16 [[SA]] to i32 + // CHECK-NEXT: [[SA:%[0-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 smaller scale and same 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:%[0-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 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:%[0-9]+]] = sext i8 [[SF]] to i32 + // CHECK-NEXT: [[SF:%[0-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:%[0-9]+]] = sext i32 [[A]] to i48 + // CHECK-NEXT: [[EXT_LF:%[0-9]+]] = sext i32 [[LF]] to i48 + // CHECK-NEXT: [[A:%[0-9]+]] = shl i48 [[EXT_A]], 16 + // CHECK-NEXT: [[SUM:%[0-9]+]] = add i48 [[A]], [[EXT_LF]] + // CHECK-NEXT: [[RES:%[0-9]+]] = ashr i48 [[SUM]], 16 + // CHECK-NEXT: [[TRUNC_RES:%[0-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: [[USA_SHR:%[0-9]+]] = lshr i16 [[USA]], 1 + // SIGNED-NEXT: [[SUM:%[0-9]+]] = add i16 [[SA]], [[USA_SHR]] + // 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: [[USA_SHR:%[0-9]+]] = lshr i32 [[USA]], 1 + // CHECK-NEXT: [[EXT_SA:%[0-9]+]] = sext i16 [[SA]] to i32 + // CHECK-NEXT: [[SA:%[0-9]+]] = shl i32 [[EXT_SA]], 8 + // SIGNED-NEXT: [[SUM:%[0-9]+]] = add i32 [[SA]], [[USA_SHR]] + // 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: [[USF_SHR:%[0-9]+]] = lshr i8 [[USF]], 1 + // SIGNED-NEXT: [[EXT_USF:%[0-9]+]] = sext i8 [[USF_SHR]] to i16 + // UNSIGNED-NEXT: [[EXT_USF:%[0-9]+]] = sext i8 [[USF]] to i16 + // CHECK-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: [[ULF_SHR:%[0-9]+]] = lshr i32 [[ULF]], 1 + // CHECK-NEXT: [[EXT_SA:%[0-9]+]] = sext i16 [[SA]] to i40 + // SIGNED-NEXT: [[EXT_ULF:%[0-9]+]] = sext i32 [[ULF_SHR]] to i40 + // UNSIGNED-NEXT: [[EXT_ULF:%[0-9]+]] = sext i32 [[ULF]] to i40 + // CHECK-NEXT: [[SA:%[0-9]+]] = shl i40 [[EXT_SA]], 24 + // CHECK-NEXT: [[SUM:%[0-9]+]] = add i40 [[SA]], [[EXT_ULF]] + // CHECK-NEXT: [[RES:%[0-9]+]] = ashr i40 [[SUM]], 24 + // CHECK-NEXT: [[RES_TRUNC:%[0-9]+]] = trunc i40 [[RES]] to i16 + // CHECK-NEXT: store i16 [[RES_TRUNC]], i16* %sa, align 2 + sa = sa + ulf; +} + +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:%[0-9]+]] = zext i16 [[USA]] to i32 + // CHECK-NEXT: [[USA:%[0-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:%[0-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:%[0-9]+]] = zext i16 [[USA]] to i24 + // CHECK-NEXT: [[UF_EXT:%[0-9]+]] = zext i16 [[UF]] to i24 + // CHECK-NEXT: [[USA:%[0-9]+]] = shl i24 [[USA_EXT]], 8 + // CHECK-NEXT: [[SUM:%[0-9]+]] = add i24 [[USA]], [[UF_EXT]] + // CHECK-NEXT: [[RES:%[0-9]+]] = lshr i24 [[SUM]], 8 + // CHECK-NEXT: [[RES_TRUNC:%[0-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; + unsigned short _Accum usa; + _Sat short _Accum sa_sat; + int i; + unsigned int ui; + + // 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:%[0-9]+]] = sext i16 [[SA]] to i39 + // CHECK-NEXT: [[I_EXT:%[0-9]+]] = sext i32 [[I]] to i39 + // CHECK-NEXT: [[I:%[0-9]+]] = shl i39 [[I_EXT]], 7 + // CHECK-NEXT: [[SUM:%[0-9]+]] = add i39 [[SA_EXT]], [[I]] + // CHECK-NEXT: [[RES:%[0-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:%[0-9]+]] = sext i16 [[SA]] to i39 + // CHECK-NEXT: [[UI_EXT:%[0-9]+]] = zext i32 [[UI]] to i39 + // CHECK-NEXT: [[UI:%[0-9]+]] = shl i39 [[UI_EXT]], 7 + // CHECK-NEXT: [[SUM:%[0-9]+]] = add i39 [[SA_EXT]], [[UI]] + // CHECK-NEXT: [[RES:%[0-9]+]] = trunc i39 [[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:%[0-9]+]] = zext i16 [[USA]] to i40 + // SIGNED-NEXT: [[I_EXT:%[0-9]+]] = sext i32 [[I]] to i40 + // SIGNED-NEXT: [[I:%[0-9]+]] = shl i40 [[I_EXT]], 8 + // SIGNED-NEXT: [[SUM:%[0-9]+]] = add i40 [[USA_EXT]], [[I]] + // SIGNED-NEXT: [[RES:%[0-9]+]] = trunc i40 [[SUM]] to i16 + // UNSIGNED-NEXT: [[USA_EXT:%[0-9]+]] = zext i16 [[USA]] to i39 + // UNSIGNED-NEXT: [[I_EXT:%[0-9]+]] = sext i32 [[I]] to i39 + // UNSIGNED-NEXT: [[I:%[0-9]+]] = shl i39 [[I_EXT]], 7 + // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = add i39 [[USA_EXT]], [[I]] + // UNSIGNED-NEXT: [[RES:%[0-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:%[0-9]+]] = zext i16 [[USA]] to i40 + // SIGNED-NEXT: [[I_EXT:%[0-9]+]] = zext i32 [[I]] to i40 + // SIGNED-NEXT: [[I:%[0-9]+]] = shl i40 [[I_EXT]], 8 + // SIGNED-NEXT: [[SUM:%[0-9]+]] = add i40 [[USA_EXT]], [[I]] + // SIGNED-NEXT: [[RES:%[0-9]+]] = trunc i40 [[SUM]] to i16 + // UNSIGNED-NEXT: [[USA_EXT:%[0-9]+]] = zext i16 [[USA]] to i39 + // UNSIGNED-NEXT: [[I_EXT:%[0-9]+]] = zext i32 [[I]] to i39 + // UNSIGNED-NEXT: [[I:%[0-9]+]] = shl i39 [[I_EXT]], 7 + // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = add i39 [[USA_EXT]], [[I]] + // UNSIGNED-NEXT: [[RES:%[0-9]+]] = trunc i39 [[SUM]] to i16 + // CHECK-NEXT: store i16 [[RES]], i16* %usa, align 2 + usa = usa + ui; +} + +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; + + 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 + // CHECK-NEXT: [[SUM:%[0-9]+]] = call i16 @llvm.uadd.sat.i16(i16 [[USA]], i16 + // [[USA_SAT]]) + // CHECK-NEXT: store i16 [[SUM]], 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 + // CHECK-NEXT: [[USA_EXT:%[0-9]+]] = zext i16 [[USA]] to i32 + // CHECK-NEXT: [[USA:%[0-9]+]] = shl i32 [[USA_EXT]], 8 + // CHECK-NEXT: [[SUM:%[0-9]+]] = call i32 @llvm.uadd.sat.i32(i32 [[UA]], i32 [[USA]]) + // CHECK-NEXT: store i32 %10, 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:%[0-9]+]] = sext i16 [[SA_SAT]] to i39 + // CHECK-NEXT: [[I_EXT:%[0-9]+]] = sext i32 [[I]] to i39 + // CHECK-NEXT: [[I:%[0-9]+]] = shl i39 [[I_EXT]], 7 + // CHECK-NEXT: [[SUM:%[0-9]+]] = add i39 [[SA_SAT_EXT]], [[I]] + // CHECK-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i39 [[SUM]], 32767 + // CHECK-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i39 [[SUM]], -32768 + // CHECK-NEXT: [[RES:%[0-9]+]] = select i1 [[USE_MIN]], i39 -32768, i39 [[SUM]] + // CHECK-NEXT: [[RES2:%[0-9]+]] = select i1 [[USE_MAX]], i39 32767, i39 [[RES]] + // CHECK-NEXT: [[RES3:%[0-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:%[0-9]+]] = sext i16 [[SA_SAT]] to i39 + // CHECK-NEXT: [[I_EXT:%[0-9]+]] = zext i32 [[I]] to i39 + // CHECK-NEXT: [[I:%[0-9]+]] = shl i39 [[I_EXT]], 7 + // CHECK-NEXT: [[SUM:%[0-9]+]] = add i39 [[SA_SAT_EXT]], [[I]] + // CHECK-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i39 [[SUM]], 32767 + // CHECK-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i39 [[SUM]], -32768 + // CHECK-NEXT: [[RES:%[0-9]+]] = select i1 [[USE_MIN]], i39 -32768, i39 [[SUM]] + // CHECK-NEXT: [[RES2:%[0-9]+]] = select i1 [[USE_MAX]], i39 32767, i39 [[RES]] + // CHECK-NEXT: [[RES3:%[0-9]+]] = trunc i39 [[RES2]] to i16 + // CHECK-NEXT: store i16 [[RES3]], i16* %sa_sat, align 2 + sa_sat = sa_sat + ui; +} Index: clang/test/Frontend/fixed_point_add_ast.c =================================================================== --- /dev/null +++ clang/test/Frontend/fixed_point_add_ast.c @@ -0,0 +1,337 @@ +// RUN: %clang_cc1 -x c -ffixed-point -ast-dump %s | FileCheck %s --strict-whitespace + +void SignedAdditions() { + // CHECK-LABEL: SignedAdditions + short _Accum sa; + _Accum a; + 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; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} 'short _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'short _Accum' + // CHECK-NEXT: `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum' + sa = sa + sa; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} '_Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} '_Accum' + // CHECK-NEXT: `-DeclRefExpr {{.*}} '_Accum' {{.*}} 'a' '_Accum' + a = sa + a; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} 'long _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'long _Accum' + // CHECK-NEXT: `-DeclRefExpr {{.*}} 'long _Accum' {{.*}} 'la' 'long _Accum' + la = sa + la; + + // CHECK-NOT: FixedPointCast + // CHECK: `-ImplicitCastExpr {{.*}} 'long _Accum' + // CHECK-NEXT: `-BinaryOperator {{.*}} '_Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} '_Accum' + // CHECK-NEXT: `-DeclRefExpr {{.*}} '_Accum' {{.*}} 'a' '_Accum' + la = sa + a; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} 'short _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'short _Fract' + // CHECK-NEXT: `-DeclRefExpr {{.*}} 'short _Fract' {{.*}} 'sf' 'short _Fract' + sa = sa + sf; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} 'short _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} '_Fract' + // CHECK-NEXT: `-DeclRefExpr {{.*}} '_Fract' {{.*}} 'f' '_Fract' + sa = sa + f; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} 'short _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'long _Fract' + // CHECK-NEXT: `-DeclRefExpr {{.*}} 'long _Fract' {{.*}} 'lf' 'long _Fract' + sa = sa + lf; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} 'short _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'unsigned short _Accum' + // CHECK-NEXT: `-DeclRefExpr {{.*}} 'unsigned short _Accum' {{.*}} 'usa' 'unsigned short _Accum' + sa = sa + usa; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} '_Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} '_Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'unsigned _Accum' + // CHECK-NEXT: `-DeclRefExpr {{.*}} 'unsigned _Accum' {{.*}} 'ua' 'unsigned _Accum' + a = sa + ua; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} 'long _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'long _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'unsigned long _Accum' + // CHECK-NEXT: `-DeclRefExpr {{.*}} 'unsigned long _Accum' {{.*}} 'ula' 'unsigned long _Accum' + la = sa + ula; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} 'short _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'short _Fract' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'unsigned short _Fract' + // CHECK-NEXT: `-DeclRefExpr {{.*}} 'unsigned short _Fract' {{.*}} 'usf' 'unsigned short _Fract' + sa = sa + usf; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} 'short _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} '_Fract' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'unsigned _Fract' + // CHECK-NEXT: `-DeclRefExpr {{.*}} 'unsigned _Fract' {{.*}} 'uf' 'unsigned _Fract' + sa = sa + uf; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} 'short _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'long _Fract' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'unsigned long _Fract' + // CHECK-NEXT: `-DeclRefExpr {{.*}} 'unsigned long _Fract' {{.*}} 'ulf' 'unsigned long _Fract' + sa = sa + ulf; +} + +void UnsignedAdditions() { + // CHECK-LABEL: UnsignedAdditions + unsigned short _Accum usa; + unsigned _Accum ua; + unsigned long _Accum ula; + + unsigned short _Fract usf; + unsigned _Fract uf; + unsigned long _Fract ulf; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} 'unsigned short _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'unsigned short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'unsigned short _Accum' {{.*}} 'usa' 'unsigned short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'unsigned short _Accum' + // CHECK-NEXT: `-DeclRefExpr {{.*}} 'unsigned short _Accum' {{.*}} 'usa' 'unsigned short _Accum' + usa = usa + usa; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} 'unsigned _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'unsigned short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'unsigned short _Accum' {{.*}} 'usa' 'unsigned short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'unsigned _Accum' + // CHECK-NEXT: `-DeclRefExpr {{.*}} 'unsigned _Accum' {{.*}} 'ua' 'unsigned _Accum' + ua = usa + ua; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} 'unsigned long _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'unsigned short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'unsigned short _Accum' {{.*}} 'usa' 'unsigned short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'unsigned long _Accum' + // CHECK-NEXT: `-DeclRefExpr {{.*}} 'unsigned long _Accum' {{.*}} 'ula' 'unsigned long _Accum' + ula = usa + ula; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} 'unsigned short _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'unsigned short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'unsigned short _Accum' {{.*}} 'usa' 'unsigned short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'unsigned short _Fract' + // CHECK-NEXT: `-DeclRefExpr {{.*}} 'unsigned short _Fract' {{.*}} 'usf' 'unsigned short _Fract' + usa = usa + usf; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} 'unsigned short _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'unsigned short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'unsigned short _Accum' {{.*}} 'usa' 'unsigned short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'unsigned _Fract' + // CHECK-NEXT: `-DeclRefExpr {{.*}} 'unsigned _Fract' {{.*}} 'uf' 'unsigned _Fract' + usa = usa + uf; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} 'unsigned short _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'unsigned short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'unsigned short _Accum' {{.*}} 'usa' 'unsigned short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'unsigned long _Fract' + // CHECK-NEXT: `-DeclRefExpr {{.*}} 'unsigned long _Fract' {{.*}} 'ulf' 'unsigned long _Fract' + usa = usa + ulf; +} + +void IntAdditions() { + // CHECK-LABEL: IntAdditions + short _Accum sa; + unsigned short _Accum usa; + int i; + unsigned int ui; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} 'short _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int' + // CHECK-NEXT: `-DeclRefExpr {{.*}} 'int' {{.*}} 'i' 'int' + sa = sa + i; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} 'short _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'unsigned int' + // CHECK-NEXT: `-DeclRefExpr {{.*}} 'unsigned int' {{.*}} 'ui' 'unsigned int' + sa = sa + ui; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} 'unsigned short _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'unsigned short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'unsigned short _Accum' {{.*}} 'usa' 'unsigned short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int' + // CHECK-NEXT: `-DeclRefExpr {{.*}} 'int' {{.*}} 'i' 'int' + usa = usa + i; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} 'unsigned short _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'unsigned short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'unsigned short _Accum' {{.*}} 'usa' 'unsigned short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'unsigned int' + // CHECK-NEXT: `-DeclRefExpr {{.*}} 'unsigned int' {{.*}} 'ui' 'unsigned int' + usa = usa + ui; +} + +void SaturatedAdditions() { + // CHECK-LABEL: SaturatedAdditions + 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; + + short _Fract sf; + _Fract f; + long _Fract lf; + unsigned short _Fract usf; + unsigned _Fract uf; + unsigned long _Fract ulf; + + int i; + unsigned int ui; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} '_Sat short _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} '_Sat short _Accum' + // CHECK-NEXT: `-DeclRefExpr {{.*}} '_Sat short _Accum' {{.*}} 'sa_sat' '_Sat short _Accum' + sa_sat = sa + sa_sat; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} '_Sat long _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} '_Sat long _Accum' + // CHECK-NEXT: `-DeclRefExpr {{.*}} '_Sat long _Accum' {{.*}} 'la_sat' '_Sat long _Accum' + la_sat = sa + la_sat; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} '_Sat long _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} '_Sat short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} '_Sat short _Accum' {{.*}} 'sa_sat' '_Sat short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'long _Accum' + // CHECK-NEXT: `-DeclRefExpr {{.*}} 'long _Accum' {{.*}} 'la' 'long _Accum' + la_sat = sa_sat + la; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} '_Sat short _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} '_Sat short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} '_Sat unsigned short _Accum' + // CHECK-NEXT: `-DeclRefExpr {{.*}} '_Sat unsigned short _Accum' {{.*}} 'usa_sat' '_Sat unsigned short _Accum' + sa_sat = sa + usa_sat; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} '_Sat long _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'short _Accum' + // CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'unsigned short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'unsigned short _Accum' {{.*}} 'usa' 'unsigned short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} '_Sat long _Accum' + // CHECK-NEXT: `-DeclRefExpr {{.*}} '_Sat long _Accum' {{.*}} 'la_sat' '_Sat long _Accum' + la_sat = usa + la_sat; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} '_Sat unsigned long _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'unsigned short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'unsigned short _Accum' {{.*}} 'usa' 'unsigned short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} '_Sat unsigned long _Accum' + // CHECK-NEXT: `-DeclRefExpr {{.*}} '_Sat unsigned long _Accum' {{.*}} 'ula_sat' '_Sat unsigned long _Accum' + ula_sat = usa + ula_sat; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} '_Sat short _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} '_Sat short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} '_Sat short _Accum' {{.*}} 'sa_sat' '_Sat short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} '_Sat short _Accum' + // CHECK-NEXT: `-DeclRefExpr {{.*}} '_Sat short _Accum' {{.*}} 'sa_sat' '_Sat short _Accum' + sa_sat = sa_sat + sa_sat; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} '_Sat long _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} '_Sat short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} '_Sat short _Accum' {{.*}} 'sa_sat' '_Sat short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} '_Sat long _Accum' + // CHECK-NEXT: `-DeclRefExpr {{.*}} '_Sat long _Accum' {{.*}} 'la_sat' '_Sat long _Accum' + la_sat = sa_sat + la_sat; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} '_Sat short _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} '_Sat short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} '_Sat short _Accum' {{.*}} 'sa_sat' '_Sat short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int' + // CHECK-NEXT: `-DeclRefExpr {{.*}} 'int' {{.*}} 'i' 'int' + sa_sat = sa_sat + i; + + // CHECK-NOT: FixedPointCast + // CHECK: `-BinaryOperator {{.*}} '_Sat short _Accum' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.*}} '_Sat short _Accum' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} '_Sat short _Accum' {{.*}} 'sa_sat' '_Sat short _Accum' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'unsigned int' + // CHECK-NEXT: `-DeclRefExpr {{.*}} 'unsigned int' {{.*}} 'ui' 'unsigned int' + sa_sat = sa_sat + ui; +}