From: Soumya AR <[email protected]>

This patch implements the expand_ifn_atomic_fetch_minmax function to
expand the IFN_ATOMIC_FETCH_MINMAX internal function to target specific
optabs, if available.

----

Each atomic_op_table entry gets a can_expand_directly callback.  When
the target has the optab, gimple-isel leaves the IFN for RTL expansion.
Otherwise, it is lowered to a CAS loop as before.  The check is exposed
as can_expand_atomic_fetch_minmax_p so we can reuse it for pattern
matching later.

----

For sub-word signed types, the LHS can be a promoted SUBREG marked as
already sign-extended (/s/u), so an extension emitted after the atomic
operation may be optimized away.  The operation is therefore done into a
temporary non-promoted register and then extended to the original
promoted target.

----

Bootstrapped and regression tested on aarch64-linux-gnu and
x86_64-linux-gnu.
Cross-compiled and regression tested for arm-linux-gnueabihf-armv7-a and
aarch64-linux-gnu without LSE.

OK for mainline?

gcc/ChangeLog:

        * atomic-ifn-lowering.cc: Include optabs.h and optabs-query.h.
        (struct atomic_op_lowering): Add can_expand_directly callback.
        (can_expand_atomic_fetch_minmax_p): New.
        (can_expand_atomic_minmax): New.
        (atomic_op_table): Use it.
        (maybe_lower_atomic_ifn): Leave the IFN for RTL expansion when it
        can be expanded directly.
        * atomic-ifn-lowering.h (can_expand_atomic_fetch_minmax_p): Declare.
        * builtins.cc (expand_ifn_atomic_fetch_minmax): New.
        * builtins.h (expand_ifn_atomic_fetch_minmax): Declare.
        * doc/md.texi (atomic_<op><mode>, atomic_fetch_<op><mode>)
        (atomic_<op>_fetch<mode>, sync_<op><mode>, sync_old_<op><mode>)
        (sync_new_<op><mode>): Document the smin, smax, umin and umax
        variants.
        * internal-fn.cc (expand_ATOMIC_FETCH_MINMAX): Call
        expand_ifn_atomic_fetch_minmax.
        * optabs.cc (get_atomic_op_for_code): Handle SMIN, SMAX, UMIN,
        UMAX.
        (maybe_emit_op): Likewise.
        * optabs.def (OPTAB_NC, OPTAB_D): Add atomic_fetch_{s,u}{min,max}
        and atomic_{s,u}{min,max} entries.
        * optabs.h (expand_atomic_fetch_op_no_fallback): Export.

Signed-off-by: Soumya AR <[email protected]>
---
 gcc/atomic-ifn-lowering.cc | 66 ++++++++++++++++++++++++++++++++++++--
 gcc/atomic-ifn-lowering.h  |  1 +
 gcc/builtins.cc            | 65 +++++++++++++++++++++++++++++++++++++
 gcc/builtins.h             |  1 +
 gcc/c-family/c-common.cc   |  4 ++-
 gcc/doc/md.texi            | 36 +++++++++++++++++++++
 gcc/internal-fn.cc         |  4 +--
 gcc/optabs.cc              | 38 +++++++++++++++++++++-
 gcc/optabs.def             | 24 ++++++++++++++
 gcc/optabs.h               |  2 ++
 10 files changed, 234 insertions(+), 7 deletions(-)

diff --git a/gcc/atomic-ifn-lowering.cc b/gcc/atomic-ifn-lowering.cc
index 6f5e889a6ca3..31f4997889cb 100644
--- a/gcc/atomic-ifn-lowering.cc
+++ b/gcc/atomic-ifn-lowering.cc
@@ -64,17 +64,22 @@ After lowering the CFG looks somewhat like:
 #include "gimple-pretty-print.h"
 #include "memmodel.h"
 #include "ssa.h"
+#include "optabs.h"
+#include "optabs-query.h"
 #include "fold-const.h"
 #include "builtins.h"
 #include "internal-fn.h"
 #include "atomic-ifn-lowering.h"
 
 /* Descriptor for an atomic-fetch IFN that this code can lower.
-   lower replaces the IFN call with a CAS loop.  */
+   can_expand_directly returns true when the backend can expand the IFN via a
+   direct optab, in which case we leave the call for the RTL expander.  lower
+   replaces the IFN call with a CAS loop.  */
 
 struct atomic_op_lowering
 {
   internal_fn ifn;
+  bool (*can_expand_directly) (gcall *);
   void (*lower) (gimple_stmt_iterator *);
 };
 
