diff mbox

[GCC/ARM,gcc-5/6-branch,ping2] Fix PR77933: stack corruption on ARM when using high registers and lr

Message ID f3ddff84-a5b3-fb75-82c4-773d64b934bc@foss.arm.com
State New
Headers show

Commit Message

Thomas Preudhomme Dec. 9, 2016, 2:24 p.m. UTC
[Seeing as an RC for GCC 6.3 was suggested on IRC for mid next week]

Ping?

backport for 6 bootstraps on Thumb-1 and testsuite shows no regression for 
either 5 or 6. Bootstrap for 5 is ongoing.

Best regards,

Thomas

On 06/12/16 11:37, Thomas Preudhomme wrote:
> Ping?

>

> Best regards,

>

> Thomas

>

> On 30/11/16 09:50, Thomas Preudhomme wrote:

>> Hi,

>>

>> Is this ok to backport to gcc-5-branch and gcc-6-branch? Patch applies cleanly

>> (patches attached for reference).

>>

>>

>> 2016-11-17  Thomas Preud'homme  <thomas.preudhomme@arm.com>

>>

>>     Backport from mainline

>>     2016-11-17  Thomas Preud'homme  <thomas.preudhomme@arm.com>

>>

>>     gcc/

>>     PR target/77933

>>     * config/arm/arm.c (thumb1_expand_prologue): Distinguish between lr

>>     being live in the function and lr needing to be saved.  Distinguish

>>     between already saved pushable registers and registers to push.

>>     Check for LR being an available pushable register.

>>

>>     gcc/testsuite/

>>     PR target/77933

>>     * gcc.target/arm/pr77933-1.c: New test.

>>     * gcc.target/arm/pr77933-2.c: Likewise.

>>

>>

>> Best regards,

>>

>> Thomas

>>

>>

>> On 17/11/16 20:15, Thomas Preudhomme wrote:

>>> Hi Kyrill,

>>>

>>> I've committed the following updated patch where the test is restricted to Thumb

>>> execution mode and skipping it if not possible since -mtpcs-leaf-frame is only

>>> available in Thumb mode. I've considered the change obvious.

>>>

>>> *** gcc/ChangeLog ***

>>>

>>> 2016-11-08  Thomas Preud'homme  <thomas.preudhomme@arm.com>

>>>

>>>         PR target/77933

>>>         * config/arm/arm.c (thumb1_expand_prologue): Distinguish between lr

>>>         being live in the function and lr needing to be saved.  Distinguish

>>>         between already saved pushable registers and registers to push.

>>>         Check for LR being an available pushable register.

>>>

>>>

>>> *** gcc/testsuite/ChangeLog ***

>>>

>>> 2016-11-08  Thomas Preud'homme  <thomas.preudhomme@arm.com>

>>>

>>>         PR target/77933

>>>         * gcc.target/arm/pr77933-1.c: New test.

>>>         * gcc.target/arm/pr77933-2.c: Likewise.

>>>

>>> Best regards,

>>>

>>> Thomas

>>>

>>> On 17/11/16 10:04, Kyrill Tkachov wrote:

>>>>

>>>> On 09/11/16 16:41, Thomas Preudhomme wrote:

>>>>> I've reworked the patch following comments from Wilco [1] (sorry could not

>>>>> find it in my MUA for some reason).

>>>>>

>>>>> [1] https://gcc.gnu.org/ml/gcc-patches/2016-11/msg00317.html

>>>>>

>>>>>

>>>>> == Context ==

>>>>>

>>>>> When saving registers, function thumb1_expand_prologue () aims at minimizing

>>>>> the number of push instructions. One of the optimization it does is to push LR

>>>>> alongside high register(s) (after having moved them to low register(s)) when

>>>>> there is no low register to save. The way this is implemented is to add LR to

>>>>> the pushable_regs mask if it is live just before pushing the registers in that

>>>>> mask. The mask of live pushable registers which is used to detect whether LR

>>>>> needs to be saved is then clear to ensure LR is only saved once.

>>>>>

>>>>>

>>>>> == Problem ==

>>>>>

>>>>> However beyond deciding what register to push pushable_regs is used to track

>>>>> what pushable register can be used to move a high register before being

>>>>> pushed, hence the name. That mask is cleared when all high registers have been

