[gcc r16-6573] s390: Remove volatile check from constraints A[QRST]

Stefan Schulze Frielinghaus stefansf@gcc.gnu.org
Thu Jan 8 10:23:17 GMT 2026


https://gcc.gnu.org/g:8ee36c421829a12858f1333008e5df2b52146aff

commit r16-6573-g8ee36c421829a12858f1333008e5df2b52146aff
Author: Stefan Schulze Frielinghaus <stefansf@gcc.gnu.org>
Date:   Thu Jan 8 11:20:54 2026 +0100

    s390: Remove volatile check from constraints A[QRST]
    
    Logical operations like *x &= -10 may be folded to a single
    storage-and-immediate instruction NI which accesses only the least
    significant byte of *x.
    
    Similarly but still distinct operations like *x &= *y may be implemented
    via storage-and-storage instruction NC which loads and stores one byte
    after another of operands.
    
    Since volatile objects must be accessed by a single load/store of the
    entire object, those optimizations must be rejected in case of volatile
    memory operands.  An exception to this are 16-byte load/stores which are
    implemented by two operations (in case of non-atomic operands).
    
    Previously, multi-letter constraints A[QRST] were intended to reject
    volatile memory operands.  However, during LRA, if a memory constraint
    is not satisfiable, as a last resort, LRA tries reloading the address.
    This, of course, doesn't fix the issue and during checking we finally
    bail out in case of a winning alternative.
    
    Fixed by enforcing non-volatile memory operands via conditions of
    instruction patterns which is done in s390_logical_operator_ok_p() for
    all AND/IOR/XOR instructions by this patch.  By removing the volatile
    check in constraints A[QRST] this fixes tests
    gcc.dg/torture/float128-basic.c, float64x-basic.c,
    fp-int-convert-float128-ieee.c, fp-int-convert-float64x.c,
    fp-int-convert-long-double.c which started failing after r16-5947.
    
    gcc/ChangeLog:
    
            * config/s390/s390.cc (s390_logical_operator_ok_p): Test for
            volatile memory.
            (s390_mem_constraint): Remove volatile condition.
            * config/s390/s390.md (*andc_split_<mode>): Test for volatile
            memory.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.target/s390/narrow-logical-op-1.c: New test.

Diff:
---
 gcc/config/s390/s390.cc                            |  18 +-
 gcc/config/s390/s390.md                            |   2 +-
 .../gcc.target/s390/narrow-logical-op-1.c          | 497 +++++++++++++++++++++
 3 files changed, 512 insertions(+), 5 deletions(-)

diff --git a/gcc/config/s390/s390.cc b/gcc/config/s390/s390.cc
index 88827ca6170a..28a025be4e8d 100644
--- a/gcc/config/s390/s390.cc
+++ b/gcc/config/s390/s390.cc
@@ -3054,9 +3054,19 @@ s390_logical_operator_ok_p (rtx *operands)
   /* If the destination operand is in memory, it needs to coincide
      with one of the source operands.  After reload, it has to be
      the first source operand.  */
-  if (GET_CODE (operands[0]) == MEM)
-    return rtx_equal_p (operands[0], operands[1])
-	   || (!reload_completed && rtx_equal_p (operands[0], operands[2]));
+  if (MEM_P (operands[0]))
+    {
+      /* Volatile loads/stores must be implemented via a single access of the
+	 entire object.  Therefore, do not fold operations into instructions
+	 like NI or NC.  */
+      if (GET_MODE (operands[0]) != QImode
+	  && (MEM_VOLATILE_P (operands[0])
+	      || (MEM_P (operands[1]) && MEM_VOLATILE_P (operands[1]))
+	      || (MEM_P (operands[2]) && MEM_VOLATILE_P (operands[2]))))
+	return false;
+      return rtx_equal_p (operands[0], operands[1])
+	     || (!reload_completed && rtx_equal_p (operands[0], operands[2]));
+    }
 
   return true;
 }
