Skip to content

Commit 53f5c8b

Browse files
committed
[AST] Add fixed-point multiplication constant evaluation.
Reviewers: rjmccall, leonardchan, bjope Subscribers: cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D73186
1 parent eccf7fc commit 53f5c8b

File tree

4 files changed

+110
-0
lines changed

4 files changed

+110
-0
lines changed

clang/include/clang/Basic/FixedPoint.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ class APFixedPoint {
129129
// explanation of the Overflow parameter.
130130
APFixedPoint add(const APFixedPoint &Other, bool *Overflow = nullptr) const;
131131
APFixedPoint sub(const APFixedPoint &Other, bool *Overflow = nullptr) const;
132+
APFixedPoint mul(const APFixedPoint &Other, bool *Overflow = nullptr) const;
132133

133134
/// Perform a unary negation (-X) on this fixed point type, taking into
134135
/// account saturation if applicable.

clang/lib/AST/ExprConstant.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12939,6 +12939,15 @@ bool FixedPointExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
1293912939
return false;
1294012940
return Success(Result, E);
1294112941
}
12942+
case BO_Mul: {
12943+
bool AddOverflow, ConversionOverflow;
12944+
APFixedPoint Result = LHSFX.mul(RHSFX, &AddOverflow)
12945+
.convert(ResultFXSema, &ConversionOverflow);
12946+
if ((AddOverflow || ConversionOverflow) &&
12947+
!HandleOverflow(Info, E, Result, E->getType()))
12948+
return false;
12949+
return Success(Result, E);
12950+
}
1294212951
default:
1294312952
return false;
1294412953
}

clang/lib/Basic/FixedPoint.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,63 @@ APFixedPoint APFixedPoint::sub(const APFixedPoint &Other,
197197
return APFixedPoint(Result, CommonFXSema);
198198
}
199199