>>>>> assigned a low register but the clearing assumes the high registers were

>>>>> assigned to the registers with the biggest number in that mask. This is not

>>>>> the case because LR is not considered when looking for a register in that

>>>>> mask. Furthermore, LR might have been saved in the TARGET_BACKTRACE path above

>>>>> yet the mask of live pushable registers is not cleared in that case.

>>>>>

>>>>>

>>>>> == Solution ==

>>>>>

>>>>> This patch changes the loop to iterate over register LR to r0 so as to both

>>>>> fix the stack corruption reported in PR77933 and reuse lr to push some high

>>>>> register when possible. This patch also introduce a new variable

>>>>> lr_needs_saving to record whether LR (still) needs to be saved at a given

>>>>> point in code and sets the variable accordingly throughout the code, thus

>>>>> fixing the second issue. Finally, this patch create a new push_mask variable

>>>>> to distinguish between the mask of registers to push and the mask of live

>>>>> pushable registers.

>>>>>

>>>>>

>>>>> == Note ==

>>>>>

>>>>> Other bits could have been improved but have been left out to allow the patch

>>>>> to be backported to stable branch:

>>>>>

>>>>> (1) using argument registers that are not holding an argument

>>>>> (2) using push_mask consistently instead of l_mask (in TARGET_BACKTRACE), mask

>>>>> (low register push) and push_mask

>>>>> (3) the !l_mask case improved in TARGET_BACKTRACE since offset == 0

>>>>> (4) rename l_mask to a more appropriate name (live_pushable_regs_mask?)

>>>>>

>>>>> ChangeLog entry are as follow:

>>>>>

>>>>> *** gcc/ChangeLog ***

>>>>>

>>>>> 2016-11-08  Thomas Preud'homme  <thomas.preudhomme@arm.com>

>>>>>

>>>>>         PR target/77933

>>>>>         * config/arm/arm.c (thumb1_expand_prologue): Distinguish between lr

>>>>>         being live in the function and lr needing to be saved. Distinguish

>>>>>         between already saved pushable registers and registers to push.

>>>>>         Check for LR being an available pushable register.

>>>>>

>>>>>

>>>>> *** gcc/testsuite/ChangeLog ***

>>>>>

>>>>> 2016-11-08  Thomas Preud'homme  <thomas.preudhomme@arm.com>

>>>>>

>>>>>         PR target/77933

>>>>>         * gcc.target/arm/pr77933-1.c: New test.

>>>>>         * gcc.target/arm/pr77933-2.c: Likewise.

>>>>>

>>>>>

>>>>> Testing: no regression on arm-none-eabi GCC cross-compiler targeting Cortex-M0

>>>>>

>>>>> Is this ok for trunk?

>>>>>

>>>>

>>>> Ok.

>>>> Thanks,

>>>> Kyrill

>>>>

>>>>> Best regards,

>>>>>

>>>>> Thomas

>>>>>

>>>>> On 02/11/16 17:08, Thomas Preudhomme wrote:

>>>>>> Hi,

>>>>>>

>>>>>> When saving registers, function thumb1_expand_prologue () aims at minimizing

>>>>>> the

>>>>>> number of push instructions. One of the optimization it does is to push lr

>>>>>> alongside high register(s) (after having moved them to low register(s)) when

>>>>>> there is no low register to save. The way this is implemented is to add lr to

>>>>>> the list of registers that can be pushed just before the push happens. This

>>>>>> would then push lr and allows it to be used for further push if there was not

>>>>>> enough registers to push all high registers to be pushed.

>>>>>>

>>>>>> However, the logic that decides what register to move high registers to

>>>>>> before

>>>>>> being pushed only looks at low registers (see for loop initialization). This

>>>>>> means not only that lr is not used for pushing high registers but also

>>>>>> that lr

>>>>>> is not removed from the list of registers to be pushed when it's not used.

>>>>>> This

>>>>>> extra lr push is not poped in epilogue leading in stack corruption.

>>>>>>

>>>>>> This patch changes the loop to iterate over register r0 to lr so as to both

>>>>>> fix

>>>>>> the stack corruption and reuse lr to push some high register when possible.

>>>>>>

>>>>>> ChangeLog entry are as follow:

>>>>>>

>>>>>> *** gcc/ChangeLog ***

>>>>>>

