Skip to content

Commit 1a1f9ce

Browse files
committed
[clang] Implement constexpr evaluation for __builtin_{add,sub}c
GCC has gained support for these multiprecision arithmetic builtins in `r14-1896-g2b4e0415ad6`, and although they aren't explicitly specified as such in the documentation, they are usable in a constexpr context. This commit adds constexpr evaluation support to Clang to match GCC's behavior. The implementation mirrors how the builtins are lowered to a pair of `u{add,sub}.with.overflow` operations and the carryout is set to 1 if either of those result in an overflow.
1 parent 5352c79 commit 1a1f9ce

File tree

4 files changed

+177
-10
lines changed

4 files changed

+177
-10
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3449,6 +3449,8 @@ The complete list of builtins are:
34493449
unsigned long __builtin_subcl (unsigned long x, unsigned long y, unsigned long carryin, unsigned long *carryout);
34503450
unsigned long long __builtin_subcll(unsigned long long x, unsigned long long y, unsigned long long carryin, unsigned long long *carryout);
34513451
3452+
These builtins can be used in constant expressions.
3453+
34523454
Checked Arithmetic Builtins
34533455
---------------------------
34543456
@@ -5173,6 +5175,11 @@ Intrinsics Support within Constant Expressions
51735175
51745176
The following builtin intrinsics can be used in constant expressions:
51755177
5178+
* ``__builtin_addc``
5179+
* ``__builtin_addcb``
5180+
* ``__builtin_addcl``
5181+
* ``__builtin_addcll``
5182+
* ``__builtin_addcs``
51765183
* ``__builtin_bitreverse8``
51775184
* ``__builtin_bitreverse16``
51785185
* ``__builtin_bitreverse32``
@@ -5219,6 +5226,11 @@ The following builtin intrinsics can be used in constant expressions:
52195226
* ``__builtin_rotateright16``
52205227
* ``__builtin_rotateright32``
52215228
* ``__builtin_rotateright64``
5229+
* ``__builtin_subc``
5230+
* ``__builtin_subcb``
5231+
* ``__builtin_subcl``
5232+
* ``__builtin_subcll``
5233+
* ``__builtin_subcs``
52225234
52235235
The following x86-specific intrinsics can be used in constant expressions:
52245236

clang/include/clang/Basic/Builtins.def

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1619,16 +1619,16 @@ BUILTIN(__builtin_assume, "vb", "nE")
16191619
BUILTIN(__builtin_assume_separate_storage, "vvCD*vCD*", "nE")
16201620

16211621
// Multiprecision Arithmetic Builtins.
1622-
BUILTIN(__builtin_addcb, "UcUcCUcCUcCUc*", "n")
1623-
BUILTIN(__builtin_addcs, "UsUsCUsCUsCUs*", "n")
1624-
BUILTIN(__builtin_addc, "UiUiCUiCUiCUi*", "n")
1625-
BUILTIN(__builtin_addcl, "ULiULiCULiCULiCULi*", "n")
1626-
BUILTIN(__builtin_addcll, "ULLiULLiCULLiCULLiCULLi*", "n")
1627-
BUILTIN(__builtin_subcb, "UcUcCUcCUcCUc*", "n")
1628-
BUILTIN(__builtin_subcs, "UsUsCUsCUsCUs*", "n")
1629-
BUILTIN(__builtin_subc, "UiUiCUiCUiCUi*", "n")
1630-
BUILTIN(__builtin_subcl, "ULiULiCULiCULiCULi*", "n")
1631-
BUILTIN(__builtin_subcll, "ULLiULLiCULLiCULLiCULLi*", "n")
1622+
BUILTIN(__builtin_addcb, "UcUcCUcCUcCUc*", "nE")
1623+
BUILTIN(__builtin_addcs, "UsUsCUsCUsCUs*", "nE")
1624+
BUILTIN(__builtin_addc, "UiUiCUiCUiCUi*", "nE")
1625+
BUILTIN(__builtin_addcl, "ULiULiCULiCULiCULi*", "nE")
1626+
BUILTIN(__builtin_addcll, "ULLiULLiCULLiCULLiCULLi*", "nE")
1627+
BUILTIN(__builtin_subcb, "UcUcCUcCUcCUc*", "nE")
1628+
BUILTIN(__builtin_subcs, "UsUsCUsCUsCUs*", "nE")
1629+
BUILTIN(__builtin_subc, "UiUiCUiCUiCUi*", "nE")
1630+
BUILTIN(__builtin_subcl, "ULiULiCULiCULiCULi*", "nE")
1631+
BUILTIN(__builtin_subcll, "ULLiULLiCULLiCULLiCULLi*", "nE")
16321632

