You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
341 lines
10 KiB
C
341 lines
10 KiB
C
/* Copyright (C) 2021,2022 fef <owo@fef.moe>. All rights reserved. */
|
|
|
|
#pragma once
|
|
|
|
#ifndef __clang__
|
|
#error "Unsupported compiler, please use clang"
|
|
#endif
|
|
|
|
#include <gay/cdefs.h>
|
|
#include <gay/types.h>
|
|
|
|
/**
|
|
* @defgroup arith Safe integer arithmetic primitives
|
|
*
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* @brief Perform an integer addition, store the result in `outptr`,
|
|
* and return whether the operation resulted in an overflow.
|
|
*
|
|
* @param outptr Where to store the result
|
|
* @param a First summand
|
|
* @param b Second summand
|
|
* @returns `true` if the addition overflowed, `false` otherwise
|
|
*/
|
|
#define add_overflow(outptr, a, b) __builtin_add_overflow(a, b, outptr)
|
|
|
|
/**
|
|
* @brief Add two `signed int`s, store the sum in `outptr`,
|
|
* and return whether the operation overflowed.
|
|
*
|
|
* @param result Where to store the result
|
|
* @param a First summand
|
|
* @param b Second summand
|
|
* @returns `true` if the addition overflowed, `false` otherwise
|
|
*/
|
|
__always_inline bool iadd_overflow(signed int *result, signed int a, signed int b)
|
|
{
|
|
return __builtin_sadd_overflow(a, b, result);
|
|
}
|
|
|
|
/**
|
|
* @brief Add two `signed long`s, store the sum in `outptr`,
|
|
* and return whether the operation overflowed.
|
|
*
|
|
* @param result Where to store the result
|
|
* @param a First summand
|
|
* @param b Second summand
|
|
* @returns `true` if the addition overflowed, `false` otherwise
|
|
*/
|
|
__always_inline bool iaddl_overflow(signed long *result, signed int a, signed int b)
|
|
{
|
|
return __builtin_saddl_overflow(a, b, result);
|
|
}
|
|
|
|
/**
|
|
* @brief Add the two `signed long long` arguments `a` and `b`,
|
|
* store the sum in `outptr`, and return whether the operation overflowed.
|
|
*
|
|
* @param result Where to store the result
|
|
* @param a First summand
|
|
* @param b Second summand
|
|
* @returns `true` if the addition overflowed, `false` otherwise
|
|
*/
|
|
__always_inline bool iaddll_overflow(signed long long *result, signed int a, signed int b)
|
|
{
|
|
return __builtin_saddll_overflow(a, b, result);
|
|
}
|
|
|
|
/**
|
|
* @brief Add the two `unsigned int` arguments `a` and `b`,
|
|
* store the sum in `outptr`, and return whether the operation overflowed.
|
|
*
|
|
* @param result Where to store the result
|
|
* @param a First summand
|
|
* @param b Second summand
|
|
* @returns `true` if the addition overflowed, `false` otherwise
|
|
*/
|
|
__always_inline bool uadd_overflow(unsigned int *result, unsigned int a, unsigned int b)
|
|
{
|
|
return __builtin_uadd_overflow(a, b, result);
|
|
}
|
|
|
|
/**
|
|
* @brief Add the two `unsigned long` arguments `a` and `b`,
|
|
* store the sum in `outptr`, and return whether the operation overflowed.
|
|
*
|
|
* @param result Where to store the result
|
|
* @param a First summand
|
|
* @param b Second summand
|
|
* @returns `true` if the addition overflowed, `false` otherwise
|
|
*/
|
|
__always_inline bool uaddl_overflow(unsigned long *result, unsigned long a, unsigned long b)
|
|
{
|
|
return __builtin_uaddl_overflow(a, b, result);
|
|
}
|
|
|
|
/**
|
|
* @brief Add the two `unsigned long long` arguments `a` and `b`,
|
|
* store the sum in `outptr`, and return whether the operation overflowed.
|
|
*
|
|
* @param result Where to store the result
|
|
* @param a First summand
|
|
* @param b Second summand
|
|
* @returns `true` if the addition overflowed, `false` otherwise
|
|
*/
|
|
__always_inline bool uaddll_overflow(unsigned long long *result,
|
|
unsigned long long a, unsigned long long b)
|
|
{
|
|
return __builtin_uaddll_overflow(a, b, result);
|
|
}
|
|
|
|
/**
|
|
* @brief Test whether the sum of `a` and `b` would overflow for
|
|
* a given `type` of result operand, but discharge the result.
|
|
*
|
|
* @param type Type that would store the result
|
|
* @param a First summand
|
|
* @param b Second summand
|
|
* @returns `true` if the sum would **not** fit inside `type`, `false` otherwise
|
|
*/
|
|
#define add_test_overflow(type, a, b) __builtin_add_p(a, b, type)
|
|
|
|
/**
|
|
* @brief Perform an integer subtraction and store the result to `outptr`,
|
|
* and return whether the operation resulted in an underflow.
|
|
*
|
|
* @param outptr Where to store the result
|
|
* @param a The minuend
|
|
* @param b The subtrahend
|
|
* @returns `true` if the difference underflowed, `false` otherwise
|
|
*/
|
|
#define sub_underflow(outptr, a, b) __builtin_sub_overflow(a, b, outptr)
|
|
|
|
/**
|
|
* @brief Add two `signed int`s, store the result,
|
|
* and return whether the operation overflowed.
|
|
*
|
|
* @param result Where to store the result
|
|
* @param a The minuend
|
|
* @param b The subtrahend
|
|
* @returns `true` if the difference underflowed, `false` otherwise
|
|
*/
|
|
__always_inline bool isub_underflow(signed int *result, signed int a, signed int b)
|
|
{
|
|
return __builtin_ssub_overflow(a, b, result);
|
|
}
|
|
|
|
/**
|
|
* @brief Add two `signed long`s, store the result,
|
|
* and return whether the operation overflowed.
|
|
*
|
|
* @param result Where to store the result
|
|
* @param a The minuend
|
|
* @param b The subtrahend
|
|
* @returns `true` if the difference underflowed, `false` otherwise
|
|
*/
|
|
__always_inline bool isubl_underflow(signed long *result, signed long a, signed long b)
|
|
{
|
|
return __builtin_ssubl_overflow(a, b, result);
|
|
}
|
|
|
|
/**
|
|
* @brief Add the two `signed long long` arguments `a` and `b`,
|
|
* store the result, and return whether the operation overflowed.
|
|
*
|
|
* @param result Where to store the result
|
|
* @param a The minuend
|
|
* @param b The subtrahend
|
|
* @returns `true` if the difference underflowed, `false` otherwise
|
|
*/
|
|
__always_inline bool isubll_underflow(signed long long *result,
|
|
signed long long a, signed long long b)
|
|
{
|
|
return __builtin_ssubll_overflow(a, b, result);
|
|
}
|
|
|
|
/**
|
|
* @brief Add the two `unsigned int` arguments `a` and `b`,
|
|
* store the result, and return whether the operation overflowed.
|
|
*
|
|
* @param result Where to store the result
|
|
* @param a The minuend
|
|
* @param b The subtrahend
|
|
* @returns `true` if the difference underflowed, `false` otherwise
|
|
*/
|
|
__always_inline bool usub_underflow(unsigned int *result, unsigned int a, unsigned int b)
|
|
{
|
|
return __builtin_usub_overflow(a, b, result);
|
|
}
|
|
|
|
/**
|
|
* @brief Add the two `unsigned long` arguments `a` and `b`,
|
|
* store the result, and return whether the operation overflowed.
|
|
*
|
|
* @param result Where to store the result
|
|
* @param a The minuend
|
|
* @param b The subtrahend
|
|
* @returns `true` if the difference underflowed, `false` otherwise
|
|
*/
|
|
__always_inline bool usubl_underflow(unsigned long *result, unsigned long a, unsigned long b)
|
|
{
|
|
return __builtin_usubl_overflow(a, b, result);
|
|
}
|
|
|
|
/**
|
|
* @brief Add the two `unsigned long long` arguments `a` and `b`,
|
|
* store the result, and return whether the operation overflowed.
|
|
*
|
|
* @param result Where to store the result
|
|
* @param a The minuend
|
|
* @param b The subtrahend
|
|
* @returns `true` if the difference underflowed, `false` otherwise
|
|
*/
|
|
__always_inline bool usubll_underflow(unsigned long long *result,
|
|
unsigned long long a, unsigned long long b)
|
|
{
|
|
return __builtin_usubll_overflow(a, b, result);
|
|
}
|
|
|
|
/**
|
|
* @brief Test whether the difference between `a` and `b` would underflow for
|
|
* a given `type` of result operand, but discharge the result.
|
|
*
|
|
* @param type Type that would store the result
|
|
* @param a The minuend
|
|
* @param b The subtrahend
|
|
* @returns `true` if the difference would underflow, `false` otherwise
|
|
*/
|
|
#define sub_test_underflow(type, a, b) __builtin_sub_p(a, b, type)
|
|
|
|
/**
|
|
* @brief Perform an integer multiplication, store the result in `outptr`,
|
|
* and return whether the operation resulted in an overflow.
|
|
*
|
|
* @param outptr Where to store the result
|
|
* @param a First factor
|
|
* @param b Second factor
|
|
* @returns `true` if the multiplication overflowed, `false` otherwise
|
|
*/
|
|
#define mul_overflow(outptr, a, b) __builtin_mul_overflow(a, b, outptr)
|
|
|
|
/**
|
|
* @brief Multiply two `signed int`s, store the result,
|
|
* and return whether the operation overflowed.
|
|
*
|
|
* @param outptr Where to store the result
|
|
* @param a First factor
|
|
* @param b Second factor
|
|
* @returns `true` if the addition overflowed, `false` otherwise
|
|
*/
|
|
__always_inline bool imul_overflow(signed int *result, signed int a, signed int b)
|
|
{
|
|
return __builtin_smul_overflow(a, b, result);
|
|
}
|
|
|
|
/**
|
|
* @brief Multiply two `signed long`s, store the result,
|
|
* and return whether the operation overflowed.
|
|
*
|
|
* @param outptr Where to store the result
|
|
* @param a First factor
|
|
* @param b Second factor
|
|
* @returns `true` if the addition overflowed, `false` otherwise
|
|
*/
|
|
__always_inline bool imul_l_overflow(signed long *result, signed int a, signed int b)
|
|
{
|
|
return __builtin_smull_overflow(a, b, result);
|
|
}
|
|
|
|
/**
|
|
* @brief Multiply the two `signed long long` arguments `a` and `b`,
|
|
* store the result, and return whether the operation overflowed.
|
|
*
|
|
* @param outptr Where to store the result
|
|
* @param a First factor
|
|
* @param b Second factor
|
|
* @returns `true` if the addition overflowed, `false` otherwise
|
|
*/
|
|
__always_inline bool imul_ll_overflow(signed long long *result, signed int a, signed int b)
|
|
{
|
|
return __builtin_smulll_overflow(a, b, result);
|
|
}
|
|
|
|
/**
|
|
* @brief Multiply the two `unsigned int` arguments `a` and `b`,
|
|
* store the result, and return whether the operation overflowed.
|
|
*
|
|
* @param result Where to store the result
|
|
* @param a First factor
|
|
* @param b Second factor
|
|
* @returns `true` if the addition overflowed, `false` otherwise
|
|
*/
|
|
__always_inline bool umul_overflow(unsigned int *result, unsigned int a, unsigned int b)
|
|
{
|
|
return __builtin_umul_overflow(a, b, result);
|
|
}
|
|
|
|
/**
|
|
* @brief Multiply the two `unsigned long` arguments `a` and `b`,
|
|
* store the result, and return whether the operation overflowed.
|
|
*
|
|
* @param result Where to store the result
|
|
* @param a First factor
|
|
* @param b Second factor
|
|
* @returns `true` if the addition overflowed, `false` otherwise
|
|
*/
|
|
__always_inline bool umul_l_overflow(unsigned long *result, unsigned long a, unsigned long b)
|
|
{
|
|
return __builtin_umull_overflow(a, b, result);
|
|
}
|
|
|
|
/**
|
|
* @brief Multiply the two `unsigned long long` arguments `a` and `b`,
|
|
* store the result, and return whether the operation overflowed.
|
|
*
|
|
* @param result Where to store the result
|
|
* @param a First factor
|
|
* @param b Second factor
|
|
* @returns `true` if the addition overflowed, `false` otherwise
|
|
*/
|
|
__always_inline bool umul_ll_overflow(unsigned long long *result,
|
|
unsigned long long a, unsigned long long b)
|
|
{
|
|
return __builtin_umulll_overflow(a, b, result);
|
|
}
|
|
|
|
/**
|
|
* @brief Test whether the product of `a` and `b` would overflow for
|
|
* a given `type` of result operand.
|
|
*
|
|
* @param type Type that would store the result
|
|
* @param a First factor
|
|
* @param b Second factor
|
|
* @returns `true` if the sum would **not** fit inside `type`, `false` otherwise
|
|
*/
|
|
#define mul_test_overflow(type, a, b) __builtin_mul_p(a, b, type)
|
|
|
|
/** @} */
|