xref: /illumos-gate/usr/src/uts/sparc/fpu/pack.c (revision 7c478bd9)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 1988,1995-1996,2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /* Pack procedures for Sparc FPU simulator. */
30 
31 #include <sys/fpu/fpu_simulator.h>
32 #include <sys/fpu/globals.h>
33 
34 /*
35  * Returns 1 if overflow should go to infinity, 0 if to max finite.
36  */
37 static int
overflow_to_infinity(fp_simd_type * pfpsd,int sign)38 overflow_to_infinity(
39 	fp_simd_type	*pfpsd,		/* Pointer to simulator data */
40 	int		sign)		/* negative or positive */
41 {
42 	int		inf;
43 
44 	switch (pfpsd->fp_direction) {
45 	case fp_nearest:
46 		inf = 1;
47 		break;
48 	case fp_tozero:
49 		inf = 0;
50 		break;
51 	case fp_positive:
52 		inf = !sign;
53 		break;
54 	case fp_negative:
55 		inf = sign;
56 		break;
57 	}
58 	return (inf);
59 }
60 
61 /*
62  * Round according to current rounding mode.
63  */
64 static void
round(fp_simd_type * pfpsd,unpacked * pu)65 round(
66 	fp_simd_type	*pfpsd,		/* Pointer to simulator data */
67 	unpacked	*pu)		/* unpacked result */
68 {
69 	int		increment;	/* boolean to indicate round up */
70 	int		sr;
71 
72 	sr = pu->sticky|pu->rounded;
73 
74 	if (sr == 0)
75 		return;
76 	fpu_set_exception(pfpsd, fp_inexact);
77 	switch (pfpsd->fp_direction) {
78 	case fp_nearest:
79 		increment = pu->rounded;
80 		break;
81 	case fp_tozero:
82 		increment = 0;
83 		break;
84 	case fp_positive:
85 		increment = (pu->sign == 0) & (sr != 0);
86 		break;
87 	case fp_negative:
88 		increment = (pu->sign != 0) & (sr != 0);
89 		break;
90 	}
91 	if (increment) {
92 	    pu->significand[3]++;
93 	    if (pu->significand[3] == 0) {
94 		pu->significand[2]++;
95 		if (pu->significand[2] == 0) {
96 		    pu->significand[1]++;
97 		    if (pu->significand[1] == 0) {
98 			pu->significand[0]++;	/* rounding carried out */
99 			if (pu->significand[0] == 0x20000) {
100 			    pu->exponent++;
101 			    pu->significand[0] = 0x10000;
102 			}
103 		    }
104 		}
105 	    }
106 	}
107 	if ((pfpsd->fp_direction == fp_nearest) &&
108 	    (pu->sticky == 0) && increment != 0) {	/* ambiguous case */
109 		pu->significand[3] &= 0xfffffffe; /* force round to even */
110 	}
111 }
112 
113 static void
packint32(fp_simd_type * pfpsd,unpacked * pu,int32_t * px)114 packint32(
115 	fp_simd_type	*pfpsd,	/* Pointer to simulator data */
116 	unpacked	*pu,	/* unpacked result */
117 	int32_t		*px)	/* packed int32_t */
118 {
119 	switch (pu->fpclass) {
120 	case fp_zero:
121 		*px = 0;
122 		break;
123 	case fp_normal:
124 		if (pu->exponent >= 32)
125 			goto overflow;
126 		fpu_rightshift(pu, 112 - pu->exponent);
127 		round(pfpsd, pu);
128 		if (pu->significand[3] >= 0x80000000)
129 			if ((pu->sign == 0)||(pu->significand[3] > 0x80000000))
130 				goto overflow;
131 		*px = pu->significand[3];
132 		if (pu->sign)
133 			*px = -*px;
134 		break;
135 	case fp_infinity:
136 	case fp_quiet:
137 	case fp_signaling:
138 overflow:
139 		if (pu->sign)
140 			*px = 0x80000000;
141 		else
142 			*px = 0x7fffffff;
143 		pfpsd->fp_current_exceptions &= ~(1 << (int)fp_inexact);
144 		fpu_set_exception(pfpsd, fp_invalid);
145 		break;
146 	}
147 }
148 
149 static void
packint64(fp_simd_type * pfpsd,unpacked * pu,int64_t * px)150 packint64(
151 	fp_simd_type	*pfpsd,	/* Pointer to simulator data */
152 	unpacked	*pu,	/* unpacked result */
153 	int64_t		*px)	/* packed int64_t */
154 {
155 	union {
156 		uint64_t ll;
157 		uint32_t i[2];
158 	} x;
159 
160 	switch (pu->fpclass) {
161 	case fp_zero:
162 		*px = 0;
163 		break;
164 	case fp_normal:
165 		if (pu->exponent >= 64)
166 			goto overflow;
167 		fpu_rightshift(pu, 112 - pu->exponent);
168 		round(pfpsd, pu);
169 		if (pu->significand[2] >= 0x80000000)
170 			if ((pu->sign == 0) ||
171 			    (pu->significand[2] > 0x80000000) ||
172 			    (((pu->significand[2] == 0x80000000) &&
173 				(pu->significand[3] > 0))))
174 				goto overflow;
175 		x.i[0] = pu->significand[2];
176 		x.i[1] = pu->significand[3];
177 		*px = x.ll;
178 		if (pu->sign)
179 			*px = -*px;
180 		break;
181 	case fp_infinity:
182 	case fp_quiet:
183 	case fp_signaling:
184 overflow:
185 		if (pu->sign)
186 			*px = (int64_t)0x8000000000000000;
187 		else
188 			*px = (int64_t)0x7fffffffffffffff;
189 		pfpsd->fp_current_exceptions &= ~(1 << (int)fp_inexact);
190 		fpu_set_exception(pfpsd, fp_invalid);
191 		break;
192 	}
193 }
194 
195 static void
packsingle(fp_simd_type * pfpsd,unpacked * pu,single_type * px)196 packsingle(
197 	fp_simd_type	*pfpsd,	/* Pointer to simulator data */
198 	unpacked	*pu,	/* unpacked result */
199 	single_type	*px)	/* packed single */
200 {
201 	px->sign = pu->sign;
202 	switch (pu->fpclass) {
203 	case fp_zero:
204 		px->exponent = 0;
205 		px->significand = 0;
206 		break;
207 	case fp_infinity:
208 infinity:
209 		px->exponent = 0xff;
210 		px->significand = 0;
211 		break;
212 	case fp_quiet:
213 	case fp_signaling:
214 		fpu_rightshift(pu, 113-24);
215 		px->exponent = 0xff;
216 		px->significand = 0x400000|(0x3fffff&pu->significand[3]);
217 		break;
218 	case fp_normal:
219 		fpu_rightshift(pu, 113-24);
220 		pu->exponent += SINGLE_BIAS;
221 		if (pu->exponent <= 0) {
222 			px->exponent = 0;
223 			fpu_rightshift(pu, 1 - pu->exponent);
224 			round(pfpsd, pu);
225 			if (pu->significand[3] == 0x800000) {
226 								/*
227 								 * rounded
228 								 * back up to
229 								 * normal
230 								 */
231 				px->exponent = 1;
232 				px->significand = 0;
233 				fpu_set_exception(pfpsd, fp_inexact);
234 			} else
235 				px->significand = 0x7fffff & pu->significand[3];
236 
237 			if (pfpsd->fp_current_exceptions & (1 << fp_inexact))
238 				fpu_set_exception(pfpsd, fp_underflow);
239 			if (pfpsd->fp_fsrtem & (1<<fp_underflow)) {
240 				fpu_set_exception(pfpsd, fp_underflow);
241 				pfpsd->fp_current_exceptions &=
242 						~(1 << (int)fp_inexact);
243 			}
244 			return;
245 		}
246 		round(pfpsd, pu);
247 		if (pu->significand[3] == 0x1000000) {	/* rounding overflow */
248 			pu->significand[3] = 0x800000;
249 			pu->exponent += 1;
250 		}
251 		if (pu->exponent >= 0xff) {
252 			fpu_set_exception(pfpsd, fp_overflow);
253 			fpu_set_exception(pfpsd, fp_inexact);
254 			if (pfpsd->fp_fsrtem & (1<<fp_overflow)) {
255 				pfpsd->fp_current_exceptions &=
256 						~(1 << (int)fp_inexact);
257 			}
258 			if (overflow_to_infinity(pfpsd, pu->sign))
259 				goto infinity;
260 			px->exponent = 0xfe;
261 			px->significand = 0x7fffff;
262 			return;
263 		}
264 		px->exponent = pu->exponent;
265 		px->significand = 0x7fffff & pu->significand[3];
266 	}
267 }
268 
269 static void
packdouble(fp_simd_type * pfpsd,unpacked * pu,double_type * px,uint_t * py)270 packdouble(
271 	fp_simd_type	*pfpsd,	/* Pointer to simulator data */
272 	unpacked	*pu,	/* unpacked result */
273 	double_type	*px,	/* packed double, sign/exponent/upper 20 bits */
274 	uint_t		*py)	/* and the lower 32 bits of the significand */
275 {
276 	px->sign = pu->sign;
277 	switch (pu->fpclass) {
278 	case fp_zero:
279 		px->exponent = 0;
280 		px->significand = 0;
281 		*py = 0;
282 		break;
283 	case fp_infinity:
284 infinity:
285 		px->exponent = 0x7ff;
286 		px->significand = 0;
287 		*py = 0;
288 		break;
289 	case fp_quiet:
290 	case fp_signaling:
291 		fpu_rightshift(pu, 113-53);
292 		px->exponent = 0x7ff;
293 		px->significand = 0x80000 | (0x7ffff & pu->significand[2]);
294 		*py = pu->significand[3];
295 		break;
296 	case fp_normal:
297 		fpu_rightshift(pu, 113-53);
298 		pu->exponent += DOUBLE_BIAS;
299 		if (pu->exponent <= 0) {	/* underflow */
300 			px->exponent = 0;
301 			fpu_rightshift(pu, 1 - pu->exponent);
302 			round(pfpsd, pu);
303 			if (pu->significand[2] == 0x100000) {
304 								/*
305 								 * rounded
306 								 * back up to
307 								 * normal
308 								 */
309 				px->exponent = 1;
310 				px->significand = 0;
311 				*py = 0;
312 				fpu_set_exception(pfpsd, fp_inexact);
313 			} else {
314 				px->exponent = 0;
315 				px->significand = 0xfffff & pu->significand[2];
316 				*py = pu->significand[3];
317 			}
318 			if (pfpsd->fp_current_exceptions & (1 << fp_inexact))
319 				fpu_set_exception(pfpsd, fp_underflow);
320 			if (pfpsd->fp_fsrtem & (1<<fp_underflow)) {
321 				fpu_set_exception(pfpsd, fp_underflow);
322 				pfpsd->fp_current_exceptions &=
323 						~(1 << (int)fp_inexact);
324 			}
325 			return;
326 		}
327 		round(pfpsd, pu);
328 		if (pu->significand[2] == 0x200000) {	/* rounding overflow */
329 			pu->significand[2] = 0x100000;
330 			pu->exponent += 1;
331 		}
332 		if (pu->exponent >= 0x7ff) {	/* overflow */
333 			fpu_set_exception(pfpsd, fp_overflow);
334 			fpu_set_exception(pfpsd, fp_inexact);
335 			if (pfpsd->fp_fsrtem & (1<<fp_overflow)) {
336 				pfpsd->fp_current_exceptions &=
337 						~(1 << (int)fp_inexact);
338 			}
339 			if (overflow_to_infinity(pfpsd, pu->sign))
340 				goto infinity;
341 			px->exponent = 0x7fe;
342 			px->significand = 0xfffff;
343 			*py = 0xffffffffU;
344 			return;
345 		}
346 		px->exponent = pu->exponent;
347 		px->significand = 0xfffff & pu->significand[2];
348 		*py = pu->significand[3];
349 		break;
350 	}
351 }
352 
353 static void
packextended(fp_simd_type * pfpsd,unpacked * pu,extended_type * px,uint_t * py,uint_t * pz,uint_t * pw)354 packextended(
355 	fp_simd_type	*pfpsd,	/* Pointer to simulator data */
356 	unpacked	*pu,	/* unpacked result */
357 	extended_type	*px,	/* packed extended, sign/exponent/16 bits */
358 	uint_t		*py,	/* 2nd word of extended significand */
359 	uint_t		*pz,	/* 3rd word of extended significand */
360 	uint_t		*pw)	/* 4th word of extended significand */
361 {
362 	px->sign = pu->sign;
363 	switch (pu->fpclass) {
364 	case fp_zero:
365 		px->exponent = 0;
366 		px->significand = 0;
367 		*pz = 0;
368 		*py = 0;
369 		*pw = 0;
370 		break;
371 	case fp_infinity:
372 infinity:
373 		px->exponent = 0x7fff;
374 		px->significand = 0;
375 		*pz = 0;
376 		*py = 0;
377 		*pw = 0;
378 		break;
379 	case fp_quiet:
380 	case fp_signaling:
381 		px->exponent = 0x7fff;
382 		px->significand = 0x8000 | pu->significand[0];
383 								/*
384 								 * Insure quiet
385 								 * nan.
386 								 */
387 		*py = pu->significand[1];
388 		*pz = pu->significand[2];
389 		*pw = pu->significand[3];
390 		break;
391 	case fp_normal:
392 		pu->exponent += EXTENDED_BIAS;
393 		if (pu->exponent <= 0) {	/* underflow */
394 			fpu_rightshift(pu, 1-pu->exponent);
395 			round(pfpsd, pu);
396 			if (pu->significand[0] < 0x00010000) {
397 								/*
398 								 * not rounded
399 								 * back up
400 								 * to normal
401 								 */
402 				px->exponent = 0;
403 			} else {
404 				px->exponent = 1;
405 				fpu_set_exception(pfpsd, fp_inexact);
406 			}
407 			if (pfpsd->fp_current_exceptions & (1 << fp_inexact))
408 				fpu_set_exception(pfpsd, fp_underflow);
409 			if (pfpsd->fp_fsrtem & (1<<fp_underflow)) {
410 				fpu_set_exception(pfpsd, fp_underflow);
411 				pfpsd->fp_current_exceptions &=
412 						~(1 << (int)fp_inexact);
413 			}
414 			px->significand = pu->significand[0];
415 			*py = pu->significand[1];
416 			*pz = pu->significand[2];
417 			*pw = pu->significand[3];
418 			return;
419 		}
420 		round(pfpsd, pu); /* rounding overflow handled in round() */
421 		if (pu->exponent >= 0x7fff) {	/* overflow */
422 			fpu_set_exception(pfpsd, fp_overflow);
423 			fpu_set_exception(pfpsd, fp_inexact);
424 			if (pfpsd->fp_fsrtem & (1<<fp_overflow)) {
425 				pfpsd->fp_current_exceptions &=
426 						~(1 << (int)fp_inexact);
427 			}
428 			if (overflow_to_infinity(pfpsd, pu->sign))
429 				goto infinity;
430 			px->exponent = 0x7ffe;	/* overflow to max norm */
431 			px->significand = 0xffff;
432 			*py = 0xffffffffU;
433 			*pz = 0xffffffffU;
434 			*pw = 0xffffffffU;
435 			return;
436 		}
437 		px->exponent = pu->exponent;
438 		px->significand = pu->significand[0];
439 		*py = pu->significand[1];
440 		*pz = pu->significand[2];
441 		*pw = pu->significand[3];
442 		break;
443 	}
444 }
445 
446 void
_fp_pack(fp_simd_type * pfpsd,unpacked * pu,uint_t n,enum fp_op_type type)447 _fp_pack(
448 	fp_simd_type	*pfpsd,	/* Pointer to simulator data */
449 	unpacked	*pu,	/* unpacked operand */
450 	uint_t		n,	/* register where datum starts */
451 	enum fp_op_type type)	/* type of datum */
452 
453 {
454 	switch (type) {
455 	case fp_op_int32:
456 		{
457 			int32_t		x;
458 
459 			packint32(pfpsd, pu, &x);
460 			if (!(pfpsd->fp_current_exceptions & pfpsd->fp_fsrtem))
461 				pfpsd->fp_current_write_freg(&x, n, pfpsd);
462 			break;
463 		}
464 	case fp_op_int64:
465 		{
466 			int64_t		x;
467 
468 			packint64(pfpsd, pu, &x);
469 			if ((n & 0x1) == 1)	/* fix register encoding */
470 				n = (n & 0x1e) | 0x20;
471 			if (!(pfpsd->fp_current_exceptions & pfpsd->fp_fsrtem))
472 			    pfpsd->fp_current_write_dreg(&x, DOUBLE(n), pfpsd);
473 			break;
474 		}
475 	case fp_op_single:
476 		{
477 			single_type	x;
478 
479 			packsingle(pfpsd, pu, &x);
480 			if (!(pfpsd->fp_current_exceptions & pfpsd->fp_fsrtem))
481 				pfpsd->fp_current_write_freg(&x, n, pfpsd);
482 			break;
483 		}
484 	case fp_op_double:
485 		{
486 			union {
487 				double_type	x[2];
488 				uint32_t	y[2];
489 				uint64_t	ll;
490 			} db;
491 
492 			packdouble(pfpsd, pu, &db.x[0], &db.y[1]);
493 			if (!(pfpsd->fp_current_exceptions &
494 			    pfpsd->fp_fsrtem)) {
495 				if ((n & 0x1) == 1) /* fix register encoding */
496 					n = (n & 0x1e) | 0x20;
497 				pfpsd->fp_current_write_dreg(&db.ll, DOUBLE(n),
498 					pfpsd);
499 			}
500 			break;
501 		}
502 	case fp_op_extended:
503 		{
504 			union {
505 				extended_type	x;
506 				uint32_t	y[4];
507 				uint64_t	ll[2];
508 			} ex;
509 			unpacked	U;
510 			int		k;
511 			switch (pfpsd->fp_precision) {
512 							/*
513 							 * Implement extended
514 							 * rounding precision
515 							 * mode.
516 							 */
517 			case fp_single:
518 				{
519 					single_type	tx;
520 
521 					packsingle(pfpsd, pu, &tx);
522 					pu = &U;
523 					unpacksingle(pfpsd, pu, tx);
524 					break;
525 				}
526 			case fp_double:
527 				{
528 					double_type	tx;
529 					uint_t		ty;
530 
531 					packdouble(pfpsd, pu, &tx, &ty);
532 					pu = &U;
533 					unpackdouble(pfpsd, pu, tx, ty);
534 					break;
535 				}
536 			case fp_precision_3:	/* rounded to 64 bits */
537 				{
538 					k = pu->exponent + EXTENDED_BIAS;
539 					if (k >= 0) k = 113-64;
540 					else	k = 113-64-k;
541 					fpu_rightshift(pu, 113-64);
542 					round(pfpsd, pu);
543 					pu->sticky = pu->rounded = 0;
544 					pu->exponent += k;
545 					fpu_normalize(pu);
546 					break;
547 				}
548 			}
549 			packextended(pfpsd, pu, &ex.x, &ex.y[1],
550 						&ex.y[2], &ex.y[3]);
551 			if (!(pfpsd->fp_current_exceptions &
552 			    pfpsd->fp_fsrtem)) {
553 				if ((n & 0x1) == 1) /* fix register encoding */
554 					n = (n & 0x1e) | 0x20;
555 				pfpsd->fp_current_write_dreg(&ex.ll[0],
556 							QUAD_E(n), pfpsd);
557 				pfpsd->fp_current_write_dreg(&ex.ll[1],
558 							QUAD_F(n), pfpsd);
559 			}
560 
561 			break;
562 		}
563 	}
564 }
565 
566 void
_fp_pack_word(fp_simd_type * pfpsd,uint32_t * pu,uint_t n)567 _fp_pack_word(
568 	fp_simd_type	*pfpsd,	/* Pointer to simulator data */
569 	uint32_t	*pu,	/* unpacked operand */
570 	uint_t		n)	/* register where datum starts */
571 {
572 	pfpsd->fp_current_write_freg(pu, n, pfpsd);
573 }
574 
575 void
_fp_pack_extword(fp_simd_type * pfpsd,uint64_t * pu,uint_t n)576 _fp_pack_extword(
577 	fp_simd_type	*pfpsd,	/* Pointer to simulator data */
578 	uint64_t	*pu,	/* unpacked operand */
579 	uint_t		n)	/* register where datum starts */
580 {
581 	if ((n & 1) == 1)	/* fix register encoding */
582 		n = (n & 0x1e) | 0x20;
583 	pfpsd->fp_current_write_dreg(pu, DOUBLE(n), pfpsd);
584 }
585