>>>>>> 2016-11-01  Thomas Preud'homme <thomas.preudhomme@arm.com>

>>>>>>

>>>>>>         PR target/77933

>>>>>>         * config/arm/arm.c (thumb1_expand_prologue): Also check for lr

>>>>>> being a

>>>>>>         pushable register.

>>>>>>

>>>>>>

>>>>>> *** gcc/testsuite/ChangeLog ***

>>>>>>

>>>>>> 2016-11-01  Thomas Preud'homme <thomas.preudhomme@arm.com>

>>>>>>

>>>>>>         PR target/77933

>>>>>>         * gcc.target/arm/pr77933.c: New test.

>>>>>>

>>>>>>

>>>>>> Testing: no regression on arm-none-eabi GCC cross-compiler targeting

>>>>>> Cortex-M0

>>>>>>

>>>>>> Is this ok for trunk?

>>>>>>

>>>>>> Best regards,

>>>>>>

>>>>>> Thomas

>>>>

Comments

Kyrill Tkachov Dec. 9, 2016, 2:30 p.m. UTC | #1
On 09/12/16 14:24, Thomas Preudhomme wrote:
> [Seeing as an RC for GCC 6.3 was suggested on IRC for mid next week]

>

> Ping?

>

> backport for 6 bootstraps on Thumb-1 and testsuite shows no regression for either 5 or 6. Bootstrap for 5 is ongoing.

>


Ok.
Thanks,
Kyrill

> Best regards,

>

> Thomas

>

> On 06/12/16 11:37, Thomas Preudhomme wrote:

>> Ping?

>>

>> Best regards,

>>

>> Thomas

>>

>> On 30/11/16 09:50, Thomas Preudhomme wrote:

>>> Hi,

>>>

>>> Is this ok to backport to gcc-5-branch and gcc-6-branch? Patch applies cleanly

>>> (patches attached for reference).

>>>

>>>

>>> 2016-11-17  Thomas Preud'homme <thomas.preudhomme@arm.com>

>>>

>>>     Backport from mainline

>>>     2016-11-17  Thomas Preud'homme <thomas.preudhomme@arm.com>

>>>

>>>     gcc/

>>>     PR target/77933

>>>     * config/arm/arm.c (thumb1_expand_prologue): Distinguish between lr

>>>     being live in the function and lr needing to be saved. Distinguish

>>>     between already saved pushable registers and registers to push.

>>>     Check for LR being an available pushable register.

>>>

>>>     gcc/testsuite/

>>>     PR target/77933

>>>     * gcc.target/arm/pr77933-1.c: New test.

>>>     * gcc.target/arm/pr77933-2.c: Likewise.

>>>

>>>

>>> Best regards,

>>>

>>> Thomas

>>>

>>>

>>> On 17/11/16 20:15, Thomas Preudhomme wrote:

>>>> Hi Kyrill,

>>>>

>>>> I've committed the following updated patch where the test is restricted to Thumb

>>>> execution mode and skipping it if not possible since -mtpcs-leaf-frame is only

>>>> available in Thumb mode. I've considered the change obvious.

>>>>

>>>> *** gcc/ChangeLog ***

>>>>

>>>> 2016-11-08  Thomas Preud'homme <thomas.preudhomme@arm.com>

>>>>

>>>>         PR target/77933

>>>>         * config/arm/arm.c (thumb1_expand_prologue): Distinguish between lr

>>>>         being live in the function and lr needing to be saved.  Distinguish

>>>>         between already saved pushable registers and registers to push.

>>>>         Check for LR being an available pushable register.

>>>>

>>>>

>>>> *** gcc/testsuite/ChangeLog ***

>>>>

>>>> 2016-11-08  Thomas Preud'homme <thomas.preudhomme@arm.com>

>>>>

>>>>         PR target/77933

>>>>         * gcc.target/arm/pr77933-1.c: New test.

>>>>         * gcc.target/arm/pr77933-2.c: Likewise.

>>>>

>>>> Best regards,

>>>>

>>>> Thomas

>>>>

>>>> On 17/11/16 10:04, Kyrill Tkachov wrote:

>>>>>

>>>>> On 09/11/16 16:41, Thomas Preudhomme wrote:

>>>>>> I've reworked the patch following comments from Wilco [1] (sorry could not

