Index: include/clang/AST/OperationKinds.def =================================================================== --- include/clang/AST/OperationKinds.def +++ include/clang/AST/OperationKinds.def @@ -197,6 +197,10 @@ /// float f = i; CAST_OPERATION(IntegralToFloating) +/// CK_FixedPointCast - Fixed point to fixed point. +/// (_Accum) 0.5r +CAST_OPERATION(FixedPointCast) + /// CK_FloatingToIntegral - Floating point to integral. Rounds /// towards zero, discarding any fractional component. /// (int) f Index: include/clang/AST/Type.h =================================================================== --- include/clang/AST/Type.h +++ include/clang/AST/Type.h @@ -1979,7 +1979,8 @@ STK_Integral, STK_Floating, STK_IntegralComplex, - STK_FloatingComplex + STK_FloatingComplex, + STK_FixedPoint }; /// Given that this is a scalar type, classify it. Index: include/clang/Basic/DiagnosticCommonKinds.td =================================================================== --- include/clang/Basic/DiagnosticCommonKinds.td +++ include/clang/Basic/DiagnosticCommonKinds.td @@ -172,6 +172,8 @@ "this value is too large for this fixed point type">; def err_fixed_point_not_enabled : Error<"compile with " "'-ffixed-point' to enable fixed point types">; +def err_unimplemented_conversion_with_fixed_point_type : Error< + "conversion between fixed point and %0 is not yet supported">; // SEH def err_seh_expected_handler : Error< Index: lib/AST/Expr.cpp =================================================================== --- lib/AST/Expr.cpp +++ lib/AST/Expr.cpp @@ -1644,6 +1644,7 @@ case CK_ZeroToOCLEvent: case CK_ZeroToOCLQueue: case CK_IntToOCLSampler: + case CK_FixedPointCast: assert(!getType()->isBooleanType() && "unheralded conversion to bool"); goto CheckNoBasePath; Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -9549,6 +9549,7 @@ case CK_NonAtomicToAtomic: case CK_AddressSpaceConversion: case CK_IntToOCLSampler: + case CK_FixedPointCast: llvm_unreachable("invalid cast kind for integral value"); case CK_BitCast: @@ -10083,6 +10084,7 @@ case CK_NonAtomicToAtomic: case CK_AddressSpaceConversion: case CK_IntToOCLSampler: + case CK_FixedPointCast: llvm_unreachable("invalid cast kind for complex value"); case CK_LValueToRValue: Index: lib/AST/Type.cpp =================================================================== --- lib/AST/Type.cpp +++ lib/AST/Type.cpp @@ -1977,6 +1977,7 @@ if (BT->getKind() == BuiltinType::NullPtr) return STK_CPointer; if (BT->isInteger()) return STK_Integral; if (BT->isFloatingPoint()) return STK_Floating; + if (BT->isFixedPointType()) return STK_FixedPoint; llvm_unreachable("unknown scalar builtin type"); } else if (isa(T)) { return STK_CPointer; Index: lib/CodeGen/CGExpr.cpp =================================================================== --- lib/CodeGen/CGExpr.cpp +++ lib/CodeGen/CGExpr.cpp @@ -4152,6 +4152,7 @@ case CK_CopyAndAutoreleaseBlockObject: case CK_AddressSpaceConversion: case CK_IntToOCLSampler: + case CK_FixedPointCast: return EmitUnsupportedLValue(E, "unexpected cast lvalue"); case CK_Dependent: Index: lib/CodeGen/CGExprAgg.cpp =================================================================== --- lib/CodeGen/CGExprAgg.cpp +++ lib/CodeGen/CGExprAgg.cpp @@ -851,6 +851,7 @@ case CK_ZeroToOCLQueue: case CK_AddressSpaceConversion: case CK_IntToOCLSampler: + case CK_FixedPointCast: llvm_unreachable("cast kind invalid for aggregate types"); } } Index: lib/CodeGen/CGExprComplex.cpp =================================================================== --- lib/CodeGen/CGExprComplex.cpp +++ lib/CodeGen/CGExprComplex.cpp @@ -509,6 +509,7 @@ case CK_ZeroToOCLQueue: case CK_AddressSpaceConversion: case CK_IntToOCLSampler: + case CK_FixedPointCast: llvm_unreachable("invalid cast kind for complex value"); case CK_FloatingRealToComplex: Index: lib/CodeGen/CGExprConstant.cpp =================================================================== --- lib/CodeGen/CGExprConstant.cpp +++ lib/CodeGen/CGExprConstant.cpp @@ -871,6 +871,7 @@ case CK_FloatingCast: case CK_ZeroToOCLEvent: case CK_ZeroToOCLQueue: + case CK_FixedPointCast: return nullptr; } llvm_unreachable("Invalid CastKind"); Index: lib/CodeGen/CGExprScalar.cpp =================================================================== --- lib/CodeGen/CGExprScalar.cpp +++ lib/CodeGen/CGExprScalar.cpp @@ -23,6 +23,7 @@ #include "clang/AST/Expr.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/StmtVisitor.h" +#include "clang/Basic/FixedPoint.h" #include "clang/Basic/TargetInfo.h" #include "clang/Frontend/CodeGenOptions.h" #include "llvm/ADT/Optional.h" @@ -325,6 +326,9 @@ SourceLocation Loc, ScalarConversionOpts Opts = ScalarConversionOpts()); + Value *EmitFixedPointConversion(Value *Src, QualType SrcTy, QualType DstTy, + SourceLocation Loc); + /// Emit a conversion from the specified complex type to the specified /// destination type, where the destination type is an LLVM scalar type. Value *EmitComplexToScalarConversion(CodeGenFunction::ComplexPairTy Src, @@ -985,6 +989,82 @@ SanitizerHandler::ImplicitConversion, StaticArgs, {Src, Dst}); } +// This code is effectively the same as APFixedPoint::convert(). +Value *ScalarExprEmitter::EmitFixedPointConversion(Value *Src, QualType SrcTy, + QualType DstTy, + SourceLocation Loc) { + using llvm::APInt; + using llvm::ConstantInt; + + assert(SrcTy->isFixedPointType()); + assert(DstTy->isFixedPointType()); + + FixedPointSemantics SrcFPSema = + CGF.getContext().getFixedPointSemantics(SrcTy); + FixedPointSemantics DstFPSema = + CGF.getContext().getFixedPointSemantics(DstTy); + unsigned SrcWidth = SrcFPSema.getWidth(); + unsigned DstWidth = DstFPSema.getWidth(); + unsigned SrcScale = SrcFPSema.getScale(); + unsigned DstScale = DstFPSema.getScale(); + bool IsSigned = SrcFPSema.isSigned(); + + Value *Result = Src; + unsigned ResultWidth = SrcWidth; + + if (DstScale > SrcScale) { + // Need to extend first before scaling up + ResultWidth = SrcWidth + DstScale - SrcScale; + llvm::Type *UpscaledTy = Builder.getIntNTy(ResultWidth); + + if (IsSigned) + Result = Builder.CreateSExt(Result, UpscaledTy); + else + Result = Builder.CreateZExt(Result, UpscaledTy); + + Result = Builder.CreateShl(Result, DstScale - SrcScale); + } else if (DstScale < SrcScale) { + if (IsSigned) + Result = Builder.CreateAShr(Result, SrcScale - DstScale); + else + Result = Builder.CreateLShr(Result, SrcScale - DstScale); + } + + if (DstFPSema.isSaturated()) { + if (DstFPSema.getIntegralBits() < SrcFPSema.getIntegralBits()) { + auto Max = ConstantInt::get( + CGF.getLLVMContext(), + APFixedPoint::getMax(DstFPSema).getValue().extOrTrunc(ResultWidth)); + Value *TooHigh = IsSigned ? Builder.CreateICmpSGT(Result, Max) + : Builder.CreateICmpUGT(Result, Max); + Result = Builder.CreateSelect(TooHigh, Max, Result); + + if (IsSigned) { + // Cannot overflow min to dest type is src is unsigned since all fixed + // point types can cover the unsigned min of 0. + auto Min = ConstantInt::get( + CGF.getLLVMContext(), + APFixedPoint::getMin(DstFPSema).getValue().extOrTrunc(ResultWidth)); + Value *TooLow = Builder.CreateICmpSLT(Result, Min); + Result = Builder.CreateSelect(TooLow, Min, Result); + } + } else if (IsSigned && !DstFPSema.isSigned()) { + Value *Zero = + ConstantInt::get(CGF.getLLVMContext(), APInt(ResultWidth, 0)); + Value *LTZero = Builder.CreateICmpSLT(Result, Zero); + Result = Builder.CreateSelect(LTZero, Zero, Result); + } + } + + // Final resizing to dst width + llvm::Type *DstIntTy = Builder.getIntNTy(DstWidth); + if (IsSigned) + Result = Builder.CreateSExtOrTrunc(Result, DstIntTy); + else + Result = Builder.CreateZExtOrTrunc(Result, DstIntTy); + return Result; +} + /// Emit a conversion from the specified type to the specified destination type, /// both of which are LLVM scalar types. Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType, @@ -1874,6 +1954,10 @@ return Builder.CreateVectorSplat(NumElements, Elt, "splat"); } + case CK_FixedPointCast: + return EmitFixedPointConversion(Visit(E), E->getType(), DestTy, + CE->getExprLoc()); + case CK_IntegralCast: { ScalarConversionOpts Opts; if (CGF.SanOpts.has(SanitizerKind::ImplicitIntegerTruncation)) { Index: lib/Edit/RewriteObjCFoundationAPI.cpp =================================================================== --- lib/Edit/RewriteObjCFoundationAPI.cpp +++ lib/Edit/RewriteObjCFoundationAPI.cpp @@ -1085,6 +1085,9 @@ case CK_BooleanToSignedIntegral: llvm_unreachable("OpenCL-specific cast in Objective-C?"); + + case CK_FixedPointCast: + llvm_unreachable("Fixed point types are disabled for Objective-C"); } } Index: lib/Sema/Sema.cpp =================================================================== --- lib/Sema/Sema.cpp +++ lib/Sema/Sema.cpp @@ -533,6 +533,8 @@ case Type::STK_Floating: return CK_FloatingToBoolean; case Type::STK_IntegralComplex: return CK_IntegralComplexToBoolean; case Type::STK_FloatingComplex: return CK_FloatingComplexToBoolean; + case Type::STK_FixedPoint: + llvm_unreachable("Unknown cast from FixedPoint to boolean"); } llvm_unreachable("unknown scalar type kind"); } Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -5868,10 +5868,36 @@ case Type::STK_FloatingComplex: case Type::STK_IntegralComplex: case Type::STK_MemberPointer: + case Type::STK_FixedPoint: llvm_unreachable("illegal cast from pointer"); } llvm_unreachable("Should have returned before this"); + case Type::STK_FixedPoint: + switch (DestTy->getScalarTypeKind()) { + case Type::STK_FixedPoint: + return CK_FixedPointCast; + case Type::STK_Bool: + Diag(Src.get()->getExprLoc(), + diag::err_unimplemented_conversion_with_fixed_point_type) + << DestTy; + return CK_IntegralToBoolean; + case Type::STK_Integral: + case Type::STK_Floating: + case Type::STK_IntegralComplex: + case Type::STK_FloatingComplex: + Diag(Src.get()->getExprLoc(), + diag::err_unimplemented_conversion_with_fixed_point_type) + << DestTy; + return CK_IntegralCast; + case Type::STK_CPointer: + case Type::STK_ObjCObjectPointer: + case Type::STK_BlockPointer: + case Type::STK_MemberPointer: + llvm_unreachable("illegal cast to pointer type"); + } + llvm_unreachable("Should have returned before this"); + case Type::STK_Bool: // casting from bool is like casting from an integer case Type::STK_Integral: switch (DestTy->getScalarTypeKind()) { @@ -5900,6 +5926,11 @@ return CK_FloatingRealToComplex; 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; } llvm_unreachable("Should have returned before this"); @@ -5927,6 +5958,11 @@ llvm_unreachable("valid float->pointer cast?"); 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; } llvm_unreachable("Should have returned before this"); @@ -5956,6 +5992,11 @@ llvm_unreachable("valid complex float->pointer cast?"); 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; } llvm_unreachable("Should have returned before this"); @@ -5985,6 +6026,11 @@ llvm_unreachable("valid complex int->pointer cast?"); 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; } llvm_unreachable("Should have returned before this"); } @@ -12725,6 +12771,12 @@ if (Context.getLangOpts().CPlusPlus) { // C++03 [expr.unary.op]p8, C++0x [expr.unary.op]p9: // operand contextually converted to bool. + if (resultType->getScalarTypeKind() == Type::STK_FixedPoint) { + return ExprError( + Diag(Input.get()->getExprLoc(), + diag::err_unimplemented_conversion_with_fixed_point_type) + << resultType); + } Input = ImpCastExprToType(Input.get(), Context.BoolTy, ScalarTypeToBooleanCastKind(resultType)); } else if (Context.getLangOpts().OpenCL && Index: lib/StaticAnalyzer/Core/ExprEngineC.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -415,7 +415,8 @@ case CK_ZeroToOCLEvent: case CK_ZeroToOCLQueue: case CK_IntToOCLSampler: - case CK_LValueBitCast: { + case CK_LValueBitCast: + case CK_FixedPointCast: { state = handleLValueBitCast(state, Ex, LCtx, T, ExTy, CastE, Bldr, Pred); continue; Index: test/Frontend/fixed_point_conversions.c =================================================================== --- /dev/null +++ test/Frontend/fixed_point_conversions.c @@ -0,0 +1,294 @@ +// RUN: %clang_cc1 -ffixed-point -S -emit-llvm %s -o - | FileCheck %s -check-prefix=DEFAULT +// RUN: %clang_cc1 -ffixed-point -S -emit-llvm %s -o - -fpadding-on-unsigned-fixed-point | FileCheck %s -check-prefix=SAME + +void TestFixedPointCastSameType() { + _Accum a = 2.5k; + _Accum a2 = a; + // DEFAULT: [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4 + // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a2, align 4 + + a2 = (_Accum)a; + // DEFAULT: [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4 + // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a2, align 4 +} + +void TestFixedPointCastDown() { + long _Accum la = 2.5lk; + _Accum a = la; + // DEFAULT: [[LACCUM:%[0-9]+]] = load i64, i64* %la, align 8 + // DEFAULT-NEXT: [[ACCUM_AS_I64:%[0-9]+]] = ashr i64 [[LACCUM]], 16 + // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = trunc i64 [[ACCUM_AS_I64]] to i32 + // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a, align 4 + + a = (_Accum)la; + // DEFAULT: [[LACCUM:%[0-9]+]] = load i64, i64* %la, align 8 + // DEFAULT-NEXT: [[ACCUM_AS_I64:%[0-9]+]] = ashr i64 [[LACCUM]], 16 + // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = trunc i64 [[ACCUM_AS_I64]] to i32 + // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a, align 4 + + short _Accum sa = a; + // DEFAULT: [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4 + // DEFAULT-NEXT: [[SACCUM_AS_I32:%[0-9]+]] = ashr i32 [[ACCUM]], 8 + // DEFAULT-NEXT: [[SACCUM:%[0-9]+]] = trunc i32 [[SACCUM_AS_I32]] to i16 + // DEFAULT-NEXT: store i16 [[SACCUM]], i16* %sa, align 2 + + sa = (short _Accum)a; + // DEFAULT: [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4 + // DEFAULT-NEXT: [[SACCUM_AS_I32:%[0-9]+]] = ashr i32 [[ACCUM]], 8 + // DEFAULT-NEXT: [[SACCUM:%[0-9]+]] = trunc i32 [[SACCUM_AS_I32]] to i16 + // DEFAULT-NEXT: store i16 [[SACCUM]], i16* %sa, align 2 +} + +void TestFixedPointCastUp() { + short _Accum sa = 2.5hk; + _Accum a = sa; + // DEFAULT: [[SACCUM:%[0-9]+]] = load i16, i16* %sa, align 2 + // DEFAULT-NEXT: [[SACCUM_BUFF:%[0-9]+]] = sext i16 [[SACCUM]] to i24 + // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = shl i24 [[SACCUM_BUFF]], 8 + // DEFAULT-NEXT: [[ACCUM_EXT:%[0-9]+]] = sext i24 [[ACCUM]] to i32 + // DEFAULT-NEXT: store i32 [[ACCUM_EXT]], i32* %a, align 4 + + long _Accum la = a; + // DEFAULT: [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4 + // DEFAULT-NEXT: [[ACCUM_BUFF:%[0-9]+]] = sext i32 [[ACCUM]] to i48 + // DEFAULT-NEXT: [[LACCUM:%[0-9]+]] = shl i48 [[ACCUM_BUFF]], 16 + // DEFAULT-NEXT: [[LACCUM_EXT:%[0-9]+]] = sext i48 [[LACCUM]] to i64 + // DEFAULT-NEXT: store i64 [[LACCUM_EXT]], i64* %la, align 8 + + a = (_Accum)sa; + // DEFAULT: [[SACCUM:%[0-9]+]] = load i16, i16* %sa, align 2 + // DEFAULT-NEXT: [[SACCUM_BUFF:%[0-9]+]] = sext i16 [[SACCUM]] to i24 + // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = shl i24 [[SACCUM_BUFF]], 8 + // DEFAULT-NEXT: [[ACCUM_EXT:%[0-9]+]] = sext i24 [[ACCUM]] to i32 + // DEFAULT-NEXT: store i32 [[ACCUM_EXT]], i32* %a, align 4 + + la = (long _Accum)a; + // DEFAULT: [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4 + // DEFAULT-NEXT: [[ACCUM_BUFF:%[0-9]+]] = sext i32 [[ACCUM]] to i48 + // DEFAULT-NEXT: [[LACCUM:%[0-9]+]] = shl i48 [[ACCUM_BUFF]], 16 + // DEFAULT-NEXT: [[LACCUM_EXT:%[0-9]+]] = sext i48 [[LACCUM]] to i64 + // DEFAULT-NEXT: store i64 [[LACCUM_EXT]], i64* %la, align 8 +} + +void TestFixedPointCastSignedness() { + _Accum a = 2.5k; + unsigned _Accum ua = a; + // DEFAULT: [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4 + // DEFAULT-NEXT: [[ACCUM_EXT:%[0-9]+]] = sext i32 [[ACCUM]] to i33 + // DEFAULT-NEXT: [[UACCUM:%[0-9]+]] = shl i33 [[ACCUM_EXT]], 1 + // DEFAULT-NEXT: [[ACCUM_TRUNC:%[0-9]+]] = trunc i33 [[UACCUM]] to i32 + // DEFAULT-NEXT: store i32 [[ACCUM_TRUNC]], i32* %ua, align 4 + // SAME: TestFixedPointCastSignedness + // SAME: [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4 + // SAME-NEXT: store i32 [[ACCUM]], i32* %ua, align 4 + + a = ua; + // DEFAULT: [[UACCUM:%[0-9]+]] = load i32, i32* %ua, align 4 + // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = lshr i32 [[UACCUM]], 1 + // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a, align 4 + // SAME: [[ACCUM:%[0-9]+]] = load i32, i32* %ua, align 4 + // SAME-NEXT: store i32 [[ACCUM]], i32* %a, align 4 + + ua = (unsigned _Accum)a; + // DEFAULT: [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4 + // DEFAULT-NEXT: [[ACCUM_EXT:%[0-9]+]] = sext i32 [[ACCUM]] to i33 + // DEFAULT-NEXT: [[UACCUM:%[0-9]+]] = shl i33 [[ACCUM_EXT]], 1 + // DEFAULT-NEXT: [[ACCUM_TRUNC:%[0-9]+]] = trunc i33 [[UACCUM]] to i32 + // DEFAULT-NEXT: store i32 [[ACCUM_TRUNC]], i32* %ua, align 4 + + a = (_Accum)ua; + // DEFAULT: [[UACCUM:%[0-9]+]] = load i32, i32* %ua, align 4 + // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = lshr i32 [[UACCUM]], 1 + // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a, align 4 + + _Accum a2; + unsigned long _Accum ula = a2; + // DEFAULT: [[ACCUM:%[0-9]+]] = load i32, i32* %a2, align 4 + // DEFAULT-NEXT: [[ACCUM_EXT:%[0-9]+]] = sext i32 [[ACCUM]] to i49 + // DEFAULT-NEXT: [[LACCUM:%[0-9]+]] = shl i49 [[ACCUM_EXT]], 17 + // DEFAULT-NEXT: [[LACCUM_EXT:%[0-9]+]] = sext i49 [[LACCUM]] to i64 + // DEFAULT-NEXT: store i64 [[LACCUM_EXT]], i64* %ula, align 8 + // SAME: [[ACCUM:%[0-9]+]] = load i32, i32* %a2, align 4 + // SAME-NEXT: [[ACCUM_EXT:%[0-9]+]] = sext i32 [[ACCUM]] to i48 + // SAME-NEXT: [[LACCUM:%[0-9]+]] = shl i48 [[ACCUM_EXT]], 16 + // SAME-NEXT: [[LACCUM_EXT:%[0-9]+]] = sext i48 [[LACCUM]] to i64 + // SAME-NEXT: store i64 [[LACCUM_EXT]], i64* %ula, align 8 +} + +void TestFixedPointCastSaturation() { + _Accum a; + _Sat short _Accum sat_sa; + _Sat _Accum sat_a; + _Sat long _Accum sat_la; + _Sat unsigned short _Accum sat_usa; + _Sat unsigned _Accum sat_ua; + _Sat unsigned long _Accum sat_ula; + _Sat short _Fract sat_sf; + _Sat _Fract sat_f; + _Sat long _Fract sat_lf; + + // Casting down between types + sat_sa = sat_a; + // DEFAULT: [[OLD_ACCUM:%[0-9]+]] = load i32, i32* %sat_a, align 4 + // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = ashr i32 [[OLD_ACCUM]], 8 + // DEFAULT-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i32 [[ACCUM]], 32767 + // DEFAULT-NEXT: [[RESULT:%[0-9]+]] = select i1 [[USE_MAX]], i32 32767, i32 [[ACCUM]] + // DEFAULT-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i32 [[RESULT]], -32768 + // DEFAULT-NEXT: [[RESULT2:%[0-9]+]] = select i1 [[USE_MIN]], i32 -32768, i32 [[RESULT]] + // DEFAULT-NEXT: [[RESULT_TRUNC:%[0-9]+]] = trunc i32 [[RESULT2]] to i16 + // DEFAULT-NEXT: store i16 [[RESULT_TRUNC]], i16* %sat_sa, align 2 + + // Accum to Fract, decreasing scale + sat_sf = sat_a; + // DEFAULT: [[OLD_ACCUM:%[0-9]+]] = load i32, i32* %sat_a, align 4 + // DEFAULT-NEXT: [[FRACT:%[0-9]+]] = ashr i32 [[OLD_ACCUM]], 8 + // DEFAULT-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i32 [[FRACT]], 127 + // DEFAULT-NEXT: [[RESULT:%[0-9]+]] = select i1 [[USE_MAX]], i32 127, i32 [[FRACT]] + // DEFAULT-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i32 [[RESULT]], -128 + // DEFAULT-NEXT: [[RESULT2:%[0-9]+]] = select i1 [[USE_MIN]], i32 -128, i32 [[RESULT]] + // DEFAULT-NEXT: [[RESULT_TRUNC:%[0-9]+]] = trunc i32 [[RESULT2]] to i8 + // DEFAULT-NEXT: store i8 [[RESULT_TRUNC]], i8* %sat_sf, align 1 + + // Accum to Fract, same scale + sat_f = a; + // DEFAULT: [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4 + // DEFAULT-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i32 [[ACCUM]], 32767 + // DEFAULT-NEXT: [[RESULT:%[0-9]+]] = select i1 [[USE_MAX]], i32 32767, i32 [[ACCUM]] + // DEFAULT-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i32 [[RESULT]], -32768 + // DEFAULT-NEXT: [[RESULT2:%[0-9]+]] = select i1 [[USE_MIN]], i32 -32768, i32 [[RESULT]] + // DEFAULT-NEXT: [[RESULT_TRUNC:%[0-9]+]] = trunc i32 [[RESULT2]] to i16 + // DEFAULT-NEXT: store i16 [[RESULT_TRUNC]], i16* %sat_f, align 2 + + // Accum to Fract, increasing scale + sat_lf = sat_a; + // DEFAULT: [[OLD_ACCUM:%[0-9]+]] = load i32, i32* %sat_a, align 4 + // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = sext i32 [[OLD_ACCUM]] to i48 + // DEFAULT-NEXT: [[FRACT:%[0-9]+]] = shl i48 [[ACCUM]], 16 + // DEFAULT-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i48 [[FRACT]], 2147483647 + // DEFAULT-NEXT: [[RESULT:%[0-9]+]] = select i1 [[USE_MAX]], i48 2147483647, i48 [[FRACT]] + // DEFAULT-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i48 [[RESULT]], -2147483648 + // DEFAULT-NEXT: [[RESULT2:%[0-9]+]] = select i1 [[USE_MIN]], i48 -2147483648, i48 [[RESULT]] + // DEFAULT-NEXT: [[RESULT_TRUNC:%[0-9]+]] = trunc i48 [[RESULT2]] to i32 + // DEFAULT-NEXT: store i32 [[RESULT_TRUNC]], i32* %sat_lf, align 4 + + // Signed to unsigned, decreasing scale + _Sat _Accum sat_a2; + sat_usa = sat_a2; + // DEFAULT: [[OLD_ACCUM:%[0-9]+]] = load i32, i32* %sat_a2, align 4 + // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = ashr i32 [[OLD_ACCUM]], 7 + // DEFAULT-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i32 [[ACCUM]], 65535 + // DEFAULT-NEXT: [[RESULT:%[0-9]+]] = select i1 [[USE_MAX]], i32 65535, i32 [[ACCUM]] + // DEFAULT-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i32 [[RESULT]], 0 + // DEFAULT-NEXT: [[RESULT2:%[0-9]+]] = select i1 [[USE_MIN]], i32 0, i32 [[RESULT]] + // DEFAULT-NEXT: [[RESULT_TRUNC:%[0-9]+]] = trunc i32 [[RESULT2]] to i16 + // DEFAULT-NEXT: store i16 [[RESULT_TRUNC]], i16* %sat_usa, align 2 + // SAME: [[OLD_ACCUM:%[0-9]+]] = load i32, i32* %sat_a2, align 4 + // SAME-NEXT: [[ACCUM:%[0-9]+]] = ashr i32 [[OLD_ACCUM]], 8 + // SAME-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i32 [[ACCUM]], 32767 + // SAME-NEXT: [[RESULT:%[0-9]+]] = select i1 [[USE_MAX]], i32 32767, i32 [[ACCUM]] + // SAME-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i32 [[RESULT]], 0 + // SAME-NEXT: [[RESULT2:%[0-9]+]] = select i1 [[USE_MIN]], i32 0, i32 [[RESULT]] + // SAME-NEXT: [[RESULT_TRUNC:%[0-9]+]] = trunc i32 [[RESULT2]] to i16 + // SAME-NEXT: store i16 [[RESULT_TRUNC]], i16* %sat_usa, align 2 + + // Signed to unsigned, increasing scale + sat_ua = sat_a; + // DEFAULT: [[OLD_ACCUM:%[0-9]+]] = load i32, i32* %sat_a, align 4 + // DEFAULT-NEXT: [[ACCUM_EXT:%[0-9]+]] = sext i32 [[OLD_ACCUM]] to i33 + // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = shl i33 [[ACCUM_EXT]], 1 + // DEFAULT-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i33 [[ACCUM]], 0 + // DEFAULT-NEXT: [[RESULT2:%[0-9]+]] = select i1 [[USE_MIN]], i33 0, i33 [[ACCUM]] + // DEFAULT-NEXT: [[RESULT_TRUNC:%[0-9]+]] = trunc i33 [[RESULT2]] to i32 + // DEFAULT-NEXT: store i32 [[RESULT_TRUNC]], i32* %sat_ua, align 4 + // SAME: [[ACCUM:%[0-9]+]] = load i32, i32* %sat_a, align 4 + // SAME-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i32 [[ACCUM]], 0 + // SAME-NEXT: [[RESULT:%[0-9]+]] = select i1 [[USE_MIN]], i32 0, i32 [[ACCUM]] + // SAME-NEXT: store i32 [[RESULT]], i32* %sat_ua, align 4 + + // Nothing when saturating to the same type and size + sat_a = a; + // DEFAULT: [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4 + // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %sat_a, align 4 + + // Nothing when assigning back + a = sat_a; + // DEFAULT: [[SAT_ACCUM:%[0-9]+]] = load i32, i32* %sat_a, align 4 + // DEFAULT-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-9]+]] = load i16, i16* %sat_f, align 2 + // DEFAULT-NEXT: [[FRACT_EXT:%[0-9]+]] = sext i16 [[FRACT]] to i32 + // DEFAULT-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-9]+]] = load i8, i8* %sat_sf, align 1 + // DEFAULT-NEXT: [[FRACT_EXT:%[0-9]+]] = sext i8 [[FRACT]] to i17 + // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = shl i17 [[FRACT_EXT]], 9 + // DEFAULT-NEXT: [[IS_NEG:%[0-9]+]] = icmp slt i17 [[ACCUM]], 0 + // DEFAULT-NEXT: [[RESULT:%[0-9]+]] = select i1 [[IS_NEG]], i17 0, i17 [[ACCUM]] + // DEFAULT-NEXT: [[RESULT_EXT:%[0-9]+]] = sext i17 [[RESULT]] to i32 + // DEFAULT-NEXT: store i32 [[RESULT_EXT]], i32* %sat_ua, align 4 + // SAME: [[FRACT:%[0-9]+]] = load i8, i8* %sat_sf, align 1 + // SAME-NEXT: [[FRACT_EXT:%[0-9]+]] = sext i8 [[FRACT]] to i16 + // SAME-NEXT: [[ACCUM:%[0-9]+]] = shl i16 [[FRACT_EXT]], 8 + // SAME-NEXT: [[IS_NEG:%[0-9]+]] = icmp slt i16 [[ACCUM]], 0 + // SAME-NEXT: [[RESULT:%[0-9]+]] = select i1 [[IS_NEG]], i16 0, i16 [[ACCUM]] + // SAME-NEXT: [[RESULT_EXT:%[0-9]+]] = sext i16 [[RESULT]] to i32 + // SAME-NEXT: store i32 [[RESULT_EXT]], i32* %sat_ua, align 4 +} + +void TestFixedPointCastBetFractAccum() { + short _Accum sa; + _Accum a; + long _Accum la; + short _Fract sf; + _Fract f; + long _Fract lf; + unsigned _Accum ua; + unsigned _Fract uf; + + // To lower scale + sf = a; + // DEFAULT: [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4 + // DEFAULT-NEXT: [[FRACT:%[0-9]+]] = ashr i32 [[ACCUM]], 8 + // DEFAULT-NEXT: [[FRACT_TRUNC:%[0-9]+]] = trunc i32 [[FRACT]] to i8 + // DEFAULT-NEXT: store i8 [[FRACT_TRUNC]], i8* %sf, align 1 + + // To higher scale + a = sf; + // DEFAULT: [[FRACT:%[0-9]+]] = load i8, i8* %sf, align 1 + // DEFAULT-NEXT: [[FRACT_EXT:%[0-9]+]] = sext i8 [[FRACT]] to i16 + // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = shl i16 [[FRACT_EXT]], 8 + // DEFAULT-NEXT: [[ACCUM_EXT:%[0-9]+]] = sext i16 [[ACCUM]] to i32 + // DEFAULT-NEXT: store i32 [[ACCUM_EXT]], i32* %a, align 4 + + // To same scale + f = a; + // DEFAULT: [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4 + // DEFAULT-NEXT: [[FRACT:%[0-9]+]] = trunc i32 [[ACCUM]] to i16 + // DEFAULT-NEXT: store i16 [[FRACT]], i16* %f, align 2 + + a = f; + // DEFAULT: [[FRACT:%[0-9]+]] = load i16, i16* %f, align 2 + // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = sext i16 [[FRACT]] to i32 + // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a, align 4 + + // To unsigned + ua = uf; + // DEFAULT: [[FRACT:%[0-9]+]] = load i16, i16* %uf, align 2 + // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = zext i16 [[FRACT]] to i32 + // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %ua, align 4 + // SAME: [[FRACT:%[0-9]+]] = load i16, i16* %uf, align 2 + // SAME-NEXT: [[ACCUM:%[0-9]+]] = zext i16 [[FRACT]] to i32 + // SAME-NEXT: store i32 [[ACCUM]], i32* %ua, align 4 + + uf = ua; + // DEFAULT: [[FRACT:%[0-9]+]] = load i32, i32* %ua, align 4 + // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = trunc i32 [[FRACT]] to i16 + // DEFAULT-NEXT: store i16 [[ACCUM]], i16* %uf, align 2 + // SAME: [[FRACT:%[0-9]+]] = load i32, i32* %ua, align 4 + // SAME-NEXT: [[ACCUM:%[0-9]+]] = trunc i32 [[FRACT]] to i16 + // SAME-NEXT: store i16 [[ACCUM]], i16* %uf, align 2 +} Index: test/Frontend/fixed_point_unknown_conversions.c =================================================================== --- /dev/null +++ test/Frontend/fixed_point_unknown_conversions.c @@ -0,0 +1,50 @@ +// RUN: %clang_cc1 -verify -ffixed-point %s + +void func() { + _Bool b; + char c; + int i; + float f; + double d; + double _Complex dc; + int _Complex ic; + struct S { + int i; + } s; + enum E { + A + } e; + int *ptr; + typedef int int_t; + int_t i2; + + _Accum accum; + _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}} + + b = accum; // expected-error{{conversion between fixed point and '_Bool' 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}} +}