diff --git a/clang/include/clang/Basic/FixedPoint.h b/clang/include/clang/Basic/FixedPoint.h --- a/clang/include/clang/Basic/FixedPoint.h +++ b/clang/include/clang/Basic/FixedPoint.h @@ -129,6 +129,7 @@ // explanation of the Overflow parameter. APFixedPoint add(const APFixedPoint &Other, bool *Overflow = nullptr) const; APFixedPoint sub(const APFixedPoint &Other, bool *Overflow = nullptr) const; + APFixedPoint mul(const APFixedPoint &Other, bool *Overflow = nullptr) const; /// Perform a unary negation (-X) on this fixed point type, taking into /// account saturation if applicable. diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -12939,6 +12939,15 @@ return false; return Success(Result, E); } + case BO_Mul: { + bool AddOverflow, ConversionOverflow; + APFixedPoint Result = LHSFX.mul(RHSFX, &AddOverflow) + .convert(ResultFXSema, &ConversionOverflow); + if ((AddOverflow || ConversionOverflow) && + !HandleOverflow(Info, E, Result, E->getType())) + return false; + return Success(Result, E); + } default: return false; } diff --git a/clang/lib/Basic/FixedPoint.cpp b/clang/lib/Basic/FixedPoint.cpp --- a/clang/lib/Basic/FixedPoint.cpp +++ b/clang/lib/Basic/FixedPoint.cpp @@ -197,6 +197,63 @@ return APFixedPoint(Result, CommonFXSema); } +APFixedPoint APFixedPoint::mul(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; + + // Widen the LHS and RHS so we can perform a full multiplication. + unsigned Wide = CommonFXSema.getWidth() * 2; + if (CommonFXSema.isSigned()) { + ThisVal = ThisVal.sextOrSelf(Wide); + OtherVal = OtherVal.sextOrSelf(Wide); + } else { + ThisVal = ThisVal.zextOrSelf(Wide); + OtherVal = OtherVal.zextOrSelf(Wide); + } + + // Perform the full multiplication and downscale to get the same scale. + // + // Note that the right shifts here perform an implicit downwards rounding. + // This rounding could discard bits that would technically place the result + // outside the representable range. We interpret the spec as allowing us to + // perform the rounding step first, avoiding the overflow case that would + // arise. + llvm::APSInt Result; + if (CommonFXSema.isSigned()) + Result = ThisVal.smul_ov(OtherVal, Overflowed) + .ashr(CommonFXSema.getScale()); + else + Result = ThisVal.umul_ov(OtherVal, Overflowed) + .lshr(CommonFXSema.getScale()); + assert(!Overflowed && "Full multiplication cannot overflow!"); + Result.setIsSigned(CommonFXSema.isSigned()); + + // If our result lies outside of the representative range of the common + // semantic, we either have overflow or saturation. + llvm::APSInt Max = APFixedPoint::getMax(CommonFXSema).getValue() + .extOrTrunc(Wide); + llvm::APSInt Min = APFixedPoint::getMin(CommonFXSema).getValue() + .extOrTrunc(Wide); + if (CommonFXSema.isSaturated()) { + if (Result < Min) + Result = Min; + else if (Result > Max) + Result = Max; + } else + Overflowed = Result < Min || Result > Max; + + if (Overflow) + *Overflow = Overflowed; + + return APFixedPoint(Result.sextOrTrunc(CommonFXSema.getWidth()), + CommonFXSema); +} + void APFixedPoint::toString(llvm::SmallVectorImpl &Str) const { llvm::APSInt Val = getValue(); unsigned Scale = getScale(); diff --git a/clang/test/Frontend/fixed_point_mul.c b/clang/test/Frontend/fixed_point_mul.c --- a/clang/test/Frontend/fixed_point_mul.c +++ b/clang/test/Frontend/fixed_point_mul.c @@ -1,6 +1,49 @@ // 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 +// Multiplication between different fixed point types +short _Accum sa_const = 2.0hk * 2.0hk; // CHECK-DAG: @sa_const = {{.*}}global i16 512, align 2 +_Accum a_const = 3.0hk * 2.0k; // CHECK-DAG: @a_const = {{.*}}global i32 196608, align 4 +long _Accum la_const = 4.0hk * 2.0lk; // CHECK-DAG: @la_const = {{.*}}global i64 17179869184, align 8 +short _Accum sa_const2 = 0.5hr * 2.0hk; // CHECK-DAG: @sa_const2 = {{.*}}global i16 128, align 2 +short _Accum sa_const3 = 0.5r * 3.0hk; // CHECK-DAG: @sa_const3 = {{.*}}global i16 192, align 2 +short _Accum sa_const4 = 0.5lr * 4.0hk; // CHECK-DAG: @sa_const4 = {{.*}}global i16 256, align 2 + +// Unsigned multiplication +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 = 20.0uhk * 3.0hk; +// CHECK-DAG: @sa_const5 = {{.*}}global i16 7680, align 2 + +// Multiplication with negative number +short _Accum sa_const6 = 0.5hr * (-2.0hk); +// CHECK-DAG: @sa_const6 = {{.*}}global i16 -128, align 2 + +// Int multiplication +unsigned short _Accum usa_const2 = 5 * 10.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 = 3 * (-0.5hk); // CHECK-DAG: @sa_const7 = {{.*}}global i16 -192, align 2 +short _Accum sa_const8 = 100 * (-2.0hk); // CHECK-DAG: @sa_const8 = {{.*}}global i16 -25600, align 2 +long _Fract lf_const = -0.25lr * 3; // CHECK-DAG: @lf_const = {{.*}}global i32 -1610612736, align 4 + +// Saturated multiplication +_Sat short _Accum sat_sa_const = (_Sat short _Accum)128.0hk * 3.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 -32768, align 2 +_Sat unsigned short _Accum sat_usa_const2 = (_Sat unsigned short _Accum)128.0uhk * 30; +// 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 SignedMultiplication() { // CHECK-LABEL: SignedMultiplication short _Accum sa;