>>>>>> find it in my MUA for some reason).

>>>>>>

>>>>>> [1] https://gcc.gnu.org/ml/gcc-patches/2016-11/msg00317.html

>>>>>>

>>>>>>

>>>>>> == Context ==

>>>>>>

>>>>>> When saving registers, function thumb1_expand_prologue () aims at minimizing

>>>>>> the number of push instructions. One of the optimization it does is to push LR

>>>>>> alongside high register(s) (after having moved them to low register(s)) when

>>>>>> there is no low register to save. The way this is implemented is to add LR to

>>>>>> the pushable_regs mask if it is live just before pushing the registers in that

>>>>>> mask. The mask of live pushable registers which is used to detect whether LR

>>>>>> needs to be saved is then clear to ensure LR is only saved once.

>>>>>>

>>>>>>

>>>>>> == Problem ==

>>>>>>

>>>>>> However beyond deciding what register to push pushable_regs is used to track

>>>>>> what pushable register can be used to move a high register before being

>>>>>> pushed, hence the name. That mask is cleared when all high registers have been

>>>>>> assigned a low register but the clearing assumes the high registers were

>>>>>> assigned to the registers with the biggest number in that mask. This is not

>>>>>> the case because LR is not considered when looking for a register in that

>>>>>> mask. Furthermore, LR might have been saved in the TARGET_BACKTRACE path above

>>>>>> yet the mask of live pushable registers is not cleared in that case.

>>>>>>

>>>>>>

>>>>>> == Solution ==

>>>>>>

>>>>>> This patch changes the loop to iterate over register LR to r0 so as to both

>>>>>> fix the stack corruption reported in PR77933 and reuse lr to push some high

>>>>>> register when possible. This patch also introduce a new variable

>>>>>> lr_needs_saving to record whether LR (still) needs to be saved at a given

>>>>>> point in code and sets the variable accordingly throughout the code, thus

>>>>>> fixing the second issue. Finally, this patch create a new push_mask variable

>>>>>> to distinguish between the mask of registers to push and the mask of live

>>>>>> pushable registers.

>>>>>>

>>>>>>

>>>>>> == Note ==

>>>>>>

>>>>>> Other bits could have been improved but have been left out to allow the patch

>>>>>> to be backported to stable branch:

>>>>>>

>>>>>> (1) using argument registers that are not holding an argument

>>>>>> (2) using push_mask consistently instead of l_mask (in TARGET_BACKTRACE), mask

>>>>>> (low register push) and push_mask

>>>>>> (3) the !l_mask case improved in TARGET_BACKTRACE since offset == 0

>>>>>> (4) rename l_mask to a more appropriate name (live_pushable_regs_mask?)

>>>>>>

>>>>>> ChangeLog entry are as follow:

>>>>>>

>>>>>> *** gcc/ChangeLog ***

>>>>>>

>>>>>> 2016-11-08  Thomas Preud'homme <thomas.preudhomme@arm.com>

>>>>>>

>>>>>>         PR target/77933

>>>>>>         * config/arm/arm.c (thumb1_expand_prologue): Distinguish between lr

>>>>>>         being live in the function and lr needing to be saved. Distinguish

>>>>>>         between already saved pushable registers and registers to push.

>>>>>>         Check for LR being an available pushable register.

>>>>>>

>>>>>>

>>>>>> *** gcc/testsuite/ChangeLog ***

>>>>>>

>>>>>> 2016-11-08  Thomas Preud'homme <thomas.preudhomme@arm.com>

>>>>>>

>>>>>>         PR target/77933

>>>>>>         * gcc.target/arm/pr77933-1.c: New test.

>>>>>>         * gcc.target/arm/pr77933-2.c: Likewise.

>>>>>>

>>>>>>

>>>>>> Testing: no regression on arm-none-eabi GCC cross-compiler targeting Cortex-M0

>>>>>>

>>>>>> Is this ok for trunk?

>>>>>>

>>>>>

>>>>> Ok.

>>>>> Thanks,

>>>>> Kyrill

>>>>>

>>>>>> Best regards,

>>>>>>

>>>>>> Thomas

>>>>>>

>>>>>> On 02/11/16 17:08, Thomas Preudhomme wrote:

>>>>>>> Hi,

>>>>>>>

>>>>>>> When saving registers, function thumb1_expand_prologue () aims at minimizing

