1 /* $Id: sparc-fpu.c,v 1.5 2010/06/05 16:08:44 fredette Exp $ */
2 
3 /* ic/sparc/sparc-fpu.c - SPARC floating-point unit implementation */
4 
5 /*
6  * Copyright (c) 2005 Matt Fredette
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *      This product includes software developed by Matt Fredette.
20  * 4. The name of the author may not be used to endorse or promote products
21  *    derived from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
27  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 #include <tme/common.h>
37 _TME_RCSID("$Id: sparc-fpu.c,v 1.5 2010/06/05 16:08:44 fredette Exp $");
38 
39 /* includes: */
40 #include "sparc-impl.h"
41 
42 /* macros: */
43 
44 /* these invoke an IEEE 754 operation: */
45 #define _TME_SPARC_FPU_BEGIN				\
46   do {							\
47     /* at this point, the only possible trap must be	\
48        TME_SPARC_FSR_FTT_IEEE754_exception, so we can	\
49        clear CEXC: */					\
50     ic->tme_sparc_fpu_fsr &= ~TME_SPARC_FSR_CEXC;	\
51   } while (/* CONSTCOND */ 0)
52 #define _TME_SPARC_FPU_OP(func, x)			\
53   do {							\
54     if (__tme_predict_false((func) == NULL)) {		\
55       if (ic->tme_sparc_fpu_incomplete_abort) {		\
56         abort();					\
57       }							\
58       tme_sparc_fpu_exception(ic, TME_SPARC_FSR_FTT_unimplemented_FPop);\
59     }							\
60     _TME_SPARC_FPU_BEGIN;				\
61     (*(func)) x;					\
62   } while (/* CONSTCOND */ 0)
63 #define _TME_SPARC_FPU_OP_MONADIC(func, src, dst) 	\
64   _TME_SPARC_FPU_OP(ic->tme_sparc_fpu_ieee754_ops->func, (&ic->tme_sparc_fpu_ieee754_ctl, src, dst))
65 #define _TME_SPARC_FPU_OP_DYADIC(func, src0, src1, dst)	\
66   _TME_SPARC_FPU_OP(ic->tme_sparc_fpu_ieee754_ops->func, (&ic->tme_sparc_fpu_ieee754_ctl, src0, src1, dst))
67 
68 /* globals: */
69 
70 /* the floating-point condition codes->conditions mapping.  this
71    array is indexed by the fcc value: */
72 const tme_uint8_t _tme_sparc_conds_fcc[4] = {
73 
74   /* E: */
75   (0),
76 
77   /* L: */
78   (TME_BIT(1)		/* fbne */
79    | TME_BIT(2)		/* fblg */
80    | TME_BIT(3)		/* fbul */
81    | TME_BIT(4)),	/* fbl */
82 
83   /* G: */
84   (TME_BIT(1)		/* fbne */
85    | TME_BIT(2)		/* fblg */
86    | TME_BIT(5)		/* fbug */
87    | TME_BIT(6)),	/* fbg */
88 
89   /* U: */
90   (TME_BIT(1)		/* fbne */
91    | TME_BIT(3)		/* fbul */
92    | TME_BIT(5)		/* fbug */
93    | TME_BIT(7))	/* fbu */
94 };
95 
96 /* this resets the FPU: */
97 void
tme_sparc_fpu_reset(struct tme_sparc * ic)98 tme_sparc_fpu_reset(struct tme_sparc *ic)
99 {
100   unsigned int fp_i;
101 
102   /* put nonsignaling NaNs in the floating-point data registers: */
103   for (fp_i = 0;
104        fp_i < TME_ARRAY_ELS(ic->tme_sparc_fpu_fpregs);
105        fp_i++) {
106     ic->tme_sparc_fpu_fpregs[fp_i].tme_float_format = TME_FLOAT_FORMAT_IEEE754_SINGLE;
107     ic->tme_sparc_fpu_fpregs[fp_i].tme_float_value_ieee754_single = ic->tme_sparc_fpu_ieee754_ctl.tme_ieee754_ctl_default_nan_single;
108     ic->tme_sparc_fpu_fpreg_sizes[fp_i] = sizeof(tme_uint32_t) / sizeof(tme_uint32_t);
109   }
110 
111   /* zero the FSR, except for the version field: */
112   ic->tme_sparc_fpu_fsr &= TME_SPARC_FSR_VER;
113 
114   /* use the strict compliance operations: */
115   ic->tme_sparc_fpu_ieee754_ops = ic->tme_sparc_fpu_ieee754_ops_strict;
116 
117   /* the FPU is in execute mode: */
118   ic->tme_sparc_fpu_mode = TME_SPARC_FPU_MODE_EXECUTE;
119 }
120 
121 /* this enables or disables FPU strict compliance: */
122 void
tme_sparc_fpu_strict(struct tme_sparc_bus_connection * conn_sparc,unsigned int strict)123 tme_sparc_fpu_strict(struct tme_sparc_bus_connection *conn_sparc, unsigned int strict)
124 {
125   struct tme_sparc *ic;
126 
127   /* recover our IC: */
128   ic = conn_sparc->tme_sparc_bus_connection.tme_bus_connection.tme_connection_element->tme_element_private;
129 
130   ic->tme_sparc_fpu_ieee754_ops
131     = (strict
132        ? ic->tme_sparc_fpu_ieee754_ops_strict
133        : ic->tme_sparc_fpu_ieee754_ops_user);
134 }
135 
136 /* this handles a sparc FPU exception: */
137 static void
tme_sparc_fpu_exception(struct tme_sparc * ic,tme_uint32_t ftt)138 tme_sparc_fpu_exception(struct tme_sparc *ic, tme_uint32_t ftt)
139 {
140 
141   /* the FPU must be in execute mode, and the FQ must be empty: */
142   assert (ic->tme_sparc_fpu_mode == TME_SPARC_FPU_MODE_EXECUTE
143 	  && (ic->tme_sparc_fpu_fsr & TME_SPARC_FSR_QNE) == 0);
144 
145   /* put the trapping instruction in the FQ: */
146   ic->tme_sparc_fpu_fq[0].tme_sparc_trapqueue_address
147     = (TME_SPARC_VERSION(ic) < 9
148        ? ic->tme_sparc_ireg_uint32(TME_SPARC_IREG_PC)
149        : ic->tme_sparc_ireg_uint64(TME_SPARC_IREG_PC));
150   ic->tme_sparc_fpu_fq[0].tme_sparc_trapqueue_insn
151     = TME_SPARC_INSN;
152 
153   /* set QNE and the FTT field in the FSR: */
154   ic->tme_sparc_fpu_fsr
155     = ((ic->tme_sparc_fpu_fsr & ~TME_SPARC_FSR_FTT)
156        | TME_SPARC_FSR_QNE
157        | ftt);
158 
159   /* enter pending exception mode and redispatch to run the next
160      instruction: */
161   ic->tme_sparc_fpu_mode = TME_SPARC_FPU_MODE_EXCEPTION_PENDING;
162   tme_sparc_redispatch(ic);
163 }
164 
165 /* this checks for a pending sparc FPU trap: */
166 void
tme_sparc_fpu_exception_check(struct tme_sparc * ic)167 tme_sparc_fpu_exception_check(struct tme_sparc *ic)
168 {
169 
170   /* if the FPU is in pending exception mode: */
171   if (ic->tme_sparc_fpu_mode == TME_SPARC_FPU_MODE_EXCEPTION_PENDING) {
172 
173     /* enter exception mode and start IU trap processing: */
174     ic->tme_sparc_fpu_mode = TME_SPARC_FPU_MODE_EXCEPTION;
175     TME_SPARC_INSN_TRAP(TME_SPARC_VERSION(ic) < 9
176 			? TME_SPARC32_TRAP_fp_exception
177 			: (ic->tme_sparc_fpu_fsr & TME_SPARC_FSR_FTT) != TME_SPARC_FSR_FTT_IEEE754_exception
178 			? TME_SPARC64_TRAP_fp_exception_other
179 			: TME_SPARC64_TRAP_fp_exception_ieee_754);
180   }
181 
182   /* otherwise, the FPU must be in exception mode: */
183   assert (ic->tme_sparc_fpu_mode == TME_SPARC_FPU_MODE_EXCEPTION);
184 
185   /* "If an FPop, floating-point load instruction, or floating-point
186      branch instruction is executed while the FPU is in fp_exception
187      state, the FPU returns to fp_exception_pending state and also
188      sets the FSR ftt field to sequence_error. The instruction that
189      caused the sequence_error is not entered into the FQ." */
190   /* XXX FIXME - apparently such an instruction does not trap
191      immediately, but since it can't run either, I interpret this to
192      mean that the instruction is simply ignored: */
193   ic->tme_sparc_fpu_fsr = (ic->tme_sparc_fpu_fsr & ~TME_SPARC_FSR_FTT) | TME_SPARC_FSR_FTT_sequence_error;
194   ic->tme_sparc_fpu_mode = TME_SPARC_FPU_MODE_EXCEPTION;
195   tme_sparc_redispatch(ic);
196 }
197 
198 /* the IEEE 754 exception handler: */
199 static void
_tme_sparc_fpu_exception_ieee754(struct tme_ieee754_ctl * ctl,tme_int8_t exception_ieee754)200 _tme_sparc_fpu_exception_ieee754(struct tme_ieee754_ctl *ctl, tme_int8_t exception_ieee754)
201 {
202   struct tme_sparc *ic;
203   tme_uint32_t exception_cexc;
204 
205   /* map the IEEE754 exception(s) to CEXC bit(s): */
206   exception_cexc = 0;
207 #define _TME_SPARC_FPU_EXCEPTION_MAP(e_ieee754, e_sparc)	\
208   if (((tme_uint8_t) exception_ieee754) & (e_ieee754))		\
209     exception_cexc |= (e_sparc)
210   _TME_SPARC_FPU_EXCEPTION_MAP(TME_FLOAT_EXCEPTION_INVALID,   TME_SPARC_FSR_CEXC_NVC);
211   _TME_SPARC_FPU_EXCEPTION_MAP(TME_FLOAT_EXCEPTION_DIVBYZERO, TME_SPARC_FSR_CEXC_DZC);
212   _TME_SPARC_FPU_EXCEPTION_MAP(TME_FLOAT_EXCEPTION_OVERFLOW,  TME_SPARC_FSR_CEXC_OFC);
213   _TME_SPARC_FPU_EXCEPTION_MAP(TME_FLOAT_EXCEPTION_UNDERFLOW, TME_SPARC_FSR_CEXC_UFC);
214   _TME_SPARC_FPU_EXCEPTION_MAP(TME_FLOAT_EXCEPTION_INEXACT,   TME_SPARC_FSR_CEXC_NXC);
215   if (exception_cexc == 0) {
216     abort();
217   }
218 
219   /* recover our data structure: */
220   ic = (struct tme_sparc *) ctl->tme_ieee754_ctl_private;
221 
222   /* set the CEXC field in the FSR.  "When a floating-point trap
223      occurs ... The value of aexc is unchanged", which is why we don't
224      update it until we're sure a trap is not going to occur: */
225   TME_FIELD_MASK_DEPOSITU(ic->tme_sparc_fpu_fsr, TME_SPARC_FSR_CEXC, exception_cexc);
226 
227   /* if any of the new exceptions are unmasked, take the exception: */
228   if (TME_FIELD_MASK_EXTRACTU(ic->tme_sparc_fpu_fsr, TME_SPARC_FSR_TEM) & exception_cexc) {
229 
230     /* unlock any lock: */
231     if (ic->tme_sparc_fpu_ieee754_ctl.tme_ieee754_ctl_lock_unlock != NULL) {
232       (*ic->tme_sparc_fpu_ieee754_ctl.tme_ieee754_ctl_lock_unlock)();
233       ic->tme_sparc_fpu_ieee754_ctl.tme_ieee754_ctl_lock_unlock = NULL;
234     }
235 
236     /* take the exception: */
237     tme_sparc_fpu_exception(ic, TME_SPARC_FSR_FTT_IEEE754_exception);
238   }
239 
240   /* now that we're sure a trap isn't going to happen, update the AEXC
241      field in the FSR: */
242   ic->tme_sparc_fpu_fsr |= exception_cexc * (TME_SPARC_FSR_AEXC / TME_SPARC_FSR_CEXC);
243 }
244 
TME_SPARC_FORMAT3(tme_sparc32_stdfq,tme_uint32_t)245 TME_SPARC_FORMAT3(tme_sparc32_stdfq, tme_uint32_t)
246 {
247   TME_SPARC_INSN_PRIV;
248   TME_SPARC_INSN_FPU_ENABLED;
249 
250   /* "An attempt to execute STDFQ on an implementation without a
251      floating-point queue causes an fp_exception trap with FSR.ftt set
252      to 4 (sequence_error). On an implementation with a floating-point
253      queue, an attempt to execute STDFQ when the FQ is empty (FSR.qne
254      = 0) should cause an fp_exception trap with FSR.ftt set to 4
255      (sequence_error)." */
256   if ((ic->tme_sparc_fpu_fsr & TME_SPARC_FSR_QNE) == 0) {
257     assert (ic->tme_sparc_fpu_mode == TME_SPARC_FPU_MODE_EXECUTE);
258     tme_sparc_fpu_exception(ic, TME_SPARC_FSR_FTT_sequence_error);
259   }
260   assert (ic->tme_sparc_fpu_mode == TME_SPARC_FPU_MODE_EXCEPTION);
261 
262   /* store the FQ entry: */
263   ic->tme_sparc_ireg_uint32(TME_SPARC_IREG_FPX + 0)
264     = ic->tme_sparc_fpu_fq[0].tme_sparc_trapqueue_address;
265   ic->tme_sparc_ireg_uint32(TME_SPARC_IREG_FPX + 1)
266     = ic->tme_sparc_fpu_fq[0].tme_sparc_trapqueue_insn;
267   tme_sparc32_std(ic, _rs1, _rs2, &ic->tme_sparc_ireg_uint32(TME_SPARC_IREG_FPX));
268 
269   /* clear the QNE bit and return to execute mode: */
270   ic->tme_sparc_fpu_fsr &= ~TME_SPARC_FSR_QNE;
271   ic->tme_sparc_fpu_mode = TME_SPARC_FPU_MODE_EXECUTE;
272 
273   TME_SPARC_INSN_OK;
274 }
275 
276 /* this decodes a floating point register number and checks that it is
277    aligned for a certain format: */
278 unsigned int
tme_sparc_fpu_fpreg_decode(struct tme_sparc * ic,unsigned int fpreg_number_encoded,unsigned int fpreg_format)279 tme_sparc_fpu_fpreg_decode(struct tme_sparc *ic,
280 			   unsigned int fpreg_number_encoded,
281 			   unsigned int fpreg_format)
282 {
283   unsigned int fpreg_number;
284 
285   /* assume that the register number and its encoding are the same: */
286   fpreg_number = fpreg_number_encoded;
287 
288   /* if this is a double or quad-precision register number encoding: */
289 #if (TME_IEEE754_FPREG_FORMAT_SINGLE != 1 || TME_IEEE754_FPREG_FORMAT_DOUBLE != 2 || TME_IEEE754_FPREG_FORMAT_QUAD != 4)
290 #error "bad TME_IEEE754_FPREG_FORMAT_ macros"
291 #endif
292   if (fpreg_format
293       & (TME_IEEE754_FPREG_FORMAT_DOUBLE
294 	 | TME_IEEE754_FPREG_FORMAT_QUAD)) {
295 
296     /* on a v9 CPU, bit zero of a double- or quad-precision register
297        number encoding is bit five of the register number: */
298     if (TME_SPARC_VERSION(ic) >= 9) {
299       fpreg_number
300 	= ((fpreg_number_encoded
301 	    + (fpreg_number_encoded * 32))
302 	   & (64 - 2));
303     }
304   }
305 
306   /* if the register number is misaligned for the format: */
307 #if (TME_IEEE754_FPREG_FORMAT_SINGLE != 1 || TME_IEEE754_FPREG_FORMAT_DOUBLE != 2 || TME_IEEE754_FPREG_FORMAT_QUAD != 4)
308 #error "bad TME_IEEE754_FPREG_FORMAT_ macros"
309 #endif
310   if (__tme_predict_false(fpreg_number
311 			  & ((fpreg_format
312 			      & (TME_IEEE754_FPREG_FORMAT_SINGLE
313 				 | TME_IEEE754_FPREG_FORMAT_DOUBLE
314 				 | TME_IEEE754_FPREG_FORMAT_QUAD))
315 			     - 1))) {
316 
317     /* if this CPU allows misaligned register numbers: */
318     if (ic->tme_sparc_fpu_flags & TME_SPARC_FPU_FLAG_OK_REG_MISALIGNED) {
319 
320       /* force the register number to be aligned: */
321       fpreg_number
322 	&= (0 - (fpreg_format
323 		 & (TME_IEEE754_FPREG_FORMAT_SINGLE
324 		    | TME_IEEE754_FPREG_FORMAT_DOUBLE
325 		    | TME_IEEE754_FPREG_FORMAT_QUAD)));
326     }
327 
328     /* otherwise, this CPU does not allow misaligned register numbers: */
329     else {
330       tme_sparc_fpu_exception(ic, TME_SPARC_FSR_FTT_invalid_fp_register);
331     }
332   }
333 
334   return (fpreg_number);
335 }
336 
337 /* this forces the given floating point register to assume the given
338    format (width, really) in the register file: */
339 void
tme_sparc_fpu_fpreg_format(struct tme_sparc * ic,unsigned int fpreg_number,unsigned int fpreg_format)340 tme_sparc_fpu_fpreg_format(struct tme_sparc *ic,
341 			   unsigned int fpreg_number,
342 			   unsigned int fpreg_format)
343 {
344 
345   /* make sure the register is in the given format: */
346   tme_ieee754_fpreg_format(ic->tme_sparc_fpu_fpregs,
347 			   ic->tme_sparc_fpu_fpreg_sizes,
348 			   fpreg_number,
349 			   (fpreg_format
350 			    | TME_IEEE754_FPREG_FORMAT_ENDIAN_BIG));
351 }
352 
353 /* this forces the given floating point register to assume the given
354    format (width, really) in the register file, then returns a pointer
355    to the register: */
356 static const struct tme_float *
tme_sparc_fpu_fpreg_read(struct tme_sparc * ic,tme_uint32_t fpreg_number_mask,unsigned int fpreg_format)357 tme_sparc_fpu_fpreg_read(struct tme_sparc *ic,
358 			 tme_uint32_t fpreg_number_mask,
359 			 unsigned int fpreg_format)
360 {
361   tme_uint32_t fpreg_number_encoded;
362   unsigned int fpreg_number;
363 
364   /* extract and decode the register number: */
365   fpreg_number_encoded = TME_SPARC_INSN;
366 #if TME_SPARC_FORMAT3_MASK_RD < TME_SPARC_FORMAT3_MASK_RS1 || TME_SPARC_FORMAT3_MASK_RS1 < TME_SPARC_FORMAT3_MASK_RS2
367 #error "TME_SPARC_FORMAT3_MASK_RS values changed"
368 #endif
369   assert (fpreg_number_mask == TME_SPARC_FORMAT3_MASK_RD
370 	  || fpreg_number_mask == TME_SPARC_FORMAT3_MASK_RS1
371 	  || fpreg_number_mask == TME_SPARC_FORMAT3_MASK_RS2);
372   if (fpreg_number_mask == TME_SPARC_FORMAT3_MASK_RD) {
373     fpreg_number_encoded /= (TME_SPARC_FORMAT3_MASK_RD / TME_SPARC_FORMAT3_MASK_RS2);
374   }
375   if (fpreg_number_mask == TME_SPARC_FORMAT3_MASK_RS1) {
376     fpreg_number_encoded /= (TME_SPARC_FORMAT3_MASK_RS1 / TME_SPARC_FORMAT3_MASK_RS2);
377   }
378   fpreg_number_encoded = TME_FIELD_MASK_EXTRACTU(fpreg_number_encoded, TME_SPARC_FORMAT3_MASK_RS2);
379   fpreg_number
380     = tme_sparc_fpu_fpreg_decode(ic,
381 				 fpreg_number_encoded,
382 				 fpreg_format);
383 
384   /* make sure the register is in the given format: */
385   tme_sparc_fpu_fpreg_format(ic, fpreg_number, fpreg_format);
386 
387   /* return a pointer to the register: */
388   return (&ic->tme_sparc_fpu_fpregs[fpreg_number]);
389 }
390 
391 /* include the automatically generated code: */
392 #include "sparc-fpu-auto.c"
393 #ifdef TME_HAVE_INT64_T
394 #include "sparc-vis-auto.c"
395 #endif /* TME_HAVE_INT64_T */
396 
397 /* this checks for an FPU argument: */
398 int
tme_sparc_fpu_new(struct tme_sparc * ic,const char * const * args,int * _arg_i,int * _usage,char ** _output)399 tme_sparc_fpu_new(struct tme_sparc *ic, const char * const *args, int *_arg_i, int *_usage, char **_output)
400 {
401   int arg_i;
402   const char *compliance;
403   int complete;
404   struct tme_ieee754_ctl *ctl;
405   tme_uint32_t ver;
406 
407   /* get the argument index: */
408   arg_i = *_arg_i;
409 
410   /* if this is not an FPU type, this is not an sparc FPU argument: */
411   if (!TME_ARG_IS(args[arg_i + 0], "fpu-type")) {
412     return (FALSE);
413   }
414 
415   /* you can't specify more than one FPU type: */
416   if ((ic->tme_sparc_fpu_fsr & TME_SPARC_FSR_VER) != TME_SPARC_FSR_VER_missing) {
417     tme_output_append_error(_output,
418 			    "%s fpu-type %s",
419 			    _("multiple"),
420 			    _("unexpected"));
421     *_usage = TRUE;
422     return (TRUE);
423   }
424 
425   /* get the FPU type: */
426   if (args[arg_i + 1] == NULL) {
427     *_usage = TRUE;
428     return (TRUE);
429   }
430   ver = (*ic->_tme_sparc_fpu_ver)(ic, args[arg_i + 1], NULL);
431   if (ver == TME_SPARC_FSR_VER_missing) {
432     tme_output_append_error(_output,
433 			    "%s fpu-type %s",
434 			    _("bad"),
435 			    args[arg_i + 1]);
436     *_usage = TRUE;
437     return (TRUE);
438   }
439   ic->tme_sparc_fpu_fsr = (ic->tme_sparc_fpu_fsr & ~TME_SPARC_FSR_VER) | ver;
440   arg_i += 2;
441 
442   /* the next argument must be a compliance level: */
443   compliance = args[arg_i + 1];
444   if (!TME_ARG_IS(args[arg_i + 0], "fpu-compliance")
445       || compliance == NULL) {
446     *_usage = TRUE;
447     return (TRUE);
448   }
449   ic->tme_sparc_fpu_ieee754_ops_user = tme_ieee754_ops_lookup(compliance);
450   if (ic->tme_sparc_fpu_ieee754_ops_user == NULL) {
451     tme_output_append_error(_output,
452 			    "%s fpu-compliance %s",
453 			    _("bad"),
454 			    compliance);
455     *_usage = TRUE;
456     return (TRUE);
457   }
458   arg_i += 2;
459 
460   /* see if the operations for this compliance level are complete: */
461 #define _TME_SPARC_FPU_OP_CHECK(func) (ic->tme_sparc_fpu_ieee754_ops_user->func != NULL)
462   complete
463     = (_TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_single_add)
464        && _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_single_div)
465        && _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_single_from_double)
466        && _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_single_mul)
467        && _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_single_mul)
468        && _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_single_sub)
469        && _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_single_sub)
470        && _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_single_sub)
471        && _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_double_add)
472        && _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_double_div)
473        && _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_double_from_single)
474        && _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_double_from_single)
475        && _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_double_from_single)
476        && _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_double_mul)
477        && _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_double_sub)
478        && _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_double_sub)
479        && _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_double_sub)
480        && ((ic->tme_sparc_fpu_flags & TME_SPARC_FPU_FLAG_NO_FSQRT) != 0
481 	   || (_TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_single_sqrt)
482 	       && _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_double_sqrt)
483 	       && ((ic->tme_sparc_fpu_flags & TME_SPARC_FPU_FLAG_NO_QUAD) != 0
484 		   || _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_quad_sqrt))))
485        && ((ic->tme_sparc_fpu_flags & TME_SPARC_FPU_FLAG_NO_QUAD) != 0
486 	   || (_TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_double_from_quad)
487 	       && _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_quad_add)
488 	       && _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_quad_div)
489 	       && _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_quad_from_double)
490 	       && _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_quad_from_double)
491 	       && _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_quad_from_double)
492 	       && _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_quad_from_single)
493 	       && _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_quad_mul)
494 	       && _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_quad_mul)
495 	       && _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_quad_sub)
496 	       && _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_quad_sub)
497 	       && _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_quad_sub)
498 	       && _TME_SPARC_FPU_OP_CHECK(tme_ieee754_ops_single_from_quad))));
499 #undef _TME_SPARC_FPU_OP_CHECK
500 
501   /* if the next argument is an incomplete disposition: */
502   if (TME_ARG_IS(args[arg_i + 0], "fpu-incomplete")) {
503 
504     if (TME_ARG_IS(args[arg_i + 1], "abort")) {
505       ic->tme_sparc_fpu_incomplete_abort = TRUE;
506     }
507     else if (TME_ARG_IS(args[arg_i + 1], "trap")) {
508       ic->tme_sparc_fpu_incomplete_abort = FALSE;
509     }
510     else {
511       tme_output_append_error(_output,
512 			      "%s fpu-incomplete %s",
513 			      _("bad"),
514 			      args[arg_i + 1]);
515       *_usage = TRUE;
516       return (TRUE);
517     }
518     arg_i += 2;
519   }
520 
521   /* otherwise, no incomplete disposition is given.  if this
522      compliance is incomplete: */
523   else if (!complete) {
524     tme_output_append_error(_output,
525 			    "%s %s %s fpu-incomplete",
526 			    _("compliance"),
527 			    compliance,
528 			    _("is incomplete, needs"));
529     *_usage = TRUE;
530     return (TRUE);
531   }
532 
533   /* initialize the IEEE 754 control: */
534   ctl = &ic->tme_sparc_fpu_ieee754_ctl;
535 
536   /* a private data structure: */
537   ctl->tme_ieee754_ctl_private = ic;
538 
539   /* the underflow tininess-detection mode.  Appendix N.5 of my V8
540      manual says that this is the mode used on the SPARC: */
541   ctl->tme_ieee754_ctl_detect_tininess = TME_IEEE754_CTL_DETECT_TININESS_BEFORE_ROUNDING;
542 
543   /* the exception function: */
544   ctl->tme_ieee754_ctl_exception = _tme_sparc_fpu_exception_ieee754;
545 
546   /* we do check whether or not a value is a sNaN when converting it
547      from one precision to another: */
548   ctl->tme_ieee754_ctl_check_snan_on_conversion = TRUE;
549 
550   /* the default generated NaN patterns: */
551   ctl->tme_ieee754_ctl_default_nan_single = 0x7fffffff;
552   ctl->tme_ieee754_ctl_default_nan_double.tme_value64_uint32_hi = 0x7fffffff;
553   ctl->tme_ieee754_ctl_default_nan_double.tme_value64_uint32_lo = 0xffffffff;
554   ctl->tme_ieee754_ctl_default_nan_quad.tme_float_ieee754_quad_hi.tme_value64_uint32_hi = 0x7fffffff;
555   ctl->tme_ieee754_ctl_default_nan_quad.tme_float_ieee754_quad_hi.tme_value64_uint32_lo = 0xffffffff;
556   ctl->tme_ieee754_ctl_default_nan_quad.tme_float_ieee754_quad_lo.tme_value64_uint32_hi = 0xffffffff;
557   ctl->tme_ieee754_ctl_default_nan_quad.tme_float_ieee754_quad_lo.tme_value64_uint32_lo = 0xffffffff;
558 
559   /* NaN tests: */
560   ctl->tme_ieee754_ctl_is_snan_single = _tme_sparc_fpu_is_snan_single;
561   ctl->tme_ieee754_ctl_is_snan_double = _tme_sparc_fpu_is_snan_double;
562   ctl->tme_ieee754_ctl_is_snan_quad = _tme_sparc_fpu_is_snan_quad;
563 
564   /* NaN canonicalization: */
565   ctl->tme_ieee754_ctl_nan_single_to_common = tme_ieee754_default_nan_single_to_common;
566   ctl->tme_ieee754_ctl_nan_common_to_single = tme_ieee754_default_nan_common_to_single;
567   ctl->tme_ieee754_ctl_nan_double_to_common = tme_ieee754_default_nan_double_to_common;
568   ctl->tme_ieee754_ctl_nan_common_to_double = tme_ieee754_default_nan_common_to_double;
569   ctl->tme_ieee754_ctl_nan_quad_to_common = tme_ieee754_default_nan_quad_to_common;
570   ctl->tme_ieee754_ctl_nan_common_to_quad = tme_ieee754_default_nan_common_to_quad;
571 
572   /* NaN propagation: */
573   ctl->tme_ieee754_ctl_nan_from_nans_single = _tme_sparc_fpu_nan_from_nans_single;
574   ctl->tme_ieee754_ctl_nan_from_nans_double = _tme_sparc_fpu_nan_from_nans_double;
575   ctl->tme_ieee754_ctl_nan_from_nans_quad = _tme_sparc_fpu_nan_from_nans_quad;
576 
577   /* look up the strict compliance operations: */
578   ic->tme_sparc_fpu_ieee754_ops_strict = tme_ieee754_ops_lookup("strict");
579   assert (ic->tme_sparc_fpu_ieee754_ops_strict != NULL);
580 
581   /* done: */
582   *_arg_i = arg_i;
583   return (TRUE);
584 }
585 
586 /* this returns the FPU usage: */
587 void
tme_sparc_fpu_usage(struct tme_sparc * ic,char ** _output)588 tme_sparc_fpu_usage(struct tme_sparc *ic, char **_output)
589 {
590   tme_output_append_error(_output,
591 			  "[ fpu-type ");
592   (*ic->_tme_sparc_fpu_ver)(ic, NULL, _output);
593   tme_output_append_error(_output,
594 			  " ] fpu-compliance %s [ fpu-incomplete { abort | trap } ] ]",
595 			  tme_ieee754_compliance_options);
596 }
597