1 /* $OpenBSD: fpu.c,v 1.4 2021/09/24 14:37:56 aoyama Exp $ */
2
3 /*
4 * Copyright (c) 2007, 2014, Miodrag Vallat.
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice, this permission notice, and the disclaimer below
9 * appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 /*
21 * Common bits between the 88100 and the 88110 floating point completion
22 * code.
23 */
24
25 #include <sys/param.h>
26 #include <sys/proc.h>
27
28 #include <machine/fpu.h>
29 #include <machine/frame.h>
30 #include <machine/ieeefp.h>
31
32 #include <lib/libkern/softfloat.h>
33
34 #include <m88k/m88k/fpu.h>
35
36 /*
37 * Values for individual bits in fcmp results.
38 */
39 #define CC_UN 0x00000001 /* unordered */
40 #define CC_LEG 0x00000002 /* less than, equal or greater than */
41 #define CC_EQ 0x00000004 /* equal */
42 #define CC_NE 0x00000008 /* not equal */
43 #define CC_GT 0x00000010 /* greater than */
44 #define CC_LE 0x00000020 /* less than or equal */
45 #define CC_LT 0x00000040 /* less than */
46 #define CC_GE 0x00000080 /* greater than or equal */
47 #define CC_OU 0x00000100 /* out of range */
48 #define CC_IB 0x00000200 /* in range or on boundary */
49 #define CC_IN 0x00000400 /* in range */
50 #define CC_OB 0x00000800 /* out of range or on boundary */
51 /* the following only on 88110 */
52 #define CC_UE 0x00001000 /* unordered or equal */
53 #define CC_LG 0x00002000 /* less than or greater than */
54 #define CC_UG 0x00004000 /* unordered or greater than */
55 #define CC_ULE 0x00008000 /* unordered or less than or equal */
56 #define CC_UL 0x00010000 /* unordered or less than */
57 #define CC_UGE 0x00020000 /* unordered or greater than or equal */
58
59 /*
60 * Inlines from softfloat-specialize.h which are not made public, needed
61 * for fpu_compare.
62 */
63 #define float32_is_nan(a) \
64 (0xff000000 < (a << 1))
65 #define float32_is_signaling_nan(a) \
66 ((((a >> 22) & 0x1ff) == 0x1fe) && (a & 0x003fffff))
67
68 /*
69 * Store a floating-point result, converting it to the required format if it
70 * is of smaller precision.
71 *
72 * This assumes the original format (orig_width) is not FTYPE_INT, and the
73 * final format (width) <= orig_width.
74 */
75 void
fpu_store(struct trapframe * frame,u_int regno,u_int orig_width,u_int width,fparg * src)76 fpu_store(struct trapframe *frame, u_int regno, u_int orig_width, u_int width,
77 fparg *src)
78 {
79 u_int32_t tmp;
80 u_int rd;
81
82 switch (width) {
83 case FTYPE_INT:
84 rd = float_get_round(frame->tf_fpcr);
85 switch (orig_width) {
86 case FTYPE_SNG:
87 if (rd == FP_RZ)
88 tmp = float32_to_int32_round_to_zero(src->sng);
89 else
90 tmp = float32_to_int32(src->sng);
91 break;
92 case FTYPE_DBL:
93 if (rd == FP_RZ)
94 tmp = float64_to_int32_round_to_zero(src->dbl);
95 else
96 tmp = float64_to_int32(src->dbl);
97 break;
98 }
99 if (regno != 0)
100 frame->tf_r[regno] = tmp;
101 break;
102 case FTYPE_SNG:
103 switch (orig_width) {
104 case FTYPE_SNG:
105 tmp = src->sng;
106 break;
107 case FTYPE_DBL:
108 tmp = float64_to_float32(src->dbl);
109 break;
110 }
111 if (regno != 0)
112 frame->tf_r[regno] = tmp;
113 break;
114 case FTYPE_DBL:
115 switch (orig_width) {
116 case FTYPE_DBL:
117 tmp = (u_int32_t)(src->dbl >> 32);
118 if (regno != 0)
119 frame->tf_r[regno] = tmp;
120 tmp = (u_int32_t)src->dbl;
121 if (regno != 31)
122 frame->tf_r[regno + 1] = tmp;
123 break;
124 }
125 break;
126 }
127 }
128
129 /*
130 * Return the largest precision of all precision inputs.
131 *
132 * This assumes none of the inputs is FTYPE_INT.
133 */
134 u_int
fpu_precision(u_int ts1,u_int ts2,u_int td)135 fpu_precision(u_int ts1, u_int ts2, u_int td)
136 {
137 return max(td, max(ts1, ts2));
138 }
139
140 /*
141 * Perform a compare instruction (fcmp, fcmpu).
142 *
143 * If either operand is NaN, the result is unordered. This causes an
144 * reserved operand exception (except for nonsignalling NaNs for fcmpu).
145 */
146 void
fpu_compare(struct trapframe * frame,fparg * s1,fparg * s2,u_int width,u_int rd,u_int fcmpu)147 fpu_compare(struct trapframe *frame, fparg *s1, fparg *s2, u_int width,
148 u_int rd, u_int fcmpu)
149 {
150 u_int32_t cc;
151 int zero, s1positive, s2positive;
152
153 /*
154 * Handle NaNs first, and raise invalid if fcmp or signaling NaN.
155 */
156 switch (width) {
157 case FTYPE_SNG:
158 if (float32_is_nan(s1->sng)) {
159 if (!fcmpu || float32_is_signaling_nan(s1->sng))
160 float_set_invalid();
161 cc = CC_UN;
162 goto done;
163 }
164 if (float32_is_nan(s2->sng)) {
165 if (!fcmpu || float32_is_signaling_nan(s2->sng))
166 float_set_invalid();
167 cc = CC_UN;
168 goto done;
169 }
170 break;
171 case FTYPE_DBL:
172 if (float64_is_nan(s1->dbl)) {
173 if (!fcmpu || float64_is_signaling_nan(s1->dbl))
174 float_set_invalid();
175 cc = CC_UN;
176 goto done;
177 }
178 if (float64_is_nan(s2->dbl)) {
179 if (!fcmpu || float64_is_signaling_nan(s2->dbl))
180 float_set_invalid();
181 cc = CC_UN;
182 goto done;
183 }
184 break;
185 }
186
187 /*
188 * Now order the two numbers.
189 */
190 switch (width) {
191 case FTYPE_SNG:
192 if (float32_eq(s1->sng, s2->sng))
193 cc = CC_EQ;
194 else if (float32_lt(s1->sng, s2->sng))
195 cc = CC_LT | CC_NE;
196 else
197 cc = CC_GT | CC_NE;
198 break;
199 case FTYPE_DBL:
200 if (float64_eq(s1->dbl, s2->dbl))
201 cc = CC_EQ;
202 else if (float64_lt(s1->dbl, s2->dbl))
203 cc = CC_LT | CC_NE;
204 else
205 cc = CC_GT | CC_NE;
206 break;
207 }
208
209 done:
210
211 /*
212 * Complete condition code mask.
213 */
214
215 if (cc & CC_UN)
216 cc |= CC_NE | CC_UE | CC_UG | CC_ULE | CC_UL | CC_UGE;
217 if (cc & CC_EQ)
218 cc |= CC_LE | CC_GE | CC_UE;
219 if (cc & CC_GT)
220 cc |= CC_GE;
221 if (cc & CC_LT)
222 cc |= CC_LE;
223 if (cc & (CC_LT | CC_GT))
224 cc |= CC_LG;
225 if (cc & (CC_LT | CC_GT | CC_EQ))
226 cc |= CC_LEG;
227 if (cc & CC_GT)
228 cc |= CC_UG;
229 if (cc & CC_LE)
230 cc |= CC_ULE;
231 if (cc & CC_LT)
232 cc |= CC_UL;
233 if (cc & CC_GE)
234 cc |= CC_UGE;
235
236 #ifdef M88100
237 if (CPU_IS88100) {
238 cc &= ~(CC_UE | CC_LG | CC_UG | CC_ULE | CC_UL | CC_UGE);
239 }
240 #endif
241
242 /*
243 * Fill the interval bits.
244 * s1 is compared to the interval [0, s2] unless s2 is negative.
245 */
246 if (!(cc & CC_UN)) {
247 switch (width) {
248 case FTYPE_SNG:
249 s2positive = s2->sng >> 31 == 0;
250 break;
251 case FTYPE_DBL:
252 s2positive = s2->dbl >> 63 == 0;
253 break;
254 }
255 if (!s2positive)
256 goto completed;
257
258 if (cc & CC_EQ) {
259 /* if s1 and s2 are equal, s1 is on boundary */
260 cc |= CC_IB | CC_OB;
261 goto completed;
262 }
263
264 /* s1 and s2 are either Zero, numbers or Inf */
265 switch (width) {
266 case FTYPE_SNG:
267 zero = float32_eq(s1->sng, 0);
268 break;
269 case FTYPE_DBL:
270 zero = float64_eq(s1->dbl, 0LL);
271 break;
272 }
273 if (zero) {
274 /* if s1 is zero, it is on boundary */
275 cc |= CC_IB | CC_OB;
276 goto completed;
277 }
278
279 if (cc & CC_GT) {
280 /* 0 <= s2 < s1 -> out of interval */
281 cc |= CC_OU | CC_OB;
282 } else {
283 switch (width) {
284 case FTYPE_SNG:
285 s1positive = s1->sng >> 31 == 0;
286 break;
287 case FTYPE_DBL:
288 s1positive = s1->dbl >> 63 == 0;
289 break;
290 }
291 if (s1positive) {
292 /* 0 < s1 < s2 -> in interval */
293 cc |= CC_IB | CC_IN;
294 } else {
295 /* s1 < 0 <= s2 */
296 cc |= CC_OU | CC_OB;
297 }
298 }
299 }
300
301 completed:
302 if (rd != 0)
303 frame->tf_r[rd] = cc;
304 }
305