>>>>>>> the

>>>>>>> number of push instructions. One of the optimization it does is to push lr

>>>>>>> alongside high register(s) (after having moved them to low register(s)) when

>>>>>>> there is no low register to save. The way this is implemented is to add lr to

>>>>>>> the list of registers that can be pushed just before the push happens. This

>>>>>>> would then push lr and allows it to be used for further push if there was not

>>>>>>> enough registers to push all high registers to be pushed.

>>>>>>>

>>>>>>> However, the logic that decides what register to move high registers to

>>>>>>> before

>>>>>>> being pushed only looks at low registers (see for loop initialization). This

>>>>>>> means not only that lr is not used for pushing high registers but also

>>>>>>> that lr

>>>>>>> is not removed from the list of registers to be pushed when it's not used.

>>>>>>> This

>>>>>>> extra lr push is not poped in epilogue leading in stack corruption.

>>>>>>>

>>>>>>> This patch changes the loop to iterate over register r0 to lr so as to both

>>>>>>> fix

>>>>>>> the stack corruption and reuse lr to push some high register when possible.

>>>>>>>

>>>>>>> ChangeLog entry are as follow:

>>>>>>>

>>>>>>> *** gcc/ChangeLog ***

>>>>>>>

>>>>>>> 2016-11-01  Thomas Preud'homme <thomas.preudhomme@arm.com>

>>>>>>>

>>>>>>>         PR target/77933

>>>>>>>         * config/arm/arm.c (thumb1_expand_prologue): Also check for lr

>>>>>>> being a

>>>>>>>         pushable register.

>>>>>>>

>>>>>>>

>>>>>>> *** gcc/testsuite/ChangeLog ***

>>>>>>>

>>>>>>> 2016-11-01  Thomas Preud'homme <thomas.preudhomme@arm.com>

>>>>>>>

>>>>>>>         PR target/77933

>>>>>>>         * gcc.target/arm/pr77933.c: New test.

>>>>>>>

>>>>>>>

>>>>>>> Testing: no regression on arm-none-eabi GCC cross-compiler targeting

>>>>>>> Cortex-M0

>>>>>>>

>>>>>>> Is this ok for trunk?

>>>>>>>

>>>>>>> Best regards,

>>>>>>>

>>>>>>> Thomas

>>>>>
Thomas Preudhomme Dec. 9, 2016, 3:27 p.m. UTC | #2
On 09/12/16 14:30, Kyrill Tkachov wrote:
>

> On 09/12/16 14:24, Thomas Preudhomme wrote:

>> [Seeing as an RC for GCC 6.3 was suggested on IRC for mid next week]

>>

>> Ping?

>>

>> backport for 6 bootstraps on Thumb-1 and testsuite shows no regression for

>> either 5 or 6. Bootstrap for 5 is ongoing.

>>

>

> Ok.

> Thanks,

> Kyrill


I've committed the backport for 6 but am waiting for the bootstrap of the 
backport for 5 to complete before committing it as well. Will probably only be 
Monday.

Best regards,

Thomas
Thomas Preudhomme Dec. 13, 2016, 9:41 a.m. UTC | #3
On 09/12/16 15:27, Thomas Preudhomme wrote:
>

>

> On 09/12/16 14:30, Kyrill Tkachov wrote:

>>

>> On 09/12/16 14:24, Thomas Preudhomme wrote:

>>> [Seeing as an RC for GCC 6.3 was suggested on IRC for mid next week]

>>>

>>> Ping?

>>>

>>> backport for 6 bootstraps on Thumb-1 and testsuite shows no regression for

>>> either 5 or 6. Bootstrap for 5 is ongoing.

>>>

>>

>> Ok.

>> Thanks,

>> Kyrill

>

> I've committed the backport for 6 but am waiting for the bootstrap of the

> backport for 5 to complete before committing it as well. Will probably only be

> Monday.


I've finally committed the backport to gcc-5-branch. The bootstrap failed when 
using in-tree gmp, mpfr, mpc and isl so I had to restart it. Bootstrap succeeded.

PR77933 is now fixed in all supported branches, but note that it still affects 
gcc-4_7-branch, gcc-4_8-branch and gcc-4_9-branch.

Best regards,

Thomas
diff mbox

Patch

diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index 83cb13d1195beb19d6301f5c83a7eb544a91d877..1dba035c62c97a5f723d02208636c92108427379 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -24710,6 +24710,7 @@  thumb1_expand_prologue (void)
   unsigned long live_regs_mask;
   unsigned long l_mask;
   unsigned high_regs_pushed = 0;
+  bool lr_needs_saving;
 
   func_type = arm_current_func_type ();
 
@@ -24732,6 +24733,7 @@  thumb1_expand_prologue (void)
 
   offsets = arm_get_frame_offsets ();
   live_regs_mask = offsets->saved_regs_mask;
+  lr_needs_saving = live_regs_mask & (1 << LR_REGNUM);
 
   /* Extract a mask of the ones we can give to the Thumb's push instruction.  */
   l_mask = live_regs_mask & 0x40ff;
@@ -24798,6 +24800,7 @@  thumb1_expand_prologue (void)
 	{
 	  insn = thumb1_emit_multi_reg_push (l_mask, l_mask);
 	  RTX_FRAME_RELATED_P (insn) = 1;
+	  lr_needs_saving = false;
 
 	  offset = bit_count (l_mask) * UNITS_PER_WORD;
 	}
@@ -24862,12 +24865,13 @@  thumb1_expand_prologue (void)
      be a push of LR and we can combine it with the push of the first high
      register.  */
   else if ((l_mask & 0xff) != 0
-	   || (high_regs_pushed == 0 && l_mask))
+	   || (high_regs_pushed == 0 && lr_needs_saving))
     {
       unsigned long mask = l_mask;
       mask |= (1 << thumb1_extra_regs_pushed (offsets, true)) - 1;
       insn = thumb1_emit_multi_reg_push (mask, mask);
       RTX_FRAME_RELATED_P (insn) = 1;
+      lr_needs_saving = false;
     }
 
   if (high_regs_pushed)
@@ -24885,7 +24889,9 @@  thumb1_expand_prologue (void)
       /* Here we need to mask out registers used for passing arguments
 	 even if they can be pushed.  This is to avoid using them to stash the high
 	 registers.  Such kind of stash may clobber the use of arguments.  */
-      pushable_regs = l_mask & (~arg_regs_mask) & 0xff;
+      pushable_regs = l_mask & (~arg_regs_mask);
+      if (lr_needs_saving)
+	pushable_regs &= ~(1 << LR_REGNUM);
 
       if (pushable_regs == 0)
 	pushable_regs = 1 << thumb_find_work_register (live_regs_mask);
@@ -24893,8 +24899,9 @@  thumb1_expand_prologue (void)
       while (high_regs_pushed > 0)
 	{
 	  unsigned long real_regs_mask = 0;
+	  unsigned long push_mask = 0;
 
-	  for (regno = LAST_LO_REGNUM; regno >= 0; regno --)
+	  for (regno = LR_REGNUM; regno >= 0; regno --)
 	    {
 	      if (pushable_regs & (1 << regno))
 		{
@@ -24903,6 +24910,7 @@  thumb1_expand_prologue (void)
 
 		  high_regs_pushed --;
 		  real_regs_mask |= (1 << next_hi_reg);
+		  push_mask |= (1 << regno);
 
 		  if (high_regs_pushed)
 		    {
@@ -24912,23 +24920,20 @@  thumb1_expand_prologue (void)
 			  break;
 		    }
 		  else
-		    {
-		      pushable_regs &= ~((1 << regno) - 1);
-		      break;
-		    }
+		    break;
 		}
 	    }
 
 	  /* If we had to find a work register and we have not yet
 	     saved the LR then add it to the list of regs to push.  */
-	  if (l_mask == (1 << LR_REGNUM))
+	  if (lr_needs_saving)
 	    {
-	      pushable_regs |= l_mask;
-	      real_regs_mask |= l_mask;
-	      l_mask = 0;
+	      push_mask |= 1 << LR_REGNUM;
+	      real_regs_mask |= 1 << LR_REGNUM;
+	      lr_needs_saving = false;
 	    }
 
-	  insn = thumb1_emit_multi_reg_push (pushable_regs, real_regs_mask);
+	  insn = thumb1_emit_multi_reg_push (push_mask, real_regs_mask);
 	  RTX_FRAME_RELATED_P (insn) = 1;
 	}
     }
