1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*
28 * Copyright 2011, Richard Lowe
29 */
30
31/* Functions in this file are duplicated in locallibm.il.  Keep them in sync */
32
33#ifndef _LIBM_INLINES_H
34#define	_LIBM_INLINES_H
35
36#ifdef __GNUC__
37
38#ifdef __cplusplus
39extern "C" {
40#endif
41
42#include <sys/types.h>
43#include <sys/ieeefp.h>
44
45#define	_LO_WORD(x)	((uint32_t *)&x)[0]
46#define	_HI_WORD(x)	((uint32_t *)&x)[1]
47#define	_HIER_WORD(x)	((uint32_t *)&x)[2]
48
49extern __GNU_INLINE double
50__inline_sqrt(double a)
51{
52	double ret;
53
54	__asm__ __volatile__("fsqrt\n\t" : "=t" (ret) : "0" (a) : "cc");
55	return (ret);
56}
57
58extern __GNU_INLINE double
59__ieee754_sqrt(double a)
60{
61	return (__inline_sqrt(a));
62}
63
64extern __GNU_INLINE float
65__inline_sqrtf(float a)
66{
67	float ret;
68
69	__asm__ __volatile__("fsqrt\n\t" : "=t" (ret) : "0" (a) : "cc");
70	return (ret);
71}
72
73extern __GNU_INLINE double
74__inline_rint(double a)
75{
76	__asm__ __volatile__(
77	    "andl $0x7fffffff,%1\n\t"
78	    "cmpl $0x43300000,%1\n\t"
79	    "jae  1f\n\t"
80	    "frndint\n\t"
81	    "1: fwait\n\t"
82	    : "+t" (a), "+&r" (_HI_WORD(a))
83	    :
84	    : "cc");
85
86	return (a);
87}
88
89/*
90 * 00 - 24 bits
91 * 01 - reserved
92 * 10 - 53 bits
93 * 11 - 64 bits
94 */
95extern __GNU_INLINE int
96__swapRP(int i)
97{
98	int ret;
99	uint16_t cw;
100
101	__asm__ __volatile__("fstcw %0\n\t" : "=m" (cw));
102
103	ret = (cw >> 8) & 0x3;
104	cw = (cw & 0xfcff) | ((i & 0x3) << 8);
105
106	__asm__ __volatile__("fldcw %0\n\t" : : "m" (cw));
107
108	return (ret);
109}
110
111/*
112 * 00 - Round to nearest, with even preferred
113 * 01 - Round down
114 * 10 - Round up
115 * 11 - Chop
116 */
117extern __GNU_INLINE enum fp_direction_type
118__swap87RD(enum fp_direction_type i)
119{
120	int ret;
121	uint16_t cw;
122
123	__asm__ __volatile__("fstcw %0\n\t" : "=m" (cw));
124
125	ret = (cw >> 10) & 0x3;
126	cw = (cw & 0xf3ff) | ((i & 0x3) << 10);
127
128	__asm__ __volatile__("fldcw %0\n\t" : : "m" (cw));
129
130	return (ret);
131}
132
133extern __GNU_INLINE double
134ceil(double d)
135{
136	/*
137	 * Let's set a Rounding Control (RC) bits from x87 FPU Control Word
138	 * to fp_positive and save old bits in rd.
139	 */
140	short rd = __swap87RD(fp_positive);
141
142	/*
143	 * The FRNDINT instruction returns a floating-point value that is the
144	 * integral value closest to the source value in the direction of the
145	 * rounding mode specified in the RC field of the x87 FPU control word.
146	 *
147	 * Rounds the source value in the ST(0) register to the nearest
148	 * integral value, depending on the current rounding mode
149	 * (setting of the RC field of the FPU control word),
150	 * and stores the result in ST(0).
151	 */
152	__asm__ __volatile__("frndint" : "+t" (d) : : "cc");
153
154	/* restore old RC bits */
155	__swap87RD(rd);
156
157	return (d);
158}
159
160extern __GNU_INLINE double
161copysign(double d1, double d2)
162{
163	__asm__ __volatile__(
164	    "andl $0x7fffffff,%0\n\t"	/* %0 <-- hi_32(abs(d)) */
165	    "andl $0x80000000,%1\n\t"	/* %1[31] <-- sign_bit(d2) */
166	    "orl  %1,%0\n\t"		/* %0 <-- hi_32(copysign(x,y)) */
167	    : "+&r" (_HI_WORD(d1)), "+r" (_HI_WORD(d2))
168	    :
169	    : "cc");
170
171	return (d1);
172}
173
174extern __GNU_INLINE double
175fabs(double d)
176{
177	__asm__ __volatile__("fabs\n\t" : "+t" (d) : : "cc");
178	return (d);
179}
180
181extern __GNU_INLINE float
182fabsf(float d)
183{
184	__asm__ __volatile__("fabs\n\t" : "+t" (d) : : "cc");
185	return (d);
186}
187
188extern __GNU_INLINE long double
189fabsl(long double d)
190{
191	__asm__ __volatile__("fabs\n\t" : "+t" (d) : : "cc");
192	return (d);
193}
194
195extern __GNU_INLINE int
196finite(double d)
197{
198	int ret = _HI_WORD(d);
199
200	__asm__ __volatile__(
201	    "notl %0\n\t"
202	    "andl $0x7ff00000,%0\n\t"
203	    "negl %0\n\t"
204	    "shrl $31,%0\n\t"
205	    : "+r" (ret)
206	    :
207	    : "cc");
208	return (ret);
209}
210
211extern __GNU_INLINE double
212floor(double d)
213{
214	short rd = __swap87RD(fp_negative);
215
216	__asm__ __volatile__("frndint" : "+t" (d), "+r" (rd) : : "cc");
217	__swap87RD(rd);
218
219	return (d);
220}
221
222/*
223 *      branchless __isnan
224 *      ((0x7ff00000-[((lx|-lx)>>31)&1]|ahx)>>31)&1 = 1 iff x is NaN
225 */
226extern __GNU_INLINE int
227isnan(double d)
228{
229	int ret;
230
231	__asm__ __volatile__(
232	    "movl %1,%%ecx\n\t"
233	    "negl %%ecx\n\t"			/* ecx <-- -lo_32(x) */
234	    "orl  %%ecx,%1\n\t"
235	    "shrl $31,%1\n\t"			/* 1 iff lx != 0 */
236	    "andl $0x7fffffff,%2\n\t"	/* ecx <-- hi_32(abs(x)) */
237	    "orl  %2,%1\n\t"
238	    "subl $0x7ff00000,%1\n\t"
239	    "negl %1\n\t"
240	    "shrl $31,%1\n\t"
241	    : "=r" (ret)
242	    : "0" (_HI_WORD(d)), "r" (_LO_WORD(d))
243	    : "ecx");
244
245	return (ret);
246}
247
248extern __GNU_INLINE int
249isnanf(float f)
250{
251	__asm__ __volatile__(
252	    "andl $0x7fffffff,%0\n\t"
253	    "negl %0\n\t"
254	    "addl $0x7f800000,%0\n\t"
255	    "shrl $31,%0\n\t"
256	    : "+r" (f)
257	    :
258	    : "cc");
259
260	return (f);
261}
262
263extern __GNU_INLINE double
264rint(double a) {
265    return (__inline_rint(a));
266}
267
268extern __GNU_INLINE double
269scalbn(double d, int n)
270{
271	double dummy;
272
273	__asm__ __volatile__(
274	    "fildl %2\n\t"	/* Convert N to extended */
275	    "fxch\n\t"
276	    "fscale\n\t"
277	    : "+t" (d), "=u" (dummy)
278	    : "m" (n)
279	    : "cc");
280
281	return (d);
282}
283
284extern __GNU_INLINE int
285signbit(double d)
286{
287	return (_HI_WORD(d) >> 31);
288}
289
290extern __GNU_INLINE int
291signbitf(float f)
292{
293	return ((*(uint32_t *)&f) >> 31);
294}
295
296extern __GNU_INLINE double
297sqrt(double d)
298{
299	return (__inline_sqrt(d));
300}
301
302extern __GNU_INLINE float
303sqrtf(float f)
304{
305	return (__inline_sqrtf(f));
306}
307
308extern __GNU_INLINE long double
309sqrtl(long double ld)
310{
311	__asm__ __volatile__("fsqrt" : "+t" (ld) : : "cc");
312	return (ld);
313}
314
315extern __GNU_INLINE int
316isnanl(long double ld)
317{
318	int ret = _HIER_WORD(ld);
319
320	__asm__ __volatile__(
321	    "andl  $0x00007fff,%0\n\t"
322	    "jz	   1f\n\t"		/* jump if exp is all 0 */
323	    "xorl  $0x00007fff,%0\n\t"
324	    "jz	   2f\n\t"		/* jump if exp is all 1 */
325	    "testl $0x80000000,%1\n\t"
326	    "jz	   3f\n\t"		/* jump if leading bit is 0 */
327	    "xorl  %0,%0\n\t"
328	    "jmp   1f\n\t"
329	    "2:\n\t"			/* note that %0 = 0 from before */
330	    "cmpl  $0x80000000,%1\n\t"	/* what is first half of significand? */
331	    "jnz   3f\n\t"		/* jump if not equal to 0x80000000 */
332	    "testl $0xffffffff,%2\n\t"	/* is second half of significand 0? */
333	    "jnz   3f\n\t"		/* jump if not equal to 0 */
334	    "jmp   1f\n\t"
335	    "3:\n\t"
336	    "movl  $1,%0\n\t"
337	    "1:\n\t"
338	    : "+&r" (ret)
339	    : "r" (_HI_WORD(ld)), "r" (_LO_WORD(ld))
340	    : "cc");
341
342	return (ret);
343}
344
345#ifdef __cplusplus
346}
347#endif
348
349#endif  /* __GNUC__ */
350
351#endif /* _LIBM_INLINES_H */
352