Hello! As discussed in the PR, different modes of a FP compare RTX are not strong enough to survive through RTL optimization passes. Attached testcase was miscompiled due to combine changing the mode of FP compare through SELECT_CC_MODE.
The solution, implemented in the attached patch, is to drop CCFPUmode (which was used to distinguish unordered and ordered compares) and use UNSPEC_NOTRAP unspec wrappers around compare RTXes for unordered comparisons. 2017-10-27 Uros Bizjak <ubiz...@gmail.com> PR target/82692 * config/i386/i386-modes.def (CCFPU): Remove definition. * config/i386/i386.c (put_condition_mode): Remove CCFPU mode handling. (ix86_cc_modes_compatible): Ditto. (ix86_expand_carry_flag_compare): Ditto. (ix86_expand_int_movcc): Ditto. (ix86_expand_int_addcc): Ditto. (ix86_reverse_condition): Ditto. (ix86_unordered_fp_compare): Rename from ix86_fp_compare_mode. Return true/false for unordered/ordered fp comparisons. (ix86_cc_mode): Always return CCFPmode for float mode comparisons. (ix86_prepare_fp_compare_args): Update for rename. (ix86_expand_fp_compare): Update for rename. Generate unordered compare RTXes wrapped with UNSPEC_NOTRAP unspec. (ix86_expand_sse_compare_and_jump): Ditto. * config/i386/predicates.md (fcmov_comparison_operator): Remove CCFPU mode handling. (ix86_comparison_operator): Ditto. (ix86_carry_flag_operator): Ditto. * config/i386/i386.md (UNSPEC_NOTRAP): New unspec. (*cmpu<mode>_i387): Wrap compare RTX with UNSPEC_NOTRAP unspec. (*cmpu<mode>_cc_i387): Ditto. (FPCMP): Remove mode iterator. (unord): Remove mode attribute. (unord_subst): New define_subst transformation (unord): New define_subst attribute. (unordered): Ditto. (*cmpi<unord><MODEF:mode>): Rewrite using unord_subst transformation. (*cmpi<unord>xf_i387): Ditto. * config/i386/sse.md (<sse>_<unord>comi<round_saeonly_name>): Merge from <sse>_comi<round_saeonly_name> and <sse>_ucomi<round_saeonly_name> using unord_subst transformation. * config/i386/subst.md (SUBST_A): Remove CCFP and CCFPU modes. (round_saeonly): Also handle CCFP mode. * reg-stack.c (subst_stack_regs_pat): Handle UNSPEC_NOTRAP unspec. Remove UNSPEC_SAHF unspec handling. testsuite/ChangeLog: 2017-10-27 Uros Bizjak <ubiz...@gmail.com> PR target/82692 * gcc.dg/torture/pr82692.c: New test. Patch was bootstrapped and regression tested on x86_64-linux-gnu {,-m32}. Committed to mainline SVN. Uros.
Index: config/i386/i386-modes.def =================================================================== --- config/i386/i386-modes.def (revision 254111) +++ config/i386/i386-modes.def (working copy) @@ -72,8 +72,8 @@ CC_MODE (CCO); CC_MODE (CCP); CC_MODE (CCS); CC_MODE (CCZ); + CC_MODE (CCFP); -CC_MODE (CCFPU); /* Vector modes. Note that VEC_CONCAT patterns require vector sizes twice as big as implemented in hardware. */ Index: config/i386/i386.c =================================================================== --- config/i386/i386.c (revision 254111) +++ config/i386/i386.c (working copy) @@ -16930,7 +16930,7 @@ put_condition_code (enum rtx_code code, machine_mo { const char *suffix; - if (mode == CCFPmode || mode == CCFPUmode) + if (mode == CCFPmode) { code = ix86_fp_compare_code_to_integer (code); mode = CCmode; @@ -21709,14 +21709,13 @@ ix86_expand_int_compare (enum rtx_code code, rtx o return gen_rtx_fmt_ee (code, VOIDmode, flags, const0_rtx); } -/* Figure out whether to use ordered or unordered fp comparisons. - Return the appropriate mode to use. */ +/* Figure out whether to use unordered fp comparisons. */ -machine_mode -ix86_fp_compare_mode (enum rtx_code code) +static bool +ix86_unordered_fp_compare (enum rtx_code code) { if (!TARGET_IEEE_FP) - return CCFPmode; + return false; switch (code) { @@ -21724,7 +21723,7 @@ ix86_expand_int_compare (enum rtx_code code, rtx o case GE: case LT: case LE: - return CCFPmode; + return false; case EQ: case NE: @@ -21737,7 +21736,7 @@ ix86_expand_int_compare (enum rtx_code code, rtx o case UNGT: case UNGE: case UNEQ: - return CCFPUmode; + return true; default: gcc_unreachable (); @@ -21752,7 +21751,7 @@ ix86_cc_mode (enum rtx_code code, rtx op0, rtx op1 if (SCALAR_FLOAT_MODE_P (mode)) { gcc_assert (!DECIMAL_FLOAT_MODE_P (mode)); - return ix86_fp_compare_mode (code); + return CCFPmode; } switch (code) @@ -21874,7 +21873,6 @@ ix86_cc_modes_compatible (machine_mode m1, machine } case E_CCFPmode: - case E_CCFPUmode: /* These are only compatible with themselves, which we already checked above. */ return VOIDmode; @@ -21978,7 +21976,7 @@ ix86_fp_comparison_strategy (enum rtx_code) static enum rtx_code ix86_prepare_fp_compare_args (enum rtx_code code, rtx *pop0, rtx *pop1) { - machine_mode fpcmp_mode = ix86_fp_compare_mode (code); + bool unordered_compare = ix86_unordered_fp_compare (code); rtx op0 = *pop0, op1 = *pop1; machine_mode op_mode = GET_MODE (op0); bool is_sse = TARGET_SSE_MATH && SSE_FLOAT_MODE_P (op_mode); @@ -21990,7 +21988,7 @@ ix86_prepare_fp_compare_args (enum rtx_code code, floating point. */ if (!is_sse - && (fpcmp_mode == CCFPUmode + && (unordered_compare || (op_mode == XFmode && ! (standard_80387_constant_p (op0) == 1 || standard_80387_constant_p (op1) == 1) @@ -22087,10 +22085,10 @@ ix86_fp_compare_code_to_integer (enum rtx_code cod static rtx ix86_expand_fp_compare (enum rtx_code code, rtx op0, rtx op1, rtx scratch) { - machine_mode fpcmp_mode, intcmp_mode; + bool unordered_compare = ix86_unordered_fp_compare (code); + machine_mode intcmp_mode; rtx tmp, tmp2; - fpcmp_mode = ix86_fp_compare_mode (code); code = ix86_prepare_fp_compare_args (code, &op0, &op1); /* Do fcomi/sahf based test when profitable. */ @@ -22097,17 +22095,19 @@ ix86_expand_fp_compare (enum rtx_code code, rtx op switch (ix86_fp_comparison_strategy (code)) { case IX86_FPCMP_COMI: - intcmp_mode = fpcmp_mode; - tmp = gen_rtx_COMPARE (fpcmp_mode, op0, op1); - tmp = gen_rtx_SET (gen_rtx_REG (fpcmp_mode, FLAGS_REG), tmp); - emit_insn (tmp); + intcmp_mode = CCFPmode; + tmp = gen_rtx_COMPARE (CCFPmode, op0, op1); + if (unordered_compare) + tmp = gen_rtx_UNSPEC (CCFPmode, gen_rtvec (1, tmp), UNSPEC_NOTRAP); + emit_insn (gen_rtx_SET (gen_rtx_REG (CCFPmode, FLAGS_REG), tmp)); break; case IX86_FPCMP_SAHF: - intcmp_mode = fpcmp_mode; - tmp = gen_rtx_COMPARE (fpcmp_mode, op0, op1); - tmp = gen_rtx_SET (gen_rtx_REG (fpcmp_mode, FLAGS_REG), tmp); - + intcmp_mode = CCFPmode; + tmp = gen_rtx_COMPARE (CCFPmode, op0, op1); + if (unordered_compare) + tmp = gen_rtx_UNSPEC (CCFPmode, gen_rtvec (1, tmp), UNSPEC_NOTRAP); + tmp = gen_rtx_SET (gen_rtx_REG (CCFPmode, FLAGS_REG), tmp); if (!scratch) scratch = gen_reg_rtx (HImode); tmp2 = gen_rtx_CLOBBER (VOIDmode, scratch); @@ -22116,11 +22116,13 @@ ix86_expand_fp_compare (enum rtx_code code, rtx op case IX86_FPCMP_ARITH: /* Sadness wrt reg-stack pops killing fpsr -- gotta get fnstsw first. */ - tmp = gen_rtx_COMPARE (fpcmp_mode, op0, op1); - tmp2 = gen_rtx_UNSPEC (HImode, gen_rtvec (1, tmp), UNSPEC_FNSTSW); + tmp = gen_rtx_COMPARE (CCFPmode, op0, op1); + if (unordered_compare) + tmp = gen_rtx_UNSPEC (CCFPmode, gen_rtvec (1, tmp), UNSPEC_NOTRAP); + tmp = gen_rtx_UNSPEC (HImode, gen_rtvec (1, tmp), UNSPEC_FNSTSW); if (!scratch) scratch = gen_reg_rtx (HImode); - emit_insn (gen_rtx_SET (scratch, tmp2)); + emit_insn (gen_rtx_SET (scratch, tmp)); /* In the unordered case, we have to check C2 for NaN's, which doesn't happen to work out to anything nice combination-wise. @@ -22562,8 +22564,7 @@ ix86_expand_carry_flag_compare (enum rtx_code code compare_seq = get_insns (); end_sequence (); - if (GET_MODE (XEXP (compare_op, 0)) == CCFPmode - || GET_MODE (XEXP (compare_op, 0)) == CCFPUmode) + if (GET_MODE (XEXP (compare_op, 0)) == CCFPmode) code = ix86_fp_compare_code_to_integer (GET_CODE (compare_op)); else code = GET_CODE (compare_op); @@ -22703,8 +22704,7 @@ ix86_expand_int_movcc (rtx operands[]) flags = XEXP (compare_op, 0); - if (GET_MODE (flags) == CCFPmode - || GET_MODE (flags) == CCFPUmode) + if (GET_MODE (flags) == CCFPmode) { fpcmp = true; compare_code @@ -24744,8 +24744,7 @@ ix86_expand_int_addcc (rtx operands[]) flags = XEXP (compare_op, 0); - if (GET_MODE (flags) == CCFPmode - || GET_MODE (flags) == CCFPUmode) + if (GET_MODE (flags) == CCFPmode) { fpcmp = true; code = ix86_fp_compare_code_to_integer (code); @@ -43208,7 +43207,7 @@ ix86_encode_section_info (tree decl, rtx rtl, int enum rtx_code ix86_reverse_condition (enum rtx_code code, machine_mode mode) { - return (mode != CCFPmode && mode != CCFPUmode + return (mode != CCFPmode ? reverse_condition (code) : reverse_condition_maybe_unordered (code)); } @@ -43823,17 +43822,20 @@ static rtx_code_label * ix86_expand_sse_compare_and_jump (enum rtx_code code, rtx op0, rtx op1, bool swap_operands) { - machine_mode fpcmp_mode = ix86_fp_compare_mode (code); + bool unordered_compare = ix86_unordered_fp_compare (code); rtx_code_label *label; - rtx tmp; + rtx tmp, reg; if (swap_operands) std::swap (op0, op1); label = gen_label_rtx (); - tmp = gen_rtx_REG (fpcmp_mode, FLAGS_REG); - emit_insn (gen_rtx_SET (tmp, gen_rtx_COMPARE (fpcmp_mode, op0, op1))); - tmp = gen_rtx_fmt_ee (code, VOIDmode, tmp, const0_rtx); + tmp = gen_rtx_COMPARE (CCFPmode, op0, op1); + if (unordered_compare) + tmp = gen_rtx_UNSPEC (CCFPmode, gen_rtvec (1, tmp), UNSPEC_NOTRAP); + reg = gen_rtx_REG (CCFPmode, FLAGS_REG); + emit_insn (gen_rtx_SET (reg, tmp)); + tmp = gen_rtx_fmt_ee (code, VOIDmode, reg, const0_rtx); tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp, gen_rtx_LABEL_REF (VOIDmode, label), pc_rtx); tmp = emit_jump_insn (gen_rtx_SET (pc_rtx, tmp)); Index: config/i386/i386.md =================================================================== --- config/i386/i386.md (revision 254111) +++ config/i386/i386.md (working copy) @@ -99,6 +99,7 @@ UNSPEC_SCAS UNSPEC_FNSTSW UNSPEC_SAHF + UNSPEC_NOTRAP UNSPEC_PARITY UNSPEC_FSTCW UNSPEC_FLDCW @@ -1478,9 +1479,6 @@ ;; FP compares, step 1: ;; Set the FP condition codes. -;; -;; CCFPmode compare with exceptions -;; CCFPUmode compare with no exceptions ;; We may not use "#" to split and emit these, since the REG_DEAD notes ;; used to manage the reg stack popping would not be preserved. @@ -1587,9 +1585,11 @@ (define_insn "*cmpu<mode>_i387" [(set (match_operand:HI 0 "register_operand" "=a") (unspec:HI - [(compare:CCFPU - (match_operand:X87MODEF 1 "register_operand" "f") - (match_operand:X87MODEF 2 "register_operand" "f"))] + [(unspec:CCFP + [(compare:CCFP + (match_operand:X87MODEF 1 "register_operand" "f") + (match_operand:X87MODEF 2 "register_operand" "f"))] + UNSPEC_NOTRAP)] UNSPEC_FNSTSW))] "TARGET_80387" "* return output_fp_compare (insn, operands, false, true);" @@ -1598,10 +1598,12 @@ (set_attr "mode" "<MODE>")]) (define_insn_and_split "*cmpu<mode>_cc_i387" - [(set (reg:CCFPU FLAGS_REG) - (compare:CCFPU - (match_operand:X87MODEF 1 "register_operand" "f") - (match_operand:X87MODEF 2 "register_operand" "f"))) + [(set (reg:CCFP FLAGS_REG) + (unspec:CCFP + [(compare:CCFP + (match_operand:X87MODEF 1 "register_operand" "f") + (match_operand:X87MODEF 2 "register_operand" "f"))] + UNSPEC_NOTRAP)) (clobber (match_operand:HI 0 "register_operand" "=a"))] "TARGET_80387 && TARGET_SAHF && !TARGET_CMOVE" "#" @@ -1608,8 +1610,10 @@ "&& reload_completed" [(set (match_dup 0) (unspec:HI - [(compare:CCFPU (match_dup 1)(match_dup 2))] - UNSPEC_FNSTSW)) + [(unspec:CCFP + [(compare:CCFP (match_dup 1)(match_dup 2))] + UNSPEC_NOTRAP)] + UNSPEC_FNSTSW)) (set (reg:CC FLAGS_REG) (unspec:CC [(match_dup 0)] UNSPEC_SAHF))] "" @@ -1697,20 +1701,28 @@ ;; Pentium Pro can do steps 1 through 3 in one go. ;; (these instructions set flags directly) -(define_mode_iterator FPCMP [CCFP CCFPU]) -(define_mode_attr unord [(CCFP "") (CCFPU "u")]) +(define_subst_attr "unord" "unord_subst" "" "u") +(define_subst_attr "unordered" "unord_subst" "false" "true") -(define_insn "*cmpi<FPCMP:unord><MODEF:mode>" - [(set (reg:FPCMP FLAGS_REG) - (compare:FPCMP +(define_subst "unord_subst" + [(set (match_operand:CCFP 0) + (match_operand:CCFP 1))] + "" + [(set (match_dup 0) + (unspec:CCFP + [(match_dup 1)] + UNSPEC_NOTRAP))]) + +(define_insn "*cmpi<unord><MODEF:mode>" + [(set (reg:CCFP FLAGS_REG) + (compare:CCFP (match_operand:MODEF 0 "register_operand" "f,v") (match_operand:MODEF 1 "register_ssemem_operand" "f,vm")))] "(SSE_FLOAT_MODE_P (<MODEF:MODE>mode) && TARGET_SSE_MATH) || (TARGET_80387 && TARGET_CMOVE)" "@ - * return output_fp_compare (insn, operands, true, \ - <FPCMP:MODE>mode == CCFPUmode); - %v<FPCMP:unord>comi<MODEF:ssemodesuffix>\t{%1, %0|%0, %1}" + * return output_fp_compare (insn, operands, true, <unordered>); + %v<unord>comi<MODEF:ssemodesuffix>\t{%1, %0|%0, %1}" [(set_attr "type" "fcmp,ssecomi") (set_attr "prefix" "orig,maybe_vex") (set_attr "mode" "<MODEF:MODE>") @@ -1739,13 +1751,12 @@ (symbol_ref "false"))))]) (define_insn "*cmpi<unord>xf_i387" - [(set (reg:FPCMP FLAGS_REG) - (compare:FPCMP + [(set (reg:CCFP FLAGS_REG) + (compare:CCFP (match_operand:XF 0 "register_operand" "f") (match_operand:XF 1 "register_operand" "f")))] "TARGET_80387 && TARGET_CMOVE" - "* return output_fp_compare (insn, operands, true, - <MODE>mode == CCFPUmode);" + "* return output_fp_compare (insn, operands, true, <unordered>);" [(set_attr "type" "fcmp") (set_attr "mode" "XF") (set_attr "athlon_decode" "vector") Index: config/i386/predicates.md =================================================================== --- config/i386/predicates.md (revision 254111) +++ config/i386/predicates.md (working copy) @@ -1301,7 +1301,7 @@ machine_mode inmode = GET_MODE (XEXP (op, 0)); enum rtx_code code = GET_CODE (op); - if (inmode == CCFPmode || inmode == CCFPUmode) + if (inmode == CCFPmode) { if (!ix86_trivial_fp_comparison_operator (op, mode)) return false; @@ -1311,7 +1311,7 @@ switch (code) { case LTU: case GTU: case LEU: case GEU: - if (inmode == CCmode || inmode == CCFPmode || inmode == CCFPUmode + if (inmode == CCmode || inmode == CCFPmode || inmode == CCCmode) return true; return false; @@ -1348,7 +1348,7 @@ machine_mode inmode = GET_MODE (XEXP (op, 0)); enum rtx_code code = GET_CODE (op); - if (inmode == CCFPmode || inmode == CCFPUmode) + if (inmode == CCFPmode) return ix86_trivial_fp_comparison_operator (op, mode); switch (code) @@ -1391,7 +1391,7 @@ machine_mode inmode = GET_MODE (XEXP (op, 0)); enum rtx_code code = GET_CODE (op); - if (inmode == CCFPmode || inmode == CCFPUmode) + if (inmode == CCFPmode) { if (!ix86_trivial_fp_comparison_operator (op, mode)) return false; Index: config/i386/sse.md =================================================================== --- config/i386/sse.md (revision 254111) +++ config/i386/sse.md (working copy) @@ -2755,7 +2755,7 @@ (set_attr "prefix" "evex") (set_attr "mode" "<sseinsnmode>")]) -(define_insn "<sse>_comi<round_saeonly_name>" +(define_insn "<sse>_<unord>comi<round_saeonly_name>" [(set (reg:CCFP FLAGS_REG) (compare:CCFP (vec_select:MODEF @@ -2765,7 +2765,7 @@ (match_operand:<ssevecmode> 1 "<round_saeonly_nimm_scalar_predicate>" "<round_saeonly_constraint>") (parallel [(const_int 0)]))))] "SSE_FLOAT_MODE_P (<MODE>mode)" - "%vcomi<ssemodesuffix>\t{<round_saeonly_op2>%1, %0|%0, %<iptr>1<round_saeonly_op2>}" + "%v<unord>comi<ssemodesuffix>\t{<round_saeonly_op2>%1, %0|%0, %<iptr>1<round_saeonly_op2>}" [(set_attr "type" "ssecomi") (set_attr "prefix" "maybe_vex") (set_attr "prefix_rep" "0") @@ -2775,26 +2775,6 @@ (const_string "0"))) (set_attr "mode" "<MODE>")]) -(define_insn "<sse>_ucomi<round_saeonly_name>" - [(set (reg:CCFPU FLAGS_REG) - (compare:CCFPU - (vec_select:MODEF - (match_operand:<ssevecmode> 0 "register_operand" "v") - (parallel [(const_int 0)])) - (vec_select:MODEF - (match_operand:<ssevecmode> 1 "<round_saeonly_nimm_scalar_predicate>" "<round_saeonly_constraint>") - (parallel [(const_int 0)]))))] - "SSE_FLOAT_MODE_P (<MODE>mode)" - "%vucomi<ssemodesuffix>\t{<round_saeonly_op2>%1, %0|%0, %<iptr>1<round_saeonly_op2>}" - [(set_attr "type" "ssecomi") - (set_attr "prefix" "maybe_vex") - (set_attr "prefix_rep" "0") - (set (attr "prefix_data16") - (if_then_else (eq_attr "mode" "DF") - (const_string "1") - (const_string "0"))) - (set_attr "mode" "<MODE>")]) - (define_expand "vec_cmp<mode><avx512fmaskmodelower>" [(set (match_operand:<avx512fmaskmode> 0 "register_operand") (match_operator:<avx512fmaskmode> 1 "" Index: config/i386/subst.md =================================================================== --- config/i386/subst.md (revision 254111) +++ config/i386/subst.md (working copy) @@ -37,8 +37,7 @@ V8DI V4DI V2DI V16SF V8SF V4SF V8DF V4DF V2DF - QI HI SI DI SF DF - CCFP CCFPU]) + QI HI SI DI SF DF]) (define_subst_attr "mask_name" "mask" "" "_mask") (define_subst_attr "mask_applied" "mask" "false" "true") @@ -183,6 +182,16 @@ UNSPEC_EMBEDDED_ROUNDING)) ]) +(define_subst "round_saeonly" + [(set (match_operand:CCFP 0) + (match_operand:CCFP 1))] + "TARGET_AVX512F" + [(set (match_dup 0) + (unspec:CCFP [(match_dup 1) + (match_operand:SI 2 "const48_operand")] + UNSPEC_EMBEDDED_ROUNDING)) +]) + (define_subst_attr "round_expand_name" "round_expand" "" "_round") (define_subst_attr "round_expand_nimm_predicate" "round_expand" "nonimmediate_operand" "register_operand") (define_subst_attr "round_expand_operand" "round_expand" "" ", operands[5]") Index: reg-stack.c =================================================================== --- reg-stack.c (revision 254111) +++ reg-stack.c (working copy) @@ -1560,12 +1560,6 @@ subst_stack_regs_pat (rtx_insn *insn, stack_ptr re switch (GET_CODE (pat_src)) { - case COMPARE: - /* `fcomi' insn can't pop two regs. */ - compare_for_stack_reg (insn, regstack, pat_src, - REGNO (*dest) != FLAGS_REG); - break; - case CALL: { int count; @@ -1966,15 +1960,6 @@ subst_stack_regs_pat (rtx_insn *insn, stack_ptr re replace_reg (src2, FIRST_STACK_REG + 1); break; - case UNSPEC_SAHF: - /* (unspec [(unspec [(compare)] UNSPEC_FNSTSW)] UNSPEC_SAHF) - The combination matches the PPRO fcomi instruction. */ - - pat_src = XVECEXP (pat_src, 0, 0); - gcc_assert (GET_CODE (pat_src) == UNSPEC); - gcc_assert (XINT (pat_src, 1) == UNSPEC_FNSTSW); - /* Fall through. */ - case UNSPEC_FNSTSW: /* Combined fcomp+fnstsw generated for doing well with CSE. When optimizing this would have been broken @@ -1981,16 +1966,29 @@ subst_stack_regs_pat (rtx_insn *insn, stack_ptr re up before now. */ pat_src = XVECEXP (pat_src, 0, 0); + if (GET_CODE (pat_src) == COMPARE) + goto do_compare; + + /* Fall through. */ + + case UNSPEC_NOTRAP: + + pat_src = XVECEXP (pat_src, 0, 0); gcc_assert (GET_CODE (pat_src) == COMPARE); + goto do_compare; - compare_for_stack_reg (insn, regstack, pat_src, true); - break; - default: gcc_unreachable (); } break; + case COMPARE: + do_compare: + /* `fcomi' insn can't pop two regs. */ + compare_for_stack_reg (insn, regstack, pat_src, + REGNO (*dest) != FLAGS_REG); + break; + case IF_THEN_ELSE: /* This insn requires the top of stack to be the destination. */ Index: testsuite/gcc.target/i386/pr82692.c =================================================================== --- testsuite/gcc.target/i386/pr82692.c (nonexistent) +++ testsuite/gcc.target/i386/pr82692.c (working copy) @@ -0,0 +1,25 @@ +/* { dg-do run } */ +/* { dg-options "-O2" } */ +/* { dg-require-effective-target fenv_exceptions } */ + +#include <fenv.h> + +extern void abort (void); +extern void exit (int); + +double __attribute__ ((noinline, noclone)) +foo (double x) +{ + if (__builtin_islessequal (x, 0.0) || __builtin_isgreater (x, 1.0)) + return x + x; + return x * x; +} + +int +main (void) +{ + volatile double x = foo (__builtin_nan ("")); + if (fetestexcept (FE_INVALID)) + abort (); + exit (0); +}