16331633
// Checked Arithmetic Builtins for Security.
16341634
BUILTIN(__builtin_add_overflow, "b.", "ntE")

clang/lib/AST/ExprConstant.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12630,6 +12630,56 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
1263012630
return false;
1263112631
return Success(DidOverflow, E);
1263212632
}
12633+
case Builtin::BI__builtin_addcb:
12634+
case Builtin::BI__builtin_addcs:
12635+
case Builtin::BI__builtin_addc:
12636+
case Builtin::BI__builtin_addcl:
12637+
case Builtin::BI__builtin_addcll:
12638+
case Builtin::BI__builtin_subcb:
12639+
case Builtin::BI__builtin_subcs:
12640+
case Builtin::BI__builtin_subc:
12641+
case Builtin::BI__builtin_subcl:
12642+
case Builtin::BI__builtin_subcll: {
12643+
APSInt X, Y, CarryIn;
12644+
LValue CarryOut;
12645+
12646+
QualType ResultType = E->getArg(3)->getType()->getPointeeType();
12647+
if (!EvaluateInteger(E->getArg(0), X, Info) ||
12648+
!EvaluateInteger(E->getArg(1), Y, Info) ||
12649+
!EvaluateInteger(E->getArg(2), CarryIn, Info) ||
12650+
!EvaluatePointer(E->getArg(3), CarryOut, Info))
12651+
return false;
12652+
12653+
APInt Result;
12654+
bool DidOverflow1 = false;
12655+
bool DidOverflow2 = false;
12656+
12657+
switch (BuiltinOp) {
12658+
default:
12659+
llvm_unreachable("Invalid value for BuiltinOp");
12660+
case Builtin::BI__builtin_addcb:
12661+
case Builtin::BI__builtin_addcs:
12662+
case Builtin::BI__builtin_addc:
12663+
case Builtin::BI__builtin_addcl:
12664+
case Builtin::BI__builtin_addcll:
12665+
Result = X.uadd_ov(Y, DidOverflow1).uadd_ov(CarryIn, DidOverflow2);
12666+
break;
12667+
case Builtin::BI__builtin_subcb:
12668+
case Builtin::BI__builtin_subcs:
12669+
case Builtin::BI__builtin_subc:
12670+
case Builtin::BI__builtin_subcl:
12671+
case Builtin::BI__builtin_subcll:
12672+
Result = X.usub_ov(Y, DidOverflow1).usub_ov(CarryIn, DidOverflow2);
12673+
break;
12674+
}
12675+
12676+
APSInt DidOverflow(
12677+
APInt(Result.getBitWidth(), DidOverflow1 || DidOverflow2));
12678+
APValue DidOverflowVal(DidOverflow);
12679+
if (!handleAssignment(Info, E, CarryOut, ResultType, DidOverflowVal))
12680+
return false;
12681+
return Success(Result, E);
12682+
}
1263312683
}
1263412684
}
1263512685

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// RUN: %clang_cc1 -fsyntax-only -std=c++17 -ffreestanding -verify %s
2+
// expected-no-diagnostics
3+
4+
#include <limits.h>
5+
6+
template<typename T>
7+
struct Result {
8+
T value;
9+
T carry;
10+
constexpr bool operator==(const Result<T> &Other) {
11+
return value == Other.value && carry == Other.carry;
12+
}
13+
};
14+
15+
template<typename T>
16+
constexpr Result<T> add(T Lhs, T Rhs, T Carryin)
17+
{
18+
T Carryout = 0;
19+
if constexpr(__is_same(T, unsigned char))
20+
return { __builtin_addcb(Lhs, Rhs, Carryin, &Carryout), Carryout };
21+
else if constexpr(__is_same(T, unsigned short))
22+
return { __builtin_addcs(Lhs, Rhs, Carryin, &Carryout), Carryout };
23+
else if constexpr(__is_same(T, unsigned int))
24+
return { __builtin_addc(Lhs, Rhs, Carryin, &Carryout), Carryout };
25+
else if constexpr(__is_same(T, unsigned long))
26+
return { __builtin_addcl(Lhs, Rhs, Carryin, &Carryout), Carryout };
27+
else if constexpr(__is_same(T, unsigned long long))
28+
return { __builtin_addcll(Lhs, Rhs, Carryin, &Carryout), Carryout };
29+
}
30+
31+
static_assert(add<unsigned char>(0, 0, 0) == Result<unsigned char>{0, 0});
32+
static_assert(add<unsigned char>(0, 0, 1) == Result<unsigned char>{1, 0});
33+
static_assert(add<unsigned char>(UCHAR_MAX - 1, 1, 1) == Result<unsigned char>{0, 1});
34+
static_assert(add<unsigned char>(UCHAR_MAX, 1, 0) == Result<unsigned char>{0, 1});
35+
static_assert(add<unsigned char>(UCHAR_MAX, 1, 1) == Result<unsigned char>{1, 1});
36+
37+
static_assert(add<unsigned short>(0, 0, 0) == Result<unsigned short>{0, 0});
38+
static_assert(add<unsigned short>(0, 0, 1) == Result<unsigned short>{1, 0});
39+
static_assert(add<unsigned short>(USHRT_MAX - 1, 1, 1) == Result<unsigned short>{0, 1});
40+
static_assert(add<unsigned short>(USHRT_MAX, 1, 0) == Result<unsigned short>{0, 1});
41+
static_assert(add<unsigned short>(USHRT_MAX, 1, 1) == Result<unsigned short>{1, 1});
42+
43+
static_assert(add<unsigned int>(0, 0, 0) == Result<unsigned int>{0, 0});
44+
static_assert(add<unsigned int>(0, 0, 1) == Result<unsigned int>{1, 0});
45+
static_assert(add<unsigned int>(UINT_MAX - 1, 1, 1) == Result<unsigned int>{0, 1});
46+
static_assert(add<unsigned int>(UINT_MAX, 1, 0) == Result<unsigned int>{0, 1});
47+
static_assert(add<unsigned int>(UINT_MAX, 1, 1) == Result<unsigned int>{1, 1});
48+
49+
static_assert(add<unsigned long>(0, 0, 0) == Result<unsigned long>{0, 0});
50+
static_assert(add<unsigned long>(0, 0, 1) == Result<unsigned long>{1, 0});
51+
static_assert(add<unsigned long>(ULONG_MAX - 1, 1, 1) == Result<unsigned long>{0, 1});
52+
static_assert(add<unsigned long>(ULONG_MAX, 1, 0) == Result<unsigned long>{0, 1});
53+
static_assert(add<unsigned long>(ULONG_MAX, 1, 1) == Result<unsigned long>{1, 1});
54+
55+
static_assert(add<unsigned long long>(0, 0, 0) == Result<unsigned long long>{0, 0});
56+
static_assert(add<unsigned long long>(0, 0, 1) == Result<unsigned long long>{1, 0});
57+
static_assert(add<unsigned long long>(ULLONG_MAX - 1, 1, 1) == Result<unsigned long long>{0, 1});
58+
static_assert(add<unsigned long long>(ULLONG_MAX, 1, 0) == Result<unsigned long long>{0, 1});
59+
static_assert(add<unsigned long long>(ULLONG_MAX, 1, 1) == Result<unsigned long long>{1, 1});
60+
61+
template<typename T>
62+
constexpr Result<T> sub(T Lhs, T Rhs, T Carryin)
63+
{
64+
T Carryout = 0;
65+
if constexpr(__is_same(T, unsigned char))
66+
return { __builtin_subcb(Lhs, Rhs, Carryin, &Carryout), Carryout };
67+
else if constexpr(__is_same(T, unsigned short))
68+
return { __builtin_subcs(Lhs, Rhs, Carryin, &Carryout), Carryout };
69+
else if constexpr(__is_same(T, unsigned int))
70+
return { __builtin_subc(Lhs, Rhs, Carryin, &Carryout), Carryout };
71+
else if constexpr(__is_same(T, unsigned long))
72+
return { __builtin_subcl(Lhs, Rhs, Carryin, &Carryout), Carryout };
73+
else if constexpr(__is_same(T, unsigned long long))
74+
return { __builtin_subcll(Lhs, Rhs, Carryin, &Carryout), Carryout };
75+
}
76+
77+
static_assert(sub<unsigned char>(0, 0, 0) == Result<unsigned char>{0, 0});
78+
static_assert(sub<unsigned char>(0, 0, 1) == Result<unsigned char>{UCHAR_MAX, 1});
79+
static_assert(sub<unsigned char>(0, 1, 0) == Result<unsigned char>{UCHAR_MAX, 1});
80+
static_assert(sub<unsigned char>(0, 1, 1) == Result<unsigned char>{UCHAR_MAX - 1, 1});
81+
static_assert(sub<unsigned char>(1, 0, 0) == Result<unsigned char>{1, 0});
82+
83+
static_assert(sub<unsigned short>(0, 0, 0) == Result<unsigned short>{0, 0});
84+
static_assert(sub<unsigned short>(0, 0, 1) == Result<unsigned short>{USHRT_MAX, 1});
85+
static_assert(sub<unsigned short>(0, 1, 0) == Result<unsigned short>{USHRT_MAX, 1});
86+
static_assert(sub<unsigned short>(0, 1, 1) == Result<unsigned short>{USHRT_MAX - 1, 1});
87+
static_assert(sub<unsigned short>(1, 0, 0) == Result<unsigned short>{1, 0});
88+
89+
static_assert(sub<unsigned int>(0, 0, 0) == Result<unsigned int>{0, 0});
90+
static_assert(sub<unsigned int>(0, 0, 1) == Result<unsigned int>{UINT_MAX, 1});
91+
static_assert(sub<unsigned int>(0, 1, 0) == Result<unsigned int>{UINT_MAX, 1});
92+
static_assert(sub<unsigned int>(0, 1, 1) == Result<unsigned int>{UINT_MAX - 1, 1});
93+
static_assert(sub<unsigned int>(1, 0, 0) == Result<unsigned int>{1, 0});
94+
95+
static_assert(sub<unsigned long>(0, 0, 0) == Result<unsigned long>{0, 0});
96+
static_assert(sub<unsigned long>(0, 0, 1) == Result<unsigned long>{ULONG_MAX, 1});
97+
static_assert(sub<unsigned long>(0, 1, 0) == Result<unsigned long>{ULONG_MAX, 1});
98+
static_assert(sub<unsigned long>(0, 1, 1) == Result<unsigned long>{ULONG_MAX - 1, 1});
99+
static_assert(sub<unsigned long>(1, 0, 0) == Result<unsigned long>{1, 0});
100+
101+
static_assert(sub<unsigned long long>(0, 0, 0) == Result<unsigned long long>{0, 0});
102+
static_assert(sub<unsigned long long>(0, 0, 1) == Result<unsigned long long>{ULLONG_MAX, 1});
103+
static_assert(sub<unsigned long long>(0, 1, 0) == Result<unsigned long long>{ULLONG_MAX, 1});
104+
static_assert(sub<unsigned long long>(0, 1, 1) == Result<unsigned long long>{ULLONG_MAX - 1, 1});
105+
static_assert(sub<unsigned long long>(1, 0, 0) == Result<unsigned long long>{1, 0});

0 commit comments

Comments
 (0)