forums.ps2dev.org Forum Index forums.ps2dev.org
Homebrew PS2, PSP & PS3 Development Discussions
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

TI mode in gcc broken.

 
Post new topic   Reply to topic    forums.ps2dev.org Forum Index -> PS2 Development
View previous topic :: View next topic  
Author Message
pixel



Joined: 30 Jan 2004
Posts: 791

PostPosted: Fri Apr 15, 2005 12:19 am    Post subject: TI mode in gcc broken. Reply with quote

Okay, this is reported by shazz.

After some investigations, I conclude the TI mode support using our current gcc patches is broken, when it comes to casting, in both optimized and non optimized mode. Here is the basic example:

Original C code:
Code:
#include <tamtypes.h>

u32 foo();

u128 bar() {
    return foo();
}

u128 foobar() {
    return 5;
}


Disassembly of the above compilation using -O2:
Code:
00000000 <bar>:
   0:   27bdfff0        addiu   sp,sp,-16
   4:   ffbf0000        sd      ra,0(sp)
   8:   0c000000        jal     0 <bar>
                        8: R_MIPS_26    foo
   c:   00000000        nop
  10:   dfbf0000        ld      ra,0(sp)
  14:   0000102d        move    v0,zero
  18:   03e00008        jr      ra
  1c:   27bd0010        addiu   sp,sp,16

00000020 <foobar>:
  20:   700014a9        por     v0,zero,zero
  24:   03e00008        jr      ra
  28:   24020005        li      v0,5
  2c:   00000000        nop


Same, without any -O:
Code:
00000000 <bar>:
   0:   27bdffe0        addiu   sp,sp,-32
   4:   ffbf0010        sd      ra,16(sp)
   8:   ffbe0000        sd      s8,0(sp)
   c:   0c000000        jal     0 <bar>
                        c: R_MIPS_26    foo
  10:   03a0f02d        move    s8,sp
  14:   0002103c        dsll32  v0,v0,0x0
  18:   0002183e        dsrl32  v1,v0,0x0
  1c:   0060102d        move    v0,v1
  20:   0000102d        move    v0,zero
  24:   03c0e82d        move    sp,s8
  28:   dfbf0010        ld      ra,16(sp)
  2c:   dfbe0000        ld      s8,0(sp)
  30:   03e00008        jr      ra
  34:   27bd0020        addiu   sp,sp,32

00000038 <foobar>:
  38:   27bdfff0        addiu   sp,sp,-16
  3c:   ffbe0000        sd      s8,0(sp)
  40:   03a0f02d        move    s8,sp
  44:   700014a9        por     v0,zero,zero
  48:   24020005        li      v0,5
  4c:   03c0e82d        move    sp,s8
  50:   dfbe0000        ld      s8,0(sp)
  54:   03e00008        jr      ra
  58:   27bd0010        addiu   sp,sp,16


In both cases, the "foobar" function returns the correct value in $v0, and the "bar" function returns 0, whatever the result of "foo" is. Worse: I believe the way it does it is wrong, since it uses a 64-bits move to erase $v0, potentially letting garbage in the upper 64 bits of $v0.


Did anyone experienced such things in the past ? Can anyone of you check this with the previous toolchain ? I can't do such test atm.
_________________
pixel: A mischievous magical spirit associated with screen displays. The computer industry has frequently borrowed from mythology. Witness the sprites in computer graphics, the demons in artificial intelligence and the trolls in the marketing department.
Back to top
View user's profile Send private message
petrs



Joined: 03 Nov 2005
Posts: 1

PostPosted: Sat Dec 16, 2006 9:20 am    Post subject: Reply with quote

Quite a long time passed from your discovery, did anything change since that time?
Back to top
View user's profile Send private message
ragnarok2040



Joined: 09 Aug 2006
Posts: 230

PostPosted: Sun Oct 04, 2009 4:29 am    Post subject: Reply with quote

Sorry about dragging up such an old topic, but I've been testing this every now and then trying to fix it in gcc, but no luck yet. I did find kind of a workaround for this case. If you use a 16-byte union like this:
Code:
typedef union {
   u8  byte[16];
   u16 hword[8];
   u32 word[4];
   u64 dword[2];
   u128 qword;
 } UQWORD;
Then you can assign the return value of foo directly to word[0] like this:
Code:
u128 bar() {
   UQWORD ret;
   ret.word[0] = foo();
   return ret.qword;
}
and bar will return foo()'s value without too much overhead...
Code:
00000008 <bar>:
   8:   27bdffe0    addiu   sp,sp,-32
   c:   ffbf0010    sd   ra,16(sp)
  10:   0c000000    jal   0 <foo>
  14:   00000000    nop
  18:   afa20000    sw   v0,0(sp)
  1c:   dfbf0010    ld   ra,16(sp)
  20:   7ba20000    lq   v0,0(sp)
  24:   03e00008    jr   ra
  28:   27bd0020    addiu   sp,sp,32
  2c:   00000000    nop
