This is the mail archive of the binutils@sourceware.cygnus.com mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

MIPS gcc `leasi' pattern vs. binutils 'la' pseudo-op.


I've think tracked down a bug in MIPS gcc or binutils.  However, I'm
not sure which one actually has the bug.  8-)

I started with binutils 2.9.1 and gcc 2.95.2, but have verified that
the current binutils exhibits the same behaviour w.r.t. assembling
`la', and the current gcc mips.md seems to still have an `leasi'
pattern with the same definition.

for the test fragment below (full test case at end of message, which
somebody might want to put into the test suite with minor mods as
appropriate to fit):

	#define K0BASE 0x80000000
	#define K2BASE 0xc0000000

	  unsigned long addr;

	  [ ... ]

	  if (addr >= K0BASE && addr < K2BASE)
	    printf("in range!\n");
	  else 
	    printf("out of range.\n");

'gcc -O2 -mips3 -mlong32' (where 'gcc' is actually a cross-compiler
targetted at mips) emits the code:

	[ addr is in $2 ]
        la $2,-2147483648($2)
        li      $3,1073676288                   # 0x3fff0000
        ori     $3,$3,0xffff
        sltu    $3,$3,$2

using the leasi pattern.  gas (again -mips3) assembles that `la' into:

  400514:       3c018000        lui     $at,0x8000
  400518:       0022102d        daddu   $v0,$at,$v0

i.e., it generates a 64-bit result which does not have bit 31 copied
into bits 32-63 of the result.

when addr is of the form 0xffffffff8000000N (sign-extended from
0x8000000N), for instance, the result of the daddu ends up being
0xffffffff0000000N, which fails the test.

with -mips1 instead of -mips3, gcc again generates:

	[ addr is in $2 ]
        la $2,-2147483648($2)
        li      $3,1073676288                   # 0x3fff0000
        ori     $3,$3,0xffff
        sltu    $3,$3,$2

gas (-mips1) assembles that `la' into:

  400514:       3c018000        lui     $at,0x8000
  400518:       00221021        addu    $v0,$at,$v0

causing bit 31 of the result to be copied into bits 32-64.  run on a
processor with 64-bit regs, with the same input, the result ends up
being 0x000000000000000N, which passes the test.


i don't have any real reference for the expected behaviour of `la',
i.e. whether it's supposed to create 32-bit values (sign-extended to
64-bits on 64-bit machines), or whether it's supposed to create
register-sized values.

as far as I can tell, either:

	* `la' should be convinced to always generate values in a way
	  that causes them to be sign-extended (and `dla' should be
	  used for leadi), or

	* the leasi pattern should only use `la' when it should be
	  "safe" (i.e., when TARGET_64BIT is not true -- note that
	  this assumes that the assembler will be using the same
	  ISA variant that the compiler is).

Looking at the way the leasi pattern is defined in gcc, and the la op
is implemented in gas, it's much easier to fix the former.  8-)

I've included a patch to leasi below, which i'm now using in my
sources.  Somebody should probably sanity check it and run it through
the test suite.  (I can't easily do so at this time, though i have
used a compiler built with the change to successfully build my
cross-compilation environment including libraries, and the test
program works.)


chris
===================================================================
2000-06-26  Chris Demetriou  <cgd@sibyte.com>

	* config/mips/mips.md (leasi): Do not use this pattern if
	TARGET_64BIT, since gas generates a result that is not
	sign extended when assembling for ISAs which have 64-bit
	registers.

Index: config/mips/mips.md
===================================================================
RCS file: /cvsroot/systemsw/tools/src/gcc/gcc/config/mips/mips.md,v
retrieving revision 1.2
diff -c -r1.2 mips.md
*** mips.md	2000/04/10 20:29:34	1.2
--- mips.md	2000/06/27 03:15:49
***************
*** 10422,10431 ****
  ;; I have no idea how many insns this can actually generate.  It should
  ;; be rare, so over-estimating as 10 instructions should not have any
  ;; real performance impact.
  (define_insn "leasi"
    [(set (match_operand:SI 0 "register_operand" "=d")
          (match_operand:SI 1 "address_operand" "p"))]
!   "Pmode == SImode"
    "la %0,%a1"
    [(set_attr "type"	"arith")
     (set_attr "mode"	"SI")
--- 10422,10436 ----
  ;; I have no idea how many insns this can actually generate.  It should
  ;; be rare, so over-estimating as 10 instructions should not have any
  ;; real performance impact.
+ 
+ ;; Note that you can't use leasi on ISAs with 64-bit regs but where
+ ;; you're using 32-bit longs.  The assembler turns `la' into a sequence
+ ;; which doesn't sign-extend the low 32 bits of the reg into the high 32
+ ;; bits.
  (define_insn "leasi"
    [(set (match_operand:SI 0 "register_operand" "=d")
          (match_operand:SI 1 "address_operand" "p"))]
!   "(Pmode == SImode) && !TARGET_64BIT"
    "la %0,%a1"
    [(set_attr "type"	"arith")
     (set_attr "mode"	"SI")
===================================================================
#include <stdlib.h>
#include <stdio.h>

#define K0BASE 0x80000000
#define K2BASE 0xc0000000

unsigned long val = 0x80000003;

unsigned long
foo(void)
{
  return val;
}

int
main(int argc, void **argv)
{
  unsigned long addr;

  addr = foo();
  if (addr >= K0BASE && addr < K2BASE)
    printf("in range!\n");
  else 
    printf("out of range.\n");

  return 0;
}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]