Improve new C23 checked arithmetic feature

This commit is contained in:
Justine Tunney 2023-06-16 15:32:18 -07:00
parent 2a1c588826
commit 4eebd6b9dc
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
10 changed files with 341 additions and 15 deletions

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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_ */

View file

@ -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")

View file

@ -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;
}

View file

@ -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) \