Skip to content

[clang] Implement constexpr evaluation for __builtin_{add,sub}c #66005

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3449,6 +3449,8 @@ The complete list of builtins are:
unsigned long __builtin_subcl (unsigned long x, unsigned long y, unsigned long carryin, unsigned long *carryout);
unsigned long long __builtin_subcll(unsigned long long x, unsigned long long y, unsigned long long carryin, unsigned long long *carryout);

These builtins can be used in constant expressions.

Checked Arithmetic Builtins
---------------------------

Expand Down Expand Up @@ -5173,6 +5175,11 @@ Intrinsics Support within Constant Expressions

The following builtin intrinsics can be used in constant expressions:

* ``__builtin_addc``
* ``__builtin_addcb``
* ``__builtin_addcl``
* ``__builtin_addcll``
* ``__builtin_addcs``
* ``__builtin_bitreverse8``
* ``__builtin_bitreverse16``
* ``__builtin_bitreverse32``
Expand Down Expand Up @@ -5219,6 +5226,11 @@ The following builtin intrinsics can be used in constant expressions:
* ``__builtin_rotateright16``
* ``__builtin_rotateright32``
* ``__builtin_rotateright64``
* ``__builtin_subc``
* ``__builtin_subcb``
* ``__builtin_subcl``
* ``__builtin_subcll``
* ``__builtin_subcs``

The following x86-specific intrinsics can be used in constant expressions:

Expand Down
20 changes: 10 additions & 10 deletions clang/include/clang/Basic/Builtins.def
Original file line number Diff line number Diff line change
Expand Up @@ -1619,16 +1619,16 @@ BUILTIN(__builtin_assume, "vb", "nE")
BUILTIN(__builtin_assume_separate_storage, "vvCD*vCD*", "nE")

// Multiprecision Arithmetic Builtins.
BUILTIN(__builtin_addcb, "UcUcCUcCUcCUc*", "n")
BUILTIN(__builtin_addcs, "UsUsCUsCUsCUs*", "n")
BUILTIN(__builtin_addc, "UiUiCUiCUiCUi*", "n")
BUILTIN(__builtin_addcl, "ULiULiCULiCULiCULi*", "n")
BUILTIN(__builtin_addcll, "ULLiULLiCULLiCULLiCULLi*", "n")
BUILTIN(__builtin_subcb, "UcUcCUcCUcCUc*", "n")
BUILTIN(__builtin_subcs, "UsUsCUsCUsCUs*", "n")
BUILTIN(__builtin_subc, "UiUiCUiCUiCUi*", "n")
BUILTIN(__builtin_subcl, "ULiULiCULiCULiCULi*", "n")
BUILTIN(__builtin_subcll, "ULLiULLiCULLiCULLiCULLi*", "n")
BUILTIN(__builtin_addcb, "UcUcCUcCUcCUc*", "nE")
BUILTIN(__builtin_addcs, "UsUsCUsCUsCUs*", "nE")
BUILTIN(__builtin_addc, "UiUiCUiCUiCUi*", "nE")
BUILTIN(__builtin_addcl, "ULiULiCULiCULiCULi*", "nE")
BUILTIN(__builtin_addcll, "ULLiULLiCULLiCULLiCULLi*", "nE")
BUILTIN(__builtin_subcb, "UcUcCUcCUcCUc*", "nE")
BUILTIN(__builtin_subcs, "UsUsCUsCUsCUs*", "nE")
BUILTIN(__builtin_subc, "UiUiCUiCUiCUi*", "nE")
BUILTIN(__builtin_subcl, "ULiULiCULiCULiCULi*", "nE")
BUILTIN(__builtin_subcll, "ULLiULLiCULLiCULLiCULLi*", "nE")

