Index: include/clang/AST/OperationKinds.def =================================================================== --- include/clang/AST/OperationKinds.def +++ include/clang/AST/OperationKinds.def @@ -200,6 +200,14 @@ /// (_Accum) 0.5r CAST_OPERATION(FixedPointCast) +/// CK_FixedPointToIntegral - Fixed point to integral. +/// (int) 2.0k +CAST_OPERATION(FixedPointToIntegral) + +/// CK_IntegralToFixedPoint - Integral to a fixed point. +/// (_Accum) 2 +CAST_OPERATION(IntegralToFixedPoint) + /// CK_FixedPointToBoolean - Fixed point to boolean. /// (bool) 0.5r CAST_OPERATION(FixedPointToBoolean) Index: include/clang/Basic/FixedPoint.h =================================================================== --- include/clang/Basic/FixedPoint.h +++ include/clang/Basic/FixedPoint.h @@ -141,6 +141,8 @@ return APFixedPoint(Val << Amt, Sema); } + /// Return the integral part of this fixed point number, rounded towards + /// zero. (-2.5k -> -2) llvm::APSInt getIntPart() const { if (Val < 0 && Val != -Val) // Cover the case when we have the min val return -(-Val >> getScale()); @@ -148,6 +150,17 @@ return Val >> getScale(); } + /// Return the integral part of this fixed point number, rounded towards + /// zero. The value is stored into an APSInt with the provided width and sign. + /// If the overflow parameter is provided, and the integral value is not able + /// to be fully stored in the provided width and sign, the overflow parameter + /// is set to true. + /// + /// If the overflow parameter is provided, set this value to true or false to + /// indicate if this operation results in an overflow. + llvm::APSInt convertToInt(unsigned DstWidth, bool DstSign, + bool *Overflow = nullptr) const; + void toString(llvm::SmallVectorImpl &Str) const; std::string toString() const { llvm::SmallString<40> S; @@ -175,6 +188,14 @@ static APFixedPoint getMax(const FixedPointSemantics &Sema); static APFixedPoint getMin(const FixedPointSemantics &Sema); + /// Create an APFixedPoint with a value equal to that of the provided integer, + /// and in the same semantics as the provided target semantics. If the value + /// is not able to fit in the specified fixed point semantics, and the + /// overflow parameter is provided, it is set to true. + static APFixedPoint getFromIntValue(const llvm::APSInt &Value, + const FixedPointSemantics &DstFXSema, + bool *Overflow = nullptr); + private: llvm::APSInt Val; FixedPointSemantics Sema; Index: lib/AST/Expr.cpp =================================================================== --- lib/AST/Expr.cpp +++ lib/AST/Expr.cpp @@ -1718,6 +1718,8 @@ case CK_ZeroToOCLOpaqueType: case CK_IntToOCLSampler: case CK_FixedPointCast: + case CK_FixedPointToIntegral: + case CK_IntegralToFixedPoint: assert(!getType()->isBooleanType() && "unheralded conversion to bool"); goto CheckNoBasePath; Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -9776,6 +9776,7 @@ case CK_AddressSpaceConversion: case CK_IntToOCLSampler: case CK_FixedPointCast: + case CK_IntegralToFixedPoint: llvm_unreachable("invalid cast kind for integral value"); case CK_BitCast: @@ -9810,6 +9811,19 @@ return Success(IntResult, E); } + case CK_FixedPointToIntegral: { + APFixedPoint Src(Info.Ctx.getFixedPointSemantics(SrcType)); + if (!EvaluateFixedPoint(SubExpr, Src, Info)) + return false; + bool Overflowed; + llvm::APSInt Result = Src.convertToInt( + Info.Ctx.getIntWidth(DestType), + DestType->isSignedIntegerOrEnumerationType(), &Overflowed); + if (Overflowed && !HandleOverflow(Info, E, Result, DestType)) + return false; + return Success(Result, E); + } + case CK_FixedPointToBoolean: { // Unsigned padding does not affect this. APValue Val; @@ -9970,6 +9984,20 @@ return false; return Success(Result, E); } + case CK_IntegralToFixedPoint: { + APSInt Src; + if (!EvaluateInteger(SubExpr, Src, Info)) + return false; + + bool Overflowed; + APFixedPoint IntResult = APFixedPoint::getFromIntValue( + Src, Info.Ctx.getFixedPointSemantics(DestType), &Overflowed); + + if (Overflowed && !HandleOverflow(Info, E, IntResult, DestType)) + return false; + + return Success(IntResult, E); + } case CK_NoOp: case CK_LValueToRValue: return ExprEvaluatorBaseTy::VisitCastExpr(E); @@ -10371,6 +10399,8 @@ case CK_IntToOCLSampler: case CK_FixedPointCast: case CK_FixedPointToBoolean: + case CK_FixedPointToIntegral: + case CK_IntegralToFixedPoint: llvm_unreachable("invalid cast kind for complex value"); case CK_LValueToRValue: Index: lib/Basic/FixedPoint.cpp =================================================================== --- lib/Basic/FixedPoint.cpp +++ lib/Basic/FixedPoint.cpp @@ -218,4 +218,41 @@ return APFixedPoint(Sema); } +llvm::APSInt APFixedPoint::convertToInt(unsigned DstWidth, bool DstSign, + bool *Overflow) const { + llvm::APSInt Result = getIntPart(); + unsigned SrcWidth = getWidth(); + + llvm::APSInt DstMin = llvm::APSInt::getMinValue(DstWidth, !DstSign); + llvm::APSInt DstMax = llvm::APSInt::getMaxValue(DstWidth, !DstSign); + + if (SrcWidth < DstWidth) { + Result = Result.extend(DstWidth); + } else if (SrcWidth > DstWidth) { + DstMin = DstMin.extend(SrcWidth); + DstMax = DstMax.extend(SrcWidth); + } + + if (Overflow) { + if (Result.isSigned() && !DstSign) { + *Overflow = Result.isNegative() || Result.ugt(DstMax); + } else if (Result.isUnsigned() && DstSign) { + *Overflow = Result.ugt(DstMax); + } else { + *Overflow = Result < DstMin || Result > DstMax; + } + } + + Result.setIsSigned(DstSign); + return Result.extOrTrunc(DstWidth); +} + +APFixedPoint APFixedPoint::getFromIntValue(const llvm::APSInt &Value, + const FixedPointSemantics &DstFXSema, + bool *Overflow) { + FixedPointSemantics IntFXSema = FixedPointSemantics::GetIntegerSemantics( + Value.getBitWidth(), Value.isSigned()); + return APFixedPoint(Value, IntFXSema).convert(DstFXSema, Overflow); +} + } // namespace clang Index: lib/CodeGen/CGExpr.cpp =================================================================== --- lib/CodeGen/CGExpr.cpp +++ lib/CodeGen/CGExpr.cpp @@ -4162,6 +4162,8 @@ case CK_IntToOCLSampler: case CK_FixedPointCast: case CK_FixedPointToBoolean: + case CK_FixedPointToIntegral: + case CK_IntegralToFixedPoint: return EmitUnsupportedLValue(E, "unexpected cast lvalue"); case CK_Dependent: Index: lib/CodeGen/CGExprAgg.cpp =================================================================== --- lib/CodeGen/CGExprAgg.cpp +++ lib/CodeGen/CGExprAgg.cpp @@ -853,6 +853,8 @@ case CK_IntToOCLSampler: case CK_FixedPointCast: case CK_FixedPointToBoolean: + case CK_FixedPointToIntegral: + case CK_IntegralToFixedPoint: llvm_unreachable("cast kind invalid for aggregate types"); } } Index: lib/CodeGen/CGExprComplex.cpp =================================================================== --- lib/CodeGen/CGExprComplex.cpp +++ lib/CodeGen/CGExprComplex.cpp @@ -509,6 +509,8 @@ case CK_IntToOCLSampler: case CK_FixedPointCast: case CK_FixedPointToBoolean: + case CK_FixedPointToIntegral: + case CK_IntegralToFixedPoint: llvm_unreachable("invalid cast kind for complex value"); case CK_FloatingRealToComplex: Index: lib/CodeGen/CGExprConstant.cpp =================================================================== --- lib/CodeGen/CGExprConstant.cpp +++ lib/CodeGen/CGExprConstant.cpp @@ -876,6 +876,8 @@ case CK_FloatingCast: case CK_FixedPointCast: case CK_FixedPointToBoolean: + case CK_FixedPointToIntegral: + case CK_IntegralToFixedPoint: case CK_ZeroToOCLOpaqueType: return nullptr; } Index: lib/CodeGen/CGExprScalar.cpp =================================================================== --- lib/CodeGen/CGExprScalar.cpp +++ lib/CodeGen/CGExprScalar.cpp @@ -363,11 +363,14 @@ SourceLocation Loc, ScalarConversionOpts Opts = ScalarConversionOpts()); + /// Convert between either a fixed point and other fixed point or fixed point + /// and an integer. Value *EmitFixedPointConversion(Value *Src, QualType SrcTy, QualType DstTy, SourceLocation Loc); Value *EmitFixedPointConversion(Value *Src, FixedPointSemantics &SrcFixedSema, FixedPointSemantics &DstFixedSema, - SourceLocation Loc); + SourceLocation Loc, + bool DstIsInteger = false); /// Emit a conversion from the specified complex type to the specified /// destination type, where the destination type is an LLVM scalar type. @@ -1225,17 +1228,25 @@ // TODO(leonardchan): When necessary, add another if statement checking for // conversions to fixed point types from other types. if (SrcType->isFixedPointType()) { - if (DstType->isFixedPointType()) { - return EmitFixedPointConversion(Src, SrcType, DstType, Loc); - } else if (DstType->isBooleanType()) { + if (DstType->isBooleanType()) + // It is important that we check this before checking if the dest type is + // an integer because booleans are technically integer types. // We do not need to check the padding bit on unsigned types if unsigned // padding is enabled because overflow into this bit is undefined // behavior. return Builder.CreateIsNotNull(Src, "tobool"); - } + if (DstType->isFixedPointType() || DstType->isIntegerType()) + return EmitFixedPointConversion(Src, SrcType, DstType, Loc); + + llvm_unreachable( + "Unhandled scalar conversion from a fixed point type to another type."); + } else if (DstType->isFixedPointType()) { + if (SrcType->isIntegerType()) + // This also includes converting booleans and enums to fixed point types. + return EmitFixedPointConversion(Src, SrcType, DstType, Loc); llvm_unreachable( - "Unhandled scalar conversion involving a fixed point type."); + "Unhandled scalar conversion to a fixed point type from another type."); } QualType NoncanonicalSrcType = SrcType; @@ -1443,19 +1454,17 @@ Value *ScalarExprEmitter::EmitFixedPointConversion(Value *Src, QualType SrcTy, QualType DstTy, SourceLocation Loc) { - assert(SrcTy->isFixedPointType()); - assert(DstTy->isFixedPointType()); - FixedPointSemantics SrcFPSema = CGF.getContext().getFixedPointSemantics(SrcTy); FixedPointSemantics DstFPSema = CGF.getContext().getFixedPointSemantics(DstTy); - return EmitFixedPointConversion(Src, SrcFPSema, DstFPSema, Loc); + return EmitFixedPointConversion(Src, SrcFPSema, DstFPSema, Loc, + DstTy->isIntegerType()); } Value *ScalarExprEmitter::EmitFixedPointConversion( Value *Src, FixedPointSemantics &SrcFPSema, FixedPointSemantics &DstFPSema, - SourceLocation Loc) { + SourceLocation Loc, bool DstIsInteger) { using llvm::APInt; using llvm::ConstantInt; using llvm::Value; @@ -1472,13 +1481,26 @@ Value *Result = Src; unsigned ResultWidth = SrcWidth; - if (!DstFPSema.isSaturated()) { - // Downscale. - if (DstScale < SrcScale) - Result = SrcIsSigned ? - Builder.CreateAShr(Result, SrcScale - DstScale, "downscale") : - Builder.CreateLShr(Result, SrcScale - DstScale, "downscale"); + // Downscale. + if (DstScale < SrcScale) { + // When converting to integers, we round towards zero. For negative numbers, + // right shifting rounds towards negative infinity. In this case, we can + // just round up before shifting. + if (DstIsInteger && SrcIsSigned) { + Value *Zero = llvm::Constant::getNullValue(Result->getType()); + Value *IsNegative = Builder.CreateICmpSLT(Result, Zero); + Value *LowBits = ConstantInt::get( + CGF.getLLVMContext(), APInt::getLowBitsSet(ResultWidth, SrcScale)); + Value *Rounded = Builder.CreateAdd(Result, LowBits); + Result = Builder.CreateSelect(IsNegative, Rounded, Result); + } + + Result = SrcIsSigned + ? Builder.CreateAShr(Result, SrcScale - DstScale, "downscale") + : Builder.CreateLShr(Result, SrcScale - DstScale, "downscale"); + } + if (!DstFPSema.isSaturated()) { // Resize. Result = Builder.CreateIntCast(Result, DstIntTy, SrcIsSigned, "resize"); @@ -1493,10 +1515,6 @@ llvm::Type *UpscaledTy = Builder.getIntNTy(ResultWidth); Result = Builder.CreateIntCast(Result, UpscaledTy, SrcIsSigned, "resize"); Result = Builder.CreateShl(Result, DstScale - SrcScale, "upscale"); - } else if (DstScale < SrcScale) { - Result = SrcIsSigned ? - Builder.CreateAShr(Result, SrcScale - DstScale, "downscale") : - Builder.CreateLShr(Result, SrcScale - DstScale, "downscale"); } // Handle saturation. @@ -2228,6 +2246,21 @@ return EmitScalarConversion(Visit(E), E->getType(), DestTy, CE->getExprLoc()); + case CK_FixedPointToIntegral: + assert(E->getType()->isFixedPointType() && + "Expected src type to be fixed point type"); + assert(DestTy->isIntegerType() && "Expected dest type to be an integer"); + return EmitScalarConversion(Visit(E), E->getType(), DestTy, + CE->getExprLoc()); + + case CK_IntegralToFixedPoint: + assert(E->getType()->isIntegerType() && + "Expected src type to be an integer"); + assert(DestTy->isFixedPointType() && + "Expected dest type to be fixed point type"); + return EmitScalarConversion(Visit(E), E->getType(), DestTy, + CE->getExprLoc()); + case CK_IntegralCast: { ScalarConversionOpts Opts; if (auto *ICE = dyn_cast(CE)) { Index: lib/Edit/RewriteObjCFoundationAPI.cpp =================================================================== --- lib/Edit/RewriteObjCFoundationAPI.cpp +++ lib/Edit/RewriteObjCFoundationAPI.cpp @@ -1086,6 +1086,8 @@ case CK_FixedPointCast: case CK_FixedPointToBoolean: + case CK_FixedPointToIntegral: + case CK_IntegralToFixedPoint: llvm_unreachable("Fixed point types are disabled for Objective-C"); } } Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -11018,10 +11018,9 @@ return; } + // Valid casts involving fixed point types should be accounted for here. if (Source->isFixedPointType()) { - // TODO: Only CK_FixedPointCast is supported now. The other valid casts - // should be accounted for here. - if (Target->isFixedPointType()) { + if (Target->isUnsaturatedFixedPointType()) { Expr::EvalResult Result; if (E->EvaluateAsFixedPoint(Result, S.Context, Expr::SE_AllowSideEffects)) { @@ -11037,6 +11036,46 @@ return; } } + } else if (Target->isIntegerType()) { + Expr::EvalResult Result; + if (E->EvaluateAsFixedPoint(Result, S.Context, + Expr::SE_AllowSideEffects)) { + APFixedPoint FXResult = Result.Val.getFixedPoint(); + + bool Overflowed; + llvm::APSInt IntResult = FXResult.convertToInt( + S.Context.getIntWidth(T), + Target->isSignedIntegerOrEnumerationType(), &Overflowed); + + if (Overflowed) { + S.DiagRuntimeBehavior(E->getExprLoc(), E, + S.PDiag(diag::warn_impcast_fixed_point_range) + << FXResult.toString() << T + << E->getSourceRange() + << clang::SourceRange(CC)); + return; + } + } + } + } else if (Target->isUnsaturatedFixedPointType()) { + if (Source->isIntegerType()) { + Expr::EvalResult Result; + if (E->EvaluateAsInt(Result, S.Context, Expr::SE_AllowSideEffects)) { + llvm::APSInt Value = Result.Val.getInt(); + + bool Overflowed; + APFixedPoint IntResult = APFixedPoint::getFromIntValue( + Value, S.Context.getFixedPointSemantics(T), &Overflowed); + + if (Overflowed) { + S.DiagRuntimeBehavior(E->getExprLoc(), E, + S.PDiag(diag::warn_impcast_fixed_point_range) + << Value.toString(/*radix=*/10) << T + << E->getSourceRange() + << clang::SourceRange(CC)); + return; + } + } } } Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -6152,6 +6152,7 @@ case Type::STK_Bool: return CK_FixedPointToBoolean; case Type::STK_Integral: + return CK_FixedPointToIntegral; case Type::STK_Floating: case Type::STK_IntegralComplex: case Type::STK_FloatingComplex: @@ -6196,10 +6197,7 @@ case Type::STK_MemberPointer: llvm_unreachable("member pointer type in C"); case Type::STK_FixedPoint: - Diag(Src.get()->getExprLoc(), - diag::err_unimplemented_conversion_with_fixed_point_type) - << SrcTy; - return CK_IntegralCast; + return CK_IntegralToFixedPoint; } llvm_unreachable("Should have returned before this"); Index: lib/StaticAnalyzer/Core/ExprEngineC.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -415,7 +415,9 @@ case CK_IntToOCLSampler: case CK_LValueBitCast: case CK_FixedPointCast: - case CK_FixedPointToBoolean: { + case CK_FixedPointToBoolean: + case CK_FixedPointToIntegral: + case CK_IntegralToFixedPoint: { state = handleLValueBitCast(state, Ex, LCtx, T, ExTy, CastE, Bldr, Pred); continue; Index: test/Frontend/fixed_point_conversions.c =================================================================== --- test/Frontend/fixed_point_conversions.c +++ test/Frontend/fixed_point_conversions.c @@ -1,142 +1,179 @@ -// RUN: %clang_cc1 -ffixed-point -triple x86_64-unknown-linux-gnu -S -emit-llvm %s -o - | FileCheck %s -check-prefix=DEFAULT -// RUN: %clang_cc1 -ffixed-point -triple x86_64-unknown-linux-gnu -S -emit-llvm %s -o - -fpadding-on-unsigned-fixed-point | FileCheck %s -check-prefix=SAME +// RUN: %clang_cc1 -ffixed-point -triple x86_64-unknown-linux-gnu -S -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,SIGNED +// RUN: %clang_cc1 -ffixed-point -triple x86_64-unknown-linux-gnu -S -emit-llvm %s -o - -fpadding-on-unsigned-fixed-point | FileCheck %s --check-prefixes=CHECK,UNSIGNED // Between different fixed point types -short _Accum sa_const = 2.5hk; // DEFAULT-DAG: @sa_const = {{.*}}global i16 320, align 2 -_Accum a_const = 2.5hk; // DEFAULT-DAG: @a_const = {{.*}}global i32 81920, align 4 -short _Accum sa_const2 = 2.5k; // DEFAULT-DAG: @sa_const2 = {{.*}}global i16 320, align 2 +short _Accum sa_const = 2.5hk; // CHECK-DAG: @sa_const = {{.*}}global i16 320, align 2 +_Accum a_const = 2.5hk; // CHECK-DAG: @a_const = {{.*}}global i32 81920, align 4 +short _Accum sa_const2 = 2.5k; // CHECK-DAG: @sa_const2 = {{.*}}global i16 320, align 2 -short _Accum sa_from_f_const = 0.5r; // DEFAULT-DAG: sa_from_f_const = {{.*}}global i16 64, align 2 -_Fract f_from_sa_const = 0.5hk; // DEFAULT-DAG: f_from_sa_const = {{.*}}global i16 16384, align 2 +short _Accum sa_from_f_const = 0.5r; // CHECK-DAG: sa_from_f_const = {{.*}}global i16 64, align 2 +_Fract f_from_sa_const = 0.5hk; // CHECK-DAG: f_from_sa_const = {{.*}}global i16 16384, align 2 unsigned short _Accum usa_const = 2.5uk; unsigned _Accum ua_const = 2.5uhk; -// DEFAULT-DAG: @usa_const = {{.*}}global i16 640, align 2 -// DEFAULT-DAG: @ua_const = {{.*}}global i32 163840, align 4 -// SAME-DAG: @usa_const = {{.*}}global i16 320, align 2 -// SAME-DAG: @ua_const = {{.*}}global i32 81920, align 4 +// SIGNED-DAG: @usa_const = {{.*}}global i16 640, align 2 +// SIGNED-DAG: @ua_const = {{.*}}global i32 163840, align 4 +// UNSIGNED-DAG: @usa_const = {{.*}}global i16 320, align 2 +// UNSIGNED-DAG: @ua_const = {{.*}}global i32 81920, align 4 + +// FixedPoint to integer +int i_const = -128.0hk; // CHECK-DAG: @i_const = {{.*}}global i32 -128, align 4 +int i_const2 = 128.0hk; // CHECK-DAG: @i_const2 = {{.*}}global i32 128, align 4 +int i_const3 = -128.0k; // CHECK-DAG: @i_const3 = {{.*}}global i32 -128, align 4 +int i_const4 = 128.0k; // CHECK-DAG: @i_const4 = {{.*}}global i32 128, align 4 +short s_const = -128.0k; // CHECK-DAG: @s_const = {{.*}}global i16 -128, align 2 +short s_const2 = 128.0k; // CHECK-DAG: @s_const2 = {{.*}}global i16 128, align 2 + +// Integer to fixed point +short _Accum sa_const5 = 2; // CHECK-DAG: @sa_const5 = {{.*}}global i16 256, align 2 +short _Accum sa_const6 = -2; // CHECK-DAG: @sa_const6 = {{.*}}global i16 -256, align 2 +short _Accum sa_const7 = -256; // CHECK-DAG: @sa_const7 = {{.*}}global i16 -32768, align 2 // Signedness unsigned short _Accum usa_const2 = 2.5hk; -// DEFAULT-DAG: @usa_const2 = {{.*}}global i16 640, align 2 -// SAME-DAG: @usa_const2 = {{.*}}global i16 320, align 2 -short _Accum sa_const3 = 2.5hk; // DEFAULT-DAG: @sa_const3 = {{.*}}global i16 320, align 2 +// SIGNED-DAG: @usa_const2 = {{.*}}global i16 640, align 2 +// UNSIGNED-DAG: @usa_const2 = {{.*}}global i16 320, align 2 +short _Accum sa_const3 = 2.5hk; // CHECK-DAG: @sa_const3 = {{.*}}global i16 320, align 2 + +int i_const5 = 128.0uhk; +unsigned int ui_const = 128.0hk; +// CHECK-DAG: @i_const5 = {{.*}}global i32 128, align 4 +// CHECK-DAG: @ui_const = {{.*}}global i32 128, align 4 + +short _Accum sa_const9 = 2u; // CHECK-DAG: @sa_const9 = {{.*}}global i16 256, align 2 +unsigned short _Accum usa_const3 = 2; +// SIGNED-DAG: @usa_const3 = {{.*}}global i16 512, align 2 +// UNSIGNED-DAG: @usa_const3 = {{.*}}global i16 256, align 2 // Overflow (this is undefined but allowed) short _Accum sa_const4 = 256.0k; +unsigned int ui_const2 = -2.5hk; +short _Accum sa_const8 = 256; +unsigned short _Accum usa_const4 = -2; // Saturation -_Sat short _Accum sat_sa_const = 2.5hk; // DEFAULT-DAG: @sat_sa_const = {{.*}}global i16 320, align 2 -_Sat short _Accum sat_sa_const2 = 256.0k; // DEFAULT-DAG: @sat_sa_const2 = {{.*}}global i16 32767, align 2 +_Sat short _Accum sat_sa_const = 2.5hk; // CHECK-DAG: @sat_sa_const = {{.*}}global i16 320, align 2 +_Sat short _Accum sat_sa_const2 = 256.0k; // CHECK-DAG: @sat_sa_const2 = {{.*}}global i16 32767, align 2 _Sat unsigned short _Accum sat_usa_const = -1.0hk; -// DEFAULT-DAG: @sat_usa_const = {{.*}}global i16 0, align 2 -// SAME-DAG: @sat_usa_const = {{.*}}global i16 0, align 2 +// CHECK-DAG: @sat_usa_const = {{.*}}global i16 0, align 2 _Sat unsigned short _Accum sat_usa_const2 = 256.0k; -// DEFAULT-DAG: @sat_usa_const2 = {{.*}}global i16 -1, align 2 -// SAME-DAG: @sat_usa_const2 = {{.*}}global i16 32767, align 2 +// SIGNED-DAG: @sat_usa_const2 = {{.*}}global i16 -1, align 2 +// UNSIGNED-DAG: @sat_usa_const2 = {{.*}}global i16 32767, align 2 + +_Sat short _Accum sat_sa_const3 = 256; // CHECK-DAG: @sat_sa_const3 = {{.*}}global i16 32767, align 2 +_Sat short _Accum sat_sa_const4 = -257; // CHECK-DAG: @sat_sa_const4 = {{.*}}global i16 -32768, align 2 +_Sat unsigned short _Accum sat_usa_const3 = -1; +// CHECK-DAG: @sat_usa_const3 = {{.*}}global i16 0, align 2 +_Sat unsigned short _Accum sat_usa_const4 = 256; +// SIGNED-DAG: @sat_usa_const4 = {{.*}}global i16 -1, align 2 +// UNSIGNED-DAG: @sat_usa_const4 = {{.*}}global i16 32767, align 2 void TestFixedPointCastSameType() { _Accum a = 2.5k; _Accum a2 = a; - // DEFAULT: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a, align 4 - // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a2, align 4 + // CHECK: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a, align 4 + // CHECK-NEXT: store i32 [[ACCUM]], i32* %a2, align 4 a2 = (_Accum)a; - // DEFAULT: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a, align 4 - // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a2, align 4 + // CHECK: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a, align 4 + // CHECK-NEXT: store i32 [[ACCUM]], i32* %a2, align 4 } void TestFixedPointCastDown() { long _Accum la = 2.5lk; _Accum a = la; - // DEFAULT: [[LACCUM:%[0-9a-z]+]] = load i64, i64* %la, align 8 - // DEFAULT-NEXT: [[ACCUM_AS_I64:%[0-9a-z]+]] = ashr i64 [[LACCUM]], 16 - // DEFAULT-NEXT: [[ACCUM:%[0-9a-z]+]] = trunc i64 [[ACCUM_AS_I64]] to i32 - // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a, align 4 + // CHECK: [[LACCUM:%[0-9a-z]+]] = load i64, i64* %la, align 8 + // CHECK-NEXT: [[ACCUM_AS_I64:%[0-9a-z]+]] = ashr i64 [[LACCUM]], 16 + // CHECK-NEXT: [[ACCUM:%[0-9a-z]+]] = trunc i64 [[ACCUM_AS_I64]] to i32 + // CHECK-NEXT: store i32 [[ACCUM]], i32* %a, align 4 a = (_Accum)la; - // DEFAULT: [[LACCUM:%[0-9a-z]+]] = load i64, i64* %la, align 8 - // DEFAULT-NEXT: [[ACCUM_AS_I64:%[0-9a-z]+]] = ashr i64 [[LACCUM]], 16 - // DEFAULT-NEXT: [[ACCUM:%[0-9a-z]+]] = trunc i64 [[ACCUM_AS_I64]] to i32 - // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a, align 4 + // CHECK: [[LACCUM:%[0-9a-z]+]] = load i64, i64* %la, align 8 + // CHECK-NEXT: [[ACCUM_AS_I64:%[0-9a-z]+]] = ashr i64 [[LACCUM]], 16 + // CHECK-NEXT: [[ACCUM:%[0-9a-z]+]] = trunc i64 [[ACCUM_AS_I64]] to i32 + // CHECK-NEXT: store i32 [[ACCUM]], i32* %a, align 4 short _Accum sa = a; - // DEFAULT: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a, align 4 - // DEFAULT-NEXT: [[SACCUM_AS_I32:%[0-9a-z]+]] = ashr i32 [[ACCUM]], 8 - // DEFAULT-NEXT: [[SACCUM:%[0-9a-z]+]] = trunc i32 [[SACCUM_AS_I32]] to i16 - // DEFAULT-NEXT: store i16 [[SACCUM]], i16* %sa, align 2 + // CHECK: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a, align 4 + // CHECK-NEXT: [[SACCUM_AS_I32:%[0-9a-z]+]] = ashr i32 [[ACCUM]], 8 + // CHECK-NEXT: [[SACCUM:%[0-9a-z]+]] = trunc i32 [[SACCUM_AS_I32]] to i16 + // CHECK-NEXT: store i16 [[SACCUM]], i16* %sa, align 2 sa = (short _Accum)a; - // DEFAULT: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a, align 4 - // DEFAULT-NEXT: [[SACCUM_AS_I32:%[0-9a-z]+]] = ashr i32 [[ACCUM]], 8 - // DEFAULT-NEXT: [[SACCUM:%[0-9a-z]+]] = trunc i32 [[SACCUM_AS_I32]] to i16 - // DEFAULT-NEXT: store i16 [[SACCUM]], i16* %sa, align 2 + // CHECK: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a, align 4 + // CHECK-NEXT: [[SACCUM_AS_I32:%[0-9a-z]+]] = ashr i32 [[ACCUM]], 8 + // CHECK-NEXT: [[SACCUM:%[0-9a-z]+]] = trunc i32 [[SACCUM_AS_I32]] to i16 + // CHECK-NEXT: store i16 [[SACCUM]], i16* %sa, align 2 } void TestFixedPointCastUp() { short _Accum sa = 2.5hk; _Accum a = sa; - // DEFAULT: [[SACCUM:%[0-9a-z]+]] = load i16, i16* %sa, align 2 - // DEFAULT-NEXT: [[SACCUM_BUFF:%[0-9a-z]+]] = sext i16 [[SACCUM]] to i32 - // DEFAULT-NEXT: [[ACCUM:%[0-9a-z]+]] = shl i32 [[SACCUM_BUFF]], 8 - // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a, align 4 + // CHECK: [[SACCUM:%[0-9a-z]+]] = load i16, i16* %sa, align 2 + // CHECK-NEXT: [[SACCUM_BUFF:%[0-9a-z]+]] = sext i16 [[SACCUM]] to i32 + // CHECK-NEXT: [[ACCUM:%[0-9a-z]+]] = shl i32 [[SACCUM_BUFF]], 8 + // CHECK-NEXT: store i32 [[ACCUM]], i32* %a, align 4 long _Accum la = a; - // DEFAULT: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a, align 4 - // DEFAULT-NEXT: [[ACCUM_BUFF:%[0-9a-z]+]] = sext i32 [[ACCUM]] to i64 - // DEFAULT-NEXT: [[LACCUM:%[0-9a-z]+]] = shl i64 [[ACCUM_BUFF]], 16 - // DEFAULT-NEXT: store i64 [[LACCUM]], i64* %la, align 8 + // CHECK: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a, align 4 + // CHECK-NEXT: [[ACCUM_BUFF:%[0-9a-z]+]] = sext i32 [[ACCUM]] to i64 + // CHECK-NEXT: [[LACCUM:%[0-9a-z]+]] = shl i64 [[ACCUM_BUFF]], 16 + // CHECK-NEXT: store i64 [[LACCUM]], i64* %la, align 8 a = (_Accum)sa; - // DEFAULT: [[SACCUM:%[0-9a-z]+]] = load i16, i16* %sa, align 2 - // DEFAULT-NEXT: [[SACCUM_BUFF:%[0-9a-z]+]] = sext i16 [[SACCUM]] to i32 - // DEFAULT-NEXT: [[ACCUM:%[0-9a-z]+]] = shl i32 [[SACCUM_BUFF]], 8 - // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a, align 4 + // CHECK: [[SACCUM:%[0-9a-z]+]] = load i16, i16* %sa, align 2 + // CHECK-NEXT: [[SACCUM_BUFF:%[0-9a-z]+]] = sext i16 [[SACCUM]] to i32 + // CHECK-NEXT: [[ACCUM:%[0-9a-z]+]] = shl i32 [[SACCUM_BUFF]], 8 + // CHECK-NEXT: store i32 [[ACCUM]], i32* %a, align 4 la = (long _Accum)a; - // DEFAULT: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a, align 4 - // DEFAULT-NEXT: [[ACCUM_BUFF:%[0-9a-z]+]] = sext i32 [[ACCUM]] to i64 - // DEFAULT-NEXT: [[LACCUM:%[0-9a-z]+]] = shl i64 [[ACCUM_BUFF]], 16 - // DEFAULT-NEXT: store i64 [[LACCUM]], i64* %la, align 8 + // CHECK: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a, align 4 + // CHECK-NEXT: [[ACCUM_BUFF:%[0-9a-z]+]] = sext i32 [[ACCUM]] to i64 + // CHECK-NEXT: [[LACCUM:%[0-9a-z]+]] = shl i64 [[ACCUM_BUFF]], 16 + // CHECK-NEXT: store i64 [[LACCUM]], i64* %la, align 8 } void TestFixedPointCastSignedness() { _Accum a = 2.5k; unsigned _Accum ua = a; - // DEFAULT: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a, align 4 - // DEFAULT-NEXT: [[UACCUM:%[0-9a-z]+]] = shl i32 [[ACCUM]], 1 - // DEFAULT-NEXT: store i32 [[UACCUM]], i32* %ua, align 4 - // SAME: TestFixedPointCastSignedness - // SAME: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a, align 4 - // SAME-NEXT: store i32 [[ACCUM]], i32* %ua, align 4 + // SIGNED: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a, align 4 + // SIGNED-NEXT: [[UACCUM:%[0-9a-z]+]] = shl i32 [[ACCUM]], 1 + // SIGNED-NEXT: store i32 [[UACCUM]], i32* %ua, align 4 + // UNSIGNED: TestFixedPointCastSignedness + // UNSIGNED: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a, align 4 + // UNSIGNED-NEXT: store i32 [[ACCUM]], i32* %ua, align 4 a = ua; - // DEFAULT: [[UACCUM:%[0-9a-z]+]] = load i32, i32* %ua, align 4 - // DEFAULT-NEXT: [[ACCUM:%[0-9a-z]+]] = lshr i32 [[UACCUM]], 1 - // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a, align 4 - // SAME: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %ua, align 4 - // SAME-NEXT: store i32 [[ACCUM]], i32* %a, align 4 + // SIGNED: [[UACCUM:%[0-9a-z]+]] = load i32, i32* %ua, align 4 + // SIGNED-NEXT: [[ACCUM:%[0-9a-z]+]] = lshr i32 [[UACCUM]], 1 + // SIGNED-NEXT: store i32 [[ACCUM]], i32* %a, align 4 + // UNSIGNED: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %ua, align 4 + // UNSIGNED-NEXT: store i32 [[ACCUM]], i32* %a, align 4 ua = (unsigned _Accum)a; - // DEFAULT: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a, align 4 - // DEFAULT-NEXT: [[UACCUM:%[0-9a-z]+]] = shl i32 [[ACCUM]], 1 - // DEFAULT-NEXT: store i32 [[UACCUM]], i32* %ua, align 4 + // SIGNED: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a, align 4 + // SIGNED-NEXT: [[UACCUM:%[0-9a-z]+]] = shl i32 [[ACCUM]], 1 + // SIGNED-NEXT: store i32 [[UACCUM]], i32* %ua, align 4 + // UNSIGNED: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a, align 4 + // UNSIGNED-NEXT: store i32 [[ACCUM]], i32* %ua, align 4 a = (_Accum)ua; - // DEFAULT: [[UACCUM:%[0-9a-z]+]] = load i32, i32* %ua, align 4 - // DEFAULT-NEXT: [[ACCUM:%[0-9a-z]+]] = lshr i32 [[UACCUM]], 1 - // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a, align 4 + // SIGNED: [[UACCUM:%[0-9a-z]+]] = load i32, i32* %ua, align 4 + // SIGNED-NEXT: [[ACCUM:%[0-9a-z]+]] = lshr i32 [[UACCUM]], 1 + // SIGNED-NEXT: store i32 [[ACCUM]], i32* %a, align 4 + // UNSIGNED: [[UACCUM:%[0-9a-z]+]] = load i32, i32* %ua, align 4 + // UNSIGNED-NEXT: store i32 [[UACCUM]], i32* %a, align 4 _Accum a2; unsigned long _Accum ula = a2; - // DEFAULT: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a2, align 4 - // DEFAULT-NEXT: [[ACCUM_EXT:%[0-9a-z]+]] = sext i32 [[ACCUM]] to i64 - // DEFAULT-NEXT: [[LACCUM:%[0-9a-z]+]] = shl i64 [[ACCUM_EXT]], 17 - // DEFAULT-NEXT: store i64 [[LACCUM]], i64* %ula, align 8 - // SAME: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a2, align 4 - // SAME-NEXT: [[ACCUM_EXT:%[0-9a-z]+]] = sext i32 [[ACCUM]] to i64 - // SAME-NEXT: [[LACCUM:%[0-9a-z]+]] = shl i64 [[ACCUM_EXT]], 16 - // SAME-NEXT: store i64 [[LACCUM]], i64* %ula, align 8 + // SIGNED: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a2, align 4 + // SIGNED-NEXT: [[ACCUM_EXT:%[0-9a-z]+]] = sext i32 [[ACCUM]] to i64 + // SIGNED-NEXT: [[LACCUM:%[0-9a-z]+]] = shl i64 [[ACCUM_EXT]], 17 + // SIGNED-NEXT: store i64 [[LACCUM]], i64* %ula, align 8 + // UNSIGNED: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a2, align 4 + // UNSIGNED-NEXT: [[ACCUM_EXT:%[0-9a-z]+]] = sext i32 [[ACCUM]] to i64 + // UNSIGNED-NEXT: [[LACCUM:%[0-9a-z]+]] = shl i64 [[ACCUM_EXT]], 16 + // UNSIGNED-NEXT: store i64 [[LACCUM]], i64* %ula, align 8 } void TestFixedPointCastSaturation() { @@ -153,112 +190,112 @@ // Casting down between types sat_sa = sat_a; - // DEFAULT: [[OLD_ACCUM:%[0-9a-z]+]] = load i32, i32* %sat_a, align 4 - // DEFAULT-NEXT: [[ACCUM:%[0-9a-z]+]] = ashr i32 [[OLD_ACCUM]], 8 - // DEFAULT-NEXT: [[USE_MAX:%[0-9a-z]+]] = icmp sgt i32 [[ACCUM]], 32767 - // DEFAULT-NEXT: [[RESULT:%[0-9a-z]+]] = select i1 [[USE_MAX]], i32 32767, i32 [[ACCUM]] - // DEFAULT-NEXT: [[USE_MIN:%[0-9a-z]+]] = icmp slt i32 [[RESULT]], -32768 - // DEFAULT-NEXT: [[RESULT2:%[0-9a-z]+]] = select i1 [[USE_MIN]], i32 -32768, i32 [[RESULT]] - // DEFAULT-NEXT: [[RESULT_TRUNC:%[0-9a-z]+]] = trunc i32 [[RESULT2]] to i16 - // DEFAULT-NEXT: store i16 [[RESULT_TRUNC]], i16* %sat_sa, align 2 + // CHECK: [[OLD_ACCUM:%[0-9a-z]+]] = load i32, i32* %sat_a, align 4 + // CHECK-NEXT: [[ACCUM:%[0-9a-z]+]] = ashr i32 [[OLD_ACCUM]], 8 + // CHECK-NEXT: [[USE_MAX:%[0-9a-z]+]] = icmp sgt i32 [[ACCUM]], 32767 + // CHECK-NEXT: [[RESULT:%[0-9a-z]+]] = select i1 [[USE_MAX]], i32 32767, i32 [[ACCUM]] + // CHECK-NEXT: [[USE_MIN:%[0-9a-z]+]] = icmp slt i32 [[RESULT]], -32768 + // CHECK-NEXT: [[RESULT2:%[0-9a-z]+]] = select i1 [[USE_MIN]], i32 -32768, i32 [[RESULT]] + // CHECK-NEXT: [[RESULT_TRUNC:%[0-9a-z]+]] = trunc i32 [[RESULT2]] to i16 + // CHECK-NEXT: store i16 [[RESULT_TRUNC]], i16* %sat_sa, align 2 // Accum to Fract, decreasing scale sat_sf = sat_a; - // DEFAULT: [[OLD_ACCUM:%[0-9a-z]+]] = load i32, i32* %sat_a, align 4 - // DEFAULT-NEXT: [[FRACT:%[0-9a-z]+]] = ashr i32 [[OLD_ACCUM]], 8 - // DEFAULT-NEXT: [[USE_MAX:%[0-9a-z]+]] = icmp sgt i32 [[FRACT]], 127 - // DEFAULT-NEXT: [[RESULT:%[0-9a-z]+]] = select i1 [[USE_MAX]], i32 127, i32 [[FRACT]] - // DEFAULT-NEXT: [[USE_MIN:%[0-9a-z]+]] = icmp slt i32 [[RESULT]], -128 - // DEFAULT-NEXT: [[RESULT2:%[0-9a-z]+]] = select i1 [[USE_MIN]], i32 -128, i32 [[RESULT]] - // DEFAULT-NEXT: [[RESULT_TRUNC:%[0-9a-z]+]] = trunc i32 [[RESULT2]] to i8 - // DEFAULT-NEXT: store i8 [[RESULT_TRUNC]], i8* %sat_sf, align 1 + // CHECK: [[OLD_ACCUM:%[0-9a-z]+]] = load i32, i32* %sat_a, align 4 + // CHECK-NEXT: [[FRACT:%[0-9a-z]+]] = ashr i32 [[OLD_ACCUM]], 8 + // CHECK-NEXT: [[USE_MAX:%[0-9a-z]+]] = icmp sgt i32 [[FRACT]], 127 + // CHECK-NEXT: [[RESULT:%[0-9a-z]+]] = select i1 [[USE_MAX]], i32 127, i32 [[FRACT]] + // CHECK-NEXT: [[USE_MIN:%[0-9a-z]+]] = icmp slt i32 [[RESULT]], -128 + // CHECK-NEXT: [[RESULT2:%[0-9a-z]+]] = select i1 [[USE_MIN]], i32 -128, i32 [[RESULT]] + // CHECK-NEXT: [[RESULT_TRUNC:%[0-9a-z]+]] = trunc i32 [[RESULT2]] to i8 + // CHECK-NEXT: store i8 [[RESULT_TRUNC]], i8* %sat_sf, align 1 // Accum to Fract, same scale sat_f = a; - // DEFAULT: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a, align 4 - // DEFAULT-NEXT: [[USE_MAX:%[0-9a-z]+]] = icmp sgt i32 [[ACCUM]], 32767 - // DEFAULT-NEXT: [[RESULT:%[0-9a-z]+]] = select i1 [[USE_MAX]], i32 32767, i32 [[ACCUM]] - // DEFAULT-NEXT: [[USE_MIN:%[0-9a-z]+]] = icmp slt i32 [[RESULT]], -32768 - // DEFAULT-NEXT: [[RESULT2:%[0-9a-z]+]] = select i1 [[USE_MIN]], i32 -32768, i32 [[RESULT]] - // DEFAULT-NEXT: [[RESULT_TRUNC:%[0-9a-z]+]] = trunc i32 [[RESULT2]] to i16 - // DEFAULT-NEXT: store i16 [[RESULT_TRUNC]], i16* %sat_f, align 2 + // CHECK: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a, align 4 + // CHECK-NEXT: [[USE_MAX:%[0-9a-z]+]] = icmp sgt i32 [[ACCUM]], 32767 + // CHECK-NEXT: [[RESULT:%[0-9a-z]+]] = select i1 [[USE_MAX]], i32 32767, i32 [[ACCUM]] + // CHECK-NEXT: [[USE_MIN:%[0-9a-z]+]] = icmp slt i32 [[RESULT]], -32768 + // CHECK-NEXT: [[RESULT2:%[0-9a-z]+]] = select i1 [[USE_MIN]], i32 -32768, i32 [[RESULT]] + // CHECK-NEXT: [[RESULT_TRUNC:%[0-9a-z]+]] = trunc i32 [[RESULT2]] to i16 + // CHECK-NEXT: store i16 [[RESULT_TRUNC]], i16* %sat_f, align 2 // Accum to Fract, increasing scale sat_lf = sat_a; - // DEFAULT: [[OLD_ACCUM:%[0-9a-z]+]] = load i32, i32* %sat_a, align 4 - // DEFAULT-NEXT: [[ACCUM:%[0-9a-z]+]] = sext i32 [[OLD_ACCUM]] to i48 - // DEFAULT-NEXT: [[FRACT:%[0-9a-z]+]] = shl i48 [[ACCUM]], 16 - // DEFAULT-NEXT: [[USE_MAX:%[0-9a-z]+]] = icmp sgt i48 [[FRACT]], 2147483647 - // DEFAULT-NEXT: [[RESULT:%[0-9a-z]+]] = select i1 [[USE_MAX]], i48 2147483647, i48 [[FRACT]] - // DEFAULT-NEXT: [[USE_MIN:%[0-9a-z]+]] = icmp slt i48 [[RESULT]], -2147483648 - // DEFAULT-NEXT: [[RESULT2:%[0-9a-z]+]] = select i1 [[USE_MIN]], i48 -2147483648, i48 [[RESULT]] - // DEFAULT-NEXT: [[RESULT_TRUNC:%[0-9a-z]+]] = trunc i48 [[RESULT2]] to i32 - // DEFAULT-NEXT: store i32 [[RESULT_TRUNC]], i32* %sat_lf, align 4 + // CHECK: [[OLD_ACCUM:%[0-9a-z]+]] = load i32, i32* %sat_a, align 4 + // CHECK-NEXT: [[RESIZE:%[0-9a-z]+]] = sext i32 [[OLD_ACCUM]] to i48 + // CHECK-NEXT: [[FRACT:%[0-9a-z]+]] = shl i48 [[RESIZE]], 16 + // CHECK-NEXT: [[USE_MAX:%[0-9a-z]+]] = icmp sgt i48 [[FRACT]], 2147483647 + // CHECK-NEXT: [[RESULT:%[0-9a-z]+]] = select i1 [[USE_MAX]], i48 2147483647, i48 [[FRACT]] + // CHECK-NEXT: [[USE_MIN:%[0-9a-z]+]] = icmp slt i48 [[RESULT]], -2147483648 + // CHECK-NEXT: [[RESULT2:%[0-9a-z]+]] = select i1 [[USE_MIN]], i48 -2147483648, i48 [[RESULT]] + // CHECK-NEXT: [[TRUNC:%[0-9a-z]+]] = trunc i48 [[RESULT2]] to i32 + // CHECK-NEXT: store i32 [[TRUNC]], i32* %sat_lf, align 4 // Signed to unsigned, decreasing scale _Sat _Accum sat_a2; sat_usa = sat_a2; - // DEFAULT: [[OLD_ACCUM:%[0-9a-z]+]] = load i32, i32* %sat_a2, align 4 - // DEFAULT-NEXT: [[ACCUM:%[0-9a-z]+]] = ashr i32 [[OLD_ACCUM]], 7 - // DEFAULT-NEXT: [[USE_MAX:%[0-9a-z]+]] = icmp sgt i32 [[ACCUM]], 65535 - // DEFAULT-NEXT: [[RESULT:%[0-9a-z]+]] = select i1 [[USE_MAX]], i32 65535, i32 [[ACCUM]] - // DEFAULT-NEXT: [[USE_MIN:%[0-9a-z]+]] = icmp slt i32 [[RESULT]], 0 - // DEFAULT-NEXT: [[RESULT2:%[0-9a-z]+]] = select i1 [[USE_MIN]], i32 0, i32 [[RESULT]] - // DEFAULT-NEXT: [[RESULT_TRUNC:%[0-9a-z]+]] = trunc i32 [[RESULT2]] to i16 - // DEFAULT-NEXT: store i16 [[RESULT_TRUNC]], i16* %sat_usa, align 2 - // SAME: [[OLD_ACCUM:%[0-9a-z]+]] = load i32, i32* %sat_a2, align 4 - // SAME-NEXT: [[ACCUM:%[0-9a-z]+]] = ashr i32 [[OLD_ACCUM]], 8 - // SAME-NEXT: [[USE_MAX:%[0-9a-z]+]] = icmp sgt i32 [[ACCUM]], 32767 - // SAME-NEXT: [[RESULT:%[0-9a-z]+]] = select i1 [[USE_MAX]], i32 32767, i32 [[ACCUM]] - // SAME-NEXT: [[USE_MIN:%[0-9a-z]+]] = icmp slt i32 [[RESULT]], 0 - // SAME-NEXT: [[RESULT2:%[0-9a-z]+]] = select i1 [[USE_MIN]], i32 0, i32 [[RESULT]] - // SAME-NEXT: [[RESULT_TRUNC:%[0-9a-z]+]] = trunc i32 [[RESULT2]] to i16 - // SAME-NEXT: store i16 [[RESULT_TRUNC]], i16* %sat_usa, align 2 + // SIGNED: [[OLD_ACCUM:%[0-9a-z]+]] = load i32, i32* %sat_a2, align 4 + // SIGNED-NEXT: [[ACCUM:%[0-9a-z]+]] = ashr i32 [[OLD_ACCUM]], 7 + // SIGNED-NEXT: [[USE_MAX:%[0-9a-z]+]] = icmp sgt i32 [[ACCUM]], 65535 + // SIGNED-NEXT: [[RESULT:%[0-9a-z]+]] = select i1 [[USE_MAX]], i32 65535, i32 [[ACCUM]] + // SIGNED-NEXT: [[USE_MIN:%[0-9a-z]+]] = icmp slt i32 [[RESULT]], 0 + // SIGNED-NEXT: [[RESULT2:%[0-9a-z]+]] = select i1 [[USE_MIN]], i32 0, i32 [[RESULT]] + // SIGNED-NEXT: [[RESULT_TRUNC:%[0-9a-z]+]] = trunc i32 [[RESULT2]] to i16 + // SIGNED-NEXT: store i16 [[RESULT_TRUNC]], i16* %sat_usa, align 2 + // UNSIGNED: [[OLD_ACCUM:%[0-9a-z]+]] = load i32, i32* %sat_a2, align 4 + // UNSIGNED-NEXT: [[ACCUM:%[0-9a-z]+]] = ashr i32 [[OLD_ACCUM]], 8 + // UNSIGNED-NEXT: [[USE_MAX:%[0-9a-z]+]] = icmp sgt i32 [[ACCUM]], 32767 + // UNSIGNED-NEXT: [[RESULT:%[0-9a-z]+]] = select i1 [[USE_MAX]], i32 32767, i32 [[ACCUM]] + // UNSIGNED-NEXT: [[USE_MIN:%[0-9a-z]+]] = icmp slt i32 [[RESULT]], 0 + // UNSIGNED-NEXT: [[RESULT2:%[0-9a-z]+]] = select i1 [[USE_MIN]], i32 0, i32 [[RESULT]] + // UNSIGNED-NEXT: [[RESULT_TRUNC:%[0-9a-z]+]] = trunc i32 [[RESULT2]] to i16 + // UNSIGNED-NEXT: store i16 [[RESULT_TRUNC]], i16* %sat_usa, align 2 // Signed to unsigned, increasing scale sat_ua = sat_a; - // DEFAULT: [[OLD_ACCUM:%[0-9a-z]+]] = load i32, i32* %sat_a, align 4 - // DEFAULT-NEXT: [[ACCUM_EXT:%[0-9a-z]+]] = sext i32 [[OLD_ACCUM]] to i33 - // DEFAULT-NEXT: [[ACCUM:%[0-9a-z]+]] = shl i33 [[ACCUM_EXT]], 1 - // DEFAULT-NEXT: [[USE_MIN:%[0-9a-z]+]] = icmp slt i33 [[ACCUM]], 0 - // DEFAULT-NEXT: [[RESULT2:%[0-9a-z]+]] = select i1 [[USE_MIN]], i33 0, i33 [[ACCUM]] - // DEFAULT-NEXT: [[RESULT_TRUNC:%[0-9a-z]+]] = trunc i33 [[RESULT2]] to i32 - // DEFAULT-NEXT: store i32 [[RESULT_TRUNC]], i32* %sat_ua, align 4 - // SAME: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %sat_a, align 4 - // SAME-NEXT: [[USE_MIN:%[0-9a-z]+]] = icmp slt i32 [[ACCUM]], 0 - // SAME-NEXT: [[RESULT:%[0-9a-z]+]] = select i1 [[USE_MIN]], i32 0, i32 [[ACCUM]] - // SAME-NEXT: store i32 [[RESULT]], i32* %sat_ua, align 4 + // SIGNED: [[OLD_ACCUM:%[0-9a-z]+]] = load i32, i32* %sat_a, align 4 + // SIGNED-NEXT: [[RESIZE:%[0-9a-z]+]] = sext i32 [[OLD_ACCUM]] to i33 + // SIGNED-NEXT: [[ACCUM:%[0-9a-z]+]] = shl i33 [[RESIZE]], 1 + // SIGNED-NEXT: [[USE_MIN:%[0-9a-z]+]] = icmp slt i33 [[ACCUM]], 0 + // SIGNED-NEXT: [[RESULT2:%[0-9a-z]+]] = select i1 [[USE_MIN]], i33 0, i33 [[ACCUM]] + // SIGNED-NEXT: [[TRUNC:%[0-9a-z]+]] = trunc i33 [[RESULT2]] to i32 + // SIGNED-NEXT: store i32 [[TRUNC]], i32* %sat_ua, align 4 + // UNSIGNED: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %sat_a, align 4 + // UNSIGNED-NEXT: [[USE_MIN:%[0-9a-z]+]] = icmp slt i32 [[ACCUM]], 0 + // UNSIGNED-NEXT: [[RESULT:%[0-9a-z]+]] = select i1 [[USE_MIN]], i32 0, i32 [[ACCUM]] + // UNSIGNED-NEXT: store i32 [[RESULT]], i32* %sat_ua, align 4 // Nothing when saturating to the same type and size sat_a = a; - // DEFAULT: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a, align 4 - // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %sat_a, align 4 + // CHECK: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a, align 4 + // CHECK-NEXT: store i32 [[ACCUM]], i32* %sat_a, align 4 // Nothing when assigning back a = sat_a; - // DEFAULT: [[SAT_ACCUM:%[0-9a-z]+]] = load i32, i32* %sat_a, align 4 - // DEFAULT-NEXT: store i32 [[SAT_ACCUM]], i32* %a, align 4 + // CHECK: [[SAT_ACCUM:%[0-9a-z]+]] = load i32, i32* %sat_a, align 4 + // CHECK-NEXT: store i32 [[SAT_ACCUM]], i32* %a, align 4 // No overflow when casting from fract to signed accum sat_a = sat_f; - // DEFAULT: [[FRACT:%[0-9a-z]+]] = load i16, i16* %sat_f, align 2 - // DEFAULT-NEXT: [[FRACT_EXT:%[0-9a-z]+]] = sext i16 [[FRACT]] to i32 - // DEFAULT-NEXT: store i32 [[FRACT_EXT]], i32* %sat_a, align 4 + // CHECK: [[FRACT:%[0-9a-z]+]] = load i16, i16* %sat_f, align 2 + // CHECK-NEXT: [[FRACT_EXT:%[0-9a-z]+]] = sext i16 [[FRACT]] to i32 + // CHECK-NEXT: store i32 [[FRACT_EXT]], i32* %sat_a, align 4 // 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 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 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 + // SIGNED: [[FRACT:%[0-9a-z]+]] = load i8, i8* %sat_sf, align 1 + // SIGNED-NEXT: [[FRACT_EXT:%[0-9a-z]+]] = sext i8 [[FRACT]] to i32 + // SIGNED-NEXT: [[ACCUM:%[0-9a-z]+]] = shl i32 [[FRACT_EXT]], 9 + // SIGNED-NEXT: [[IS_NEG:%[0-9a-z]+]] = icmp slt i32 [[ACCUM]], 0 + // SIGNED-NEXT: [[RESULT:%[0-9a-z]+]] = select i1 [[IS_NEG]], i32 0, i32 [[ACCUM]] + // SIGNED-NEXT: store i32 [[RESULT]], i32* %sat_ua, align 4 + // UNSIGNED: [[FRACT:%[0-9a-z]+]] = load i8, i8* %sat_sf, align 1 + // UNSIGNED-NEXT: [[FRACT_EXT:%[0-9a-z]+]] = sext i8 [[FRACT]] to i32 + // UNSIGNED-NEXT: [[ACCUM:%[0-9a-z]+]] = shl i32 [[FRACT_EXT]], 8 + // UNSIGNED-NEXT: [[IS_NEG:%[0-9a-z]+]] = icmp slt i32 [[ACCUM]], 0 + // UNSIGNED-NEXT: [[RESULT:%[0-9a-z]+]] = select i1 [[IS_NEG]], i32 0, i32 [[ACCUM]] + // UNSIGNED-NEXT: store i32 [[RESULT]], i32* %sat_ua, align 4 } void TestFixedPointCastBetFractAccum() { @@ -273,43 +310,176 @@ // To lower scale sf = a; - // DEFAULT: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a, align 4 - // DEFAULT-NEXT: [[FRACT:%[0-9a-z]+]] = ashr i32 [[ACCUM]], 8 - // DEFAULT-NEXT: [[FRACT_TRUNC:%[0-9a-z]+]] = trunc i32 [[FRACT]] to i8 - // DEFAULT-NEXT: store i8 [[FRACT_TRUNC]], i8* %sf, align 1 + // CHECK: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a, align 4 + // CHECK-NEXT: [[FRACT:%[0-9a-z]+]] = ashr i32 [[ACCUM]], 8 + // CHECK-NEXT: [[FRACT_TRUNC:%[0-9a-z]+]] = trunc i32 [[FRACT]] to i8 + // CHECK-NEXT: store i8 [[FRACT_TRUNC]], i8* %sf, align 1 // To higher scale a = sf; - // DEFAULT: [[FRACT:%[0-9a-z]+]] = load i8, i8* %sf, align 1 - // DEFAULT-NEXT: [[FRACT_EXT:%[0-9a-z]+]] = sext i8 [[FRACT]] to i32 - // DEFAULT-NEXT: [[ACCUM:%[0-9a-z]+]] = shl i32 [[FRACT_EXT]], 8 - // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a, align 4 + // CHECK: [[FRACT:%[0-9a-z]+]] = load i8, i8* %sf, align 1 + // CHECK-NEXT: [[FRACT_EXT:%[0-9a-z]+]] = sext i8 [[FRACT]] to i32 + // CHECK-NEXT: [[ACCUM:%[0-9a-z]+]] = shl i32 [[FRACT_EXT]], 8 + // CHECK-NEXT: store i32 [[ACCUM]], i32* %a, align 4 // To same scale f = a; - // DEFAULT: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a, align 4 - // DEFAULT-NEXT: [[FRACT:%[0-9a-z]+]] = trunc i32 [[ACCUM]] to i16 - // DEFAULT-NEXT: store i16 [[FRACT]], i16* %f, align 2 + // CHECK: [[ACCUM:%[0-9a-z]+]] = load i32, i32* %a, align 4 + // CHECK-NEXT: [[FRACT:%[0-9a-z]+]] = trunc i32 [[ACCUM]] to i16 + // CHECK-NEXT: store i16 [[FRACT]], i16* %f, align 2 a = f; - // DEFAULT: [[FRACT:%[0-9a-z]+]] = load i16, i16* %f, align 2 - // DEFAULT-NEXT: [[ACCUM:%[0-9a-z]+]] = sext i16 [[FRACT]] to i32 - // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a, align 4 + // CHECK: [[FRACT:%[0-9a-z]+]] = load i16, i16* %f, align 2 + // CHECK-NEXT: [[ACCUM:%[0-9a-z]+]] = sext i16 [[FRACT]] to i32 + // CHECK-NEXT: store i32 [[ACCUM]], i32* %a, align 4 // To unsigned ua = uf; - // DEFAULT: [[FRACT:%[0-9a-z]+]] = load i16, i16* %uf, align 2 - // DEFAULT-NEXT: [[ACCUM:%[0-9a-z]+]] = zext i16 [[FRACT]] to i32 - // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %ua, align 4 - // SAME: [[FRACT:%[0-9a-z]+]] = load i16, i16* %uf, align 2 - // SAME-NEXT: [[ACCUM:%[0-9a-z]+]] = zext i16 [[FRACT]] to i32 - // SAME-NEXT: store i32 [[ACCUM]], i32* %ua, align 4 + // CHECK: [[FRACT:%[0-9a-z]+]] = load i16, i16* %uf, align 2 + // CHECK-NEXT: [[ACCUM:%[0-9a-z]+]] = zext i16 [[FRACT]] to i32 + // CHECK-NEXT: store i32 [[ACCUM]], i32* %ua, align 4 uf = ua; - // DEFAULT: [[FRACT:%[0-9a-z]+]] = load i32, i32* %ua, align 4 - // DEFAULT-NEXT: [[ACCUM:%[0-9a-z]+]] = trunc i32 [[FRACT]] to i16 - // DEFAULT-NEXT: store i16 [[ACCUM]], i16* %uf, align 2 - // SAME: [[FRACT:%[0-9a-z]+]] = load i32, i32* %ua, align 4 - // SAME-NEXT: [[ACCUM:%[0-9a-z]+]] = trunc i32 [[FRACT]] to i16 - // SAME-NEXT: store i16 [[ACCUM]], i16* %uf, align 2 + // CHECK: [[FRACT:%[0-9a-z]+]] = load i32, i32* %ua, align 4 + // CHECK-NEXT: [[ACCUM:%[0-9a-z]+]] = trunc i32 [[FRACT]] to i16 + // CHECK-NEXT: store i16 [[ACCUM]], i16* %uf, align 2 +} + +void TestFixedPointToInt() { + int i; + short _Accum sa; + unsigned short _Accum usa; + + // Will need to check for negative values + i = sa; + // CHECK: [[FX:%[0-9]+]] = load i16, i16* %sa, align 2 + // CHECK-NEXT: [[NEG:%[0-9]+]] = icmp slt i16 [[FX]], 0 + // CHECK-NEXT: [[ROUNDED:%[0-9]+]] = add i16 [[FX]], 127 + // CHECK-NEXT: [[VAL:%[0-9]+]] = select i1 [[NEG]], i16 [[ROUNDED]], i16 [[FX]] + // CHECK-NEXT: [[RES:%[a-z0-9]+]] = ashr i16 [[VAL]], 7 + // CHECK-NEXT: [[RES2:%[a-z0-9]+]] = sext i16 [[RES]] to i32 + // CHECK-NEXT: store i32 [[RES2]], i32* %i, align 4 + + // No check needed for unsigned fixed points. Can just right shift. + i = usa; + // SIGNED: [[FX:%[0-9]+]] = load i16, i16* %usa, align 2 + // SIGNED-NEXT: [[INT:%[a-z0-9]+]] = lshr i16 [[FX]], 8 + // SIGNED-NEXT: [[RES:%[a-z0-9]+]] = zext i16 [[INT]] to i32 + // SIGNED-NEXT: store i32 [[RES]], i32* %i, align 4 + // UNSIGNED: [[FX:%[0-9]+]] = load i16, i16* %usa, align 2 + // UNSIGNED-NEXT: [[INT:%[a-z0-9]+]] = lshr i16 [[FX]], 7 + // UNSIGNED-NEXT: [[RES:%[a-z0-9]+]] = zext i16 [[INT]] to i32 + // UNSIGNED-NEXT: store i32 [[RES]], i32* %i, align 4 +} + +void TestIntToFixedPoint() { + short s; + int i, i2; + unsigned int ui; + short _Accum sa; + long _Accum la; + unsigned short _Accum usa; + _Sat short _Accum sat_sa; + _Sat unsigned short _Accum sat_usa; + + sa = i; + // CHECK: [[I:%[0-9]+]] = load i32, i32* %i, align 4 + // CHECK-NEXT: [[I_EXT:%[a-z0-9]+]] = trunc i32 [[I]] to i16 + // CHECK-NEXT: [[FX:%[a-z0-9]+]] = shl i16 [[I_EXT]], 7 + // CHECK-NEXT: store i16 [[FX]], i16* %sa, align 2 + + sa = ui; + // CHECK: [[I:%[0-9]+]] = load i32, i32* %ui, align 4 + // CHECK-NEXT: [[I_EXT:%[a-z0-9]+]] = trunc i32 [[I]] to i16 + // CHECK-NEXT: [[FX:%[a-z0-9]+]] = shl i16 [[I_EXT]], 7 + // CHECK-NEXT: store i16 [[FX]], i16* %sa, align 2 + + usa = i2; + // SIGNED: [[I:%[0-9]+]] = load i32, i32* %i2, align 4 + // SIGNED-NEXT: [[I_EXT:%[a-z0-9]+]] = trunc i32 [[I]] to i16 + // SIGNED-NEXT: [[FX:%[a-z0-9]+]] = shl i16 [[I_EXT]], 8 + // SIGNED-NEXT: store i16 [[FX]], i16* %usa, align 2 + // UNSIGNED: [[I:%[0-9]+]] = load i32, i32* %i2, align 4 + // UNSIGNED-NEXT: [[I_EXT:%[a-z0-9]+]] = trunc i32 [[I]] to i16 + // UNSIGNED-NEXT: [[FX:%[a-z0-9]+]] = shl i16 [[I_EXT]], 7 + // UNSIGNED-NEXT: store i16 [[FX]], i16* %usa, align 2 + + usa = ui; + // SIGNED: [[I:%[0-9]+]] = load i32, i32* %ui, align 4 + // SIGNED-NEXT: [[I_EXT:%[a-z0-9]+]] = trunc i32 [[I]] to i16 + // SIGNED-NEXT: [[FX:%[a-z0-9]+]] = shl i16 [[I_EXT]], 8 + // SIGNED-NEXT: store i16 [[FX]], i16* %usa, align 2 + // UNSIGNED: [[I:%[0-9]+]] = load i32, i32* %ui, align 4 + // UNSIGNED-NEXT: [[I_EXT:%[a-z0-9]+]] = trunc i32 [[I]] to i16 + // UNSIGNED-NEXT: [[FX:%[a-z0-9]+]] = shl i16 [[I_EXT]], 7 + // UNSIGNED-NEXT: store i16 [[FX]], i16* %usa, align 2 + + la = s; + // CHECK: [[I:%[0-9]+]] = load i16, i16* %s, align 2 + // CHECK-NEXT: [[I_EXT:%[a-z0-9]+]] = sext i16 [[I]] to i64 + // CHECK-NEXT: [[FX:%[a-z0-9]+]] = shl i64 [[I_EXT]], 31 + // CHECK-NEXT: store i64 [[FX]], i64* %la, align 8 +} + +void TestIntToSatFixedPoint() { + int i, i2; + unsigned int ui; + _Sat short _Accum sat_sa; + _Sat unsigned short _Accum sat_usa; + + sat_sa = i; + // CHECK: [[I:%[0-9]+]] = load i32, i32* %i, align 4 + // CHECK-NEXT: [[I_EXT:%[a-z0-9]+]] = sext i32 [[I]] to i39 + // CHECK-NEXT: [[FX:%[a-z0-9]+]] = shl i39 [[I_EXT]], 7 + // CHECK-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i39 [[FX]], 32767 + // CHECK-NEXT: [[SATMAX:%[a-z0-9]+]] = select i1 [[USE_MAX]], i39 32767, i39 [[FX]] + // CHECK-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i39 [[SATMAX]], -32768 + // CHECK-NEXT: [[SATMIN:%[a-z0-9]+]] = select i1 [[USE_MIN]], i39 -32768, i39 [[SATMAX]] + // CHECK-NEXT: [[RES:%[a-z0-9]+]] = trunc i39 [[SATMIN]] to i16 + // CHECK-NEXT: store i16 [[RES]], i16* %sat_sa, align 2 + + sat_sa = ui; + // CHECK: [[I:%[0-9]+]] = load i32, i32* %ui, align 4 + // CHECK-NEXT: [[I_EXT:%[a-z0-9]+]] = zext i32 [[I]] to i39 + // CHECK-NEXT: [[FX:%[a-z0-9]+]] = shl i39 [[I_EXT]], 7 + // CHECK-NEXT: [[USE_MAX:%[0-9]+]] = icmp ugt i39 [[FX]], 32767 + // CHECK-NEXT: [[SATMAX:%[a-z0-9]+]] = select i1 [[USE_MAX]], i39 32767, i39 [[FX]] + // CHECK-NEXT: [[RES:%[a-z0-9]+]] = trunc i39 [[SATMAX]] to i16 + // CHECK-NEXT: store i16 [[RES]], i16* %sat_sa, align 2 + + sat_usa = i2; + // SIGNED: [[I:%[0-9]+]] = load i32, i32* %i2, align 4 + // SIGNED-NEXT: [[I_EXT:%[a-z0-9]+]] = sext i32 [[I]] to i40 + // SIGNED-NEXT: [[FX:%[a-z0-9]+]] = shl i40 [[I_EXT]], 8 + // SIGNED-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i40 [[FX]], 65535 + // SIGNED-NEXT: [[SATMAX:%[a-z0-9]+]] = select i1 [[USE_MAX]], i40 65535, i40 [[FX]] + // SIGNED-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i40 [[SATMAX]], 0 + // SIGNED-NEXT: [[SATMIN:%[a-z0-9]+]] = select i1 [[USE_MIN]], i40 0, i40 [[SATMAX]] + // SIGNED-NEXT: [[RES:%[a-z0-9]+]] = trunc i40 [[SATMIN]] to i16 + // SIGNED-NEXT: store i16 [[RES]], i16* %sat_usa, align 2 + // UNSIGNED: [[I:%[0-9]+]] = load i32, i32* %i2, align 4 + // UNSIGNED-NEXT: [[I_EXT:%[a-z0-9]+]] = sext i32 [[I]] to i39 + // UNSIGNED-NEXT: [[FX:%[a-z0-9]+]] = shl i39 [[I_EXT]], 7 + // UNSIGNED-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i39 [[FX]], 32767 + // UNSIGNED-NEXT: [[SATMAX:%[a-z0-9]+]] = select i1 [[USE_MAX]], i39 32767, i39 [[FX]] + // UNSIGNED-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i39 [[SATMAX]], 0 + // UNSIGNED-NEXT: [[SATMIN:%[a-z0-9]+]] = select i1 [[USE_MIN]], i39 0, i39 [[SATMAX]] + // UNSIGNED-NEXT: [[RES:%[a-z0-9]+]] = trunc i39 [[SATMIN]] to i16 + // UNSIGNED-NEXT: store i16 [[RES]], i16* %sat_usa, align 2 + + sat_usa = ui; + // SIGNED: [[I:%[0-9]+]] = load i32, i32* %ui, align 4 + // SIGNED-NEXT: [[I_EXT:%[a-z0-9]+]] = zext i32 [[I]] to i40 + // SIGNED-NEXT: [[FX:%[a-z0-9]+]] = shl i40 [[I_EXT]], 8 + // SIGNED-NEXT: [[USE_MAX:%[0-9]+]] = icmp ugt i40 [[FX]], 65535 + // SIGNED-NEXT: [[SATMAX:%[a-z0-9]+]] = select i1 [[USE_MAX]], i40 65535, i40 [[FX]] + // SIGNED-NEXT: [[RES:%[a-z0-9]+]] = trunc i40 [[SATMAX]] to i16 + // SIGNED-NEXT: store i16 [[RES]], i16* %sat_usa, align 2 + // UNSIGNED: [[I:%[0-9]+]] = load i32, i32* %ui, align 4 + // UNSIGNED-NEXT: [[I_EXT:%[a-z0-9]+]] = zext i32 [[I]] to i39 + // UNSIGNED-NEXT: [[FX:%[a-z0-9]+]] = shl i39 [[I_EXT]], 7 + // UNSIGNED-NEXT: [[USE_MAX:%[0-9]+]] = icmp ugt i39 [[FX]], 32767 + // UNSIGNED-NEXT: [[SATMAX:%[a-z0-9]+]] = select i1 [[USE_MAX]], i39 32767, i39 [[FX]] + // UNSIGNED-NEXT: [[RES:%[a-z0-9]+]] = trunc i39 [[SATMAX]] to i16 + // UNSIGNED-NEXT: store i16 [[RES]], i16* %sat_usa, align 2 } Index: test/Frontend/fixed_point_errors.c =================================================================== --- test/Frontend/fixed_point_errors.c +++ test/Frontend/fixed_point_errors.c @@ -233,8 +233,20 @@ // expected-warning@-1{{type specifier missing, defaults to 'int'}} } +// Ok conversions +int i_const = -2.5hk; +_Sat short _Accum sat_sa_const2 = 256.0k; +_Sat unsigned short _Accum sat_usa_const = -1.0hk; +short _Accum sa_const3 = 2; +short _Accum sa_const4 = -2; + // Overflow short _Accum sa_const = 256.0k; // expected-warning{{implicit conversion from 256.0 cannot fit within the range of values for 'short _Accum'}} short _Fract sf_const = 1.0hk; // expected-warning{{implicit conversion from 1.0 cannot fit within the range of values for 'short _Fract'}} unsigned _Accum ua_const = -1.0k; // expected-warning{{implicit conversion from -1.0 cannot fit within the range of values for 'unsigned _Accum'}} short _Accum sa_const2 = 128.0k + 128.0k; // expected-warning{{implicit conversion from 256.0 cannot fit within the range of values for 'short _Accum'}} +short s_const = 65536.0lk; // expected-warning{{implicit conversion from 65536.0 cannot fit within the range of values for 'short'}} +unsigned u_const = -2.5hk; // expected-warning{{implicit conversion from -2.5 cannot fit within the range of values for 'unsigned int'}} +char c_const = 256.0uk; // expected-warning{{implicit conversion from 256.0 cannot fit within the range of values for 'char'}} +short _Accum sa_const5 = 256; // expected-warning{{implicit conversion from 256 cannot fit within the range of values for 'short _Accum'}} +unsigned short _Accum usa_const2 = -2; // expected-warning{{implicit conversion from -2 cannot fit within the range of values for 'unsigned short _Accum'}} Index: test/Frontend/fixed_point_unknown_conversions.c =================================================================== --- test/Frontend/fixed_point_unknown_conversions.c +++ test/Frontend/fixed_point_unknown_conversions.c @@ -22,28 +22,19 @@ _Fract fract = accum; // ok _Accum *accum_ptr; - accum = b; // expected-error{{conversion between fixed point and '_Bool' is not yet supported}} - accum = i; // expected-error{{conversion between fixed point and 'int' is not yet supported}} - accum = i; // expected-error{{conversion between fixed point and 'int' is not yet supported}} accum = f; // expected-error{{conversion between fixed point and 'float' is not yet supported}} accum = d; // expected-error{{conversion between fixed point and 'double' is not yet supported}} accum = dc; // expected-error{{conversion between fixed point and '_Complex double' is not yet supported}} accum = ic; // expected-error{{conversion between fixed point and '_Complex int' is not yet supported}} accum = s; // expected-error{{assigning to '_Accum' from incompatible type 'struct S'}} - accum = e; // expected-error{{conversion between fixed point and 'enum E' is not yet supported}} accum = ptr; // expected-error{{assigning to '_Accum' from incompatible type 'int *'}} accum_ptr = ptr; // expected-warning{{incompatible pointer types assigning to '_Accum *' from 'int *'}} - accum = i2; // expected-error{{conversion between fixed point and 'int_t' (aka 'int') is not yet supported}} - c = accum; // expected-error{{conversion between fixed point and 'char' is not yet supported}} - i = accum; // expected-error{{conversion between fixed point and 'int' is not yet supported}} f = accum; // expected-error{{conversion between fixed point and 'float' is not yet supported}} d = accum; // expected-error{{conversion between fixed point and 'double' is not yet supported}} dc = accum; // expected-error{{conversion between fixed point and '_Complex double' is not yet supported}} ic = accum; // expected-error{{conversion between fixed point and '_Complex int' is not yet supported}} s = accum; // expected-error{{assigning to 'struct S' from incompatible type '_Accum'}} - e = accum; // expected-error{{conversion between fixed point and 'enum E' is not yet supported}} ptr = accum; // expected-error{{assigning to 'int *' from incompatible type '_Accum'}} ptr = accum_ptr; // expected-warning{{incompatible pointer types assigning to 'int *' from '_Accum *'}} - i2 = accum; // expected-error{{conversion between fixed point and 'int' is not yet supported}} }