Skip to content

Commit f209352

Browse files
authored
[clang] Allow builtin addc/subc to be constant evaluated (#81656)
[clang] Allow builtin addc/subc to be constant evaluated
1 parent 6e11ed2 commit f209352

File tree

5 files changed

+117
-2
lines changed

5 files changed

+117
-2
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5274,6 +5274,11 @@ Intrinsics Support within Constant Expressions
52745274
52755275
The following builtin intrinsics can be used in constant expressions:
52765276
5277+
* ``__builtin_addcb``
5278+
* ``__builtin_addcs``
5279+
* ``__builtin_addc``
5280+
* ``__builtin_addcl``
5281+
* ``__builtin_addcll``
52775282
* ``__builtin_bitreverse8``
52785283
* ``__builtin_bitreverse16``
52795284
* ``__builtin_bitreverse32``
@@ -5320,6 +5325,11 @@ The following builtin intrinsics can be used in constant expressions:
53205325
* ``__builtin_rotateright16``
53215326
* ``__builtin_rotateright32``
53225327
* ``__builtin_rotateright64``
5328+
* ``__builtin_subcb``
5329+
* ``__builtin_subcs``
5330+
* ``__builtin_subc``
5331+
* ``__builtin_subcl``
5332+
* ``__builtin_subcll``
53235333
53245334
The following x86-specific intrinsics can be used in constant expressions:
53255335

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,9 @@ Non-comprehensive list of changes in this release
141141
- Added ``__builtin_readsteadycounter`` for reading fixed frequency hardware
142142
counters.
143143

144+
- ``__builtin_addc``, ``__builtin_subc``, and the other sizes of those
145+
builtins are now constexpr and may be used in constant expressions.
146+
144147
New Compiler Flags
145148
------------------
146149

clang/include/clang/Basic/Builtins.td

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4067,14 +4067,14 @@ class MPATemplate : Template<
40674067

40684068
def Addc : Builtin, MPATemplate {
40694069
let Spellings = ["__builtin_addc"];
4070-
let Attributes = [NoThrow];
4070+
let Attributes = [NoThrow, Constexpr];
40714071
// FIXME: Why are these argumentes marked const?
40724072
let Prototype = "T(T const, T const, T const, T*)";
40734073
}
40744074

40754075
def Subc : Builtin, MPATemplate {
40764076
let Spellings = ["__builtin_subc"];
4077-
let Attributes = [NoThrow];
4077+
let Attributes = [NoThrow, Constexpr];
40784078
// FIXME: Why are these argumentes marked const?
40794079
let Prototype = "T(T const, T const, T const, T*)";
40804080
}

clang/lib/AST/ExprConstant.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12696,6 +12696,59 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
1269612696
return BuiltinOp == Builtin::BI__atomic_always_lock_free ?
1269712697
Success(0, E) : Error(E);
1269812698
}
12699+
case Builtin::BI__builtin_addcb:
12700+
case Builtin::BI__builtin_addcs:
12701+
case Builtin::BI__builtin_addc:
12702+
case Builtin::BI__builtin_addcl:
12703+
case Builtin::BI__builtin_addcll:
12704+
case Builtin::BI__builtin_subcb:
12705+
case Builtin::BI__builtin_subcs:
12706+
case Builtin::BI__builtin_subc:
12707+
case Builtin::BI__builtin_subcl:
12708+
case Builtin::BI__builtin_subcll: {
12709+
LValue CarryOutLValue;
12710+
APSInt LHS, RHS, CarryIn, CarryOut, Result;
12711+
QualType ResultType = E->getArg(0)->getType();
12712+
if (!EvaluateInteger(E->getArg(0), LHS, Info) ||
12713+
!EvaluateInteger(E->getArg(1), RHS, Info) ||
12714+
!EvaluateInteger(E->getArg(2), CarryIn, Info) ||
12715+
!EvaluatePointer(E->getArg(3), CarryOutLValue, Info))
12716+
return false;
12717+
// Copy the number of bits and sign.
12718+
Result = LHS;
12719+
CarryOut = LHS;
12720+
12721+
bool FirstOverflowed = false;
12722+
bool SecondOverflowed = false;
12723+
switch (BuiltinOp) {
12724+
default:
12725+
llvm_unreachable("Invalid value for BuiltinOp");
12726+
case Builtin::BI__builtin_addcb:
12727+
case Builtin::BI__builtin_addcs:
12728+
case Builtin::BI__builtin_addc:
12729+
case Builtin::BI__builtin_addcl:
12730+
case Builtin::BI__builtin_addcll:
12731+
Result =
12732+
LHS.uadd_ov(RHS, FirstOverflowed).uadd_ov(CarryIn, SecondOverflowed);
12733+
break;
12734+
case Builtin::BI__builtin_subcb:
12735+
case Builtin::BI__builtin_subcs:
12736+
case Builtin::BI__builtin_subc:
12737+
case Builtin::BI__builtin_subcl:
12738+
case Builtin::BI__builtin_subcll:
12739+
Result =
12740+
LHS.usub_ov(RHS, FirstOverflowed).usub_ov(CarryIn, SecondOverflowed);
12741+
break;
12742+
}
12743+
12744+
// It is possible for both overflows to happen but CGBuiltin uses an OR so
12745+
// this is consistent.
12746+
CarryOut = (uint64_t)(FirstOverflowed | SecondOverflowed);
12747+
APValue APV{CarryOut};
12748+
if (!handleAssignment(Info, E, CarryOutLValue, ResultType, APV))
12749+
return false;
12750+
return Success(Result, E);
12751+
}
1269912752
case Builtin::BI__builtin_add_overflow:
1270012753
case Builtin::BI__builtin_sub_overflow:
1270112754
case Builtin::BI__builtin_mul_overflow:

