/* Copyright (C) 2019-2020 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 . */ #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 #include #include #include #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 */