c/2898: Illegal function return in ARM code when compiling with -mthumb-interwork -O2

Philip Blundell philb@gnu.org
Tue Sep 18 01:46:00 GMT 2001


Did you have any further thoughts on this?

p.

--

From: Philip Blundell <philb@gnu.org>
Date: Tue, 29 May 2001 20:34:44 +0100
To: Richard.Earnshaw@arm.com
Cc: gcc-patches@gcc.gnu.org
Subject: Re: c/2898: Illegal function return in ARM code when compiling with -mthumb-interwork -O2 

>I realise this is what the old code did, but it's a truly horrible hack, 
>particularly since it leaves a trailing space.  Any chance the code could 
>be reworked to avoid the need for this?  BTW, repeated use of strcat is 
>horribly inefficient.

Well, here's another version (in `diff -c' form for your viewing pleasure).
It abolishes most of the repeated strcats; in terms of ugly code this cure is 
in danger of being worse than the original disease, but you can judge for 
yourself.

p.

Index: arm.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/arm/arm.c,v
retrieving revision 1.146
diff -u -p -c -r1.146 arm.c
*** arm.c	2001/05/24 21:09:05	1.146
--- arm.c	2001/05/29 19:28:25
*************** output_return_instruction (operand, real
*** 7033,7053 ****
  
    live_regs_mask = arm_compute_save_reg_mask ();
  
!   /* On some ARM architectures it is faster to use LDR rather than LDM to
!      load a single register.  On other architectures, the cost is the same.
!      In 26 bit mode we have to use LDM in order to be able to restore the CPSR.  */
!   if ((live_regs_mask  == (1 << LR_REGNUM))
!       && ! TARGET_INTERWORK
!       && ! IS_INTERRUPT (func_type)
!       && (! really_return || TARGET_APCS_32))
      {
!       if (! really_return)
! 	sprintf (instr, "ldr%s\t%%|lr, [%%|sp], #4", conditional);
        else
! 	sprintf (instr, "ldr%s\t%%|pc, [%%|sp], #4", conditional);
!     }
!   else if (live_regs_mask)
!     {
        if ((live_regs_mask & (1 << IP_REGNUM)) == (1 << IP_REGNUM))
  	/* There are two possible reasons for the IP register being saved.
  	   Either a stack frame was created, in which case IP contains the
--- 7050,7069 ----
  
    live_regs_mask = arm_compute_save_reg_mask ();
  
!   if (live_regs_mask)
      {
!       const char * return_reg;
! 
!       /* If we do not have any special requirements for function exit 
! 	 (eg interworking, or ISR) then we can load the return address 
! 	 directly into the PC.  Otherwise we must load it into LR.  */
!       if (! TARGET_INTERWORK
! 	  && ! IS_INTERRUPT (func_type)
! 	  && really_return)
! 	return_reg = reg_names[PC_REGNUM];
        else
! 	return_reg = reg_names[LR_REGNUM];
! 
        if ((live_regs_mask & (1 << IP_REGNUM)) == (1 << IP_REGNUM))
  	/* There are two possible reasons for the IP register being saved.
  	   Either a stack frame was created, in which case IP contains the
*************** output_return_instruction (operand, real
*** 7058,7103 ****
  	    live_regs_mask &= ~ (1 << IP_REGNUM);
  	    live_regs_mask |=   (1 << SP_REGNUM);
  	  }
- 
-       /* Generate the load multiple instruction to restore the registers.  */
-       if (frame_pointer_needed)
- 	sprintf (instr, "ldm%sea\t%%|fp, {", conditional);
-       else
- 	sprintf (instr, "ldm%sfd\t%%|sp!, {", conditional);
  
!       for (reg = 0; reg <= SP_REGNUM; reg++)
! 	if (live_regs_mask & (1 << reg))
! 	  {
! 	    strcat (instr, "%|");
! 	    strcat (instr, reg_names[reg]);
! 	    strcat (instr, ", ");
! 	  }
! 
!       if ((live_regs_mask & (1 << LR_REGNUM)) == 0)
  	{
! 	  /* If we are not restoring the LR register then we will
! 	     have added one too many commas to the list above.
! 	     Replace it with a closing brace.  */
! 	  instr [strlen (instr) - 2] =  '}';
  	}
        else
  	{
! 	  strcat (instr, "%|");
  
! 	  /* At this point there should only be one or two registers left in
! 	     live_regs_mask: always LR, and possibly PC if we created a stack
! 	     frame.  LR contains the return address.  If we do not have any
! 	     special requirements for function exit (eg interworking, or ISR)
! 	     then we can load this value directly into the PC and save an
! 	     instruction.  */
! 	  if (! TARGET_INTERWORK
! 	      && ! IS_INTERRUPT (func_type)
! 	      && really_return)
! 	    strcat (instr, reg_names [PC_REGNUM]);
  	  else
! 	    strcat (instr, reg_names [LR_REGNUM]);
  
! 	  strcat (instr, (TARGET_APCS_32 || !really_return) ? "}" : "}^");
  	}
  
        if (really_return)
--- 7074,7135 ----
  	    live_regs_mask &= ~ (1 << IP_REGNUM);
  	    live_regs_mask |=   (1 << SP_REGNUM);
  	  }
  
!       /* On some ARM architectures it is faster to use LDR rather than LDM to
! 	 load a single register.  On other architectures, the cost is the same.
! 	 In 26 bit mode we have to use LDM in order to be able to restore the CPSR.  */
!       if ((live_regs_mask  == (1 << LR_REGNUM))
! 	  && (! really_return || TARGET_APCS_32))
  	{
! 	  sprintf (instr, "ldr%s\t%%|%s, [%%|sp], #4", conditional, return_reg);
  	}
        else
  	{
! 	  char *p;
! 	  int first = 1;
  
! 	  /* Generate the load multiple instruction to restore the registers.  */
! 	  if (frame_pointer_needed)
! 	    sprintf (instr, "ldm%sea\t%%|fp, {", conditional);
  	  else
! 	    sprintf (instr, "ldm%sfd\t%%|sp!, {", conditional);
! 
! 	  p = instr + strlen (instr);
! 
! 	  for (reg = 0; reg <= SP_REGNUM; reg++)
! 	    if (live_regs_mask & (1 << reg))
! 	      {
! 		int l = strlen (reg_names[reg]);
! 
! 		if (first)
! 		  first = 0;
! 		else
! 		  {
! 		    memcpy (p, ", ", 2);
! 		    p += 2;
! 		  }
! 
! 		memcpy (p, "%|", 2);
! 		memcpy (p + 2, reg_names[reg], l);
! 		p += l + 2;
! 	      }
! 	  
! 	  if (live_regs_mask & (1 << LR_REGNUM))
! 	    {
! 	      int l = strlen (return_reg);
  
! 	      if (! first)
! 		{
! 		  memcpy (p, ", ", 2);
! 		  p += 2;
! 		}
! 
! 	      memcpy (p, "%|", 2);
! 	      memcpy (p + 2, return_reg, l);
! 	      strcpy (p + 2 + l, (TARGET_APCS_32 || !really_return) ? "}" : "}^");
! 	    }
! 	  else
! 	    strcpy (p, "}");
  	}
  
        if (really_return)


-- 
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.5 (GNU/Linux)
Comment: Exmh version 2.1.1 10/15/1999 (debian)

iD8DBQE7pwnKVTLPJe9CT30RAkaCAJ9vmW+tbIrK1kk6ETavYtl+fURvogCePbIi
0ESbm+vijgprItufGLUf3BA=
=8uaa
-----END PGP SIGNATURE-----


More information about the Gcc-patches mailing list