@@ -350,6 +355,58 @@ emit_minmax_desired_value (gimple_stmt_iterator *gsi_loop, 
tree current_value,
           : insert_type_conversion (gsi_loop, newval, atomic_type);
 }
 
+/* Return true if the backend can expand an atomic fetch MIN (if IS_MIN) or
+   MAX on DATATYPE directly via a target optab.  UNUSED_RESULT is true if
+   nothing uses the fetched value.  */
+
+bool
+can_expand_atomic_fetch_minmax_p (tree datatype, bool is_min,
+                                 bool unused_result)
+{
+  if (!flag_inline_atomics)
+    return false;
+
+  bool is_signed = !TYPE_UNSIGNED (datatype);
+  machine_mode mode = TYPE_MODE (datatype);
+
+  if (!can_atomic_load_p (mode))
+    return false;
+
+  optab fetch_op_optab
+    = is_signed ? (is_min ? atomic_fetch_smin_optab : atomic_fetch_smax_optab)
+               : (is_min ? atomic_fetch_umin_optab : atomic_fetch_umax_optab);
+
+  if (optab_handler (fetch_op_optab, mode) != CODE_FOR_nothing)
+    return true;
+
+  /* If nothing uses the result, the no-result optab also suffices.  MIN/MAX
+     has no op_fetch form, so we do not need those optabs.  */
+  if (unused_result)
+    {
+      optab no_result_optab
+       = is_signed ? (is_min ? atomic_smin_optab : atomic_smax_optab)
+                   : (is_min ? atomic_umin_optab : atomic_umax_optab);
+      if (direct_optab_handler (no_result_optab, mode) != CODE_FOR_nothing)
+       return true;
+    }
+
+  return false;
+}
+
+/* Return true when the backend can expand an IFN_ATOMIC_FETCH_MINMAX call
+   directly via a target optab.  */
+
+static bool
+can_expand_atomic_minmax (gcall *call)
+{
+  tree datatype = TREE_TYPE (gimple_call_arg (call, 1));
+  tree_code cmp_code
+    = (tree_code) tree_to_uhwi (gimple_call_arg (call, 2));
+  gcc_assert (cmp_code == MIN_EXPR || cmp_code == MAX_EXPR);
+  return can_expand_atomic_fetch_minmax_p (datatype, cmp_code == MIN_EXPR,
+                                          gimple_call_lhs (call) == NULL_TREE);
+}
+
 /* Lower an IFN_ATOMIC_FETCH_MINMAX call to a CAS loop.  */
 
 static void
@@ -358,10 +415,10 @@ lower_minmax_call (gimple_stmt_iterator *gsi)
   lower_via_cas_loop (gsi, emit_minmax_desired_value);
 }
 
-/* Atomic-fetch IFNs supported by this lowering code.  */
+/* Atomic-fetch IFNs that we lower when the target lacks a direct optab.  */
 
 static const atomic_op_lowering atomic_op_table[] = {
-  { IFN_ATOMIC_FETCH_MINMAX, lower_minmax_call },
+  { IFN_ATOMIC_FETCH_MINMAX, can_expand_atomic_minmax, lower_minmax_call },
 };
 
 /* Lower the IFN at GSI.  On a true return, GSI points to the first statement 
in
@@ -378,6 +435,9 @@ maybe_lower_atomic_ifn (gimple_stmt_iterator *gsi)
   for (const atomic_op_lowering &op : atomic_op_table)
     if (ifn == op.ifn)
       {
+       if (op.can_expand_directly (call))
+         return false;
+
        if (dump_file)
          {
            fprintf (dump_file, "Lowering atomic IFN:\n  ");
diff --git a/gcc/atomic-ifn-lowering.h b/gcc/atomic-ifn-lowering.h
index 370a449c2fd1..924cd54385ae 100644
--- a/gcc/atomic-ifn-lowering.h
+++ b/gcc/atomic-ifn-lowering.h
@@ -21,5 +21,6 @@
 #define GCC_ATOMIC_IFN_LOWERING_H
 
 extern unsigned int lower_atomic_ifn (const vec<gcall *> &);
+extern bool can_expand_atomic_fetch_minmax_p (tree, bool, bool);
 
 #endif /* GCC_ATOMIC_IFN_LOWERING_H */
