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 */ 75void 76fpu_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 */ 134u_int 135fpu_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 */ 146void 147fpu_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 209done: 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 301completed: 302 if (rd != 0) 303 frame->tf_r[rd] = cc; 304} 305