[PATCH] rs6000: Fix type size checks for __builtin_ppc_atomic_cas_local [PR126513]

Avinash Jayakar <[email protected]> Tue, 4 Aug 2026 14:32:21 +0530
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
Hi Michael/Jakub,

Below patch is to fix PR126513. Bootstrapped and regtested on
powerpc64le-linux-gnu. Ok for trunk and affected branches (16 and 15)?

I would need some advice on whether we need to fix it for 16.2 release as well.
Although the issue reported in PR126513 is a negative test where compiler must
report error, but currently ICE happens. While testing it I see that case c4
mentioned in the commit message was also not handled. This is a positive test
where compiler must produce output, but ICEs and I think this is a bit more
higher severity issue. So do you think this should go in 16.2?

Thanks and regards,
Avinash Jayakar

Type checks for the first 3 operands of the
__builtin_ppc_atomic_cas_local were missed with its initial
implementation. This patch adds the following checks
c1. Make sure the size of first 3 operands is the same.
c2. Do not allow function pointers as arguments.
c3. Report error when -mno-quad-memory-atomic is used but 128 bit
operands are used as the first 3 operands.
c4. A separate issue was when no lhs was assigned to this builtin, ICE would
happen as a result of emit_cmp_and_jump_insns function dereferencing the
target rtx which would be null. Added the check in rs6000_expand_builtin
to allocate a reg_rtx if target is null or const0_rtx.

Tests have been added to check all the implemented checks described
above.
1. pr126513-acmp-tst-1.c: Checks c1 and c2.
2. pr126513-acmp-tst-2.c: Checks c3.
3. pr126513-acmp-tst-3.c: Checks the ICE in c4.

2026-08-04  Avinash Jayakar  <[email protected]>

gcc/ChangeLog:
        PR target/126513
        * config/rs6000/rs6000-builtin.cc (rs6000_expand_builtin): Allocate reg
	if target is null or const0_rtx.
        * config/rs6000/rs6000-c.cc (altivec_resolve_overloaded_builtin): Add
	error handling for c1, c2 and c3 described in this commit.

gcc/testsuite/ChangeLog:
        PR target/126513
        * gcc.target/powerpc/pr126513-acmp-tst-1.c: New test.
        * gcc.target/powerpc/pr126513-acmp-tst-2.c: New test.
        * gcc.target/powerpc/pr126513-acmp-tst-3.c: New test.
---
 gcc/config/rs6000/rs6000-builtin.cc           |   4 +
 gcc/config/rs6000/rs6000-c.cc                 |  79 +++++---
 .../gcc.target/powerpc/pr126513-acmp-tst-1.c  |  25 +++
 .../gcc.target/powerpc/pr126513-acmp-tst-2.c  |  15 ++
 .../gcc.target/powerpc/pr126513-acmp-tst-3.c  | 176 ++++++++++++++++++
 5 files changed, 270 insertions(+), 29 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/powerpc/pr126513-acmp-tst-1.c
 create mode 100644 gcc/testsuite/gcc.target/powerpc/pr126513-acmp-tst-2.c
 create mode 100644 gcc/testsuite/gcc.target/powerpc/pr126513-acmp-tst-3.c