diff --git a/gcc/builtins.cc b/gcc/builtins.cc
index 3127e0e82a1c..3c7b48940d02 100644
--- a/gcc/builtins.cc
+++ b/gcc/builtins.cc
@@ -6787,6 +6787,71 @@ expand_builtin_atomic_compare_exchange (machine_mode 
mode, tree exp,
   return target;
 }
 
+/* Expand IFN_ATOMIC_FETCH_MINMAX internal function.  */
+
+void
+expand_ifn_atomic_fetch_minmax (gcall *call)
+{
+  tree ptr = gimple_call_arg (call, 0);
+  tree value = gimple_call_arg (call, 1);
+  tree_code cmp_code = (tree_code) tree_to_uhwi (gimple_call_arg (call, 2));
+  gcc_assert (cmp_code == MIN_EXPR || cmp_code == MAX_EXPR);
+  tree memorder_tree = gimple_call_arg (call, 3);
+  tree datatype = TREE_TYPE (value);
+
+  bool is_min = (cmp_code == MIN_EXPR);
+  bool is_signed = !TYPE_UNSIGNED (datatype);
+
+  memmodel model = get_memmodel (memorder_tree);
+  tree lhs = gimple_call_lhs (call);
+
+  enum rtx_code code;
+  if (is_signed)
+    code = is_min ? SMIN : SMAX;
+  else
+    code = is_min ? UMIN : UMAX;
+
+  machine_mode mode = TYPE_MODE (datatype);
+
+  rtx mem = get_builtin_sync_mem (ptr, mode);
+
+  rtx val = expand_expr_force_mode (value, mode);
+
+  rtx target;
+  if (lhs)
+    target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
+  else
+    target = const0_rtx;
+
+  /* For sub-word signed operations, we need to handle sign extension
+     carefully.  The issue is that narrow atomic operations may not return
+     properly sign-extended values, but the expanded destination might be a
+     promoted subreg that has been marked as sign-extended.  A forced sign
+     extension after the atomic operation might get optimized away if the
+     promoted target has been marked as already sign-extended.
+
+     Use a temporary non-promoted register for the atomic operation
+     to avoid this.  */
+  rtx temp_target = target;
+  if (GET_CODE (target) == SUBREG && SUBREG_PROMOTED_VAR_P (target)
+      && GET_MODE_SIZE (mode).to_constant () < UNITS_PER_WORD)
+    temp_target = gen_reg_rtx (mode);
+
+  rtx result = expand_atomic_fetch_op_no_fallback (temp_target, mem, val, code,
+                                                  model, false);
+
+  if (!result)
+    gcc_unreachable ();
+
+  if (target == const0_rtx)
+    return;
+
+  if (temp_target != target)
+    convert_move (SUBREG_REG (target), result, !is_signed);
+  else if (result != target)
+    emit_move_insn (target, result);
+}
+
 /* Helper function for expand_ifn_atomic_compare_exchange - expand
    internal ATOMIC_COMPARE_EXCHANGE call into __atomic_compare_exchange_N
    call.  The weak parameter must be dropped to match the expected parameter
diff --git a/gcc/builtins.h b/gcc/builtins.h
index 97b79908d0dd..0ce1fa6735f4 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -133,6 +133,7 @@ extern void expand_builtin_trap (void);
 extern void expand_ifn_atomic_bit_test_and (gcall *);
 extern void expand_ifn_atomic_compare_exchange (gcall *);
 extern void expand_ifn_atomic_op_fetch_cmp_0 (gcall *);
+extern void expand_ifn_atomic_fetch_minmax (gcall *);
 extern rtx expand_builtin_crc_table_based (internal_fn, scalar_mode,
                                           scalar_mode, machine_mode,
                                           tree, rtx);
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index e9de4930118f..75f256d93fcc 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -8961,7 +8961,9 @@ resolve_overloaded_builtin (location_t loc, tree function,
                                                       params);
          }
 
-       /* Implement atomic fetch min/max builtins through an IFN.  */
+       /* Atomic fetch min/max builtins are implemented through an IFN
+          that will be either lowered to a CAS loop or expanded via a
+          direct optab, depending on the target.  */
        if (orig_code == BUILT_IN_ATOMIC_FETCH_MIN
            || orig_code == BUILT_IN_ATOMIC_FETCH_MAX)
          {
diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi
index 648c12f6e7b9..3b205b67d095 100644
--- a/gcc/doc/md.texi
+++ b/gcc/doc/md.texi
@@ -6839,9 +6839,15 @@ interruptible locking.
 @mdindex sync_and@var{mode}
 @mdindex sync_xor@var{mode}
 @mdindex sync_nand@var{mode}
+@mdindex sync_smin@var{mode}
+@mdindex sync_smax@var{mode}
+@mdindex sync_umin@var{mode}
+@mdindex sync_umax@var{mode}
 @item @samp{sync_add@var{mode}}, @samp{sync_sub@var{mode}}
 @itemx @samp{sync_ior@var{mode}}, @samp{sync_and@var{mode}}
 @itemx @samp{sync_xor@var{mode}}, @samp{sync_nand@var{mode}}
+@itemx @samp{sync_smin@var{mode}}, @samp{sync_smax@var{mode}}
+@itemx @samp{sync_umin@var{mode}}, @samp{sync_umax@var{mode}}
 These patterns emit code for an atomic operation on memory.
 Operand 0 is the memory on which the atomic operation is performed.
 Operand 1 is the second operand to the binary operator.
@@ -6860,9 +6866,15 @@ from a compare-and-swap operation, if defined.
 @mdindex sync_old_and@var{mode}
 @mdindex sync_old_xor@var{mode}
 @mdindex sync_old_nand@var{mode}
+@mdindex sync_old_smin@var{mode}
+@mdindex sync_old_smax@var{mode}
+@mdindex sync_old_umin@var{mode}
+@mdindex sync_old_umax@var{mode}
 @item @samp{sync_old_add@var{mode}}, @samp{sync_old_sub@var{mode}}
 @itemx @samp{sync_old_ior@var{mode}}, @samp{sync_old_and@var{mode}}
 @itemx @samp{sync_old_xor@var{mode}}, @samp{sync_old_nand@var{mode}}
+@itemx @samp{sync_old_smin@var{mode}}, @samp{sync_old_smax@var{mode}}
+@itemx @samp{sync_old_umin@var{mode}}, @samp{sync_old_umax@var{mode}}
 These patterns emit code for an atomic operation on memory,
 and return the value that the memory contained before the operation.
 Operand 0 is the result value, operand 1 is the memory on which the
@@ -6883,9 +6895,15 @@ from a compare-and-swap operation, if defined.
 @mdindex sync_new_and@var{mode}
 @mdindex sync_new_xor@var{mode}
 @mdindex sync_new_nand@var{mode}
+@mdindex sync_new_smin@var{mode}
+@mdindex sync_new_smax@var{mode}
+@mdindex sync_new_umin@var{mode}
+@mdindex sync_new_umax@var{mode}
 @item @samp{sync_new_add@var{mode}}, @samp{sync_new_sub@var{mode}}
 @itemx @samp{sync_new_ior@var{mode}}, @samp{sync_new_and@var{mode}}
 @itemx @samp{sync_new_xor@var{mode}}, @samp{sync_new_nand@var{mode}}
+@itemx @samp{sync_new_smin@var{mode}}, @samp{sync_new_smax@var{mode}}
+@itemx @samp{sync_new_umin@var{mode}}, @samp{sync_new_umax@var{mode}}
 These patterns are like their @code{sync_old_@var{op}} counterparts,
 except that they return the value that exists in the memory location
 after the operation, rather than before the operation.
@@ -7002,9 +7020,15 @@ compare and swap loop.
 @mdindex atomic_and@var{mode}
 @mdindex atomic_xor@var{mode}
 @mdindex atomic_nand@var{mode}
+@mdindex atomic_smin@var{mode}
+@mdindex atomic_smax@var{mode}
+@mdindex atomic_umin@var{mode}
+@mdindex atomic_umax@var{mode}
 @item @samp{atomic_add@var{mode}}, @samp{atomic_sub@var{mode}}
 @itemx @samp{atomic_or@var{mode}}, @samp{atomic_and@var{mode}}
 @itemx @samp{atomic_xor@var{mode}}, @samp{atomic_nand@var{mode}}
+@itemx @samp{atomic_smin@var{mode}}, @samp{atomic_smax@var{mode}}
+@itemx @samp{atomic_umin@var{mode}}, @samp{atomic_umax@var{mode}}
 These patterns emit code for an atomic operation on memory with memory
 model semantics. Operand 0 is the memory on which the atomic operation is
 performed.  Operand 1 is the second operand to the binary operator.
@@ -7020,9 +7044,15 @@ none of these are available a compare-and-swap loop will 
be used.
 @mdindex atomic_fetch_and@var{mode}
 @mdindex atomic_fetch_xor@var{mode}
 @mdindex atomic_fetch_nand@var{mode}
+@mdindex atomic_fetch_smin@var{mode}
+@mdindex atomic_fetch_smax@var{mode}
+@mdindex atomic_fetch_umin@var{mode}
+@mdindex atomic_fetch_umax@var{mode}
 @item @samp{atomic_fetch_add@var{mode}}, @samp{atomic_fetch_sub@var{mode}}
 @itemx @samp{atomic_fetch_or@var{mode}}, @samp{atomic_fetch_and@var{mode}}
 @itemx @samp{atomic_fetch_xor@var{mode}}, @samp{atomic_fetch_nand@var{mode}}
+@itemx @samp{atomic_fetch_smin@var{mode}}, @samp{atomic_fetch_smax@var{mode}}
+@itemx @samp{atomic_fetch_umin@var{mode}}, @samp{atomic_fetch_umax@var{mode}}
 These patterns emit code for an atomic operation on memory with memory
 model semantics, and return the original value. Operand 0 is an output 
 operand which contains the value of the memory location before the 
@@ -7040,9 +7070,15 @@ loop will be used.
 @mdindex atomic_and_fetch@var{mode}
 @mdindex atomic_xor_fetch@var{mode}
 @mdindex atomic_nand_fetch@var{mode}
+@mdindex atomic_smin_fetch@var{mode}
+@mdindex atomic_smax_fetch@var{mode}
+@mdindex atomic_umin_fetch@var{mode}
+@mdindex atomic_umax_fetch@var{mode}
 @item @samp{atomic_add_fetch@var{mode}}, @samp{atomic_sub_fetch@var{mode}}
 @itemx @samp{atomic_or_fetch@var{mode}}, @samp{atomic_and_fetch@var{mode}}
 @itemx @samp{atomic_xor_fetch@var{mode}}, @samp{atomic_nand_fetch@var{mode}}
+@itemx @samp{atomic_smin_fetch@var{mode}}, @samp{atomic_smax_fetch@var{mode}}
+@itemx @samp{atomic_umin_fetch@var{mode}}, @samp{atomic_umax_fetch@var{mode}}
 These patterns emit code for an atomic operation on memory with memory
 model semantics and return the result after the operation is performed.
 Operand 0 is an output operand which contains the value after the
diff --git a/gcc/internal-fn.cc b/gcc/internal-fn.cc
index 6b6ab9bc86bd..3802e69197f9 100644
--- a/gcc/internal-fn.cc
+++ b/gcc/internal-fn.cc
@@ -3776,9 +3776,9 @@ expand_ATOMIC_XOR_FETCH_CMP_0 (internal_fn, gcall *call)
 /* Expand atomic fetch minmax.  */
 
 static void
-expand_ATOMIC_FETCH_MINMAX (internal_fn, gcall *)
+expand_ATOMIC_FETCH_MINMAX (internal_fn, gcall *call)
 {
-  /* Implement this.  */
+  expand_ifn_atomic_fetch_minmax (call);
 }
 
 /* Expand LAUNDER to assignment, lhs = arg0.  */
diff --git a/gcc/optabs.cc b/gcc/optabs.cc
index 3b7cf55a81bd..bc8991b1fd5b 100644
--- a/gcc/optabs.cc
+++ b/gcc/optabs.cc
@@ -8006,6 +8006,42 @@ get_atomic_op_for_code (struct atomic_op_functions *op, 
enum rtx_code code)
       op->no_result = sync_nand_optab;
       op->reverse_code = UNKNOWN;
       break;
+    case SMIN:
+      op->mem_fetch_before = atomic_fetch_smin_optab;
+      op->mem_fetch_after = atomic_smin_fetch_optab;
+      op->mem_no_result = atomic_smin_optab;
+      op->fetch_before = sync_old_smin_optab;
+      op->fetch_after = sync_new_smin_optab;
+      op->no_result = sync_smin_optab;
+      op->reverse_code = UNKNOWN;
+      break;
+    case SMAX:
+      op->mem_fetch_before = atomic_fetch_smax_optab;
+      op->mem_fetch_after = atomic_smax_fetch_optab;
+      op->mem_no_result = atomic_smax_optab;
+      op->fetch_before = sync_old_smax_optab;
+      op->fetch_after = sync_new_smax_optab;
+      op->no_result = sync_smax_optab;
+      op->reverse_code = UNKNOWN;
+      break;
+    case UMIN:
+      op->mem_fetch_before = atomic_fetch_umin_optab;
+      op->mem_fetch_after = atomic_umin_fetch_optab;
+      op->mem_no_result = atomic_umin_optab;
+      op->fetch_before = sync_old_umin_optab;
+      op->fetch_after = sync_new_umin_optab;
+      op->no_result = sync_umin_optab;
+      op->reverse_code = UNKNOWN;
+      break;
+    case UMAX:
+      op->mem_fetch_before = atomic_fetch_umax_optab;
+      op->mem_fetch_after = atomic_umax_fetch_optab;
+      op->mem_no_result = atomic_umax_optab;
+      op->fetch_before = sync_old_umax_optab;
+      op->fetch_after = sync_new_umax_optab;
+      op->no_result = sync_umax_optab;
+      op->reverse_code = UNKNOWN;
+      break;
     default:
       gcc_unreachable ();
     }
