Skip partial register clearing logic when dealing with FP_REGS in aggregates as
these are always fully cleared and the logic assumes a mask for each of the 4
argument GPR_REGS.

gcc/ChangeLog:

        PR target/122539
        * config/arm/arm.cc (comp_not_to_clear_mask_str_un): Skip partial
        register clearing logic for FP_REGS.
        (compute_not_to_clear_mask): Likewise.

gcc/testsuite/ChangeLog:

        * gcc.target/arm/cmse/mainline/8m/hard/union-fp.c: New.
        * gcc.target/arm/cmse/baseline/union-4.c: New.
        * gcc.target/arm/cmse/mainline/8m/hard/union-4.c: New.
        * gcc.target/arm/cmse/mainline/8m/soft/union-4.c: New.
        * gcc.target/arm/cmse/mainline/8m/softfp/union-4.c: New.
        * gcc.target/arm/cmse/union-4.x: New.
---
 gcc/config/arm/arm.cc                         | 60 ++++++++++-------
 .../gcc.target/arm/cmse/baseline/union-4.c    | 20 ++++++
 .../arm/cmse/mainline/8m/hard/union-4.c       | 27 ++++++++
 .../arm/cmse/mainline/8m/hard/union-fp.c      | 66 +++++++++++++++++++
 .../arm/cmse/mainline/8m/soft/union-4.c       | 18 +++++
 .../arm/cmse/mainline/8m/softfp/union-4.c     | 19 ++++++
 gcc/testsuite/gcc.target/arm/cmse/union-4.x   | 41 ++++++++++++
 7 files changed, 229 insertions(+), 22 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/arm/cmse/baseline/union-4.c
 create mode 100644 gcc/testsuite/gcc.target/arm/cmse/mainline/8m/hard/union-4.c
 create mode 100644 
gcc/testsuite/gcc.target/arm/cmse/mainline/8m/hard/union-fp.c
 create mode 100644 gcc/testsuite/gcc.target/arm/cmse/mainline/8m/soft/union-4.c
 create mode 100644 
gcc/testsuite/gcc.target/arm/cmse/mainline/8m/softfp/union-4.c
 create mode 100644 gcc/testsuite/gcc.target/arm/cmse/union-4.x

