Index: include/clang/AST/ASTContext.h =================================================================== --- include/clang/AST/ASTContext.h +++ include/clang/AST/ASTContext.h @@ -2422,6 +2422,13 @@ /// \p LHS < \p RHS, return -1. int getFloatingTypeOrder(QualType LHS, QualType RHS) const; + /// \brief Compare the rank of the two specified fixed point types according + /// to 4.1.1 of ISO/IEC JTC1 SC22 WG14 N1169. + /// + /// If \p LHS > \p RHS, returns 1. If \p LHS == \p RHS, returns 0. If + /// \p LHS < \p RHS, return -1. + int getFixedPointTypeOrder(QualType LHS, QualType RHS) const; + /// \brief Return a real floating point or a complex type (based on /// \p typeDomain/\p typeSize). /// Index: include/clang/AST/OperationKinds.def =================================================================== --- include/clang/AST/OperationKinds.def +++ include/clang/AST/OperationKinds.def @@ -197,8 +197,13 @@ /// float f = i; CAST_OPERATION(IntegralToFloating) +/// CK_FixedPointCast - Cast between fixed point types. +/// short _Accum a = 1.2k; +/// (short _Fract) a +CAST_OPERATION(FixedPointCast) + /// CK_IntegralToFixedPoint - Integral to fixed point. -/// (short _Accum) i; +/// (short _Accum) i CAST_OPERATION(IntegralToFixedPoint) /// CK_FloatingToIntegral - Floating point to integral. Rounds Index: include/clang/AST/Type.h =================================================================== --- include/clang/AST/Type.h +++ include/clang/AST/Type.h @@ -2117,12 +2117,21 @@ // Return true if this is a fixed point type that is signed according // to ISO/IEC JTC1 SC22 WG14 N1169. [short _Fract, _Accum, long _Fract...] + // This type can also be saturated. bool isSignedFixedPointType() const; // Return true if this is a fixed point type that is unsigned according - // to ISO/IEC JTC1 SC22 WG14 N1169. + // to ISO/IEC JTC1 SC22 WG14 N1169. This type can also be saturated. bool isUnsignedFixedPointType() const; + // Return true if this is a fixed point type that is also an _Accum type. + // This type can also be saturated. + bool isAccumFixedPointType() const; + + // Return true if this is a fixed point type that is also an _Fract type. + // This type can also be saturated. + bool isFractFixedPointType() const; + /// Return true if this is not a variable sized type, /// according to the rules of C99 6.7.5p3. It is not legal to call this on /// incomplete types. @@ -6347,6 +6356,26 @@ return false; } +inline bool Type::isAccumFixedPointType() const { + if (const auto *BT = dyn_cast(CanonicalType)) { + return ((BT->getKind() >= BuiltinType::ShortAccum && + BT->getKind() <= BuiltinType::ULongAccum) || + (BT->getKind() >= BuiltinType::SatShortAccum && + BT->getKind() <= BuiltinType::SatULongAccum)); + } + return false; +} + +inline bool Type::isFractFixedPointType() const { + if (const auto *BT = dyn_cast(CanonicalType)) { + return ((BT->getKind() >= BuiltinType::ShortFract && + BT->getKind() <= BuiltinType::ULongFract) || + (BT->getKind() >= BuiltinType::SatShortFract && + BT->getKind() <= BuiltinType::SatULongFract)); + } + return false; +} + inline bool Type::isScalarType() const { if (const auto *BT = dyn_cast(CanonicalType)) return BT->getKind() > BuiltinType::Void && @@ -6542,6 +6571,12 @@ return cast(Decayed)->getPointeeType(); } +// Return the number of fractional bits in a fixed point type. +unsigned getFixedPointFBits(const QualType &Ty); + +// Return the number of integral bits in a fixed point type. +unsigned getFixedPointIBits(const QualType &Ty); + } // namespace clang #endif // LLVM_CLANG_AST_TYPE_H Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -60,6 +60,7 @@ #include "clang/Basic/TargetCXXABI.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/XRayLists.h" +#include "clang/Lex/Preprocessor.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/APSInt.h" #include "llvm/ADT/ArrayRef.h" @@ -111,6 +112,15 @@ Float16Rank, HalfRank, FloatRank, DoubleRank, LongDoubleRank, Float128Rank }; +enum FixedPointRank { + ShortFractRank, + FractRank, + LongFractRank, + ShortAccumRank, + AccumRank, + LongAccumRank +}; + RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const { if (!CommentsLoaded && ExternalSource) { ExternalSource->ReadComments(); @@ -5349,6 +5359,52 @@ } } +/// getFixedPointRank - Return a relative rank for fixed point types. +/// This routine will assert if passed a built-in type that isn't a fixed point. +static FixedPointRank getFixedPointRank(QualType T) { + assert(T->isFixedPointType() && T->getAs() && + "getFixedPointRank(): not a fixed point type"); + switch (T->getAs()->getKind()) { + default: + llvm_unreachable("getFixedPointRank(): not a fixed point type"); + case BuiltinType::ShortAccum: + case BuiltinType::UShortAccum: + case BuiltinType::SatShortAccum: + case BuiltinType::SatUShortAccum: + return ShortAccumRank; + + case BuiltinType::Accum: + case BuiltinType::UAccum: + case BuiltinType::SatAccum: + case BuiltinType::SatUAccum: + return AccumRank; + + case BuiltinType::LongAccum: + case BuiltinType::ULongAccum: + case BuiltinType::SatLongAccum: + case BuiltinType::SatULongAccum: + return LongAccumRank; + + case BuiltinType::ShortFract: + case BuiltinType::UShortFract: + case BuiltinType::SatShortFract: + case BuiltinType::SatUShortFract: + return ShortFractRank; + + case BuiltinType::Fract: + case BuiltinType::UFract: + case BuiltinType::SatFract: + case BuiltinType::SatUFract: + return FractRank; + + case BuiltinType::LongFract: + case BuiltinType::ULongFract: + case BuiltinType::SatLongFract: + case BuiltinType::SatULongFract: + return LongFractRank; + } +} + /// getFloatingTypeOfSizeWithinDomain - Returns a real floating /// point or a complex type (based on typeDomain/typeSize). /// 'typeDomain' is a real floating point or complex type. @@ -5394,6 +5450,19 @@ return -1; } +/// getFixedPointTypeOrder - Compare the rank of the two specified fixed +/// point types. +/// If LHS > RHS, return 1. If LHS == RHS, return 0. If +/// LHS < RHS, return -1. +int ASTContext::getFixedPointTypeOrder(QualType LHS, QualType RHS) const { + FixedPointRank LHSR = getFixedPointRank(LHS); + FixedPointRank RHSR = getFixedPointRank(RHS); + + if (LHSR == RHSR) return 0; + if (LHSR > RHSR) return 1; + return -1; +} + /// getIntegerRank - Return an integer conversion rank (C99 6.3.1.1p1). This /// routine will assert if passed a built-in type that isn't an integer or enum, /// or if it is not canonicalized. Index: lib/AST/Expr.cpp =================================================================== --- lib/AST/Expr.cpp +++ lib/AST/Expr.cpp @@ -1654,6 +1654,7 @@ case CK_ZeroToOCLQueue: case CK_IntToOCLSampler: case CK_IntegralToFixedPoint: + 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 @@ -7275,6 +7275,7 @@ } bool VisitUnaryOperator(const UnaryOperator *E); + bool VisitCastExpr(const CastExpr *E); }; } // end anonymous namespace @@ -8952,6 +8953,38 @@ return Success(Opcode == BO_EQ || Opcode == BO_LE || Opcode == BO_GE, E); } + if (LHSTy->isFixedPointType() && RHSTy->isFixedPointType()) { + APValue LHSVal, RHSVal; + + if (!FixedPointExprEvaluator(Info, LHSVal).Visit(E->getLHS())) { + return false; + } + + if (!FixedPointExprEvaluator(Info, RHSVal).Visit(E->getRHS())) { + return false; + } + + const APSInt& LHSInt = LHSVal.getInt(); + const APSInt& RHSInt = RHSVal.getInt(); + + switch (E->getOpcode()) { + default: + llvm_unreachable("Invalid binary operator!"); + case BO_LT: + return Success(LHSInt < RHSInt, E); + case BO_GT: + return Success(LHSInt > RHSInt, E); + case BO_LE: + return Success(LHSInt <= RHSInt, E); + case BO_GE: + return Success(LHSInt >= RHSInt, E); + case BO_EQ: + return Success(LHSInt == RHSInt, E); + case BO_NE: + return Success(LHSInt != RHSInt, E); + } + } + assert((!LHSTy->isIntegralOrEnumerationType() || !RHSTy->isIntegralOrEnumerationType()) && "DataRecursiveIntBinOpEvaluator should have handled integral types"); @@ -9127,6 +9160,8 @@ QualType SrcType = SubExpr->getType(); switch (E->getCastKind()) { + case CK_FixedPointCast: + llvm_unreachable("CK_FixedPointCast"); // TODO case CK_IntegralToFixedPoint: llvm_unreachable( "IntExprEvaluator::VisitCastExpr CK_IntegralToFixedPoint"); // TODO @@ -9339,6 +9374,70 @@ } } +/// HandleCast - This is used to evaluate implicit or explicit casts where the +/// result type is integer. +bool FixedPointExprEvaluator::VisitCastExpr(const CastExpr *E) { + const Expr *SubExpr = E->getSubExpr(); + QualType DestType = E->getType(); + QualType SrcType = SubExpr->getType(); + + switch (E->getCastKind()) { + default: + llvm_unreachable("unknown cast resulting in fixed point value"); + case CK_FixedPointCast: { + assert(DestType->isFixedPointType()); + assert(SrcType->isFixedPointType()); + + if (!Visit(SubExpr)) return false; + + assert(Result.isInt()); + + unsigned dest_fbits = getFixedPointFBits(DestType); + unsigned src_fbits = getFixedPointFBits(SrcType); + APSInt Val = Result.getInt(); + + Val.setIsSigned(DestType->isSignedFixedPointType()); + unsigned DestWidth = Info.Ctx.getIntWidth(DestType); + Val = Val.extOrTrunc(DestWidth); + + if (dest_fbits > src_fbits) { + Val <<= (dest_fbits - src_fbits); + } else if (dest_fbits < src_fbits) { + Val >>= (src_fbits - dest_fbits); + } + + return Success(Val, E); + } + + case CK_IntegralToFixedPoint: { + assert(DestType->isFixedPointType()); + assert(SrcType->isIntegralOrEnumerationType()); + + if (!Visit(SubExpr)) return false; + + assert(Result.isInt()); + + unsigned dest_fbits = getFixedPointFBits(DestType); + + APSInt Val = Result.getInt(); + + Val.setIsSigned(DestType->isSignedFixedPointType()); + Val <<= dest_fbits; + + unsigned DestWidth = Info.Ctx.getIntWidth(DestType); + Val = Val.extOrTrunc(DestWidth); + + return Success(Val, E); + } + + case CK_UserDefinedConversion: + case CK_LValueToRValue: + case CK_AtomicToNonAtomic: + case CK_NoOp: + return ExprEvaluatorBaseTy::VisitCastExpr(E); + } +} + //===----------------------------------------------------------------------===// // Float Evaluation //===----------------------------------------------------------------------===// @@ -9656,6 +9755,8 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) { switch (E->getCastKind()) { + case CK_FixedPointCast: + llvm_unreachable("CK_FixedPointCast"); // TODO case CK_IntegralToFixedPoint: llvm_unreachable( "ComplexExprEvaluator::VisitCastExpr CK_IntegralToFixedPoint"); // TODO Index: lib/AST/Type.cpp =================================================================== --- lib/AST/Type.cpp +++ lib/AST/Type.cpp @@ -29,6 +29,7 @@ #include "clang/AST/TypeVisitor.h" #include "clang/Basic/AddressSpaces.h" #include "clang/Basic/ExceptionSpecificationType.h" +#include "clang/Basic/FixedPoint.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/LangOptions.h" @@ -3992,3 +3993,107 @@ CXXRecordDecl *MemberPointerType::getMostRecentCXXRecordDecl() const { return getClass()->getAsCXXRecordDecl()->getMostRecentDecl(); } + +unsigned clang::getFixedPointFBits(const QualType &Ty) { + assert(Ty->isFixedPointType()); + + const auto *BT = Ty->getAs(); + switch (BT->getKind()) { + default: + llvm_unreachable("Not a fixed point type!"); + case BuiltinType::ShortAccum: + case BuiltinType::SatShortAccum: + return BUILTIN_SACCUM_FBIT; + + case BuiltinType::Accum: + case BuiltinType::SatAccum: + return BUILTIN_ACCUM_FBIT; + + case BuiltinType::LongAccum: + case BuiltinType::SatLongAccum: + return BUILTIN_LACCUM_FBIT; + + case BuiltinType::UShortAccum: + case BuiltinType::SatUShortAccum: + return BUILTIN_USACCUM_FBIT; + + case BuiltinType::UAccum: + case BuiltinType::SatUAccum: + return BUILTIN_UACCUM_FBIT; + + case BuiltinType::ULongAccum: + case BuiltinType::SatULongAccum: + return BUILTIN_ULACCUM_FBIT; + + case BuiltinType::ShortFract: + case BuiltinType::SatShortFract: + return BUILTIN_SFRACT_FBIT; + + case BuiltinType::Fract: + case BuiltinType::SatFract: + return BUILTIN_FRACT_FBIT; + + case BuiltinType::LongFract: + case BuiltinType::SatLongFract: + return BUILTIN_LFRACT_FBIT; + + case BuiltinType::UShortFract: + case BuiltinType::SatUShortFract: + return BUILTIN_USFRACT_FBIT; + + case BuiltinType::UFract: + case BuiltinType::SatUFract: + return BUILTIN_UFRACT_FBIT; + + case BuiltinType::ULongFract: + case BuiltinType::SatULongFract: + return BUILTIN_ULFRACT_FBIT; + } +} + +unsigned clang::getFixedPointIBits(const QualType &Ty) { + assert(Ty->isFixedPointType()); + + const auto *BT = Ty->getAs(); + switch (BT->getKind()) { + default: + llvm_unreachable("Not a fixed point type!"); + case BuiltinType::ShortAccum: + case BuiltinType::SatShortAccum: + return BUILTIN_SACCUM_IBIT; + + case BuiltinType::Accum: + case BuiltinType::SatAccum: + return BUILTIN_ACCUM_IBIT; + + case BuiltinType::LongAccum: + case BuiltinType::SatLongAccum: + return BUILTIN_LACCUM_IBIT; + + case BuiltinType::UShortAccum: + case BuiltinType::SatUShortAccum: + return BUILTIN_USACCUM_IBIT; + + case BuiltinType::UAccum: + case BuiltinType::SatUAccum: + return BUILTIN_UACCUM_IBIT; + + case BuiltinType::ULongAccum: + case BuiltinType::SatULongAccum: + return BUILTIN_ULACCUM_IBIT; + + case BuiltinType::ShortFract: + case BuiltinType::Fract: + case BuiltinType::LongFract: + case BuiltinType::UShortFract: + case BuiltinType::UFract: + case BuiltinType::ULongFract: + case BuiltinType::SatShortFract: + case BuiltinType::SatFract: + case BuiltinType::SatLongFract: + case BuiltinType::SatUShortFract: + case BuiltinType::SatUFract: + case BuiltinType::SatULongFract: + return 0; + } +} Index: lib/CodeGen/CGExpr.cpp =================================================================== --- lib/CodeGen/CGExpr.cpp +++ lib/CodeGen/CGExpr.cpp @@ -4044,6 +4044,8 @@ /// cast from scalar to union. LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) { switch (E->getCastKind()) { + case CK_FixedPointCast: + llvm_unreachable("CK_FixedPointCast"); // TODO case CK_IntegralToFixedPoint: llvm_unreachable( "CodeGenFunction::EmitCastLValue CK_IntegralToFixedPoint"); // TODO Index: lib/CodeGen/CGExprAgg.cpp =================================================================== --- lib/CodeGen/CGExprAgg.cpp +++ lib/CodeGen/CGExprAgg.cpp @@ -671,6 +671,8 @@ if (const auto *ECE = dyn_cast(E)) CGF.CGM.EmitExplicitCastExprType(ECE, &CGF); switch (E->getCastKind()) { + case CK_FixedPointCast: + llvm_unreachable("CK_FixedPointCast"); // TODO case CK_IntegralToFixedPoint: llvm_unreachable( "AggExprEmitter::VisitCastExpr CK_IntegralToFixedPoint"); // TODO Index: lib/CodeGen/CGExprComplex.cpp =================================================================== --- lib/CodeGen/CGExprComplex.cpp +++ lib/CodeGen/CGExprComplex.cpp @@ -446,6 +446,8 @@ ComplexPairTy ComplexExprEmitter::EmitCast(CastKind CK, Expr *Op, QualType DestTy) { switch (CK) { + case CK_FixedPointCast: + llvm_unreachable("CK_FixedPointCast"); // TODO case CK_IntegralToFixedPoint: llvm_unreachable( "ComplexExprEmitter::EmitCast CK_IntegralToFixedPoint"); // TODO Index: lib/CodeGen/CGExprConstant.cpp =================================================================== --- lib/CodeGen/CGExprConstant.cpp +++ lib/CodeGen/CGExprConstant.cpp @@ -686,6 +686,8 @@ Expr *subExpr = E->getSubExpr(); switch (E->getCastKind()) { + case CK_FixedPointCast: + llvm_unreachable("CK_FixedPointCast"); // TODO case CK_IntegralToFixedPoint: llvm_unreachable("VisitCastExpr CK_IntegralToFixedPoint"); // TODO case CK_ToUnion: { Index: lib/CodeGen/CGExprScalar.cpp =================================================================== --- lib/CodeGen/CGExprScalar.cpp +++ lib/CodeGen/CGExprScalar.cpp @@ -655,6 +655,42 @@ } } + // If either is a fixed point, we will need to cast up before multiplying. + if (Ops.Ty->isFixedPointType()) { + const auto *BinExpr = dyn_cast(Ops.E); + const Expr *LHS = BinExpr->getLHS(); + const Expr *RHS = BinExpr->getRHS(); + Value *LHSVal = Ops.LHS; + Value *RHSVal = Ops.RHS; + QualType LHSTy = LHS->getType(); + QualType RHSTy = RHS->getType(); + + // At least one side should be implicitely casted up to fixed point + assert(LHSTy->isFixedPointType() && RHSTy->isFixedPointType()); + assert(LHSTy == RHSTy); + + bool isSignedResult = + LHSTy->isSignedFixedPointType() || RHSTy->isSignedFixedPointType(); + + // Round up the bit widths to allocate enough space for calculating the + // result. + unsigned LHSWidth = CGF.getContext().getIntWidth(LHSTy); + unsigned bufferWidth = LHSWidth * 2; + if (bufferWidth > 128) { + bufferWidth = 128; + } + + LHSVal = Builder.CreateIntCast(LHSVal, Builder.getIntNTy(bufferWidth), + isSignedResult); + RHSVal = Builder.CreateIntCast(RHSVal, Builder.getIntNTy(bufferWidth), + isSignedResult); + + llvm::Value *MulResult = Builder.CreateMul(LHSVal, RHSVal); + MulResult = Builder.CreateAShr(MulResult, getFixedPointFBits(Ops.Ty)); + return Builder.CreateIntCast(MulResult, Builder.getIntNTy(LHSWidth), + isSignedResult); + } + if (Ops.Ty->isUnsignedIntegerType() && CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow) && !CanElideOverflowCheck(CGF.getContext(), Ops)) @@ -1084,7 +1120,8 @@ } if (isa(SrcTy)) { - bool InputSigned = SrcType->isSignedIntegerOrEnumerationType(); + bool InputSigned = (SrcType->isSignedIntegerOrEnumerationType() || + SrcType->isFixedPointType()); if (SrcType->isBooleanType() && TreatBooleanAsSigned) { InputSigned = true; } @@ -1777,56 +1814,48 @@ return Builder.CreateVectorSplat(NumElements, Elt, "splat"); } - case CK_IntegralToFixedPoint: { + case CK_FixedPointCast: { + // Casting between fixed point types involves separating the integral and + // fractional bits, potentially shifting them, then joining back together. assert(DestTy->isFixedPointType()); - assert(E->getType()->isIntegerType()); + assert(E->getType()->isFixedPointType()); - unsigned fbits; - const auto *BT = DestTy->getAs(); - switch (BT->getKind()) { - default: - llvm_unreachable("Not a fixed point type!"); - case BuiltinType::ShortAccum: - fbits = BUILTIN_SACCUM_FBIT; - break; - case BuiltinType::Accum: - fbits = BUILTIN_ACCUM_FBIT; - break; - case BuiltinType::LongAccum: - fbits = BUILTIN_LACCUM_FBIT; - break; - case BuiltinType::UShortAccum: - fbits = BUILTIN_USACCUM_FBIT; - break; - case BuiltinType::UAccum: - fbits = BUILTIN_UACCUM_FBIT; - break; - case BuiltinType::ULongAccum: - fbits = BUILTIN_ULACCUM_FBIT; - break; - case BuiltinType::ShortFract: - fbits = BUILTIN_SFRACT_FBIT; - break; - case BuiltinType::Fract: - fbits = BUILTIN_FRACT_FBIT; - break; - case BuiltinType::LongFract: - fbits = BUILTIN_LFRACT_FBIT; - break; - case BuiltinType::UShortFract: - fbits = BUILTIN_USFRACT_FBIT; - break; - case BuiltinType::UFract: - fbits = BUILTIN_UFRACT_FBIT; - break; - case BuiltinType::ULongFract: - fbits = BUILTIN_ULFRACT_FBIT; - break; + unsigned dest_fbits = getFixedPointFBits(DestTy); + unsigned src_fbits = getFixedPointFBits(E->getType()); + unsigned dest_ibits = getFixedPointIBits(DestTy); + unsigned src_ibits = getFixedPointIBits(E->getType()); + + llvm::Value *result = + EmitScalarConversion(Visit(E), E->getType(), DestTy, CE->getExprLoc()); + + // If the number of integral bits is decreasing, trim off any extra bits + // while retaining the sign. + if (dest_ibits < src_ibits) { + result = Builder.CreateShl(result, src_ibits - dest_ibits); + result = Builder.CreateAShr(result, src_ibits - dest_ibits); + } + + // Move the radix. For irrational numbers, there will be loss of precision + // using this method when the number of fbits increases since we will be + // right padding zeros. Precision can still be retained if we temporarily + // convert to a float and perform some floating point arithmetic, though + // this may cost more. Enable that if #pragma FX_FULL_PRECISION is provided. + if (dest_fbits > src_fbits) { + result = + EmitScalarConversion(result, E->getType(), DestTy, CE->getExprLoc()); + result = Builder.CreateShl(result, dest_fbits - src_fbits); + } else if (dest_fbits < src_fbits) { + result = Builder.CreateAShr(result, src_fbits - dest_fbits); } + return result; + } + case CK_IntegralToFixedPoint: { + assert(DestTy->isFixedPointType()); + assert(E->getType()->isIntegerType()); return Builder.CreateShl( EmitScalarConversion(Visit(E), E->getType(), DestTy, CE->getExprLoc()), - fbits, "fixed_point_shl"); + getFixedPointFBits(DestTy), "int_to_fixed"); } case CK_IntegralCast: @@ -2175,7 +2204,7 @@ break; } llvm::Value *amt = - llvm::ConstantInt::get(value->getType(), 1 << fbits, + llvm::ConstantInt::get(value->getType(), 1ULL << fbits, /*isSigned=*/type->isSignedFixedPointType()); if (isInc) { value = Builder.CreateAdd(value, amt, "fixed_point_post_inc"); @@ -2650,6 +2679,42 @@ } } + // If either is a fixed point, we will need to cast up before dividing. + if (Ops.Ty->isFixedPointType()) { + const auto *BinExpr = dyn_cast(Ops.E); + const Expr *LHS = BinExpr->getLHS(); + const Expr *RHS = BinExpr->getRHS(); + Value *LHSVal = Ops.LHS; + Value *RHSVal = Ops.RHS; + QualType LHSTy = LHS->getType(); + QualType RHSTy = RHS->getType(); + + // At least one side should be implicitely casted up to fixed point + assert(LHSTy->isFixedPointType() && RHSTy->isFixedPointType()); + assert(LHSTy == RHSTy); + + bool isSignedResult = + LHSTy->isSignedFixedPointType() || RHSTy->isSignedFixedPointType(); + + // Round up the bit widths to allocate enough space for calculating the + // result. + unsigned LHSWidth = CGF.getContext().getIntWidth(LHSTy); + unsigned bufferWidth = LHSWidth * 2; + if (bufferWidth > 128) { + bufferWidth = 128; + } + + LHSVal = Builder.CreateIntCast(LHSVal, Builder.getIntNTy(bufferWidth), + isSignedResult); + RHSVal = Builder.CreateIntCast(RHSVal, Builder.getIntNTy(bufferWidth), + isSignedResult); + LHSVal = Builder.CreateShl(LHSVal, getFixedPointFBits(LHSTy)); + + llvm::Value *DivResult = Builder.CreateSDiv(LHSVal, RHSVal); + return Builder.CreateIntCast(DivResult, Builder.getIntNTy(LHSWidth), + isSignedResult); + } + if (Ops.LHS->getType()->isFPOrFPVectorTy()) { llvm::Value *Val = Builder.CreateFDiv(Ops.LHS, Ops.RHS, "div"); if (CGF.getLangOpts().OpenCL && Index: lib/Edit/RewriteObjCFoundationAPI.cpp =================================================================== --- lib/Edit/RewriteObjCFoundationAPI.cpp +++ lib/Edit/RewriteObjCFoundationAPI.cpp @@ -1002,6 +1002,9 @@ if (const ImplicitCastExpr *ICE = dyn_cast(Arg)) { switch (ICE->getCastKind()) { + case CK_FixedPointCast: + llvm_unreachable( + "rewriteToNumericBoxedExpression CK_FixedPointCast"); // TODO case CK_IntegralToFixedPoint: llvm_unreachable( "rewriteToNumericBoxedExpression CK_IntegralToFixedPoint"); // TODO Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -1218,7 +1218,6 @@ CK_IntegralRealToComplex); return ComplexType; } - /// \brief Handle arithmetic conversion from integer to fixed point. Helper /// function of UsualArithmeticConversions() static QualType handleIntToFixedPointConversion(Sema &S, @@ -1248,7 +1247,29 @@ if (LHSFixed && RHSFixed) { // Cast up the smaller operand to the bigger - llvm_unreachable("Unhandled conversion between fixed point types"); // TODO + int order = S.Context.getFixedPointTypeOrder(LHSType, RHSType); + if (order > 0) { + RHS = S.ImpCastExprToType(RHS.get(), LHSType, CK_FixedPointCast); + return LHSType; + } else if (!order) { + bool LHSSigned = LHSType->isSignedFixedPointType(); + bool RHSSigned = RHSType->isSignedFixedPointType(); + if (LHSSigned && !RHSSigned) { + RHS = S.ImpCastExprToType(RHS.get(), LHSType, CK_FixedPointCast); + return LHSType; + } else if (!LHSSigned && RHSSigned) { + if (!IsCompAssign) + LHS = S.ImpCastExprToType(LHS.get(), RHSType, CK_FixedPointCast); + return RHSType; + } else { + assert(LHSType == RHSType); + return LHSType; + } + } else { + if (!IsCompAssign) + LHS = S.ImpCastExprToType(LHS.get(), RHSType, CK_FixedPointCast); + return RHSType; + } } else if (LHSFixed) { assert(RHSType->isIntegerType()); return handleIntToFixedPointConversion(S, LHS, RHS, LHSType, RHSType); @@ -1256,7 +1277,8 @@ assert(LHSType->isIntegerType()); return handleIntToFixedPointConversion(S, RHS, LHS, RHSType, LHSType); } else { - llvm_unreachable("Expected LHS and RHS to both be fixed point types."); + llvm_unreachable( + "Expected LHS and RHS to both be fixed point or integral types."); } } @@ -3509,9 +3531,9 @@ // Make sure the integral part fits into the integral bits we have. uint64_t max_int_val = 0; if (isSigned) { - max_int_val = 1 << (ibits - 1); // min signed is -2^(n-1) + max_int_val = 1ULL << (ibits - 1); // min signed is -2^(n-1) } else { - max_int_val = (1 << ibits) - 1; + max_int_val = (1ULL << ibits) - 1; } // TODO: What should be done for literals with no unsigned suffix whose @@ -3526,7 +3548,7 @@ } uint64_t fract_part_as_int = - static_cast(fabs(fract_part) * (1 << fbits)); + static_cast(fract_part * (1ULL << fbits)); uint64_t final_fixed_point_as_int = (int_part_as_int << fbits) + fract_part_as_int; @@ -9531,8 +9553,11 @@ // C99 6.5.7p2: Each of the operands shall have integer type. if (!LHSType->hasIntegerRepresentation() || - !RHSType->hasIntegerRepresentation()) - return InvalidOperands(Loc, LHS, RHS); + !RHSType->hasIntegerRepresentation()) { + if (!LHSType->isFixedPointType()) { + return InvalidOperands(Loc, LHS, RHS); + } + } // C++0x: Don't allow scoped enums. FIXME: Use something better than // hasIntegerRepresentation() above instead of this. Index: lib/StaticAnalyzer/Core/ExprEngineC.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -321,6 +321,8 @@ const LocationContext *LCtx = Pred->getLocationContext(); switch (CastE->getCastKind()) { + case CK_FixedPointCast: + llvm_unreachable("CK_FixedPointCast"); // TODO case CK_IntegralToFixedPoint: llvm_unreachable( "ExprEngine::VisitCast CK_IntegralToFixedPoint"); // TODO Index: test/Frontend/fixed_point_validation.c =================================================================== --- test/Frontend/fixed_point_validation.c +++ test/Frontend/fixed_point_validation.c @@ -74,6 +74,9 @@ assert(2 == s_accum); // CHECK: {{.*}} = icmp eq i16 256, {{.*}} + assert(2 == 2.0hk); + assert(2 != 2.01hk); + int x = 2; assert(s_accum == x); // CHECK: {{.*}} = load i32, i32* %x, align 4 @@ -128,6 +131,46 @@ // numbers. assert(2 == 2.001hk); // This is valid if SACCUM_FBITS == 7 + // Comparisons between fixed-point types + // Signed _Accum to signed _Accum types. + assert(2.5hk == 2.5k); + assert(2.5k == 2.5lk); + assert(-2.5hk == -2.5k); + assert(-2.5k == -2.5lk); + + // Unsigned _Accum to unigned _Accum + assert(2.5uhk == 2.5uk); + assert(2.5uk == 2.5ulk); + + // Signed _Fract to signed _Fract types. + assert(0.333hr != 0.333r); // Loss of precision since different fractional widths + assert(0.333r != 0.333lr); + assert(-0.333hr != -0.333r); + assert(-0.333r != -0.333lr); + + // Unsigned _Fract to unsigned _Fract types. + assert(0.333uhr != 0.333ur); + assert(0.333ur != 0.333ulr); + + // Signed _Accum to signed _Fract + assert(0.333hk == 0.333hr); + assert(0.333k == 0.333r); + assert(0.333lk == 0.333lr); + assert(0.333hk == 0.333r); // Although _Fract has higher precision, it gets casted up to + // short _Accum which (using default precisions) + // has fewer fractional bits. + + // Signed _Accum to unsigned _Fract + assert(0.333hk == 0.333uhr); + assert(0.333k == 0.333ur); + assert(0.333lk == 0.333ulr); + + // Signed _Accum to unsigned _Accum + assert(2.5hk == 2.5uhk); + assert(2.5k == 2.5uk); + assert(2.5lk == 2.5ulk); + + /**************** Unary operations ***************/ s_accum = 0.0hk; @@ -167,4 +210,58 @@ assert(+s_fract == s_fract); assert(+s_fract2 == s_fract2); // s_fract2 is negative assert(-s_fract == s_fract2); + + /**************** Binary operations ***************/ + + // Addition + s_accum = 3.0hk; + short _Accum s_accum_sum = s_accum + s_accum2; + assert(s_accum_sum == 5); + assert(s_fract + s_fract2 == 0); + + // Subtraction + short _Accum s_accum_diff = s_accum - s_accum2; + assert(s_accum_diff == 1); + assert(s_accum2 - s_accum == -1); + + // Multiplication + short _Accum s_accum_mul = s_accum * s_accum2; + assert(s_accum_mul == 6); + assert(2.0hk * 3.0hk == 6); + assert(2.0hk * 3 == 6); + assert(2.5hk * 3 == 7.5k); + assert(-2.5hk * 3 == -7.5lk); + assert(3 * -2.5hk == -7.5hk); + assert(-2.5hk * 0 == 0); + + // Division + const short _Accum s_accum3 = 2.5hk; + short _Accum s_accum_div = s_accum3 / s_accum2; + assert(s_accum_div == 1.25hk); + assert(5.0hk / s_accum3 == 2); + assert(-5.0hk / s_accum3 == -2); + assert(9.9k / 3.3k == 3); + assert(9.9hk / 3.3k != 3); // We lose precision when converting between types of different + // fractional width. + assert(6.75hk / 2.25k == 3); // Unless the fractional part can be evenly represented with + // sums of powers of 2. + assert(0 / 2.0hk == 0); + + // Left shift + short _Accum s_accum_shl = s_accum2 << 3; + assert(s_accum_shl == 16); + assert(1.0hk << 3 == 8); + assert(-1.0hk << 3 == -8); + assert(1.5k << 1 == 3); // LShift is equivalent to multiplying by 2 + assert(-1.25hk << 2 == -5); + + // Right shift + const signed short _Accum s_accum4 = 16.0hk; + short _Accum s_accum_shr = s_accum4 >> 3; + assert(s_accum_shr == 2); + assert(s_accum_shr >> 1 == 1); + assert(s_accum_shr >> 2 == 0.5hr); // RShift is equivalent to dividing by 2 + assert(5.0hk >> 2 == 1.25hk); + assert(-5.0hk >> 2 == -1.25k); + assert(0.0hr >> 2 == 0); }