blob: 2fc48c696d32037bfc3f6a8d5728ccf2a2584e10 [file] [log] [blame]
/* Copyright (C) 2019-2021 Free Software Foundation, Inc.
This file is part of LIBF7, which is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
Under Section 7 of GPL version 3, you are granted additional
permissions described in the GCC Runtime Library Exception, version
3.1, as published by the Free Software Foundation.
You should have received a copy of the GNU General Public License and
a copy of the GCC Runtime Library Exception along with this program;
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
<http://www.gnu.org/licenses/>. */
#ifndef LIBF7_H
#define LIBF7_H
#define IN_LIBF7_H
#include "f7-renames.h"
#define F7_MANT_BYTES 7
#define F7_MANT_BITS (8 * F7_MANT_BYTES)
/* Using the following GCC features:
-- Unnamed structs / unions (GNU-C)
-- Fixed-point types (GNU-C)
-- Inline asm
-- Setting assembler names by means of __asm (GNU-C).
-- Attributes: alias, always_inline, const, noinline, unused,
progmem, pure, weak, warning
-- GCC built-ins: __builtin_abort, __builtin_constant_p
-- AVR built-ins: __builtin_avr_bitsr, __builtin_avr_rbits
*/
/* We have 2 kinds of flags:
A) The flags that are stored in f7_t.flags:
-- f7_t.is_nan (NaN)
-- f7_t.is_inf (+Inf or -Inf)
-- f7_t.sign (negative or -Inf).
B) The flags that are returned by f7_classify(). This are the
flags from A) together with
-- _zero: indicate that a number is zero.
*/
#define F7_FLAGNO_sign 0
#define F7_FLAGNO_zero 1
#define F7_FLAGNO_nan 2
#define F7_FLAGNO_inf 7
#define F7_HAVE_Inf 1
// Flags that might be set by f7_classify().
#define F7_FLAG_sign (1 << F7_FLAGNO_sign)
#define F7_FLAG_zero (1 << F7_FLAGNO_zero)
#define F7_FLAG_nan (1 << F7_FLAGNO_nan)
#define F7_FLAG_inf (F7_HAVE_Inf << F7_FLAGNO_inf)
// Flags that might be set in f7_t.flags.
#define F7_FLAGS (F7_FLAG_inf | F7_FLAG_nan | F7_FLAG_sign)
#if !defined __ASSEMBLER__
#ifndef IN_LIBGCC2
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#else
/* Do not assume that we have std headers when we build libgcc. */
typedef __UINT64_TYPE__ uint64_t;
typedef __UINT32_TYPE__ uint32_t;
typedef __UINT16_TYPE__ uint16_t;
typedef __UINT8_TYPE__ uint8_t;
typedef __INT64_TYPE__ int64_t;
typedef __INT32_TYPE__ int32_t;
typedef __INT16_TYPE__ int16_t;
typedef __INT8_TYPE__ int8_t;
typedef _Bool bool;
#define false 0
#define true 1
#define INT8_MIN (-1 - __INT8_MAX__)
#define INT16_MAX __INT16_MAX__
#define NULL ((void*) 0)
#endif /* IN_LIBGCC2 */
#include "asm-defs.h"
#ifdef __cplusplus
extern "C" {
#define _Static_assert(X, Y) static_assert (X)
#endif // C++
#define F7_INLINE inline __attribute__((__always_inline__))
#define F7_NOINLINE __attribute__((__noinline__))
#define F7_WEAK __attribute__((__weak__))
#define F7_PURE __attribute__((__pure__))
#define F7_UNUSED __attribute__((__unused__))
#define F7_CONST __attribute__((__const__))
#define F7_STRINGY2(X) #X
#define F7_STRINGY(X) F7_STRINGY2(X)
#define F7ASM(X) __asm (F7_STRINGY2(X))
typedef struct f7_t
{
union
{
struct
{
uint8_t sign :1;
uint8_t reserved1 :1;
uint8_t is_nan :1;
uint8_t reserved2 :4;
uint8_t is_inf :1;
};
uint8_t flags;
};
uint8_t mant[7];
int16_t expo;
} f7_t;
typedef uint64_t f7_double_t;
#define F7_MANT_HI4(X) \
(*(uint32_t*) & (X)->mant[F7_MANT_BYTES - 4])
#define F7_MANT_CONST_HI4(X) \
(*(const uint32_t*) & (X)->mant[F7_MANT_BYTES - 4])
#define F7_MANT_HI2(X) \
(*(uint16_t*) & (X)->mant[F7_MANT_BYTES - 2])
static F7_INLINE F7_PURE
uint8_t f7_classify (const f7_t *aa)
{
extern void f7_classify_asm (void);
register uint8_t rclass __asm ("r24");
__asm ("%~call %x[f]"
: "=r" (rclass)
: [f] "i" (f7_classify_asm), "z" (aa));
return rclass;
}
// +Inf or -Inf
static F7_INLINE
bool f7_class_inf (uint8_t c)
{
#if defined (F7_HAVE_Inf) && F7_HAVE_Inf == 1
return c >= F7_FLAG_inf;
#elif defined (F7_HAVE_Inf) && F7_HAVE_Inf == 0
(void) c;
return false;
#else
#error macro F7_HAVE_Inf must be defined to 0 or to 1.
#endif // Have Inf
}
static F7_INLINE
bool f7_is_inf (const f7_t *aa)
{
return f7_class_inf (aa->flags);
}
// Not-a-Number (NaN).
static F7_INLINE
bool f7_class_nan (uint8_t c)
{
return c & F7_FLAG_nan;
}
static F7_INLINE
bool f7_is_nan (const f7_t *aa)
{
return f7_class_nan (aa->flags);
}
// Some number
static F7_INLINE
bool f7_class_number (uint8_t c)
{
return c <= (F7_FLAG_sign | F7_FLAG_zero);
}
static F7_INLINE
bool f7_is_number (const f7_t *aa)
{
return f7_class_number (f7_classify (aa));
}
// Zero
static F7_INLINE
bool f7_class_zero (uint8_t c)
{
return c & F7_FLAG_zero;
}
static F7_INLINE
bool f7_is_zero (const f7_t *aa)
{
return f7_class_zero (f7_classify (aa));
}
// A non-zero number.
static F7_INLINE
bool f7_class_nonzero (uint8_t c)
{
return c <= F7_FLAG_sign;
}
static F7_INLINE
bool f7_is_nonzero (const f7_t *aa)
{
return f7_class_nonzero (f7_classify (aa));
}
static F7_INLINE
bool f7_class_sign (uint8_t c)
{
return c & F7_FLAG_sign;
}
static F7_INLINE
bool f7_signbit (const f7_t *aa)
{
return aa->flags & F7_FLAG_sign;
}
static F7_INLINE
void f7_set_sign (f7_t *cc, bool sign)
{
_Static_assert (F7_FLAGNO_sign == 0, "");
cc->flags &= ~F7_FLAG_sign;
cc->flags |= sign;
}
static F7_INLINE
void f7_set_nan (f7_t *cc)
{
cc->flags = F7_FLAG_nan;
}
static F7_INLINE
void f7_clr (f7_t *cc)
{
extern void f7_clr_asm (void);
__asm ("%~call %x[f]"
:
: [f] "i" (f7_clr_asm), "z" (cc)
: "memory");
}
static F7_INLINE
f7_t* f7_copy (f7_t *cc, const f7_t *aa)
{
extern void f7_copy_asm (void);
__asm ("%~call %x[f]"
:
: [f] "i" (f7_copy_asm), "z" (cc), "x" (aa)
: "memory");
return cc;
}
static F7_INLINE
f7_t* f7_copy_P (f7_t *cc, const f7_t *aa)
{
extern void f7_copy_P_asm (void);
__asm ("%~call %x[f]"
:
: [f] "i" (f7_copy_P_asm), "x" (cc), "z" (aa)
: "memory");
return cc;
}
static F7_INLINE
void f7_copy_mant (f7_t *cc, const f7_t *aa)
{
extern void f7_copy_mant_asm (void);
__asm ("%~call %x[f]"
:
: [f] "i" (f7_copy_mant_asm), "z" (cc), "x" (aa)
: "memory");
}
static F7_INLINE
void f7_set_inf (f7_t *cc, bool sign)
{
#if F7_HAVE_Inf == 1
cc->flags = F7_FLAG_inf | sign;
#else
(void) sign;
cc->flags = F7_FLAG_nan;
#endif // Have Inf
}
static F7_INLINE
bool f7_msbit (const f7_t *aa)
{
return aa->mant[F7_MANT_BYTES - 1] & 0x80;
}
// Quick test against 0 if A is known to be a number (neither NaN nor Inf).
static F7_INLINE
bool f7_is0 (const f7_t *aa)
{
return 0 == f7_msbit (aa);
}
static F7_INLINE
int8_t f7_cmp_mant (const f7_t *aa, const f7_t *bb)
{
extern void f7_cmp_mant_asm (void);
register int8_t r24 __asm ("r24");
__asm ("%~call %x[f] ;; %1 %3"
: "=r" (r24)
: [f] "i" (f7_cmp_mant_asm), "x" (aa), "z" (bb));
return r24;
}
static F7_INLINE
bool f7_store_expo (f7_t *cc, int16_t expo)
{
extern void f7_store_expo_asm (void);
register bool r24 __asm ("r24");
register int16_t rexpo __asm ("r24") = expo;
__asm ("%~call %x[f] ;; %0 %2 %3"
: "=r" (r24)
: [f] "i" (f7_store_expo_asm), "z" (cc), "r" (rexpo));
return r24;
}
static F7_INLINE
f7_t* f7_abs (f7_t *cc, const f7_t *aa)
{
f7_copy (cc, aa);
f7_set_sign (cc, 0);
return cc;
}
F7_PURE extern int8_t f7_cmp (const f7_t*, const f7_t*);
F7_PURE extern bool f7_lt_impl (const f7_t*, const f7_t*);
F7_PURE extern bool f7_le_impl (const f7_t*, const f7_t*);
F7_PURE extern bool f7_gt_impl (const f7_t*, const f7_t*);
F7_PURE extern bool f7_ge_impl (const f7_t*, const f7_t*);
F7_PURE extern bool f7_ne_impl (const f7_t*, const f7_t*);
F7_PURE extern bool f7_eq_impl (const f7_t*, const f7_t*);
F7_PURE extern bool f7_unord_impl (const f7_t*, const f7_t*);
static F7_INLINE
bool f7_lt (const f7_t *aa, const f7_t *bb)
{
return 2 & f7_cmp (aa, bb);
}
static F7_INLINE
bool f7_gt (const f7_t *aa, const f7_t *bb)
{
return 1 == f7_cmp (aa, bb);
}
static F7_INLINE
bool f7_le (const f7_t *aa, const f7_t *bb)
{
int8_t c = f7_cmp (aa, bb);
return (uint8_t) (c + 1) <= 1;
}
static F7_INLINE
bool f7_ge (const f7_t *aa, const f7_t *bb)
{
return f7_cmp (aa, bb) >= 0;
}
static F7_INLINE
bool f7_unordered (const f7_t *aa, const f7_t *bb)
{
return INT8_MIN == f7_cmp (aa, bb);
}
static F7_INLINE
bool f7_ordered (const f7_t *aa, const f7_t *bb)
{
return INT8_MIN != f7_cmp (aa, bb);
}
static F7_INLINE
bool f7_eq (const f7_t *aa, const f7_t *bb)
{
return 0 == f7_cmp (aa, bb);
}
static F7_INLINE
bool f7_ne (const f7_t *aa, const f7_t *bb)
{
return 1 & f7_cmp (aa, bb);
}
extern void f7_clr (f7_t*);
__attribute__((warning ("foo_u16"))) void foo_u16 (void);
__attribute__((warning ("foo_s16"))) void foo_s16 (void);
extern f7_t* f7_set_s16_impl (f7_t*, int16_t);
extern f7_t* f7_set_u16_impl (f7_t*, uint16_t);
static F7_INLINE
f7_t* f7_set_u16_worker (f7_t *cc, uint16_t u16)
{
if (__builtin_constant_p (u16))
{
if (u16 == 0)
return cc;
uint8_t off = __builtin_clz (u16);
if (15 - off)
* (uint8_t*) & cc->expo = (uint8_t) (15 - off);
u16 <<= off;
if (u16 & 0xff)
cc->mant[5] = (uint8_t) u16;
if (u16 & 0xff00)
cc->mant[6] = (uint8_t) (u16 >> 8);
return cc;
}
else
{
foo_u16();
__builtin_abort();
return NULL;
}
}
static F7_INLINE
f7_t* f7_set_u16 (f7_t *cc, uint16_t u16)
{
if (__builtin_constant_p (u16))
{
f7_clr (cc);
return f7_set_u16_worker (cc, u16);
}
return f7_set_u16_impl (cc, u16);
}
static F7_INLINE
f7_t* f7_set_s16 (f7_t *cc, int16_t s16)
{
if (__builtin_constant_p (s16))
{
f7_clr (cc);
uint16_t u16 = (uint16_t) s16;
if (s16 < 0)
{
u16 = -u16;
cc->flags = F7_FLAG_sign;
}
return f7_set_u16_worker (cc, u16);
}
return f7_set_s16_impl (cc, s16);
}
static F7_INLINE
void f7_set_eps (f7_t *cc, uint8_t eps, bool sign)
{
cc = f7_set_u16 (cc, 1);
if (!__builtin_constant_p (sign) || sign)
cc->flags = sign;
cc->mant[0] = eps;
}
static F7_INLINE
f7_t* f7_set_1pow2 (f7_t *cc, int16_t expo, bool sign)
{
cc = f7_set_u16 (cc, 1);
cc->expo = expo;
if (!__builtin_constant_p (sign) || sign)
cc->flags = sign;
return cc;
}
static F7_INLINE
f7_t* f7_set_u64 (f7_t *cc, uint64_t u64)
{
extern f7_t* f7_set_u64_asm (uint64_t, f7_t*);
return f7_set_u64_asm (u64, cc);
}
static F7_INLINE
f7_t* f7_set_s64 (f7_t *cc, int64_t s64)
{
extern f7_t* f7_set_s64_asm (int64_t, f7_t*);
return f7_set_s64_asm (s64, cc);
}
extern void f7_set_double_impl (f7_double_t, f7_t*);
static F7_INLINE
void f7_set_double (f7_t *cc, f7_double_t val64)
{
f7_set_double_impl (val64, cc);
}
extern f7_t* f7_init_impl (uint64_t, uint8_t, f7_t*, int16_t);
static F7_INLINE
f7_t* f7_init (f7_t *cc, uint8_t flags, uint64_t mant, int16_t expo)
{
return f7_init_impl (mant, flags, cc, expo);
}
extern f7_t* f7_set_s32 (f7_t*, int32_t);
extern f7_t* f7_set_u16 (f7_t*, uint16_t);
extern f7_t* f7_set_u32 (f7_t*, uint32_t);
extern void f7_set_float (f7_t*, float);
extern void f7_set_pdouble (f7_t*, const f7_double_t*);
F7_PURE extern int16_t f7_get_s16 (const f7_t*);
F7_PURE extern int32_t f7_get_s32 (const f7_t*);
F7_PURE extern int64_t f7_get_s64 (const f7_t*);
F7_PURE extern uint16_t f7_get_u16 (const f7_t*);
F7_PURE extern uint32_t f7_get_u32 (const f7_t*);
F7_PURE extern uint64_t f7_get_u64 (const f7_t*);
F7_PURE extern float f7_get_float (const f7_t*);
F7_PURE extern f7_double_t f7_get_double (const f7_t*);
#if USE_LPM == 1
#define F7_PGMSPACE __attribute__((__progmem__))
#define f7_copy_flash f7_copy_P
#define f7_const(X, NAME) \
f7_copy_P ((X), & F7_(const_ ## NAME ## _P))
#define F7_CONST_DEF(NAME, FLAGS, M0, M1, M2, M3, M4, M5, M6, EXPO) \
extern const f7_t F7_(const_ ## NAME ## _P);
#include "libf7-const.def"
#undef F7_CONST_DEF
#else
#define F7_PGMSPACE // Empty
#define f7_copy_flash f7_copy
#define f7_const(X, NAME) \
f7_copy ((X), & F7_(const_ ## NAME))
#define F7_CONST_DEF(NAME, FLAGS, M0, M1, M2, M3, M4, M5, M6, EXPO) \
extern const f7_t F7_(const_ ## NAME);
#include "libf7-const.def"
#undef F7_CONST_DEF
#endif // USE_LPM
// Basic floating point arithmetic:
// double output <=> f7_t*
// double input <=> const f7_t*
extern f7_t* f7_neg (f7_t*, const f7_t*);
extern void f7_add (f7_t*, const f7_t*, const f7_t*);
extern void f7_sub (f7_t*, const f7_t*, const f7_t*);
extern void f7_mul (f7_t*, const f7_t*, const f7_t*);
extern void f7_div (f7_t*, const f7_t*, const f7_t*);
// Analogies of functions from math.h:
// double output <=> f7_t*
// double input <=> const f7_t*
extern void f7_fabs (f7_t*, const f7_t*);
extern void f7_fmod (f7_t*, const f7_t*, const f7_t*);
extern void f7_frexp (f7_t*, const f7_t*, int*);
extern void f7_exp (f7_t*, const f7_t*);
extern void f7_log (f7_t*, const f7_t*);
extern void f7_pow (f7_t*, const f7_t*, const f7_t*);
extern void f7_sqrt (f7_t*, const f7_t*);
extern void f7_cbrt (f7_t*, const f7_t*);
extern void f7_hypot (f7_t*, const f7_t*, const f7_t*);
extern f7_t* f7_ldexp (f7_t*, const f7_t*, int);
extern f7_t* f7_fmax (f7_t*, const f7_t*, const f7_t*);
extern f7_t* f7_fmin (f7_t*, const f7_t*, const f7_t*);
extern f7_t* f7_trunc (f7_t*, const f7_t*);
extern f7_t* f7_floor (f7_t*, const f7_t*);
extern void f7_ceil (f7_t*, const f7_t*);
extern void f7_round (f7_t*, const f7_t*);
extern void f7_sin (f7_t*, const f7_t*);
extern void f7_cos (f7_t*, const f7_t*);
extern void f7_tan (f7_t*, const f7_t*);
extern void f7_atan (f7_t*, const f7_t*);
extern void f7_asin (f7_t*, const f7_t*);
extern void f7_acos (f7_t*, const f7_t*);
extern void f7_tanh (f7_t*, const f7_t*);
extern void f7_sinh (f7_t*, const f7_t*);
extern void f7_cosh (f7_t*, const f7_t*);
extern void f7_log2 (f7_t*, const f7_t*);
extern void f7_log10 (f7_t*, const f7_t*);
extern void f7_exp10 (f7_t*, const f7_t*);
extern void f7_pow10 (f7_t*, const f7_t*);
// Just prototypes, not implemented yet.
extern void f7_atan2 (f7_t*, const f7_t*, const f7_t*);
extern long f7_lrint (const f7_t*);
extern long f7_lround (const f7_t*);
// Helper functions, aliases, convenience.
extern void f7_div1 (f7_t*, const f7_t*);
extern void f7_square (f7_t*, const f7_t*);
extern void f7_powi (f7_t*, const f7_t*, int);
extern f7_t* f7_max (f7_t*, const f7_t*, const f7_t*);
extern f7_t* f7_min (f7_t*, const f7_t*, const f7_t*);
extern f7_t* f7_truncx (f7_t*, const f7_t*, bool);
extern void f7_cotan (f7_t*, const f7_t*);
extern void f7_sincos (f7_t*, f7_t*, const f7_t*);
extern void f7_asinacos (f7_t*, const f7_t*, uint8_t);
extern void f7_sinhcosh (f7_t*, const f7_t*, bool);
extern void f7_horner (f7_t*, const f7_t*, uint8_t, const f7_t *coeff, f7_t*);
extern void f7_mul_noround (f7_t*, const f7_t*, const f7_t*);
extern void f7_clr_mant_lsbs (f7_t*, const f7_t*, uint8_t) F7ASM(f7_clr_mant_lsbs_asm);
F7_PURE extern int8_t f7_cmp_unordered (const f7_t*, const f7_t*, bool);
F7_PURE extern int8_t f7_cmp_abs (const f7_t*, const f7_t*);
F7_PURE extern bool f7_abscmp_msb_ge (const f7_t*, uint8_t msb, int16_t expo);
extern void f7_addsub (f7_t*, const f7_t*, const f7_t*, bool neg_b);
extern void f7_madd_msub (f7_t*, const f7_t*, const f7_t*, const f7_t*, bool);
extern void f7_madd (f7_t*, const f7_t*, const f7_t*, const f7_t*);
extern void f7_msub (f7_t*, const f7_t*, const f7_t*, const f7_t*);
extern uint8_t f7_mulx (f7_t*, const f7_t*, const f7_t*, bool);
extern void f7_divx (f7_t*, const f7_t*, const f7_t*, uint8_t);
extern void f7_logx (f7_t*, const f7_t*, const f7_t*);
extern f7_t* f7_minmax (f7_t*, const f7_t*, const f7_t*, bool);
// Idem:
// f7_Ifunc (y) = f7_func (y, y)
// f7_Ifunc (y, x) = f7_func (y, y, x)
extern void f7_Iadd (f7_t*, const f7_t*);
extern void f7_Isub (f7_t*, const f7_t*);
extern void f7_Imul (f7_t*, const f7_t*);
extern void f7_Idiv (f7_t*, const f7_t*);
extern void f7_IRsub (f7_t*, const f7_t*);
extern void f7_Ineg (f7_t*);
extern void f7_Isqrt (f7_t*);
extern void f7_Isquare (f7_t*);
extern f7_t* f7_Ildexp (f7_t*, int);
// Protoypes for some functions from libf7-asm.sx.
F7_CONST extern uint16_t f7_sqrt16_round (uint16_t) F7ASM(f7_sqrt16_round_asm);
F7_CONST extern uint8_t f7_sqrt16_floor (uint16_t) F7ASM(f7_sqrt16_floor_asm);
extern void f7_addsub_mant_scaled_asm (f7_t*, const f7_t*, const f7_t*, uint8_t);
extern uint8_t f7_mul_mant_asm (f7_t*, const f7_t*, const f7_t*, uint8_t);
extern void f7_sqrt_approx_asm (f7_t*, const f7_t*);
extern uint64_t f7_lshrdi3 (uint64_t, uint8_t) F7ASM(f7_lshrdi3_asm);
extern uint64_t f7_ashldi3 (uint64_t, uint8_t) F7ASM(f7_ashldi3_asm);
// Normalize a non-Inf, non-NaN value. Sets .sign to 0.
extern f7_t* f7_normalize_asm (f7_t*);
// Dumping.
#ifndef IN_LIBGCC2
extern void f7_dump (const f7_t*);
extern void f7_dump_mant (const f7_t*);
extern void f7_put_C (const f7_t*, FILE*);
extern void f7_put_CDEF (const char *name, const f7_t*, FILE*);
#endif /* IN_LIBGCC2 */
#ifdef __cplusplus
} // extern "C"
#include "libf7-class.h"
#endif // C++
#endif /* __ASSEMBLER__ */
#undef IN_LIBF7_H
#endif /* LIBF7_H */