diff --git a/gcc/config/arm/arm.cc b/gcc/config/arm/arm.cc
index 527d526cd4a..a2c1bd9fe38 100644
--- a/gcc/config/arm/arm.cc
+++ b/gcc/config/arm/arm.cc
@@ -18483,6 +18483,9 @@ comp_not_to_clear_mask_str_un (tree arg_type, int * regno,
 
 	  if (*last_used_bit != offset)
 	    {
+	      /* We never clear padding bits in any other registers than the
+		 first 4 GPRs.  */
+	      gcc_assert (*regno < 4);
 	      if (offset < *last_used_bit)
 		{
 		  /* This field's offset is before the 'last_used_bit', that
@@ -18575,19 +18578,25 @@ comp_not_to_clear_mask_str_un (tree arg_type, int * regno,
 	      last_used_bit_t = (starting_bit + field_size) % 32;
 	    }
 
-	  for (i = *regno; i < regno_t; i++)
+	  /* We only clear padding bits in the first 4 GPRs.  No need to check
+	     regno_t, since there is no way where this field would have been
+	     put into part GPR part FP reg.  */
+	  if (*regno < 4)
 	    {
-	      /* For all but the last register used by this field only keep the
-		 padding bits that were padding bits in this field.  */
-	      padding_bits_to_clear_res[i] &= padding_bits_to_clear_t[i];
-	    }
+	      for (i = *regno; i < regno_t; i++)
+		{
+		  /* For all but the last register used by this field only keep
+		     the padding bits that were padding bits in this field.  */
+		  padding_bits_to_clear_res[i] &= padding_bits_to_clear_t[i];
+		}
 
-	    /* For the last register, keep all padding bits that were padding
-	       bits in this field and any padding bits that are still valid
-	       as padding bits but fall outside of this field's size.  */
-	    mask = (((uint32_t) -1) - ((uint32_t) 1 << last_used_bit_t)) + 1;
-	    padding_bits_to_clear_res[regno_t]
-	      &= padding_bits_to_clear_t[regno_t] | mask;
+	      /* For the last register, keep all padding bits that were padding
+		 bits in this field and any padding bits that are still valid
+		 as padding bits but fall outside of this field's size.  */
+	      mask = (((uint32_t) -1) - ((uint32_t) 1 << last_used_bit_t)) + 1;
+	      padding_bits_to_clear_res[regno_t]
+		&= padding_bits_to_clear_t[regno_t] | mask;
+	    }
 
 	  /* Update the maximum size of the fields in terms of registers used
 	     ('max_reg') and the 'last_used_bit' in said register.  */
@@ -18602,16 +18611,22 @@ comp_not_to_clear_mask_str_un (tree arg_type, int * regno,
 	  field = TREE_CHAIN (field);
 	}
 
-      /* Update the current padding_bits_to_clear using the intersection of the
-	 padding bits of all the fields.  */
-      for (i=*regno; i < max_reg; i++)
-	padding_bits_to_clear[i] |= padding_bits_to_clear_res[i];
+      /* We only clear padding bits in the first 4 GPRs.  No need to check
+	 regno_t, since there is no way where this field would have been
+	 put into part GPR part FP reg.  */
+      if (*regno < 4)
+	{
+	  /* Update the current padding_bits_to_clear using the intersection of the
+	     padding bits of all the fields.  */
+	  for (i=*regno; i < max_reg; i++)
+	    padding_bits_to_clear[i] |= padding_bits_to_clear_res[i];
 
-      /* Do not keep trailing padding bits, we do not know yet whether this
-	 is the end of the argument.  */
-      mask = ((uint32_t) 1 << max_bit) - 1;
-      padding_bits_to_clear[max_reg]
-	|= padding_bits_to_clear_res[max_reg] & mask;
+	  /* Do not keep trailing padding bits, we do not know yet whether this
+	     is the end of the argument.  */
+	  mask = ((uint32_t) 1 << max_bit) - 1;
+	  padding_bits_to_clear[max_reg]
+	    |= padding_bits_to_clear_res[max_reg] & mask;
+	}
 
       for (int i = *regno; i < max_reg; ++i)
 	not_to_clear_reg_mask |= HOST_WIDE_INT_1U << i;
@@ -18656,8 +18671,9 @@ compute_not_to_clear_mask (tree arg_type, rtx arg_rtx, int regno,
       /* If the 'last_used_bit' is not zero, that means we are still using a
 	 part of the last 'regno'.  In such cases we must clear the trailing
 	 bits.  Otherwise we are not using regno and we should mark it as to
-	 clear.  */
-      if (last_used_bit != 0)
+	 clear.  We only clear padding bits for scalar values that are passed
+	 in registers, so regno is never 4 or higher.  */
+      if (regno < 4 && last_used_bit != 0)
 	padding_bits_to_clear[regno]
 	  |= ((uint32_t)-1) - ((uint32_t) 1 << last_used_bit) + 1;
       else
diff --git a/gcc/testsuite/gcc.target/arm/cmse/baseline/union-4.c b/gcc/testsuite/gcc.target/arm/cmse/baseline/union-4.c
new file mode 100644
index 00000000000..58c06ceddda
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/baseline/union-4.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-mcmse" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include "../union-4.x"
+
+/*
+** fn:
+** ...
+**	lsrs	r4, r4, #1
+**	lsls	r4, r4, #1
+**	mov	ip, r4
+**	movw	r4, #7939
+**	ands	r0, r4
+**	mov	r4, ip
+**	movs	r2, r4
+**	movs	r3, r4
+**	bl	__gnu_cmse_nonsecure_call
+** ...
+*/
diff --git a/gcc/testsuite/gcc.target/arm/cmse/mainline/8m/hard/union-4.c b/gcc/testsuite/gcc.target/arm/cmse/mainline/8m/hard/union-4.c
new file mode 100644
index 00000000000..671a4e83078
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/mainline/8m/hard/union-4.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-mcmse -mfloat-abi=hard -mfpu=fpv5-d16" }  */
+/* { dg-skip-if "Incompatible float ABI" { *-*-* } { "-mfloat-abi=*" } { "-mfloat-abi=hard" } } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include "../../../union-4.x"
+
+/*
+** fn:
+** ...
+**	lsrs	r4, r4, #1
+**	lsls	r4, r4, #1
+**	movw	ip, #7939
+**	and	r0, r0, ip
+**	mov	r2, r4
+**	mov	r3, r4
+**	vmov.f64	d0, #1.0e\+0
+**	vmov.f64	d1, #1.0e\+0
+**	vmov.f64	d2, #1.0e\+0
+**	vmov.f64	d3, #1.0e\+0
+**	vmov.f64	d4, #1.0e\+0
+**	vmov.f64	d5, #1.0e\+0
+**	vmov.f64	d6, #1.0e\+0
+**	vmov.f64	d7, #1.0e\+0
+**	bl	__gnu_cmse_nonsecure_call
+** ...
+*/
diff --git a/gcc/testsuite/gcc.target/arm/cmse/mainline/8m/hard/union-fp.c b/gcc/testsuite/gcc.target/arm/cmse/mainline/8m/hard/union-fp.c
new file mode 100644
index 00000000000..3d45ec6bf77
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/mainline/8m/hard/union-fp.c
@@ -0,0 +1,66 @@
+/* { dg-do compile } */
+/* { dg-options "-mcmse -mfloat-abi=hard -mfpu=fpv5-d16" }  */
+/* { dg-skip-if "Incompatible float ABI" { *-*-* } { "-mfloat-abi=*" } { "-mfloat-abi=hard" } } */
+/* { dg-skip-if "Skip these if testing single precision" {*-*-*} {"-mfpu=*-sp-*"} {""} } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+union u_t_0 {
+    float f;
+};
+union u_t_1 {
+    double d;
+};
+
+typedef void (__attribute__ ((cmse_nonsecure_call)) fn_t_0)(union u_t_0);
+typedef void (__attribute__ ((cmse_nonsecure_call)) fn_t_1)(union u_t_1);
+
+void fn_caller_0 (fn_t_0 *f_ptr) {
+    union u_t_0 x = {0.0f};
+    f_ptr (x);
+}
+
+/*
+** fn_caller_0:
+** ...
+**	lsrs	r4, r4, #1
+**	lsls	r4, r4, #1
+**	mov	r0, r4
+**	mov	r1, r4
+**	mov	r2, r4
+**	mov	r3, r4
+**	vmov.f32	s1, #1.0e\+0
+**	vmov.f64	d1, #1.0e\+0
+**	vmov.f64	d2, #1.0e\+0
+**	vmov.f64	d3, #1.0e\+0
+**	vmov.f64	d4, #1.0e\+0
+**	vmov.f64	d5, #1.0e\+0
+**	vmov.f64	d6, #1.0e\+0
+**	vmov.f64	d7, #1.0e\+0
+**	bl	__gnu_cmse_nonsecure_call
+** ...
+*/
+
+void fn_caller_1 (fn_t_1 *f_ptr) {
+    union u_t_1 x = {0.0};
+    f_ptr (x);
+}
+
+/*
+** fn_caller_1:
+** ...
+**	lsrs	r4, r4, #1
+**	lsls	r4, r4, #1
+**	mov	r0, r4
+**	mov	r1, r4
+**	mov	r2, r4
+**	mov	r3, r4
+**	vmov.f64	d1, #1.0e\+0
+**	vmov.f64	d2, #1.0e\+0
+**	vmov.f64	d3, #1.0e\+0
+**	vmov.f64	d4, #1.0e\+0
+**	vmov.f64	d5, #1.0e\+0
+**	vmov.f64	d6, #1.0e\+0
+**	vmov.f64	d7, #1.0e\+0
+**	bl	__gnu_cmse_nonsecure_call
+** ...
+*/
diff --git a/gcc/testsuite/gcc.target/arm/cmse/mainline/8m/soft/union-4.c b/gcc/testsuite/gcc.target/arm/cmse/mainline/8m/soft/union-4.c
new file mode 100644
index 00000000000..b0a83db05bf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/mainline/8m/soft/union-4.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-options "-mcmse" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include "../../../union-4.x"
+
+/*
+** fn:
+** ...
+**	lsrs	r4, r4, #1
+**	lsls	r4, r4, #1
+**	movw	ip, #7939
+**	and	r0, r0, ip
+**	mov	r2, r4
+**	mov	r3, r4
+**	bl	__gnu_cmse_nonsecure_call
+** ...
+*/
diff --git a/gcc/testsuite/gcc.target/arm/cmse/mainline/8m/softfp/union-4.c b/gcc/testsuite/gcc.target/arm/cmse/mainline/8m/softfp/union-4.c
new file mode 100644
index 00000000000..9842a0fee00
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/mainline/8m/softfp/union-4.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-options "-mcmse -mfloat-abi=softfp -mfpu=fpv5-d16" }  */
+/* { dg-skip-if "Incompatible float ABI" { *-*-* } { "-mfloat-abi=*" } { "-mfloat-abi=softfp" } } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include "../../../union-4.x"
+
+/*
+** fn:
+** ...
+**	lsrs	r4, r4, #1
+**	lsls	r4, r4, #1
+**	movw	ip, #7939
+**	and	r0, r0, ip
+**	mov	r2, r4
+**	mov	r3, r4
+**	bl	__gnu_cmse_nonsecure_call
+** ...
+*/
diff --git a/gcc/testsuite/gcc.target/arm/cmse/union-4.x b/gcc/testsuite/gcc.target/arm/cmse/union-4.x
new file mode 100644
index 00000000000..20678296157
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/union-4.x
@@ -0,0 +1,41 @@
+typedef struct
+{
+  unsigned char	  a :2;
+  unsigned char	    :0;
+  unsigned short  b :5;
+  float		  f;
+} test_st_1;
+
+typedef union
+{
+  test_st_1 st_1;
+}test_un;
+
+typedef union
+{
+  test_un un;
+  struct
+    {
+      unsigned int v1;
+      unsigned int v2;
+      unsigned int v3;
+      unsigned int v4;
+    }values;
+} read_un;
+
+
+typedef void __attribute__ ((cmse_nonsecure_call)) (*foo_ns) (test_un);
+
+int
+fn (foo_ns fptr)
+{
+  read_un r;
+
+  r.values.v1 = 0xFFFFFFFF;
+  r.values.v2 = 0xFFFFFFFF;
+  r.values.v3 = 0xFFFFFFFF;
+  r.values.v4 = 0xFFFFFFFF;
+
+  fptr (r.un);
+  return 0;
+}

Reply via email to