/**
|
* Generic arithmetic/conversion routines.
|
* Copyright © 2005 Stelian Pop.
|
* Copyright © 2005 Gilles Chanteperdrix.
|
*
|
* This library is free software; you can redistribute it and/or
|
* modify it under the terms of the GNU Lesser General Public
|
* License as published by the Free Software Foundation; either
|
* version 2 of the License, or (at your option) any later version.
|
*
|
* This library 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
|
* Lesser General Public License for more details.
|
|
* You should have received a copy of the GNU Lesser General Public
|
* License along with this library; if not, write to the Free Software
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
*/
|
#ifndef _COBALT_UAPI_ASM_GENERIC_ARITH_H
|
#define _COBALT_UAPI_ASM_GENERIC_ARITH_H
|
|
#ifndef xnarch_u64tou32
|
#define xnarch_u64tou32(ull, h, l) ({ \
|
union { \
|
unsigned long long _ull; \
|
struct endianstruct _s; \
|
} _u; \
|
_u._ull = (ull); \
|
(h) = _u._s._h; \
|
(l) = _u._s._l; \
|
})
|
#endif /* !xnarch_u64tou32 */
|
|
#ifndef xnarch_u64fromu32
|
#define xnarch_u64fromu32(h, l) ({ \
|
union { \
|
unsigned long long _ull; \
|
struct endianstruct _s; \
|
} _u; \
|
_u._s._h = (h); \
|
_u._s._l = (l); \
|
_u._ull; \
|
})
|
#endif /* !xnarch_u64fromu32 */
|
|
#ifndef xnarch_ullmul
|
static inline __attribute__((__const__)) unsigned long long
|
xnarch_generic_ullmul(const unsigned m0, const unsigned m1)
|
{
|
return (unsigned long long) m0 * m1;
|
}
|
#define xnarch_ullmul(m0,m1) xnarch_generic_ullmul((m0),(m1))
|
#endif /* !xnarch_ullmul */
|
|
#ifndef xnarch_ulldiv
|
static inline unsigned long long xnarch_generic_ulldiv (unsigned long long ull,
|
const unsigned uld,
|
unsigned long *const rp)
|
{
|
const unsigned r = do_div(ull, uld);
|
|
if (rp)
|
*rp = r;
|
|
return ull;
|
}
|
#define xnarch_ulldiv(ull,uld,rp) xnarch_generic_ulldiv((ull),(uld),(rp))
|
#endif /* !xnarch_ulldiv */
|
|
#ifndef xnarch_uldivrem
|
#define xnarch_uldivrem(ull,ul,rp) ((unsigned) xnarch_ulldiv((ull),(ul),(rp)))
|
#endif /* !xnarch_uldivrem */
|
|
#ifndef xnarch_divmod64
|
static inline unsigned long long
|
xnarch_generic_divmod64(unsigned long long a,
|
unsigned long long b,
|
unsigned long long *rem)
|
{
|
unsigned long long q;
|
#if defined(__KERNEL__) && BITS_PER_LONG < 64
|
unsigned long long
|
xnarch_generic_full_divmod64(unsigned long long a,
|
unsigned long long b,
|
unsigned long long *rem);
|
if (b <= 0xffffffffULL) {
|
unsigned long r;
|
q = xnarch_ulldiv(a, b, &r);
|
if (rem)
|
*rem = r;
|
} else {
|
if (a < b) {
|
if (rem)
|
*rem = a;
|
return 0;
|
}
|
|
return xnarch_generic_full_divmod64(a, b, rem);
|
}
|
#else /* !(__KERNEL__ && BITS_PER_LONG < 64) */
|
q = a / b;
|
if (rem)
|
*rem = a % b;
|
#endif /* !(__KERNEL__ && BITS_PER_LONG < 64) */
|
return q;
|
}
|
#define xnarch_divmod64(a,b,rp) xnarch_generic_divmod64((a),(b),(rp))
|
#endif /* !xnarch_divmod64 */
|
|
#ifndef xnarch_imuldiv
|
static inline __attribute__((__const__)) int xnarch_generic_imuldiv(int i,
|
int mult,
|
int div)
|
{
|
/* (int)i = (unsigned long long)i*(unsigned)(mult)/(unsigned)div. */
|
const unsigned long long ull = xnarch_ullmul(i, mult);
|
return xnarch_uldivrem(ull, div, NULL);
|
}
|
#define xnarch_imuldiv(i,m,d) xnarch_generic_imuldiv((i),(m),(d))
|
#endif /* !xnarch_imuldiv */
|
|
#ifndef xnarch_imuldiv_ceil
|
static inline __attribute__((__const__)) int xnarch_generic_imuldiv_ceil(int i,
|
int mult,
|
int div)
|
{
|
/* Same as xnarch_generic_imuldiv, rounding up. */
|
const unsigned long long ull = xnarch_ullmul(i, mult);
|
return xnarch_uldivrem(ull + (unsigned)div - 1, div, NULL);
|
}
|
#define xnarch_imuldiv_ceil(i,m,d) xnarch_generic_imuldiv_ceil((i),(m),(d))
|
#endif /* !xnarch_imuldiv_ceil */
|
|
/* Division of an unsigned 96 bits ((h << 32) + l) by an unsigned 32 bits.
|
Building block for llimd. Without const qualifiers, gcc reload registers
|
after each call to uldivrem. */
|
static inline unsigned long long
|
xnarch_generic_div96by32(const unsigned long long h,
|
const unsigned l,
|
const unsigned d,
|
unsigned long *const rp)
|
{
|
unsigned long rh;
|
const unsigned qh = xnarch_uldivrem(h, d, &rh);
|
const unsigned long long t = xnarch_u64fromu32(rh, l);
|
const unsigned ql = xnarch_uldivrem(t, d, rp);
|
|
return xnarch_u64fromu32(qh, ql);
|
}
|
|
#ifndef xnarch_llimd
|
static inline __attribute__((__const__))
|
unsigned long long xnarch_generic_ullimd(const unsigned long long op,
|
const unsigned m,
|
const unsigned d)
|
{
|
unsigned int oph, opl, tlh, tll;
|
unsigned long long th, tl;
|
|
xnarch_u64tou32(op, oph, opl);
|
tl = xnarch_ullmul(opl, m);
|
xnarch_u64tou32(tl, tlh, tll);
|
th = xnarch_ullmul(oph, m);
|
th += tlh;
|
|
return xnarch_generic_div96by32(th, tll, d, NULL);
|
}
|
|
static inline __attribute__((__const__)) long long
|
xnarch_generic_llimd (long long op, unsigned m, unsigned d)
|
{
|
long long ret;
|
int sign = 0;
|
|
if (op < 0LL) {
|
sign = 1;
|
op = -op;
|
}
|
ret = xnarch_generic_ullimd(op, m, d);
|
|
return sign ? -ret : ret;
|
}
|
#define xnarch_llimd(ll,m,d) xnarch_generic_llimd((ll),(m),(d))
|
#endif /* !xnarch_llimd */
|
|
#ifndef _xnarch_u96shift
|
#define xnarch_u96shift(h, m, l, s) ({ \
|
unsigned int _l = (l); \
|
unsigned int _m = (m); \
|
unsigned int _s = (s); \
|
_l >>= _s; \
|
_l |= (_m << (32 - _s)); \
|
_m >>= _s; \
|
_m |= ((h) << (32 - _s)); \
|
xnarch_u64fromu32(_m, _l); \
|
})
|
#endif /* !xnarch_u96shift */
|
|
static inline long long xnarch_llmi(int i, int j)
|
{
|
/* Fast 32x32->64 signed multiplication */
|
return (long long) i * j;
|
}
|
|
#ifndef xnarch_llmulshft
|
/* Fast scaled-math-based replacement for long long multiply-divide */
|
static inline long long
|
xnarch_generic_llmulshft(const long long op,
|
const unsigned m,
|
const unsigned s)
|
{
|
unsigned int oph, opl, tlh, tll, thh, thl;
|
unsigned long long th, tl;
|
|
xnarch_u64tou32(op, oph, opl);
|
tl = xnarch_ullmul(opl, m);
|
xnarch_u64tou32(tl, tlh, tll);
|
th = xnarch_llmi(oph, m);
|
th += tlh;
|
xnarch_u64tou32(th, thh, thl);
|
|
return xnarch_u96shift(thh, thl, tll, s);
|
}
|
#define xnarch_llmulshft(ll, m, s) xnarch_generic_llmulshft((ll), (m), (s))
|
#endif /* !xnarch_llmulshft */
|
|
#ifdef XNARCH_HAVE_NODIV_LLIMD
|
|
/* Representation of a 32 bits fraction. */
|
struct xnarch_u32frac {
|
unsigned long long frac;
|
unsigned integ;
|
};
|
|
static inline void xnarch_init_u32frac(struct xnarch_u32frac *const f,
|
const unsigned m,
|
const unsigned d)
|
{
|
/*
|
* Avoid clever compiler optimizations to occur when d is
|
* known at compile-time. The performance of this function is
|
* not critical since it is only called at init time.
|
*/
|
volatile unsigned vol_d = d;
|
f->integ = m / d;
|
f->frac = xnarch_generic_div96by32
|
(xnarch_u64fromu32(m % d, 0), 0, vol_d, NULL);
|
}
|
|
#ifndef xnarch_nodiv_imuldiv
|
static inline __attribute__((__const__)) unsigned
|
xnarch_generic_nodiv_imuldiv(unsigned op, const struct xnarch_u32frac f)
|
{
|
return (xnarch_ullmul(op, f.frac >> 32) >> 32) + f.integ * op;
|
}
|
#define xnarch_nodiv_imuldiv(op, f) xnarch_generic_nodiv_imuldiv((op),(f))
|
#endif /* xnarch_nodiv_imuldiv */
|
|
#ifndef xnarch_nodiv_imuldiv_ceil
|
static inline __attribute__((__const__)) unsigned
|
xnarch_generic_nodiv_imuldiv_ceil(unsigned op, const struct xnarch_u32frac f)
|
{
|
unsigned long long full = xnarch_ullmul(op, f.frac >> 32) + ~0U;
|
return (full >> 32) + f.integ * op;
|
}
|
#define xnarch_nodiv_imuldiv_ceil(op, f) \
|
xnarch_generic_nodiv_imuldiv_ceil((op),(f))
|
#endif /* xnarch_nodiv_imuldiv_ceil */
|
|
#ifndef xnarch_nodiv_ullimd
|
|
#ifndef xnarch_add96and64
|
#error "xnarch_add96and64 must be implemented."
|
#endif
|
|
static inline __attribute__((__const__)) unsigned long long
|
xnarch_mul64by64_high(const unsigned long long op, const unsigned long long m)
|
{
|
/* Compute high 64 bits of multiplication 64 bits x 64 bits. */
|
register unsigned long long t0, t1, t2, t3;
|
register unsigned int oph, opl, mh, ml, t0h, t0l, t1h, t1l, t2h, t2l, t3h, t3l;
|
|
xnarch_u64tou32(op, oph, opl);
|
xnarch_u64tou32(m, mh, ml);
|
t0 = xnarch_ullmul(opl, ml);
|
xnarch_u64tou32(t0, t0h, t0l);
|
t3 = xnarch_ullmul(oph, mh);
|
xnarch_u64tou32(t3, t3h, t3l);
|
xnarch_add96and64(t3h, t3l, t0h, 0, t0l >> 31);
|
t1 = xnarch_ullmul(oph, ml);
|
xnarch_u64tou32(t1, t1h, t1l);
|
xnarch_add96and64(t3h, t3l, t0h, t1h, t1l);
|
t2 = xnarch_ullmul(opl, mh);
|
xnarch_u64tou32(t2, t2h, t2l);
|
xnarch_add96and64(t3h, t3l, t0h, t2h, t2l);
|
|
return xnarch_u64fromu32(t3h, t3l);
|
}
|
|
static inline unsigned long long
|
xnarch_generic_nodiv_ullimd(const unsigned long long op,
|
const unsigned long long frac,
|
unsigned int integ)
|
{
|
return xnarch_mul64by64_high(op, frac) + integ * op;
|
}
|
#define xnarch_nodiv_ullimd(op, f, i) xnarch_generic_nodiv_ullimd((op),(f), (i))
|
#endif /* !xnarch_nodiv_ullimd */
|
|
#ifndef xnarch_nodiv_llimd
|
static inline __attribute__((__const__)) long long
|
xnarch_generic_nodiv_llimd(long long op, unsigned long long frac,
|
unsigned int integ)
|
{
|
long long ret;
|
int sign = 0;
|
|
if (op < 0LL) {
|
sign = 1;
|
op = -op;
|
}
|
ret = xnarch_nodiv_ullimd(op, frac, integ);
|
|
return sign ? -ret : ret;
|
}
|
#define xnarch_nodiv_llimd(ll,frac,integ) xnarch_generic_nodiv_llimd((ll),(frac),(integ))
|
#endif /* !xnarch_nodiv_llimd */
|
|
#endif /* XNARCH_HAVE_NODIV_LLIMD */
|
|
static inline void xnarch_init_llmulshft(const unsigned m_in,
|
const unsigned d_in,
|
unsigned *m_out,
|
unsigned *s_out)
|
{
|
/*
|
* Avoid clever compiler optimizations to occur when d is
|
* known at compile-time. The performance of this function is
|
* not critical since it is only called at init time.
|
*/
|
volatile unsigned int vol_d = d_in;
|
unsigned long long mult;
|
|
*s_out = 31;
|
while (1) {
|
mult = ((unsigned long long)m_in) << *s_out;
|
do_div(mult, vol_d);
|
if (mult <= 0x7FFFFFFF)
|
break;
|
(*s_out)--;
|
}
|
*m_out = (unsigned int)mult;
|
}
|
|
#define xnarch_ullmod(ull,uld,rem) ({ xnarch_ulldiv(ull,uld,rem); (*rem); })
|
#define xnarch_uldiv(ull, d) xnarch_uldivrem(ull, d, NULL)
|
#define xnarch_ulmod(ull, d) ({ unsigned long _rem; \
|
xnarch_uldivrem(ull,d,&_rem); _rem; })
|
|
#define xnarch_div64(a,b) xnarch_divmod64((a),(b),NULL)
|
#define xnarch_mod64(a,b) ({ unsigned long long _rem; \
|
xnarch_divmod64((a),(b),&_rem); _rem; })
|
|
#endif /* _COBALT_UAPI_ASM_GENERIC_ARITH_H */
|