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