From 4eebd6b9dc22bc8914c1a1dc48c722203428833c Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Fri, 16 Jun 2023 15:32:18 -0700 Subject: [PATCH] Improve new C23 checked arithmetic feature --- .../compiler_rt => libc/intrin}/divmodti4.c | 0 .../compiler_rt => libc/intrin}/divti3.c | 0 .../compiler_rt => libc/intrin}/mulodi4.c | 6 +- .../compiler_rt => libc/intrin}/mulosi4.c | 6 +- .../compiler_rt => libc/intrin}/muloti4.c | 6 +- .../compiler_rt => libc/intrin}/udivmodti4.c | 0 libc/stdckdint.h | 91 ++++++- libc/type2str.h | 2 + test/libc/intrin/stdckdint_test.c | 237 ++++++++++++++++++ test/libc/intrin/test.mk | 8 + 10 files changed, 341 insertions(+), 15 deletions(-) rename {third_party/compiler_rt => libc/intrin}/divmodti4.c (100%) rename {third_party/compiler_rt => libc/intrin}/divti3.c (100%) rename {third_party/compiler_rt => libc/intrin}/mulodi4.c (92%) rename {third_party/compiler_rt => libc/intrin}/mulosi4.c (92%) rename {third_party/compiler_rt => libc/intrin}/muloti4.c (92%) rename {third_party/compiler_rt => libc/intrin}/udivmodti4.c (100%) create mode 100644 test/libc/intrin/stdckdint_test.c diff --git a/third_party/compiler_rt/divmodti4.c b/libc/intrin/divmodti4.c similarity index 100% rename from third_party/compiler_rt/divmodti4.c rename to libc/intrin/divmodti4.c diff --git a/third_party/compiler_rt/divti3.c b/libc/intrin/divti3.c similarity index 100% rename from third_party/compiler_rt/divti3.c rename to libc/intrin/divti3.c diff --git a/third_party/compiler_rt/mulodi4.c b/libc/intrin/mulodi4.c similarity index 92% rename from third_party/compiler_rt/mulodi4.c rename to libc/intrin/mulodi4.c index d5637f58f..c2a49342e 100644 --- a/third_party/compiler_rt/mulodi4.c +++ b/libc/intrin/mulodi4.c @@ -13,8 +13,6 @@ * ===----------------------------------------------------------------------=== */ -STATIC_YOINK("huge_compiler_rt_license"); - #include "third_party/compiler_rt/int_lib.h" /* Returns: a * b */ @@ -25,10 +23,10 @@ COMPILER_RT_ABI di_int __mulodi4(di_int a, di_int b, int* overflow) { const int N = (int)(sizeof(di_int) * CHAR_BIT); - const di_int MIN = (di_int)1 << (N-1); + const di_int MIN = (du_int)1 << (N-1); const di_int MAX = ~MIN; *overflow = 0; - di_int result = a * b; + di_int result = (du_int)a * (du_int)b; if (a == MIN) { if (b != 0 && b != 1) diff --git a/third_party/compiler_rt/mulosi4.c b/libc/intrin/mulosi4.c similarity index 92% rename from third_party/compiler_rt/mulosi4.c rename to libc/intrin/mulosi4.c index 083b33a60..5261a6f90 100644 --- a/third_party/compiler_rt/mulosi4.c +++ b/libc/intrin/mulosi4.c @@ -13,8 +13,6 @@ * ===----------------------------------------------------------------------=== */ -STATIC_YOINK("huge_compiler_rt_license"); - #include "third_party/compiler_rt/int_lib.h" /* Returns: a * b */ @@ -25,10 +23,10 @@ COMPILER_RT_ABI si_int __mulosi4(si_int a, si_int b, int* overflow) { const int N = (int)(sizeof(si_int) * CHAR_BIT); - const si_int MIN = (si_int)1 << (N-1); + const si_int MIN = (su_int)1 << (N-1); const si_int MAX = ~MIN; *overflow = 0; - si_int result = a * b; + si_int result = (su_int)a * (su_int)b; if (a == MIN) { if (b != 0 && b != 1) diff --git a/third_party/compiler_rt/muloti4.c b/libc/intrin/muloti4.c similarity index 92% rename from third_party/compiler_rt/muloti4.c rename to libc/intrin/muloti4.c index 8589c3b42..bc799d654 100644 --- a/third_party/compiler_rt/muloti4.c +++ b/libc/intrin/muloti4.c @@ -13,8 +13,6 @@ * ===----------------------------------------------------------------------=== */ -STATIC_YOINK("huge_compiler_rt_license"); - #include "third_party/compiler_rt/int_lib.h" #ifdef CRT_HAS_128BIT @@ -27,10 +25,10 @@ COMPILER_RT_ABI ti_int __muloti4(ti_int a, ti_int b, int* overflow) { const int N = (int)(sizeof(ti_int) * CHAR_BIT); - const ti_int MIN = (ti_int)1 << (N-1); + const ti_int MIN = (tu_int)1 << (N-1); const ti_int MAX = ~MIN; *overflow = 0; - ti_int result = a * b; + ti_int result = (tu_int)a * (tu_int)b; if (a == MIN) { if (b != 0 && b != 1) diff --git a/third_party/compiler_rt/udivmodti4.c b/libc/intrin/udivmodti4.c similarity index 100% rename from third_party/compiler_rt/udivmodti4.c rename to libc/intrin/udivmodti4.c diff --git a/libc/stdckdint.h b/libc/stdckdint.h index b505b2d5d..f2ea4dbcc 100644 --- a/libc/stdckdint.h +++ b/libc/stdckdint.h @@ -1,8 +1,93 @@ #ifndef COSMOPOLITAN_LIBC_STDCKDINT_H_ #define COSMOPOLITAN_LIBC_STDCKDINT_H_ +#if !defined(MODE_DBG) && \ + ((defined(__GNUC__) && __GNUC__ >= 5 && !defined(__ICC)) || \ + (defined(__has_builtin) && (__has_builtin(__builtin_add_overflow) && \ + __has_builtin(__builtin_sub_overflow) && \ + __has_builtin(__builtin_mul_overflow)))) -#define ckd_add(R, A, B) __builtin_add_overflow((A), (B), (R)) -#define ckd_sub(R, A, B) __builtin_sub_overflow((A), (B), (R)) -#define ckd_mul(R, A, B) __builtin_mul_overflow((A), (B), (R)) +#define ckd_add(res, x, y) __builtin_add_overflow((x), (y), (res)) +#define ckd_sub(res, x, y) __builtin_sub_overflow((x), (y), (res)) +#define ckd_mul(res, x, y) __builtin_mul_overflow((x), (y), (res)) +#else + +#define ckd_add(res, x, y) __ckd_arithmetic(add, res, x, y) +#define ckd_sub(res, x, y) __ckd_arithmetic(sub, res, x, y) +#define ckd_mul(res, x, y) __ckd_arithmetic(mul, res, x, y) + +#if defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 8 && \ + ((defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 406) || \ + defined(__llvm__)) && \ + !defined(__STRICT_ANSI__) +#define __ckd_dword __int128 +#else +#define __ckd_dword long long +#endif + +#define __ckd_arithmetic(op, res, x, y) \ + (sizeof(*(res)) == sizeof(int) ? __ckd_##op((int *)(res), (x), (y)) \ + : sizeof(*(res)) == sizeof(long) ? __ckd_##op##l((long *)(res), (x), (y)) \ + : sizeof(*(res)) == sizeof(__ckd_dword) \ + ? __ckd_##op##ll((__ckd_dword *)(res), (x), (y)) \ + : __ckd_trap()) + +__funline int __ckd_trap(void) { + volatile int __x = 0; + return 1 / __x; +} + +__funline int __ckd_add(int *__z, int __x, int __y) { + unsigned int __a, __b, __c; + *__z = __c = (__a = __x) + (__b = __y); + return ((__c ^ __a) & (__c ^ __b)) >> (sizeof(int) * CHAR_BIT - 1); +} +__funline int __ckd_addl(long *__z, long __x, long __y) { + unsigned long __a, __b, __c; + *__z = __c = (__a = __x) + (__b = __y); + return ((__c ^ __a) & (__c ^ __b)) >> (sizeof(long) * CHAR_BIT - 1); +} +__funline int __ckd_addll(__ckd_dword *__z, __ckd_dword __x, __ckd_dword __y) { + unsigned __ckd_dword __a, __b, __c; + *__z = __c = (__a = __x) + (__b = __y); + return ((__c ^ __a) & (__c ^ __b)) >> (sizeof(__ckd_dword) * CHAR_BIT - 1); +} + +__funline int __ckd_sub(int *__z, int __x, int __y) { + unsigned int __a, __b, __c; + *__z = __c = (__a = __x) - (__b = __y); + return ((__a ^ __b) & (__c ^ __a)) >> (sizeof(int) * CHAR_BIT - 1); +} +__funline int __ckd_subl(long *__z, long __x, long __y) { + unsigned long __a, __b, __c; + *__z = __c = (__a = __x) - (__b = __y); + return ((__a ^ __b) & (__c ^ __a)) >> (sizeof(long) * CHAR_BIT - 1); +} +__funline int __ckd_subll(__ckd_dword *__z, __ckd_dword __x, __ckd_dword __y) { + unsigned __ckd_dword __a, __b, __c; + *__z = __c = (__a = __x) - (__b = __y); + return ((__a ^ __b) & (__c ^ __a)) >> (sizeof(__ckd_dword) * CHAR_BIT - 1); +} + +int __mulosi4(int, int, int *); +long __mulodi4(long, long, int *); +__ckd_dword __muloti4(__ckd_dword, __ckd_dword, int *); + +__funline int __ckd_mul(int *__z, int __x, int __y) { + int __o; + *__z = __mulosi4(__x, __y, &__o); + return __o; +} +__funline int __ckd_mull(long *__z, long __x, long __y) { + int __o; + *__z = __mulodi4(__x, __y, &__o); + return __o; +} +__funline int __ckd_mulll(__ckd_dword *__z, __ckd_dword __x, __ckd_dword __y) { + int __o; + *__z = __muloti4(__x, __y, &__o); + return __o; +} + +#endif #endif /* COSMOPOLITAN_LIBC_STDCKDINT_H_ */ diff --git a/libc/type2str.h b/libc/type2str.h index ad48948b3..ec62d002b 100644 --- a/libc/type2str.h +++ b/libc/type2str.h @@ -17,6 +17,8 @@ unsigned long: "unsigned long", \ long long: "long long", \ unsigned long long: "unsigned long long", \ + __int128: "__int128", \ + unsigned __int128: "unsigned __int128", \ float: "float", \ double: "double", \ long double: "long double") diff --git a/test/libc/intrin/stdckdint_test.c b/test/libc/intrin/stdckdint_test.c new file mode 100644 index 000000000..3fdd5f8d7 --- /dev/null +++ b/test/libc/intrin/stdckdint_test.c @@ -0,0 +1,237 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/stdckdint.h" +#include "libc/intrin/kprintf.h" +#include "libc/macros.internal.h" +#include "libc/str/str.h" + +#define uintmax_t uint128_t + +#define T int +#define TBIT (sizeof(T) * CHAR_BIT - 1) +#define TMIN (((T) ~(T)0) > 1 ? (T)0 : (T)((uintmax_t)1 << TBIT)) +#define TMAX (((T) ~(T)0) > 1 ? (T) ~(T)0 : (T)(((uintmax_t)1 << TBIT) - 1)) +T Vint[] = {5, 4, 2, 77, 4, 7, + 0, 1, 2, 3, 4, -1, + -2, -3, -4, TMIN, TMIN + 1, TMIN + 2, + TMIN + 3, TMIN + 5, TMIN + 7, TMAX, TMAX - 1, TMAX - 2, + TMAX - 77, TMAX - 3, TMAX - 5, TMAX - 7, TMAX - 50, TMIN / 2, + TMAX / 2, TMAX / 2 - 3}; +#undef TMAX +#undef TMIN +#undef TBIT +#undef T + +#define T long +#define TBIT (sizeof(T) * CHAR_BIT - 1) +#define TMIN (((T) ~(T)0) > 1 ? (T)0 : (T)((uintmax_t)1 << TBIT)) +#define TMAX (((T) ~(T)0) > 1 ? (T) ~(T)0 : (T)(((uintmax_t)1 << TBIT) - 1)) +T Vlong[] = {5, 4, 2, 77, 4, 7, + 0, 1, 2, 3, 4, -1, + -2, -3, -4, TMIN, TMIN + 1, TMIN + 2, + TMIN + 3, TMIN + 5, TMIN + 7, TMAX, TMAX - 1, TMAX - 2, + TMAX - 77, TMAX - 3, TMAX - 5, TMAX - 7, TMAX - 50, TMIN / 2, + TMAX / 2, TMAX / 2 - 3}; +#undef TMAX +#undef TMIN +#undef TBIT +#undef T + +#define T __int128 +#define TBIT (sizeof(T) * CHAR_BIT - 1) +#define TMIN (((T) ~(T)0) > 1 ? (T)0 : (T)((uintmax_t)1 << TBIT)) +#define TMAX (((T) ~(T)0) > 1 ? (T) ~(T)0 : (T)(((uintmax_t)1 << TBIT) - 1)) +T Vint128[] = {5, 4, 2, 77, 4, 7, + 0, 1, 2, 3, 4, -1, + -2, -3, -4, TMIN, TMIN + 1, TMIN + 2, + TMIN + 3, TMIN + 5, TMIN + 7, TMAX, TMAX - 1, TMAX - 2, + TMAX - 77, TMAX - 3, TMAX - 5, TMAX - 7, TMAX - 50, TMIN / 2, + TMAX / 2, TMAX / 2 - 3}; +#undef TMAX +#undef TMIN +#undef TBIT +#undef T + +int failed; + +void test_ckd_add(void) { + for (int i = 0; i < ARRAYLEN(Vint); ++i) { + int x = Vint[i]; + for (int j = 0; j < ARRAYLEN(Vint); ++j) { + int z1, z2, o1, o2, y = Vint[i]; + o1 = ckd_add(&z1, x, y); + o2 = __builtin_add_overflow(x, y, &z2); + if (z1 != z2 && ++failed < 100) { + kprintf("add %d * %d = %d vs. %d\n", x, y, z1, z2); + } + if (o1 != o2 && ++failed < 100) { + kprintf("add %d * %d overflow disagreement\n", x, y); + } + } + } +} + +void test_ckd_sub(void) { + for (int i = 0; i < ARRAYLEN(Vint); ++i) { + int x = Vint[i]; + for (int j = 0; j < ARRAYLEN(Vint); ++j) { + int z1, z2, o1, o2, y = Vint[i]; + o1 = ckd_sub(&z1, x, y); + o2 = __builtin_sub_overflow(x, y, &z2); + if (z1 != z2 && ++failed < 100) { + kprintf("sub %d * %d = %d vs. %d\n", x, y, z1, z2); + } + if (o1 != o2 && ++failed < 100) { + kprintf("sub %d * %d overflow disagreement\n", x, y); + } + } + } +} + +void test_ckd_mul(void) { + for (int i = 0; i < ARRAYLEN(Vint); ++i) { + int x = Vint[i]; + for (int j = 0; j < ARRAYLEN(Vint); ++j) { + int z1, z2, o1, o2, y = Vint[i]; + o1 = ckd_mul(&z1, x, y); + o2 = __builtin_mul_overflow(x, y, &z2); + if (z1 != z2 && ++failed < 100) { + kprintf("mul %d * %d = %d vs. %d\n", x, y, z1, z2); + } + if (o1 != o2 && ++failed < 100) { + kprintf("mul %d * %d overflow disagreement\n", x, y); + } + } + } +} + +void test_ckd_add_long(void) { + for (long i = 0; i < ARRAYLEN(Vlong); ++i) { + long x = Vlong[i]; + for (long j = 0; j < ARRAYLEN(Vlong); ++j) { + long z1, z2, o1, o2, y = Vlong[i]; + o1 = ckd_add(&z1, x, y); + o2 = __builtin_add_overflow(x, y, &z2); + if (z1 != z2 && ++failed < 100) { + kprintf("add %d * %d = %d vs. %d\n", x, y, z1, z2); + } + if (o1 != o2 && ++failed < 100) { + kprintf("add %d * %d overflow disagreement\n", x, y); + } + } + } +} + +void test_ckd_sub_long(void) { + for (long i = 0; i < ARRAYLEN(Vlong); ++i) { + long x = Vlong[i]; + for (long j = 0; j < ARRAYLEN(Vlong); ++j) { + long z1, z2, o1, o2, y = Vlong[i]; + o1 = ckd_sub(&z1, x, y); + o2 = __builtin_sub_overflow(x, y, &z2); + if (z1 != z2 && ++failed < 100) { + kprintf("sub %d * %d = %d vs. %d\n", x, y, z1, z2); + } + if (o1 != o2 && ++failed < 100) { + kprintf("sub %d * %d overflow disagreement\n", x, y); + } + } + } +} + +void test_ckd_mul_long(void) { + for (long i = 0; i < ARRAYLEN(Vlong); ++i) { + long x = Vlong[i]; + for (long j = 0; j < ARRAYLEN(Vlong); ++j) { + long z1, z2, o1, o2, y = Vlong[i]; + o1 = ckd_mul(&z1, x, y); + o2 = __builtin_mul_overflow(x, y, &z2); + if (z1 != z2 && ++failed < 100) { + kprintf("mul %d * %d = %d vs. %d\n", x, y, z1, z2); + } + if (o1 != o2 && ++failed < 100) { + kprintf("mul %d * %d overflow disagreement\n", x, y); + } + } + } +} + +void test_ckd_add_int128(void) { + for (int128_t i = 0; i < ARRAYLEN(Vint128); ++i) { + int128_t x = Vint128[i]; + for (int128_t j = 0; j < ARRAYLEN(Vint128); ++j) { + int128_t z1, z2, o1, o2, y = Vint128[i]; + o1 = ckd_add(&z1, x, y); + o2 = __builtin_add_overflow(x, y, &z2); + if (z1 != z2 && ++failed < 100) { + kprintf("add %d * %d = %d vs. %d\n", x, y, z1, z2); + } + if (o1 != o2 && ++failed < 100) { + kprintf("add %d * %d overflow disagreement\n", x, y); + } + } + } +} + +void test_ckd_sub_int128(void) { + for (int128_t i = 0; i < ARRAYLEN(Vint128); ++i) { + int128_t x = Vint128[i]; + for (int128_t j = 0; j < ARRAYLEN(Vint128); ++j) { + int128_t z1, z2, o1, o2, y = Vint128[i]; + o1 = ckd_sub(&z1, x, y); + o2 = __builtin_sub_overflow(x, y, &z2); + if (z1 != z2 && ++failed < 100) { + kprintf("sub %d * %d = %d vs. %d\n", x, y, z1, z2); + } + if (o1 != o2 && ++failed < 100) { + kprintf("sub %d * %d overflow disagreement\n", x, y); + } + } + } +} + +void test_ckd_mul_int128(void) { + for (int128_t i = 0; i < ARRAYLEN(Vint128); ++i) { + int128_t x = Vint128[i]; + for (int128_t j = 0; j < ARRAYLEN(Vint128); ++j) { + int128_t z1, z2, o1, o2, y = Vint128[i]; + o1 = ckd_mul(&z1, x, y); + o2 = __builtin_mul_overflow(x, y, &z2); + if (z1 != z2 && ++failed < 100) { + kprintf("mul %d * %d = %d vs. %d\n", x, y, z1, z2); + } + if (o1 != o2 && ++failed < 100) { + kprintf("mul %d * %d overflow disagreement\n", x, y); + } + } + } +} + +int main(int argc, char *argv[]) { + test_ckd_add(); + test_ckd_sub(); + test_ckd_mul(); + test_ckd_add_long(); + test_ckd_sub_long(); + test_ckd_mul_long(); + test_ckd_add_int128(); + test_ckd_sub_int128(); + test_ckd_mul_int128(); + return failed ? 1 : 0; +} diff --git a/test/libc/intrin/test.mk b/test/libc/intrin/test.mk index afbd74181..ace6a84aa 100644 --- a/test/libc/intrin/test.mk +++ b/test/libc/intrin/test.mk @@ -59,6 +59,14 @@ o/$(MODE)/test/libc/intrin/%.com.dbg: \ $(APE_NO_MODIFY_SELF) @$(APELINK) +o/$(MODE)/test/libc/intrin/stdckdint_test.com.dbg: \ + $(TEST_LIBC_INTRIN_DEPS) \ + o/$(MODE)/test/libc/intrin/stdckdint_test.o \ + o/$(MODE)/test/libc/intrin/intrin.pkg \ + $(CRT) \ + $(APE_NO_MODIFY_SELF) + @$(APELINK) + # Test what happens when *NSYNC isn't linked. o/$(MODE)/test/libc/intrin/lock_test.com.dbg: \ $(TEST_LIBC_INTRIN_DEPS) \