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
39 extern "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 
49 extern __GNU_INLINE double
__inline_sqrt(double a)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 
58 extern __GNU_INLINE double
__ieee754_sqrt(double a)59 __ieee754_sqrt(double a)
60 {
61 	return (__inline_sqrt(a));
62 }
63 
64 extern __GNU_INLINE float
__inline_sqrtf(float a)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 
73 extern __GNU_INLINE double
__inline_rint(double a)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  */
95 extern __GNU_INLINE int
__swapRP(int i)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  */
117 extern __GNU_INLINE enum fp_direction_type
__swap87RD(enum fp_direction_type i)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 
133 extern __GNU_INLINE double
ceil(double d)134 ceil(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 
160 extern __GNU_INLINE double
copysign(double d1,double d2)161 copysign(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 
174 extern __GNU_INLINE double
fabs(double d)175 fabs(double d)
176 {
177 	__asm__ __volatile__("fabs\n\t" : "+t" (d) : : "cc");
178 	return (d);
179 }
180 
181 extern __GNU_INLINE float
fabsf(float d)182 fabsf(float d)
183 {
184 	__asm__ __volatile__("fabs\n\t" : "+t" (d) : : "cc");
185 	return (d);
186 }
187 
188 extern __GNU_INLINE long double
fabsl(long double d)189 fabsl(long double d)
190 {
191 	__asm__ __volatile__("fabs\n\t" : "+t" (d) : : "cc");
192 	return (d);
193 }
194 
195 extern __GNU_INLINE int
finite(double d)196 finite(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 
211 extern __GNU_INLINE double
floor(double d)212 floor(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  */
226 extern __GNU_INLINE int
isnan(double d)227 isnan(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 
248 extern __GNU_INLINE int
isnanf(float f)249 isnanf(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 
263 extern __GNU_INLINE double
rint(double a)264 rint(double a) {
265     return (__inline_rint(a));
266 }
267 
268 extern __GNU_INLINE double
scalbn(double d,int n)269 scalbn(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 
284 extern __GNU_INLINE int
signbit(double d)285 signbit(double d)
286 {
287 	return (_HI_WORD(d) >> 31);
288 }
289 
290 extern __GNU_INLINE int
signbitf(float f)291 signbitf(float f)
292 {
293 	return ((*(uint32_t *)&f) >> 31);
294 }
295 
296 extern __GNU_INLINE double
sqrt(double d)297 sqrt(double d)
298 {
299 	return (__inline_sqrt(d));
300 }
301 
302 extern __GNU_INLINE float
sqrtf(float f)303 sqrtf(float f)
304 {
305 	return (__inline_sqrtf(f));
306 }
307 
308 extern __GNU_INLINE long double
sqrtl(long double ld)309 sqrtl(long double ld)
310 {
311 	__asm__ __volatile__("fsqrt" : "+t" (ld) : : "cc");
312 	return (ld);
313 }
314 
315 extern __GNU_INLINE int
isnanl(long double ld)316 isnanl(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