From: Soumya AR <[email protected]>

This patch adds support for atomic min/max instructions offered by aarch64
under LSE.

The implementation provides three execution paths:

1. When LSE is available at compile time (-march=armv8.1-a or later):
   Emits inline LSE atomic min/max instructions (ldsmin, ldsmax, ldumin,
   ldumax).

2. When LSE availability is unknown at compile time (default):
   Uses outline atomics ie. calls to libgcc functions that perform runtime
   detection for LSE and dispatch to either LSE instructions or LL/SC
   sequences.

3. When outline atomics are explicitly disabled (-mno-outline-atomics) on
   non-LSE targets: Emits inline LL/SC (LDXR, STXR etc) sequences using
   conditional select instructions for min/max.

----

The atomic MD patterns use the r<const_atomic> constraint. const_atomic
entries for smin/smax/umin/umax are empty since LL/SC fallback uses
CSEL, which has no immediate form.

----

lse.S is responsible for emitting the appropriate LSE or non-LSE
sequence. For min/max on non-LSE systems, this is done using a
conditional select. There is, however, a unique case where systems with
the CSSC extension have native min/max instructions as well. In that
case, it would be preferable to emit the LL/SC sequence using the native
min/max instructions. But, this would only occur on targets with CSSC
but without LSE, which is quite improbable, and thus, I haven't added
special handling for the CSSC feature.

----

For the non-LSE inline path, atomic_fetch_<op> is implemented by an
LL/SC sequence that gets split via aarch64_split_atomic_op. That
function is extended to handle SMIN, SMAX, UMIN, UMAX.

We have to be careful about QI/HI modes here: LDXR zero-extends the
memory value into a full W register, and the comparison is done on the
full register.  The QI/HI operand, however, only has its low 8/16 bits
defined, so its upper bits can affect the comparison.  For unsigned
min/max we therefore zero-extend the operand to match the loaded value,
and for signed min/max we sign-extend both.

----

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:

        * config/aarch64/aarch64-protos.h (aarch64_ool_ldsmin_names)
        (aarch64_ool_ldsmax_names, aarch64_ool_ldumin_names)
        (aarch64_ool_ldumax_names): Declare.
        * config/aarch64/aarch64.cc (aarch64_ool_ldsmin_names)
        (aarch64_ool_ldsmax_names, aarch64_ool_ldumin_names)
        (aarch64_ool_ldumax_names): New.
        (aarch64_split_atomic_op): Handle SMIN, SMAX, UMIN and UMAX.
        * config/aarch64/atomics.md (atomic_<atomic_optab><mode>)
        (atomic_fetch_<atomic_optab><mode>): Use the outline min/max
        helpers when LSE is not available.
        (atomic_<atomic_optab>_fetch<mode>): Use a signed operation for
        SMIN and SMAX.
        * config/aarch64/iterators.md (UNSPECV_ATOMIC_LDOP_SMAX)
        (UNSPECV_ATOMIC_LDOP_SMIN, UNSPECV_ATOMIC_LDOP_UMAX)
        (UNSPECV_ATOMIC_LDOP_UMIN): New unspecv values.
        (atomic_op): Add smin, smax, umin and umax.
        (atomic_optab, atomic_op_operand, const_atomic): Handle them.
        (ATOMIC_LDOP, atomic_ldop, atomic_ldoptab): Add the min/max
        unspecs.

libgcc/ChangeLog:

        * config/aarch64/lse.S: Implement outline atomic min/max
        functions.
        * config/aarch64/t-lse: Add min/max function entries.

gcc/testsuite/ChangeLog:

        * gcc.target/aarch64/atomic-minmax-lse.c: New test.
        * gcc.target/aarch64/atomic-minmax-nolse.c: New test.
        * gcc.target/aarch64/atomic-minmax.c: New test.
        * gcc.target/aarch64/atomic-minmax.x: New file.

Signed-off-by: Soumya AR <[email protected]>
---
 gcc/config/aarch64/aarch64-protos.h           |   4 +
 gcc/config/aarch64/aarch64.cc                 |  66 ++++++
 gcc/config/aarch64/atomics.md                 |  54 ++++-
 gcc/config/aarch64/iterators.md               |  30 ++-
 .../gcc.target/aarch64/atomic-minmax-lse.c    | 122 +++++++++++
 .../gcc.target/aarch64/atomic-minmax-nolse.c  | 200 ++++++++++++++++++
 .../gcc.target/aarch64/atomic-minmax.c        | 128 +++++++++++
 .../gcc.target/aarch64/atomic-minmax.x        | 186 ++++++++++++++++
 libgcc/config/aarch64/lse.S                   |  65 +++++-
 libgcc/config/aarch64/t-lse                   |   3 +-
 10 files changed, 848 insertions(+), 10 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/aarch64/atomic-minmax-lse.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/atomic-minmax-nolse.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/atomic-minmax.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/atomic-minmax.x

diff --git a/gcc/config/aarch64/aarch64-protos.h 
b/gcc/config/aarch64/aarch64-protos.h
index bccdbffc194b..4db91b1db414 100644
--- a/gcc/config/aarch64/aarch64-protos.h
+++ b/gcc/config/aarch64/aarch64-protos.h
@@ -1288,6 +1288,10 @@ extern const atomic_ool_names aarch64_ool_ldadd_names;
 extern const atomic_ool_names aarch64_ool_ldset_names;
 extern const atomic_ool_names aarch64_ool_ldclr_names;
 extern const atomic_ool_names aarch64_ool_ldeor_names;
+extern const atomic_ool_names aarch64_ool_ldsmin_names;
+extern const atomic_ool_names aarch64_ool_ldsmax_names;
+extern const atomic_ool_names aarch64_ool_ldumin_names;
+extern const atomic_ool_names aarch64_ool_ldumax_names;
 
 tree aarch64_resolve_overloaded_builtin_general (location_t, tree, void *);
 
diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
index 5b1c78462a37..0757cfc221b4 100644
--- a/gcc/config/aarch64/aarch64.cc
+++ b/gcc/config/aarch64/aarch64.cc
@@ -27390,6 +27390,10 @@ const atomic_ool_names aarch64_ool_ldadd_names = { { 
DEF4(ldadd) } };
 const atomic_ool_names aarch64_ool_ldset_names = { { DEF4(ldset) } };
 const atomic_ool_names aarch64_ool_ldclr_names = { { DEF4(ldclr) } };
 const atomic_ool_names aarch64_ool_ldeor_names = { { DEF4(ldeor) } };
+const atomic_ool_names aarch64_ool_ldsmin_names = { { DEF4(ldsmin) } };
+const atomic_ool_names aarch64_ool_ldsmax_names = { { DEF4(ldsmax) } };
+const atomic_ool_names aarch64_ool_ldumin_names = { { DEF4(ldumin) } };
+const atomic_ool_names aarch64_ool_ldumax_names = { { DEF4(ldumax) } };
 
 #undef DEF0
 #undef DEF4
@@ -27642,6 +27646,68 @@ aarch64_split_atomic_op (enum rtx_code code, rtx 
old_out, rtx new_out, rtx mem,
       emit_insn (gen_rtx_SET (new_out, x));
       break;
 
+    case SMIN:
+    case SMAX:
+    case UMIN:
+    case UMAX:
+      {
+       rtx_code cmp_code;
+       switch (code)
+         {
+         case SMIN:
+           cmp_code = LT;
+           break;
+         case SMAX:
+           cmp_code = GT;
+           break;
+         case UMIN:
+           cmp_code = LTU;
+           break;
+         case UMAX:
+           cmp_code = GTU;
+           break;
+         default:
+           gcc_unreachable ();
+         }
+
+       if (mode == QImode || mode == HImode)
+         {
+           /* COND is free until stxr.  We can use it temporarily to sign/zero
+              extend and compare.  */
+           if (code == SMIN || code == SMAX)
+             {
+               rtx old_extended = gen_rtx_REG (wmode, REGNO (old_out));
+               emit_insn (gen_rtx_SET
+                          (old_extended,
+                           gen_rtx_SIGN_EXTEND
+                             (wmode, gen_lowpart (mode, old_out))));
+               old_out = old_extended;
+
+               rtx value_extended = cond;
+               emit_insn (gen_rtx_SET
+                          (value_extended,
+                           gen_rtx_SIGN_EXTEND
+                             (wmode, gen_lowpart (mode, value))));
+               value = value_extended;
+             }
+           else
+             {
+               /* The exclusive load already zero-extends OLD_OUT.  */
+               rtx value_extended = cond;
+               emit_insn (gen_rtx_SET
+                          (value_extended,
+                           gen_rtx_ZERO_EXTEND
+                             (wmode, gen_lowpart (mode, value))));
+               value = value_extended;
+             }
+         }
+       rtx cc_reg = aarch64_gen_compare_reg (cmp_code, old_out, value);
+       rtx cond = gen_rtx_fmt_ee (cmp_code, VOIDmode, cc_reg, const0_rtx);
+       x = gen_rtx_IF_THEN_ELSE (wmode, cond, old_out, value);
+       emit_insn (gen_rtx_SET (new_out, x));
+       break;
+      }
+
     case MINUS:
       if (CONST_INT_P (value))
        {
diff --git a/gcc/config/aarch64/atomics.md b/gcc/config/aarch64/atomics.md
index 87e955b713c7..1a2f5d876752 100644
--- a/gcc/config/aarch64/atomics.md
+++ b/gcc/config/aarch64/atomics.md
@@ -284,6 +284,18 @@
          case XOR:
            gen = gen_aarch64_atomic_xor<mode>_lse;
            break;
+         case SMAX:
+           gen = gen_aarch64_atomic_smax<mode>_lse;
+           break;
+         case SMIN:
+           gen = gen_aarch64_atomic_smin<mode>_lse;
+           break;
+         case UMAX:
+           gen = gen_aarch64_atomic_umax<mode>_lse;
+           break;
+         case UMIN:
+           gen = gen_aarch64_atomic_umin<mode>_lse;
+           break;
          case AND:
            operands[1] = expand_simple_unop (<MODE>mode, NOT, operands[1],
                                              NULL, 1);
@@ -317,6 +329,18 @@
                                              NULL, 1);
            names = &aarch64_ool_ldclr_names;
            break;
+         case SMIN:
+           names = &aarch64_ool_ldsmin_names;
+           break;
+         case SMAX:
+           names = &aarch64_ool_ldsmax_names;
+           break;
+         case UMIN:
+           names = &aarch64_ool_ldumin_names;
+           break;
+         case UMAX:
+           names = &aarch64_ool_ldumax_names;
+           break;
          default:
            gcc_unreachable ();
          }
@@ -442,6 +466,18 @@
        case XOR:
          gen = gen_aarch64_atomic_fetch_xor<mode>_lse;
          break;
+       case SMAX:
+         gen = gen_aarch64_atomic_fetch_smax<mode>_lse;
+         break;
+       case SMIN:
+         gen = gen_aarch64_atomic_fetch_smin<mode>_lse;
+         break;
+       case UMAX:
+         gen = gen_aarch64_atomic_fetch_umax<mode>_lse;
+         break;
+       case UMIN:
+         gen = gen_aarch64_atomic_fetch_umin<mode>_lse;
+         break;
        case AND:
          operands[2] = expand_simple_unop (<MODE>mode, NOT, operands[2],
                                            NULL, 1);
