Index: clang/include/clang/AST/OperationKinds.def =================================================================== --- clang/include/clang/AST/OperationKinds.def +++ clang/include/clang/AST/OperationKinds.def @@ -201,6 +201,14 @@ /// (_Accum) 0.5r CAST_OPERATION(FixedPointCast) +/// CK_FixedPointToIntegral - Fixed point to an 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: clang/include/clang/Basic/FixedPoint.h =================================================================== --- clang/include/clang/Basic/FixedPoint.h +++ clang/include/clang/Basic/FixedPoint.h @@ -142,6 +142,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()); Index: clang/lib/AST/Expr.cpp =================================================================== --- clang/lib/AST/Expr.cpp +++ clang/lib/AST/Expr.cpp @@ -1717,6 +1717,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: clang/lib/AST/ExprConstant.cpp =================================================================== --- clang/lib/AST/ExprConstant.cpp +++ clang/lib/AST/ExprConstant.cpp @@ -9766,6 +9766,7 @@ case CK_AddressSpaceConversion: case CK_IntToOCLSampler: case CK_FixedPointCast: + case CK_IntegralToFixedPoint: llvm_unreachable("invalid cast kind for integral value"); case CK_BitCast: @@ -9800,6 +9801,43 @@ return Success(IntResult, E); } + case CK_FixedPointToIntegral: { + APFixedPoint Src(Info.Ctx.getFixedPointSemantics(SrcType)); + if (!EvaluateFixedPoint(SubExpr, Src, Info)) + return false; + APSInt Result = Src.getIntPart(); + unsigned DstWidth = Info.Ctx.getIntWidth(DestType); + unsigned SrcWidth = Result.getBitWidth(); + bool DstSigned = DestType->isSignedIntegerOrEnumerationType(); + + llvm::APSInt DstMin = llvm::APSInt::getMinValue(DstWidth, !DstSigned); + llvm::APSInt DstMax = llvm::APSInt::getMaxValue(DstWidth, !DstSigned); + + if (SrcWidth < DstWidth) { + Result = Result.extend(DstWidth); + } else if (SrcWidth > DstWidth) { + DstMin = DstMin.extend(SrcWidth); + DstMax = DstMax.extend(SrcWidth); + } + + bool Overflow; + if (Result.isSigned() && !DstSigned) { + Overflow = Result < 0 || Result.ugt(DstMax); + } else if (Result.isUnsigned() && DstSigned) { + Overflow = Result.ugt(DstMax); + } else { + Overflow = Result < DstMin || Result > DstMax; + } + + if (Overflow && !HandleOverflow(Info, E, Result, DestType)) + return false; + + Result.setIsSigned(DstSigned); + Result = Result.extOrTrunc(DstWidth); + + return Success(Result, E); + } + case CK_FixedPointToBoolean: { // Unsigned padding does not affect this. APValue Val; @@ -9960,6 +9998,24 @@ return false; return Success(Result, E); } + case CK_IntegralToFixedPoint: { + APSInt Src; + if (!EvaluateInteger(SubExpr, Src, Info)) + return false; + + unsigned SrcWidth = Info.Ctx.getIntWidth(SubExpr->getType()); + FixedPointSemantics IntFXSema = + FixedPointSemantics::GetIntegerSemantics(SrcWidth, Src.isSigned()); + FixedPointSemantics DstFXSema = Info.Ctx.getFixedPointSemantics(DestType); + + bool Overflowed; + APFixedPoint IntResult = + APFixedPoint(Src, IntFXSema).convert(DstFXSema, &Overflowed); + if (Overflowed && !HandleOverflow(Info, E, IntResult, DestType)) + return false; + + return Success(IntResult, E); + } case CK_NoOp: case CK_LValueToRValue: return ExprEvaluatorBaseTy::VisitCastExpr(E); @@ -10361,6 +10417,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: clang/lib/CodeGen/CGExpr.cpp =================================================================== --- clang/lib/CodeGen/CGExpr.cpp +++ clang/lib/CodeGen/CGExpr.cpp @@ -4175,6 +4175,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: clang/lib/CodeGen/CGExprAgg.cpp =================================================================== --- clang/lib/CodeGen/CGExprAgg.cpp +++ clang/lib/CodeGen/CGExprAgg.cpp @@ -856,6 +856,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: clang/lib/CodeGen/CGExprComplex.cpp =================================================================== --- clang/lib/CodeGen/CGExprComplex.cpp +++ clang/lib/CodeGen/CGExprComplex.cpp @@ -513,6 +513,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: clang/lib/CodeGen/CGExprConstant.cpp =================================================================== --- clang/lib/CodeGen/CGExprConstant.cpp +++ clang/lib/CodeGen/CGExprConstant.cpp @@ -875,6 +875,8 @@ case CK_FloatingCast: case CK_FixedPointCast: case CK_FixedPointToBoolean: + case CK_FixedPointToIntegral: + case CK_IntegralToFixedPoint: case CK_ZeroToOCLOpaqueType: return nullptr; } Index: clang/lib/CodeGen/CGExprScalar.cpp =================================================================== --- clang/lib/CodeGen/CGExprScalar.cpp +++ clang/lib/CodeGen/CGExprScalar.cpp @@ -356,11 +356,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. @@ -1218,17 +1221,26 @@ // 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"); + } else if (DstType->isFixedPointType() || DstType->isIntegerType()) { + return EmitFixedPointConversion(Src, SrcType, DstType, Loc); } llvm_unreachable( - "Unhandled scalar conversion involving a fixed point type."); + "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 to a fixed point type from another type."); } QualType NoncanonicalSrcType = SrcType; @@ -1436,19 +1448,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; @@ -1465,13 +1475,36 @@ Value *Result = Src; unsigned ResultWidth = SrcWidth; - if (!DstFPSema.isSaturated()) { - // Downscale. - if (DstScale < SrcScale) + // Downscale. + if (DstScale < SrcScale) { + // When converting to integers, we round towards zero. For negative numbers, + // right shifting rounds towards negative infinity. We can round towards + // zero by instead making the value positive, right shifting, then negating + // the result. + if (DstIsInteger && SrcIsSigned) { + Value *Zero = llvm::Constant::getNullValue(Result->getType()); + Value *MinVal = ConstantInt::get(CGF.getLLVMContext(), + APInt::getSignedMinValue(ResultWidth)); + Value *IsNegative = Builder.CreateICmpSLT(Result, Zero); + Value *IsNotMinVal = Builder.CreateICmpNE(Result, MinVal); + Value *IsNegFractional = Builder.CreateAnd(IsNegative, IsNotMinVal); + + Value *NegResult = Builder.CreateNeg(Result); + NegResult = + Builder.CreateAShr(NegResult, SrcScale - DstScale, "downscale"); + NegResult = Builder.CreateNeg(NegResult); + + Result = Builder.CreateSelect( + IsNegFractional, NegResult, + Builder.CreateAShr(Result, SrcScale - DstScale, "downscale")); + } else { 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"); @@ -1486,10 +1519,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. @@ -2221,6 +2250,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: clang/lib/Edit/RewriteObjCFoundationAPI.cpp =================================================================== --- clang/lib/Edit/RewriteObjCFoundationAPI.cpp +++ clang/lib/Edit/RewriteObjCFoundationAPI.cpp @@ -1087,6 +1087,8 @@ case CK_FixedPointCast: case CK_FixedPointToBoolean: + case CK_FixedPointToIntegral: + case CK_IntegralToFixedPoint: llvm_unreachable("Fixed point types are disabled for Objective-C"); } } Index: clang/lib/Sema/SemaChecking.cpp =================================================================== --- clang/lib/Sema/SemaChecking.cpp +++ clang/lib/Sema/SemaChecking.cpp @@ -11013,10 +11013,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)) { @@ -11032,6 +11031,69 @@ return; } } + } else if (Target->isIntegerType()) { + Expr::EvalResult Result; + if (E->EvaluateAsFixedPoint(Result, S.Context, + Expr::SE_AllowSideEffects)) { + APFixedPoint FXResult = Result.Val.getFixedPoint(); + llvm::APSInt Value = FXResult.getIntPart(); + unsigned DstWidth = S.Context.getIntWidth(T); + unsigned SrcWidth = Value.getBitWidth(); + bool DstUnsigned = Target->isUnsignedIntegerType(); + + llvm::APSInt DstMin = llvm::APSInt::getMinValue(DstWidth, DstUnsigned); + llvm::APSInt DstMax = llvm::APSInt::getMaxValue(DstWidth, DstUnsigned); + + if (SrcWidth < DstWidth) { + Value = Value.extend(DstWidth); + } else if (SrcWidth > DstWidth) { + DstMin = DstMin.extend(SrcWidth); + DstMax = DstMax.extend(SrcWidth); + } + + bool Overflow; + if (Value.isSigned() && DstUnsigned) { + Overflow = Value < 0 || Value.ugt(DstMax); + } else if (Value.isUnsigned() && !DstUnsigned) { + Overflow = Value.ugt(DstMax); + } else { + Overflow = Value < DstMin || Value > DstMax; + } + + if (Overflow) { + 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(); + + unsigned SrcWidth = Value.getBitWidth(); + FixedPointSemantics IntFXSema = + FixedPointSemantics::GetIntegerSemantics(SrcWidth, + Value.isSigned()); + FixedPointSemantics DstFXSema = S.Context.getFixedPointSemantics(T); + + bool Overflowed; + APFixedPoint IntResult = + APFixedPoint(Value, IntFXSema).convert(DstFXSema, &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: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -6120,6 +6120,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: @@ -6164,10 +6165,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: clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -416,7 +416,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: clang/test/Frontend/fixed_point_conversions.c =================================================================== --- clang/test/Frontend/fixed_point_conversions.c +++ clang/test/Frontend/fixed_point_conversions.c @@ -16,14 +16,42 @@ // SAME-DAG: @usa_const = {{.*}}global i16 320, align 2 // SAME-DAG: @ua_const = {{.*}}global i32 81920, align 4 +// FixedPoint to integer +int i_const = -128.0hk; // DEFAULT-DAG: @i_const = {{.*}}global i32 -128, align 4 +int i_const2 = 128.0hk; // DEFAULT-DAG: @i_const2 = {{.*}}global i32 128, align 4 +int i_const3 = -128.0k; // DEFAULT-DAG: @i_const3 = {{.*}}global i32 -128, align 4 +int i_const4 = 128.0k; // DEFAULT-DAG: @i_const4 = {{.*}}global i32 128, align 4 +short s_const = -128.0k; // DEFAULT-DAG: @s_const = {{.*}}global i16 -128, align 2 +short s_const2 = 128.0k; // DEFAULT-DAG: @s_const2 = {{.*}}global i16 128, align 2 + +// Integer to fixed point +short _Accum sa_const5 = 2; // DEFAULT-DAG: @sa_const5 = {{.*}}global i16 256, align 2 +short _Accum sa_const6 = -2; // DEFAULT-DAG: @sa_const6 = {{.*}}global i16 -256, align 2 +short _Accum sa_const7 = -256; // DEFAULT-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 +int i_const5 = 128.0uhk; +unsigned int ui_const = 128.0hk; +// DEFAULT-DAG: @i_const5 = {{.*}}global i32 128, align 4 +// DEFAULT-DAG: @ui_const = {{.*}}global i32 128, align 4 +// SAME-DAG: @i_const5 = {{.*}}global i32 128, align 4 +// SAME-DAG: @ui_const = {{.*}}global i32 128, align 4 + +short _Accum sa_const9 = 2u; // DEFAULT-DAG: @sa_const9 = {{.*}}global i16 256, align 2 +unsigned short _Accum usa_const3 = 2; +// DEFAULT-DAG: @usa_const3 = {{.*}}global i16 512, align 2 +// SAME-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 @@ -35,6 +63,15 @@ // DEFAULT-DAG: @sat_usa_const2 = {{.*}}global i16 -1, align 2 // SAME-DAG: @sat_usa_const2 = {{.*}}global i16 32767, align 2 +_Sat short _Accum sat_sa_const3 = 256; // DEFAULT-DAG: @sat_sa_const3 = {{.*}}global i16 32767, align 2 +_Sat short _Accum sat_sa_const4 = -257; // DEFAULT-DAG: @sat_sa_const4 = {{.*}}global i16 -32768, align 2 +_Sat unsigned short _Accum sat_usa_const3 = -1; +// DEFAULT-DAG: @sat_usa_const3 = {{.*}}global i16 0, align 2 +// SAME-DAG: @sat_usa_const3 = {{.*}}global i16 0, align 2 +_Sat unsigned short _Accum sat_usa_const4 = 256; +// DEFAULT-DAG: @sat_usa_const4 = {{.*}}global i16 -1, align 2 +// SAME-DAG: @sat_usa_const4 = {{.*}}global i16 32767, align 2 + void TestFixedPointCastSameType() { _Accum a = 2.5k; _Accum a2 = a; @@ -313,3 +350,138 @@ // SAME-NEXT: [[ACCUM:%[0-9a-z]+]] = trunc i32 [[FRACT]] to i16 // SAME-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; + // DEFAULT: [[FX:%[0-9]+]] = load i16, i16* %sa, align 2 + // DEFAULT-NEXT: [[NEG:%[0-9]+]] = icmp slt i16 [[FX]], 0 + // DEFAULT-NEXT: [[NOTMIN:%[0-9]+]] = icmp ne i16 [[FX]], -32768 + // DEFAULT-NEXT: [[NEGFRACT:%[0-9]+]] = and i1 [[NEG]], [[NOTMIN]] + // DEFAULT-NEXT: [[NEGFX:%[0-9]+]] = sub i16 0, [[FX]] + // DEFAULT-NEXT: [[INT:%[a-z0-9]+]] = ashr i16 [[NEGFX]], 7 + // DEFAULT-NEXT: [[NEGINT:%[0-9]+]] = sub i16 0, [[INT]] + // DEFAULT-NEXT: [[INT:%[a-z0-9]+]] = ashr i16 [[FX]], 7 + // DEFAULT-NEXT: [[RES:%[0-9]+]] = select i1 [[NEGFRACT]], i16 [[NEGINT]], i16 [[INT]] + // DEFAULT-NEXT: [[RES2:%[a-z0-9]+]] = sext i16 [[RES]] to i32 + // DEFAULT-NEXT: store i32 [[RES2]], i32* %i, align 4 + + // No check needed for unsigned fixed points. Can just right shift. + i = usa; + // DEFAULT: [[FX:%[0-9]+]] = load i16, i16* %usa, align 2 + // DEFAULT-NEXT: [[INT:%[a-z0-9]+]] = lshr i16 [[FX]], 8 + // DEFAULT-NEXT: [[RES:%[a-z0-9]+]] = zext i16 [[INT]] to i32 + // DEFAULT-NEXT: store i32 [[RES]], i32* %i, align 4 + // SAME: [[FX:%[0-9]+]] = load i16, i16* %usa, align 2 + // SAME-NEXT: [[INT:%[a-z0-9]+]] = lshr i16 [[FX]], 7 + // SAME-NEXT: [[RES:%[a-z0-9]+]] = zext i16 [[INT]] to i32 + // SAME-NEXT: store i32 [[RES]], i32* %i, align 4 +} + +void TestIntToFixedPoint() { + int i, i2; + unsigned int ui; + short _Accum sa; + unsigned short _Accum usa; + _Sat short _Accum sat_sa; + _Sat unsigned short _Accum sat_usa; + + sa = i; + // DEFAULT: [[I:%[0-9]+]] = load i32, i32* %i, align 4 + // DEFAULT-NEXT: [[I_EXT:%[a-z0-9]+]] = trunc i32 [[I]] to i16 + // DEFAULT-NEXT: [[FX:%[a-z0-9]+]] = shl i16 [[I_EXT]], 7 + // DEFAULT-NEXT: store i16 [[FX]], i16* %sa, align 2 + + sa = ui; + // DEFAULT: [[I:%[0-9]+]] = load i32, i32* %ui, align 4 + // DEFAULT-NEXT: [[I_EXT:%[a-z0-9]+]] = trunc i32 [[I]] to i16 + // DEFAULT-NEXT: [[FX:%[a-z0-9]+]] = shl i16 [[I_EXT]], 7 + // DEFAULT-NEXT: store i16 [[FX]], i16* %sa, align 2 + + usa = i2; + // DEFAULT: [[I:%[0-9]+]] = load i32, i32* %i2, align 4 + // DEFAULT-NEXT: [[I_EXT:%[a-z0-9]+]] = trunc i32 [[I]] to i16 + // DEFAULT-NEXT: [[FX:%[a-z0-9]+]] = shl i16 [[I_EXT]], 8 + // DEFAULT-NEXT: store i16 [[FX]], i16* %usa, align 2 + // SAME: [[I:%[0-9]+]] = load i32, i32* %i2, align 4 + // SAME-NEXT: [[I_EXT:%[a-z0-9]+]] = trunc i32 [[I]] to i16 + // SAME-NEXT: [[FX:%[a-z0-9]+]] = shl i16 [[I_EXT]], 7 + // SAME-NEXT: store i16 [[FX]], i16* %usa, align 2 + + usa = ui; + // DEFAULT: [[I:%[0-9]+]] = load i32, i32* %ui, align 4 + // DEFAULT-NEXT: [[I_EXT:%[a-z0-9]+]] = trunc i32 [[I]] to i16 + // DEFAULT-NEXT: [[FX:%[a-z0-9]+]] = shl i16 [[I_EXT]], 8 + // DEFAULT-NEXT: store i16 [[FX]], i16* %usa, align 2 + // SAME: [[I:%[0-9]+]] = load i32, i32* %ui, align 4 + // SAME-NEXT: [[I_EXT:%[a-z0-9]+]] = trunc i32 [[I]] to i16 + // SAME-NEXT: [[FX:%[a-z0-9]+]] = shl i16 [[I_EXT]], 7 + // SAME-NEXT: store i16 [[FX]], i16* %usa, align 2 +} + +void TestIntToSatFixedPoint() { + int i, i2; + unsigned int ui; + _Sat short _Accum sat_sa; + _Sat unsigned short _Accum sat_usa; + + sat_sa = i; + // DEFAULT: [[I:%[0-9]+]] = load i32, i32* %i, align 4 + // DEFAULT-NEXT: [[I_EXT:%[a-z0-9]+]] = sext i32 [[I]] to i39 + // DEFAULT-NEXT: [[FX:%[a-z0-9]+]] = shl i39 [[I_EXT]], 7 + // DEFAULT-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i39 [[FX]], 32767 + // DEFAULT-NEXT: [[SATMAX:%[a-z0-9]+]] = select i1 [[USE_MAX]], i39 32767, i39 [[FX]] + // DEFAULT-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i39 [[SATMAX]], -32768 + // DEFAULT-NEXT: [[SATMIN:%[a-z0-9]+]] = select i1 [[USE_MIN]], i39 -32768, i39 [[SATMAX]] + // DEFAULT-NEXT: [[RES:%[a-z0-9]+]] = trunc i39 [[SATMIN]] to i16 + // DEFAULT-NEXT: store i16 [[RES]], i16* %sat_sa, align 2 + + sat_sa = ui; + // DEFAULT: [[I:%[0-9]+]] = load i32, i32* %ui, align 4 + // DEFAULT-NEXT: [[I_EXT:%[a-z0-9]+]] = zext i32 [[I]] to i39 + // DEFAULT-NEXT: [[FX:%[a-z0-9]+]] = shl i39 [[I_EXT]], 7 + // DEFAULT-NEXT: [[USE_MAX:%[0-9]+]] = icmp ugt i39 [[FX]], 32767 + // DEFAULT-NEXT: [[SATMAX:%[a-z0-9]+]] = select i1 [[USE_MAX]], i39 32767, i39 [[FX]] + // DEFAULT-NEXT: [[RES:%[a-z0-9]+]] = trunc i39 [[SATMAX]] to i16 + // DEFAULT-NEXT: store i16 [[RES]], i16* %sat_sa, align 2 + + sat_usa = i2; + // DEFAULT: [[I:%[0-9]+]] = load i32, i32* %i2, align 4 + // DEFAULT-NEXT: [[I_EXT:%[a-z0-9]+]] = sext i32 [[I]] to i40 + // DEFAULT-NEXT: [[FX:%[a-z0-9]+]] = shl i40 [[I_EXT]], 8 + // DEFAULT-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i40 [[FX]], 65535 + // DEFAULT-NEXT: [[SATMAX:%[a-z0-9]+]] = select i1 [[USE_MAX]], i40 65535, i40 [[FX]] + // DEFAULT-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i40 [[SATMAX]], 0 + // DEFAULT-NEXT: [[SATMIN:%[a-z0-9]+]] = select i1 [[USE_MIN]], i40 0, i40 [[SATMAX]] + // DEFAULT-NEXT: [[RES:%[a-z0-9]+]] = trunc i40 [[SATMIN]] to i16 + // DEFAULT-NEXT: store i16 [[RES]], i16* %sat_usa, align 2 + // SAME: [[I:%[0-9]+]] = load i32, i32* %i2, align 4 + // SAME-NEXT: [[I_EXT:%[a-z0-9]+]] = sext i32 [[I]] to i39 + // SAME-NEXT: [[FX:%[a-z0-9]+]] = shl i39 [[I_EXT]], 7 + // SAME-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i39 [[FX]], 32767 + // SAME-NEXT: [[SATMAX:%[a-z0-9]+]] = select i1 [[USE_MAX]], i39 32767, i39 [[FX]] + // SAME-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i39 [[SATMAX]], 0 + // SAME-NEXT: [[SATMIN:%[a-z0-9]+]] = select i1 [[USE_MIN]], i39 0, i39 [[SATMAX]] + // SAME-NEXT: [[RES:%[a-z0-9]+]] = trunc i39 [[SATMIN]] to i16 + // SAME-NEXT: store i16 [[RES]], i16* %sat_usa, align 2 + + sat_usa = ui; + // DEFAULT: [[I:%[0-9]+]] = load i32, i32* %ui, align 4 + // DEFAULT-NEXT: [[I_EXT:%[a-z0-9]+]] = zext i32 [[I]] to i40 + // DEFAULT-NEXT: [[FX:%[a-z0-9]+]] = shl i40 [[I_EXT]], 8 + // DEFAULT-NEXT: [[USE_MAX:%[0-9]+]] = icmp ugt i40 [[FX]], 65535 + // DEFAULT-NEXT: [[SATMAX:%[a-z0-9]+]] = select i1 [[USE_MAX]], i40 65535, i40 [[FX]] + // DEFAULT-NEXT: [[RES:%[a-z0-9]+]] = trunc i40 [[SATMAX]] to i16 + // DEFAULT-NEXT: store i16 [[RES]], i16* %sat_usa, align 2 + // SAME: [[I:%[0-9]+]] = load i32, i32* %ui, align 4 + // SAME-NEXT: [[I_EXT:%[a-z0-9]+]] = zext i32 [[I]] to i39 + // SAME-NEXT: [[FX:%[a-z0-9]+]] = shl i39 [[I_EXT]], 7 + // SAME-NEXT: [[USE_MAX:%[0-9]+]] = icmp ugt i39 [[FX]], 32767 + // SAME-NEXT: [[SATMAX:%[a-z0-9]+]] = select i1 [[USE_MAX]], i39 32767, i39 [[FX]] + // SAME-NEXT: [[RES:%[a-z0-9]+]] = trunc i39 [[SATMAX]] to i16 + // SAME-NEXT: store i16 [[RES]], i16* %sat_usa, align 2 +} Index: clang/test/Frontend/fixed_point_errors.c =================================================================== --- clang/test/Frontend/fixed_point_errors.c +++ clang/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: clang/test/Frontend/fixed_point_unknown_conversions.c =================================================================== --- clang/test/Frontend/fixed_point_unknown_conversions.c +++ clang/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}} }