This patch replaces IFN_GOMP_SIMT_XCHG_BFLY with a more general
IFN_GOMP_SIMT_REDUCE that defers any assumptions about the hardware reduction
operators to the backend.  The final result is the same, on NVPTX, as before
except that instead of generating a loop with a variable holding the magic
number, it now unrolls that loop and emits the shuffles with the opcode numbers
hard-coded.

The main reason for dropping the loop was laziness, but the final instruction
count is similar, and the BabelStream "Dot" performance may actually be a tiny
bit faster (tested on an RTX4080, but there's quite a bit of noise in the
results).  Perhaps the PTX finalizer prefers the straight-line code with
clearer parameters?

The motivation for IFN_GOMP_SIMT_REDUCE is to allow implementing GCN reductions
via a different algorithm (there's no easy bfly instruction, and the
instructions that do exist need the shuffle parameter hard-coded in the
assembler).

gcc/ChangeLog:

        * config/nvptx/nvptx-protos.h (nvptx_expand_simt_reduce): New
        prototype.
        * config/nvptx/nvptx.cc (nvptx_expand_simt_reduce): New function.
        * config/nvptx/nvptx.md (omp_simt_xchg_bfly): Delete.
        (omp_simt_reduce): New.
        * doc/ifn.texi: Delete IFN_GOMP_SIMT_XCHG_BFLY.
        Add IFN_GOMP_SIMT_REDUCE.
        * internal-fn.def (GOMP_SIMT_XCHG_BFLY): Delete.
        (GOMP_SIMT_REDUCE): New.
        * internal-fn.cc (expand_GOMP_SIMT_XCHG_BFLY): Delete.
        (expand_GOMP_SIMT_REDUCE): New.
        * omp-low.cc (lower_rec_input_clauses): Replace
        IFN_GOMP_SIMT_XCHG_BFLY algorithm with IFN_GOMP_SIMT_REDUCE call.
        Remove the now-unused simt_lane temporary.
        * omp-offload.cc (execute_omp_device_lower): Update for the
        IFN_GOMP_SIMT_XCHG_BFLY to IFN_GOMP_SIMT_REDUCE change.
        * target-insns.def (omp_simt_xchg_bfly): Delete.
        (omp_simt_reduce): New.
        * tree-cfg.cc (gimple_can_duplicate_bb_p): Update for the
        IFN_GOMP_SIMT_XCHG_BFLY to IFN_GOMP_SIMT_REDUCE change.
---
 gcc/config/nvptx/nvptx-protos.h |  1 +
 gcc/config/nvptx/nvptx.cc       | 21 +++++++++++++++
 gcc/config/nvptx/nvptx.md       | 12 ++++-----
 gcc/doc/ifn.texi                |  8 +++---
 gcc/internal-fn.cc              | 31 +++++++++++++++------
 gcc/internal-fn.def             |  2 +-
 gcc/omp-low.cc                  | 48 +++++----------------------------
 gcc/omp-offload.cc              |  2 +-
 gcc/target-insns.def            |  2 +-
 gcc/tree-cfg.cc                 |  7 ++---
 10 files changed, 69 insertions(+), 65 deletions(-)

diff --git a/gcc/config/nvptx/nvptx-protos.h b/gcc/config/nvptx/nvptx-protos.h
index 87e8647ebe1..b5bf2d5aea5 100644
--- a/gcc/config/nvptx/nvptx-protos.h
+++ b/gcc/config/nvptx/nvptx-protos.h
@@ -51,6 +51,7 @@ extern void nvptx_expand_oacc_fork (unsigned);
 extern void nvptx_expand_oacc_join (unsigned);
 extern void nvptx_expand_call (rtx, rtx);
 extern rtx nvptx_gen_shuffle (rtx, rtx, rtx, nvptx_shuffle_kind);
+extern void nvptx_expand_simt_reduce (rtx, rtx, rtx);
 extern rtx nvptx_expand_compare (rtx);
 extern const char *nvptx_ptx_type_from_mode (machine_mode, bool);
 extern const char *nvptx_output_mov_insn (rtx, rtx);