@@ -475,6 +511,18 @@
                                            NULL, 1);
          names = &aarch64_ool_ldclr_names;
          break;
+       case SMIN:
+         names = &aarch64_ool_ldsmin_names;
+         break;
+       case SMAX:
+         names = &aarch64_ool_ldsmax_names;
+         break;
+       case UMIN:
+         names = &aarch64_ool_ldumin_names;
+         break;
+       case UMAX:
+         names = &aarch64_ool_ldumax_names;
+         break;
        default:
          gcc_unreachable ();
        }
@@ -581,7 +629,11 @@
       operands[2] = force_reg (<MODE>mode, operands[2]);
       emit_insn (gen_atomic_fetch_<atomic_optab><mode>
                  (tmp, operands[1], operands[2], operands[3]));
-      tmp = expand_simple_binop (<MODE>mode, <CODE>, tmp, operands[2],
+      if (<CODE> == SMIN || <CODE> == SMAX)
+       tmp = expand_simple_binop (<MODE>mode, <CODE>, tmp, operands[2],
+                                operands[0], 0, OPTAB_WIDEN);
+      else
+       tmp = expand_simple_binop (<MODE>mode, <CODE>, tmp, operands[2],
                                 operands[0], 1, OPTAB_WIDEN);
       emit_move_insn (operands[0], tmp);
     }
diff --git a/gcc/config/aarch64/iterators.md b/gcc/config/aarch64/iterators.md
index f38a39fd6e36..10cada4a08bf 100644
--- a/gcc/config/aarch64/iterators.md
+++ b/gcc/config/aarch64/iterators.md
@@ -1502,6 +1502,10 @@
     UNSPECV_ATOMIC_LDOP_BIC    ; Represent an atomic load-bic
     UNSPECV_ATOMIC_LDOP_XOR    ; Represent an atomic load-xor
     UNSPECV_ATOMIC_LDOP_PLUS   ; Represent an atomic load-add
+    UNSPECV_ATOMIC_LDOP_SMAX   ; Represent an atomic load-smax
+    UNSPECV_ATOMIC_LDOP_SMIN   ; Represent an atomic load-smin
+    UNSPECV_ATOMIC_LDOP_UMAX   ; Represent an atomic load-umax
+    UNSPECV_ATOMIC_LDOP_UMIN   ; Represent an atomic load-umin
 ])
 
 ;; -------------------------------------------------------------------
@@ -3249,7 +3253,7 @@
 ;; Iterator for __sync_<op> operations that where the operation can be
 ;; represented directly RTL.  This is all of the sync operations bar
 ;; nand.
-(define_code_iterator atomic_op [plus minus ior xor and])
+(define_code_iterator atomic_op [plus minus ior xor and smin smax umin umax])
 
 ;; Iterator for integer conversions
 (define_code_iterator FIXUORS [fix unsigned_fix])
@@ -3569,21 +3573,27 @@
 
 ;; Atomic operations
 (define_code_attr atomic_optab
-  [(ior "or") (xor "xor") (and "and") (plus "add") (minus "sub")])
+  [(ior "or") (xor "xor") (and "and") (plus "add") (minus "sub")
+  (smin "smin") (smax "smax") (umin "umin") (umax "umax")])
 
 (define_code_attr atomic_op_operand
   [(ior "aarch64_logical_operand")
    (xor "aarch64_logical_operand")
    (and "aarch64_logical_operand")
    (plus "aarch64_plus_operand")
-   (minus "aarch64_plus_operand")])
+   (minus "aarch64_plus_operand")
+   (smin "aarch64_sminmax_operand")
+   (smax "aarch64_sminmax_operand")
+   (umin "aarch64_uminmax_operand")
+   (umax "aarch64_uminmax_operand")])
 
 ;; Constants acceptable for atomic operations.
 ;; This definition must appear in this file before the iterators it refers to.
 (define_code_attr const_atomic
  [(plus "IJ") (minus "IJ")
   (xor "<lconst_atomic>") (ior "<lconst_atomic>")
-  (and "<lconst_atomic>")])
+  (and "<lconst_atomic>")
+  (smin "") (smax "") (umin "") (umax "")])
 
 ;; Attribute to describe constants acceptable in atomic logical operations
 (define_mode_attr lconst_atomic [(QI "K") (HI "K") (SI "K") (DI "L")])
@@ -4477,7 +4487,9 @@
 
 (define_int_iterator ATOMIC_LDOP
  [UNSPECV_ATOMIC_LDOP_OR UNSPECV_ATOMIC_LDOP_BIC
-  UNSPECV_ATOMIC_LDOP_XOR UNSPECV_ATOMIC_LDOP_PLUS])
+  UNSPECV_ATOMIC_LDOP_XOR UNSPECV_ATOMIC_LDOP_PLUS
+  UNSPECV_ATOMIC_LDOP_SMAX UNSPECV_ATOMIC_LDOP_SMIN
+  UNSPECV_ATOMIC_LDOP_UMAX UNSPECV_ATOMIC_LDOP_UMIN])
 
 (define_int_iterator SUBDI_BITS [8 16 32])
 