@@ -3700,7 +3710,7 @@ s390_mem_constraint (const char *str, rtx op)
     {
     case 'A':
       /* Check for offsettable variants of memory constraints.  */
-      if (!MEM_P (op) || MEM_VOLATILE_P (op))
+      if (!MEM_P (op))
 	return 0;
       if ((reload_completed || reload_in_progress)
 	  ? !offsettable_memref_p (op) : !offsettable_nonstrict_memref_p (op))
diff --git a/gcc/config/s390/s390.md b/gcc/config/s390/s390.md
index c6fa6db3ecae..21730950edeb 100644
--- a/gcc/config/s390/s390.md
+++ b/gcc/config/s390/s390.md
@@ -8345,7 +8345,7 @@
    && (GET_CODE (operands[0]) != MEM
       /* Ensure that s390_logical_operator_ok_p will succeed even
 	 on the split xor if (b & a) is stored into a pseudo.  */
-       || rtx_equal_p (operands[0], operands[2]))"
+       || (rtx_equal_p (operands[0], operands[2]) && !MEM_VOLATILE_P (operands[0])))"
   "#"
   "&& 1"
   [
diff --git a/gcc/testsuite/gcc.target/s390/narrow-logical-op-1.c b/gcc/testsuite/gcc.target/s390/narrow-logical-op-1.c
new file mode 100644
index 000000000000..d69509f6ad10
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/narrow-logical-op-1.c
@@ -0,0 +1,497 @@
+/* { dg-do compile { target lp64 } } */
+/* { dg-options "-O2 -march=z9-109" } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+
+
+/************************************
+ * Storage-and-Immediate Operations *
+ ************************************/
+
+
+
+/*
+** and_char:
+**	ni	0\(%r2\),246
+**	br	%r14
+*/
+void and_char (char *x) { *x &= -10; }
+
+/*
+** and_char_v:
+**	ni	0\(%r2\),246
+**	br	%r14
+*/
+void and_char_v (volatile char *x) { *x &= -10; }
+
+/*
+** and_short:
+**	ni	1\(%r2\),246
+**	br	%r14
+*/
+void and_short (short *x) { *x &= -10; }
+
+/*
+** and_short_v:
+**	lh	(%r[0-9]+),0\(%r2\)
+**	nill	\1,65526
+**	sth	\1,0\(%r2\)
+**	br	%r14
+*/
+void and_short_v (volatile short *x) { *x &= -10; }
+
+/*
+** and_int:
+**	ni	3\(%r2\),246
+**	br	%r14
+*/
+void and_int (int *x) { *x &= -10; }
+
+/*
+** and_int_v:
+**	l	(%r[0-9]+),0\(%r2\)
+**	nill	\1,65526
+**	st	\1,0\(%r2\)
+**	br	%r14
+*/
+void and_int_v (volatile int *x) { *x &= -10; }
+
+/*
+** and_long:
+**	ni	7\(%r2\),246
+**	br	%r14
+*/
+void and_long (long *x) { *x &= -10; }
+
+/*
+** and_long_v:
+**	lg	(%r[0-9]+),0\(%r2\)
+**	nill	\1,65526
+**	stg	\1,0\(%r2\)
+**	br	%r14
+*/
+void and_long_v (volatile long *x) { *x &= -10; }
+
+/*
+** ior_char:
+**	oi	0\(%r2\),10
+**	br	%r14
+*/
+void ior_char (char *x) { *x |= 10; }
+
+/*
+** ior_char_v:
+**	oi	0\(%r2\),10
+**	br	%r14
+*/
+void ior_char_v (volatile char *x) { *x |= 10; }
+
+/*
+** ior_short:
+**	oi	1\(%r2\),10
+**	br	%r14
+*/
+void ior_short (short *x) { *x |= 10; }
+
+/*
+** ior_short_v:
+**	lh	(%r[0-9]+),0\(%r2\)
+**	oill	\1,10
+**	sth	\1,0\(%r2\)
+**	br	%r14
+*/
+void ior_short_v (volatile short *x) { *x |= 10; }
+
+/*
+** ior_int:
+**	oi	3\(%r2\),10
+**	br	%r14
+*/
+void ior_int (int *x) { *x |= 10; }
+
+/*
+** ior_int_v:
+**	l	(%r[0-9]+),0\(%r2\)
+**	oill	\1,10
+**	st	\1,0\(%r2\)
+**	br	%r14
+*/
+void ior_int_v (volatile int *x) { *x |= 10; }
+
+/*
+** ior_long:
+**	oi	7\(%r2\),10
+**	br	%r14
+*/
+void ior_long (long *x) { *x |= 10; }
+
+/*
+** ior_long_v:
+**	lg	(%r[0-9]+),0\(%r2\)
+**	oill	\1,10
+**	stg	\1,0\(%r2\)
+**	br	%r14
+*/
+void ior_long_v (volatile long *x) { *x |= 10; }
+
+/*
+** xor_char:
+**	xi	0\(%r2\),10
+**	br	%r14
+*/
+void xor_char (char *x) { *x ^= 10; }
+
+/*
+** xor_char_v:
+**	xi	0\(%r2\),10
+**	br	%r14
+*/
+void xor_char_v (volatile char *x) { *x ^= 10; }
+
+/*
+** xor_short:
+**	xi	1\(%r2\),10
+**	br	%r14
+*/
+void xor_short (short *x) { *x ^= 10; }
+
+/*
+** xor_short_v:
+**	lh	(%r[0-9]+),0\(%r2\)
+**	xilf	\1,10
+**	sth	\1,0\(%r2\)
+**	br	%r14
+*/
+void xor_short_v (volatile short *x) { *x ^= 10; }
+
+/*
+** xor_int:
+**	xi	3\(%r2\),10
+**	br	%r14
+*/
+void xor_int (int *x) { *x ^= 10; }
+
+/*
+** xor_int_v:
+**	l	(%r[0-9]+),0\(%r2\)
+**	xilf	\1,10
+**	st	\1,0\(%r2\)
+**	br	%r14
+*/
+void xor_int_v (volatile int *x) { *x ^= 10; }
+
+/*
+** xor_long:
+**	xi	7\(%r2\),10
+**	br	%r14
+*/
+void xor_long (long *x) { *x ^= 10; }
+
+/*
+** xor_long_v:
+**	lg	(%r[0-9]+),0\(%r2\)
+**	xilf	\1,10
+**	stg	\1,0\(%r2\)
+**	br	%r14
+*/
+void xor_long_v (volatile long *x) { *x ^= 10; }
+
+
+
+/**********************************
+ * Storage-and-Storage Operations *
+ **********************************/
+
+/* <OP>_<TYPE>_v0 zero volatile operands
+   <OP>_<TYPE>_v1 first operand is volatile
+   <OP>_<TYPE>_v2 second operand is volatile */
+
+
+
+/*
+** and_char_v0:
+**	nc	0\(1,%r2\),0\(%r3\)
+**	br	%r14
+*/
+void and_char_v0 (char *x, char *y) { *x &= *y; }
+
+/*
+** and_char_v1:
+**	nc	0\(1,%r2\),0\(%r3\)
+**	br	%r14
+*/
+void and_char_v1 (volatile char *x, char *y) { *x &= *y; }
+
+/*
+** and_char_v2:
+**	nc	0\(1,%r2\),0\(%r3\)
+**	br	%r14
+*/
+void and_char_v2 (char *x, volatile char *y) { *x &= *y; }
+
+/*
+** and_short_v0:
+**	nc	0\(2,%r2\),0\(%r3\)
+**	br	%r14
+*/
+void and_short_v0 (short *x, short *y) { *x &= *y; }
+
+/*
+** and_short_v1:
+**	lh	%r[0-9]+,0\(%r[23]\)
+**	lh	%r[0-9]+,0\(%r[23]\)
+**	nr	(%r[0-9]+),%r[0-9]+
+**	sth	\1,0\(%r2\)
+**	br	%r14
+*/
+void and_short_v1 (volatile short *x, short *y) { *x &= *y; }
+
+/*
+** and_short_v2:
+**	...
+**	nc	0\(2,%r2\),166\(%r15\)
+**	...
+*/
+void and_short_v2 (short *x, volatile short *y) { *x &= *y; }
+
+/*
+** and_int_v0:
+**	nc	0\(4,%r2\),0\(%r3\)
+**	br	%r14
+*/
+void and_int_v0 (int *x, int *y) { *x &= *y; }
+
+/*
+** and_int_v1:
+**	l	(%r[0-9]+),0\(%r[23]\)
+**	n	\1,0\(%r[23]\)
+**	st	\1,0\(%r[23]\)
+**	br	%r14
+*/
+void and_int_v1 (volatile int *x, int *y) { *x &= *y; }
+
+/*
+** and_int_v2:
+**	l	(%r[0-9]+),0\(%r[23]\)
+**	n	\1,0\(%r[23]\)
+**	st	\1,0\(%r[23]\)
+**	br	%r14
+*/
+void and_int_v2 (int *x, volatile int *y) { *x &= *y; }
+
+/*
+** and_long_v0:
+**	nc	0\(8,%r2\),0\(%r3\)
+**	br	%r14
+*/
+void and_long_v0 (long *x, long *y) { *x &= *y; }
+
+/*
+** and_long_v1:
+**	lg	(%r[0-9]+),0\(%r[23]\)
+**	ng	\1,0\(%r[23]\)
+**	stg	\1,0\(%r[23]\)
+**	br	%r14
+*/
+void and_long_v1 (volatile long *x, long *y) { *x &= *y; }
+
+/*
+** and_long_v2:
+**	lg	(%r[0-9]+),0\(%r[23]\)
+**	ng	\1,0\(%r[23]\)
+**	stg	\1,0\(%r[23]\)
+**	br	%r14
+*/
+void and_long_v2 (long *x, volatile long *y) { *x &= *y; }
+
+/*
+** ior_char_v0:
+**	oc	0\(1,%r2\),0\(%r3\)
+**	br	%r14
+*/
+void ior_char_v0 (char *x, char *y) { *x |= *y; }
+
+/*
+** ior_char_v1:
+**	oc	0\(1,%r2\),0\(%r3\)
+**	br	%r14
+*/
+void ior_char_v1 (volatile char *x, char *y) { *x |= *y; }
+
+/*
+** ior_char_v2:
+**	oc	0\(1,%r2\),0\(%r3\)
+**	br	%r14
+*/
+void ior_char_v2 (char *x, volatile char *y) { *x |= *y; }
+
+/*
+** ior_short_v0:
+**	oc	0\(2,%r2\),0\(%r3\)
+**	br	%r14
+*/
+void ior_short_v0 (short *x, short *y) { *x |= *y; }
+
+/*
+** ior_short_v1:
+**	lh	%r[0-9]+,0\(%r[23]\)
+**	lh	%r[0-9]+,0\(%r[23]\)
+**	or	(%r[0-9]+),%r[0-9]+
+**	sth	\1,0\(%r2\)
+**	br	%r14
+*/
+void ior_short_v1 (volatile short *x, short *y) { *x |= *y; }
+
+/*
+** ior_short_v2:
+**	...
+**	oc	0\(2,%r2\),166\(%r15\)
+**	...
+*/
+void ior_short_v2 (short *x, volatile short *y) { *x |= *y; }
+
+/*
+** ior_int_v0:
+**	oc	0\(4,%r2\),0\(%r3\)
+**	br	%r14
+*/
+void ior_int_v0 (int *x, int *y) { *x |= *y; }
+
+/*
+** ior_int_v1:
+**	l	(%r[0-9]+),0\(%r[23]\)
+**	o	\1,0\(%r[23]\)
+**	st	\1,0\(%r[23]\)
+**	br	%r14
+*/
+void ior_int_v1 (volatile int *x, int *y) { *x |= *y; }
+
+/*
+** ior_int_v2:
+**	l	(%r[0-9]+),0\(%r[23]\)
+**	o	\1,0\(%r[23]\)
+**	st	\1,0\(%r[23]\)
+**	br	%r14
+*/
+void ior_int_v2 (int *x, volatile int *y) { *x |= *y; }
+
+/*
+** ior_long_v0:
+**	oc	0\(8,%r2\),0\(%r3\)
+**	br	%r14
+*/
+void ior_long_v0 (long *x, long *y) { *x |= *y; }
+
+/*
+** ior_long_v1:
+**	lg	(%r[0-9]+),0\(%r[23]\)
+**	og	\1,0\(%r[23]\)
+**	stg	\1,0\(%r[23]\)
+**	br	%r14
+*/
+void ior_long_v1 (volatile long *x, long *y) { *x |= *y; }
+
+/*
+** ior_long_v2:
+**	lg	(%r[0-9]+),0\(%r[23]\)
+**	og	\1,0\(%r[23]\)
+**	stg	\1,0\(%r[23]\)
+**	br	%r14
+*/
+void ior_long_v2 (long *x, volatile long *y) { *x |= *y; }
+
+/*
+** xor_char_v0:
+**	xc	0\(1,%r2\),0\(%r3\)
+**	br	%r14
+*/
+void xor_char_v0 (char *x, char *y) { *x ^= *y; }
+
+/*
+** xor_char_v1:
+**	xc	0\(1,%r2\),0\(%r3\)
+**	br	%r14
+*/
+void xor_char_v1 (volatile char *x, char *y) { *x ^= *y; }
+
+/*
+** xor_char_v2:
+**	xc	0\(1,%r2\),0\(%r3\)
+**	br	%r14
+*/
+void xor_char_v2 (char *x, volatile char *y) { *x ^= *y; }
+
+/*
+** xor_short_v0:
+**	xc	0\(2,%r2\),0\(%r3\)
+**	br	%r14
+*/
+void xor_short_v0 (short *x, short *y) { *x ^= *y; }
+
+/*
+** xor_short_v1:
+**	lh	%r[0-9]+,0\(%r[23]\)
+**	lh	%r[0-9]+,0\(%r[23]\)
+**	xr	(%r[0-9]+),%r[0-9]+
+**	sth	\1,0\(%r2\)
+**	br	%r14
+*/
+void xor_short_v1 (volatile short *x, short *y) { *x ^= *y; }
+
+/*
+** xor_short_v2:
+**	...
+**	xc	0\(2,%r2\),166\(%r15\)
+**	...
+*/
+void xor_short_v2 (short *x, volatile short *y) { *x ^= *y; }
+
+/*
+** xor_int_v0:
+**	xc	0\(4,%r2\),0\(%r3\)
+**	br	%r14
+*/
+void xor_int_v0 (int *x, int *y) { *x ^= *y; }
+
+/*
+** xor_int_v1:
+**	l	(%r[0-9]+),0\(%r[23]\)
+**	x	\1,0\(%r[23]\)
+**	st	\1,0\(%r[23]\)
+**	br	%r14
+*/
+void xor_int_v1 (volatile int *x, int *y) { *x ^= *y; }
+
+/*
+** xor_int_v2:
+**	l	(%r[0-9]+),0\(%r[23]\)
+**	x	\1,0\(%r[23]\)
+**	st	\1,0\(%r[23]\)
+**	br	%r14
+*/
+void xor_int_v2 (int *x, volatile int *y) { *x ^= *y; }
+
+/*
+** xor_long_v0:
+**	xc	0\(8,%r2\),0\(%r3\)
+**	br	%r14
+*/
+void xor_long_v0 (long *x, long *y) { *x ^= *y; }
+
+/*
+** xor_long_v1:
+**	lg	(%r[0-9]+),0\(%r[23]\)
+**	xg	\1,0\(%r[23]\)
+**	stg	\1,0\(%r[23]\)
+**	br	%r14
+*/
+void xor_long_v1 (volatile long *x, long *y) { *x ^= *y; }
+
+/*
+** xor_long_v2:
+**	lg	(%r[0-9]+),0\(%r[23]\)
+**	xg	\1,0\(%r[23]\)
+**	stg	\1,0\(%r[23]\)
+**	br	%r14
+*/
+void xor_long_v2 (long *x, volatile long *y) { *x ^= *y; }


More information about the Gcc-cvs mailing list