// Checked Arithmetic Builtins for Security.
BUILTIN(__builtin_add_overflow, "b.", "ntE")
Expand Down
50 changes: 50 additions & 0 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12630,6 +12630,56 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
return false;
return Success(DidOverflow, E);
}
case Builtin::BI__builtin_addcb:
case Builtin::BI__builtin_addcs:
case Builtin::BI__builtin_addc:
case Builtin::BI__builtin_addcl:
case Builtin::BI__builtin_addcll:
case Builtin::BI__builtin_subcb:
case Builtin::BI__builtin_subcs:
case Builtin::BI__builtin_subc:
case Builtin::BI__builtin_subcl:
case Builtin::BI__builtin_subcll: {
APSInt X, Y, CarryIn;
LValue CarryOut;

QualType ResultType = E->getArg(3)->getType()->getPointeeType();
if (!EvaluateInteger(E->getArg(0), X, Info) ||
!EvaluateInteger(E->getArg(1), Y, Info) ||
!EvaluateInteger(E->getArg(2), CarryIn, Info) ||
!EvaluatePointer(E->getArg(3), CarryOut, Info))
return false;

APInt Result;
bool DidOverflow1 = false;
bool DidOverflow2 = false;

switch (BuiltinOp) {
default:
llvm_unreachable("Invalid value for BuiltinOp");
case Builtin::BI__builtin_addcb:
case Builtin::BI__builtin_addcs:
case Builtin::BI__builtin_addc:
case Builtin::BI__builtin_addcl:
case Builtin::BI__builtin_addcll:
Result = X.uadd_ov(Y, DidOverflow1).uadd_ov(CarryIn, DidOverflow2);
break;
case Builtin::BI__builtin_subcb:
case Builtin::BI__builtin_subcs:
case Builtin::BI__builtin_subc:
case Builtin::BI__builtin_subcl:
case Builtin::BI__builtin_subcll:
Result = X.usub_ov(Y, DidOverflow1).usub_ov(CarryIn, DidOverflow2);
break;
}

APSInt DidOverflow(
APInt(Result.getBitWidth(), DidOverflow1 || DidOverflow2));
APValue DidOverflowVal(DidOverflow);
if (!handleAssignment(Info, E, CarryOut, ResultType, DidOverflowVal))
return false;
return Success(Result, E);
}
}
}

Expand Down
105 changes: 105 additions & 0 deletions clang/test/SemaCXX/builtins-multiprecision.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// RUN: %clang_cc1 -fsyntax-only -std=c++17 -ffreestanding -verify %s
// expected-no-diagnostics

#include <limits.h>

template<typename T>
struct Result {
T value;
T carry;
constexpr bool operator==(const Result<T> &Other) {
return value == Other.value && carry == Other.carry;
}
};

template<typename T>
constexpr Result<T> add(T Lhs, T Rhs, T Carryin)
{
T Carryout = 0;
if constexpr(__is_same(T, unsigned char))
return { __builtin_addcb(Lhs, Rhs, Carryin, &Carryout), Carryout };
else if constexpr(__is_same(T, unsigned short))
return { __builtin_addcs(Lhs, Rhs, Carryin, &Carryout), Carryout };
else if constexpr(__is_same(T, unsigned int))
return { __builtin_addc(Lhs, Rhs, Carryin, &Carryout), Carryout };
else if constexpr(__is_same(T, unsigned long))
return { __builtin_addcl(Lhs, Rhs, Carryin, &Carryout), Carryout };
else if constexpr(__is_same(T, unsigned long long))
return { __builtin_addcll(Lhs, Rhs, Carryin, &Carryout), Carryout };
}

static_assert(add<unsigned char>(0, 0, 0) == Result<unsigned char>{0, 0});
static_assert(add<unsigned char>(0, 0, 1) == Result<unsigned char>{1, 0});
static_assert(add<unsigned char>(UCHAR_MAX - 1, 1, 1) == Result<unsigned char>{0, 1});
static_assert(add<unsigned char>(UCHAR_MAX, 1, 0) == Result<unsigned char>{0, 1});
static_assert(add<unsigned char>(UCHAR_MAX, 1, 1) == Result<unsigned char>{1, 1});

static_assert(add<unsigned short>(0, 0, 0) == Result<unsigned short>{0, 0});
static_assert(add<unsigned short>(0, 0, 1) == Result<unsigned short>{1, 0});
static_assert(add<unsigned short>(USHRT_MAX - 1, 1, 1) == Result<unsigned short>{0, 1});
static_assert(add<unsigned short>(USHRT_MAX, 1, 0) == Result<unsigned short>{0, 1});
static_assert(add<unsigned short>(USHRT_MAX, 1, 1) == Result<unsigned short>{1, 1});

static_assert(add<unsigned int>(0, 0, 0) == Result<unsigned int>{0, 0});
static_assert(add<unsigned int>(0, 0, 1) == Result<unsigned int>{1, 0});
static_assert(add<unsigned int>(UINT_MAX - 1, 1, 1) == Result<unsigned int>{0, 1});
static_assert(add<unsigned int>(UINT_MAX, 1, 0) == Result<unsigned int>{0, 1});
static_assert(add<unsigned int>(UINT_MAX, 1, 1) == Result<unsigned int>{1, 1});