@@ -8124,7 +8160,7 @@ maybe_emit_op (const struct atomic_op_functions *optab, 
rtx target, rtx mem,
    This function will *only* generate instructions if there is a direct
    optab. No compare and swap loops or libcalls will be generated. */
 
-static rtx
+rtx
 expand_atomic_fetch_op_no_fallback (rtx target, rtx mem, rtx val,
                                    enum rtx_code code, enum memmodel model,
                                    bool after)
diff --git a/gcc/optabs.def b/gcc/optabs.def
index faecb796d015..e6adda552b1b 100644
--- a/gcc/optabs.def
+++ b/gcc/optabs.def
@@ -218,12 +218,20 @@ OPTAB_NC(sync_old_ior_optab, "sync_old_ior$I$a", UNKNOWN)
 OPTAB_NC(sync_old_and_optab, "sync_old_and$I$a", UNKNOWN)
 OPTAB_NC(sync_old_xor_optab, "sync_old_xor$I$a", UNKNOWN)
 OPTAB_NC(sync_old_nand_optab, "sync_old_nand$I$a", UNKNOWN)
+OPTAB_NC(sync_old_smin_optab, "sync_old_smin$I$a", UNKNOWN)
+OPTAB_NC(sync_old_smax_optab, "sync_old_smax$I$a", UNKNOWN)
+OPTAB_NC(sync_old_umin_optab, "sync_old_umin$I$a", UNKNOWN)
+OPTAB_NC(sync_old_umax_optab, "sync_old_umax$I$a", UNKNOWN)
 OPTAB_NC(sync_new_add_optab, "sync_new_add$I$a", UNKNOWN)
 OPTAB_NC(sync_new_sub_optab, "sync_new_sub$I$a", UNKNOWN)
 OPTAB_NC(sync_new_ior_optab, "sync_new_ior$I$a", UNKNOWN)
 OPTAB_NC(sync_new_and_optab, "sync_new_and$I$a", UNKNOWN)
 OPTAB_NC(sync_new_xor_optab, "sync_new_xor$I$a", UNKNOWN)
 OPTAB_NC(sync_new_nand_optab, "sync_new_nand$I$a", UNKNOWN)
+OPTAB_NC(sync_new_smin_optab, "sync_new_smin$I$a", UNKNOWN)
+OPTAB_NC(sync_new_smax_optab, "sync_new_smax$I$a", UNKNOWN)
+OPTAB_NC(sync_new_umin_optab, "sync_new_umin$I$a", UNKNOWN)
+OPTAB_NC(sync_new_umax_optab, "sync_new_umax$I$a", UNKNOWN)
 OPTAB_NC(sync_compare_and_swap_optab, "sync_compare_and_swap$I$a", UNKNOWN)
 OPTAB_NC(sync_lock_test_and_set_optab, "sync_lock_test_and_set$I$a", UNKNOWN)
 
@@ -527,6 +535,10 @@ OPTAB_D (sync_and_optab, "sync_and$I$a")
 OPTAB_D (sync_ior_optab, "sync_ior$I$a")
 OPTAB_D (sync_lock_release_optab, "sync_lock_release$I$a")
 OPTAB_D (sync_nand_optab, "sync_nand$I$a")
+OPTAB_D (sync_smin_optab, "sync_smin$I$a")
+OPTAB_D (sync_smax_optab, "sync_smax$I$a")
+OPTAB_D (sync_umin_optab, "sync_umin$I$a")
+OPTAB_D (sync_umax_optab, "sync_umax$I$a")
 OPTAB_D (sync_sub_optab, "sync_sub$I$a")
 OPTAB_D (sync_xor_optab, "sync_xor$I$a")
 
@@ -545,6 +557,18 @@ OPTAB_D (atomic_fetch_nand_optab, "atomic_fetch_nand$I$a")
 OPTAB_D (atomic_fetch_or_optab, "atomic_fetch_or$I$a")
 OPTAB_D (atomic_fetch_sub_optab, "atomic_fetch_sub$I$a")
 OPTAB_D (atomic_fetch_xor_optab, "atomic_fetch_xor$I$a")
+OPTAB_D (atomic_fetch_smax_optab, "atomic_fetch_smax$I$a")
+OPTAB_D (atomic_fetch_smin_optab, "atomic_fetch_smin$I$a")
+OPTAB_D (atomic_smax_fetch_optab, "atomic_smax_fetch$I$a")
+OPTAB_D (atomic_smin_fetch_optab, "atomic_smin_fetch$I$a")
+OPTAB_D (atomic_smax_optab, "atomic_smax$I$a")
+OPTAB_D (atomic_smin_optab, "atomic_smin$I$a")
+OPTAB_D (atomic_fetch_umax_optab, "atomic_fetch_umax$I$a")
+OPTAB_D (atomic_fetch_umin_optab, "atomic_fetch_umin$I$a")
+OPTAB_D (atomic_umax_fetch_optab, "atomic_umax_fetch$I$a")
+OPTAB_D (atomic_umin_fetch_optab, "atomic_umin_fetch$I$a")
+OPTAB_D (atomic_umax_optab, "atomic_umax$I$a")
+OPTAB_D (atomic_umin_optab, "atomic_umin$I$a")
 OPTAB_D (atomic_load_optab, "atomic_load$I$a")
 OPTAB_D (atomic_nand_fetch_optab, "atomic_nand_fetch$I$a")
 OPTAB_D (atomic_nand_optab, "atomic_nand$I$a")
diff --git a/gcc/optabs.h b/gcc/optabs.h
index fe4152b34809..5ca032c9c728 100644
--- a/gcc/optabs.h
+++ b/gcc/optabs.h
@@ -365,6 +365,8 @@ rtx expand_atomic_load (rtx, rtx, enum memmodel);
 rtx expand_atomic_store (rtx, rtx, enum memmodel, bool);
 rtx expand_atomic_fetch_op (rtx, rtx, rtx, enum rtx_code, enum memmodel,
                              bool);
+rtx expand_atomic_fetch_op_no_fallback (rtx, rtx, rtx, enum rtx_code,
+                                       enum memmodel, bool);
 
 extern void expand_asm_reg_clobber_mem_blockage (HARD_REG_SET);
 
-- 
2.43.0

Reply via email to