Index: clang/include/clang/Basic/FixedPoint.h =================================================================== --- clang/include/clang/Basic/FixedPoint.h +++ clang/include/clang/Basic/FixedPoint.h @@ -32,6 +32,7 @@ /// in the value this represents is treaded as padding. class FixedPointSemantics { public: + FixedPointSemantics() = default; FixedPointSemantics(unsigned Width, unsigned Scale, bool IsSigned, bool IsSaturated, bool HasUnsignedPadding) : Width(Width), Scale(Scale), IsSigned(IsSigned), @@ -94,6 +95,7 @@ /// native IR support. class APFixedPoint { public: + APFixedPoint() = default; APFixedPoint(const llvm::APInt &Val, const FixedPointSemantics &Sema) : Val(Val, !Sema.isSigned()), Sema(Sema) { assert(Val.getBitWidth() == Sema.getWidth() && @@ -110,13 +112,19 @@ 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; } // Convert this number to match the semantics provided. APFixedPoint convert(const FixedPointSemantics &DstSema) 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. + APFixedPoint add(const APFixedPoint &Other) const; + APFixedPoint shr(unsigned Amt) const { return APFixedPoint(Val >> Amt, Sema); - } + } APFixedPoint shl(unsigned Amt) const { return APFixedPoint(Val << Amt, Sema); 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,10 @@ EvalInfo &Info); static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result); +/// Evaluate an integer or fixed point expression into an APFixedPoint. +static bool EvaluateFixedPointOrInteger(const Expr *E, APFixedPoint &Result, + EvalInfo &Info); + //===----------------------------------------------------------------------===// // Misc utilities //===----------------------------------------------------------------------===// @@ -7523,6 +7528,10 @@ return Success(V.getInt(), E); } + bool Success(const APFixedPoint &V, const Expr *E) { + return Success(V.getValue(), E); + } + bool ZeroInitialization(const Expr *E) { return Success(0, E); } //===--------------------------------------------------------------------===// @@ -7533,7 +7542,9 @@ return Success(E->getValue(), E); } + bool VisitCastExpr(const CastExpr *E); bool VisitUnaryOperator(const UnaryOperator *E); + bool VisitBinaryOperator(const BinaryOperator *E); }; } // end anonymous namespace @@ -7565,6 +7576,28 @@ return true; } +static bool EvaluateFixedPointOrInteger(const Expr *E, APFixedPoint &Result, + EvalInfo &Info) { + auto FXSema = Info.Ctx.getFixedPointSemantics(E->getType()); + if (E->getType()->isIntegerType()) { + APSInt Val; + if (!EvaluateInteger(E, Val, Info)) + return false; + Result = APFixedPoint(Val, FXSema); + return true; + } else if (E->getType()->isFixedPointType()) { + APValue Val; + if (!FixedPointExprEvaluator(Info, Val).Visit(E)) + return false; + if (!Val.isInt()) + return false; + + Result = APFixedPoint(Val.getInt(), FXSema); + return true; + } + 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. @@ -9905,6 +9938,50 @@ } } +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: { + APFixedPoint Src; + if (!EvaluateFixedPointOrInteger(SubExpr, Src, Info)) + return false; + APFixedPoint Result = Src.convert(DestFXSema); + return Success(Result, E); + } + default: + return Error(E); + } + llvm_unreachable("unknown cast resulting in fixed point value"); +} + +bool FixedPointExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { + const Expr *LHS = E->getLHS(); + const Expr *RHS = E->getRHS(); + FixedPointSemantics ResultFXSema = + Info.Ctx.getFixedPointSemantics(E->getType()); + + APFixedPoint 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.add(RHSFX).convert(ResultFXSema); + return Success(Result, E); + } + default: + return false; + } + llvm_unreachable("Should've exited before this"); +} + //===----------------------------------------------------------------------===// // Float Evaluation //===----------------------------------------------------------------------===// Index: clang/lib/Basic/FixedPoint.cpp =================================================================== --- clang/lib/Basic/FixedPoint.cpp +++ clang/lib/Basic/FixedPoint.cpp @@ -39,7 +39,7 @@ if (!(Masked == Mask || Masked == 0)) NewVal = NewVal.isNegative() ? Mask : ~Mask; - if (!DstSema.isSigned() && NewVal.isNegative()) + if (!DstSema.isSigned() && NewVal.isSigned() && NewVal.isNegative()) NewVal = 0; } @@ -137,4 +137,21 @@ ResultIsSaturated, ResultHasUnsignedPadding); } +APFixedPoint APFixedPoint::add(const APFixedPoint &Other) 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(); + + llvm::APInt Result; + if (CommonFXSema.isSaturated()) { + Result = CommonFXSema.isSigned() ? ThisVal.sadd_sat(OtherVal) + : ThisVal.uadd_sat(OtherVal); + } else + Result = ThisVal + OtherVal; + + return APFixedPoint(Result, CommonFXSema); +} + } // namespace clang Index: clang/test/Frontend/fixed_point_add.c =================================================================== --- clang/test/Frontend/fixed_point_add.c +++ clang/test/Frontend/fixed_point_add.c @@ -1,6 +1,49 @@ // 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 +// 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 short _Accum sa;