Discussion:
mips address+symbol issue.
(too old to reply)
c***@broadcom.com
2004-01-23 21:00:59 UTC
Permalink
This is case of strange compiler + assembler issues ("surprise
surprise" 8-). I ran into it testing mipsisa64-elf on real HW.
(strangely, sim didn't flag it, dunno what's up with that.)

I was testing code with and without -mno-explicit-relocs, to see if a
change i made to binutils was OK. (I was using a combined tree w/
sources as of 2004-01-21.)

The problematic test is gcc.c-torture/execute/960321-1.c. I was
testing on a real target board (BCM91250A), with "-Tcfe.ld" as the
linker script. The test binary comes out as EABI64 in that
configuration.

For the -O0 case, GCC is emitting code like:

acc_a:
.frame $fp,16,$31 # vars= 8, regs= 1/0, args= 0,
gp= 0
.mask 0x40000000,-8
.fmask 0x00000000,0
daddiu $sp,$sp,-16
sd $fp,8($sp)
move $fp,$sp
sd $4,0($fp)
ld $3,0($fp)
dla $2,a-2000000000
daddu $2,$3,$2
lb $2,0($2)

What's happening is that the assembler turns that 'dla' into:

14: 3c0288ca lui v0,0x88ca
14: R_MIPS_HI16 a
18: 24426c00 addiu v0,v0,27648
18: R_MIPS_LO16 a

and then at link time A ends in kseg0:

80022808 g O .data 0000000a a

so the final instructions in the object file end up as:

8002020c: 3c0208cd lui v0,0x8cd
80020210: 24429408 addiu v0,v0,-27640

which end up having the effect:

v0 = 0x8cc9408

Unfortunately:

0x8cc9408 + 2000000000 != 0x(ffffffff)80022808


without -mno-explicit-relocs, the compiler gets it right.


I can think of several possible solutions here:

(1) don't allow GCC to emit "dla sym+offset" for
-mno-explicit-relocs, or maybe just for -mno-explicit-relocs
and the ABIs which stuff 64-bit pointers into 32-bit object
files.

(2) make binutils emit long code sequences for sym + offset when
assembling objects for an ABI which stuffs 64-bit pointers
into 32-bit object files.

(3) punt. (maybe even xfail the test for some MIPS targets w/
-mno-explicit-relocs.)

It seems to me that (2) is *probably* the right fix, but others'
opinions would be appreciated. I think I'm OK with (3), too, but it
might bite others down the road...



chris
Ian Lance Taylor
2004-01-23 21:09:33 UTC
Permalink
Post by c***@broadcom.com
(1) don't allow GCC to emit "dla sym+offset" for
-mno-explicit-relocs, or maybe just for -mno-explicit-relocs
and the ABIs which stuff 64-bit pointers into 32-bit object
files.
(2) make binutils emit long code sequences for sym + offset when
assembling objects for an ABI which stuffs 64-bit pointers
into 32-bit object files.
(3) punt. (maybe even xfail the test for some MIPS targets w/
-mno-explicit-relocs.)
It seems to me that (2) is *probably* the right fix, but others'
opinions would be appreciated. I think I'm OK with (3), too, but it
might bite others down the road...
I'm not really happy about (2), because for those people who stuff
64-bit pointers into 32-bit object files it pessimizes the ordinary
case.

Personally, I would just punt. It's too bad that code like that
doesn't work, but does anybody really write it? Plus it's not
supported by the C standard.

Ian
c***@broadcom.com
2004-01-23 21:17:51 UTC
Permalink
Post by Ian Lance Taylor
Post by c***@broadcom.com
(1) don't allow GCC to emit "dla sym+offset" for
-mno-explicit-relocs, or maybe just for -mno-explicit-relocs
and the ABIs which stuff 64-bit pointers into 32-bit object
files.
(2) make binutils emit long code sequences for sym + offset when
assembling objects for an ABI which stuffs 64-bit pointers
into 32-bit object files.
(3) punt. (maybe even xfail the test for some MIPS targets w/
-mno-explicit-relocs.)
It seems to me that (2) is *probably* the right fix, but others'
opinions would be appreciated. I think I'm OK with (3), too, but it
might bite others down the road...
I'm not really happy about (2), because for those people who stuff
64-bit pointers into 32-bit object files it pessimizes the ordinary
case.
yes, exactly. It's also harder than (3). 8-)
Post by Ian Lance Taylor
Personally, I would just punt. It's too bad that code like that
doesn't work, but does anybody really write it? Plus it's not
supported by the C standard.
(I don't know what the C standard says about this... I thought it had
issues with accessing an array out of bounds, which this doesn't do.
the array ends up being accessed with index 0, but how that index is
calculated is fairly tortuous.)

I could easily believe that there are people who write code like this,
e.g. to turn kseg0 array accesses into kseg1 array accesses. (IIRC,
there was something like that on a Linux port on another arch, right?
where bad array offsets were behaving unexpectedly.) But that doesn't
mean it's a good thing to do (and the code generation would work out
OK in that case, since they wouldn't be moving between regions w/
differences in the 31st bit of the address).


