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 #ifdef __cplusplus
38 extern "C" {
39 #endif
40
41 #include <sys/types.h>
42 #include <sys/ieeefp.h>
43
44 #define _LO_WORD(x) ((uint32_t *)&x)[0]
45 #define _HI_WORD(x) ((uint32_t *)&x)[1]
46 #define _HIER_WORD(x) ((uint32_t *)&x)[2]
47
48 extern __GNU_INLINE double
49 __inline_sqrt(double a)
50 {
51 double ret;
52
53 __asm__ __volatile__("fsqrt\n\t" : "=t" (ret) : "0" (a) : "cc");
54 return (ret);
55 }
56
57 extern __GNU_INLINE double
58 __ieee754_sqrt(double a)
59 {
60 return (__inline_sqrt(a));
61 }
62
63 extern __GNU_INLINE float
64 __inline_sqrtf(float a)
65 {
66 float ret;
67
68 __asm__ __volatile__("fsqrt\n\t" : "=t" (ret) : "0" (a) : "cc");
69 return (ret);
70 }
71
72 extern __GNU_INLINE double
73 __inline_rint(double a)
74 {
75 __asm__ __volatile__(
76 "andl $0x7fffffff,%1\n\t"
77 "cmpl $0x43300000,%1\n\t"
78 "jae 1f\n\t"
79 "frndint\n\t"
80 "1: fwait\n\t"
81 : "+t" (a), "+&r" (_HI_WORD(a))
82 :
83 : "cc");
84
85 return (a);
86 }
87
88 /*
89 * 00 - 24 bits
90 * 01 - reserved
91 * 10 - 53 bits
92 * 11 - 64 bits
93 */
94 extern __GNU_INLINE int
95 __swapRP(int i)
96 {
97 int ret;
98 uint16_t cw;
99
100 __asm__ __volatile__("fstcw %0\n\t" : "=m" (cw));
101
102 ret = (cw >> 8) & 0x3;
103 cw = (cw & 0xfcff) | ((i & 0x3) << 8);
104
105 __asm__ __volatile__("fldcw %0\n\t" : : "m" (cw));
106
107 return (ret);
108 }
109
110 /*
111 * 00 - Round to nearest, with even preferred
112 * 01 - Round down
113 * 10 - Round up
114 * 11 - Chop
115 */
116 extern __GNU_INLINE enum fp_direction_type
117 __swap87RD(enum fp_direction_type i)
118 {
119 int ret;
120 uint16_t cw;
121
122 __asm__ __volatile__("fstcw %0\n\t" : "=m" (cw));
123
124 ret = (cw >> 10) & 0x3;
125 cw = (cw & 0xf3ff) | ((i & 0x3) << 10);
126
127 __asm__ __volatile__("fldcw %0\n\t" : : "m" (cw));
128
129 return (ret);
130 }
131
132 extern __GNU_INLINE double
133 ceil(double d)
134 {
135 /*
136 * Let's set a Rounding Control (RC) bits from x87 FPU Control Word
137 * to fp_positive and save old bits in rd.
138 */
139 short rd = __swap87RD(fp_positive);
140
141 /*
142 * The FRNDINT instruction returns a floating-point value that is the
143 * integral value closest to the source value in the direction of the
144 * rounding mode specified in the RC field of the x87 FPU control word.
145 *
146 * Rounds the source value in the ST(0) register to the nearest
147 * integral value, depending on the current rounding mode
148 * (setting of the RC field of the FPU control word),
149 * and stores the result in ST(0).
150 */
151 __asm__ __volatile__("frndint" : "+t" (d) : : "cc");
152
153 /* restore old RC bits */
154 __swap87RD(rd);
155
156 return (d);
157 }
158
159 extern __GNU_INLINE double
160 copysign(double d1, double d2)
161 {
162 __asm__ __volatile__(
163 "andl $0x7fffffff,%0\n\t" /* %0 <-- hi_32(abs(d)) */
164 "andl $0x80000000,%1\n\t" /* %1[31] <-- sign_bit(d2) */
165 "orl %1,%0\n\t" /* %0 <-- hi_32(copysign(x,y)) */
166 : "+&r" (_HI_WORD(d1)), "+r" (_HI_WORD(d2))
167 :
168 : "cc");
169
170 return (d1);
171 }
172
173 extern __GNU_INLINE double
174 fabs(double d)
175 {
176 __asm__ __volatile__("fabs\n\t" : "+t" (d) : : "cc");
177 return (d);
178 }
179
180 extern __GNU_INLINE float
181 fabsf(float d)
182 {
183 __asm__ __volatile__("fabs\n\t" : "+t" (d) : : "cc");
184 return (d);
185 }
186
187 extern __GNU_INLINE long double
188 fabsl(long double d)
189 {
190 __asm__ __volatile__("fabs\n\t" : "+t" (d) : : "cc");
191 return (d);
192 }
193
194 extern __GNU_INLINE int
195 finite(double d)
196 {
197 int ret = _HI_WORD(d);
198
199 __asm__ __volatile__(
200 "notl %0\n\t"
201 "andl $0x7ff00000,%0\n\t"
202 "negl %0\n\t"
203 "shrl $31,%0\n\t"
204 : "+r" (ret)
205 :
206 : "cc");
207 return (ret);
208 }
209
210 extern __GNU_INLINE double
211 floor(double d)
212 {
213 short rd = __swap87RD(fp_negative);
214
215 __asm__ __volatile__("frndint" : "+t" (d), "+r" (rd) : : "cc");
216 __swap87RD(rd);
217
218 return (d);
219 }
220
221 /*
222 * branchless __isnan
223 * ((0x7ff00000-[((lx|-lx)>>31)&1]|ahx)>>31)&1 = 1 iff x is NaN
224 */
225 extern __GNU_INLINE int
226 isnan(double d)
227 {
228 int ret;
229
230 __asm__ __volatile__(
231 "movl %1,%%ecx\n\t"
232 "negl %%ecx\n\t" /* ecx <-- -lo_32(x) */
233 "orl %%ecx,%1\n\t"
234 "shrl $31,%1\n\t" /* 1 iff lx != 0 */
235 "andl $0x7fffffff,%2\n\t" /* ecx <-- hi_32(abs(x)) */
236 "orl %2,%1\n\t"
237 "subl $0x7ff00000,%1\n\t"
238 "negl %1\n\t"
239 "shrl $31,%1\n\t"
240 : "=r" (ret)
241 : "0" (_HI_WORD(d)), "r" (_LO_WORD(d))
242 : "ecx");
243
244 return (ret);
245 }
246
247 extern __GNU_INLINE int
248 isnanf(float f)
249 {
250 __asm__ __volatile__(
251 "andl $0x7fffffff,%0\n\t"
252 "negl %0\n\t"
253 "addl $0x7f800000,%0\n\t"
254 "shrl $31,%0\n\t"
255 : "+r" (f)
256 :
257 : "cc");
258
259 return (f);
260 }
261
262 extern __GNU_INLINE double
263 rint(double a)
264 {
265 return (__inline_rint(a));
266 }
267
268 extern __GNU_INLINE double
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
285 signbit(double d)
286 {
287 return (_HI_WORD(d) >> 31);
288 }
289
290 extern __GNU_INLINE int
291 signbitf(float f)
292 {
293 return ((*(uint32_t *)&f) >> 31);
294 }
295
296 extern __GNU_INLINE double
297 sqrt(double d)
298 {
299 return (__inline_sqrt(d));
300 }
301
302 extern __GNU_INLINE float
303 sqrtf(float f)
304 {
305 return (__inline_sqrtf(f));
306 }
307
308 extern __GNU_INLINE long double
309 sqrtl(long double ld)
310 {
311 __asm__ __volatile__("fsqrt" : "+t" (ld) : : "cc");
312 return (ld);
313 }
314
315 extern __GNU_INLINE int
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 #endif /* __GNUC__ */
349 #endif /* _LIBM_INLINES_H */