clang/test/SemaCXX/builtins-overflow.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,52 @@ static_assert(smul(17,22) == Result<int>{false, 374});
9494
static_assert(smul(INT_MAX / 22, 23) == Result<int>{true, -2049870757});
9595
static_assert(smul(INT_MIN / 22, -23) == Result<int>{true, -2049870757});
9696

97+
template<typename T>
98+
struct CarryResult {
99+
T CarryOut;
100+
T Value;
101+
constexpr bool operator==(const CarryResult<T> &Other) {
102+
return CarryOut == Other.CarryOut && Value == Other.Value;
103+
}
104+
};
105+
106+
constexpr CarryResult<unsigned char> addcb(unsigned char lhs, unsigned char rhs, unsigned char carry) {
107+
unsigned char carry_out{};
108+
unsigned char sum{};
109+
sum = __builtin_addcb(lhs, rhs, carry, &carry_out);
110+
return {carry_out, sum};
111+
}
112+
113+
static_assert(addcb(120, 10, 0) == CarryResult<unsigned char>{0, 130});
114+
static_assert(addcb(250, 10, 0) == CarryResult<unsigned char>{1, 4});
115+
static_assert(addcb(255, 255, 0) == CarryResult<unsigned char>{1, 254});
116+
static_assert(addcb(255, 255, 1) == CarryResult<unsigned char>{1, 255});
117+
static_assert(addcb(255, 0, 1) == CarryResult<unsigned char>{1, 0});
118+
static_assert(addcb(255, 1, 0) == CarryResult<unsigned char>{1, 0});
119+
static_assert(addcb(255, 1, 1) == CarryResult<unsigned char>{1, 1});
120+
// This is currently supported with the carry still producing a value of 1.
121+
// If support for carry outside of 0-1 is removed, change this test to check
122+
// that it is not supported.
123+
static_assert(addcb(255, 255, 2) == CarryResult<unsigned char>{1, 0});
124+
125+
constexpr CarryResult<unsigned char> subcb(unsigned char lhs, unsigned char rhs, unsigned char carry) {
126+
unsigned char carry_out{};
127+
unsigned char sum{};
128+
sum = __builtin_subcb(lhs, rhs, carry, &carry_out);
129+
return {carry_out, sum};
130+
}
131+
132+
static_assert(subcb(20, 10, 0) == CarryResult<unsigned char>{0, 10});
133+
static_assert(subcb(10, 10, 0) == CarryResult<unsigned char>{0, 0});
134+
static_assert(subcb(10, 15, 0) == CarryResult<unsigned char>{1, 251});
135+
// The carry is subtracted from the result
136+
static_assert(subcb(10, 15, 1) == CarryResult<unsigned char>{1, 250});
137+
static_assert(subcb(0, 0, 1) == CarryResult<unsigned char>{1, 255});
138+
static_assert(subcb(0, 1, 0) == CarryResult<unsigned char>{1, 255});
139+
static_assert(subcb(0, 1, 1) == CarryResult<unsigned char>{1, 254});
140+
static_assert(subcb(0, 255, 0) == CarryResult<unsigned char>{1, 1});
141+
static_assert(subcb(0, 255, 1) == CarryResult<unsigned char>{1, 0});
142+
// This is currently supported with the carry still producing a value of 1.
143+
// If support for carry outside of 0-1 is removed, change this test to check
144+
// that it is not supported.
145+
static_assert(subcb(0, 255, 2) == CarryResult<unsigned char>{1, 255});

0 commit comments

Comments
 (0)