(I think i'm fine w/ (3), but i'd like to hear others' opinions.)


chris
Ian Lance Taylor
2004-01-23 21:20:27 UTC
Permalink
Post by c***@broadcom.com
(I don't know what the C standard says about this... I thought it had
issues with accessing an array out of bounds, which this doesn't do.
the array ends up being accessed with index 0, but how that index is
calculated is fairly tortuous.)
(My understanding is that the C standard doesn't even permit you to
form the address of an out-of-bounds array access. Doing so moves you
out of the range of standardized behaviour, and no promises are made.)

Ian
Robert Dewar
2004-01-23 21:44:39 UTC
Permalink
Post by Ian Lance Taylor
(My understanding is that the C standard doesn't even permit you to
form the address of an out-of-bounds array access. Doing so moves you
out of the range of standardized behaviour, and no promises are made.)
Ian
That's correct, with the odd exception that you can point just past
the end of an array, and get a valid pointer value that can be
compared with any pointer within the array (it's larger :-) but
cannot be dereferenced. At least that's my understanding!
c***@broadcom.com
2004-01-23 21:54:49 UTC
Permalink
Post by Ian Lance Taylor
(My understanding is that the C standard doesn't even permit you to
form the address of an out-of-bounds array access. Doing so moves you
out of the range of standardized behaviour, and no promises are made.)
FWIW, looking at the test case, and not being a language lawyer, i
don't know that it *does* form the address of an out-of-bounds array
access.

the test case is:

char a[10] = "deadbeef";

char
acc_a (long i)
{
return a[i-2000000000L];
}

main ()
{
if (acc_a (2000000000L) != 'd')
abort ();
exit (0);
}


Two ways to interpret this, i guess:

long tmp;

tmp = i - 2000000000L;
return a[tmp];

and:
char *tmp;

tmp = &a[-2000000000L];
return *(tmp + i); // or just: return tmp[i];


I'd suspect the former is what the order of operations would require,
but the latter is what the compiler's emitting as assembly code.


chris
Ian Lance Taylor
2004-01-23 21:59:38 UTC
Permalink
Post by c***@broadcom.com
Post by Ian Lance Taylor
(My understanding is that the C standard doesn't even permit you to
form the address of an out-of-bounds array access. Doing so moves you
out of the range of standardized behaviour, and no promises are made.)
FWIW, looking at the test case, and not being a language lawyer, i
don't know that it *does* form the address of an out-of-bounds array
access.
You're right. My error. This probably ought to work.

Still, I don't think many people write code this way.

Ian
Erik Trulsson
2004-01-23 22:10:51 UTC
Permalink
Post by c***@broadcom.com
Post by Ian Lance Taylor
(My understanding is that the C standard doesn't even permit you to
form the address of an out-of-bounds array access. Doing so moves you
out of the range of standardized behaviour, and no promises are made.)
I believe that is correct.
Post by c***@broadcom.com
FWIW, looking at the test case, and not being a language lawyer, i
don't know that it *does* form the address of an out-of-bounds array
access.
It does.
Post by c***@broadcom.com
char a[10] = "deadbeef";
char
acc_a (long i)
{
return a[i-2000000000L];
}
main ()
{
if (acc_a (2000000000L) != 'd')
abort ();
exit (0);
}
There are many ways to interpret one could interpret it, but the
interpretation that I believe is the correct is the following:

Remember that a[i] is just syntactic sugar for *(a+i).
This means that the expression a[i-2000000000L] is the same as
*(a+i-2000000000L). With i==2000000000L, this becomes
*(a+2000000000L-2000000000L) which is the same as
*((a+2000000000L)-2000000000L).
a+2000000000L is *not* a valid address, and thus undefined behaviour is
invoked.
--
<Insert your favourite quote here.>
Erik Trulsson
***@student.uu.se
Andreas Schwab
2004-01-23 22:21:53 UTC
Permalink
Post by Erik Trulsson
Remember that a[i] is just syntactic sugar for *(a+i).
Actually it is (*((a)+(i))).
Post by Erik Trulsson
This means that the expression a[i-2000000000L] is the same as
*(a+i-2000000000L).
(*((a)+(i-2000000000L)))
Post by Erik Trulsson
With i==2000000000L, this becomes *(a+2000000000L-2000000000L)
(*((a)+(2000000000L-2000000000L)))
Post by Erik Trulsson
which is the same as *((a+2000000000L)-2000000000L).
(*((a)+(0)))

All expressions are perfectly valid.

Andreas.
--
Andreas Schwab, SuSE Labs, ***@suse.de
SuSE Linux AG, Maxfeldstraße 5, 90409 Nürnberg, Germany
Key fingerprint = 58CA 54C7 6D53 942B 1756 01D3 44D5 214B 8276 4ED5
"And now for something completely different."
Robert Dewar
2004-01-23 22:26:37 UTC
Permalink
Post by Erik Trulsson
Remember that a[i] is just syntactic sugar for *(a+i).
I don't think that's right, I think the intention is that
a[i] is syntactic sugar for *(a+(i)). Nothing else makes
sense. For example you would be saying that you could
not have a subscript of the form x[a==b] since it would
mean *((x+a) == b). That just can't be right.

To me the quoted program is clearly correct, and if it
does not work, it's a compiler bug.

Note that your interpretation would also mean that

int a[10];
int x;

x = 11;
.. = a[x-2];

would be undefined, and that seems equally implausible.
Erik Trulsson
2004-01-23 22:46:47 UTC
Permalink
Post by Robert Dewar
Post by Erik Trulsson
Remember that a[i] is just syntactic sugar for *(a+i).
I don't think that's right, I think the intention is that
a[i] is syntactic sugar for *(a+(i)). Nothing else makes
sense. For example you would be saying that you could
not have a subscript of the form x[a==b] since it would
mean *((x+a) == b). That just can't be right.
To me the quoted program is clearly correct, and if it
does not work, it's a compiler bug.
Yes, you seem to be right, and I was mistaken.
To be exact the standard says:

The definition of the subscript operator [] is that E1[E2] is
identical to (*((E1)+(E2))).
--
<Insert your favourite quote here.>
Erik Trulsson
***@student.uu.se
Michel Lespinasse
2004-01-23 22:28:41 UTC
Permalink
Post by Erik Trulsson
There are many ways to interpret one could interpret it, but the
Remember that a[i] is just syntactic sugar for *(a+i).
Should it not be syntactic sugar for *(a+(i)) instead ?
Post by Erik Trulsson
This means that the expression a[i-2000000000L] is the same as
*(a+i-2000000000L). With i==2000000000L, this becomes
*(a+2000000000L-2000000000L) which is the same as
*((a+2000000000L)-2000000000L).
a+2000000000L is *not* a valid address, and thus undefined behaviour is
invoked.
Do you recommend people use a[(i-2000000000L)] instead ? Sounds funny to me :)

Cheers,
--
Michel "Walken" Lespinasse
"In this time of war against Osama bin Laden and the oppressive
Taliban regime, we are thankful that OUR leader isn't the spoiled son
of a powerful politician from a wealthy oil family who is supported by
religious fundamentalists, operates through clandestine organizations,
has no respect for the democratic electoral process, bombs innocents,
and uses war to deny people their civil liberties." --The Boondocks
Erik Trulsson
2004-01-23 22:47:43 UTC
Permalink
Post by Michel Lespinasse
Post by Erik Trulsson
There are many ways to interpret one could interpret it, but the
Remember that a[i] is just syntactic sugar for *(a+i).
Should it not be syntactic sugar for *(a+(i)) instead ?
(*((a)+(i))) actually, but otherwise you are right.
--
<Insert your favourite quote here.>
Erik Trulsson
***@student.uu.se
Richard Sandiford
2004-01-24 11:44:07 UTC
Permalink
Post by c***@broadcom.com
(1) don't allow GCC to emit "dla sym+offset" for
-mno-explicit-relocs, or maybe just for -mno-explicit-relocs
and the ABIs which stuff 64-bit pointers into 32-bit object
files.
(2) make binutils emit long code sequences for sym + offset when
assembling objects for an ABI which stuffs 64-bit pointers
into 32-bit object files.
(3) punt. (maybe even xfail the test for some MIPS targets w/
-mno-explicit-relocs.)
It seems to me that (2) is *probably* the right fix, but others'
opinions would be appreciated. I think I'm OK with (3), too, but it
might bite others down the road...
I guess the arguments in favour of (2) are that:

- It would work for older gccs. Some people refuse to use anything
newer than 2.95, for understandable reasons.

- It's counter-intuitive for "dla $2,a-2000000000" not to load the
address "a-2000000000"

But I agree with Ian (and you?) that pessimising the common case would
be a bad idea. Another downside is that it would make the macro longer
than it traditionally has been, which might lead to gcc (of whatever
vintage) generating out-of-range branches.

(I guess that's only a minor problem though, since the MIPS port
has traditionally been poor at predicting whether a branch will
be out of range.)

As you say, this doesn't happen for -mexplicit-relocs, but (as you
probably guessed) that's not because we're being smart. It's just
that we're not optimising this case particularly well. Explicit-reloc
code could in theory contain %his and %los with same sort of big offsets
that gas uses.

