Index: clang/include/clang/AST/APValue.h =================================================================== --- clang/include/clang/AST/APValue.h +++ clang/include/clang/AST/APValue.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_AST_APVALUE_H #define LLVM_CLANG_AST_APVALUE_H +#include "clang/Basic/FixedPoint.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/APSInt.h" @@ -43,6 +44,7 @@ Uninitialized, Int, Float, + FixedPoint, ComplexInt, ComplexFloat, LValue, @@ -175,6 +177,10 @@ explicit APValue(APFloat F) : Kind(Uninitialized) { MakeFloat(); setFloat(std::move(F)); } + explicit APValue(APFixedPoint FX) : Kind(Uninitialized) { + MakeFixedPoint(); + setFixedPoint(std::move(FX)); + } explicit APValue(const APValue *E, unsigned N) : Kind(Uninitialized) { MakeVector(); setVector(E, N); } @@ -233,6 +239,7 @@ bool isUninit() const { return Kind == Uninitialized; } bool isInt() const { return Kind == Int; } bool isFloat() const { return Kind == Float; } + bool isFixedPoint() const { return Kind == FixedPoint; } bool isComplexInt() const { return Kind == ComplexInt; } bool isComplexFloat() const { return Kind == ComplexFloat; } bool isLValue() const { return Kind == LValue; } @@ -265,6 +272,14 @@ return const_cast(this)->getFloat(); } + APFixedPoint &getFixedPoint() { + assert(isFixedPoint() && "Invalid accessor"); + return *(APFixedPoint *)(char *)Data.buffer; + } + const APFixedPoint &getFixedPoint() const { + return const_cast(this)->getFixedPoint(); + } + APSInt &getComplexIntReal() { assert(isComplexInt() && "Invalid accessor"); return ((ComplexAPSInt*)(char*)Data.buffer)->Real; @@ -406,6 +421,10 @@ assert(isFloat() && "Invalid accessor"); *(APFloat *)(char *)Data.buffer = std::move(F); } + void setFixedPoint(APFixedPoint FX) { + assert(isFixedPoint() && "Invalid accessor"); + *(APFixedPoint *)(char *)Data.buffer = std::move(FX); + } void setVector(const APValue *E, unsigned N) { assert(isVector() && "Invalid accessor"); ((Vec*)(char*)Data.buffer)->Elts = new APValue[N]; @@ -465,6 +484,12 @@ new ((void*)(char*)Data.buffer) APFloat(0.0); Kind = Float; } + void MakeFixedPoint() { + assert(isUninit() && "Bad state change"); + new ((void *)(char *)Data.buffer) + APFixedPoint(APSInt(1), FixedPointSemantics(1, 0, false, false, false)); + Kind = FixedPoint; + } void MakeVector() { assert(isUninit() && "Bad state change"); new ((void*)(char*)Data.buffer) Vec(); Index: clang/include/clang/AST/Expr.h =================================================================== --- clang/include/clang/AST/Expr.h +++ clang/include/clang/AST/Expr.h @@ -23,6 +23,7 @@ #include "clang/AST/TemplateBase.h" #include "clang/AST/Type.h" #include "clang/Basic/CharInfo.h" +#include "clang/Basic/FixedPoint.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/SyncScope.h" #include "clang/Basic/TypeTraits.h" @@ -611,6 +612,12 @@ EvaluateAsFloat(llvm::APFloat &Result, const ASTContext &Ctx, SideEffectsKind AllowSideEffects = SE_NoSideEffects) const; + /// EvaluateAsFloat - Return true if this is a constant which we can fold and + /// convert to a fixed point value. + bool EvaluateAsFixedPoint( + EvalResult &Result, const ASTContext &Ctx, + SideEffectsKind AllowSideEffects = SE_NoSideEffects) const; + /// isEvaluatable - Call EvaluateAsRValue to see if this expression can be /// constant folded without side-effects, but discard the result. bool isEvaluatable(const ASTContext &Ctx, Index: clang/include/clang/AST/Type.h =================================================================== --- clang/include/clang/AST/Type.h +++ clang/include/clang/AST/Type.h @@ -6848,6 +6848,8 @@ // Get the decimal string representation of a fixed point type, represented // as a scaled integer. +// TODO: At some point, we should change the arguments to instead just accept an +// APFixedPoint instead of APSInt and scale. void FixedPointValueToString(SmallVectorImpl &Str, llvm::APSInt Val, unsigned Scale); Index: clang/include/clang/Basic/DiagnosticGroups.td =================================================================== --- clang/include/clang/Basic/DiagnosticGroups.td +++ clang/include/clang/Basic/DiagnosticGroups.td @@ -61,6 +61,7 @@ def EnumConversion : DiagGroup<"enum-conversion">; def ImplicitIntConversion : DiagGroup<"implicit-int-conversion">; def ImplicitFloatConversion : DiagGroup<"implicit-float-conversion">; +def ImplicitFixedPointConversion : DiagGroup<"implicit-fixed-point-conversion">; def FloatOverflowConversion : DiagGroup<"float-overflow-conversion">; def FloatZeroConversion : DiagGroup<"float-zero-conversion">; Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3180,6 +3180,10 @@ "implicit truncation from %2 to bit-field changes value from %0 to %1">, InGroup; +def warn_impcast_fixed_point_range : Warning< + "implicit conversion from %0 cannot fit within the range of values for %1">, + InGroup; + def warn_impcast_literal_float_to_integer : Warning< "implicit conversion from %0 to %1 changes value from %2 to %3">, InGroup; Index: clang/include/clang/Basic/FixedPoint.h =================================================================== --- clang/include/clang/Basic/FixedPoint.h +++ clang/include/clang/Basic/FixedPoint.h @@ -18,6 +18,7 @@ #define LLVM_CLANG_BASIC_FIXEDPOINT_H #include "llvm/ADT/APSInt.h" +#include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" namespace clang { @@ -104,19 +105,34 @@ : APFixedPoint(llvm::APInt(Sema.getWidth(), Val, Sema.isSigned()), Sema) {} + // Zero initialization. + APFixedPoint(const FixedPointSemantics &Sema) : APFixedPoint(0, Sema) {} + llvm::APSInt getValue() const { return llvm::APSInt(Val, !Sema.isSigned()); } inline unsigned getWidth() const { return Sema.getWidth(); } inline unsigned getScale() const { return Sema.getScale(); } inline bool isSaturated() const { return Sema.isSaturated(); } inline bool isSigned() const { return Sema.isSigned(); } inline bool hasPadding() const { return Sema.hasUnsignedPadding(); } + FixedPointSemantics getSemantics() const { return Sema; } + + bool getBoolValue() const { return Val.getBoolValue(); } - // Convert this number to match the semantics provided. - APFixedPoint convert(const FixedPointSemantics &DstSema) const; + // Convert this number to match the semantics provided. If the overflow + // parameter is provided, set this value to true or false to indicate if this + // operation results in an overflow. + APFixedPoint convert(const FixedPointSemantics &DstSema, + bool *Overflow = nullptr) const; + + // Perform binary operations on a fixed point type. The resulting fixed point + // value will be in the common, full precision semantics that can represent + // the precision and ranges os both input values. See convert() for an + // explanation of the Overflow parameter. + APFixedPoint add(const APFixedPoint &Other, bool *Overflow = nullptr) const; APFixedPoint shr(unsigned Amt) const { return APFixedPoint(Val >> Amt, Sema); - } + } APFixedPoint shl(unsigned Amt) const { return APFixedPoint(Val << Amt, Sema); @@ -129,6 +145,13 @@ return Val >> getScale(); } + void toString(llvm::SmallVectorImpl &Str) const; + std::string toString() const { + llvm::SmallString<40> S; + toString(S); + return S.str(); + } + // If LHS > RHS, return 1. If LHS == RHS, return 0. If LHS < RHS, return -1. int compare(const APFixedPoint &Other) const; bool operator==(const APFixedPoint &Other) const { @@ -154,6 +177,12 @@ FixedPointSemantics Sema; }; +inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const APFixedPoint &FX) { + OS << FX.toString(); + return OS; +} + } // namespace clang #endif Index: clang/lib/AST/APValue.cpp =================================================================== --- clang/lib/AST/APValue.cpp +++ clang/lib/AST/APValue.cpp @@ -176,6 +176,10 @@ MakeFloat(); setFloat(RHS.getFloat()); break; + case FixedPoint: + MakeFixedPoint(); + setFixedPoint(RHS.getFixedPoint()); + break; case Vector: MakeVector(); setVector(((const Vec *)(const char *)RHS.Data.buffer)->Elts, @@ -233,6 +237,8 @@ ((APSInt*)(char*)Data.buffer)->~APSInt(); else if (Kind == Float) ((APFloat*)(char*)Data.buffer)->~APFloat(); + else if (Kind == FixedPoint) + ((APFixedPoint *)(char *)Data.buffer)->~APFixedPoint(); else if (Kind == Vector) ((Vec*)(char*)Data.buffer)->~Vec(); else if (Kind == ComplexInt) @@ -268,6 +274,8 @@ return getInt().needsCleanup(); case Float: return getFloat().needsCleanup(); + case FixedPoint: + return getFixedPoint().getValue().needsCleanup(); case ComplexFloat: assert(getComplexFloatImag().needsCleanup() == getComplexFloatReal().needsCleanup() && @@ -321,6 +329,9 @@ case Float: OS << "Float: " << GetApproxValue(getFloat()); return; + case FixedPoint: + OS << "FixedPoint : " << getFixedPoint(); + return; case Vector: OS << "Vector: "; getVectorElt(0).dump(OS); @@ -397,6 +408,9 @@ case APValue::Float: Out << GetApproxValue(getFloat()); return; + case APValue::FixedPoint: + Out << getFixedPoint(); + return; case APValue::Vector: { Out << '{'; QualType ElemTy = Ty->getAs()->getElementType(); Index: clang/lib/AST/ExprConstant.cpp =================================================================== --- clang/lib/AST/ExprConstant.cpp +++ clang/lib/AST/ExprConstant.cpp @@ -44,6 +44,7 @@ #include "clang/AST/StmtVisitor.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/Builtins.h" +#include "clang/Basic/FixedPoint.h" #include "clang/Basic/TargetInfo.h" #include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/raw_ostream.h" @@ -1613,6 +1614,13 @@ EvalInfo &Info); static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result); +/// Evaluate an integer or fixed point expression into an APResult. +static bool EvaluateFixedPointOrInteger(const Expr *E, APValue &Result, + EvalInfo &Info); + +/// Evaluate only a fixed point expression into an APResult. +static bool EvaluateFixedPoint(const Expr *E, APValue &Result, EvalInfo &Info); + //===----------------------------------------------------------------------===// // Misc utilities //===----------------------------------------------------------------------===// @@ -2027,6 +2035,9 @@ case APValue::Int: Result = Val.getInt().getBoolValue(); return true; + case APValue::FixedPoint: + Result = Val.getFixedPoint().getBoolValue(); + return true; case APValue::Float: Result = !Val.getFloat().isZero(); return true; @@ -7490,53 +7501,27 @@ FixedPointExprEvaluator(EvalInfo &info, APValue &result) : ExprEvaluatorBaseTy(info), Result(result) {} - bool Success(const llvm::APSInt &SI, const Expr *E, APValue &Result) { - assert(E->getType()->isFixedPointType() && "Invalid evaluation result."); - assert(SI.isSigned() == E->getType()->isSignedFixedPointType() && - "Invalid evaluation result."); - assert(SI.getBitWidth() == Info.Ctx.getIntWidth(E->getType()) && - "Invalid evaluation result."); - Result = APValue(SI); - return true; - } - bool Success(const llvm::APSInt &SI, const Expr *E) { - return Success(SI, E, Result); - } - - bool Success(const llvm::APInt &I, const Expr *E, APValue &Result) { - assert(E->getType()->isFixedPointType() && "Invalid evaluation result."); - assert(I.getBitWidth() == Info.Ctx.getIntWidth(E->getType()) && - "Invalid evaluation result."); - Result = APValue(APSInt(I)); - Result.getInt().setIsUnsigned(E->getType()->isUnsignedFixedPointType()); - return true; - } bool Success(const llvm::APInt &I, const Expr *E) { - return Success(I, E, Result); + return Success( + APFixedPoint(I, Info.Ctx.getFixedPointSemantics(E->getType())), E); } - bool Success(uint64_t Value, const Expr *E, APValue &Result) { - assert(E->getType()->isFixedPointType() && "Invalid evaluation result."); - Result = APValue(Info.Ctx.MakeIntValue(Value, E->getType())); - return true; - } bool Success(uint64_t Value, const Expr *E) { - return Success(Value, E, Result); - } - - bool Success(CharUnits Size, const Expr *E) { - return Success(Size.getQuantity(), E); + return Success( + APFixedPoint(Value, Info.Ctx.getFixedPointSemantics(E->getType())), E); } bool Success(const APValue &V, const Expr *E) { - if (V.isLValue() || V.isAddrLabelDiff()) { - Result = V; - return true; - } - return Success(V.getInt(), E); + return Success(V.getFixedPoint(), E); } - bool ZeroInitialization(const Expr *E) { return Success(0, E); } + bool Success(const APFixedPoint &V, const Expr *E) { + assert(E->getType()->isFixedPointType() && "Invalid evaluation result."); + assert(V.getWidth() == Info.Ctx.getIntWidth(E->getType()) && + "Invalid evaluation result."); + Result = APValue(V); + return true; + } //===--------------------------------------------------------------------===// // Visitor Methods @@ -7546,7 +7531,9 @@ return Success(E->getValue(), E); } + bool VisitCastExpr(const CastExpr *E); bool VisitUnaryOperator(const UnaryOperator *E); + bool VisitBinaryOperator(const BinaryOperator *E); }; } // end anonymous namespace @@ -7578,6 +7565,35 @@ return true; } +static bool EvaluateFixedPoint(const Expr *E, APValue &Result, EvalInfo &Info) { + if (E->getType()->isFixedPointType()) { + APValue Val; + if (!FixedPointExprEvaluator(Info, Val).Visit(E)) + return false; + if (!Val.isFixedPoint()) + return false; + + Result = APValue(Val.getFixedPoint()); + return true; + } + return false; +} + +static bool EvaluateFixedPointOrInteger(const Expr *E, APValue &Result, + EvalInfo &Info) { + auto FXSema = Info.Ctx.getFixedPointSemantics(E->getType()); + if (E->getType()->isIntegerType()) { + APSInt Val; + if (!EvaluateInteger(E, Val, Info)) + return false; + Result = APValue(APFixedPoint(Val, FXSema)); + return true; + } else if (E->getType()->isFixedPointType()) { + return EvaluateFixedPoint(E, Result, Info); + } + return false; +} + /// Check whether the given declaration can be directly converted to an integral /// rvalue. If not, no diagnostic is produced; there are other things we can /// try. @@ -9778,7 +9794,7 @@ APValue Val; if (!Evaluate(Val, Info, SubExpr)) return false; - return Success(Val.getInt().getBoolValue(), E); + return Success(Val.getFixedPoint().getBoolValue(), E); } case CK_IntegralCast: { @@ -9898,8 +9914,9 @@ return Visit(E->getSubExpr()); case UO_Minus: { if (!Visit(E->getSubExpr())) return false; - if (!Result.isInt()) return Error(E); - const APSInt &Value = Result.getInt(); + if (!Result.isFixedPoint()) + return Error(E); + const APSInt &Value = Result.getFixedPoint().getValue(); if (Value.isSigned() && Value.isMinSignedValue() && E->canOverflow()) { SmallString<64> S; FixedPointValueToString(S, Value, @@ -9918,6 +9935,56 @@ } } +bool FixedPointExprEvaluator::VisitCastExpr(const CastExpr *E) { + const Expr *SubExpr = E->getSubExpr(); + QualType DestType = E->getType(); + assert(DestType->isFixedPointType() && + "Expected destination type to be a fixed point type"); + auto DestFXSema = Info.Ctx.getFixedPointSemantics(DestType); + + switch (E->getCastKind()) { + case CK_FixedPointCast: { + APValue Src; + if (!EvaluateFixedPoint(SubExpr, Src, Info)) + return false; + bool Overflowed; + APFixedPoint Result = Src.getFixedPoint().convert(DestFXSema, &Overflowed); + if (Overflowed && !Info.noteUndefinedBehavior()) + return false; + return Success(Result, E); + } + case CK_NoOp: + case CK_LValueToRValue: + return ExprEvaluatorBaseTy::VisitCastExpr(E); + default: + return Error(E); + } +} + +bool FixedPointExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { + const Expr *LHS = E->getLHS(); + const Expr *RHS = E->getRHS(); + FixedPointSemantics ResultFXSema = + Info.Ctx.getFixedPointSemantics(E->getType()); + + APValue LHSFX, RHSFX; + if (!EvaluateFixedPointOrInteger(LHS, LHSFX, Info)) + return false; + if (!EvaluateFixedPointOrInteger(RHS, RHSFX, Info)) + return false; + + switch (E->getOpcode()) { + case BO_Add: { + APFixedPoint Result = + LHSFX.getFixedPoint().add(RHSFX.getFixedPoint()).convert(ResultFXSema); + return Success(Result, E); + } + default: + return false; + } + llvm_unreachable("Should've exited before this"); +} + //===----------------------------------------------------------------------===// // Float Evaluation //===----------------------------------------------------------------------===// @@ -10929,6 +10996,23 @@ return true; } +static bool EvaluateAsFixedPoint(const Expr *E, Expr::EvalResult &ExprResult, + const ASTContext &Ctx, + Expr::SideEffectsKind AllowSideEffects, + EvalInfo &Info) { + if (!E->getType()->isFixedPointType()) + return false; + + if (!::EvaluateAsRValue(E, ExprResult, Ctx, Info)) + return false; + + if (!ExprResult.Val.isFixedPoint() || + hasUnacceptableSideEffect(ExprResult, AllowSideEffects)) + return false; + + return true; +} + /// EvaluateAsRValue - Return true if this is a constant which we can fold using /// any crazy technique (that has nothing to do with language standards) that /// we want to. If this function returns true, it returns the folded constant @@ -10954,6 +11038,12 @@ return ::EvaluateAsInt(this, Result, Ctx, AllowSideEffects, Info); } +bool Expr::EvaluateAsFixedPoint(EvalResult &Result, const ASTContext &Ctx, + SideEffectsKind AllowSideEffects) const { + EvalInfo Info(Ctx, Result, EvalInfo::EM_IgnoreSideEffects); + return ::EvaluateAsFixedPoint(this, Result, Ctx, AllowSideEffects, Info); +} + bool Expr::EvaluateAsFloat(APFloat &Result, const ASTContext &Ctx, SideEffectsKind AllowSideEffects) const { if (!getType()->isRealFloatingType()) Index: clang/lib/AST/Type.cpp =================================================================== --- clang/lib/AST/Type.cpp +++ clang/lib/AST/Type.cpp @@ -4016,25 +4016,8 @@ void clang::FixedPointValueToString(SmallVectorImpl &Str, llvm::APSInt Val, unsigned Scale) { - if (Val.isSigned() && Val.isNegative() && Val != -Val) { - Val = -Val; - Str.push_back('-'); - } - - llvm::APSInt IntPart = Val >> Scale; - - // Add 4 digits to hold the value after multiplying 10 (the radix) - unsigned Width = Val.getBitWidth() + 4; - llvm::APInt FractPart = Val.zextOrTrunc(Scale).zext(Width); - llvm::APInt FractPartMask = llvm::APInt::getAllOnesValue(Scale).zext(Width); - llvm::APInt RadixInt = llvm::APInt(Width, 10); - - IntPart.toString(Str, /*radix=*/10); - Str.push_back('.'); - do { - (FractPart * RadixInt) - .lshr(Scale) - .toString(Str, /*radix=*/10, Val.isSigned()); - FractPart = (FractPart * RadixInt) & FractPartMask; - } while (FractPart != 0); + FixedPointSemantics FXSema(Val.getBitWidth(), Scale, Val.isSigned(), + /*isSaturated=*/false, + /*hasUnsignedPadding=*/false); + APFixedPoint(Val, FXSema).toString(Str); } Index: clang/lib/Basic/FixedPoint.cpp =================================================================== --- clang/lib/Basic/FixedPoint.cpp +++ clang/lib/Basic/FixedPoint.cpp @@ -16,11 +16,13 @@ namespace clang { -APFixedPoint APFixedPoint::convert(const FixedPointSemantics &DstSema) const { +APFixedPoint APFixedPoint::convert(const FixedPointSemantics &DstSema, + bool *Overflow) const { llvm::APSInt NewVal = Val; unsigned DstWidth = DstSema.getWidth(); unsigned DstScale = DstSema.getScale(); bool Upscaling = DstScale > getScale(); + bool Overflowed = false; if (Upscaling) { NewVal = NewVal.extend(NewVal.getBitWidth() + DstScale - getScale()); @@ -29,20 +31,33 @@ NewVal >>= (getScale() - DstScale); } - if (DstSema.isSaturated()) { - auto Mask = llvm::APInt::getBitsSetFrom( - NewVal.getBitWidth(), - std::min(DstScale + DstSema.getIntegralBits(), NewVal.getBitWidth())); - llvm::APInt Masked(NewVal & Mask); + auto Mask = llvm::APInt::getBitsSetFrom( + NewVal.getBitWidth(), + std::min(DstScale + DstSema.getIntegralBits(), NewVal.getBitWidth())); + llvm::APInt Masked(NewVal & Mask); - // Change in the bits above the sign - if (!(Masked == Mask || Masked == 0)) + // Change in the bits above the sign + if (!(Masked == Mask || Masked == 0)) { + // Found overflow in the bits above the sign + if (DstSema.isSaturated()) NewVal = NewVal.isNegative() ? Mask : ~Mask; + else + Overflowed = true; + } - if (!DstSema.isSigned() && NewVal.isNegative()) + // If the dst semantics are unsigned, but our value is signed and negative, we + // clamp to zero. + if (!DstSema.isSigned() && NewVal.isSigned() && NewVal.isNegative()) { + // Found negative overflow for unsigned result + if (DstSema.isSaturated()) NewVal = 0; + else + Overflowed = true; } + if (Overflow) + *Overflow = Overflowed; + NewVal = NewVal.extOrTrunc(DstWidth); NewVal.setIsSigned(DstSema.isSigned()); return APFixedPoint(NewVal, DstSema); @@ -137,4 +152,55 @@ ResultIsSaturated, ResultHasUnsignedPadding); } +APFixedPoint APFixedPoint::add(const APFixedPoint &Other, + bool *Overflow) const { + auto CommonFXSema = Sema.getCommonSemantics(Other.getSemantics()); + APFixedPoint ConvertedThis = convert(CommonFXSema); + APFixedPoint ConvertedOther = Other.convert(CommonFXSema); + llvm::APSInt ThisVal = ConvertedThis.getValue(); + llvm::APSInt OtherVal = ConvertedOther.getValue(); + bool Overflowed = false; + + llvm::APSInt Result; + if (CommonFXSema.isSaturated()) { + Result = CommonFXSema.isSigned() ? ThisVal.sadd_sat(OtherVal) + : ThisVal.uadd_sat(OtherVal); + } else { + Result = ThisVal + OtherVal; + Overflowed = ThisVal.isSigned() ? Result.slt(ThisVal) : Result.ult(ThisVal); + } + + if (Overflow) + *Overflow = Overflowed; + + return APFixedPoint(Result, CommonFXSema); +} + +void APFixedPoint::toString(llvm::SmallVectorImpl &Str) const { + llvm::APSInt Val = getValue(); + unsigned Scale = getScale(); + + if (Val.isSigned() && Val.isNegative() && Val != -Val) { + Val = -Val; + Str.push_back('-'); + } + + llvm::APSInt IntPart = Val >> Scale; + + // Add 4 digits to hold the value after multiplying 10 (the radix) + unsigned Width = Val.getBitWidth() + 4; + llvm::APInt FractPart = Val.zextOrTrunc(Scale).zext(Width); + llvm::APInt FractPartMask = llvm::APInt::getAllOnesValue(Scale).zext(Width); + llvm::APInt RadixInt = llvm::APInt(Width, 10); + + IntPart.toString(Str, /*radix=*/10); + Str.push_back('.'); + do { + (FractPart * RadixInt) + .lshr(Scale) + .toString(Str, /*radix=*/10, Val.isSigned()); + FractPart = (FractPart * RadixInt) & FractPartMask; + } while (FractPart != 0); +} + } // namespace clang Index: clang/lib/CodeGen/CGExprConstant.cpp =================================================================== --- clang/lib/CodeGen/CGExprConstant.cpp +++ clang/lib/CodeGen/CGExprConstant.cpp @@ -1873,6 +1873,9 @@ return ConstantLValueEmitter(*this, Value, DestType).tryEmit(); case APValue::Int: return llvm::ConstantInt::get(CGM.getLLVMContext(), Value.getInt()); + case APValue::FixedPoint: + return llvm::ConstantInt::get(CGM.getLLVMContext(), + Value.getFixedPoint().getValue()); case APValue::ComplexInt: { llvm::Constant *Complex[2]; Index: clang/lib/Sema/SemaChecking.cpp =================================================================== --- clang/lib/Sema/SemaChecking.cpp +++ clang/lib/Sema/SemaChecking.cpp @@ -11013,6 +11013,28 @@ return; } + if (Source->isFixedPointType()) { + // TODO: Only CK_FixedPointCast is supported now. The other valid casts + // should be accounted for here. + if (Target->isFixedPointType()) { + Expr::EvalResult Result; + if (E->EvaluateAsFixedPoint(Result, S.Context, + Expr::SE_AllowSideEffects)) { + APFixedPoint Value = Result.Val.getFixedPoint(); + APFixedPoint MaxVal = S.Context.getFixedPointMax(T); + APFixedPoint MinVal = S.Context.getFixedPointMin(T); + if (Value > MaxVal || Value < MinVal) { + S.DiagRuntimeBehavior(E->getExprLoc(), E, + S.PDiag(diag::warn_impcast_fixed_point_range) + << Value.toString() << T + << E->getSourceRange() + << clang::SourceRange(CC)); + return; + } + } + } + } + DiagnoseNullConversion(S, E, T, CC); S.DiscardMisalignedMemberAddress(Target, E); Index: clang/lib/Sema/SemaTemplate.cpp =================================================================== --- clang/lib/Sema/SemaTemplate.cpp +++ clang/lib/Sema/SemaTemplate.cpp @@ -6342,6 +6342,7 @@ } case APValue::AddrLabelDiff: return Diag(StartLoc, diag::err_non_type_template_arg_addr_label_diff); + case APValue::FixedPoint: case APValue::Float: case APValue::ComplexInt: case APValue::ComplexFloat: Index: clang/test/Frontend/fixed_point_add.c =================================================================== --- clang/test/Frontend/fixed_point_add.c +++ clang/test/Frontend/fixed_point_add.c @@ -1,5 +1,48 @@ -// RUN: %clang_cc1 -ffixed-point -S -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,SIGNED -// RUN: %clang_cc1 -ffixed-point -fpadding-on-unsigned-fixed-point -S -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,UNSIGNED +// RUN: %clang_cc1 -ffixed-point -triple x86_64-unknown-linux-gnu -S -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,SIGNED +// RUN: %clang_cc1 -ffixed-point -triple x86_64-unknown-linux-gnu -fpadding-on-unsigned-fixed-point -S -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,UNSIGNED + +// Addition between different fixed point types +short _Accum sa_const = 1.0hk + 2.0hk; // CHECK-DAG: @sa_const = {{.*}}global i16 384, align 2 +_Accum a_const = 1.0hk + 2.0k; // CHECK-DAG: @a_const = {{.*}}global i32 98304, align 4 +long _Accum la_const = 1.0hk + 2.0lk; // CHECK-DAG: @la_const = {{.*}}global i64 6442450944, align 8 +short _Accum sa_const2 = 0.5hr + 2.0hk; // CHECK-DAG: @sa_const2 = {{.*}}global i16 320, align 2 +short _Accum sa_const3 = 0.5r + 2.0hk; // CHECK-DAG: @sa_const3 = {{.*}}global i16 320, align 2 +short _Accum sa_const4 = 0.5lr + 2.0hk; // CHECK-DAG: @sa_const4 = {{.*}}global i16 320, align 2 + +// Unsigned addition +unsigned short _Accum usa_const = 1.0uhk + 2.0uhk; +// CHECK-SIGNED-DAG: @usa_const = {{.*}}global i16 768, align 2 +// CHECK-UNSIGNED-DAG: @usa_const = {{.*}}global i16 384, align 2 + +// Unsigned + signed +short _Accum sa_const5 = 1.0uhk + 2.0hk; +// CHECK-DAG: @sa_const5 = {{.*}}global i16 384, align 2 + +// Addition with negative number +short _Accum sa_const6 = 0.5hr + (-2.0hk); +// CHECK-DAG: @sa_const6 = {{.*}}global i16 -192, align 2 + +// Int addition +unsigned short _Accum usa_const2 = 2 + 0.5uhk; +// CHECK-SIGNED-DAG: @usa_const2 = {{.*}}global i16 640, align 2 +// CHECK-UNSIGNED-DAG: @usa_const2 = {{.*}}global i16 320, align 2 +short _Accum sa_const7 = 2 + (-0.5hk); // CHECK-DAG: @sa_const7 = {{.*}}global i16 192, align 2 +short _Accum sa_const8 = 257 + (-2.0hk); // CHECK-DAG: @sa_const8 = {{.*}}global i16 32640, align 2 +long _Fract lf_const = -0.5lr + 1; // CHECK-DAG: @lf_const = {{.*}}global i32 1073741824, align 4 + +// Saturated addition +_Sat short _Accum sat_sa_const = (_Sat short _Accum)128.0hk + 128.0hk; +// CHECK-DAG: @sat_sa_const = {{.*}}global i16 32767, align 2 +_Sat unsigned short _Accum sat_usa_const = (_Sat unsigned short _Accum)128.0uhk + 128.0uhk; +// CHECK-SIGNED-DAG: @sat_usa_const = {{.*}}global i16 65535, align 2 +// CHECK-UNSIGNED-DAG: @sat_usa_const = {{.*}}global i16 32767, align 2 +_Sat short _Accum sat_sa_const2 = (_Sat short _Accum)128.0hk + 128; +// CHECK-DAG: @sat_sa_const2 = {{.*}}global i16 32767, align 2 +_Sat unsigned short _Accum sat_usa_const2 = (_Sat unsigned short _Accum)128.0uhk + 128; +// CHECK-SIGNED-DAG: @sat_usa_const2 = {{.*}}global i16 65535, align 2 +// CHECK-UNSIGNED-DAG: @sat_usa_const2 = {{.*}}global i16 32767, align 2 +_Sat unsigned short _Accum sat_usa_const3 = (_Sat unsigned short _Accum)0.5uhk + (-2); +// CHECK-DAG: @sat_usa_const3 = {{.*}}global i16 0, align 2 void SignedAddition() { // CHECK-LABEL: SignedAddition Index: clang/test/Frontend/fixed_point_conversions.c =================================================================== --- clang/test/Frontend/fixed_point_conversions.c +++ clang/test/Frontend/fixed_point_conversions.c @@ -1,5 +1,39 @@ -// 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 +// RUN: %clang_cc1 -ffixed-point -triple x86_64-unknown-linux-gnu -S -emit-llvm %s -o - | FileCheck %s -check-prefix=DEFAULT +// RUN: %clang_cc1 -ffixed-point -triple x86_64-unknown-linux-gnu -S -emit-llvm %s -o - -fpadding-on-unsigned-fixed-point | FileCheck %s -check-prefix=SAME + +// Between different fixed point types +short _Accum sa_const = 2.5hk; // DEFAULT-DAG: @sa_const = {{.*}}global i16 320, align 2 +_Accum a_const = 2.5hk; // DEFAULT-DAG: @a_const = {{.*}}global i32 81920, align 4 +short _Accum sa_const2 = 2.5k; // DEFAULT-DAG: @sa_const2 = {{.*}}global i16 320, align 2 + +short _Accum sa_from_f_const = 0.5r; // DEFAULT-DAG: sa_from_f_const = {{.*}}global i16 64, align 2 +_Fract f_from_sa_const = 0.5hk; // DEFAULT-DAG: f_from_sa_const = {{.*}}global i16 16384, align 2 + +unsigned short _Accum usa_const = 2.5uk; +unsigned _Accum ua_const = 2.5uhk; +// DEFAULT-DAG: @usa_const = {{.*}}global i16 640, align 2 +// DEFAULT-DAG: @ua_const = {{.*}}global i32 163840, align 4 +// SAME-DAG: @usa_const = {{.*}}global i16 320, align 2 +// SAME-DAG: @ua_const = {{.*}}global i32 81920, align 4 + +// Signedness +unsigned short _Accum usa_const2 = 2.5hk; +// DEFAULT-DAG: @usa_const2 = {{.*}}global i16 640, align 2 +// SAME-DAG: @usa_const2 = {{.*}}global i16 320, align 2 +short _Accum sa_const3 = 2.5hk; // DEFAULT-DAG: @sa_const3 = {{.*}}global i16 320, align 2 + +// Overflow (this is undefined but allowed) +short _Accum sa_const4 = 256.0k; + +// Saturation +_Sat short _Accum sat_sa_const = 2.5hk; // DEFAULT-DAG: @sat_sa_const = {{.*}}global i16 320, align 2 +_Sat short _Accum sat_sa_const2 = 256.0k; // DEFAULT-DAG: @sat_sa_const2 = {{.*}}global i16 32767, align 2 +_Sat unsigned short _Accum sat_usa_const = -1.0hk; +// DEFAULT-DAG: @sat_usa_const = {{.*}}global i16 0, align 2 +// SAME-DAG: @sat_usa_const = {{.*}}global i16 0, align 2 +_Sat unsigned short _Accum sat_usa_const2 = 256.0k; +// DEFAULT-DAG: @sat_usa_const2 = {{.*}}global i16 -1, align 2 +// SAME-DAG: @sat_usa_const2 = {{.*}}global i16 32767, align 2 void TestFixedPointCastSameType() { _Accum a = 2.5k; Index: clang/test/Frontend/fixed_point_errors.c =================================================================== --- clang/test/Frontend/fixed_point_errors.c +++ clang/test/Frontend/fixed_point_errors.c @@ -232,3 +232,8 @@ auto auto_accum = 0k; // expected-error{{invalid suffix 'k' on integer constant}} // expected-warning@-1{{type specifier missing, defaults to 'int'}} } + +// Overflow +short _Accum sa_const = 256.0k; // expected-warning{{implicit conversion from 256.0 cannot fit within the range of values for 'short _Accum'}} +short _Fract sf_const = 1.0hk; // expected-warning{{implicit conversion from 1.0 cannot fit within the range of values for 'short _Fract'}} +unsigned _Accum ua_const = -1.0k; // expected-warning{{implicit conversion from -1.0 cannot fit within the range of values for 'unsigned _Accum'}}