1 /* $NetBSD: t_fpsetmask.c,v 1.3 2011/10/19 15:27:16 njoly 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 <atf-c.h> 30 31 #include <stdio.h> 32 #include <signal.h> 33 #include <float.h> 34 #include <setjmp.h> 35 #include <stdlib.h> 36 #include <string.h> 37 38 #ifndef _FLOAT_IEEE754 39 40 ATF_TC(no_test); 41 ATF_TC_HEAD(no_test, tc) 42 { 43 44 atf_tc_set_md_var(tc, "descr", "Dummy test case"); 45 } 46 47 ATF_TC_BODY(no_test, tc) 48 { 49 50 atf_tc_skip("Test not available on this architecture."); 51 } 52 53 #else /* defined(_FLOAT_IEEE754) */ 54 55 #include <ieeefp.h> 56 57 const char *skip_mesg; 58 const char *skip_arch; 59 60 void sigfpe(int, siginfo_t *, void *); 61 62 volatile sig_atomic_t signal_caught; 63 volatile int sicode; 64 65 static volatile const float f_one = 1.0; 66 static volatile const float f_zero = 0.0; 67 static volatile const double d_one = 1.0; 68 static volatile const double d_zero = 0.0; 69 static volatile const long double ld_one = 1.0; 70 static volatile const long double ld_zero = 0.0; 71 72 static volatile const float f_huge = FLT_MAX; 73 static volatile const float f_tiny = FLT_MIN; 74 static volatile const double d_huge = DBL_MAX; 75 static volatile const double d_tiny = DBL_MIN; 76 static volatile const long double ld_huge = LDBL_MAX; 77 static volatile const long double ld_tiny = LDBL_MIN; 78 79 static volatile float f_x; 80 static volatile double d_x; 81 static volatile long double ld_x; 82 83 /* trip divide by zero */ 84 static void 85 f_dz(void) 86 { 87 88 f_x = f_one / f_zero; 89 } 90 91 static void 92 d_dz(void) 93 { 94 95 d_x = d_one / d_zero; 96 } 97 98 static void 99 ld_dz(void) 100 { 101 102 ld_x = ld_one / ld_zero; 103 } 104 105 /* trip invalid operation */ 106 static void 107 d_inv(void) 108 { 109 110 d_x = d_zero / d_zero; 111 } 112 113 static void 114 ld_inv(void) 115 { 116 117 ld_x = ld_zero / ld_zero; 118 } 119 120 static void 121 f_inv(void) 122 { 123 124 f_x = f_zero / f_zero; 125 } 126 127 /* trip overflow */ 128 static void 129 f_ofl(void) 130 { 131 132 f_x = f_huge * f_huge; 133 } 134 135 static void 136 d_ofl(void) 137 { 138 139 d_x = d_huge * d_huge; 140 } 141 142 static void 143 ld_ofl(void) 144 { 145 146 ld_x = ld_huge * ld_huge; 147 } 148 149 /* trip underflow */ 150 static void 151 f_ufl(void) 152 { 153 154 f_x = f_tiny * f_tiny; 155 } 156 157 static void 158 d_ufl(void) 159 { 160 161 d_x = d_tiny * d_tiny; 162 } 163 164 static void 165 ld_ufl(void) 166 { 167 168 ld_x = ld_tiny * ld_tiny; 169 } 170 171 struct ops { 172 void (*op)(void); 173 fp_except mask; 174 int sicode; 175 }; 176 177 static const struct ops float_ops[] = { 178 { f_dz, FP_X_DZ, FPE_FLTDIV }, 179 { f_inv, FP_X_INV, FPE_FLTINV }, 180 { f_ofl, FP_X_OFL, FPE_FLTOVF }, 181 { f_ufl, FP_X_UFL, FPE_FLTUND }, 182 { NULL, 0, 0 } 183 }; 184 185 static const struct ops double_ops[] = { 186 { d_dz, FP_X_DZ, FPE_FLTDIV }, 187 { d_inv, FP_X_INV, FPE_FLTINV }, 188 { d_ofl, FP_X_OFL, FPE_FLTOVF }, 189 { d_ufl, FP_X_UFL, FPE_FLTUND }, 190 { NULL, 0, 0 } 191 }; 192 193 static const struct ops long_double_ops[] = { 194 { ld_dz, FP_X_DZ, FPE_FLTDIV }, 195 { ld_inv, FP_X_INV, FPE_FLTINV }, 196 { ld_ofl, FP_X_OFL, FPE_FLTOVF }, 197 { ld_ufl, FP_X_UFL, FPE_FLTUND }, 198 { NULL, 0, 0 } 199 }; 200 201 static sigjmp_buf b; 202 203 static void 204 fpsetmask_masked(const struct ops *test_ops) 205 { 206 struct sigaction sa; 207 fp_except ex1, ex2; 208 const struct ops *t; 209 210 /* mask all exceptions, clear history */ 211 fpsetmask(0); 212 fpsetsticky(0); 213 214 /* set up signal handler */ 215 sa.sa_sigaction = sigfpe; 216 sigemptyset(&sa.sa_mask); 217 sa.sa_flags = SA_SIGINFO; 218 sigaction(SIGFPE, &sa, 0); 219 signal_caught = 0; 220 221 /* 222 * exceptions masked, check whether "sticky" bits are set correctly 223 */ 224 for (t = test_ops; t->op != NULL; t++) { 225 (*t->op)(); 226 ex1 = fpgetsticky(); 227 ATF_CHECK_EQ(ex1 & t->mask, t->mask); 228 ATF_CHECK_EQ(signal_caught, 0); 229 230 /* check correct fpsetsticky() behaviour */ 231 ex2 = fpsetsticky(0); 232 ATF_CHECK_EQ(fpgetsticky(), 0); 233 ATF_CHECK_EQ(ex1, ex2); 234 } 235 } 236 237 /* force delayed exceptions to be delivered */ 238 #define BARRIER() fpsetmask(0); f_x = f_one * f_one 239 240 static void 241 fpsetmask_unmasked(const struct ops *test_ops) 242 { 243 struct sigaction sa; 244 int r; 245 const struct ops *volatile t; 246 247 /* mask all exceptions, clear history */ 248 fpsetmask(0); 249 fpsetsticky(0); 250 251 /* set up signal handler */ 252 sa.sa_sigaction = sigfpe; 253 sigemptyset(&sa.sa_mask); 254 sa.sa_flags = SA_SIGINFO; 255 sigaction(SIGFPE, &sa, 0); 256 signal_caught = 0; 257 258 /* 259 * exception unmasked, check SIGFPE delivery and correct siginfo 260 */ 261 for (t = test_ops; t->op != NULL; t++) { 262 fpsetmask(t->mask); 263 r = sigsetjmp(b, 1); 264 if (!r) { 265 (*t->op)(); 266 BARRIER(); 267 } 268 ATF_CHECK_EQ(signal_caught, 1); 269 ATF_CHECK_EQ(sicode, t->sicode); 270 signal_caught = 0; 271 } 272 } 273 274 void 275 sigfpe(int s, siginfo_t *si, void *c) 276 { 277 signal_caught = 1; 278 sicode = si->si_code; 279 siglongjmp(b, 1); 280 } 281 282 #define TEST(m, t) \ 283 ATF_TC(m##_##t); \ 284 \ 285 ATF_TC_HEAD(m##_##t, tc) \ 286 { \ 287 \ 288 atf_tc_set_md_var(tc, "descr", \ 289 "Test " ___STRING(m) " exceptions for " \ 290 ___STRING(t) "values"); \ 291 } \ 292 \ 293 ATF_TC_BODY(m##_##t, tc) \ 294 { \ 295 \ 296 if (system("cpuctl identify 0 | grep -q QEMU") == 0) \ 297 atf_tc_skip("Test not applicable on QEMU"); \ 298 if (system("cpuctl identify 0 | grep -q \ 299 'cpu0: Intel Pentium II (Klamath) (686-class), id 0x633'")\ 300 == 0) \ 301 atf_tc_skip("Test not applicable on QEMU " \ 302 "(heuristic match)"); \ 303 m(t##_ops); \ 304 } 305 306 TEST(fpsetmask_masked, float) 307 TEST(fpsetmask_masked, double) 308 TEST(fpsetmask_masked, long_double) 309 TEST(fpsetmask_unmasked, float) 310 TEST(fpsetmask_unmasked, double) 311 TEST(fpsetmask_unmasked, long_double) 312 313 ATF_TC(fpsetmask_basic); 314 ATF_TC_HEAD(fpsetmask_basic, tc) 315 { 316 atf_tc_set_md_var(tc, "descr", "A basic test of fpsetmask(3)"); 317 } 318 319 ATF_TC_BODY(fpsetmask_basic, tc) 320 { 321 size_t i; 322 fp_except_t msk, lst[] = { FP_X_INV, FP_X_DZ, FP_X_OFL, FP_X_UFL }; 323 324 msk = fpgetmask(); 325 for (i = 0; i < __arraycount(lst); i++) { 326 fpsetmask(msk | lst[i]); 327 ATF_CHECK((fpgetmask() & lst[i]) != 0); 328 fpsetmask(msk & lst[i]); 329 ATF_CHECK((fpgetmask() & lst[i]) == 0); 330 } 331 332 } 333 334 #endif /* defined(_FLOAT_IEEE754) */ 335 336 ATF_TP_ADD_TCS(tp) 337 { 338 339 #ifndef _FLOAT_IEEE754 340 ATF_TP_ADD_TC(tp, no_test); 341 #else 342 ATF_TP_ADD_TC(tp, fpsetmask_basic); 343 ATF_TP_ADD_TC(tp, fpsetmask_masked_float); 344 ATF_TP_ADD_TC(tp, fpsetmask_masked_double); 345 ATF_TP_ADD_TC(tp, fpsetmask_masked_long_double); 346 ATF_TP_ADD_TC(tp, fpsetmask_unmasked_float); 347 ATF_TP_ADD_TC(tp, fpsetmask_unmasked_double); 348 ATF_TP_ADD_TC(tp, fpsetmask_unmasked_long_double); 349 #endif 350 351 return atf_no_error(); 352 } 353