@@ -5662,11 +5674,15 @@
 
 (define_int_attr atomic_ldop
  [(UNSPECV_ATOMIC_LDOP_OR "set") (UNSPECV_ATOMIC_LDOP_BIC "clr")
-  (UNSPECV_ATOMIC_LDOP_XOR "eor") (UNSPECV_ATOMIC_LDOP_PLUS "add")])
+  (UNSPECV_ATOMIC_LDOP_XOR "eor") (UNSPECV_ATOMIC_LDOP_PLUS "add")
+  (UNSPECV_ATOMIC_LDOP_SMAX "smax") (UNSPECV_ATOMIC_LDOP_SMIN "smin")
+  (UNSPECV_ATOMIC_LDOP_UMAX "umax") (UNSPECV_ATOMIC_LDOP_UMIN "umin")])
 
 (define_int_attr atomic_ldoptab
  [(UNSPECV_ATOMIC_LDOP_OR "ior") (UNSPECV_ATOMIC_LDOP_BIC "bic")
-  (UNSPECV_ATOMIC_LDOP_XOR "xor") (UNSPECV_ATOMIC_LDOP_PLUS "add")])
+  (UNSPECV_ATOMIC_LDOP_XOR "xor") (UNSPECV_ATOMIC_LDOP_PLUS "add")
+  (UNSPECV_ATOMIC_LDOP_SMAX "smax") (UNSPECV_ATOMIC_LDOP_SMIN "smin")
+  (UNSPECV_ATOMIC_LDOP_UMAX "umax") (UNSPECV_ATOMIC_LDOP_UMIN "umin")])
 
 (define_int_attr fp8_cvt_uns_op
   [(UNSPEC_F1CVT "f1cvt")
diff --git a/gcc/testsuite/gcc.target/aarch64/atomic-minmax-lse.c 
b/gcc/testsuite/gcc.target/aarch64/atomic-minmax-lse.c
new file mode 100644
index 000000000000..8cb5cbee7e79
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/atomic-minmax-lse.c
@@ -0,0 +1,122 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-march=armv8-a+lse" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include "atomic-minmax.x"
+
+/* { dg-final { scan-assembler-not "\tldxr" } } */
+/* { dg-final { scan-assembler-not "\tldaxr" } } */
+/* { dg-final { scan-assembler-not "\tstxr" } } */
+/* { dg-final { scan-assembler-not "\tstlxr" } } */
+
+/*
+** test_smin_s8:
+**     ...
+**     ldsminb w[0-9]+, w[0-9]+, \[x[0-9]+\]
+**     ...
+*/
+
+/*
+** test_smax_s8:
+**     ...
+**     ldsmaxlb        w[0-9]+, w[0-9]+, \[x[0-9]+\]
+**     ...
+*/
+
+/*
+** test_smin_s16:
+**     ...
+**     ldsminah        w[0-9]+, w[0-9]+, \[x[0-9]+\]
+**     ...
+*/
+
+/*
+** test_smax_s16:
+**     ...
+**     ldsmaxalh       w[0-9]+, w[0-9]+, \[x[0-9]+\]
+**     ...
+*/
+
+/*
+** test_smin_s32:
+**     ...
+**     ldsmin  w[0-9]+, w[0-9]+, \[x[0-9]+\]
+**     ...
+*/
+
+/*
+** test_smax_s32:
+**     ...
+**     ldsmaxal        w[0-9]+, w[0-9]+, \[x[0-9]+\]
+**     ...
+*/
+
+/*
+** test_smin_s64:
+**     ...
+**     ldsmina x[0-9]+, x[0-9]+, \[x[0-9]+\]
+**     ...
+*/
+
+/*
+** test_smax_s64:
+**     ...
+**     ldsmax  x[0-9]+, x[0-9]+, \[x[0-9]+\]
+**     ...
+*/
+
+/*
+** test_umin_u8:
+**     ...
+**     lduminb w[0-9]+, w[0-9]+, \[x[0-9]+\]
+**     ...
+*/
+
+/*
+** test_umax_u8:
+**     ...
+**     ldumaxab        w[0-9]+, w[0-9]+, \[x[0-9]+\]
+**     ...
+*/
+
+/*
+** test_umin_u16:
+**     ...
+**     lduminah        w[0-9]+, w[0-9]+, \[x[0-9]+\]
+**     ...
+*/
+
+/*
+** test_umax_u16:
+**     ...
+**     ldumaxlh        w[0-9]+, w[0-9]+, \[x[0-9]+\]
+**     ...
+*/
+
+/*
+** test_umin_u32:
+**     ...
+**     lduminal        w[0-9]+, w[0-9]+, \[x[0-9]+\]
+**     ...
+*/
+
+/*
+** test_umax_u32:
+**     ...
+**     ldumax  w[0-9]+, w[0-9]+, \[x[0-9]+\]
+**     ...
+*/
+
+/*
+** test_umin_u64:
+**     ...
+**     ldumin  x[0-9]+, x[0-9]+, \[x[0-9]+\]
+**     ...
+*/
+
+/*
+** test_umax_u64:
+**     ...
+**     ldumaxal        x[0-9]+, x[0-9]+, \[x[0-9]+\]
+**     ...
+*/
diff --git a/gcc/testsuite/gcc.target/aarch64/atomic-minmax-nolse.c 
b/gcc/testsuite/gcc.target/aarch64/atomic-minmax-nolse.c
new file mode 100644
index 000000000000..4c6dfc8c1665
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/atomic-minmax-nolse.c
@@ -0,0 +1,200 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-march=armv8-a+nolse -mno-outline-atomics" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include "atomic-minmax.x"
+
+/* { dg-final { scan-assembler-not "\tldsmin" } } */
+/* { dg-final { scan-assembler-not "\tldsmax" } } */
+/* { dg-final { scan-assembler-not "\tldumin" } } */
+/* { dg-final { scan-assembler-not "\tldumax" } } */
+
+/* { dg-final { scan-assembler-not "__aarch64_" } } */
+
+/*
+** test_smin_s8:
+**     ...
+**     ldxrb   w[0-9]+, \[x[0-9]+\]
+**     sxtb    w[0-9]+, w[0-9]+
+**     sxtb    w[0-9]+, w[0-9]+
+**     cmp     w[0-9]+, w[0-9]+
+**     csel    w[0-9]+, w[0-9]+, w[0-9]+, lt
+**     stxrb   w[0-9]+, w[0-9]+, \[x[0-9]+\]
+**     cbnz    w[0-9]+, .*
+**     ...
+*/
+
+/*
+** test_smax_s8:
+**     ...
+**     ldxrb   w[0-9]+, \[x[0-9]+\]
+**     sxtb    w[0-9]+, w[0-9]+
+**     sxtb    w[0-9]+, w[0-9]+
+**     cmp     w[0-9]+, w[0-9]+
+**     csel    w[0-9]+, w[0-9]+, w[0-9]+, gt
+**     stlxrb  w[0-9]+, w[0-9]+, \[x[0-9]+\]
+**     cbnz    w[0-9]+, .*
+**     ...
+*/
+
+/*
+** test_smin_s16:
+**     ...
+**     ldaxrh  w[0-9]+, \[x[0-9]+\]
+**     sxth    w[0-9]+, w[0-9]+
+**     sxth    w[0-9]+, w[0-9]+
+**     cmp     w[0-9]+, w[0-9]+
+**     csel    w[0-9]+, w[0-9]+, w[0-9]+, lt
+**     stxrh   w[0-9]+, w[0-9]+, \[x[0-9]+\]
+**     cbnz    w[0-9]+, .*
+**     ...
+*/
+
+/*
+** test_smax_s16:
+**     ...
+**     ldaxrh  w[0-9]+, \[x[0-9]+\]
+**     sxth    w[0-9]+, w[0-9]+
+**     sxth    w[0-9]+, w[0-9]+
+**     cmp     w[0-9]+, w[0-9]+
+**     csel    w[0-9]+, w[0-9]+, w[0-9]+, gt
+**     stlxrh  w[0-9]+, w[0-9]+, \[x[0-9]+\]
+**     cbnz    w[0-9]+, .*
+**     ...
+*/
+
+/*
+** test_smin_s32:
+**     ...
+**     ldxr    w[0-9]+, \[x[0-9]+\]
+**     cmp     w[0-9]+, w[0-9]+
+**     csel    w[0-9]+, w[0-9]+, w[0-9]+, lt
+**     stxr    w[0-9]+, w[0-9]+, \[x[0-9]+\]
+**     cbnz    w[0-9]+, .*
+**     ...
+*/
+
+/*
+** test_smax_s32:
+**     ...
+**     ldaxr   w[0-9]+, \[x[0-9]+\]
+**     cmp     w[0-9]+, w[0-9]+
+**     csel    w[0-9]+, w[0-9]+, w[0-9]+, gt
+**     stlxr   w[0-9]+, w[0-9]+, \[x[0-9]+\]
+**     cbnz    w[0-9]+, .*
+**     ...
+*/
+
+/*
+** test_smin_s64:
+**     ...
+**     ldaxr   x[0-9]+, \[x[0-9]+\]
+**     cmp     x[0-9]+, x[0-9]+
+**     csel    x[0-9]+, x[0-9]+, x[0-9]+, lt
+**     stxr    w[0-9]+, x[0-9]+, \[x[0-9]+\]
+**     cbnz    w[0-9]+, .*
+**     ...
+*/
+
+/*
+** test_smax_s64:
+**     ...
+**     ldxr    x[0-9]+, \[x[0-9]+\]
+**     cmp     x[0-9]+, x[0-9]+
+**     csel    x[0-9]+, x[0-9]+, x[0-9]+, gt
+**     stxr    w[0-9]+, x[0-9]+, \[x[0-9]+\]
+**     cbnz    w[0-9]+, .*
+**     ...
+*/
+
+/*
+** test_umin_u8:
+**     ...
+**     ldxrb   w[0-9]+, \[x[0-9]+\]
+**     and     w[0-9]+, w[0-9]+, 255
+**     cmp     w[0-9]+, w[0-9]+
+**     csel    w[0-9]+, w[0-9]+, w[0-9]+, cc
+**     stxrb   w[0-9]+, w[0-9]+, \[x[0-9]+\]
+**     cbnz    w[0-9]+, .*
+**     ...
+*/
+
+/*
+** test_umax_u8:
+**     ...
+**     ldaxrb  w[0-9]+, \[x[0-9]+\]
+**     and     w[0-9]+, w[0-9]+, 255
+**     cmp     w[0-9]+, w[0-9]+
+**     csel    w[0-9]+, w[0-9]+, w[0-9]+, hi
+**     stxrb   w[0-9]+, w[0-9]+, \[x[0-9]+\]
+**     cbnz    w[0-9]+, .*
+**     ...
+*/
+
+/*
+** test_umin_u16:
+**     ...
+**     ldaxrh  w[0-9]+, \[x[0-9]+\]
+**     and     w[0-9]+, w[0-9]+, 65535
+**     cmp     w[0-9]+, w[0-9]+
+**     csel    w[0-9]+, w[0-9]+, w[0-9]+, cc
+**     stxrh   w[0-9]+, w[0-9]+, \[x[0-9]+\]
+**     cbnz    w[0-9]+, .*
+**     ...
+*/
+
+/*
+** test_umax_u16:
+**     ...
+**     ldxrh   w[0-9]+, \[x[0-9]+\]
+**     and     w[0-9]+, w[0-9]+, 65535
+**     cmp     w[0-9]+, w[0-9]+
+**     csel    w[0-9]+, w[0-9]+, w[0-9]+, hi
+**     stlxrh  w[0-9]+, w[0-9]+, \[x[0-9]+\]
+**     cbnz    w[0-9]+, .*
+**     ...
+*/
+
+/*
+** test_umin_u32:
+**     ...
+**     ldaxr   w[0-9]+, \[x[0-9]+\]
+**     cmp     w[0-9]+, w[0-9]+
+**     csel    w[0-9]+, w[0-9]+, w[0-9]+, cc
+**     stlxr   w[0-9]+, w[0-9]+, \[x[0-9]+\]
+**     cbnz    w[0-9]+, .*
+**     ...
+*/
+
+/*
+** test_umax_u32:
+**     ...
+**     ldxr    w[0-9]+, \[x[0-9]+\]
+**     cmp     w[0-9]+, w[0-9]+
+**     csel    w[0-9]+, w[0-9]+, w[0-9]+, hi
+**     stxr    w[0-9]+, w[0-9]+, \[x[0-9]+\]
+**     cbnz    w[0-9]+, .*
+**     ...
+*/
+
+/*
+** test_umin_u64:
+**     ...
+**     ldxr    x[0-9]+, \[x[0-9]+\]
+**     cmp     x[0-9]+, x[0-9]+
+**     csel    x[0-9]+, x[0-9]+, x[0-9]+, cc
+**     stxr    w[0-9]+, x[0-9]+, \[x[0-9]+\]
+**     cbnz    w[0-9]+, .*
+**     ...
+*/
+
+/*
+** test_umax_u64:
+**     ...
+**     ldaxr   x[0-9]+, \[x[0-9]+\]
+**     cmp     x[0-9]+, x[0-9]+
+**     csel    x[0-9]+, x[0-9]+, x[0-9]+, hi
+**     stlxr   w[0-9]+, x[0-9]+, \[x[0-9]+\]
+**     cbnz    w[0-9]+, .*
+**     ...
+*/
diff --git a/gcc/testsuite/gcc.target/aarch64/atomic-minmax.c 
b/gcc/testsuite/gcc.target/aarch64/atomic-minmax.c
new file mode 100644
index 000000000000..e4d04e1fa79b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/atomic-minmax.c
@@ -0,0 +1,128 @@
+/* { dg-do run } */
+/* { dg-additional-options "--save-temps -march=armv8-a+nolse 
-moutline-atomics" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include "atomic-minmax.x"
+
+int main ()
+{
+  run_tests();
+  return 0;
+}
+
+/* { dg-final { scan-assembler-not "\tldsmin" } } */
+/* { dg-final { scan-assembler-not "\tldsmax" } } */
+/* { dg-final { scan-assembler-not "\tldumin" } } */
+/* { dg-final { scan-assembler-not "\tldumax" } } */
+
+/*
+** test_smin_s8:
+**     ...
+**     bl      __aarch64_ldsmin1_relax
+**     ...
+*/
+
+/*
+** test_smax_s8:
+**     ...
+**     bl      __aarch64_ldsmax1_rel
+**     ...
+*/
+
+/*
+** test_smin_s16:
+**     ...
+**     bl      __aarch64_ldsmin2_acq
+**     ...
+*/
+
+/*
+** test_smax_s16:
+**     ...
+**     bl      __aarch64_ldsmax2_acq_rel
+**     ...
+*/
+
+/*
+** test_smin_s32:
+**     ...
+**     bl      __aarch64_ldsmin4_relax
+**     ...
+*/
+
+/*
+** test_smax_s32:
+**     ...
+**     bl      __aarch64_ldsmax4_acq_rel
+**     ...
+*/
+
+/*
+** test_smin_s64:
+**     ...
+**     bl      __aarch64_ldsmin8_acq
+**     ...
+*/
+
+/*
+** test_smax_s64:
+**     ...
+**     bl      __aarch64_ldsmax8_relax
+**     ...     
+*/
+
+/*
+** test_umin_u8:
+**     ...
+**     bl      __aarch64_ldumin1_relax
+**     ...
+*/
+
+/*
+** test_umax_u8:
+**     ...
+**     bl      __aarch64_ldumax1_acq
+**     ...
+*/
+
+/*
+** test_umin_u16:
+**     ...
+**     bl      __aarch64_ldumin2_acq
+**     ...
+*/
+
+/*
+** test_umax_u16:
+**     ...
+**     bl      __aarch64_ldumax2_rel
+**     ...
+*/
+
+/*
+** test_umin_u32:
+**     ...
+**     bl      __aarch64_ldumin4_acq_rel
+**     ...
+*/
+
+/*
+** test_umax_u32:
+**     ...
+**     bl      __aarch64_ldumax4_relax
+**     ...
+*/
+
+/*
+** test_umin_u64:
+**     ...
+**     bl      __aarch64_ldumin8_relax
+**     ...
+*/
+
+/*
+** test_umax_u64:
+**     ...
+**     bl      __aarch64_ldumax8_acq_rel
+**     ...     
+*/
diff --git a/gcc/testsuite/gcc.target/aarch64/atomic-minmax.x 
b/gcc/testsuite/gcc.target/aarch64/atomic-minmax.x
new file mode 100644
index 000000000000..1113d3c4d8f4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/atomic-minmax.x
@@ -0,0 +1,186 @@
+#include <stdint.h>
+
+extern void abort (void);
+
+#define TEST_FETCH_OP(TYPE, VAR, OP, INIT, ARG, EXPECTED_OLD, EXPECTED_NEW,    
\
+                     MEM_ORDER)                                               \
+  do                                                                           
\
+    {                                                                          
\
+      TYPE old_val;                                                            
\
+      TYPE new_val;                                                            
\
+      VAR = INIT;                                                              
\
+      old_val = __atomic_fetch_##OP (&VAR, ARG, MEM_ORDER);                    
\
+      new_val = VAR;                                                           
\
+      if (old_val != EXPECTED_OLD)                                             
\
+       {                                                                      \
+         abort ();                                                            \
+       }                                                                      \
+      if (new_val != EXPECTED_NEW)                                             
\
+       {                                                                      \
+         abort ();                                                            \
+       }                                                                      \
+    }                                                                          
\
+  while (0)
+
+#define GEN_TYPE_TESTS(TYPE, VAR, SUFFIX, IS_SIGNED, HIGH_BIT)                 
\
+  TYPE VAR;                                                                    
\
+  void test_##SUFFIX ()                                                        
\
+  {                                                                            
\
+    TEST_FETCH_OP (TYPE, VAR, min, 10, 5, 10, 5, __ATOMIC_RELAXED);            
\
+    TEST_FETCH_OP (TYPE, VAR, min, 10, 20, 10, 10, __ATOMIC_SEQ_CST);          
\
+    TEST_FETCH_OP (TYPE, VAR, max, 10, 20, 10, 20, __ATOMIC_ACQUIRE);          
\
+    TEST_FETCH_OP (TYPE, VAR, max, 10, 5, 10, 10, __ATOMIC_RELEASE);           
\
+                                                                               
\
+    if (IS_SIGNED)                                                             
\
+      {                                                                        
\
+       TEST_FETCH_OP (TYPE, VAR, min, -10, -20, -10, -20, __ATOMIC_ACQUIRE);  \
+       TEST_FETCH_OP (TYPE, VAR, max, -10, 5, -10, 5, __ATOMIC_RELEASE);      \
+       TEST_FETCH_OP (TYPE, VAR, min, -5, -3, -5, -5, __ATOMIC_RELAXED);      \
+       TEST_FETCH_OP (TYPE, VAR, max, -20, -10, -20, -10, __ATOMIC_SEQ_CST);  \
+      }                                                                        
\
+    else                                                                       
\
+      {                                                                        
\
+       TEST_FETCH_OP (TYPE, VAR, min, HIGH_BIT, 1, HIGH_BIT, 1,               \
+                      __ATOMIC_ACQUIRE);                                      \
+       TEST_FETCH_OP (TYPE, VAR, max, HIGH_BIT, 1, HIGH_BIT, HIGH_BIT,        \
+                      __ATOMIC_RELEASE);                                      \
+       TEST_FETCH_OP (TYPE, VAR, min, 1, HIGH_BIT, 1, 1, __ATOMIC_RELAXED);   \
+       TEST_FETCH_OP (TYPE, VAR, max, 1, HIGH_BIT, 1, HIGH_BIT,               \
+                      __ATOMIC_SEQ_CST);                                      \
+      }                                                                        
\
+  }
+
+GEN_TYPE_TESTS (int8_t, s8_var_test, s8, 1, 0)
+GEN_TYPE_TESTS (int16_t, s16_var_test, s16, 1, 0)
+GEN_TYPE_TESTS (int32_t, s32_var_test, s32, 1, 0)
+GEN_TYPE_TESTS (int64_t, s64_var_test, s64, 1, 0)
+
+GEN_TYPE_TESTS (uint8_t, u8_var_test, u8, 0, UINT8_MAX)
+GEN_TYPE_TESTS (uint16_t, u16_var_test, u16, 0, UINT16_MAX)
+GEN_TYPE_TESTS (uint32_t, u32_var_test, u32, 0, UINT32_MAX)
+GEN_TYPE_TESTS (uint64_t, u64_var_test, u64, 0, UINT64_MAX)
+
+void
+run_tests ()
+{
+  test_s8 ();
+  test_s16 ();
+  test_s32 ();
+  test_s64 ();
+  test_u8 ();
+  test_u16 ();
+  test_u32 ();
+  test_u64 ();
+}
+
+int8_t s8_var = 0;
+
+int8_t
+test_smin_s8 (int8_t a)
+{
+  return __atomic_fetch_min (&s8_var, a, __ATOMIC_RELAXED);
+}
+
+int8_t
+test_smax_s8 (int8_t a)
+{
+  return __atomic_fetch_max (&s8_var, a, __ATOMIC_RELEASE);
+}
+
+int16_t s16_var = 0;
+
+int16_t
+test_smin_s16 (int16_t a)
+{
+  return __atomic_fetch_min (&s16_var, a, __ATOMIC_ACQUIRE);
+}
+
+int16_t
+test_smax_s16 (int16_t a)
+{
+  return __atomic_fetch_max (&s16_var, a, __ATOMIC_ACQ_REL);
+}
+
+int32_t s32_var = 0;
+
+int32_t
+test_smin_s32 (int32_t a)
+{
+  return __atomic_fetch_min (&s32_var, a, __ATOMIC_RELAXED);
+}
+
+int32_t
+test_smax_s32 (int32_t a)
+{
+  return __atomic_fetch_max (&s32_var, a, __ATOMIC_SEQ_CST);
+}
+
+int64_t s64_var = 0;
+
+int64_t
+test_smin_s64 (int64_t a)
+{
+  return __atomic_fetch_min (&s64_var, a, __ATOMIC_ACQUIRE);
+}
+
+int64_t
+test_smax_s64 (int64_t a)
+{
+  return __atomic_fetch_max (&s64_var, a, __ATOMIC_RELAXED);
+}
+
+uint8_t u8_var = 0;
+
+uint8_t
+test_umin_u8 (uint8_t a)
+{
+  return __atomic_fetch_min (&u8_var, a, __ATOMIC_RELAXED);
+}
+
+uint8_t
+test_umax_u8 (uint8_t a)
+{
+  return __atomic_fetch_max (&u8_var, a, __ATOMIC_CONSUME);
+}
+
+uint16_t u16_var = 0;
+
+uint16_t
+test_umin_u16 (uint16_t a)
+{
+  return __atomic_fetch_min (&u16_var, a, __ATOMIC_ACQUIRE);
+}
+
+uint16_t
+test_umax_u16 (uint16_t a)
+{
+  return __atomic_fetch_max (&u16_var, a, __ATOMIC_RELEASE);
+}
+
+uint32_t u32_var = 0;
+
+uint32_t
+test_umin_u32 (uint32_t a)
+{
+  return __atomic_fetch_min (&u32_var, a, __ATOMIC_ACQ_REL);
+}
+
+uint32_t
+test_umax_u32 (uint32_t a)
+{
+  return __atomic_fetch_max (&u32_var, a, __ATOMIC_RELAXED);
+}
+
+uint64_t u64_var = 0;
+
+uint64_t
+test_umin_u64 (uint64_t a)
+{
+  return __atomic_fetch_min (&u64_var, a, __ATOMIC_RELAXED);
+}
+
+uint64_t
+test_umax_u64 (uint64_t a)
+{
+  return __atomic_fetch_max (&u64_var, a, __ATOMIC_ACQ_REL);
+}
diff --git a/libgcc/config/aarch64/lse.S b/libgcc/config/aarch64/lse.S
index e31ffa415147..491500e9dbe7 100644
--- a/libgcc/config/aarch64/lse.S
+++ b/libgcc/config/aarch64/lse.S
@@ -276,7 +276,9 @@ ENDFN       NAME(swp)
 #endif
 
 #if defined(L_ldadd) || defined(L_ldclr) \
-    || defined(L_ldeor) || defined(L_ldset)
+    || defined(L_ldeor) || defined(L_ldset) \
+    || defined(L_ldsmin) || defined(L_ldsmax) \
+    || defined(L_ldumin) || defined(L_ldumax)
 
 #ifdef L_ldadd
 #define LDNM   ldadd
@@ -294,6 +296,26 @@ ENDFN      NAME(swp)
 #define LDNM   ldset
 #define OP     orr
 #define OPN    0x3000
+#elif defined(L_ldsmin)
+#define LDNM   ldsmin
+#define OP     smin
+#define OPN    0x5000
+#define IS_MINMAX 1
+#elif defined(L_ldsmax)
+#define LDNM   ldsmax
+#define OP     smax
+#define OPN    0x4000
+#define IS_MINMAX 1
+#elif defined(L_ldumin)
+#define LDNM   ldumin
+#define OP     umin
+#define OPN    0x7000
+#define IS_MINMAX 1
+#elif defined(L_ldumax)
+#define LDNM   ldumax
+#define OP     umax
+#define OPN    0x6000
+#define IS_MINMAX 1
 #else
 #error
 #endif
@@ -311,7 +333,48 @@ STARTFN    NAME(LDNM)
 
 8:     mov             s(tmp0), s(0)
 0:     LDXR            s(0), [x1]
+#ifdef IS_MINMAX
+       /* For min/max, extend if needed, compare, and select.  */
+#if SIZE < 4
+  #if defined(L_ldsmin) || defined(L_ldsmax)
+    /* Sign extend for signed comparisons.  */
+    #if SIZE == 1
+       sxtb            w(tmp1), w(0)
+       sxtb            w(tmp3), w(tmp0)
+    #else /* SIZE == 2 */
+       sxth            w(tmp1), w(0)
+       sxth            w(tmp3), w(tmp0)
+    #endif
+  #else /* L_ldumin || L_ldumax */
+    /* Zero extend the input value for unsigned comparisons.  The
+       exclusive load already zero-extends the old memory value.  */
+    #if SIZE == 1
+       uxtb            w(tmp3), w(tmp0)
+    #else /* SIZE == 2 */
+       uxth            w(tmp3), w(tmp0)
+    #endif
+  #endif
+  #if defined(L_ldumin) || defined(L_ldumax)
+       cmp             w(tmp3), w(0)
+  #else
+       cmp             w(tmp3), w(tmp1)
+  #endif
+#else /* SIZE >= 4 */
+       cmp             s(tmp0), s(0)
+#endif
+  /* Select based on condition.  */
+  #if defined(L_ldsmin)
+       csel            s(tmp1), s(tmp0), s(0), lt
+  #elif defined(L_ldsmax)
+       csel            s(tmp1), s(tmp0), s(0), gt
+  #elif defined(L_ldumin)
+       csel            s(tmp1), s(tmp0), s(0), lo
+  #elif defined(L_ldumax)
+       csel            s(tmp1), s(tmp0), s(0), hi
+  #endif
+#else /* Not IS_MINMAX */
        OP              s(tmp1), s(0), s(tmp0)
+#endif /* IS_MINMAX */
        STXR            w(tmp2), s(tmp1), [x1]
        cbnz            w(tmp2), 0b
        BARRIER
diff --git a/libgcc/config/aarch64/t-lse b/libgcc/config/aarch64/t-lse
index c58f003114f8..955243341b36 100644
--- a/libgcc/config/aarch64/t-lse
+++ b/libgcc/config/aarch64/t-lse
@@ -23,7 +23,8 @@ S0 := $(foreach s, 1 2 4 8 16, $(addsuffix _$(s), cas))
 O0 := $(foreach m, 1 2 3 4 5, $(addsuffix _$(m)$(objext), $(S0)))
 
 # Swap, Load-and-operate have 4 sizes and 5 memory models
-S1 := $(foreach s, 1 2 4 8, $(addsuffix _$(s), swp ldadd ldclr ldeor ldset))
+S1 := $(foreach s, 1 2 4 8, $(addsuffix _$(s), swp ldadd ldclr ldeor ldset \
+                             ldsmin ldsmax ldumin ldumax))
 O1 := $(foreach m, 1 2 3 4 5, $(addsuffix _$(m)$(objext), $(S1)))
 
 LSE_OBJS := $(O0) $(O1)
-- 
2.43.0

Reply via email to