BertalanD created this revision. BertalanD added reviewers: aaron.ballman, cjdb, erichkeane. Herald added a project: All. BertalanD requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits.
GCC has gained support for these multiprecision arithmetic builtins in [`r14-1896-g2b4e0415ad6`](https://gcc.gnu.org/g:2b4e0415ad6), 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. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D156156 Files: clang/include/clang/Basic/Builtins.def clang/lib/AST/ExprConstant.cpp clang/test/SemaCXX/builtins-multiprecision.cpp
Index: clang/test/SemaCXX/builtins-multiprecision.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/builtins-multiprecision.cpp @@ -0,0 +1,105 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++17 -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}); Index: clang/lib/AST/ExprConstant.cpp =================================================================== --- clang/lib/AST/ExprConstant.cpp +++ clang/lib/AST/ExprConstant.cpp @@ -12551,6 +12551,56 @@ 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); + } } } Index: clang/include/clang/Basic/Builtins.def =================================================================== --- clang/include/clang/Basic/Builtins.def +++ clang/include/clang/Basic/Builtins.def @@ -1611,16 +1611,16 @@ 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")
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits