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