diff --git a/gcc/testsuite/gcc.target/arm/pr77933-1.c b/gcc/testsuite/gcc.target/arm/pr77933-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..95cf68ea7531bcc453371f493a05bd40caa5541b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/pr77933-1.c
@@ -0,0 +1,46 @@ 
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+__attribute__ ((noinline, noclone)) void
+clobber_lr_and_highregs (void)
+{
+  __asm__ volatile ("" : : : "r8", "r9", "lr");
+}
+
+int
+main (void)
+{
+  int ret;
+
+  __asm volatile ("mov\tr4, #0xf4\n\t"
+		  "mov\tr5, #0xf5\n\t"
+		  "mov\tr6, #0xf6\n\t"
+		  "mov\tr7, #0xf7\n\t"
+		  "mov\tr0, #0xf8\n\t"
+		  "mov\tr8, r0\n\t"
+		  "mov\tr0, #0xfa\n\t"
+		  "mov\tr10, r0"
+		  : : : "r0", "r4", "r5", "r6", "r7", "r8", "r10");
+
+  clobber_lr_and_highregs ();
+
+  __asm volatile ("cmp\tr4, #0xf4\n\t"
+		  "bne\tfail\n\t"
+		  "cmp\tr5, #0xf5\n\t"
+		  "bne\tfail\n\t"
+		  "cmp\tr6, #0xf6\n\t"
+		  "bne\tfail\n\t"
+		  "cmp\tr7, #0xf7\n\t"
+		  "bne\tfail\n\t"
+		  "mov\tr0, r8\n\t"
+		  "cmp\tr0, #0xf8\n\t"
+		  "bne\tfail\n\t"
+		  "mov\tr0, r10\n\t"
+		  "cmp\tr0, #0xfa\n\t"
+		  "bne\tfail\n\t"
+		  "mov\t%0, #1\n"
+		  "fail:\n\t"
+		  "sub\tr0, #1"
+		  : "=r" (ret) : :);
+  return ret;
+}
diff --git a/gcc/testsuite/gcc.target/arm/pr77933-2.c b/gcc/testsuite/gcc.target/arm/pr77933-2.c
new file mode 100644
index 0000000000000000000000000000000000000000..9028c4fcab4229591fa057f15c641d2b5597cd1d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/pr77933-2.c
@@ -0,0 +1,47 @@ 
+/* { dg-do run } */
+/* { dg-skip-if "" { ! { arm_thumb1_ok || arm_thumb2_ok } } } */
+/* { dg-options "-mthumb -O2 -mtpcs-leaf-frame" } */
+
+__attribute__ ((noinline, noclone)) void
+clobber_lr_and_highregs (void)
+{
+  __asm__ volatile ("" : : : "r8", "r9", "lr");
+}
+
+int
+main (void)
+{
+  int ret;
+
+  __asm volatile ("mov\tr4, #0xf4\n\t"
+		  "mov\tr5, #0xf5\n\t"
+		  "mov\tr6, #0xf6\n\t"
+		  "mov\tr7, #0xf7\n\t"
+		  "mov\tr0, #0xf8\n\t"
+		  "mov\tr8, r0\n\t"
+		  "mov\tr0, #0xfa\n\t"
+		  "mov\tr10, r0"
+		  : : : "r0", "r4", "r5", "r6", "r7", "r8", "r10");
+
+  clobber_lr_and_highregs ();
+
+  __asm volatile ("cmp\tr4, #0xf4\n\t"
+		  "bne\tfail\n\t"
+		  "cmp\tr5, #0xf5\n\t"
+		  "bne\tfail\n\t"
+		  "cmp\tr6, #0xf6\n\t"
+		  "bne\tfail\n\t"
+		  "cmp\tr7, #0xf7\n\t"
+		  "bne\tfail\n\t"
+		  "mov\tr0, r8\n\t"
+		  "cmp\tr0, #0xf8\n\t"
+		  "bne\tfail\n\t"
+		  "mov\tr0, r10\n\t"
+		  "cmp\tr0, #0xfa\n\t"
+		  "bne\tfail\n\t"
+		  "mov\t%0, #1\n"
+		  "fail:\n\t"
+		  "sub\tr0, #1"
+		  : "=r" (ret) : :);
+  return ret;
+}