static_assert(add<unsigned long>(0, 0, 0) == Result<unsigned long>{0, 0});
static_assert(add<unsigned long>(0, 0, 1) == Result<unsigned long>{1, 0});
static_assert(add<unsigned long>(ULONG_MAX - 1, 1, 1) == Result<unsigned long>{0, 1});
static_assert(add<unsigned long>(ULONG_MAX, 1, 0) == Result<unsigned long>{0, 1});
static_assert(add<unsigned long>(ULONG_MAX, 1, 1) == Result<unsigned long>{1, 1});

static_assert(add<unsigned long long>(0, 0, 0) == Result<unsigned long long>{0, 0});
static_assert(add<unsigned long long>(0, 0, 1) == Result<unsigned long long>{1, 0});
static_assert(add<unsigned long long>(ULLONG_MAX - 1, 1, 1) == Result<unsigned long long>{0, 1});
static_assert(add<unsigned long long>(ULLONG_MAX, 1, 0) == Result<unsigned long long>{0, 1});
static_assert(add<unsigned long long>(ULLONG_MAX, 1, 1) == Result<unsigned long long>{1, 1});

template<typename T>
constexpr Result<T> sub(T Lhs, T Rhs, T Carryin)
{
T Carryout = 0;
if constexpr(__is_same(T, unsigned char))
return { __builtin_subcb(Lhs, Rhs, Carryin, &Carryout), Carryout };
else if constexpr(__is_same(T, unsigned short))
return { __builtin_subcs(Lhs, Rhs, Carryin, &Carryout), Carryout };
else if constexpr(__is_same(T, unsigned int))
return { __builtin_subc(Lhs, Rhs, Carryin, &Carryout), Carryout };
else if constexpr(__is_same(T, unsigned long))
return { __builtin_subcl(Lhs, Rhs, Carryin, &Carryout), Carryout };
else if constexpr(__is_same(T, unsigned long long))
return { __builtin_subcll(Lhs, Rhs, Carryin, &Carryout), Carryout };
}

static_assert(sub<unsigned char>(0, 0, 0) == Result<unsigned char>{0, 0});
static_assert(sub<unsigned char>(0, 0, 1) == Result<unsigned char>{UCHAR_MAX, 1});
static_assert(sub<unsigned char>(0, 1, 0) == Result<unsigned char>{UCHAR_MAX, 1});
static_assert(sub<unsigned char>(0, 1, 1) == Result<unsigned char>{UCHAR_MAX - 1, 1});
static_assert(sub<unsigned char>(1, 0, 0) == Result<unsigned char>{1, 0});

static_assert(sub<unsigned short>(0, 0, 0) == Result<unsigned short>{0, 0});
static_assert(sub<unsigned short>(0, 0, 1) == Result<unsigned short>{USHRT_MAX, 1});
static_assert(sub<unsigned short>(0, 1, 0) == Result<unsigned short>{USHRT_MAX, 1});
static_assert(sub<unsigned short>(0, 1, 1) == Result<unsigned short>{USHRT_MAX - 1, 1});
static_assert(sub<unsigned short>(1, 0, 0) == Result<unsigned short>{1, 0});

static_assert(sub<unsigned int>(0, 0, 0) == Result<unsigned int>{0, 0});
static_assert(sub<unsigned int>(0, 0, 1) == Result<unsigned int>{UINT_MAX, 1});
static_assert(sub<unsigned int>(0, 1, 0) == Result<unsigned int>{UINT_MAX, 1});
static_assert(sub<unsigned int>(0, 1, 1) == Result<unsigned int>{UINT_MAX - 1, 1});
static_assert(sub<unsigned int>(1, 0, 0) == Result<unsigned int>{1, 0});

static_assert(sub<unsigned long>(0, 0, 0) == Result<unsigned long>{0, 0});
static_assert(sub<unsigned long>(0, 0, 1) == Result<unsigned long>{ULONG_MAX, 1});
static_assert(sub<unsigned long>(0, 1, 0) == Result<unsigned long>{ULONG_MAX, 1});
static_assert(sub<unsigned long>(0, 1, 1) == Result<unsigned long>{ULONG_MAX - 1, 1});
static_assert(sub<unsigned long>(1, 0, 0) == Result<unsigned long>{1, 0});

static_assert(sub<unsigned long long>(0, 0, 0) == Result<unsigned long long>{0, 0});
static_assert(sub<unsigned long long>(0, 0, 1) == Result<unsigned long long>{ULLONG_MAX, 1});
static_assert(sub<unsigned long long>(0, 1, 0) == Result<unsigned long long>{ULLONG_MAX, 1});
static_assert(sub<unsigned long long>(0, 1, 1) == Result<unsigned long long>{ULLONG_MAX - 1, 1});
static_assert(sub<unsigned long long>(1, 0, 0) == Result<unsigned long long>{1, 0});