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
+
+template
+struct Result {
+ T value;
+ T carry;
+ constexpr bool operator==(const Result ) {
+return value == Other.value && carry == Other.carry;
+ }
+};
+
+template
+constexpr Result add(T Lhs, T Rhs, T Carryin)
+{
+ T Carryout = 0;
+ if constexpr(__is_same(T, unsigned char))
+return { __builtin_addcb(Lhs, Rhs, Carryin, ), Carryout };
+ else if constexpr(__is_same(T, unsigned short))
+return { __builtin_addcs(Lhs, Rhs, Carryin, ), Carryout };
+ else if constexpr(__is_same(T, unsigned int))
+return { __builtin_addc(Lhs, Rhs, Carryin, ), Carryout };
+ else if constexpr(__is_same(T, unsigned long))
+return { __builtin_addcl(Lhs, Rhs, Carryin, ), Carryout };
+ else if constexpr(__is_same(T, unsigned long long))
+return { __builtin_addcll(Lhs, Rhs, Carryin, ), Carryout };
+}
+
+static_assert(add(0, 0, 0) == Result{0, 0});
+static_assert(add(0, 0, 1) == Result{1, 0});
+static_assert(add(UCHAR_MAX - 1, 1, 1) == Result{0, 1});
+static_assert(add(UCHAR_MAX, 1, 0) == Result{0, 1});
+static_assert(add(UCHAR_MAX, 1, 1) == Result{1, 1});
+
+static_assert(add(0, 0, 0) == Result{0, 0});
+static_assert(add(0, 0, 1) == Result{1, 0});
+static_assert(add(USHRT_MAX - 1, 1, 1) == Result{0, 1});
+static_assert(add(USHRT_MAX, 1, 0) == Result{0, 1});
+static_assert(add(USHRT_MAX, 1, 1) == Result{1, 1});
+
+static_assert(add(0, 0, 0) == Result{0, 0});
+static_assert(add(0, 0, 1) == Result{1, 0});
+static_assert(add(UINT_MAX - 1, 1, 1) == Result{0, 1});
+static_assert(add(UINT_MAX, 1, 0) == Result{0, 1});
+static_assert(add(UINT_MAX, 1, 1) == Result{1, 1});
+
+static_assert(add(0, 0, 0) == Result{0, 0});
+static_assert(add(0, 0, 1) == Result{1, 0});
+static_assert(add(ULONG_MAX - 1, 1, 1) == Result{0, 1});
+static_assert(add(ULONG_MAX, 1, 0) == Result{0, 1});
+static_assert(add(ULONG_MAX, 1, 1) == Result{1, 1});
+
+static_assert(add(0, 0, 0) == Result{0, 0});
+static_assert(add(0, 0, 1) == Result{1, 0});
+static_assert(add(ULLONG_MAX - 1, 1, 1) == Result{0, 1});
+static_assert(add(ULLONG_MAX, 1, 0) == Result{0, 1});
+static_assert(add(ULLONG_MAX, 1, 1) == Result{1, 1});
+
+template
+constexpr Result sub(T Lhs, T Rhs, T Carryin)
+{
+ T Carryout = 0;
+ if constexpr(__is_same(T, unsigned char))
+return { __builtin_subcb(Lhs, Rhs, Carryin, ), Carryout };
+ else if constexpr(__is_same(T, unsigned short))
+return { __builtin_subcs(Lhs, Rhs, Carryin, ), Carryout };
+ else if constexpr(__is_same(T, unsigned int))
+return { __builtin_subc(Lhs, Rhs, Carryin, ), Carryout };
+ else if constexpr(__is_same(T, unsigned long))
+return { __builtin_subcl(Lhs, Rhs, Carryin, ), Carryout };
+ else if constexpr(__is_same(T, unsigned long long))
+return { __builtin_subcll(Lhs, Rhs, Carryin, ), Carryout };
+}
+
+static_assert(sub(0, 0, 0) == Result{0, 0});
+static_assert(sub(0, 0, 1) == Result{UCHAR_MAX, 1});
+static_assert(sub(0, 1, 0) == Result{UCHAR_MAX, 1});
+static_assert(sub(0, 1, 1) == Result{UCHAR_MAX - 1, 1});
+static_assert(sub(1, 0, 0) == Result{1, 0});
+
+static_assert(sub(0, 0, 0) == Result{0, 0});
+static_assert(sub(0, 0, 1) == Result{USHRT_MAX, 1});
+static_assert(sub(0, 1, 0) == Result{USHRT_MAX, 1});
+static_assert(sub(0, 1, 1) == Result{USHRT_MAX - 1, 1});
+static_assert(sub(1, 0, 0) == Result{1, 0});
+
+static_assert(sub(0, 0, 0) == Result{0, 0});
+static_assert(sub(0, 0, 1) == Result{UINT_MAX, 1});
+static_assert(sub(0, 1, 0) == Result{UINT_MAX, 1});
+static_assert(sub(0, 1, 1) == Result{UINT_MAX - 1, 1});
+static_assert(sub(1, 0, 0) == Result{1, 0});
+
+static_assert(sub(0, 0, 0) == Result{0, 0});
+static_assert(sub(0, 0, 1) == Result{ULONG_MAX, 1});
+static_assert(sub(0, 1, 0) == Result{ULONG_MAX, 1});
+static_assert(sub(0, 1, 1)