diff --git a/gcc/config/rs6000/rs6000-builtin.cc b/gcc/config/rs6000/rs6000-builtin.cc
index d8eef2b65fe..ad6f1fc09f4 100644
--- a/gcc/config/rs6000/rs6000-builtin.cc
+++ b/gcc/config/rs6000/rs6000-builtin.cc
@@ -3504,6 +3504,10 @@ rs6000_expand_builtin (tree exp, rtx target, rtx /* subtarget */,
 
       // 0: Boolean return (Output)
       struct expand_operand ops[8];
+      // Make sure we always have a place for bool operand.
+      if (target == const0_rtx || !target)
+	target = gen_reg_rtx (SImode);
+
       create_output_operand (&ops[0], target, SImode);
 
       // 1: Old value return (Output)
diff --git a/gcc/config/rs6000/rs6000-c.cc b/gcc/config/rs6000/rs6000-c.cc
index 3cbdb6fb2ba..4984ec1c35a 100644
--- a/gcc/config/rs6000/rs6000-c.cc
+++ b/gcc/config/rs6000/rs6000-c.cc
@@ -1734,41 +1734,55 @@ altivec_resolve_overloaded_builtin (location_t loc, tree fndecl,
 	    error_at (loc, "%qE requires 6 arguments", fndecl);
 	  return error_mark_node;
 	}
+      unsigned HOST_WIDE_INT size;
+      for (int i=0; i<3; i++) {
+	/* Get the first argument to determine the actual type.  */
+	tree arg0 = (*arglist)[i];
+	tree type0 = TREE_TYPE (arg0);
+
+	/* Must be a pointer.  */
+	if (!POINTER_TYPE_P (type0))
+	  {
+	    if (complain)
+	      error_at (loc, "argument %d to %qE must be a pointer",
+			i + 1, fndecl);
+	    return error_mark_node;
+	  }
 
-      /* Get the first argument to determine the actual type.  */
-      tree arg0 = (*arglist)[0];
-      tree type0 = TREE_TYPE (arg0);
-
-      /* Must be a pointer.  */
-      if (!POINTER_TYPE_P (type0))
-	{
-	  if (complain)
-	    error_at (loc, "first argument to %qE must be a pointer", fndecl);
-	  return error_mark_node;
-	}
-
-      /* Get the pointee type.  */
-      tree pointee_type = TREE_TYPE (type0);
+	/* Get the pointee type.  */
+	tree pointee_type = TREE_TYPE (type0);
 
-      /* Must be a complete type.  */
-      if (!COMPLETE_TYPE_P (pointee_type))
-	{
-	  if (complain)
-	    error_at (loc, "first argument to %qE must point to a complete"
-		      " type", fndecl);
-	  return error_mark_node;
-	}
+	/* Must be a complete type.  */
+	if (!COMPLETE_TYPE_P (pointee_type))
+	  {
+	    if (complain)
+	      error_at (loc, "argument %d to %qE must point to a complete"
+			" type", i + 1, fndecl);
+	    return error_mark_node;
+	  }
+	if (FUNCTION_POINTER_TYPE_P (type0))
+	  {
+	    if (complain)
+	      error_at (loc, "argument %d to %qE must not be a pointer to a"
+			" function", i + 1, fndecl);
+	    return error_mark_node;
+	  }
 
-      /* Get size in bytes.  */
-      tree size_tree = TYPE_SIZE_UNIT (pointee_type);
-      if (!tree_fits_uhwi_p (size_tree))
-	{
+	/* Get size in bytes.  */
+	tree size_tree = TYPE_SIZE_UNIT (pointee_type);
+	if (!tree_fits_uhwi_p (size_tree))
+	  {
+		if (complain)
+		error_at (loc, "type size must be constant");
+		return error_mark_node;
+	  }
+	if (i > 0 && size != tree_to_uhwi (size_tree)) {
 	  if (complain)
-	    error_at (loc, "type size must be constant");
+	    error_at(loc, "size mismatch in argument %d", i + 1);
 	  return error_mark_node;
 	}
-
-      unsigned HOST_WIDE_INT size = tree_to_uhwi (size_tree);
+	size = tree_to_uhwi (size_tree);
+      }
 
       /* Determine which size-specific builtin to use.  */
       rs6000_gen_builtins target_fcode;
@@ -1793,6 +1807,13 @@ altivec_resolve_overloaded_builtin (location_t loc, tree fndecl,
 	  int_type = long_long_unsigned_type_node;
 	  break;
 	case 16:
+	  if (!TARGET_QUAD_MEMORY_ATOMIC)
+	    {
+	      if (complain)
+		error_at (loc, "%qE requires the %qs option for 16-byte operands",
+			  fndecl, "-mquad-memory-atomic");
+	      return error_mark_node;
+	    }
 	  target_fcode = RS6000_BIF_PPC_ATOMIC_CAS_TI;
 	  int_type = unsigned_intTI_type_node;
 	  break;
diff --git a/gcc/testsuite/gcc.target/powerpc/pr126513-acmp-tst-1.c b/gcc/testsuite/gcc.target/powerpc/pr126513-acmp-tst-1.c
new file mode 100644
index 00000000000..8d71152de77
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/pr126513-acmp-tst-1.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target lp64 } */
+
+// Need power8 for l<b,h,q>arx
+/* { dg-options "-O2 -mdejagnu-cpu=power8" } */
+
+__int128 word_exchange_uti_ptr;
+int word_exchange_uti_expected;
+unsigned word_exchange_uti_desired() {
+  __builtin_ppc_atomic_cas_local( /* { dg-error "argument 2 to '__builtin_ppc_atomic_cas_local' must be a pointer" } */
+      &word_exchange_uti_ptr, word_exchange_uti_expected,
+      word_exchange_uti_desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE);
+}
+
+unsigned word_exchange_uti_desired_fptr() {
+  __builtin_ppc_atomic_cas_local( /* { dg-error "argument 1 to '__builtin_ppc_atomic_cas_local' must not be a pointer to a function" } */
+      &word_exchange_uti_desired_fptr, &word_exchange_uti_desired,
+      &word_exchange_uti_desired_fptr, 0, __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE);
+}
+
+unsigned word_exchange_uti_desired_mismatch() {
+  __builtin_ppc_atomic_cas_local( /* { dg-error "size mismatch in argument 2" } */
+      &word_exchange_uti_ptr, &word_exchange_uti_expected,
+      &word_exchange_uti_ptr, 0, __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE);
+}
diff --git a/gcc/testsuite/gcc.target/powerpc/pr126513-acmp-tst-2.c b/gcc/testsuite/gcc.target/powerpc/pr126513-acmp-tst-2.c
new file mode 100644
index 00000000000..2c7c3c796c4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/pr126513-acmp-tst-2.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target lp64 } */
+
+// Need power8 for l<b,h,q>arx
+/* { dg-options "-O2 -mdejagnu-cpu=power8 -mno-quad-memory-atomic" } */
+
+__int128 word_exchange_uti_ptr;
+__int128 word_exchange_uti_ptr_expected;
+__int128 word_exchange_uti_ptr_desired;
+
+unsigned word_exchange_uti_desired() {
+  __builtin_ppc_atomic_cas_local( /* { dg-error "'__builtin_ppc_atomic_cas_local' requires the '-mquad-memory-atomic' option for 16-byte operands" } */
+      &word_exchange_uti_ptr, &word_exchange_uti_ptr_expected,
+      &word_exchange_uti_ptr_desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE);
+}
diff --git a/gcc/testsuite/gcc.target/powerpc/pr126513-acmp-tst-3.c b/gcc/testsuite/gcc.target/powerpc/pr126513-acmp-tst-3.c
new file mode 100644
index 00000000000..da6e4756058
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/pr126513-acmp-tst-3.c
@@ -0,0 +1,176 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target lp64 } */
+
+// Need power8 for l<b,h,q>arx
+/* { dg-options "-O2 -mdejagnu-cpu=power8" } */
+
+typedef struct udt_1
+{
+  char *a;
+} udt_1t;
+typedef struct udt_2
+{
+  char a;
+  char b;
+} udt_2t;
+typedef struct udt_4
+{
+  short a;
+  short b;
+} udt_4t;
+typedef struct udt_8
+{
+  int a;
+  int b;
+} udt_8t;
+typedef struct udt_16
+{
+  long long a;
+  long long b;
+} udt_16t;
+void
+word_exchange_nqi (char *ptr, char *expected, char *desired)
+{
+  __builtin_ppc_atomic_cas_local (ptr, expected, desired, 0,
+					 __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE);
+}
+void
+word_exchange_qi (signed char *ptr, signed char *expected, signed char *desired)
+{
+  __builtin_ppc_atomic_cas_local (ptr, expected, desired, 0,
+					 __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE);
+}
+void
+word_exchange_uqi (unsigned char *ptr, unsigned char *expected,
+                   unsigned char *desired)
+{
+  __builtin_ppc_atomic_cas_local (ptr, expected, desired, 0,
+					 __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE);
+}
+void
+word_exchange_hi (short *ptr, short *expected, short *desired)
+{
+  __builtin_ppc_atomic_cas_local (ptr, expected, desired, 0,
+					 __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE);
+}
+void
+word_exchange_shi (signed short *ptr, signed short *expected,
+                   signed short *desired)
+{
+  __builtin_ppc_atomic_cas_local (ptr, expected, desired, 0,
+					 __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE);
+}
+void
+word_exchange_uhi (unsigned short *ptr, unsigned short *expected,
+                   unsigned short *desired)
+{
+  __builtin_ppc_atomic_cas_local (ptr, expected, desired, 0,
+					 __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE);
+}
+void
+word_exchange_si (int *ptr, int *expected, int *desired)
+{
+  __builtin_ppc_atomic_cas_local (ptr, expected, desired, 0,
+					 __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE);
+}
+void
+word_exchange_ssi (signed int *ptr, signed int *expected, signed int *desired)
+{
+  __builtin_ppc_atomic_cas_local (ptr, expected, desired, 0,
+					 __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE);
+}
+void
+word_exchange_usi (unsigned int *ptr, unsigned int *expected,
+                   unsigned int *desired)
+{
+  __builtin_ppc_atomic_cas_local (ptr, expected, desired, 0,
+					 __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE);
+}
+void
+word_exchange_di (long long *ptr, long long *expected, long long *desired)
+{
+  __builtin_ppc_atomic_cas_local (ptr, expected, desired, 0,
+					 __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE);
+}
+void
+word_exchange_sdi (signed long long *ptr, signed long long *expected,
+                   signed long long *desired)
+{
+  __builtin_ppc_atomic_cas_local (ptr, expected, desired, 0,
+					 __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE);
+}
+void
+word_exchange_udi (unsigned long long *ptr, unsigned long long *expected,
+                   unsigned long long *desired)
+{
+  __builtin_ppc_atomic_cas_local (ptr, expected, desired, 0,
+					 __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE);
+}
+void
+word_exchange_sti (signed __int128 *ptr, signed __int128 *expected,
+                   signed __int128 *desired)
+{
+  __builtin_ppc_atomic_cas_local (ptr, expected, desired, 0,
+					 __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE);
+}
+void
+word_exchange_uti (unsigned __int128 *ptr, unsigned __int128 *expected,
+                   unsigned __int128 *desired)
+{
+  __builtin_ppc_atomic_cas_local (ptr, expected, desired, 0,
+					 __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE);
+}
+void
+word_exchange_f32 (float *ptr, float *expected, float *desired)
+{
+  __builtin_ppc_atomic_cas_local (ptr, expected, desired, 0,
+					 __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE);
+}
+void
+word_exchange_f64 (double *ptr, double *expected, double *desired)
+{
+  __builtin_ppc_atomic_cas_local (ptr, expected, desired, 0,
+					 __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE);
+}
+void
+word_exchange_f128 (__ieee128 *ptr, __ieee128 *expected, __ieee128 *desired)
+{
+  __builtin_ppc_atomic_cas_local (ptr, expected, desired, 0,
+					 __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE);
+}
+void
+word_exchange_udt_1 (udt_1t *ptr, udt_1t *expected, udt_1t *desired)
+{
+  __builtin_ppc_atomic_cas_local (ptr, expected, desired, 0,
+					 __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE);
+}
+void
+word_exchange_udt_2 (udt_2t *ptr, udt_2t *expected, udt_2t *desired)
+{
+  __builtin_ppc_atomic_cas_local (ptr, expected, desired, 0,
+					 __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE);
+}
+void
+word_exchange_udt_4 (udt_4t *ptr, udt_4t *expected, udt_4t *desired)
+{
+  __builtin_ppc_atomic_cas_local (ptr, expected, desired, 0,
+					 __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE);
+}
+void
+word_exchange_udt_8 (udt_8t *ptr, udt_8t *expected, udt_8t *desired)
+{
+  __builtin_ppc_atomic_cas_local (ptr, expected, desired, 0,
+					 __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE);
+}
+void
+word_exchange_udt_16 (udt_16t *ptr, udt_16t *expected, udt_16t *desired)
+{
+  __builtin_ppc_atomic_cas_local (ptr, expected, desired, 0,
+					 __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE);
+}
+
+/* { dg-final { scan-assembler-times {\mlbarx +[0-9]+,[0-9]+,[0-9]+,1} 3 } } */
+/* { dg-final { scan-assembler-times {\mlharx +[0-9]+,[0-9]+,[0-9]+,1} 4 } } */
+/* { dg-final { scan-assembler-times {\mlwarx +[0-9]+,[0-9]+,[0-9]+,1} 5 } } */
+/* { dg-final { scan-assembler-times {\mldarx +[0-9]+,[0-9]+,[0-9]+,1} 6 } } */
+/* { dg-final { scan-assembler-times {\mlqarx +[0-9]+,[0-9]+,[0-9]+,1} 4 } } */
-- 
2.54.0