From 463d9108766dcbb6a1051985e6c840a46897fe10 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Mon, 17 Jan 2022 13:39:05 +0100 Subject: [PATCH] widening_mul, i386: Improve spaceship expansion on x86 [PR103973] C++20: #include auto cmp4way(double a, double b) { return a <=> b; } expands to: ucomisd %xmm1, %xmm0 jp .L8 movl $0, %eax jne .L8 .L2: ret .p2align 4,,10 .p2align 3 .L8: comisd %xmm0, %xmm1 movl $-1, %eax ja .L2 ucomisd %xmm1, %xmm0 setbe %al addl $1, %eax ret That is 3 comparisons of the same operands. The following patch improves it to just one comparison: comisd %xmm1, %xmm0 jp .L4 seta %al movl $0, %edx leal -1(%rax,%rax), %eax cmove %edx, %eax ret .L4: movl $2, %eax ret While a <=> b expands to a == b ? 0 : a < b ? -1 : a > b ? 1 : 2 where the first comparison is equality and this shouldn't raise exceptions on qNaN operands, if the operands aren't equal (which includes unordered cases), then it immediately performs < or > comparison and that raises exceptions even on qNaNs, so we can just perform a single comparison that raises exceptions on qNaN. As the 4 different cases are encoded as ZF CF PF 1 1 1 a unordered b 0 0 0 a > b 0 1 0 a < b 1 0 0 a == b we can emit optimal sequence of comparions, first jp for the unordered case, then je for the == case and finally jb for the < case. The patch pattern recognizes spaceship-like comparisons during widening_mul if the spaceship optab is implemented, and replaces those comparisons with comparisons of .SPACESHIP ifn which returns -1/0/1/2 based on the comparison. This seems to work well both for the case of just returning the -1/0/1/2 (when we have just a common successor with a PHI) or when the different cases are handled with various other basic blocks. The testcases cover both of those cases, the latter with different function calls in those. 2022-01-17 Jakub Jelinek PR target/103973 * tree-cfg.h (cond_only_block_p): Declare. * tree-ssa-phiopt.c (cond_only_block_p): Move function to ... * tree-cfg.c (cond_only_block_p): ... here. No longer static. * optabs.def (spaceship_optab): New optab. * internal-fn.def (SPACESHIP): New internal function. * internal-fn.h (expand_SPACESHIP): Declare. * internal-fn.c (expand_PHI): Formatting fix. (expand_SPACESHIP): New function. * tree-ssa-math-opts.c (optimize_spaceship): New function. (math_opts_dom_walker::after_dom_children): Use it. * config/i386/i386.md (spaceship3): New define_expand. * config/i386/i386-protos.h (ix86_expand_fp_spaceship): Declare. * config/i386/i386-expand.c (ix86_expand_fp_spaceship): New function. * doc/md.texi (spaceship@var{m}3): Document. * gcc.target/i386/pr103973-1.c: New test. * gcc.target/i386/pr103973-2.c: New test. * gcc.target/i386/pr103973-3.c: New test. * gcc.target/i386/pr103973-4.c: New test. * gcc.target/i386/pr103973-5.c: New test. * gcc.target/i386/pr103973-6.c: New test. * gcc.target/i386/pr103973-7.c: New test. * gcc.target/i386/pr103973-8.c: New test. * gcc.target/i386/pr103973-9.c: New test. * gcc.target/i386/pr103973-10.c: New test. * gcc.target/i386/pr103973-11.c: New test. * gcc.target/i386/pr103973-12.c: New test. * gcc.target/i386/pr103973-13.c: New test. * gcc.target/i386/pr103973-14.c: New test. * gcc.target/i386/pr103973-15.c: New test. * gcc.target/i386/pr103973-16.c: New test. * gcc.target/i386/pr103973-17.c: New test. * gcc.target/i386/pr103973-18.c: New test. * gcc.target/i386/pr103973-19.c: New test. * gcc.target/i386/pr103973-20.c: New test. * g++.target/i386/pr103973-1.C: New test. * g++.target/i386/pr103973-2.C: New test. * g++.target/i386/pr103973-3.C: New test. * g++.target/i386/pr103973-4.C: New test. * g++.target/i386/pr103973-5.C: New test. * g++.target/i386/pr103973-6.C: New test. * g++.target/i386/pr103973-7.C: New test. * g++.target/i386/pr103973-8.C: New test. * g++.target/i386/pr103973-9.C: New test. * g++.target/i386/pr103973-10.C: New test. * g++.target/i386/pr103973-11.C: New test. * g++.target/i386/pr103973-12.C: New test. * g++.target/i386/pr103973-13.C: New test. * g++.target/i386/pr103973-14.C: New test. * g++.target/i386/pr103973-15.C: New test. * g++.target/i386/pr103973-16.C: New test. * g++.target/i386/pr103973-17.C: New test. * g++.target/i386/pr103973-18.C: New test. * g++.target/i386/pr103973-19.C: New test. * g++.target/i386/pr103973-20.C: New test. --- gcc/config/i386/i386-expand.c | 49 +++++ gcc/config/i386/i386-protos.h | 1 + gcc/config/i386/i386.md | 22 ++ gcc/doc/md.texi | 9 + gcc/internal-fn.c | 24 ++- gcc/internal-fn.def | 3 + gcc/internal-fn.h | 1 + gcc/optabs.def | 1 + gcc/testsuite/g++.target/i386/pr103973-1.C | 71 +++++++ gcc/testsuite/g++.target/i386/pr103973-10.C | 7 + gcc/testsuite/g++.target/i386/pr103973-11.C | 8 + gcc/testsuite/g++.target/i386/pr103973-12.C | 8 + gcc/testsuite/g++.target/i386/pr103973-13.C | 62 ++++++ gcc/testsuite/g++.target/i386/pr103973-14.C | 7 + gcc/testsuite/g++.target/i386/pr103973-15.C | 8 + gcc/testsuite/g++.target/i386/pr103973-16.C | 8 + gcc/testsuite/g++.target/i386/pr103973-17.C | 8 + gcc/testsuite/g++.target/i386/pr103973-18.C | 8 + gcc/testsuite/g++.target/i386/pr103973-19.C | 8 + gcc/testsuite/g++.target/i386/pr103973-2.C | 7 + gcc/testsuite/g++.target/i386/pr103973-20.C | 8 + gcc/testsuite/g++.target/i386/pr103973-3.C | 8 + gcc/testsuite/g++.target/i386/pr103973-4.C | 8 + gcc/testsuite/g++.target/i386/pr103973-5.C | 66 ++++++ gcc/testsuite/g++.target/i386/pr103973-6.C | 7 + gcc/testsuite/g++.target/i386/pr103973-7.C | 8 + gcc/testsuite/g++.target/i386/pr103973-8.C | 8 + gcc/testsuite/g++.target/i386/pr103973-9.C | 67 ++++++ gcc/testsuite/gcc.target/i386/pr103973-1.c | 98 +++++++++ gcc/testsuite/gcc.target/i386/pr103973-10.c | 7 + gcc/testsuite/gcc.target/i386/pr103973-11.c | 8 + gcc/testsuite/gcc.target/i386/pr103973-12.c | 8 + gcc/testsuite/gcc.target/i386/pr103973-13.c | 76 +++++++ gcc/testsuite/gcc.target/i386/pr103973-14.c | 7 + gcc/testsuite/gcc.target/i386/pr103973-15.c | 8 + gcc/testsuite/gcc.target/i386/pr103973-16.c | 8 + gcc/testsuite/gcc.target/i386/pr103973-17.c | 8 + gcc/testsuite/gcc.target/i386/pr103973-18.c | 8 + gcc/testsuite/gcc.target/i386/pr103973-19.c | 8 + gcc/testsuite/gcc.target/i386/pr103973-2.c | 7 + gcc/testsuite/gcc.target/i386/pr103973-20.c | 8 + gcc/testsuite/gcc.target/i386/pr103973-3.c | 8 + gcc/testsuite/gcc.target/i386/pr103973-4.c | 8 + gcc/testsuite/gcc.target/i386/pr103973-5.c | 85 ++++++++ gcc/testsuite/gcc.target/i386/pr103973-6.c | 7 + gcc/testsuite/gcc.target/i386/pr103973-7.c | 8 + gcc/testsuite/gcc.target/i386/pr103973-8.c | 8 + gcc/testsuite/gcc.target/i386/pr103973-9.c | 89 ++++++++ gcc/tree-cfg.c | 25 +++ gcc/tree-cfg.h | 1 + gcc/tree-ssa-math-opts.c | 223 ++++++++++++++++++++ gcc/tree-ssa-phiopt.c | 25 --- 52 files changed, 1220 insertions(+), 26 deletions(-) create mode 100644 gcc/testsuite/g++.target/i386/pr103973-1.C create mode 100644 gcc/testsuite/g++.target/i386/pr103973-10.C create mode 100644 gcc/testsuite/g++.target/i386/pr103973-11.C create mode 100644 gcc/testsuite/g++.target/i386/pr103973-12.C create mode 100644 gcc/testsuite/g++.target/i386/pr103973-13.C create mode 100644 gcc/testsuite/g++.target/i386/pr103973-14.C create mode 100644 gcc/testsuite/g++.target/i386/pr103973-15.C create mode 100644 gcc/testsuite/g++.target/i386/pr103973-16.C create mode 100644 gcc/testsuite/g++.target/i386/pr103973-17.C create mode 100644 gcc/testsuite/g++.target/i386/pr103973-18.C create mode 100644 gcc/testsuite/g++.target/i386/pr103973-19.C create mode 100644 gcc/testsuite/g++.target/i386/pr103973-2.C create mode 100644 gcc/testsuite/g++.target/i386/pr103973-20.C create mode 100644 gcc/testsuite/g++.target/i386/pr103973-3.C create mode 100644 gcc/testsuite/g++.target/i386/pr103973-4.C create mode 100644 gcc/testsuite/g++.target/i386/pr103973-5.C create mode 100644 gcc/testsuite/g++.target/i386/pr103973-6.C create mode 100644 gcc/testsuite/g++.target/i386/pr103973-7.C create mode 100644 gcc/testsuite/g++.target/i386/pr103973-8.C create mode 100644 gcc/testsuite/g++.target/i386/pr103973-9.C create mode 100644 gcc/testsuite/gcc.target/i386/pr103973-1.c create mode 100644 gcc/testsuite/gcc.target/i386/pr103973-10.c create mode 100644 gcc/testsuite/gcc.target/i386/pr103973-11.c create mode 100644 gcc/testsuite/gcc.target/i386/pr103973-12.c create mode 100644 gcc/testsuite/gcc.target/i386/pr103973-13.c create mode 100644 gcc/testsuite/gcc.target/i386/pr103973-14.c create mode 100644 gcc/testsuite/gcc.target/i386/pr103973-15.c create mode 100644 gcc/testsuite/gcc.target/i386/pr103973-16.c create mode 100644 gcc/testsuite/gcc.target/i386/pr103973-17.c create mode 100644 gcc/testsuite/gcc.target/i386/pr103973-18.c create mode 100644 gcc/testsuite/gcc.target/i386/pr103973-19.c create mode 100644 gcc/testsuite/gcc.target/i386/pr103973-2.c create mode 100644 gcc/testsuite/gcc.target/i386/pr103973-20.c create mode 100644 gcc/testsuite/gcc.target/i386/pr103973-3.c create mode 100644 gcc/testsuite/gcc.target/i386/pr103973-4.c create mode 100644 gcc/testsuite/gcc.target/i386/pr103973-5.c create mode 100644 gcc/testsuite/gcc.target/i386/pr103973-6.c create mode 100644 gcc/testsuite/gcc.target/i386/pr103973-7.c create mode 100644 gcc/testsuite/gcc.target/i386/pr103973-8.c create mode 100644 gcc/testsuite/gcc.target/i386/pr103973-9.c diff --git a/gcc/config/i386/i386-expand.c b/gcc/config/i386/i386-expand.c index 138580da96e..5fae4228df2 100644 --- a/gcc/config/i386/i386-expand.c +++ b/gcc/config/i386/i386-expand.c @@ -2879,6 +2879,55 @@ ix86_expand_setcc (rtx dest, enum rtx_code code, rtx op0, rtx op1) emit_insn (gen_rtx_SET (dest, ret)); } +/* Expand floating point op0 <=> op1, i.e. + dest = op0 == op1 ? 0 : op0 < op1 ? -1 : op0 > op1 ? 1 : 2. */ + +void +ix86_expand_fp_spaceship (rtx dest, rtx op0, rtx op1) +{ + gcc_checking_assert (ix86_fp_comparison_strategy (GT) != IX86_FPCMP_ARITH); + rtx gt = ix86_expand_fp_compare (GT, op0, op1); + rtx l0 = gen_label_rtx (); + rtx l1 = gen_label_rtx (); + rtx l2 = TARGET_IEEE_FP ? gen_label_rtx () : NULL_RTX; + rtx lend = gen_label_rtx (); + rtx tmp; + rtx_insn *jmp; + if (l2) + { + rtx un = gen_rtx_fmt_ee (UNORDERED, VOIDmode, + gen_rtx_REG (CCFPmode, FLAGS_REG), const0_rtx); + tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, un, + gen_rtx_LABEL_REF (VOIDmode, l2), pc_rtx); + jmp = emit_jump_insn (gen_rtx_SET (pc_rtx, tmp)); + add_reg_br_prob_note (jmp, profile_probability:: very_unlikely ()); + } + rtx eq = gen_rtx_fmt_ee (UNEQ, VOIDmode, + gen_rtx_REG (CCFPmode, FLAGS_REG), const0_rtx); + tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, eq, + gen_rtx_LABEL_REF (VOIDmode, l0), pc_rtx); + jmp = emit_jump_insn (gen_rtx_SET (pc_rtx, tmp)); + add_reg_br_prob_note (jmp, profile_probability::unlikely ()); + tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, gt, + gen_rtx_LABEL_REF (VOIDmode, l1), pc_rtx); + jmp = emit_jump_insn (gen_rtx_SET (pc_rtx, tmp)); + add_reg_br_prob_note (jmp, profile_probability::even ()); + emit_move_insn (dest, constm1_rtx); + emit_jump (lend); + emit_label (l0); + emit_move_insn (dest, const0_rtx); + emit_jump (lend); + emit_label (l1); + emit_move_insn (dest, const1_rtx); + emit_jump (lend); + if (l2) + { + emit_label (l2); + emit_move_insn (dest, const2_rtx); + } + emit_label (lend); +} + /* Expand comparison setting or clearing carry flag. Return true when successful and set pop for the operation. */ static bool diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h index 6d14a406bdb..a697dd24142 100644 --- a/gcc/config/i386/i386-protos.h +++ b/gcc/config/i386/i386-protos.h @@ -150,6 +150,7 @@ extern bool ix86_expand_int_vec_cmp (rtx[]); extern bool ix86_expand_fp_vec_cmp (rtx[]); extern void ix86_expand_sse_movcc (rtx, rtx, rtx, rtx); extern void ix86_expand_sse_unpack (rtx, rtx, bool, bool); +extern void ix86_expand_fp_spaceship (rtx, rtx, rtx); extern bool ix86_expand_int_addcc (rtx[]); extern rtx_insn *ix86_expand_call (rtx, rtx, rtx, rtx, rtx, bool); extern bool ix86_call_use_plt_p (rtx); diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index 7b1694314ea..a2f095eb5c7 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -23886,6 +23886,28 @@ [(set_attr "type" "other") (set_attr "length" "4")]) +;; Spaceship optimization +(define_expand "spaceship3" + [(match_operand:SI 0 "register_operand") + (match_operand:MODEF 1 "cmp_fp_expander_operand") + (match_operand:MODEF 2 "cmp_fp_expander_operand")] + "(TARGET_80387 || (SSE_FLOAT_MODE_P (mode) && TARGET_SSE_MATH)) + && (TARGET_CMOVE || (TARGET_SAHF && TARGET_USE_SAHF))" +{ + ix86_expand_fp_spaceship (operands[0], operands[1], operands[2]); + DONE; +}) + +(define_expand "spaceshipxf3" + [(match_operand:SI 0 "register_operand") + (match_operand:XF 1 "nonmemory_operand") + (match_operand:XF 2 "nonmemory_operand")] + "TARGET_80387 && (TARGET_CMOVE || (TARGET_SAHF && TARGET_USE_SAHF))" +{ + ix86_expand_fp_spaceship (operands[0], operands[1], operands[2]); + DONE; +}) + (include "mmx.md") (include "sse.md") (include "sync.md") diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi index 261933a4212..4dea8bb2304 100644 --- a/gcc/doc/md.texi +++ b/gcc/doc/md.texi @@ -8055,6 +8055,15 @@ inclusive and operand 1 exclusive. If this pattern is not defined, a call to the library function @code{__clear_cache} is used. +@cindex @code{spaceship@var{m}3} instruction pattern +@item @samp{spaceship@var{m}3} +Initialize output operand 0 with mode of integer type to -1, 0, 1 or 2 +if operand 1 with mode @var{m} compares less than operand 2, equal to +operand 2, greater than operand 2 or is unordered with operand 2. +@var{m} should be a scalar floating point mode. + +This pattern is not allowed to @code{FAIL}. + @end table @end ifset diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c index b922e6a0090..e7f6302aad4 100644 --- a/gcc/internal-fn.c +++ b/gcc/internal-fn.c @@ -4425,5 +4425,27 @@ expand_SHUFFLEVECTOR (internal_fn, gcall *) void expand_PHI (internal_fn, gcall *) { - gcc_unreachable (); + gcc_unreachable (); +} + +void +expand_SPACESHIP (internal_fn, gcall *stmt) +{ + tree lhs = gimple_call_lhs (stmt); + tree rhs1 = gimple_call_arg (stmt, 0); + tree rhs2 = gimple_call_arg (stmt, 1); + tree type = TREE_TYPE (rhs1); + + rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE); + rtx op1 = expand_normal (rhs1); + rtx op2 = expand_normal (rhs2); + + class expand_operand ops[3]; + create_output_operand (&ops[0], target, TYPE_MODE (TREE_TYPE (lhs))); + create_input_operand (&ops[1], op1, TYPE_MODE (type)); + create_input_operand (&ops[2], op2, TYPE_MODE (type)); + insn_code icode = optab_handler (spaceship_optab, TYPE_MODE (type)); + expand_insn (icode, 3, ops); + if (!rtx_equal_p (target, ops[0].value)) + emit_move_insn (target, ops[0].value); } diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def index 8891071a6a3..d2d550d3586 100644 --- a/gcc/internal-fn.def +++ b/gcc/internal-fn.def @@ -430,6 +430,9 @@ DEF_INTERNAL_FN (NOP, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL) /* Temporary vehicle for __builtin_shufflevector. */ DEF_INTERNAL_FN (SHUFFLEVECTOR, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL) +/* <=> optimization. */ +DEF_INTERNAL_FN (SPACESHIP, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL) + #undef DEF_INTERNAL_INT_FN #undef DEF_INTERNAL_FLT_FN #undef DEF_INTERNAL_FLT_FLOATN_FN diff --git a/gcc/internal-fn.h b/gcc/internal-fn.h index 036d59d7138..23c014a963c 100644 --- a/gcc/internal-fn.h +++ b/gcc/internal-fn.h @@ -241,6 +241,7 @@ extern void expand_internal_call (gcall *); extern void expand_internal_call (internal_fn, gcall *); extern void expand_PHI (internal_fn, gcall *); extern void expand_SHUFFLEVECTOR (internal_fn, gcall *); +extern void expand_SPACESHIP (internal_fn, gcall *); extern bool vectorized_internal_fn_supported_p (internal_fn, tree); diff --git a/gcc/optabs.def b/gcc/optabs.def index 5fcf5386a0b..bb46083b904 100644 --- a/gcc/optabs.def +++ b/gcc/optabs.def @@ -259,6 +259,7 @@ OPTAB_D (usubv4_optab, "usubv$I$a4") OPTAB_D (umulv4_optab, "umulv$I$a4") OPTAB_D (negv3_optab, "negv$I$a3") OPTAB_D (addptr3_optab, "addptr$a3") +OPTAB_D (spaceship_optab, "spaceship$a3") OPTAB_D (smul_highpart_optab, "smul$a3_highpart") OPTAB_D (umul_highpart_optab, "umul$a3_highpart") diff --git a/gcc/testsuite/g++.target/i386/pr103973-1.C b/gcc/testsuite/g++.target/i386/pr103973-1.C new file mode 100644 index 00000000000..77f8b4ea325 --- /dev/null +++ b/gcc/testsuite/g++.target/i386/pr103973-1.C @@ -0,0 +1,71 @@ +// PR target/103973 +// { dg-do run } +// { dg-options "-O2 -std=c++20 -save-temps" } +// { dg-final { scan-assembler-not "'\tucomisd" { target { ! ia32 } } } } +// { dg-final { scan-assembler-times "\tcomisd" 2 { target { ! ia32 } } } } + +#include + +#ifndef double_type +#define double_type double +#endif + +__attribute__((noipa)) auto +foo (double_type a, double_type b) +{ + return a <=> b; +} + +__attribute__((noipa)) int +bar (double_type a, double_type b) +{ + auto c = foo (a, b); + if (c == std::partial_ordering::less) + return -1; + if (c == std::partial_ordering::equivalent) + return 0; + if (c == std::partial_ordering::greater) + return 1; + return 2; +} + +__attribute__((noipa)) auto +baz (double_type a) +{ + return a <=> 0.0f; +} + +__attribute__((noipa)) int +qux (double_type a) +{ + auto c = baz (a); + if (c == std::partial_ordering::less) + return -1; + if (c == std::partial_ordering::equivalent) + return 0; + if (c == std::partial_ordering::greater) + return 1; + return 2; +} + +int +main () +{ + double_type m5 = -5.0; + double_type p5 = 5.0; + volatile double_type p0 = 0.0; + double_type nan = p0 / p0; + if (bar (p5, p5) != 0 || bar (m5, m5) != 0) + __builtin_abort (); + if (bar (m5, p5) != -1 || bar (p5, m5) != 1) + __builtin_abort (); + if (bar (m5, nan) != 2 || bar (nan, p5) != 2) + __builtin_abort (); + if (bar (nan, nan) != 2) + __builtin_abort (); + if (qux (p0) != 0 || qux (nan) != 2) + __builtin_abort (); + if (qux (m5) != -1 || qux (p5) != 1) + __builtin_abort (); + return 0; +} diff --git a/gcc/testsuite/g++.target/i386/pr103973-10.C b/gcc/testsuite/g++.target/i386/pr103973-10.C new file mode 100644 index 00000000000..53352f7d0c5 --- /dev/null +++ b/gcc/testsuite/g++.target/i386/pr103973-10.C @@ -0,0 +1,7 @@ +// PR target/103973 +// { dg-do compile { target ia32 } } +// { dg-options "-O2 -march=i686 -mfpmath=387 -std=c++20" } +// { dg-final { scan-assembler-not "'\tfucom" } } +// { dg-final { scan-assembler-times "\tfcom" 2 } } + +#include "pr103973-9.C" diff --git a/gcc/testsuite/g++.target/i386/pr103973-11.C b/gcc/testsuite/g++.target/i386/pr103973-11.C new file mode 100644 index 00000000000..549c1cc0027 --- /dev/null +++ b/gcc/testsuite/g++.target/i386/pr103973-11.C @@ -0,0 +1,8 @@ +// PR target/103973 +// { dg-do run } +// { dg-options "-O2 -save-temps -std=c++20" } +// { dg-final { scan-assembler-not "'\tucomiss" { target { ! ia32 } } } } +// { dg-final { scan-assembler-times "\tcomiss" 2 { target { ! ia32 } } } } + +#define double_type float +#include "pr103973-9.C" diff --git a/gcc/testsuite/g++.target/i386/pr103973-12.C b/gcc/testsuite/g++.target/i386/pr103973-12.C new file mode 100644 index 00000000000..0fefd039f7e --- /dev/null +++ b/gcc/testsuite/g++.target/i386/pr103973-12.C @@ -0,0 +1,8 @@ +// PR target/103973 +// { dg-do compile { target ia32 } } +// { dg-options "-O2 -march=i686 -mfpmath=387 -std=c++20" } +// { dg-final { scan-assembler-not "'\tfucom" } } +// { dg-final { scan-assembler-times "\tfcom" 2 } } + +#define double_type float +#include "pr103973-9.C" diff --git a/gcc/testsuite/g++.target/i386/pr103973-13.C b/gcc/testsuite/g++.target/i386/pr103973-13.C new file mode 100644 index 00000000000..fdd13b99795 --- /dev/null +++ b/gcc/testsuite/g++.target/i386/pr103973-13.C @@ -0,0 +1,62 @@ +// PR target/103973 +// { dg-do run } +// { dg-options "-O2 -ffast-math -std=c++20 -save-temps" } +// { dg-final { scan-assembler-not "'\tucomisd" { target { ! ia32 } } } } +// { dg-final { scan-assembler-times "\tcomisd" 2 { target { ! ia32 } } } } + +#include + +#ifndef double_type +#define double_type double +#endif + +__attribute__((noipa)) auto +foo (double_type a, double_type b) +{ + return a <=> b; +} + +__attribute__((noipa)) int +bar (double_type a, double_type b) +{ + auto c = foo (a, b); + if (c == std::partial_ordering::less) + return -1; + if (c == std::partial_ordering::equivalent) + return 0; + return 1; +} + +__attribute__((noipa)) auto +baz (double_type a) +{ + return a <=> 0.0f; +} + +__attribute__((noipa)) int +qux (double_type a) +{ + auto c = baz (a); + if (c == std::partial_ordering::greater) + return 1; + if (c == std::partial_ordering::equivalent) + return 0; + return -1; +} + +int +main () +{ + double_type m5 = -5.0; + double_type p5 = 5.0; + double_type p0 = 0.0; + if (bar (p5, p5) != 0 || bar (m5, m5) != 0) + __builtin_abort (); + if (bar (m5, p5) != -1 || bar (p5, m5) != 1) + __builtin_abort (); + if (qux (p0) != 0) + __builtin_abort (); + if (qux (m5) != -1 || qux (p5) != 1) + __builtin_abort (); + return 0; +} diff --git a/gcc/testsuite/g++.target/i386/pr103973-14.C b/gcc/testsuite/g++.target/i386/pr103973-14.C new file mode 100644 index 00000000000..b4fc6fed79b --- /dev/null +++ b/gcc/testsuite/g++.target/i386/pr103973-14.C @@ -0,0 +1,7 @@ +// PR target/103973 +// { dg-do compile { target ia32 } } +// { dg-options "-O2 -ffast-math -march=i686 -mfpmath=387 -std=c++20" } +// { dg-final { scan-assembler-not "'\tfucom" } } +// { dg-final { scan-assembler-times "\tfcom" 2 } } + +#include "pr103973-13.C" diff --git a/gcc/testsuite/g++.target/i386/pr103973-15.C b/gcc/testsuite/g++.target/i386/pr103973-15.C new file mode 100644 index 00000000000..1b20b12f934 --- /dev/null +++ b/gcc/testsuite/g++.target/i386/pr103973-15.C @@ -0,0 +1,8 @@ +// PR target/103973 +// { dg-do run } +// { dg-options "-O2 -ffast-math -save-temps -std=c++20" } +// { dg-final { scan-assembler-not "'\tucomiss" { target { ! ia32 } } } } +// { dg-final { scan-assembler-times "\tcomiss" 2 { target { ! ia32 } } } } + +#define double_type float +#include "pr103973-13.C" diff --git a/gcc/testsuite/g++.target/i386/pr103973-16.C b/gcc/testsuite/g++.target/i386/pr103973-16.C new file mode 100644 index 00000000000..95b6967eecf --- /dev/null +++ b/gcc/testsuite/g++.target/i386/pr103973-16.C @@ -0,0 +1,8 @@ +// PR target/103973 +// { dg-do compile { target ia32 } } +// { dg-options "-O2 -ffast-math -march=i686 -mfpmath=387 -std=c++20" } +// { dg-final { scan-assembler-not "'\tfucom" } } +// { dg-final { scan-assembler-times "\tfcom" 2 } } + +#define double_type float +#include "pr103973-13.C" diff --git a/gcc/testsuite/g++.target/i386/pr103973-17.C b/gcc/testsuite/g++.target/i386/pr103973-17.C new file mode 100644 index 00000000000..f84b5523cce --- /dev/null +++ b/gcc/testsuite/g++.target/i386/pr103973-17.C @@ -0,0 +1,8 @@ +// PR target/103973 +// { dg-do run { target large_long_double } } +// { dg-options "-O2 -std=c++20 -save-temps" } +// { dg-final { scan-assembler-not "'\tfucom" } } +// { dg-final { scan-assembler-times "\tfcom" 2 } } + +#define double_type long double +#include "pr103973-1.C" diff --git a/gcc/testsuite/g++.target/i386/pr103973-18.C b/gcc/testsuite/g++.target/i386/pr103973-18.C new file mode 100644 index 00000000000..57ff1f2c859 --- /dev/null +++ b/gcc/testsuite/g++.target/i386/pr103973-18.C @@ -0,0 +1,8 @@ +// PR target/103973 +// { dg-do run { target large_long_double } } +// { dg-options "-O2 -ffast-math -std=c++20 -save-temps" } +// { dg-final { scan-assembler-not "'\tfucom" } } +// { dg-final { scan-assembler-times "\tfcom" 2 } } + +#define double_type long double +#include "pr103973-5.C" diff --git a/gcc/testsuite/g++.target/i386/pr103973-19.C b/gcc/testsuite/g++.target/i386/pr103973-19.C new file mode 100644 index 00000000000..b94bcfd7046 --- /dev/null +++ b/gcc/testsuite/g++.target/i386/pr103973-19.C @@ -0,0 +1,8 @@ +// PR target/103973 +// { dg-do run { target large_long_double } } +// { dg-options "-O2 -std=c++20 -save-temps" } +// { dg-final { scan-assembler-not "'\tfucom" } } +// { dg-final { scan-assembler-times "\tfcom" 2 } } + +#define double_type long double +#include "pr103973-9.C" diff --git a/gcc/testsuite/g++.target/i386/pr103973-2.C b/gcc/testsuite/g++.target/i386/pr103973-2.C new file mode 100644 index 00000000000..be8978e6ddf --- /dev/null +++ b/gcc/testsuite/g++.target/i386/pr103973-2.C @@ -0,0 +1,7 @@ +// PR target/103973 +// { dg-do compile { target ia32 } } +// { dg-options "-O2 -march=i686 -mfpmath=387 -std=c++20" } +// { dg-final { scan-assembler-not "'\tfucom" } } +// { dg-final { scan-assembler-times "\tfcom" 2 } } + +#include "pr103973-1.C" diff --git a/gcc/testsuite/g++.target/i386/pr103973-20.C b/gcc/testsuite/g++.target/i386/pr103973-20.C new file mode 100644 index 00000000000..8d5d91943eb --- /dev/null +++ b/gcc/testsuite/g++.target/i386/pr103973-20.C @@ -0,0 +1,8 @@ +// PR target/103973 +// { dg-do run { target large_long_double } } +// { dg-options "-O2 -ffast-math -std=c++20 -save-temps" } +// { dg-final { scan-assembler-not "'\tfucom" } } +// { dg-final { scan-assembler-times "\tfcom" 2 } } + +#define double_type long double +#include "pr103973-13.C" diff --git a/gcc/testsuite/g++.target/i386/pr103973-3.C b/gcc/testsuite/g++.target/i386/pr103973-3.C new file mode 100644 index 00000000000..3d4476cbc17 --- /dev/null +++ b/gcc/testsuite/g++.target/i386/pr103973-3.C @@ -0,0 +1,8 @@ +// PR target/103973 +// { dg-do run } +// { dg-options "-O2 -save-temps -std=c++20" } +// { dg-final { scan-assembler-not "'\tucomiss" { target { ! ia32 } } } } +// { dg-final { scan-assembler-times "\tcomiss" 2 { target { ! ia32 } } } } + +#define double_type float +#include "pr103973-1.C" diff --git a/gcc/testsuite/g++.target/i386/pr103973-4.C b/gcc/testsuite/g++.target/i386/pr103973-4.C new file mode 100644 index 00000000000..5fec7c9713a --- /dev/null +++ b/gcc/testsuite/g++.target/i386/pr103973-4.C @@ -0,0 +1,8 @@ +// PR target/103973 +// { dg-do compile { target ia32 } } +// { dg-options "-O2 -march=i686 -mfpmath=387 -std=c++20" } +// { dg-final { scan-assembler-not "'\tfucom" } } +// { dg-final { scan-assembler-times "\tfcom" 2 } } + +#define double_type float +#include "pr103973-1.C" diff --git a/gcc/testsuite/g++.target/i386/pr103973-5.C b/gcc/testsuite/g++.target/i386/pr103973-5.C new file mode 100644 index 00000000000..b198fe6ad2c --- /dev/null +++ b/gcc/testsuite/g++.target/i386/pr103973-5.C @@ -0,0 +1,66 @@ +// PR target/103973 +// { dg-do run } +// { dg-options "-O2 -ffast-math -std=c++20 -save-temps" } +// { dg-final { scan-assembler-not "'\tucomisd" { target { ! ia32 } } } } +// { dg-final { scan-assembler-times "\tcomisd" 2 { target { ! ia32 } } } } + +#include + +#ifndef double_type +#define double_type double +#endif + +__attribute__((noipa)) auto +foo (double_type a, double_type b) +{ + return a <=> b; +} + +__attribute__((noipa)) int +bar (double_type a, double_type b) +{ + auto c = foo (a, b); + if (c == std::partial_ordering::less) + return -1; + if (c == std::partial_ordering::equivalent) + return 0; + if (c == std::partial_ordering::greater) + return 1; + return 2; +} + +__attribute__((noipa)) auto +baz (double_type a) +{ + return a <=> 0.0f; +} + +__attribute__((noipa)) int +qux (double_type a) +{ + auto c = baz (a); + if (c == std::partial_ordering::less) + return -1; + if (c == std::partial_ordering::equivalent) + return 0; + if (c == std::partial_ordering::greater) + return 1; + return 2; +} + +int +main () +{ + double_type m5 = -5.0; + double_type p5 = 5.0; + double_type p0 = 0.0; + if (bar (p5, p5) != 0 || bar (m5, m5) != 0) + __builtin_abort (); + if (bar (m5, p5) != -1 || bar (p5, m5) != 1) + __builtin_abort (); + if (qux (p0) != 0) + __builtin_abort (); + if (qux (m5) != -1 || qux (p5) != 1) + __builtin_abort (); + return 0; +} diff --git a/gcc/testsuite/g++.target/i386/pr103973-6.C b/gcc/testsuite/g++.target/i386/pr103973-6.C new file mode 100644 index 00000000000..e50fe094305 --- /dev/null +++ b/gcc/testsuite/g++.target/i386/pr103973-6.C @@ -0,0 +1,7 @@ +// PR target/103973 +// { dg-do compile { target ia32 } } +// { dg-options "-O2 -ffast-math -march=i686 -mfpmath=387 -std=c++20" } +// { dg-final { scan-assembler-not "'\tfucom" } } +// { dg-final { scan-assembler-times "\tfcom" 2 } } + +#include "pr103973-5.C" diff --git a/gcc/testsuite/g++.target/i386/pr103973-7.C b/gcc/testsuite/g++.target/i386/pr103973-7.C new file mode 100644 index 00000000000..17579610475 --- /dev/null +++ b/gcc/testsuite/g++.target/i386/pr103973-7.C @@ -0,0 +1,8 @@ +// PR target/103973 +// { dg-do run } +// { dg-options "-O2 -ffast-math -save-temps -std=c++20" } +// { dg-final { scan-assembler-not "'\tucomiss" { target { ! ia32 } } } } +// { dg-final { scan-assembler-times "\tcomiss" 2 { target { ! ia32 } } } } + +#define double_type float +#include "pr103973-5.C" diff --git a/gcc/testsuite/g++.target/i386/pr103973-8.C b/gcc/testsuite/g++.target/i386/pr103973-8.C new file mode 100644 index 00000000000..1150d289a8a --- /dev/null +++ b/gcc/testsuite/g++.target/i386/pr103973-8.C @@ -0,0 +1,8 @@ +// PR target/103973 +// { dg-do compile { target ia32 } } +// { dg-options "-O2 -ffast-math -march=i686 -mfpmath=387 -std=c++20" } +// { dg-final { scan-assembler-not "'\tfucom" } } +// { dg-final { scan-assembler-times "\tfcom" 2 } } + +#define double_type float +#include "pr103973-5.C" diff --git a/gcc/testsuite/g++.target/i386/pr103973-9.C b/gcc/testsuite/g++.target/i386/pr103973-9.C new file mode 100644 index 00000000000..deb48f93d9b --- /dev/null +++ b/gcc/testsuite/g++.target/i386/pr103973-9.C @@ -0,0 +1,67 @@ +// PR target/103973 +// { dg-do run } +// { dg-options "-O2 -std=c++20 -save-temps" } +// { dg-final { scan-assembler-not "'\tucomisd" { target { ! ia32 } } } } +// { dg-final { scan-assembler-times "\tcomisd" 2 { target { ! ia32 } } } } + +#include + +#ifndef double_type +#define double_type double +#endif + +__attribute__((noipa)) auto +foo (double_type a, double_type b) +{ + return a <=> b; +} + +__attribute__((noipa)) int +bar (double_type a, double_type b) +{ + auto c = foo (a, b); + if (c == std::partial_ordering::less) + return -1; + if (c == std::partial_ordering::equivalent) + return 0; + return 1; +} + +__attribute__((noipa)) auto +baz (double_type a) +{ + return a <=> 0.0f; +} + +__attribute__((noipa)) int +qux (double_type a) +{ + auto c = baz (a); + if (c == std::partial_ordering::less) + return -1; + if (c == std::partial_ordering::equivalent) + return 0; + return 1; +} + +int +main () +{ + double_type m5 = -5.0; + double_type p5 = 5.0; + volatile double_type p0 = 0.0; + double_type nan = p0 / p0; + if (bar (p5, p5) != 0 || bar (m5, m5) != 0) + __builtin_abort (); + if (bar (m5, p5) != -1 || bar (p5, m5) != 1) + __builtin_abort (); + if (bar (m5, nan) != 1 || bar (nan, p5) != 1) + __builtin_abort (); + if (bar (nan, nan) != 1) + __builtin_abort (); + if (qux (p0) != 0 || qux (nan) != 1) + __builtin_abort (); + if (qux (m5) != -1 || qux (p5) != 1) + __builtin_abort (); + return 0; +} diff --git a/gcc/testsuite/gcc.target/i386/pr103973-1.c b/gcc/testsuite/gcc.target/i386/pr103973-1.c new file mode 100644 index 00000000000..d0a3bc2339b --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr103973-1.c @@ -0,0 +1,98 @@ +/* PR target/103973 */ +/* { dg-do run } */ +/* { dg-options "-O2 -save-temps" } */ +/* { dg-final { scan-assembler-not "'\tucomisd" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "\tcomisd" 4 { target { ! ia32 } } } } */ + +__attribute__((noipa)) int m1 (void) { return -1; } +__attribute__((noipa)) int p0 (void) { return 0; } +__attribute__((noipa)) int p1 (void) { return 1; } +__attribute__((noipa)) int p2 (void) { return 2; } + +__attribute__((noipa)) int +foo (double a, double b) +{ + if (a == b) + return 0; + if (a < b) + return -1; + if (a > b) + return 1; + return 2; +} + +__attribute__((noipa)) int +bar (double a, double b) +{ + if (a == b) + return p0 (); + if (a < b) + return m1 (); + if (a > b) + return p1 (); + return p2 (); +} + +__attribute__((noipa)) int +baz (double a, double b) +{ + if (a == b) + return p0 (); + if (b < a) + return p1 (); + if (a < b) + return m1 (); + return p2 (); +} + +__attribute__((noipa)) int +qux (double a) +{ + if (a != 0.0f) + { + if (a <= 0.0f) + return -1; + if (a >= 0.0f) + return 1; + return 2; + } + return 0; +} + +int +main () +{ + double m5 = -5.0f; + double p5 = 5.0f; + volatile double p0 = 0.0f; + double nan = p0 / p0; + if (foo (p5, p5) != 0 || foo (m5, m5) != 0) + __builtin_abort (); + if (foo (m5, p5) != -1 || foo (p5, m5) != 1) + __builtin_abort (); + if (foo (m5, nan) != 2 || foo (nan, p5) != 2) + __builtin_abort (); + if (foo (nan, nan) != 2) + __builtin_abort (); + if (bar (p5, p5) != 0 || bar (m5, m5) != 0) + __builtin_abort (); + if (bar (m5, p5) != -1 || bar (p5, m5) != 1) + __builtin_abort (); + if (bar (m5, nan) != 2 || bar (nan, p5) != 2) + __builtin_abort (); + if (bar (nan, nan) != 2) + __builtin_abort (); + if (baz (p5, p5) != 0 || baz (m5, m5) != 0) + __builtin_abort (); + if (baz (m5, p5) != -1 || baz (p5, m5) != 1) + __builtin_abort (); + if (baz (m5, nan) != 2 || baz (nan, p5) != 2) + __builtin_abort (); + if (baz (nan, nan) != 2) + __builtin_abort (); + if (qux (p0) != 0 || qux (nan) != 2) + __builtin_abort (); + if (qux (m5) != -1 || qux (p5) != 1) + __builtin_abort (); + return 0; +} diff --git a/gcc/testsuite/gcc.target/i386/pr103973-10.c b/gcc/testsuite/gcc.target/i386/pr103973-10.c new file mode 100644 index 00000000000..afca4214b8b --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr103973-10.c @@ -0,0 +1,7 @@ +/* PR target/103973 */ +/* { dg-do compile { target ia32 } } */ +/* { dg-options "-O2 -march=i686 -mfpmath=387" } */ +/* { dg-final { scan-assembler-not "'\tfucom" } } */ +/* { dg-final { scan-assembler-times "\tfcom" 4 } } */ + +#include "pr103973-9.c" diff --git a/gcc/testsuite/gcc.target/i386/pr103973-11.c b/gcc/testsuite/gcc.target/i386/pr103973-11.c new file mode 100644 index 00000000000..a906d140e48 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr103973-11.c @@ -0,0 +1,8 @@ +/* PR target/103973 */ +/* { dg-do run } */ +/* { dg-options "-O2 -save-temps" } */ +/* { dg-final { scan-assembler-not "'\tucomiss" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "\tcomiss" 4 { target { ! ia32 } } } } */ + +#define double float +#include "pr103973-9.c" diff --git a/gcc/testsuite/gcc.target/i386/pr103973-12.c b/gcc/testsuite/gcc.target/i386/pr103973-12.c new file mode 100644 index 00000000000..7990f427073 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr103973-12.c @@ -0,0 +1,8 @@ +/* PR target/103973 */ +/* { dg-do compile { target ia32 } } */ +/* { dg-options "-O2 -march=i686 -mfpmath=387" } */ +/* { dg-final { scan-assembler-not "'\tfucom" } } */ +/* { dg-final { scan-assembler-times "\tfcom" 4 } } */ + +#define double float +#include "pr103973-9.c" diff --git a/gcc/testsuite/gcc.target/i386/pr103973-13.c b/gcc/testsuite/gcc.target/i386/pr103973-13.c new file mode 100644 index 00000000000..7e6dc4043cb --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr103973-13.c @@ -0,0 +1,76 @@ +/* PR target/103973 */ +/* { dg-do run } */ +/* { dg-options "-O2 -ffast-math -save-temps" } */ +/* { dg-final { scan-assembler-not "'\tucomisd" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "\tcomisd" 4 { target { ! ia32 } } } } */ + +__attribute__((noipa)) int m1 (void) { return -1; } +__attribute__((noipa)) int p0 (void) { return 0; } +__attribute__((noipa)) int p1 (void) { return 1; } + +__attribute__((noipa)) int +foo (double a, double b) +{ + if (a == b) + return 0; + if (a < b) + return -1; + return 1; +} + +__attribute__((noipa)) int +bar (double a, double b) +{ + if (a == b) + return p0 (); + if (a < b) + return m1 (); + return p1 (); +} + +__attribute__((noipa)) int +baz (double a, double b) +{ + if (a == b) + return p0 (); + if (b < a) + return p1 (); + return m1 (); +} + +__attribute__((noipa)) int +qux (double a) +{ + if (a != 0.0f) + { + if (a <= 0.0f) + return -1; + return 1; + } + return 0; +} + +int +main () +{ + double m5 = -5.0f; + double p5 = 5.0f; + double p0 = 0.0f; + if (foo (p5, p5) != 0 || foo (m5, m5) != 0) + __builtin_abort (); + if (foo (m5, p5) != -1 || foo (p5, m5) != 1) + __builtin_abort (); + if (bar (p5, p5) != 0 || bar (m5, m5) != 0) + __builtin_abort (); + if (bar (m5, p5) != -1 || bar (p5, m5) != 1) + __builtin_abort (); + if (baz (p5, p5) != 0 || baz (m5, m5) != 0) + __builtin_abort (); + if (baz (m5, p5) != -1 || baz (p5, m5) != 1) + __builtin_abort (); + if (qux (p0) != 0) + __builtin_abort (); + if (qux (m5) != -1 || qux (p5) != 1) + __builtin_abort (); + return 0; +} diff --git a/gcc/testsuite/gcc.target/i386/pr103973-14.c b/gcc/testsuite/gcc.target/i386/pr103973-14.c new file mode 100644 index 00000000000..3d1d831df4e --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr103973-14.c @@ -0,0 +1,7 @@ +/* PR target/103973 */ +/* { dg-do compile { target ia32 } } */ +/* { dg-options "-O2 -ffast-math -march=i686 -mfpmath=387" } */ +/* { dg-final { scan-assembler-not "'\tfucom" } } */ +/* { dg-final { scan-assembler-times "\tfcom" 4 } } */ + +#include "pr103973-13.c" diff --git a/gcc/testsuite/gcc.target/i386/pr103973-15.c b/gcc/testsuite/gcc.target/i386/pr103973-15.c new file mode 100644 index 00000000000..3b65a131ea5 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr103973-15.c @@ -0,0 +1,8 @@ +/* PR target/103973 */ +/* { dg-do run } */ +/* { dg-options "-O2 -ffast-math -save-temps" } */ +/* { dg-final { scan-assembler-not "'\tucomiss" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "\tcomiss" 4 { target { ! ia32 } } } } */ + +#define double float +#include "pr103973-13.c" diff --git a/gcc/testsuite/gcc.target/i386/pr103973-16.c b/gcc/testsuite/gcc.target/i386/pr103973-16.c new file mode 100644 index 00000000000..9be4e947d78 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr103973-16.c @@ -0,0 +1,8 @@ +/* PR target/103973 */ +/* { dg-do compile { target ia32 } } */ +/* { dg-options "-O2 -ffast-math -march=i686 -mfpmath=387" } */ +/* { dg-final { scan-assembler-not "'\tfucom" } } */ +/* { dg-final { scan-assembler-times "\tfcom" 4 } } */ + +#define double float +#include "pr103973-13.c" diff --git a/gcc/testsuite/gcc.target/i386/pr103973-17.c b/gcc/testsuite/gcc.target/i386/pr103973-17.c new file mode 100644 index 00000000000..1a148256ae8 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr103973-17.c @@ -0,0 +1,8 @@ +/* PR target/103973 */ +/* { dg-do run { target large_long_double } } */ +/* { dg-options "-O2 -save-temps" } */ +/* { dg-final { scan-assembler-not "'\tfucom" } } */ +/* { dg-final { scan-assembler-times "\tfcom" 4 } } */ + +#define double long double +#include "pr103973-1.c" diff --git a/gcc/testsuite/gcc.target/i386/pr103973-18.c b/gcc/testsuite/gcc.target/i386/pr103973-18.c new file mode 100644 index 00000000000..d268eaa237c --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr103973-18.c @@ -0,0 +1,8 @@ +/* PR target/103973 */ +/* { dg-do run { target large_long_double } } */ +/* { dg-options "-O2 -ffast-math -save-temps" } */ +/* { dg-final { scan-assembler-not "'\tfucom" } } */ +/* { dg-final { scan-assembler-times "\tfcom" 4 } } */ + +#define double long double +#include "pr103973-5.c" diff --git a/gcc/testsuite/gcc.target/i386/pr103973-19.c b/gcc/testsuite/gcc.target/i386/pr103973-19.c new file mode 100644 index 00000000000..ad6bddc43ed --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr103973-19.c @@ -0,0 +1,8 @@ +/* PR target/103973 */ +/* { dg-do run { target large_long_double } } */ +/* { dg-options "-O2 -save-temps" } */ +/* { dg-final { scan-assembler-not "'\tfucom" } } */ +/* { dg-final { scan-assembler-times "\tfcom" 4 } } */ + +#define double long double +#include "pr103973-9.c" diff --git a/gcc/testsuite/gcc.target/i386/pr103973-2.c b/gcc/testsuite/gcc.target/i386/pr103973-2.c new file mode 100644 index 00000000000..2bd2c11d5c4 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr103973-2.c @@ -0,0 +1,7 @@ +/* PR target/103973 */ +/* { dg-do compile { target ia32 } } */ +/* { dg-options "-O2 -march=i686 -mfpmath=387" } */ +/* { dg-final { scan-assembler-not "'\tfucom" } } */ +/* { dg-final { scan-assembler-times "\tfcom" 4 } } */ + +#include "pr103973-1.c" diff --git a/gcc/testsuite/gcc.target/i386/pr103973-20.c b/gcc/testsuite/gcc.target/i386/pr103973-20.c new file mode 100644 index 00000000000..447dac305e0 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr103973-20.c @@ -0,0 +1,8 @@ +/* PR target/103973 */ +/* { dg-do run { target large_long_double } } */ +/* { dg-options "-O2 -ffast-math -save-temps" } */ +/* { dg-final { scan-assembler-not "'\tfucom" } } */ +/* { dg-final { scan-assembler-times "\tfcom" 4 } } */ + +#define double long double +#include "pr103973-13.c" diff --git a/gcc/testsuite/gcc.target/i386/pr103973-3.c b/gcc/testsuite/gcc.target/i386/pr103973-3.c new file mode 100644 index 00000000000..2b9124e998c --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr103973-3.c @@ -0,0 +1,8 @@ +/* PR target/103973 */ +/* { dg-do run } */ +/* { dg-options "-O2 -save-temps" } */ +/* { dg-final { scan-assembler-not "'\tucomiss" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "\tcomiss" 4 { target { ! ia32 } } } } */ + +#define double float +#include "pr103973-1.c" diff --git a/gcc/testsuite/gcc.target/i386/pr103973-4.c b/gcc/testsuite/gcc.target/i386/pr103973-4.c new file mode 100644 index 00000000000..764e2e48053 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr103973-4.c @@ -0,0 +1,8 @@ +/* PR target/103973 */ +/* { dg-do compile { target ia32 } } */ +/* { dg-options "-O2 -march=i686 -mfpmath=387" } */ +/* { dg-final { scan-assembler-not "'\tfucom" } } */ +/* { dg-final { scan-assembler-times "\tfcom" 4 } } */ + +#define double float +#include "pr103973-1.c" diff --git a/gcc/testsuite/gcc.target/i386/pr103973-5.c b/gcc/testsuite/gcc.target/i386/pr103973-5.c new file mode 100644 index 00000000000..9ee6ca23ee0 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr103973-5.c @@ -0,0 +1,85 @@ +/* PR target/103973 */ +/* { dg-do run } */ +/* { dg-options "-O2 -ffast-math -save-temps" } */ +/* { dg-final { scan-assembler-not "'\tucomisd" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "\tcomisd" 4 { target { ! ia32 } } } } */ + +__attribute__((noipa)) int m1 (void) { return -1; } +__attribute__((noipa)) int p0 (void) { return 0; } +__attribute__((noipa)) int p1 (void) { return 1; } +__attribute__((noipa)) int p2 (void) { return 2; } + +__attribute__((noipa)) int +foo (double a, double b) +{ + if (a == b) + return 0; + if (a < b) + return -1; + if (a > b) + return 1; + return 2; +} + +__attribute__((noipa)) int +bar (double a, double b) +{ + if (a == b) + return p0 (); + if (a < b) + return m1 (); + if (a > b) + return p1 (); + return p2 (); +} + +__attribute__((noipa)) int +baz (double a, double b) +{ + if (a == b) + return p0 (); + if (b < a) + return p1 (); + if (a < b) + return m1 (); + return p2 (); +} + +__attribute__((noipa)) int +qux (double a) +{ + if (a != 0.0f) + { + if (a <= 0.0f) + return -1; + if (a >= 0.0f) + return 1; + return 2; + } + return 0; +} + +int +main () +{ + double m5 = -5.0f; + double p5 = 5.0f; + double p0 = 0.0f; + if (foo (p5, p5) != 0 || foo (m5, m5) != 0) + __builtin_abort (); + if (foo (m5, p5) != -1 || foo (p5, m5) != 1) + __builtin_abort (); + if (bar (p5, p5) != 0 || bar (m5, m5) != 0) + __builtin_abort (); + if (bar (m5, p5) != -1 || bar (p5, m5) != 1) + __builtin_abort (); + if (baz (p5, p5) != 0 || baz (m5, m5) != 0) + __builtin_abort (); + if (baz (m5, p5) != -1 || baz (p5, m5) != 1) + __builtin_abort (); + if (qux (p0) != 0) + __builtin_abort (); + if (qux (m5) != -1 || qux (p5) != 1) + __builtin_abort (); + return 0; +} diff --git a/gcc/testsuite/gcc.target/i386/pr103973-6.c b/gcc/testsuite/gcc.target/i386/pr103973-6.c new file mode 100644 index 00000000000..4c1418496b0 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr103973-6.c @@ -0,0 +1,7 @@ +/* PR target/103973 */ +/* { dg-do compile { target ia32 } } */ +/* { dg-options "-O2 -ffast-math -march=i686 -mfpmath=387" } */ +/* { dg-final { scan-assembler-not "'\tfucom" } } */ +/* { dg-final { scan-assembler-times "\tfcom" 4 } } */ + +#include "pr103973-5.c" diff --git a/gcc/testsuite/gcc.target/i386/pr103973-7.c b/gcc/testsuite/gcc.target/i386/pr103973-7.c new file mode 100644 index 00000000000..b883f8c5307 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr103973-7.c @@ -0,0 +1,8 @@ +/* PR target/103973 */ +/* { dg-do run } */ +/* { dg-options "-O2 -ffast-math -save-temps" } */ +/* { dg-final { scan-assembler-not "'\tucomiss" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "\tcomiss" 4 { target { ! ia32 } } } } */ + +#define double float +#include "pr103973-5.c" diff --git a/gcc/testsuite/gcc.target/i386/pr103973-8.c b/gcc/testsuite/gcc.target/i386/pr103973-8.c new file mode 100644 index 00000000000..5f490c810d4 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr103973-8.c @@ -0,0 +1,8 @@ +/* PR target/103973 */ +/* { dg-do compile { target ia32 } } */ +/* { dg-options "-O2 -ffast-math -march=i686 -mfpmath=387" } */ +/* { dg-final { scan-assembler-not "'\tfucom" } } */ +/* { dg-final { scan-assembler-times "\tfcom" 4 } } */ + +#define double float +#include "pr103973-5.c" diff --git a/gcc/testsuite/gcc.target/i386/pr103973-9.c b/gcc/testsuite/gcc.target/i386/pr103973-9.c new file mode 100644 index 00000000000..cc583c0e286 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr103973-9.c @@ -0,0 +1,89 @@ +/* PR target/103973 */ +/* { dg-do run } */ +/* { dg-options "-O2 -save-temps" } */ +/* { dg-final { scan-assembler-not "'\tucomisd" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "\tcomisd" 4 { target { ! ia32 } } } } */ + +__attribute__((noipa)) int m1 (void) { return -1; } +__attribute__((noipa)) int p0 (void) { return 0; } +__attribute__((noipa)) int p1 (void) { return 1; } + +__attribute__((noipa)) int +foo (double a, double b) +{ + if (a == b) + return 0; + if (a < b) + return -1; + return 1; +} + +__attribute__((noipa)) int +bar (double a, double b) +{ + if (a == b) + return p0 (); + if (a < b) + return m1 (); + return p1 (); +} + +__attribute__((noipa)) int +baz (double a, double b) +{ + if (a == b) + return p0 (); + if (b < a) + return p1 (); + return m1 (); +} + +__attribute__((noipa)) int +qux (double a) +{ + if (a != 0.0f) + { + if (a <= 0.0f) + return -1; + return 1; + } + return 0; +} + +int +main () +{ + double m5 = -5.0f; + double p5 = 5.0f; + volatile double p0 = 0.0f; + double nan = p0 / p0; + if (foo (p5, p5) != 0 || foo (m5, m5) != 0) + __builtin_abort (); + if (foo (m5, p5) != -1 || foo (p5, m5) != 1) + __builtin_abort (); + if (foo (m5, nan) != 1 || foo (nan, p5) != 1) + __builtin_abort (); + if (foo (nan, nan) != 1) + __builtin_abort (); + if (bar (p5, p5) != 0 || bar (m5, m5) != 0) + __builtin_abort (); + if (bar (m5, p5) != -1 || bar (p5, m5) != 1) + __builtin_abort (); + if (bar (m5, nan) != 1 || bar (nan, p5) != 1) + __builtin_abort (); + if (bar (nan, nan) != 1) + __builtin_abort (); + if (baz (p5, p5) != 0 || baz (m5, m5) != 0) + __builtin_abort (); + if (baz (m5, p5) != -1 || baz (p5, m5) != 1) + __builtin_abort (); + if (baz (m5, nan) != -1 || baz (nan, p5) != -1) + __builtin_abort (); + if (baz (nan, nan) != -1) + __builtin_abort (); + if (qux (p0) != 0 || qux (nan) != 1) + __builtin_abort (); + if (qux (m5) != -1 || qux (p5) != 1) + __builtin_abort (); + return 0; +} diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c index b7fe313b70c..14f121d8e82 100644 --- a/gcc/tree-cfg.c +++ b/gcc/tree-cfg.c @@ -9410,6 +9410,31 @@ gimple_switch_default_edge (function *ifun, gswitch *gs) return gimple_switch_edge (ifun, gs, 0); } +/* Return true if the only executable statement in BB is a GIMPLE_COND. */ + +bool +cond_only_block_p (basic_block bb) +{ + /* BB must have no executable statements. */ + gimple_stmt_iterator gsi = gsi_after_labels (bb); + if (phi_nodes (bb)) + return false; + while (!gsi_end_p (gsi)) + { + gimple *stmt = gsi_stmt (gsi); + if (is_gimple_debug (stmt)) + ; + else if (gimple_code (stmt) == GIMPLE_NOP + || gimple_code (stmt) == GIMPLE_PREDICT + || gimple_code (stmt) == GIMPLE_COND) + ; + else + return false; + gsi_next (&gsi); + } + return true; +} + /* Emit return warnings. */ diff --git a/gcc/tree-cfg.h b/gcc/tree-cfg.h index 218ac5f7b57..cb67cdf87ac 100644 --- a/gcc/tree-cfg.h +++ b/gcc/tree-cfg.h @@ -111,6 +111,7 @@ extern basic_block gimple_switch_label_bb (function *, gswitch *, unsigned); extern basic_block gimple_switch_default_bb (function *, gswitch *); extern edge gimple_switch_edge (function *, gswitch *, unsigned); extern edge gimple_switch_default_edge (function *, gswitch *); +extern bool cond_only_block_p (basic_block); /* Return true if the LHS of a call should be removed. */ diff --git a/gcc/tree-ssa-math-opts.c b/gcc/tree-ssa-math-opts.c index 1b6a57b8a77..e3c3bd84f49 100644 --- a/gcc/tree-ssa-math-opts.c +++ b/gcc/tree-ssa-math-opts.c @@ -4637,6 +4637,227 @@ convert_mult_to_highpart (gassign *stmt, gimple_stmt_iterator *gsi) return true; } +/* If target has spaceship3 expander, pattern recognize + [local count: 1073741824]: + if (a_2(D) == b_3(D)) + goto ; [34.00%] + else + goto ; [66.00%] + + [local count: 708669601]: + if (a_2(D) < b_3(D)) + goto ; [1.04%] + else + goto ; [98.96%] + + [local count: 701299439]: + if (a_2(D) > b_3(D)) + goto ; [48.89%] + else + goto ; [51.11%] + + [local count: 342865295]: + + [local count: 1073741824]: + and turn it into: + [local count: 1073741824]: + _1 = .SPACESHIP (a_2(D), b_3(D)); + if (_1 == 0) + goto ; [34.00%] + else + goto ; [66.00%] + + [local count: 708669601]: + if (_1 == -1) + goto ; [1.04%] + else + goto ; [98.96%] + + [local count: 701299439]: + if (_1 == 1) + goto ; [48.89%] + else + goto ; [51.11%] + + [local count: 342865295]: + + [local count: 1073741824]: + so that the backend can emit optimal comparison and + conditional jump sequence. */ + +static void +optimize_spaceship (gimple *stmt) +{ + enum tree_code code = gimple_cond_code (stmt); + if (code != EQ_EXPR && code != NE_EXPR) + return; + tree arg1 = gimple_cond_lhs (stmt); + tree arg2 = gimple_cond_rhs (stmt); + if (!SCALAR_FLOAT_TYPE_P (TREE_TYPE (arg1)) + || optab_handler (spaceship_optab, + TYPE_MODE (TREE_TYPE (arg1))) == CODE_FOR_nothing + || operand_equal_p (arg1, arg2, 0)) + return; + + basic_block bb0 = gimple_bb (stmt), bb1, bb2 = NULL; + edge em1 = NULL, e1 = NULL, e2 = NULL; + bb1 = EDGE_SUCC (bb0, 1)->dest; + if (((EDGE_SUCC (bb0, 0)->flags & EDGE_TRUE_VALUE) != 0) ^ (code == EQ_EXPR)) + bb1 = EDGE_SUCC (bb0, 0)->dest; + + gimple *g = last_stmt (bb1); + if (g == NULL + || gimple_code (g) != GIMPLE_COND + || !single_pred_p (bb1) + || (operand_equal_p (gimple_cond_lhs (g), arg1, 0) + ? !operand_equal_p (gimple_cond_rhs (g), arg2, 0) + : (!operand_equal_p (gimple_cond_lhs (g), arg2, 0) + || !operand_equal_p (gimple_cond_rhs (g), arg1, 0))) + || !cond_only_block_p (bb1)) + return; + + enum tree_code ccode = (operand_equal_p (gimple_cond_lhs (g), arg1, 0) + ? LT_EXPR : GT_EXPR); + switch (gimple_cond_code (g)) + { + case LT_EXPR: + case LE_EXPR: + break; + case GT_EXPR: + case GE_EXPR: + ccode = ccode == LT_EXPR ? GT_EXPR : LT_EXPR; + break; + default: + return; + } + + for (int i = 0; i < 2; ++i) + { + /* With NaNs, />= are false, so we need to look for the + third comparison on the false edge from whatever non-equality + comparison the second comparison is. */ + if (HONOR_NANS (TREE_TYPE (arg1)) + && (EDGE_SUCC (bb1, i)->flags & EDGE_TRUE_VALUE) != 0) + continue; + + bb2 = EDGE_SUCC (bb1, i)->dest; + g = last_stmt (bb2); + if (g == NULL + || gimple_code (g) != GIMPLE_COND + || !single_pred_p (bb2) + || (operand_equal_p (gimple_cond_lhs (g), arg1, 0) + ? !operand_equal_p (gimple_cond_rhs (g), arg2, 0) + : (!operand_equal_p (gimple_cond_lhs (g), arg2, 0) + || !operand_equal_p (gimple_cond_rhs (g), arg1, 0))) + || !cond_only_block_p (bb2) + || EDGE_SUCC (bb2, 0)->dest == EDGE_SUCC (bb2, 1)->dest) + continue; + + enum tree_code ccode2 + = (operand_equal_p (gimple_cond_lhs (g), arg1, 0) ? LT_EXPR : GT_EXPR); + switch (gimple_cond_code (g)) + { + case LT_EXPR: + case LE_EXPR: + break; + case GT_EXPR: + case GE_EXPR: + ccode2 = ccode2 == LT_EXPR ? GT_EXPR : LT_EXPR; + break; + default: + continue; + } + if (HONOR_NANS (TREE_TYPE (arg1)) && ccode == ccode2) + continue; + + if ((ccode == LT_EXPR) + ^ ((EDGE_SUCC (bb1, i)->flags & EDGE_TRUE_VALUE) != 0)) + { + em1 = EDGE_SUCC (bb1, 1 - i); + e1 = EDGE_SUCC (bb2, 0); + e2 = EDGE_SUCC (bb2, 1); + if ((ccode2 == LT_EXPR) ^ ((e1->flags & EDGE_TRUE_VALUE) == 0)) + std::swap (e1, e2); + } + else + { + e1 = EDGE_SUCC (bb1, 1 - i); + em1 = EDGE_SUCC (bb2, 0); + e2 = EDGE_SUCC (bb2, 1); + if ((ccode2 != LT_EXPR) ^ ((em1->flags & EDGE_TRUE_VALUE) == 0)) + std::swap (em1, e2); + } + break; + } + + if (em1 == NULL) + { + if ((ccode == LT_EXPR) + ^ ((EDGE_SUCC (bb1, 0)->flags & EDGE_TRUE_VALUE) != 0)) + { + em1 = EDGE_SUCC (bb1, 1); + e1 = EDGE_SUCC (bb1, 0); + e2 = (e1->flags & EDGE_TRUE_VALUE) ? em1 : e1; + } + else + { + em1 = EDGE_SUCC (bb1, 0); + e1 = EDGE_SUCC (bb1, 1); + e2 = (e1->flags & EDGE_TRUE_VALUE) ? em1 : e1; + } + } + + g = gimple_build_call_internal (IFN_SPACESHIP, 2, arg1, arg2); + tree lhs = make_ssa_name (integer_type_node); + gimple_call_set_lhs (g, lhs); + gimple_stmt_iterator gsi = gsi_for_stmt (stmt); + gsi_insert_before (&gsi, g, GSI_SAME_STMT); + + gcond *cond = as_a (stmt); + gimple_cond_set_lhs (cond, lhs); + gimple_cond_set_rhs (cond, integer_zero_node); + update_stmt (stmt); + + g = last_stmt (bb1); + cond = as_a (g); + gimple_cond_set_lhs (cond, lhs); + if (em1->src == bb1 && e2 != em1) + { + gimple_cond_set_rhs (cond, integer_minus_one_node); + gimple_cond_set_code (cond, (em1->flags & EDGE_TRUE_VALUE) + ? EQ_EXPR : NE_EXPR); + } + else + { + gcc_assert (e1->src == bb1 && e2 != e1); + gimple_cond_set_rhs (cond, integer_one_node); + gimple_cond_set_code (cond, (e1->flags & EDGE_TRUE_VALUE) + ? EQ_EXPR : NE_EXPR); + } + update_stmt (g); + + if (e2 != e1 && e2 != em1) + { + g = last_stmt (bb2); + cond = as_a (g); + gimple_cond_set_lhs (cond, lhs); + if (em1->src == bb2) + gimple_cond_set_rhs (cond, integer_minus_one_node); + else + { + gcc_assert (e1->src == bb2); + gimple_cond_set_rhs (cond, integer_one_node); + } + gimple_cond_set_code (cond, + (e2->flags & EDGE_TRUE_VALUE) ? NE_EXPR : EQ_EXPR); + update_stmt (g); + } + + wide_int wm1 = wi::minus_one (TYPE_PRECISION (integer_type_node)); + wide_int w2 = wi::two (TYPE_PRECISION (integer_type_node)); + set_range_info (lhs, VR_RANGE, wm1, w2); +} + /* Find integer multiplications where the operands are extended from smaller types, and replace the MULT_EXPR with a WIDEN_MULT_EXPR @@ -4798,6 +5019,8 @@ math_opts_dom_walker::after_dom_children (basic_block bb) break; } } + else if (gimple_code (stmt) == GIMPLE_COND) + optimize_spaceship (stmt); gsi_next (&gsi); } if (fma_state.m_deferring_p diff --git a/gcc/tree-ssa-phiopt.c b/gcc/tree-ssa-phiopt.c index b4dd233f34c..562468b7f02 100644 --- a/gcc/tree-ssa-phiopt.c +++ b/gcc/tree-ssa-phiopt.c @@ -1958,31 +1958,6 @@ minmax_replacement (basic_block cond_bb, basic_block middle_bb, return true; } -/* Return true if the only executable statement in BB is a GIMPLE_COND. */ - -static bool -cond_only_block_p (basic_block bb) -{ - /* BB must have no executable statements. */ - gimple_stmt_iterator gsi = gsi_after_labels (bb); - if (phi_nodes (bb)) - return false; - while (!gsi_end_p (gsi)) - { - gimple *stmt = gsi_stmt (gsi); - if (is_gimple_debug (stmt)) - ; - else if (gimple_code (stmt) == GIMPLE_NOP - || gimple_code (stmt) == GIMPLE_PREDICT - || gimple_code (stmt) == GIMPLE_COND) - ; - else - return false; - gsi_next (&gsi); - } - return true; -} - /* Attempt to optimize (x <=> y) cmp 0 and similar comparisons. For strong ordering <=> try to match something like: : // cond3_bb (== cond2_bb)