diff --git a/gcc/config/nvptx/nvptx.cc b/gcc/config/nvptx/nvptx.cc
index 39ea6e6f923..8fe14775310 100644
--- a/gcc/config/nvptx/nvptx.cc
+++ b/gcc/config/nvptx/nvptx.cc
@@ -6194,6 +6194,27 @@ nvptx_expand_shuffle (tree exp, rtx target, machine_mode 
mode, int ignore)
   return target;
 }
 
+/* Implement the "omp_simt_reduce" expander.  */
+
+void
+nvptx_expand_simt_reduce (rtx target, rtx src, rtx code)
+{
+  machine_mode mode = GET_MODE (target);
+  rtx var = src;
+
+  for (int shift = 1; shift < PTX_WARP_SIZE; shift <<= 1)
+    {
+      rtx idx = GEN_INT (shift);
+      rtx bfly = gen_reg_rtx (mode);
+      emit_insn (nvptx_gen_shuffle (bfly, var, idx, SHUFFLE_BFLY));
+      rtx next = expand_simple_binop (mode, (enum rtx_code) INTVAL (code),
+                                     var, bfly, NULL_RTX, 1, OPTAB_DIRECT);
+      gcc_assert (next);
+      var = next;
+    }
+  emit_move_insn (target, var);
+}
+
 /* Expander for the bit reverse builtins.  */
 
 static rtx
diff --git a/gcc/config/nvptx/nvptx.md b/gcc/config/nvptx/nvptx.md
index 21731da32ce..2f2e5a34839 100644
--- a/gcc/config/nvptx/nvptx.md
+++ b/gcc/config/nvptx/nvptx.md
@@ -2026,16 +2026,16 @@ (define_expand "omp_simt_ordered"
   DONE;
 })
 
-;; Implement IFN_GOMP_SIMT_XCHG_BFLY: perform a "butterfly" exchange
-;; across lanes
-(define_expand "omp_simt_xchg_bfly"
+;; Implement IFN_GOMP_SIMT_REDUCE: reduce operand 1 across all lanes of the
+;; warp using the rtx_code reduction operator given as a literal constant
+;; in operand 2, leaving the result in operand 0 in every lane.
+(define_expand "omp_simt_reduce"
   [(match_operand 0 "nvptx_register_or_complex_di_df_register_operand" "=R")
    (match_operand 1 "nvptx_register_or_complex_di_df_register_operand" "R")
-   (match_operand:SI 2 "nvptx_nonmemory_operand" "Ri")]
+   (match_operand:SI 2 "const_int_operand" "n")]
   ""
 {
-  emit_insn (nvptx_gen_shuffle (operands[0], operands[1], operands[2],
-                               SHUFFLE_BFLY));
+  nvptx_expand_simt_reduce (operands[0], operands[1], operands[2]);
   DONE;
 })
 
diff --git a/gcc/doc/ifn.texi b/gcc/doc/ifn.texi
index 7cfc80eecf2..e8dd6f63df6 100644
--- a/gcc/doc/ifn.texi
+++ b/gcc/doc/ifn.texi
@@ -1172,10 +1172,10 @@ The @code{IFN_GOMP_SIMT_ORDERED_PRED} internal function 
is expanded by the
 The @code{IFN_GOMP_SIMT_VOTE_ANY} internal function is expanded by the
 @code{expand_GOMP_SIMT_VOTE_ANY} function.
 
-@cindex @code{IFN_GOMP_SIMT_XCHG_BFLY} internal function
-@item @samp{IFN_GOMP_SIMT_XCHG_BFLY}
-The @code{IFN_GOMP_SIMT_XCHG_BFLY} internal function is expanded by the
-@code{expand_GOMP_SIMT_XCHG_BFLY} function.
+@cindex @code{IFN_GOMP_SIMT_REDUCE} internal function
+@item @samp{IFN_GOMP_SIMT_REDUCE}
+The @code{IFN_GOMP_SIMT_REDUCE} internal function is expanded by the
+@code{expand_GOMP_SIMT_REDUCE} function.
 
 @cindex @code{IFN_GOMP_SIMT_XCHG_IDX} internal function
 @item @samp{IFN_GOMP_SIMT_XCHG_IDX}