200+
APFixedPoint APFixedPoint::mul(const APFixedPoint &Other,
201+
bool *Overflow) const {
202+
auto CommonFXSema = Sema.getCommonSemantics(Other.getSemantics());
203+
APFixedPoint ConvertedThis = convert(CommonFXSema);
204+
APFixedPoint ConvertedOther = Other.convert(CommonFXSema);
205+
llvm::APSInt ThisVal = ConvertedThis.getValue();
206+
llvm::APSInt OtherVal = ConvertedOther.getValue();
207+
bool Overflowed = false;
208+
209+
// Widen the LHS and RHS so we can perform a full multiplication.
210+
unsigned Wide = CommonFXSema.getWidth() * 2;
211+
if (CommonFXSema.isSigned()) {
212+
ThisVal = ThisVal.sextOrSelf(Wide);
213+
OtherVal = OtherVal.sextOrSelf(Wide);
214+
} else {
215+
ThisVal = ThisVal.zextOrSelf(Wide);
216+
OtherVal = OtherVal.zextOrSelf(Wide);
217+
}
218+
219+
// Perform the full multiplication and downscale to get the same scale.
220+
//
221+
// Note that the right shifts here perform an implicit downwards rounding.
222+
// This rounding could discard bits that would technically place the result
223+
// outside the representable range. We interpret the spec as allowing us to
224+
// perform the rounding step first, avoiding the overflow case that would
225+
// arise.
226+
llvm::APSInt Result;
227+
if (CommonFXSema.isSigned())
228+
Result = ThisVal.smul_ov(OtherVal, Overflowed)
229+
.ashr(CommonFXSema.getScale());
230+
else
231+
Result = ThisVal.umul_ov(OtherVal, Overflowed)
232+
.lshr(CommonFXSema.getScale());
233+
assert(!Overflowed && "Full multiplication cannot overflow!");
234+
Result.setIsSigned(CommonFXSema.isSigned());
235+
236+
// If our result lies outside of the representative range of the common
237+
// semantic, we either have overflow or saturation.
238+
llvm::APSInt Max = APFixedPoint::getMax(CommonFXSema).getValue()
239+
.extOrTrunc(Wide);
240+
llvm::APSInt Min = APFixedPoint::getMin(CommonFXSema).getValue()
241+
.extOrTrunc(Wide);
242+
if (CommonFXSema.isSaturated()) {
243+
if (Result < Min)
244+
Result = Min;
245+
else if (Result > Max)
246+
Result = Max;
247+
} else
248+
Overflowed = Result < Min || Result > Max;
249+
250+
if (Overflow)
251+
*Overflow = Overflowed;
252+
253+
return APFixedPoint(Result.sextOrTrunc(CommonFXSema.getWidth()),
254+
CommonFXSema);
255+
}
256+
200257
void APFixedPoint::toString(llvm::SmallVectorImpl<char> &Str) const {
201258
llvm::APSInt Val = getValue();
202259
unsigned Scale = getScale();

clang/test/Frontend/fixed_point_mul.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,49 @@
11
// RUN: %clang_cc1 -ffixed-point -triple x86_64-unknown-linux-gnu -S -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,SIGNED
22
// 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
33

4+
// Multiplication between different fixed point types
5+
short _Accum sa_const = 2.0hk * 2.0hk; // CHECK-DAG: @sa_const = {{.*}}global i16 512, align 2
6+
_Accum a_const = 3.0hk * 2.0k; // CHECK-DAG: @a_const = {{.*}}global i32 196608, align 4
7+
long _Accum la_const = 4.0hk * 2.0lk; // CHECK-DAG: @la_const = {{.*}}global i64 17179869184, align 8
8+
short _Accum sa_const2 = 0.5hr * 2.0hk; // CHECK-DAG: @sa_const2 = {{.*}}global i16 128, align 2
9+
short _Accum sa_const3 = 0.5r * 3.0hk; // CHECK-DAG: @sa_const3 = {{.*}}global i16 192, align 2
10+
short _Accum sa_const4 = 0.5lr * 4.0hk; // CHECK-DAG: @sa_const4 = {{.*}}global i16 256, align 2
11+
12+
// Unsigned multiplication
13+
unsigned short _Accum usa_const = 1.0uhk * 2.0uhk;
14+
// CHECK-SIGNED-DAG: @usa_const = {{.*}}global i16 768, align 2
15+
// CHECK-UNSIGNED-DAG: @usa_const = {{.*}}global i16 384, align 2
16+
17+
// Unsigned * signed
18+
short _Accum sa_const5 = 20.0uhk * 3.0hk;
19+
// CHECK-DAG: @sa_const5 = {{.*}}global i16 7680, align 2
20+
21+
// Multiplication with negative number
22+
short _Accum sa_const6 = 0.5hr * (-2.0hk);
23+
// CHECK-DAG: @sa_const6 = {{.*}}global i16 -128, align 2
24+
25+
// Int multiplication
26+
unsigned short _Accum usa_const2 = 5 * 10.5uhk;
27+
// CHECK-SIGNED-DAG: @usa_const2 = {{.*}}global i16 640, align 2
28+
// CHECK-UNSIGNED-DAG: @usa_const2 = {{.*}}global i16 320, align 2
29+
short _Accum sa_const7 = 3 * (-0.5hk); // CHECK-DAG: @sa_const7 = {{.*}}global i16 -192, align 2
30+
short _Accum sa_const8 = 100 * (-2.0hk); // CHECK-DAG: @sa_const8 = {{.*}}global i16 -25600, align 2
31+
long _Fract lf_const = -0.25lr * 3; // CHECK-DAG: @lf_const = {{.*}}global i32 -1610612736, align 4
32+
33+
// Saturated multiplication
34+
_Sat short _Accum sat_sa_const = (_Sat short _Accum)128.0hk * 3.0hk;
35+
// CHECK-DAG: @sat_sa_const = {{.*}}global i16 32767, align 2
36+
_Sat unsigned short _Accum sat_usa_const = (_Sat unsigned short _Accum)128.0uhk * 128.0uhk;
37+
// CHECK-SIGNED-DAG: @sat_usa_const = {{.*}}global i16 65535, align 2
38+
// CHECK-UNSIGNED-DAG: @sat_usa_const = {{.*}}global i16 32767, align 2
39+
_Sat short _Accum sat_sa_const2 = (_Sat short _Accum)128.0hk * -128;
40+
// CHECK-DAG: @sat_sa_const2 = {{.*}}global i16 -32768, align 2
41+
_Sat unsigned short _Accum sat_usa_const2 = (_Sat unsigned short _Accum)128.0uhk * 30;
42+
// CHECK-SIGNED-DAG: @sat_usa_const2 = {{.*}}global i16 65535, align 2
43+
// CHECK-UNSIGNED-DAG: @sat_usa_const2 = {{.*}}global i16 32767, align 2
44+
_Sat unsigned short _Accum sat_usa_const3 = (_Sat unsigned short _Accum)0.5uhk * (-2);
45+
// CHECK-DAG: @sat_usa_const3 = {{.*}}global i16 0, align 2
46+
447
void SignedMultiplication() {
548
// CHECK-LABEL: SignedMultiplication
649
short _Accum sa;

0 commit comments

Comments
 (0)