1 /* $NetBSD: t_fpsetmask.c,v 1.22 2024/05/14 14:55:43 riastradh Exp $ */ 2 3 /*- 4 * Copyright (c) 1995 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/param.h> 30 31 #include <atf-c.h> 32 33 #include <stdio.h> 34 #include <signal.h> 35 #include <float.h> 36 #include <setjmp.h> 37 #include <stdlib.h> 38 #include <string.h> 39 40 #include "isqemu.h" 41 42 #ifndef _FLOAT_IEEE754 43 44 ATF_TC(no_test); 45 ATF_TC_HEAD(no_test, tc) 46 { 47 48 atf_tc_set_md_var(tc, "descr", "Dummy test case"); 49 } 50 51 ATF_TC_BODY(no_test, tc) 52 { 53 54 atf_tc_skip("Test not available on this architecture."); 55 } 56 57 #else /* defined(_FLOAT_IEEE754) */ 58 59 #include <ieeefp.h> 60 61 #if (__arm__ && !__SOFTFP__) || __aarch64__ 62 /* 63 * Some NEON fpus do not trap on IEEE 754 FP exceptions. 64 * Skip these tests if running on them and compiled for 65 * hard float. 66 */ 67 #define FPU_PREREQ() do \ 68 { \ 69 if (0 == fpsetmask(fpsetmask(FP_X_INV))) \ 70 atf_tc_skip("FPU does not implement traps on FP exceptions"); \ 71 } while (0) 72 #endif 73 74 #ifdef __riscv__ 75 #ifdef __riscv__ 76 #define FPU_PREREQ() \ 77 atf_tc_skip("RISC-V does not support floating-point exception traps") 78 #endif 79 #endif 80 81 #ifndef FPU_PREREQ 82 #define FPU_PREREQ() __nothing 83 #endif 84 85 void sigfpe(int, siginfo_t *, void *); 86 87 volatile sig_atomic_t signal_caught; 88 volatile int sicode; 89 90 static volatile const float f_one = 1.0; 91 static volatile const float f_zero = 0.0; 92 static volatile const double d_one = 1.0; 93 static volatile const double d_zero = 0.0; 94 static volatile const long double ld_one = 1.0; 95 static volatile const long double ld_zero = 0.0; 96 97 static volatile const float f_huge = FLT_MAX; 98 static volatile const float f_tiny = FLT_MIN; 99 static volatile const double d_huge = DBL_MAX; 100 static volatile const double d_tiny = DBL_MIN; 101 static volatile const long double ld_huge = LDBL_MAX; 102 static volatile const long double ld_tiny = LDBL_MIN; 103 104 static volatile float f_x; 105 static volatile double d_x; 106 static volatile long double ld_x; 107 108 /* trip divide by zero */ 109 static void 110 f_dz(void) 111 { 112 113 f_x = f_one / f_zero; 114 } 115 116 static void 117 d_dz(void) 118 { 119 120 d_x = d_one / d_zero; 121 } 122 123 static void 124 ld_dz(void) 125 { 126 127 ld_x = ld_one / ld_zero; 128 } 129 130 /* trip invalid operation */ 131 static void 132 d_inv(void) 133 { 134 135 d_x = d_zero / d_zero; 136 } 137 138 static void 139 ld_inv(void) 140 { 141 142 ld_x = ld_zero / ld_zero; 143 } 144 145 static void 146 f_inv(void) 147 { 148 149 f_x = f_zero / f_zero; 150 } 151 152 /* trip overflow */ 153 static void 154 f_ofl(void) 155 { 156 157 f_x = f_huge * f_huge; 158 } 159 160 static void 161 d_ofl(void) 162 { 163 164 d_x = d_huge * d_huge; 165 } 166 167 static void 168 ld_ofl(void) 169 { 170 171 ld_x = ld_huge * ld_huge; 172 } 173 174 /* trip underflow */ 175 static void 176 f_ufl(void) 177 { 178 179 f_x = f_tiny * f_tiny; 180 } 181 182 static void 183 d_ufl(void) 184 { 185 186 d_x = d_tiny * d_tiny; 187 } 188 189 static void 190 ld_ufl(void) 191 { 192 193 ld_x = ld_tiny * ld_tiny; 194 } 195 196 struct ops { 197 void (*op)(void); 198 fp_except mask; 199 int sicode; 200 }; 201 202 static const struct ops float_ops[] = { 203 { f_dz, FP_X_DZ, FPE_FLTDIV }, 204 { f_inv, FP_X_INV, FPE_FLTINV }, 205 { f_ofl, FP_X_OFL, FPE_FLTOVF }, 206 { f_ufl, FP_X_UFL, FPE_FLTUND }, 207 { NULL, 0, 0 } 208 }; 209 210 static const struct ops double_ops[] = { 211 { d_dz, FP_X_DZ, FPE_FLTDIV }, 212 { d_inv, FP_X_INV, FPE_FLTINV }, 213 { d_ofl, FP_X_OFL, FPE_FLTOVF }, 214 { d_ufl, FP_X_UFL, FPE_FLTUND }, 215 { NULL, 0, 0 } 216 }; 217 218 static const struct ops long_double_ops[] = { 219 { ld_dz, FP_X_DZ, FPE_FLTDIV }, 220 { ld_inv, FP_X_INV, FPE_FLTINV }, 221 { ld_ofl, FP_X_OFL, FPE_FLTOVF }, 222 { ld_ufl, FP_X_UFL, FPE_FLTUND }, 223 { NULL, 0, 0 } 224 }; 225 226 static sigjmp_buf b; 227 228 static void 229 fpsetmask_masked(const struct ops *test_ops) 230 { 231 struct sigaction sa; 232 fp_except ex1, ex2; 233 const struct ops *t; 234 235 /* mask all exceptions, clear history */ 236 fpsetmask(0); 237 fpsetsticky(0); 238 239 /* set up signal handler */ 240 sa.sa_sigaction = sigfpe; 241 sigemptyset(&sa.sa_mask); 242 sa.sa_flags = SA_SIGINFO; 243 sigaction(SIGFPE, &sa, 0); 244 signal_caught = 0; 245 246 /* 247 * exceptions masked, check whether "sticky" bits are set correctly 248 */ 249 for (t = test_ops; t->op != NULL; t++) { 250 (*t->op)(); 251 ex1 = fpgetsticky(); 252 ATF_CHECK_EQ(ex1 & t->mask, t->mask); 253 ATF_CHECK_EQ(signal_caught, 0); 254 255 /* check correct fpsetsticky() behaviour */ 256 ex2 = fpsetsticky(0); 257 ATF_CHECK_EQ(fpgetsticky(), 0); 258 ATF_CHECK_EQ(ex1, ex2); 259 } 260 } 261 262 /* force delayed exceptions to be delivered */ 263 #define BARRIER() fpsetmask(0); f_x = f_one * f_one 264 265 static void 266 fpsetmask_unmasked(const struct ops *test_ops) 267 { 268 struct sigaction sa; 269 int r; 270 const struct ops *volatile t; 271 272 /* mask all exceptions, clear history */ 273 fpsetmask(0); 274 fpsetsticky(0); 275 276 /* set up signal handler */ 277 sa.sa_sigaction = sigfpe; 278 sigemptyset(&sa.sa_mask); 279 sa.sa_flags = SA_SIGINFO; 280 sigaction(SIGFPE, &sa, 0); 281 signal_caught = 0; 282 283 /* 284 * exception unmasked, check SIGFPE delivery and correct siginfo 285 */ 286 for (t = test_ops; t->op != NULL; t++) { 287 fpsetmask(t->mask); 288 r = sigsetjmp(b, 1); 289 if (!r) { 290 (*t->op)(); 291 BARRIER(); 292 } 293 ATF_CHECK_EQ(signal_caught, 1); 294 ATF_CHECK_EQ(sicode, t->sicode); 295 signal_caught = 0; 296 } 297 } 298 299 void 300 sigfpe(int s, siginfo_t *si, void *c) 301 { 302 signal_caught = 1; 303 sicode = si->si_code; 304 siglongjmp(b, 1); 305 } 306 307 #define TEST(m, t) \ 308 ATF_TC(m##_##t); \ 309 \ 310 ATF_TC_HEAD(m##_##t, tc) \ 311 { \ 312 \ 313 atf_tc_set_md_var(tc, "descr", \ 314 "Test " ___STRING(m) " exceptions for " \ 315 ___STRING(t) "values"); \ 316 } \ 317 \ 318 ATF_TC_BODY(m##_##t, tc) \ 319 { \ 320 \ 321 FPU_PREREQ(); \ 322 \ 323 if (isQEMU_TCG()) \ 324 atf_tc_expect_fail("PR misc/44767"); \ 325 \ 326 m(t##_ops); \ 327 } 328 329 TEST(fpsetmask_masked, float) 330 TEST(fpsetmask_masked, double) 331 TEST(fpsetmask_masked, long_double) 332 TEST(fpsetmask_unmasked, float) 333 TEST(fpsetmask_unmasked, double) 334 TEST(fpsetmask_unmasked, long_double) 335 336 ATF_TC(fpsetmask_basic); 337 ATF_TC_HEAD(fpsetmask_basic, tc) 338 { 339 atf_tc_set_md_var(tc, "descr", "A basic test of fpsetmask(3)"); 340 } 341 342 ATF_TC_BODY(fpsetmask_basic, tc) 343 { 344 size_t i; 345 fp_except_t msk, lst[] = { FP_X_INV, FP_X_DZ, FP_X_OFL, FP_X_UFL }; 346 347 FPU_PREREQ(); 348 349 msk = fpgetmask(); 350 for (i = 0; i < __arraycount(lst); i++) { 351 fpsetmask(msk | lst[i]); 352 ATF_CHECK((fpgetmask() & lst[i]) != 0); 353 fpsetmask(msk & ~lst[i]); 354 ATF_CHECK((fpgetmask() & lst[i]) == 0); 355 } 356 357 } 358 359 #endif /* defined(_FLOAT_IEEE754) */ 360 361 ATF_TP_ADD_TCS(tp) 362 { 363 364 #ifndef _FLOAT_IEEE754 365 ATF_TP_ADD_TC(tp, no_test); 366 #else 367 ATF_TP_ADD_TC(tp, fpsetmask_basic); 368 ATF_TP_ADD_TC(tp, fpsetmask_masked_float); 369 ATF_TP_ADD_TC(tp, fpsetmask_masked_double); 370 ATF_TP_ADD_TC(tp, fpsetmask_masked_long_double); 371 ATF_TP_ADD_TC(tp, fpsetmask_unmasked_float); 372 ATF_TP_ADD_TC(tp, fpsetmask_unmasked_double); 373 ATF_TP_ADD_TC(tp, fpsetmask_unmasked_long_double); 374 #endif 375 376 return atf_no_error(); 377 } 378