diff --git a/gcc/internal-fn.cc b/gcc/internal-fn.cc
index 45ad08d2cf6..6cc3e6073d1 100644
--- a/gcc/internal-fn.cc
+++ b/gcc/internal-fn.cc
@@ -609,26 +609,41 @@ expand_GOMP_SIMT_VOTE_ANY (internal_fn, gcall *stmt)
   assign_call_lhs (lhs, target, &ops[0]);
 }
 
-/* Exchange between SIMT lanes with a "butterfly" pattern: source lane index
-   is destination lane index XOR given offset.  */
+/* Expand using "omp_simt_reduce<mode>".  Operand 1 of the
+   call is an INTEGER_CST holding the reduction operator as a tree_code,
+   which needs to be converted to an rtx_code for the backend.  */
 
 static void
-expand_GOMP_SIMT_XCHG_BFLY (internal_fn, gcall *stmt)
+expand_GOMP_SIMT_REDUCE (internal_fn, gcall *stmt)
 {
   tree lhs = gimple_call_lhs (stmt);
   if (!lhs)
     return;
 
+  tree arg0 = gimple_call_arg (stmt, 0);
+  tree_code code = (tree_code) TREE_INT_CST_LOW (gimple_call_arg (stmt, 1));
+  enum rtx_code rcode
+    = (code == PLUS_EXPR ? PLUS
+       : code == MULT_EXPR ? MULT
+       : code == MIN_EXPR ? (TYPE_UNSIGNED (TREE_TYPE (arg0)) ? UMIN : SMIN)
+       : code == MAX_EXPR ? (TYPE_UNSIGNED (TREE_TYPE (arg0)) ? UMAX : SMAX)
+       : code == BIT_AND_EXPR ? AND
+       : code == BIT_IOR_EXPR ? IOR
+       : code == BIT_XOR_EXPR ? XOR
+       : code == TRUTH_ANDIF_EXPR ? AND
+       : code == TRUTH_ORIF_EXPR ? IOR
+       : UNKNOWN);
+  gcc_assert (rcode != UNKNOWN);
+
   rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
-  rtx src = expand_normal (gimple_call_arg (stmt, 0));
-  rtx idx = expand_normal (gimple_call_arg (stmt, 1));
+  rtx src = expand_normal (arg0);
   machine_mode mode = TYPE_MODE (TREE_TYPE (lhs));
   class expand_operand ops[3];
   create_call_lhs_operand (&ops[0], target, mode);
   create_input_operand (&ops[1], src, mode);
-  create_input_operand (&ops[2], idx, SImode);
-  gcc_assert (targetm.have_omp_simt_xchg_bfly ());
-  expand_insn (targetm.code_for_omp_simt_xchg_bfly, 3, ops);
+  create_integer_operand (&ops[2], rcode);
+  gcc_assert (targetm.have_omp_simt_reduce ());
+  expand_insn (targetm.code_for_omp_simt_reduce, 3, ops);
   assign_call_lhs (lhs, target, &ops[0]);
 }
 
diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
index b1c46a82a52..53244ce1bfd 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -506,7 +506,7 @@ DEF_INTERNAL_FN (GOMP_MAX_VF, ECF_CONST | ECF_LEAF | 
ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (GOMP_SIMT_LAST_LANE, ECF_NOVOPS | ECF_LEAF | ECF_NOTHROW, 
NULL)
 DEF_INTERNAL_FN (GOMP_SIMT_ORDERED_PRED, ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (GOMP_SIMT_VOTE_ANY, ECF_NOVOPS | ECF_LEAF | ECF_NOTHROW, NULL)
-DEF_INTERNAL_FN (GOMP_SIMT_XCHG_BFLY, ECF_NOVOPS | ECF_LEAF | ECF_NOTHROW, 
NULL)
+DEF_INTERNAL_FN (GOMP_SIMT_REDUCE, ECF_NOVOPS | ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (GOMP_SIMT_XCHG_IDX, ECF_NOVOPS | ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (GOMP_SIMD_LANE, ECF_NOVOPS | ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (GOMP_SIMD_VF, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
diff --git a/gcc/omp-low.cc b/gcc/omp-low.cc
index bcc482fb839..2502967c699 100644
--- a/gcc/omp-low.cc
+++ b/gcc/omp-low.cc
@@ -4965,7 +4965,7 @@ lower_rec_input_clauses (tree clauses, gimple_seq *ilist, 
gimple_seq *dlist,
   bool is_simd = (gimple_code (ctx->stmt) == GIMPLE_OMP_FOR
                  && gimple_omp_for_kind (ctx->stmt) == GF_OMP_FOR_KIND_SIMD);
   omplow_simd_context sctx = omplow_simd_context ();
-  tree simt_lane = NULL_TREE, simtrec = NULL_TREE;
+  tree simtrec = NULL_TREE;
   tree ivar = NULL_TREE, lvar = NULL_TREE, uid = NULL_TREE;
   gimple_seq llist[4] = { };
   tree nonconst_simd_if = NULL_TREE;
@@ -6825,15 +6825,10 @@ lower_rec_input_clauses (tree clauses, gimple_seq 
*ilist, gimple_seq *dlist,
 
                      if (sctx.is_simt)
                        {
-                         if (!simt_lane)
-                           simt_lane = create_tmp_var (unsigned_type_node);
+                         tree op = build_int_cst (unsigned_type_node, code);
                          x = build_call_expr_internal_loc
-                           (UNKNOWN_LOCATION, IFN_GOMP_SIMT_XCHG_BFLY,
-                            TREE_TYPE (ivar), 2, ivar, simt_lane);
-                         /* Make sure x is evaluated unconditionally.  */
-                         tree bfly_var = create_tmp_var (TREE_TYPE (ivar));
-                         gimplify_assign (bfly_var, x, &llist[2]);
-                         x = build2 (code, TREE_TYPE (ivar), ivar, bfly_var);
+                           (UNKNOWN_LOCATION, IFN_GOMP_SIMT_REDUCE,
+                            TREE_TYPE (ivar), 2, ivar, op);
                          gimplify_assign (ivar, x, &llist[2]);
                        }
                      tree ivar2 = ivar;
@@ -6997,39 +6992,10 @@ lower_rec_input_clauses (tree clauses, gimple_seq 
*ilist, gimple_seq *dlist,
          gimple_seq_add_stmt (dlist, g);
          gimple_seq_add_seq (dlist, llist[3]);
        }
-      /* Emit reductions across SIMT lanes in log_2(simt_vf) steps.  */
+      /* Emit the IFN_GOMP_SIMT_REDUCE call built above, if any.
+        The actual reduction algorithm is deferred to the back-end.  */
       if (llist[2])
-       {
-         tree simt_vf = create_tmp_var (unsigned_type_node);
-         g = gimple_build_call_internal (IFN_GOMP_SIMT_VF, 0);
-         gimple_call_set_lhs (g, simt_vf);
-         gimple_seq_add_stmt (dlist, g);
-
-         tree t = build_int_cst (unsigned_type_node, 1);
-         g = gimple_build_assign (simt_lane, INTEGER_CST, t);
-         gimple_seq_add_stmt (dlist, g);
-
-         t = build_int_cst (unsigned_type_node, 0);
-         g = gimple_build_assign (sctx.idx, INTEGER_CST, t);
-         gimple_seq_add_stmt (dlist, g);
-
-         tree body = create_artificial_label (UNKNOWN_LOCATION);
-         tree header = create_artificial_label (UNKNOWN_LOCATION);
-         tree end = create_artificial_label (UNKNOWN_LOCATION);
-         gimple_seq_add_stmt (dlist, gimple_build_goto (header));
-         gimple_seq_add_stmt (dlist, gimple_build_label (body));
-
-         gimple_seq_add_seq (dlist, llist[2]);
-
-         g = gimple_build_assign (simt_lane, LSHIFT_EXPR, simt_lane, 
integer_one_node);
-         gimple_seq_add_stmt (dlist, g);
-
-         gimple_seq_add_stmt (dlist, gimple_build_label (header));
-         g = gimple_build_cond (LT_EXPR, simt_lane, simt_vf, body, end);
-         gimple_seq_add_stmt (dlist, g);
-
-         gimple_seq_add_stmt (dlist, gimple_build_label (end));
-       }
+       gimple_seq_add_seq (dlist, llist[2]);
       for (int i = 0; i < 2; i++)
        if (llist[i])
          {
diff --git a/gcc/omp-offload.cc b/gcc/omp-offload.cc
index f39635712e4..aa35d2394dd 100644
--- a/gcc/omp-offload.cc
+++ b/gcc/omp-offload.cc
@@ -2861,7 +2861,7 @@ execute_omp_device_lower ()
              unlink_stmt_vdef (stmt);
            break;
          case IFN_GOMP_SIMT_VOTE_ANY:
-         case IFN_GOMP_SIMT_XCHG_BFLY:
+         case IFN_GOMP_SIMT_REDUCE:
          case IFN_GOMP_SIMT_XCHG_IDX:
            rhs = vf == 1 ? gimple_call_arg (stmt, 0) : NULL_TREE;
            break;
diff --git a/gcc/target-insns.def b/gcc/target-insns.def
index 8da504f9557..1f0cc9df88c 100644
--- a/gcc/target-insns.def
+++ b/gcc/target-insns.def
@@ -75,7 +75,7 @@ DEF_TARGET_INSN (omp_simt_lane, (rtx x0))
 DEF_TARGET_INSN (omp_simt_last_lane, (rtx x0, rtx x1))
 DEF_TARGET_INSN (omp_simt_ordered, (rtx x0, rtx x1))
 DEF_TARGET_INSN (omp_simt_vote_any, (rtx x0, rtx x1))
-DEF_TARGET_INSN (omp_simt_xchg_bfly, (rtx x0, rtx x1, rtx x2))
+DEF_TARGET_INSN (omp_simt_reduce, (rtx x0, rtx x1, rtx x2))
 DEF_TARGET_INSN (omp_simt_xchg_idx, (rtx x0, rtx x1, rtx x2))
 DEF_TARGET_INSN (prefetch, (rtx x0, rtx x1, rtx x2))
 DEF_TARGET_INSN (probe_stack, (rtx x0))
diff --git a/gcc/tree-cfg.cc b/gcc/tree-cfg.cc
index ba1d4a125ad..116e07f1d39 100644
--- a/gcc/tree-cfg.cc
+++ b/gcc/tree-cfg.cc
@@ -6489,13 +6489,14 @@ gimple_can_duplicate_bb_p (const_basic_block bb)
 
       /* An IFN_GOMP_SIMT_ENTER_ALLOC/IFN_GOMP_SIMT_EXIT call must be
         duplicated as part of its group, or not at all.
-        The IFN_GOMP_SIMT_VOTE_ANY and IFN_GOMP_SIMT_XCHG_* are part of such a
-        group, so the same holds there.  */
+        The IFN_GOMP_SIMT_VOTE_ANY, IFN_GOMP_SIMT_XCHG_IDX and
+        IFN_GOMP_SIMT_REDUCE are part of such a group, so the same holds
+        there.  */
       if (is_gimple_call (g)
          && (gimple_call_internal_p (g, IFN_GOMP_SIMT_ENTER_ALLOC)
              || gimple_call_internal_p (g, IFN_GOMP_SIMT_EXIT)
              || gimple_call_internal_p (g, IFN_GOMP_SIMT_VOTE_ANY)
-             || gimple_call_internal_p (g, IFN_GOMP_SIMT_XCHG_BFLY)
+             || gimple_call_internal_p (g, IFN_GOMP_SIMT_REDUCE)
              || gimple_call_internal_p (g, IFN_GOMP_SIMT_XCHG_IDX)))
        return false;
     }
-- 
2.54.0

Reply via email to