diff --git a/tools/regression/usr.bin/cc/Makefile b/tools/regression/usr.bin/cc/Makefile new file mode 100644 index 000000000000..e5b1b90908f1 --- /dev/null +++ b/tools/regression/usr.bin/cc/Makefile @@ -0,0 +1,12 @@ +# $FreeBSD$ + +TESTS= float +CFLAGS+=-lm + +.PHONY: tests +tests: ${TESTS} + for p in ${TESTS}; do ${.OBJDIR}/$$p; done + +.PHONY: clean +clean: + -rm -f ${TESTS} diff --git a/tools/regression/usr.bin/cc/float.c b/tools/regression/usr.bin/cc/float.c new file mode 100644 index 000000000000..9bdd7d959c16 --- /dev/null +++ b/tools/regression/usr.bin/cc/float.c @@ -0,0 +1,271 @@ +/*- + * Copyright (c) 2012 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Test that floating-point arithmetic works as specified by the C standard. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#ifdef __i386__ +#include +#endif + +#define ALL_STD_EXCEPT (FE_DIVBYZERO | FE_INEXACT | FE_INVALID | \ + FE_OVERFLOW | FE_UNDERFLOW) + +#define TWICE(x) ((x) + (x)) +#define test(desc, pass) test1((desc), (pass), 0) +#define skiptest(desc, pass) test1((desc), (pass), 1) + +#pragma STDC FENV_ACCESS ON + +static const float one_f = 1.0 + FLT_EPSILON / 2; +static const double one_d = 1.0 + DBL_EPSILON / 2; +static const long double one_ld = 1.0L + LDBL_EPSILON / 2; + +static int testnum, failures; + +static void +test1(const char *testdesc, int pass, int skip) +{ + + testnum++; + printf("%sok %d - %s%s\n", pass || skip ? "" : "not ", testnum, + skip ? "(SKIPPED) " : "", testdesc); + if (!pass && !skip) + failures++; +} + +/* + * Compare d1 and d2 using special rules: NaN == NaN and +0 != -0. + */ +static int +fpequal(long double d1, long double d2) +{ + + if (d1 != d2) + return (isnan(d1) && isnan(d2)); + return (copysignl(1.0, d1) == copysignl(1.0, d2)); +} + +void +run_zero_opt_test(double d1, double d2) +{ + + test("optimizations don't break the sign of 0", + fpequal(d1 - d2, 0.0) + && fpequal(-d1 + 0.0, 0.0) + && fpequal(-d1 - d2, -0.0) + && fpequal(-(d1 - d2), -0.0) + && fpequal(-d1 - (-d2), 0.0)); +} + +void +run_inf_opt_test(double d) +{ + + test("optimizations don't break infinities", + fpequal(d / d, NAN) && fpequal(0.0 * d, NAN)); +} + +static inline double +todouble(long double ld) +{ + + return (ld); +} + +static inline float +tofloat(double d) +{ + + return (d); +} + +void +run_tests(void) +{ + volatile long double vld; + long double ld; + volatile double vd; + double d; + volatile float vf; + float f; + int x; + + test("sign bits", fpequal(-0.0, -0.0) && !fpequal(0.0, -0.0)); + + vd = NAN; + test("NaN equality", fpequal(NAN, NAN) && NAN != NAN && vd != vd); + + feclearexcept(ALL_STD_EXCEPT); + test("NaN comparison returns false", !(vd <= vd)); + /* + * XXX disabled; gcc/amd64 botches this IEEE 754 requirement by + * emitting ucomisd instead of comisd. + */ + skiptest("FENV_ACCESS: NaN comparison raises invalid exception", + fetestexcept(ALL_STD_EXCEPT) == FE_INVALID); + + vd = 0.0; + run_zero_opt_test(vd, vd); + + vd = INFINITY; + run_inf_opt_test(vd); + + feclearexcept(ALL_STD_EXCEPT); + vd = INFINITY; + x = (int)vd; + /* XXX disabled (works with -O0); gcc doesn't support FENV_ACCESS */ + skiptest("FENV_ACCESS: Inf->int conversion raises invalid exception", + fetestexcept(ALL_STD_EXCEPT) == FE_INVALID); + + /* Raising an inexact exception here is an IEEE-854 requirement. */ + feclearexcept(ALL_STD_EXCEPT); + vd = 0.75; + x = (int)vd; + test("0.75->int conversion rounds toward 0, raises inexact exception", + x == 0 && fetestexcept(ALL_STD_EXCEPT) == FE_INEXACT); + + feclearexcept(ALL_STD_EXCEPT); + vd = -42.0; + x = (int)vd; + test("-42.0->int conversion is exact, raises no exception", + x == -42 && fetestexcept(ALL_STD_EXCEPT) == 0); + + feclearexcept(ALL_STD_EXCEPT); + x = (int)INFINITY; + /* XXX disabled; gcc doesn't support FENV_ACCESS */ + skiptest("FENV_ACCESS: const Inf->int conversion raises invalid", + fetestexcept(ALL_STD_EXCEPT) == FE_INVALID); + + feclearexcept(ALL_STD_EXCEPT); + x = (int)0.5; + /* XXX disabled; gcc doesn't support FENV_ACCESS */ + skiptest("FENV_ACCESS: const double->int conversion raises inexact", + x == 0 && fetestexcept(ALL_STD_EXCEPT) == FE_INEXACT); + + test("compile-time constants don't have too much precision", + one_f == 1.0L && one_d == 1.0L && one_ld == 1.0L); + + test("const minimum rounding precision", + 1.0F + FLT_EPSILON != 1.0F && + 1.0 + DBL_EPSILON != 1.0 && + 1.0L + LDBL_EPSILON != 1.0L); + + /* It isn't the compiler's fault if this fails on FreeBSD/i386. */ + vf = FLT_EPSILON; + vd = DBL_EPSILON; + vld = LDBL_EPSILON; + test("runtime minimum rounding precision", + 1.0F + vf != 1.0F && 1.0 + vd != 1.0 && 1.0L + vld != 1.0L); + + test("explicit float to float conversion discards extra precision", + (float)(1.0F + FLT_EPSILON * 0.5F) == 1.0F && + (float)(1.0F + vf * 0.5F) == 1.0F); + test("explicit double to float conversion discards extra precision", + (float)(1.0 + FLT_EPSILON * 0.5) == 1.0F && + (float)(1.0 + vf * 0.5) == 1.0F); + test("explicit ldouble to float conversion discards extra precision", + (float)(1.0L + FLT_EPSILON * 0.5L) == 1.0F && + (float)(1.0L + vf * 0.5L) == 1.0F); + + test("explicit double to double conversion discards extra precision", + (double)(1.0 + DBL_EPSILON * 0.5) == 1.0 && + (double)(1.0 + vd * 0.5) == 1.0); + test("explicit ldouble to double conversion discards extra precision", + (double)(1.0L + DBL_EPSILON * 0.5L) == 1.0 && + (double)(1.0L + vd * 0.5L) == 1.0); + + /* + * FLT_EVAL_METHOD > 1 implies that float expressions are always + * evaluated in double precision or higher, but some compilers get + * this wrong when registers spill to memory. The following expression + * forces a spill when there are at most 8 FP registers. + */ + test("implicit promption to double or higher precision is consistent", +#if FLT_EVAL_METHOD == 1 || FLT_EVAL_METHOD == 2 || defined(__i386__) + TWICE(TWICE(TWICE(TWICE(TWICE( + TWICE(TWICE(TWICE(TWICE(1.0F + vf * 0.5F))))))))) + == (1.0 + FLT_EPSILON * 0.5) * 512.0 +#else + 1 +#endif + ); + + f = 1.0 + FLT_EPSILON * 0.5; + d = 1.0L + DBL_EPSILON * 0.5L; + test("const assignment discards extra precision", f == 1.0F && d == 1.0); + + f = 1.0 + vf * 0.5; + d = 1.0L + vd * 0.5L; + test("variable assignment discards explicit extra precision", + f == 1.0F && d == 1.0); + f = 1.0F + vf * 0.5F; + d = 1.0 + vd * 0.5; + test("variable assignment discards implicit extra precision", + f == 1.0F && d == 1.0); + + test("return discards extra precision", + tofloat(1.0 + vf * 0.5) == 1.0F && + todouble(1.0L + vd * 0.5L) == 1.0); + + fesetround(FE_UPWARD); + /* XXX disabled (works with -frounding-math) */ + skiptest("FENV_ACCESS: constant arithmetic respects rounding mode", + 1.0F + FLT_MIN == 1.0F + FLT_EPSILON && + 1.0 + DBL_MIN == 1.0 + DBL_EPSILON && + 1.0L + LDBL_MIN == 1.0L + LDBL_EPSILON); + fesetround(FE_TONEAREST); + + ld = vld * 0.5; + test("associativity is respected", + 1.0L + ld + (LDBL_EPSILON * 0.5) == 1.0L && + 1.0L + (LDBL_EPSILON * 0.5) + ld == 1.0L && + ld + 1.0 + (LDBL_EPSILON * 0.5) == 1.0L && + ld + (LDBL_EPSILON * 0.5) + 1.0 == 1.0L + LDBL_EPSILON); +} + +int +main(int argc, char *argv[]) +{ + + printf("1..26\n"); + +#ifdef __i386__ + fpsetprec(FP_PE); +#endif + run_tests(); + + return (failures); +} diff --git a/tools/regression/usr.bin/cc/float.t b/tools/regression/usr.bin/cc/float.t new file mode 100644 index 000000000000..8bdfd03be81b --- /dev/null +++ b/tools/regression/usr.bin/cc/float.t @@ -0,0 +1,10 @@ +#!/bin/sh +# $FreeBSD$ + +cd `dirname $0` + +executable=`basename $0 .t` + +make $executable 2>&1 > /dev/null + +exec ./$executable