Index: include/clang/AST/ASTContext.h =================================================================== --- include/clang/AST/ASTContext.h +++ include/clang/AST/ASTContext.h @@ -1949,6 +1949,9 @@ unsigned char getFixedPointScale(QualType Ty) const; unsigned char getFixedPointIBits(QualType Ty) const; + llvm::APInt getFixedPointMax(QualType Ty) const; + llvm::APInt getFixedPointMin(QualType Ty) const; + llvm::APInt getFixedPointOne(QualType Ty) const; DeclarationNameInfo getNameForTemplate(TemplateName Name, SourceLocation NameLoc) const; Index: include/clang/AST/OperationKinds.def =================================================================== --- include/clang/AST/OperationKinds.def +++ include/clang/AST/OperationKinds.def @@ -197,6 +197,30 @@ /// float f = i; CAST_OPERATION(IntegralToFloating) +/// CK_FixedPointToBoolean - Fixed point to boolean. A check against zero. +/// (bool) 2.0k +CAST_OPERATION(FixedPointToBoolean) + +/// CK_IntegralToFixedPoint - Integral to fixed point. +/// _Accum a = i; +CAST_OPERATION(IntegralToFixedPoint) + +/// CK_FixedPointToIntegral - Fixed point to integral. +/// (int) 2.0k +CAST_OPERATION(FixedPointToIntegral) + +/// CK_FloatingToFixedPoint - Floating to fixed point. +/// _Accum a = f; +CAST_OPERATION(FloatingToFixedPoint) + +/// CK_FixedPointToFloating - Fixed point to floating. +/// (float) 2.5k +CAST_OPERATION(FixedPointToFloating) + +/// CK_FixedPointCast - Fixed point tyo 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 @@ -1919,7 +1919,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/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -7220,6 +7220,8 @@ def warn_unused_volatile : Warning< "expression result unused; assign into a variable to force a volatile load">, InGroup>; +def err_invalid_cast_with_fixed_point : Error< + "%0 cannot be cast to or from a fixed point type">; def ext_cxx14_attr : Extension< "use of the %0 attribute is a C++14 extension">, InGroup; Index: include/clang/Basic/TargetInfo.h =================================================================== --- include/clang/Basic/TargetInfo.h +++ include/clang/Basic/TargetInfo.h @@ -311,6 +311,13 @@ } } + /// In the event this target uses the same number of fractional bits for its + /// unsigned types as it does with its signed counterparts, there will be + /// exactly one bit of padding. + /// Return true if unsigned fixed point types have padding for this target. + /// False otherwise. + bool unsignedFixedPointTypesHavePadding() const { return SameFBits; } + /// Return the width (in bits) of the specified integer type enum. /// /// For example, SignedInt -> getIntWidth(). Index: include/clang/Lex/LiteralSupport.h =================================================================== --- include/clang/Lex/LiteralSupport.h +++ include/clang/Lex/LiteralSupport.h @@ -72,7 +72,9 @@ bool isFract : 1; // 1.0hr/r/lr/uhr/ur/ulr bool isAccum : 1; // 1.0hk/k/lk/uhk/uk/ulk - bool isFixedPointLiteral() const { return saw_fixed_point_suffix; } + bool isFixedPointLiteral() const { + return saw_fixed_point_suffix && (saw_period || saw_exponent); + } bool isIntegerLiteral() const { return !saw_period && !saw_exponent && !isFixedPointLiteral(); Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -2306,6 +2306,10 @@ QualType EnumUnderlyingTy, bool IsFixed, const EnumDecl *Prev); + /// Returns true if the source type can be casted to or from a fixed point + /// type. + static bool CheckSupportedFixedPointCast(const QualType &Ty); + /// Determine whether the body of an anonymous enumeration should be skipped. /// \param II The name of the first enumerator. SkipBodyInfo shouldSkipAnonEnumBody(Scope *S, IdentifierInfo *II, Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -10251,3 +10251,34 @@ return 0; } } + +llvm::APInt ASTContext::getFixedPointMax(QualType Ty) const { + assert(Ty->isFixedPointType()); + unsigned NumBits = getTypeSize(Ty); + llvm::APInt Val; + + if (Ty->isSignedFixedPointType()) + Val = llvm::APInt::getSignedMaxValue(NumBits); + else + Val = llvm::APInt::getMaxValue(NumBits); + + if (getTargetInfo().unsignedFixedPointTypesHavePadding()) Val = Val.lshr(1); + return Val; +} + +llvm::APInt ASTContext::getFixedPointMin(QualType Ty) const { + assert(Ty->isFixedPointType()); + unsigned NumBits = getTypeSize(Ty); + if (Ty->isSignedFixedPointType()) + return llvm::APInt::getSignedMinValue(NumBits); + else + return llvm::APInt::getMinValue(NumBits); +} + +llvm::APInt ASTContext::getFixedPointOne(QualType Ty) const { + assert(Ty->isFixedPointType()); + unsigned Scale = getFixedPointScale(Ty); + unsigned NumBits = getTypeSize(Ty); + llvm::APInt One(NumBits, 1, Ty->isSignedFixedPointType()); + return One.shl(Scale); +} Index: lib/AST/Expr.cpp =================================================================== --- lib/AST/Expr.cpp +++ lib/AST/Expr.cpp @@ -1640,6 +1640,11 @@ case CK_ZeroToOCLEvent: case CK_ZeroToOCLQueue: case CK_IntToOCLSampler: + case CK_IntegralToFixedPoint: + case CK_FixedPointToIntegral: + case CK_FloatingToFixedPoint: + case CK_FixedPointToFloating: + case CK_FixedPointCast: assert(!getType()->isBooleanType() && "unheralded conversion to bool"); goto CheckNoBasePath; @@ -1657,6 +1662,7 @@ case CK_LValueBitCast: // -> bool& case CK_UserDefinedConversion: // operator bool() case CK_BuiltinFnToFnPtr: + case CK_FixedPointToBoolean: CheckNoBasePath: assert(path_empty() && "Cast kind should not have a base path!"); break; Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -9386,6 +9386,10 @@ case CK_NonAtomicToAtomic: case CK_AddressSpaceConversion: case CK_IntToOCLSampler: + case CK_IntegralToFixedPoint: + case CK_FloatingToFixedPoint: + case CK_FixedPointToFloating: + case CK_FixedPointCast: llvm_unreachable("invalid cast kind for integral value"); case CK_BitCast: @@ -9410,7 +9414,8 @@ case CK_FloatingToBoolean: case CK_BooleanToSignedIntegral: case CK_FloatingComplexToBoolean: - case CK_IntegralComplexToBoolean: { + case CK_IntegralComplexToBoolean: + case CK_FixedPointToBoolean: { bool BoolResult; if (!EvaluateAsBooleanCondition(SubExpr, BoolResult, Info)) return false; @@ -9487,6 +9492,14 @@ return false; return Success(Value, E); } + + case CK_FixedPointToIntegral: { + assert(SrcType->isFixedPointType()); + unsigned Scale = Info.Ctx.getFixedPointScale(SrcType); + if (!FixedPointExprEvaluator(Info, Result).Visit(E)) + return false; + return Success(Result.getInt() >> Scale, E); + } } llvm_unreachable("unknown cast resulting in integral value"); @@ -9921,6 +9934,12 @@ case CK_NonAtomicToAtomic: case CK_AddressSpaceConversion: case CK_IntToOCLSampler: + case CK_FixedPointToBoolean: + case CK_IntegralToFixedPoint: + case CK_FixedPointToIntegral: + case CK_FloatingToFixedPoint: + case CK_FixedPointToFloating: + 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 @@ -1936,6 +1936,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 @@ -4111,6 +4111,12 @@ case CK_CopyAndAutoreleaseBlockObject: case CK_AddressSpaceConversion: case CK_IntToOCLSampler: + case CK_FixedPointToBoolean: + case CK_IntegralToFixedPoint: + case CK_FixedPointToIntegral: + case CK_FloatingToFixedPoint: + case CK_FixedPointToFloating: + 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,12 @@ case CK_ZeroToOCLQueue: case CK_AddressSpaceConversion: case CK_IntToOCLSampler: + case CK_FixedPointToBoolean: + case CK_IntegralToFixedPoint: + case CK_FixedPointToIntegral: + case CK_FloatingToFixedPoint: + case CK_FixedPointToFloating: + 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,12 @@ case CK_ZeroToOCLQueue: case CK_AddressSpaceConversion: case CK_IntToOCLSampler: + case CK_FixedPointToBoolean: + case CK_IntegralToFixedPoint: + case CK_FixedPointToIntegral: + case CK_FloatingToFixedPoint: + case CK_FixedPointToFloating: + 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 @@ -859,6 +859,12 @@ case CK_FloatingCast: case CK_ZeroToOCLEvent: case CK_ZeroToOCLQueue: + case CK_FixedPointToBoolean: + case CK_IntegralToFixedPoint: + case CK_FixedPointToIntegral: + case CK_FloatingToFixedPoint: + case CK_FixedPointToFloating: + case CK_FixedPointCast: return nullptr; } llvm_unreachable("Invalid CastKind"); Index: lib/CodeGen/CGExprScalar.cpp =================================================================== --- lib/CodeGen/CGExprScalar.cpp +++ lib/CodeGen/CGExprScalar.cpp @@ -762,6 +762,17 @@ } Value *VisitAsTypeExpr(AsTypeExpr *CE); Value *VisitAtomicExpr(AtomicExpr *AE); + + Value *ClearFixedPointPadding(QualType Ty, Value *Val) { + if (CGF.getContext().getTargetInfo().unsignedFixedPointTypesHavePadding() && + Ty->isUnsignedFixedPointType()) { + unsigned NumBits = CGF.getContext().getTypeSize(Ty); + auto Mask = llvm::APInt::getLowBitsSet(NumBits, NumBits - 1); + auto MaskVal = llvm::ConstantInt::get(CGF.getLLVMContext(), Mask); + return Builder.CreateAnd(Val, MaskVal); + } + return Val; + } }; } // end anonymous namespace. @@ -780,7 +791,8 @@ if (const MemberPointerType *MPT = dyn_cast(SrcType)) return CGF.CGM.getCXXABI().EmitMemberPointerIsNotNull(CGF, Src, MPT); - assert((SrcType->isIntegerType() || isa(Src->getType())) && + assert((SrcType->isIntegerType() || SrcType->isFixedPointType() || + isa(Src->getType())) && "Unknown scalar type to convert"); if (isa(Src->getType())) @@ -1775,6 +1787,188 @@ return Builder.CreateVectorSplat(NumElements, Elt, "splat"); } + case CK_FixedPointToIntegral: { + const QualType &SrcTy = E->getType(); + assert(SrcTy->isFixedPointType()); + assert(DestTy->isIntegerType()); + unsigned scale = CGF.getContext().getFixedPointScale(SrcTy); + llvm::Value *Val = ClearFixedPointPadding(SrcTy, Visit(E)); + if (DestTy->isSignedIntegerType()) + return Builder.CreateAShr( + EmitScalarConversion(Val, SrcTy, DestTy, CE->getExprLoc()), scale); + else + return Builder.CreateLShr( + EmitScalarConversion(Val, SrcTy, DestTy, CE->getExprLoc()), scale); + } + + case CK_IntegralToFixedPoint: { + const QualType &SrcTy = E->getType(); + assert(SrcTy->isIntegerType()); + assert(DestTy->isFixedPointType()); + unsigned scale = CGF.getContext().getFixedPointScale(DestTy); + + llvm::Value *IntVal = Visit(E); + llvm::Value *Result = Builder.CreateShl( + EmitScalarConversion(IntVal, SrcTy, DestTy, CE->getExprLoc()), scale); + + if (DestTy->isSaturatedFixedPointType()) { + // Cast both sides up to one more than the highest width of the 2 to + // prevent truncating any bits and be able to do a simple signed + // comparison regardless of the signedness of either operand. + bool isSignedSrc = SrcTy->isSignedIntegerType(); + bool isSignedDst = DestTy->isSignedFixedPointType(); + unsigned IntWidth = IntVal->getType()->getIntegerBitWidth(); + unsigned BufferWidth = + std::max(IntWidth, ConvertType(DestTy)->getIntegerBitWidth()) + 1; + llvm::Type *TmpType = Builder.getIntNTy(BufferWidth); + + llvm::APInt MaxVal = CGF.getContext().getFixedPointMax(DestTy); + llvm::APInt MinVal = CGF.getContext().getFixedPointMin(DestTy); + + llvm::APInt MaxValIntPart = MaxVal.lshr(scale); + llvm::APInt MinValIntPart = MinVal.ashr(scale); + + MaxValIntPart = isSignedDst ? MaxValIntPart.sextOrSelf(BufferWidth) + : MaxValIntPart.zextOrSelf(BufferWidth); + MinValIntPart = isSignedDst ? MinValIntPart.sextOrSelf(BufferWidth) + : MinValIntPart.zextOrSelf(BufferWidth); + + auto MaxValLLVM = llvm::ConstantInt::get(CGF.getLLVMContext(), MaxVal); + auto MinValLLVM = llvm::ConstantInt::get(CGF.getLLVMContext(), MinVal); + auto MaxValIntPartLLVM = + llvm::ConstantInt::get(CGF.getLLVMContext(), MaxValIntPart); + auto MinValIntPartLLVM = + llvm::ConstantInt::get(CGF.getLLVMContext(), MinValIntPart); + Value *IntValCasted = Builder.CreateIntCast(IntVal, TmpType, isSignedSrc); + + llvm::Value *UseMax = + Builder.CreateICmpSGT(IntValCasted, MaxValIntPartLLVM); + llvm::Value *UseMin = + Builder.CreateICmpSLT(IntValCasted, MinValIntPartLLVM); + Result = Builder.CreateSelect( + UseMax, MaxValLLVM, Builder.CreateSelect(UseMin, MinValLLVM, Result)); + } + + return Result; + } + + case CK_FixedPointToFloating: { + // IIRC the optimization of divide-by-power-of-two --> multiply-by-inverse + // does not occur at -O0, so it would be better to multiply by 2^(-fbits) + // instead. + const QualType &SrcTy = E->getType(); + assert(SrcTy->isFixedPointType()); + assert(DestTy->isFloatingType()); + unsigned scale = CGF.getContext().getFixedPointScale(SrcTy); + auto NormalizerVal = llvm::ConstantFP::get( + CGF.CGM.getTypes().ConvertTypeForMem(DestTy), 1.0 / (1ULL << scale)); + llvm::Value *Val = ClearFixedPointPadding(SrcTy, Visit(E)); + llvm::Value *Result; + if (SrcTy->isSignedFixedPointType()) + Result = Builder.CreateSIToFP(Val, CGF.ConvertType(DestTy)); + else + Result = Builder.CreateUIToFP(Val, CGF.ConvertType(DestTy)); + return Builder.CreateFMul(Result, NormalizerVal); + } + + case CK_FloatingToFixedPoint: { + const QualType &SrcTy = E->getType(); + assert(DestTy->isFixedPointType()); + assert(SrcTy->isFloatingType()); + bool isSignedDst = DestTy->isSignedFixedPointType(); + const llvm::fltSemantics &SrcSema = + CGF.getContext().getFloatTypeSemantics(SrcTy); + + llvm::APInt Normalizer = CGF.getContext().getFixedPointOne(DestTy); + llvm::APFloat NormalizerFlt(SrcSema, 1); + llvm::APFloat::roundingMode Rounding = llvm::APFloat::rmNearestTiesToEven; + assert(!NormalizerFlt.convertFromAPInt(Normalizer, isSignedDst, Rounding)); + + llvm::Value *Flt = Visit(E); + auto NormalizerVal = llvm::ConstantFP::get(Flt->getType(), NormalizerFlt); + llvm::Value *Result = Builder.CreateFMul(Flt, NormalizerVal); + + if (DestTy->isSignedFixedPointType()) + Result = Builder.CreateFPToSI(Result, CGF.ConvertType(DestTy)); + else + Result = Builder.CreateFPToUI(Result, CGF.ConvertType(DestTy)); + + if (DestTy->isSaturatedFixedPointType()) { + llvm::APInt MaxVal = CGF.getContext().getFixedPointMax(DestTy); + llvm::APInt MinVal = CGF.getContext().getFixedPointMin(DestTy); + auto MaxValLLVM = llvm::ConstantInt::get(CGF.getLLVMContext(), MaxVal); + auto MinValLLVM = llvm::ConstantInt::get(CGF.getLLVMContext(), MinVal); + llvm::APFloat MaxValFlt(SrcSema, 1); + llvm::APFloat MinValFlt(SrcSema, 1); + + if (MaxValFlt.convertFromAPInt(MaxVal, isSignedDst, Rounding) & + llvm::APFloat::opOverflow) { + return MaxValLLVM; + } + + if (MinValFlt.convertFromAPInt(MinVal, isSignedDst, Rounding) & + llvm::APFloat::opOverflow) { + return MinValLLVM; + } + + MaxValFlt = MaxValFlt / NormalizerFlt; + MinValFlt = MinValFlt / NormalizerFlt; + + auto MaxValFltLLVM = + llvm::ConstantFP::get(CGF.getLLVMContext(), MaxValFlt); + auto MinValFltLLVM = + llvm::ConstantFP::get(CGF.getLLVMContext(), MinValFlt); + + llvm::Value *UseMax = Builder.CreateFCmpUGE(Flt, MaxValFltLLVM); + llvm::Value *UseMin = Builder.CreateFCmpULE(Flt, MinValFltLLVM); + Result = Builder.CreateSelect( + UseMax, MaxValLLVM, Builder.CreateSelect(UseMin, MinValLLVM, Result)); + } + + return Result; + } + + case CK_FixedPointToBoolean: { + const QualType &SrcTy = E->getType(); + assert(SrcTy->isFixedPointType()); + assert(DestTy->isBooleanType()); + return Builder.CreateIsNotNull(ClearFixedPointPadding(SrcTy, Visit(E))); + } + + case CK_FixedPointCast: { + const QualType &SrcTy = E->getType(); + assert(DestTy->isFixedPointType()); + assert(SrcTy->isFixedPointType()); + + unsigned SrcScale = CGF.getContext().getFixedPointScale(SrcTy); + unsigned DstScale = CGF.getContext().getFixedPointScale(DestTy); + unsigned SrcWidth = CGF.getContext().getTypeInfo(SrcTy).Width; + unsigned DstWidth = CGF.getContext().getTypeInfo(DestTy).Width; + llvm::Value *Result = ClearFixedPointPadding(SrcTy, Visit(E)); + bool isSigned = SrcTy->isSignedFixedPointType(); + + if (DstWidth > SrcWidth) + // Upcast beforhand + Result = Builder.CreateIntCast( + Result, CGF.CGM.getTypes().ConvertTypeForMem(DestTy), isSigned); + + if (DstScale > SrcScale) { + 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 (DstWidth < SrcWidth) + // Downcast afterwards + Result = Builder.CreateIntCast( + Result, CGF.CGM.getTypes().ConvertTypeForMem(DestTy), isSigned); + + return Result; + } + case CK_IntegralCast: case CK_IntegralToFloating: case CK_FloatingToIntegral: Index: lib/Edit/RewriteObjCFoundationAPI.cpp =================================================================== --- lib/Edit/RewriteObjCFoundationAPI.cpp +++ lib/Edit/RewriteObjCFoundationAPI.cpp @@ -1086,6 +1086,14 @@ case CK_BooleanToSignedIntegral: llvm_unreachable("OpenCL-specific cast in Objective-C?"); + + case CK_FixedPointToBoolean: + case CK_IntegralToFixedPoint: + case CK_FixedPointToIntegral: + case CK_FloatingToFixedPoint: + case CK_FixedPointToFloating: + case CK_FixedPointCast: + llvm_unreachable("Fixed point types are disabled for Objective-C"); } } Index: lib/Lex/LiteralSupport.cpp =================================================================== --- lib/Lex/LiteralSupport.cpp +++ lib/Lex/LiteralSupport.cpp @@ -589,11 +589,13 @@ switch (*s) { case 'R': case 'r': + if (!isFixedPointLiteral()) break; if (isFract || isAccum) break; isFract = true; continue; case 'K': case 'k': + if (!isFixedPointLiteral()) break; if (isFract || isAccum) break; isAccum = true; continue; @@ -732,7 +734,7 @@ } } - if (!hadError && saw_fixed_point_suffix) { + if (!hadError && isFixedPointLiteral()) { assert(isFract || isAccum); assert(radix == 16 || radix == 10); } Index: lib/Sema/Sema.cpp =================================================================== --- lib/Sema/Sema.cpp +++ lib/Sema/Sema.cpp @@ -532,6 +532,7 @@ 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: return CK_FixedPointToBoolean; } llvm_unreachable("unknown scalar type kind"); } Index: lib/Sema/SemaCast.cpp =================================================================== --- lib/Sema/SemaCast.cpp +++ lib/Sema/SemaCast.cpp @@ -2607,7 +2607,25 @@ return; } } - + + // Check on fixed point types + if (SrcType->isFixedPointType() && + !Sema::CheckSupportedFixedPointCast(DestType)) { + Self.Diag(SrcExpr.get()->getLocStart(), + diag::err_invalid_cast_with_fixed_point) + << DestType << SrcExpr.get()->getSourceRange(); + SrcExpr = ExprError(); + return; + } + if (DestType->isFixedPointType() && + !Sema::CheckSupportedFixedPointCast(SrcType)) { + Self.Diag(SrcExpr.get()->getLocStart(), + diag::err_invalid_cast_with_fixed_point) + << SrcType << SrcExpr.get()->getSourceRange(); + SrcExpr = ExprError(); + return; + } + DiagnoseCastOfObjCSEL(Self, SrcExpr, DestType); DiagnoseCallingConvCast(Self, SrcExpr, DestType, OpRange); DiagnoseBadFunctionCast(Self, SrcExpr, DestType); @@ -2705,3 +2723,10 @@ Op.ValueKind, CastTypeInfo, Op.Kind, Op.SrcExpr.get(), &Op.BasePath, LPLoc, RPLoc)); } + +bool Sema::CheckSupportedFixedPointCast(const QualType &Ty) { + if (!Ty->isScalarType()) return false; + Type::ScalarTypeKind Kind = Ty->getScalarTypeKind(); + return (Kind == Type::STK_Bool || Kind == Type::STK_Integral || + Kind == Type::STK_Floating || Kind == Type::STK_FixedPoint); +} Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -3351,16 +3351,14 @@ bool isSigned = !Literal.isUnsigned; unsigned scale = Context.getFixedPointScale(Ty); - unsigned ibits = Context.getFixedPointIBits(Ty); unsigned bit_width = Context.getTypeInfo(Ty).Width; llvm::APInt Val(bit_width, 0, isSigned); bool Overflowed = Literal.GetFixedPointValue(Val, scale); + bool ValIsZero = Val.isNullValue() && !Overflowed; - // Do not use bit_width since some types may have padding like _Fract or - // unsigned _Accums if SameFBits is set. - auto MaxVal = llvm::APInt::getMaxValue(ibits + scale).zextOrSelf(bit_width); - if (Literal.isFract && Val == MaxVal + 1) + auto MaxVal = Context.getFixedPointMax(Ty); + if (Literal.isFract && Val == MaxVal + 1 && !ValIsZero) // Clause 6.4.4 - The value of a constant shall be in the range of // representable values for its type, with exception for constants of a // fract type with a value of exactly 1; such a constant shall denote @@ -5852,6 +5850,7 @@ 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"); @@ -5884,6 +5883,32 @@ return CK_FloatingRealToComplex; case Type::STK_MemberPointer: llvm_unreachable("member pointer type in C"); + case Type::STK_FixedPoint: + return CK_IntegralToFixedPoint; + } + llvm_unreachable("Should have returned before this"); + + case Type::STK_FixedPoint: + switch (DestTy->getScalarTypeKind()) { + case Type::STK_CPointer: + case Type::STK_ObjCObjectPointer: + case Type::STK_BlockPointer: + llvm_unreachable("Invalid fixed point to pointer cast"); + case Type::STK_Bool: + return CK_FixedPointToBoolean; + case Type::STK_Integral: + return CK_FixedPointToIntegral; + case Type::STK_Floating: + return CK_FixedPointToFloating; + case Type::STK_IntegralComplex: + case Type::STK_FloatingComplex: + // Do not allow conversion with floating points for now since these are + // not required by Embedded-C. + llvm_unreachable("Invalid fixed point to complex cast"); + case Type::STK_MemberPointer: + llvm_unreachable("member pointer type in C"); + case Type::STK_FixedPoint: + return CK_FixedPointCast; } llvm_unreachable("Should have returned before this"); @@ -5911,6 +5936,8 @@ llvm_unreachable("valid float->pointer cast?"); case Type::STK_MemberPointer: llvm_unreachable("member pointer type in C"); + case Type::STK_FixedPoint: + return CK_FloatingToFixedPoint; } llvm_unreachable("Should have returned before this"); @@ -5940,6 +5967,8 @@ llvm_unreachable("valid complex float->pointer cast?"); case Type::STK_MemberPointer: llvm_unreachable("member pointer type in C"); + case Type::STK_FixedPoint: + llvm_unreachable("Invalid complex float to fixed point cast"); } llvm_unreachable("Should have returned before this"); @@ -5969,6 +5998,8 @@ llvm_unreachable("valid complex int->pointer cast?"); case Type::STK_MemberPointer: llvm_unreachable("member pointer type in C"); + case Type::STK_FixedPoint: + llvm_unreachable("Invalid complex int to fixed point cast"); } llvm_unreachable("Should have returned before this"); } @@ -6870,6 +6901,18 @@ return QualType(); } + // Check on fixed point types + if (RHSTy->isFixedPointType() && !Sema::CheckSupportedFixedPointCast(LHSTy)) { + Diag(LHS.get()->getLocStart(), diag::err_invalid_cast_with_fixed_point) + << LHSTy << LHS.get()->getSourceRange(); + return QualType(); + } + if (LHSTy->isFixedPointType() && !Sema::CheckSupportedFixedPointCast(RHSTy)) { + Diag(RHS.get()->getLocStart(), diag::err_invalid_cast_with_fixed_point) + << RHSTy << RHS.get()->getSourceRange(); + return QualType(); + } + // If both operands have arithmetic type, do the usual arithmetic conversions // to find a common type: C99 6.5.15p3,5. if (LHSTy->isArithmeticType() && RHSTy->isArithmeticType()) { @@ -7715,6 +7758,14 @@ !LHSType->getAs()) return Incompatible; + // Check on fixed point types + if ((RHSType->isFixedPointType() && + !Sema::CheckSupportedFixedPointCast(LHSType)) || + (LHSType->isFixedPointType() && + !Sema::CheckSupportedFixedPointCast(RHSType))) { + return Incompatible; + } + // Arithmetic conversions. if (LHSType->isArithmeticType() && RHSType->isArithmeticType() && !(getLangOpts().CPlusPlus && LHSType->isEnumeralType())) { Index: lib/StaticAnalyzer/Core/ExprEngineC.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -415,7 +415,13 @@ case CK_ZeroToOCLEvent: case CK_ZeroToOCLQueue: case CK_IntToOCLSampler: - case CK_LValueBitCast: { + case CK_LValueBitCast: + case CK_FixedPointToBoolean: + case CK_IntegralToFixedPoint: + case CK_FixedPointToIntegral: + case CK_FloatingToFixedPoint: + case CK_FixedPointToFloating: + case CK_FixedPointCast: { state = handleLValueBitCast(state, Ex, LCtx, T, ExTy, CastE, Bldr, Pred); continue; Index: test/Frontend/fixed_point_bit_widths.c =================================================================== --- test/Frontend/fixed_point_bit_widths.c +++ test/Frontend/fixed_point_bit_widths.c @@ -1,7 +1,4 @@ -// RUN: %clang -x c -ffixed-point -S -emit-llvm -o - %s | FileCheck %s -// RUN: %clang -x c -ffixed-point -S -emit-llvm -o - --target=x86_64-scei-ps4-ubuntu-fast %s | FileCheck %s -// RUN: %clang -x c -ffixed-point -S -emit-llvm -o - --target=ppc64 %s | FileCheck %s -// RUN: %clang -x c -ffixed-point -S -emit-llvm -o - --target=x86_64-scei-ps4-windows10pro-fast %s | FileCheck %s +// RUN: %clang -x c -ffixed-point -S -emit-llvm -o - --target=x86_64-linux %s | FileCheck %s /* Primary signed _Accum */ Index: test/Frontend/fixed_point_conversions.c =================================================================== --- /dev/null +++ test/Frontend/fixed_point_conversions.c @@ -0,0 +1,472 @@ +// RUN: %clang --target=x86_64-linux -ffixed-point -S -emit-llvm %s -o - | FileCheck %s -check-prefix=DEFAULT + +void TestFixedPointCastSameType() { + _Accum a = 2.5k; + _Accum a2 = a; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a, align 4 +// DEFAULT-NEXT: store i32 [[ACC]], i32* %a2, align 4 + + a2 = (_Accum)a; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a, align 4 +// DEFAULT-NEXT: store i32 [[ACC]], i32* %a2, align 4 +} + +void TestFixedToBool() { + _Accum a = 2.0k; + _Bool b = a; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a, align 4 +// DEFAULT-NEXT: [[NE:%[-1-9]+]] = icmp ne i32 [[ACC]], 0 +// DEFAULT-NEXT: [[FROMBOOL:%[a-z0-9]+]] = zext i1 [[NE]] to i8 +// DEFAULT-NEXT: store i8 [[FROMBOOL]], i8* %b, align 1 + + if (a) {} +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a, align 4 +// DEFAULT-NEXT: [[TOBOOL:%[a-z0-9]+]] = icmp ne i32 [[ACC]], 0 +// DEFAULT-NEXT: br i1 [[TOBOOL]], label %if.then, label %if.end + + b = a ? 1 : 2; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a, align 4 +// DEFAULT-NEXT: [[TOBOOL:%[a-z0-9]+]] = icmp ne i32 [[ACC]], 0 +// DEFAULT: {{.*}} = select i1 [[TOBOOL]], i32 1, i32 2 + + b = (_Bool)a; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a, align 4 +// DEFAULT-NEXT: [[NE:%[0-9]+]] = icmp ne i32 [[ACC]], 0 +// DEFAULT-NEXT: [[FROMBOOL:%[a-z0-9]+]] = zext i1 [[NE]] to i8 +// DEFAULT-NEXT: store i8 [[FROMBOOL]], i8* %b, align 1 +} + +_Bool FixedToBool(_Accum a) { return a; } +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a.addr, align 4 +// DEFAULT-NEXT: [[NE:%[0-9]+]] = icmp ne i32 [[ACC]], 0 +// DEFAULT-NEXT: ret i1 [[NE]] + +void TestFixedToInt() { + _Accum a = 2.0k; + unsigned _Accum ua = 2.0uk; + int i; + unsigned int ui; + + i = a; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a, align 4 +// DEFAULT-NEXT: [[INT:%[0-9]+]] = ashr i32 [[ACC]], 15 +// DEFAULT-NEXT: store i32 [[INT]], i32* %i, align 4 + + i = ua; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %ua, align 4 +// DEFAULT-NEXT: [[INT:%[0-9]+]] = ashr i32 [[ACC]], 16 +// DEFAULT-NEXT: store i32 [[INT]], i32* %i, align 4 + + ui = a; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a, align 4 +// DEFAULT-NEXT: [[INT:%[0-9]+]] = lshr i32 [[ACC]], 15 +// DEFAULT-NEXT: store i32 [[INT]], i32* %ui, align 4 + + ui = ua; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %ua, align 4 +// DEFAULT-NEXT: [[INT:%[0-9]+]] = lshr i32 [[ACC]], 16 +// DEFAULT-NEXT: store i32 [[INT]], i32* %ui, align 4 + + i = (int)a; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a, align 4 +// DEFAULT-NEXT: [[INT:%[0-9]+]] = ashr i32 [[ACC]], 15 +// DEFAULT-NEXT: store i32 [[INT]], i32* %i, align 4 + + i = (int)ua; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %ua, align 4 +// DEFAULT-NEXT: [[INT:%[0-9]+]] = ashr i32 [[ACC]], 16 +// DEFAULT-NEXT: store i32 [[INT]], i32* %i, align 4 + + ui = (unsigned int)a; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a, align 4 +// DEFAULT-NEXT: [[INT:%[0-9]+]] = lshr i32 [[ACC]], 15 +// DEFAULT-NEXT: store i32 [[INT]], i32* %ui, align 4 + + ui = (unsigned int)ua; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %ua, align 4 +// DEFAULT-NEXT: [[INT:%[0-9]+]] = lshr i32 [[ACC]], 16 +// DEFAULT-NEXT: store i32 [[INT]], i32* %ui, align 4 +} + +int FixedPointToInt(_Accum a) { return a; } +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a.addr, align 4 +// DEFAULT-NEXT: [[INT:%[0-9]+]] = ashr i32 [[ACC]], 15 +// DEFAULT-NEXT: ret i32 [[INT]] + +int UFixedPointToInt(unsigned _Accum a) { return a; } +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a.addr, align 4 +// DEFAULT-NEXT: [[INT:%[0-9]+]] = ashr i32 [[ACC]], 16 +// DEFAULT-NEXT: ret i32 [[INT]] + +unsigned int FixedPointToUInt(_Accum a) { return a; } +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a.addr, align 4 +// DEFAULT-NEXT: [[INT:%[0-9]+]] = lshr i32 [[ACC]], 15 +// DEFAULT-NEXT: ret i32 [[INT]] + +unsigned int UFixedPointToUInt(unsigned _Accum a) { return a; } +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a.addr, align 4 +// DEFAULT-NEXT: [[INT:%[0-9]+]] = lshr i32 [[ACC]], 16 +// DEFAULT-NEXT: ret i32 [[INT]] + +void TestIntToFixed() { + int i = 2; + unsigned int ui = 2; + _Accum a; + unsigned _Accum ua; + + a = i; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %i, align 4 +// DEFAULT-NEXT: [[INT:%[0-9]+]] = shl i32 [[ACC]], 15 +// DEFAULT-NEXT: store i32 [[INT]], i32* %a, align 4 + + a = ui; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %ui, align 4 +// DEFAULT-NEXT: [[INT:%[0-9]+]] = shl i32 [[ACC]], 15 +// DEFAULT-NEXT: store i32 [[INT]], i32* %a, align 4 + + ua = i; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %i, align 4 +// DEFAULT-NEXT: [[INT:%[0-9]+]] = shl i32 [[ACC]], 16 +// DEFAULT-NEXT: store i32 [[INT]], i32* %ua, align 4 + + ua = ui; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %ui, align 4 +// DEFAULT-NEXT: [[INT:%[0-9]+]] = shl i32 [[ACC]], 16 +// DEFAULT-NEXT: store i32 [[INT]], i32* %ua, align 4 + + a = (_Accum)i; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %i, align 4 +// DEFAULT-NEXT: [[INT:%[0-9]+]] = shl i32 [[ACC]], 15 +// DEFAULT-NEXT: store i32 [[INT]], i32* %a, align 4 + + a = (_Accum)ui; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %ui, align 4 +// DEFAULT-NEXT: [[INT:%[0-9]+]] = shl i32 [[ACC]], 15 +// DEFAULT-NEXT: store i32 [[INT]], i32* %a, align 4 + + ua = (unsigned _Accum)i; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %i, align 4 +// DEFAULT-NEXT: [[INT:%[0-9]+]] = shl i32 [[ACC]], 16 +// DEFAULT-NEXT: store i32 [[INT]], i32* %ua, align 4 + + ua = (unsigned _Accum)ui; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %ui, align 4 +// DEFAULT-NEXT: [[INT:%[0-9]+]] = shl i32 [[ACC]], 16 +// DEFAULT-NEXT: store i32 [[INT]], i32* %ua, align 4 +} + +_Accum IntToFixedPoint(int i) { return i; } +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %i.addr, align 4 +// DEFAULT-NEXT: [[INT:%[0-9]+]] = shl i32 [[ACC]], 15 +// DEFAULT-NEXT: ret i32 [[INT]] + +_Accum UIntToFixedPoint(unsigned int i) { return i; } +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %i.addr, align 4 +// DEFAULT-NEXT: [[INT:%[0-9]+]] = shl i32 [[ACC]], 15 +// DEFAULT-NEXT: ret i32 [[INT]] + +unsigned _Accum IntToUFixedPoint(int i) { return i; } +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %i.addr, align 4 +// DEFAULT-NEXT: [[INT:%[0-9]+]] = shl i32 [[ACC]], 16 +// DEFAULT-NEXT: ret i32 [[INT]] + +unsigned _Accum UIntToUFixedPoint(int i) { return i; } +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %i.addr, align 4 +// DEFAULT-NEXT: [[INT:%[0-9]+]] = shl i32 [[ACC]], 16 +// DEFAULT-NEXT: ret i32 [[INT]] + +void TestFixedPointToFloat() { + _Accum a = 2.5k; + unsigned _Accum ua = 2.5k; + float f = a; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a, align 4 +// DEFAULT-NEXT: [[CONV:%[a-z0-9]+]] = sitofp i32 [[ACC]] to float +// DEFAULT-NEXT: [[ASFLOAT:%[0-9]+]] = fmul float [[CONV]], 0x3F00000000000000 +// DEFAULT-NEXT: store float [[ASFLOAT]], float* %f, align 4 + + f = ua; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %ua, align 4 +// DEFAULT-NEXT: [[CONV:%[a-z0-9]+]] = uitofp i32 [[ACC]] to float +// DEFAULT-NEXT: [[ASFLOAT:%[0-9]+]] = fmul float [[CONV]], 0x3EF0000000000000 +// DEFAULT-NEXT: store float [[ASFLOAT]], float* %f, align 4 + + f = (float)a; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a, align 4 +// DEFAULT-NEXT: [[CONV:%[a-z0-9]+]] = sitofp i32 [[ACC]] to float +// DEFAULT-NEXT: [[ASFLOAT:%[0-9]+]] = fmul float [[CONV]], 0x3F00000000000000 +// DEFAULT-NEXT: store float [[ASFLOAT]], float* %f, align 4 + + f = (float)ua; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %ua, align 4 +// DEFAULT-NEXT: [[CONV:%[a-z0-9]+]] = uitofp i32 [[ACC]] to float +// DEFAULT-NEXT: [[ASFLOAT:%[0-9]+]] = fmul float [[CONV]], 0x3EF0000000000000 +// DEFAULT-NEXT: store float [[ASFLOAT]], float* %f, align 4 +} + +float FixedPointToFloat(_Accum a) { return a; } +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a.addr, align 4 +// DEFAULT-NEXT: [[CONV:%[a-z0-9]+]] = sitofp i32 [[ACC]] to float +// DEFAULT-NEXT: [[ASFLOAT:%[0-9]+]] = fmul float [[CONV]], 0x3F00000000000000 +// DEFAULT-NEXT: ret float [[ASFLOAT]] + +float UFixedPointToFloat(unsigned _Accum a) { return a; } +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a.addr, align 4 +// DEFAULT-NEXT: [[CONV:%[a-z0-9]+]] = uitofp i32 [[ACC]] to float +// DEFAULT-NEXT: [[ASFLOAT:%[0-9]+]] = fmul float [[CONV]], 0x3EF0000000000000 +// DEFAULT-NEXT: ret float [[ASFLOAT]] + +void TestFloatToFixedPoint() { + float f = 2.5; + _Accum a = f; +// DEFAULT: [[FLOAT:%[0-9]+]] = load float, float* %f, align 4 +// DEFAULT-NEXT: [[ACC_AS_FLT:%[0-9]+]] = fmul float [[FLOAT]], 3.276800e+04 +// DEFAULT-NEXT: [[ACC:%[0-9]+]] = fptosi float [[ACC_AS_FLT]] to i32 +// DEFAULT-NEXT: store i32 [[ACC]], i32* %a, align 4 + + unsigned _Accum ua = f; +// DEFAULT: [[FLOAT:%[0-9]+]] = load float, float* %f, align 4 +// DEFAULT-NEXT: [[ACC_AS_FLT:%[0-9]+]] = fmul float [[FLOAT]], 6.553600e+04 +// DEFAULT-NEXT: [[ACC:%[0-9]+]] = fptoui float [[ACC_AS_FLT]] to i32 +// DEFAULT-NEXT: store i32 [[ACC]], i32* %ua, align 4 + + a = (_Accum)f; +// DEFAULT: [[FLOAT:%[0-9]+]] = load float, float* %f, align 4 +// DEFAULT-NEXT: [[ACC_AS_FLT:%[0-9]+]] = fmul float [[FLOAT]], 3.276800e+04 +// DEFAULT-NEXT: [[ACC:%[0-9]+]] = fptosi float [[ACC_AS_FLT]] to i32 +// DEFAULT-NEXT: store i32 [[ACC]], i32* %a, align 4 + + ua = f; +// DEFAULT: [[FLOAT:%[0-9]+]] = load float, float* %f, align 4 +// DEFAULT-NEXT: [[ACC_AS_FLT:%[0-9]+]] = fmul float [[FLOAT]], 6.553600e+04 +// DEFAULT-NEXT: [[ACC:%[0-9]+]] = fptoui float [[ACC_AS_FLT]] to i32 +// DEFAULT-NEXT: store i32 [[ACC]], i32* %ua, align 4 +} + +_Accum FloatToFixedPoint(float f) { return f; } +// DEFAULT: [[FLOAT:%[0-9]+]] = load float, float* %f.addr, align 4 +// DEFAULT-NEXT: [[ACC_AS_FLT:%[0-9]+]] = fmul float [[FLOAT]], 3.276800e+04 +// DEFAULT-NEXT: [[ACC:%[0-9]+]] = fptosi float [[ACC_AS_FLT]] to i32 +// DEFAULT-NEXT: ret i32 [[ACC]] + +unsigned _Accum FloatToUFixedPoint(float f) { return f; } +// DEFAULT: [[FLOAT:%[0-9]+]] = load float, float* %f.addr, align 4 +// DEFAULT-NEXT: [[ACC_AS_FLT:%[0-9]+]] = fmul float [[FLOAT]], 6.553600e+04 +// DEFAULT-NEXT: [[ACC:%[0-9]+]] = fptoui float [[ACC_AS_FLT]] to i32 +// DEFAULT-NEXT: ret i32 [[ACC]] + +void TestFixedPointCastDown() { + long _Accum la = 2.5lk; + _Accum a = la; +// DEFAULT: [[LACC:%[0-9]+]] = load i64, i64* %la, align 8 +// DEFAULT-NEXT: [[ACC_AS_I64:%[0-9]+]] = ashr i64 [[LACC]], 16 +// DEFAULT-NEXT: [[ACC:%[0-9]+]] = trunc i64 [[ACC_AS_I64]] to i32 +// DEFAULT-NEXT: store i32 [[ACC]], i32* %a, align 4 + + a = (_Accum)la; +// DEFAULT: [[LACC:%[0-9]+]] = load i64, i64* %la, align 8 +// DEFAULT-NEXT: [[ACC_AS_I64:%[0-9]+]] = ashr i64 [[LACC]], 16 +// DEFAULT-NEXT: [[ACC:%[0-9]+]] = trunc i64 [[ACC_AS_I64]] to i32 +// DEFAULT-NEXT: store i32 [[ACC]], i32* %a, align 4 + + short _Accum sa = a; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a, align 4 +// DEFAULT-NEXT: [[SACC_AS_I32:%[0-9]+]] = ashr i32 [[ACC]], 8 +// DEFAULT-NEXT: [[SACC:%[0-9]+]] = trunc i32 [[SACC_AS_I32]] to i16 +// DEFAULT-NEXT: store i16 [[SACC]], i16* %sa, align 2 + + sa = (short _Accum)a; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a, align 4 +// DEFAULT-NEXT: [[SACC_AS_I32:%[0-9]+]] = ashr i32 [[ACC]], 8 +// DEFAULT-NEXT: [[SACC:%[0-9]+]] = trunc i32 [[SACC_AS_I32]] to i16 +// DEFAULT-NEXT: store i16 [[SACC]], i16* %sa, align 2 +} + +void TestFixedPointCastUp() { + short _Accum sa = 2.5hk; + _Accum a = sa; +// DEFAULT: [[SACC:%[0-9]+]] = load i16, i16* %sa, align 2 +// DEFAULT-NEXT: [[SACC_AS_I32:%[0-9]+]] = sext i16 [[SACC]] to i32 +// DEFAULT-NEXT: [[ACC:%[0-9]+]] = shl i32 [[SACC_AS_I32]], 8 +// DEFAULT-NEXT: store i32 [[ACC]], i32* %a, align 4 + + long _Accum la = a; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a, align 4 +// DEFAULT-NEXT: [[ACC_AS_I64:%[0-9]+]] = sext i32 [[ACC]] to i64 +// DEFAULT-NEXT: [[LACC:%[0-9]+]] = shl i64 [[ACC_AS_I64]], 16 +// DEFAULT-NEXT: store i64 [[LACC]], i64* %la, align 8 + + a = (_Accum)sa; +// DEFAULT: [[SACC:%[0-9]+]] = load i16, i16* %sa, align 2 +// DEFAULT-NEXT: [[SACC_AS_I32:%[0-9]+]] = sext i16 [[SACC]] to i32 +// DEFAULT-NEXT: [[ACC:%[0-9]+]] = shl i32 [[SACC_AS_I32]], 8 +// DEFAULT-NEXT: store i32 [[ACC]], i32* %a, align 4 + + la = (long _Accum)a; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a, align 4 +// DEFAULT-NEXT: [[ACC_AS_I64:%[0-9]+]] = sext i32 [[ACC]] to i64 +// DEFAULT-NEXT: [[LACC:%[0-9]+]] = shl i64 [[ACC_AS_I64]], 16 +// DEFAULT-NEXT: store i64 [[LACC]], i64* %la, align 8 +} + +void TestFixedPointCastSignedness() { + _Accum a = 2.5k; + unsigned _Accum ua = a; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a, align 4 +// DEFAULT-NEXT: [[UACC:%[0-9]+]] = shl i32 [[ACC]], 1 +// DEFAULT-NEXT: store i32 [[UACC]], i32* %ua, align 4 + + a = ua; +// DEFAULT: [[UACC:%[0-9]+]] = load i32, i32* %ua, align 4 +// DEFAULT-NEXT: [[ACC:%[0-9]+]] = lshr i32 [[UACC]], 1 +// DEFAULT-NEXT: store i32 [[ACC]], i32* %a, align 4 + + ua = (unsigned _Accum)a; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a, align 4 +// DEFAULT-NEXT: [[UACC:%[0-9]+]] = shl i32 [[ACC]], 1 +// DEFAULT-NEXT: store i32 [[UACC]], i32* %ua, align 4 + + a = (_Accum)ua; +// DEFAULT: [[UACC:%[0-9]+]] = load i32, i32* %ua, align 4 +// DEFAULT-NEXT: [[ACC:%[0-9]+]] = lshr i32 [[UACC]], 1 +// DEFAULT-NEXT: store i32 [[ACC]], i32* %a, align 4 +} + +void TestFixedPointCastSaturation() { + _Accum a = 2.5k; + _Sat _Accum sat_a = a; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a, align 4 +// DEFAULT-NEXT: store i32 [[ACC]], i32* %sat_a, align 4 + + a = sat_a; +// DEFAULT: [[SAT_ACC:%[0-9]+]] = load i32, i32* %sat_a, align 4 +// DEFAULT-NEXT: store i32 [[SAT_ACC]], i32* %a, align 4 + + sat_a = (_Sat _Accum)a; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a, align 4 +// DEFAULT-NEXT: store i32 [[ACC]], i32* %sat_a, align 4 + + a = (_Accum)sat_a; +// DEFAULT: [[SAT_ACC:%[0-9]+]] = load i32, i32* %sat_a, align 4 +// DEFAULT-NEXT: store i32 [[SAT_ACC]], i32* %a, align 4 +} + +void TestFixedPointCastBetFractAccum() { + _Accum a = 0.5k; + _Fract f = a; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a, align 4 +// DEFAULT-NEXT: [[FRACT:%[0-9]+]] = trunc i32 [[ACC]] 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: [[ACC:%[0-9]+]] = sext i16 [[FRACT]] to i32 +// DEFAULT-NEXT: store i32 [[ACC]], i32* %a, align 4 +} + +void TestIntToSaturatedFixedPoint() { + int i = 2; + unsigned int ui = 2; + + _Sat _Accum sat_a = i; +// DEFAULT: [[INT:%[0-9]+]] = load i32, i32* %i, align 4 +// DEFAULT-NEXT: [[SAT_A:%[0-9]+]] = shl i32 [[INT]], 15 +// DEFAULT-NEXT: [[EXT_INT:%[0-9]+]] = sext i32 [[INT]] to i33 +// DEFAULT-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i33 [[EXT_INT]], 65535 +// DEFAULT-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i33 [[EXT_INT]], -65536 +// DEFAULT-NEXT: [[RESULT1:%[0-9]+]] = select i1 [[USE_MIN]], i32 -2147483648, i32 [[SAT_A]] +// DEFAULT-NEXT: [[RESULT2:%[0-9]+]] = select i1 [[USE_MAX]], i32 2147483647, i32 [[RESULT1]] +// DEFAULT-NEXT: store i32 [[RESULT2]], i32* %sat_a, align 4 + + sat_a = ui; +// DEFAULT: [[INT:%[0-9]+]] = load i32, i32* %ui, align 4 +// DEFAULT-NEXT: [[SAT_A:%[0-9]+]] = shl i32 [[INT]], 15 +// DEFAULT-NEXT: [[EXT_INT:%[0-9]+]] = zext i32 [[INT]] to i33 +// DEFAULT-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i33 [[EXT_INT]], 65535 +// DEFAULT-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i33 [[EXT_INT]], -65536 +// DEFAULT-NEXT: [[RESULT1:%[0-9]+]] = select i1 [[USE_MIN]], i32 -2147483648, i32 [[SAT_A]] +// DEFAULT-NEXT: [[RESULT2:%[0-9]+]] = select i1 [[USE_MAX]], i32 2147483647, i32 [[RESULT1]] +// DEFAULT-NEXT: store i32 [[RESULT2]], i32* %sat_a, align 4 + + _Sat unsigned _Accum sat_ua = i; +// DEFAULT: [[INT:%[0-9]+]] = load i32, i32* %i, align 4 +// DEFAULT-NEXT: [[SAT_A:%[0-9]+]] = shl i32 [[INT]], 16 +// DEFAULT-NEXT: [[EXT_INT:%[0-9]+]] = sext i32 [[INT]] to i33 +// DEFAULT-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i33 [[EXT_INT]], 65535 +// DEFAULT-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i33 [[EXT_INT]], 0 +// DEFAULT-NEXT: [[RESULT1:%[0-9]+]] = select i1 [[USE_MIN]], i32 0, i32 [[SAT_A]] +// DEFAULT-NEXT: [[RESULT2:%[0-9]+]] = select i1 [[USE_MAX]], i32 -1, i32 [[RESULT1]] +// DEFAULT-NEXT: store i32 [[RESULT2]], i32* %sat_ua, align 4 + + sat_ua = ui; +// DEFAULT: [[INT:%[0-9]+]] = load i32, i32* %ui, align 4 +// DEFAULT-NEXT: [[SAT_A:%[0-9]+]] = shl i32 [[INT]], 16 +// DEFAULT-NEXT: [[EXT_INT:%[0-9]+]] = zext i32 [[INT]] to i33 +// DEFAULT-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i33 [[EXT_INT]], 65535 +// DEFAULT-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i33 [[EXT_INT]], 0 +// DEFAULT-NEXT: [[RESULT1:%[0-9]+]] = select i1 [[USE_MIN]], i32 0, i32 [[SAT_A]] +// DEFAULT-NEXT: [[RESULT2:%[0-9]+]] = select i1 [[USE_MAX]], i32 -1, i32 [[RESULT1]] +// DEFAULT-NEXT: store i32 [[RESULT2]], i32* %sat_ua, align 4 +} + +void TestFloatToSaturatedFixedPoint() { + float f = 2.5; + _Sat _Accum a = f; +// DEFAULT: [[FLT:%[0-9]+]] = load float, float* %f, align 4 +// DEFAULT-NEXT: [[ACC_AS_FLT:%[0-9]+]] = fmul float [[FLT]], 3.276800e+04 +// DEFAULT-NEXT: [[ACC:%[0-9]+]] = fptosi float [[ACC_AS_FLT]] to i32 +// DEFAULT-NEXT: [[USE_MAX:%[0-9]+]] = fcmp uge float [[FLT]], 6.553600e+04 +// DEFAULT-NEXT: [[USE_MIN:%[0-9]+]] = fcmp ule float [[FLT]], -6.553600e+04 +// DEFAULT-NEXT: [[RESULT:%[0-9]+]] = select i1 [[USE_MIN]], i32 -2147483648, i32 [[ACC]] +// DEFAULT-NEXT: [[RESULT2:%[0-9]+]] = select i1 [[USE_MAX]], i32 2147483647, i32 [[RESULT]] +// DEFAULT-NEXT: store i32 [[RESULT2]], i32* %a, align 4 + + _Sat unsigned _Accum ua = f; +// DEFAULT: [[FLT:%[0-9]+]] = load float, float* %f, align 4 +// DEFAULT-NEXT: [[ACC_AS_FLT:%[0-9]+]] = fmul float [[FLT]], 6.553600e+04 +// DEFAULT-NEXT: [[ACC:%[0-9]+]] = fptoui float [[ACC_AS_FLT]] to i32 +// DEFAULT-NEXT: [[USE_MAX:%[0-9]+]] = fcmp uge float [[FLT]], 6.553600e+04 +// DEFAULT-NEXT: [[USE_MIN:%[0-9]+]] = fcmp ule float [[FLT]], 0.000000e+00 +// DEFAULT-NEXT: [[RESULT:%[0-9]+]] = select i1 [[USE_MIN]], i32 0, i32 [[ACC]] +// DEFAULT-NEXT: [[RESULT2:%[0-9]+]] = select i1 [[USE_MAX]], i32 -1, i32 [[RESULT]] +// DEFAULT-NEXT: store i32 [[RESULT2]], i32* %ua, align 4 +} + +void TestUnsignedSameFBitsFixedPointToBool() { + unsigned _Accum a = 2.0k; + _Bool b = a; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a, align 4 +// DEFAULT-NEXT: [[NE:%[-1-9]+]] = icmp ne i32 [[ACC]], 0 +// DEFAULT-NEXT: [[FROMBOOL:%[a-z0-9]+]] = zext i1 [[NE]] to i8 +// DEFAULT-NEXT: store i8 [[FROMBOOL]], i8* %b, align 1 +// SAME: [[ACC:%[0-9]+]] = load i32, i32* %a, align 4 +// SAME-NEXT: [[MASKED_ACC:%[0-9]+]] = and i32 [[ACC]], 2147483647 +// SAME-NEXT: [[NE:%[0-9]+]] = icmp ne i32 [[MASKED_ACC]], 0 +// SAME-NEXT: [[FROMBOOL:%[a-z0-9]+]] = zext i1 [[NE]] to i8 +// SAME-NEXT: store i8 [[FROMBOOL]], i8* %b, align 1 +} + +void TestUnsignedSameFBitsFixedPointToInt() { + unsigned _Accum a = 2; + unsigned int i = a; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a, align 4 +// DEFAULT-NEXT: [[INT:%[0-9]+]] = lshr i32 [[ACC]], 16 +// DEFAULT-NEXT: store i32 [[INT]], i32* %i, align 4 +// SAME: [[ACC:%[0-9]+]] = load i32, i32* %a, align 4 +// SAME-NEXT: [[MASKED_ACC:%[0-9]+]] = and i32 [[ACC]], 2147483647 +// SAME-NEXT: [[INT:%[0-9]+]] = lshr i32 [[MASKED_ACC]], 15 +// SAME-NEXT: store i32 [[INT]], i32* %i, align 4 +} + +void TestUnsignedSameFBitsFixedPointToFloat() { + unsigned _Accum a = 2.5k; + float f = a; +// DEFAULT: [[ACC:%[0-9]+]] = load i32, i32* %a, align 4 +// DEFAULT-NEXT: [[CONV:%[a-z0-9]+]] = uitofp i32 [[ACC]] to float +// DEFAULT-NEXT: [[ASFLOAT:%[0-9]+]] = fmul float [[CONV]], 0x3EF0000000000000 +// DEFAULT-NEXT: store float [[ASFLOAT]], float* %f, align 4 +// SAME: [[ACC:%[0-9]+]] = load i32, i32* %a, align 4 +// SAME-NEXT: [[MASKED_ACC:%[0-9]+]] = and i32 [[ACC]], 2147483647 +// SAME-NEXT: [[FLT:%[0-9]+]] = uitofp i32 [[MASKED_ACC]] to float +// SAME-NEXT: [[FIXED_AS_FLT:%[0-9]+]] = fmul float [[FLT]], 0x3F00000000000000 +// SAME-NEXT: store float [[FIXED_AS_FLT]], float* %f, align 4 +} Index: test/Frontend/fixed_point_declarations.c =================================================================== --- test/Frontend/fixed_point_declarations.c +++ test/Frontend/fixed_point_declarations.c @@ -1,5 +1,4 @@ // RUN: %clang -ffixed-point -S -emit-llvm %s -o - --target=x86_64-linux | FileCheck %s -// RUN: %clang -ffixed-point -S -emit-llvm %s -o - --target=x86_64-scei-ps4-ubuntu-fast | FileCheck %s // Primary fixed point types signed short _Accum s_short_accum; // CHECK-DAG: @s_short_accum = {{.*}}global i16 0, align 2 @@ -111,3 +110,18 @@ unsigned short _Fract u_short_fract_eps = 0x1p-8uhr; // CHECK-DAG: @u_short_fract_eps = {{.*}}global i8 1, align 1 unsigned _Fract u_fract_eps = 0x1p-16ur; // CHECK-DAG: @u_fract_eps = {{.*}}global i16 1, align 2 unsigned long _Fract u_long_fract_eps = 0x1p-32ulr; // CHECK-DAG: @u_long_fract_eps = {{.*}}global i32 1, align 4 + +// Zero +short _Accum short_accum_zero = 0.0hk; // CHECK-DAG: @short_accum_zero = {{.*}}global i16 0, align 2 + _Accum accum_zero = 0.0k; // CHECK-DAG: @accum_zero = {{.*}}global i32 0, align 4 +long _Accum long_accum_zero = 0.0lk; // CHECK-DAG: @long_accum_zero = {{.*}}global i64 0, align 8 +unsigned short _Accum u_short_accum_zero = 0.0uhk; // CHECK-DAG: @u_short_accum_zero = {{.*}}global i16 0, align 2 +unsigned _Accum u_accum_zero = 0.0uk; // CHECK-DAG: @u_accum_zero = {{.*}}global i32 0, align 4 +unsigned long _Accum u_long_accum_zero = 0.0ulk; // CHECK-DAG: @u_long_accum_zero = {{.*}}global i64 0, align 8 + +short _Fract short_fract_zero = 0.0hr; // CHECK-DAG: @short_fract_zero = {{.*}}global i8 0, align 1 + _Fract fract_zero = 0.0r; // CHECK-DAG: @fract_zero = {{.*}}global i16 0, align 2 +long _Fract long_fract_zero = 0.0lr; // CHECK-DAG: @long_fract_zero = {{.*}}global i32 0, align 4 +unsigned short _Fract u_short_fract_zero = 0.0uhr; // CHECK-DAG: @u_short_fract_zero = {{.*}}global i8 0, align 1 +unsigned _Fract u_fract_zero = 0.0ur; // CHECK-DAG: @u_fract_zero = {{.*}}global i16 0, align 2 +unsigned long _Fract u_long_fract_zero = 0.0ulr; // CHECK-DAG: @u_long_fract_zero = {{.*}}global i32 0, align 4 Index: test/Frontend/fixed_point_errors.c =================================================================== --- test/Frontend/fixed_point_errors.c +++ test/Frontend/fixed_point_errors.c @@ -142,6 +142,8 @@ _Accum rk = 1.0rk; // expected-error{{invalid suffix 'rk' on integer constant}} _Accum rk = 1.0rr; // expected-error{{invalid suffix 'rr' on integer constant}} _Accum qk = 1.0qr; // expected-error{{invalid suffix 'qr' on integer constant}} +_Accum no_dec = 0k; // expected-error{{invalid suffix 'k' on integer constant}} +_Fract no_dec2 = 0r; // expected-error{{invalid suffix 'r' on integer constant}} /* Using wrong exponent notation */ _Accum dec_with_hex_exp1 = 0.1p10k; // expected-error{{invalid suffix 'p10k' on integer constant}} Index: test/Frontend/fixed_point_same_fbits.c =================================================================== --- test/Frontend/fixed_point_same_fbits.c +++ test/Frontend/fixed_point_same_fbits.c @@ -1,5 +1,5 @@ -// RUN: %clang -ffixed-point -S -emit-llvm -o - %s | FileCheck %s -check-prefix=DEFAULT -// RUN: %clang -ffixed-point -fsame-fbits -S -emit-llvm -o - %s | FileCheck %s -check-prefix=SAME +// RUN: %clang --target=x86_64-linux -ffixed-point -S -emit-llvm -o - %s | FileCheck %s -check-prefix=DEFAULT +// RUN: %clang --target=x86_64-linux -ffixed-point -fsame-fbits -S -emit-llvm -o - %s | FileCheck %s -check-prefix=SAME /* The scale for unsigned fixed point types should be the same as that of signed * fixed point types when -fsame-fbits is enabled. */