Subject: egcs-980406 (and earlier)/ARM ldrsb - incorrect offset range

From: Dave Gilbert <[email-masked]>
Date: Sat, 11 Apr 1998 01:36:41 +0100 (BST)

Hi,
  In egcs-980406 (and earlier versions) the address offset range of the
ldrsb instruction (introduced in ARM architecture v4) is set to be the
same as normal ARM ldr (load register) instructions when it is actually
a lot smaller.  The following example excersises this:

------------------------------------------------------------------------
struct foo {
  int padding[200];
  signed char toget;
};

signed char bar(struct foo* p) {
  return p->toget;
};
-------------------------------------------------------------------------
gcc -mcpu=strongarm110  egbug.c -O2
/tmp/cca06251.s: Assembler messages:
/tmp/cca06251.s:19: Error: address offset too large

_bar:
        @ args = 0, pretend = 0, frame = 0
        @ frame_needed = 1, current_function_anonymous_args = 0
        mov     ip, sp
        stmfd   sp!, {fp, ip, lr, pc}
        sub     fp, ip, #4
        ldrsb   r0, [r0, #800]     ******** Range should be +/- 255
        ldmea   fp, {fp, sp, pc}^

(This cropped up while trying to compile the tulip (DEC 21x ether)
driver in Linux for StrongARM, while cross building from Alpha)

Dave

 --------------------------------------------------------------------
/ Dr. David Alan Gilbert      | Running Linux on Alpha & ARM         \ 
\   gro.gilbert @ treblig.org | ------- Happy in hex -------         /
 \____________________________|___ http://www.treblig.demon.co.uk __/

unsubscribe: body of `unsubscribe linux-arm' to [email-masked]

---

From: Nick Clifton <[email-masked]>
Date: Mon, 13 Apr 1998 11:30:29 -0700

: Date: Sat, 11 Apr 1998 01:36:41 +0100 (BST)
: From: Dave Gilbert <[email-masked]>
: 
: Hi,
:   In egcs-980406 (and earlier versions) the address offset range of the
: ldrsb instruction (introduced in ARM architecture v4) is set to be the
: same as normal ARM ldr (load register) instructions when it is actually
: a lot smaller.  The following example excersises this:
: 
: ------------------------------------------------------------------------
: struct foo {
:   int padding[200];
:   signed char toget;
: };
: 
: signed char bar(struct foo* p) {
:   return p->toget;
: };
: -------------------------------------------------------------------------
: gcc -mcpu=strongarm110  egbug.c -O2
: /tmp/cca06251.s: Assembler messages:
: /tmp/cca06251.s:19: Error: address offset too large
: 
: _bar:
:         @ args = 0, pretend = 0, frame = 0
:         @ frame_needed = 1, current_function_anonymous_args = 0
:         mov     ip, sp
:         stmfd   sp!, {fp, ip, lr, pc}
:         sub     fp, ip, #4
:         ldrsb   r0, [r0, #800]     ******** Range should be +/- 255
:         ldmea   fp, {fp, sp, pc}^
: 

Applying this patch fixes the problem:

Index: arm.h
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/config/arm/arm.h,v
retrieving revision 1.5
diff -p -r1.5 arm.h
*** arm.h	1998/04/08 06:19:00	1.5
--- arm.h	1998/04/13 18:30:02
*************** do									\
*** 1408,1414 ****
  	      && INTVAL (op) <= 31)					\
  	    goto LABEL;							\
          }								\
!       range = (MODE) == HImode ? (arm_arch4 ? 256 : 4095) : 4096;	\
        if (code == CONST_INT && INTVAL (INDEX) < range			\
  	  && INTVAL (INDEX) > -range)  	      				\
          goto LABEL;							\
--- 1408,1416 ----
  	      && INTVAL (op) <= 31)					\
  	    goto LABEL;							\
          }								\
!       /* NASTY: Since this limits the addressing of unsigned byte loads */      \
!       range = ((MODE) == HImode || (MODE) == QImode)                    \
!               ? (arm_arch4 ? 256 : 4095) : 4096;                        \
        if (code == CONST_INT && INTVAL (INDEX) < range			\
  	  && INTVAL (INDEX) > -range)  	      				\
          goto LABEL;							\

The code now produced looks like this:

  _bar:
        mov     ip, sp
        mov     r3, #800
        sub     fp, ip, #4
        ldrsb   r0, [r0, r3]
        ldmea   fp, {fp, sp, pc}

Nick