Back to top
View user's profile Send private message
dlanor



Joined: 28 Oct 2004
Posts: 269
Location: Stockholm, Sweden

PostPosted: Sun Oct 04, 2009 9:04 am    Post subject: Reply with quote

ragnarok2040 wrote:
Sorry about dragging up such an old topic, but I've been testing this every now and then trying to fix it in gcc, but no luck yet. I did find kind of a workaround for this case. If you use a 16-byte union like this:
There are various methods to make a function return what we want, not least the possibility of forcing the result by hand-optimized asm (the only truly certain way). But this does not really address the true problem here, which is that the compiler is inherently bugged in how it translates basic typecasting needs into real assembly code.

There are other examples of a similar nature that do not involve any function return values, but merely normal variable to variable assignments where explicit typecasting written by a C coder has no effect at all. Like typecasting a u64 to u128, and finding that the compiled code completely ignores the upper 64 bits, instead of properly zeroing them in the transfer to a 128 bit unsigned variable, which thus retains its original upper bits after the transfer.

I think that one root of this problem is that the compiler itself is not properly 'aware' of whether or not various EE-specific instructions affect 64 or 128 register bits, and tends to assume that either unsigned zero extension or sign-bit extension will normally affect the upper bits, even when that is not the case.

Still, the original example is quite stunning in its total compiler stupidity, since there is no excuse whatever, no matter what extension assumptions apply, for zeroing the lower bits of the value returned by 'foobar'.

It is almost as if some coder had been interrupted in designing how an implied typecast to 128 bits should be done, leaving only the parts already completed and intended to clear the upper bits (but affecting the entire value), and forgetting to later add any parts for preserving the original lower bits...

Best regards: dlanor
Back to top
View user's profile Send private message
ragnarok2040



Joined: 09 Aug 2006
Posts: 230

PostPosted: Mon Oct 05, 2009 10:00 am    Post subject: Reply with quote

Yeah, I've run into that 64-bit typecasting problem myself. Doing direct assignments with implicit typecasting to the other types in the union seems to work fine as long as the qword has been initialized to 0. For negative values, I can always store the value, pnor the register with the zero register, and load it back to sign extend.

I did make some handwritten assembly just in case I come into a situation where it's needed, :D. I also noticed if I just insert an empty asm("": "=r" (ret) : "r" (foo()); into bar(), it gets rid of the move instruction and the value of v0 is left unmodified allowing it to be passed. That might cause random bits in v0 to be left behind since foo() just loads 5 immediately into v0.
Back to top
View user's profile Send private message
dlanor



Joined: 28 Oct 2004
Posts: 269
Location: Stockholm, Sweden

PostPosted: Tue Oct 06, 2009 3:57 am    Post subject: Reply with quote

ragnarok2040 wrote:
Yeah, I've run into that 64-bit typecasting problem myself. Doing direct assignments with implicit typecasting to the other types in the union seems to work fine as long as the qword has been initialized to 0. For negative values, I can always store the value, pnor the register with the zero register, and load it back to sign extend.
For an assembly programmer there is always something we can do about it, with direct control of low-level code and ability to "jr $ra" whenever we feel all is right. But even for us it can sometimes be hard to realize that such special measures are needed. And without that realization, we too are stuck with the errors. Hopefully they will then show up in debugging, so we can use asm methods to eliminate them, but with bad luck we don't get any clear indication of these bugs, which then remain and cause the program to misbehave in the future...

Quote:
I did make some handwritten assembly just in case I come into a situation where it's needed, :D. I also noticed if I just insert an empty asm("": "=r" (ret) : "r" (foo()); into bar(), it gets rid of the move instruction and the value of v0 is left unmodified allowing it to be passed. That might cause random bits in v0 to be left behind since foo() just loads 5 immediately into v0.
True, but once we are aware of such a problem we'll always manage to fix it properly, after some tries at least. The real problem is for the cases when we are not aware that such bugs are involved.

This is bad enough for us who do know how to use asm to get around such problems, and have some understanding of how these things happen, but consider how bad it will be for those many C coders who lack experience and/or knowledge of asm. They have very little chance to understand what is happening when this kind of bug strikes, and even less chance to do anything effective to counter it.

Best regards: dlanor
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    forums.ps2dev.org Forum Index -> PS2 Development All times are GMT + 10 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group