Index: clang/include/clang/AST/ASTContext.h =================================================================== --- clang/include/clang/AST/ASTContext.h +++ clang/include/clang/AST/ASTContext.h @@ -2633,6 +2633,10 @@ // 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. + QualType getCorrespondingSignedFixedPointType(QualType Ty) const; + //===--------------------------------------------------------------------===// // Integer Values //===--------------------------------------------------------------------===// Index: clang/include/clang/AST/Type.h =================================================================== --- clang/include/clang/AST/Type.h +++ clang/include/clang/AST/Type.h @@ -2275,6 +2275,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; @@ -6582,6 +6585,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: clang/include/clang/Basic/FixedPoint.h =================================================================== --- clang/include/clang/Basic/FixedPoint.h +++ clang/include/clang/Basic/FixedPoint.h @@ -36,6 +36,9 @@ : Width(Width), Scale(Scale), IsSigned(IsSigned), IsSaturated(IsSaturated), HasUnsignedPadding(HasUnsignedPadding) { assert(Width >= Scale && "Not enough room for the scale"); + if (IsSigned) + assert(!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,16 @@ return Width - Scale; } + /// Return the FixedPointSemantics that allows for calculating the full + /// precision semantic between this one and another one. The semantics will + /// not be saturated since the entire result is able to be stored in these + /// semantics. + FixedPointSemantics + getCommonSemantics(const FixedPointSemantics &Other) const; + + /// Return the FixedPointSemantics for an integer type. + static FixedPointSemantics GetIntegerSemantics(unsigned Width, bool IsSigned); + private: unsigned Width; unsigned Scale; Index: clang/lib/AST/ASTContext.cpp =================================================================== --- clang/lib/AST/ASTContext.cpp +++ clang/lib/AST/ASTContext.cpp @@ -10481,7 +10481,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, @@ -10498,3 +10504,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: clang/lib/Basic/FixedPoint.cpp =================================================================== --- clang/lib/Basic/FixedPoint.cpp +++ clang/lib/Basic/FixedPoint.cpp @@ -112,4 +112,31 @@ return APFixedPoint(Val, Sema); } +FixedPointSemantics FixedPointSemantics::GetIntegerSemantics(unsigned Width, + bool IsSigned) { + return FixedPointSemantics(Width, /*Scale=*/0, IsSigned, + /*IsSaturated=*/false, + /*HasUnsignedPadding=*/false); +} + +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 ResultHasUnsignedPadding = false; + if (!ResultIsSigned) + // Both are unsigned. + ResultHasUnsignedPadding = + hasUnsignedPadding() && Other.hasUnsignedPadding(); + + if (ResultIsSigned || ResultHasUnsignedPadding) + CommonWidth++; + + return FixedPointSemantics(CommonWidth, CommonScale, ResultIsSigned, + /*IsSaturated=*/false, ResultHasUnsignedPadding); +} + } // namespace clang 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) { @@ -340,6 +347,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. @@ -718,6 +728,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 &), @@ -1412,10 +1425,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()); @@ -1423,6 +1432,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(); @@ -1451,7 +1470,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"); @@ -1482,7 +1502,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; } @@ -3328,9 +3349,60 @@ 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 = 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::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. If the result has unsigned padding, then we + // do not need to use saturation since overflow will occur in the padding. + Value *Result; + if (ResultFixedSema.isSaturated() && !ResultFixedSema.hasUnsignedPadding()) { + llvm::Intrinsic::ID IID = CommonFixedSema.isSigned() + ? llvm::Intrinsic::sadd_sat + : llvm::Intrinsic::uadd_sat; + Result = Builder.CreateBinaryIntrinsic(IID, FullLHS, FullRHS); + } else { + Result = Builder.CreateAdd(FullLHS, FullRHS); + } + + // 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: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -1229,6 +1229,99 @@ 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) { + if (Ty->isIntegerType()) + return 0; + + const auto *BTy = Ty->getAs(); + 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: + 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, ExprResult &LHSExpr, + ExprResult &RHSExpr, 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()) { + QualType ResultTy = S.Context.getCorrespondingSignedFixedPointType(LHSTy); + LHSExpr = S.ImpCastExprToType(LHSExpr.get(), ResultTy, CK_FixedPointCast); + LHSTy = ResultTy; + } else if (RHSTy->isUnsignedFixedPointType() && + LHSTy->isSignedFixedPointType()) { + QualType ResultTy = S.Context.getCorrespondingSignedFixedPointType(RHSTy); + RHSExpr = S.ImpCastExprToType(RHSExpr.get(), ResultTy, CK_FixedPointCast); + RHSTy = 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. + // 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 @@ -1301,12 +1394,14 @@ return handleComplexIntConversion(*this, LHS, RHS, LHSType, RHSType, IsCompAssign); + if (LHSType->isFixedPointType() || RHSType->isFixedPointType()) + return handleFixedPointConversion(*this, LHS, RHS, LHSType, RHSType); + // Finally, we have two differing integer types. return handleIntegerConversion (*this, LHS, RHS, LHSType, RHSType, IsCompAssign); } - //===----------------------------------------------------------------------===// // Semantic Analysis for various Expression Types //===----------------------------------------------------------------------===// Index: clang/test/Frontend/fixed_point_add.c =================================================================== --- /dev/null +++ clang/test/Frontend/fixed_point_add.c @@ -0,0 +1,312 @@ +// 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:%[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: [[USA_SHR:%[a-z0-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:%[a-z0-9]+]] = lshr i32 [[USA]], 1 + // CHECK-NEXT: [[EXT_SA:%[a-z0-9]+]] = sext i16 [[SA]] to i32 + // CHECK-NEXT: [[SA:%[a-z0-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:%[a-z0-9]+]] = lshr i8 [[USF]], 1 + // SIGNED-NEXT: [[EXT_USF:%[a-z0-9]+]] = sext i8 [[USF_SHR]] to i16 + // UNSIGNED-NEXT: [[EXT_USF:%[a-z0-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:%[a-z0-9]+]] = lshr i32 [[ULF]], 1 + // CHECK-NEXT: [[EXT_SA:%[a-z0-9]+]] = sext i16 [[SA]] to i40 + // CHECK-NEXT: [[SA:%[a-z0-9]+]] = shl i40 [[EXT_SA]], 24 + // SIGNED-NEXT: [[EXT_ULF:%[a-z0-9]+]] = sext i32 [[ULF_SHR]] to i40 + // UNSIGNED-NEXT: [[EXT_ULF:%[a-z0-9]+]] = sext i32 [[ULF]] to i40 + // CHECK-NEXT: [[SUM:%[0-9]+]] = add i40 [[SA]], [[EXT_ULF]] + // CHECK-NEXT: [[RES:%[a-z0-9]+]] = ashr i40 [[SUM]], 24 + // CHECK-NEXT: [[RES_TRUNC:%[a-z0-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:%[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; + unsigned short _Accum usa; + _Sat short _Accum sa_sat; + int i; + unsigned int ui; + long _Fract lf; + + // 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; +} + +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]]) + // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = add i16 [[USA]], [[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:%[a-z0-9]+]] = zext i16 [[USA]] to i32 + // CHECK-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]]) + // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = add i32 [[UA]], [[USA]] + // CHECK-NEXT: store i32 [[SUM]], 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]]) + // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = add i16 [[UF_SAT]], [[UF_SAT2]] + // CHECK-NEXT: store i16 [[SUM]], i16* %uf_sat, align 2 + uf_sat = uf_sat + uf_sat; +} 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; +} Index: clang/test/Frontend/fixed_point_conversions.c =================================================================== --- clang/test/Frontend/fixed_point_conversions.c +++ clang/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() {