The patch to implement (1) would be:

@@ -918,8 +918,7 @@ mips_symbolic_constant_p (rtx x, enum mi
switch (*symbol_type)
{
case SYMBOL_GENERAL:
- /* %hi() and %lo() can handle anything. */
- return true;
+ return Pmode == SImode || ABI_HAS_64BIT_SYMBOLS;

case SYMBOL_SMALL_DATA:
/* Make sure that the offset refers to something within the

...which should fix both the explicit-reloc and macro cases. We could
do better by allowing offsets that refer to something in the underlying
object: I'll try to come up with a patch soon.

I'm reluctant to punt since the code is valid.

On a related note: what do folks think about the current default ABI for
mipsisa64-elf (i.e. the LP64 version of EABI64)? Is it really the best
choice? The use of 32-bit object files kind-of limits what you can with
64-bit addresses, and if you only need 32-bit addresses, you get much
better performance from the -mlong32 version.

I think we can choose whatever ABI we like for 3.4 since 3.3 used
the infamous, now-defunct "MEABI".

Richard
c***@broadcom.com
2004-01-24 22:22:24 UTC
Permalink
Post by Richard Sandiford
On a related note: what do folks think about the current default ABI for
mipsisa64-elf (i.e. the LP64 version of EABI64)? Is it really the best
choice? The use of 32-bit object files kind-of limits what you can with
64-bit addresses, and if you only need 32-bit addresses, you get much
better performance from the -mlong32 version.
Based on http://sources.redhat.com/ml/binutils/2003-06/msg00436.html
i believe that the intent of "EABI64" is LP64 unless the user
specifies something else like -mlong32.

I think this is quite reasonable, since if you're doing 64-bit
embedded programming, access to XKPHYS is often quite desirable.

it might be nice if there were a well-defined LP32 EABI for 64-bit
ISAs (incl. 64-bit floating point modes), like n32 fits between o32
and n64... but at least for now there's not, and even if there were
it wouldn't fit well in the "-mabi=eabi" world.



chris
Richard Sandiford
2004-01-24 22:43:45 UTC
Permalink
Post by c***@broadcom.com
it might be nice if there were a well-defined LP32 EABI for 64-bit
ISAs (incl. 64-bit floating point modes), like n32 fits between o32
and n64... but at least for now there's not, and even if there were
it wouldn't fit well in the "-mabi=eabi" world.
It might not be documented, but the EABI64 code in gcc supports both
LP32 and LP64, with "-mabi=eabi -mlong32" selecting the 32-bit version.
It's one of the many multilibs built by mips64vrel-elf.

Unfortunately, we even have the -mgp32 -mlong64 version. In other
words, the EABI code effectively supports the combination {-mgp32,-mgp64}
x {-mlong32,-mlong64}.

But...
Post by c***@broadcom.com
I think this is quite reasonable, since if you're doing 64-bit
embedded programming, access to XKPHYS is often quite desirable.
...fair enough. Just thought I'd ask. ;)

Richard

Thiemo Seufer
2004-01-24 12:44:05 UTC
Permalink
***@broadcom.com wrote:
[snip]
Post by c***@broadcom.com
(1) don't allow GCC to emit "dla sym+offset" for
-mno-explicit-relocs, or maybe just for -mno-explicit-relocs
and the ABIs which stuff 64-bit pointers into 32-bit object
files.
(2) make binutils emit long code sequences for sym + offset when
assembling objects for an ABI which stuffs 64-bit pointers
into 32-bit object files.
AFAICS this is only EABI64, so we can just add it to the
HAS_64BIT_ADDRESSES macro in gas.
Post by c***@broadcom.com
(3) punt. (maybe even xfail the test for some MIPS targets w/
-mno-explicit-relocs.)
It seems to me that (2) is *probably* the right fix, but others'
opinions would be appreciated. I think I'm OK with (3), too, but it
might bite others down the road...
I prefer (2), slow but working code is better than broken code.


Thiemo
c***@broadcom.com
2004-01-24 22:23:07 UTC
Permalink
Post by Thiemo Seufer
AFAICS this is only EABI64, so we can just add it to the
HAS_64BIT_ADDRESSES macro in gas.
(also -mabi=o64 -mlong64, i think.)



chris
Loading...