ps2dev / ps2sdk

Homebrew PS2 SDK
Other
935 stars 133 forks source link

Compiler doesn't use registers f12 and f13 when passing floating point arguments. #244

Closed Dnawrkshp closed 2 years ago

Dnawrkshp commented 2 years ago

I'm building a program to run alongside and hook into a game. I'm calling some of the game's functions from within my program. I've been using v1.2.0 which has worked wonderfully but I've wanted to upgrade to the newer version of GCC. Building with this version (11.2), the compiler starts passing floating point arguments in registers f14 and up. This breaks the interop I had and need with the game.

If this is intentional, is there a possible workaround? Thanks!

Source:

int lerp(int a, int b, float t)
{
    return a + (b-a)*t;
}

int main(void)
{
    return lerp(0, 10, 0.5F);
}

PS2SDK v1.2.0 Output:

    .file   1 "main.c"
    .section .mdebug.eabi64
    .previous
    .text
    .align  2
    .globl  lerp
    .ent    lerp
lerp:
    .frame  $fp,32,$31      # vars= 16, regs= 2/0, args= 0, extra= 0
    .mask   0x40000000,-16
    .fmask  0x00000000,0
    subu    $sp,$sp,32
    sd  $fp,16($sp)
    move    $fp,$sp
    sw  $4,0($fp)
    sw  $5,4($fp)
    s.s $f12,8($fp)
    l.s $f4,0($fp)
    cvt.s.w $f4,$f4
    lw  $3,4($fp)
    lw  $2,0($fp)
    subu    $2,$3,$2
    mtc1    $2,$f2
    cvt.s.w $f2,$f2
    l.s $f0,8($fp)
    mul.s   $f0,$f2,$f0
    add.s   $f0,$f4,$f0
    cvt.w.s $f2,$f0
    mfc1    $2,$f2
    move    $sp,$fp
    ld  $fp,16($sp)
    addu    $sp,$sp,32
    j   $31
    .end    lerp
$Lfe1:
    .size   lerp,$Lfe1-lerp
    .align  2
    .globl  main
    .ent    main
main:
    .frame  $fp,32,$31      # vars= 0, regs= 4/0, args= 0, extra= 0
    .mask   0xc0000000,-16
    .fmask  0x00000000,0
    subu    $sp,$sp,32
    sd  $31,16($sp)
    sd  $fp,0($sp)
    move    $fp,$sp
    move    $4,$0
    li  $5,10           # 0xa
    li.s    $f12,5.00000000000000000000e-1
    jal lerp
    move    $sp,$fp
    ld  $31,16($sp)
    ld  $fp,0($sp)
    addu    $sp,$sp,32
    j   $31
    .end    main
$Lfe2:
    .size   main,$Lfe2-main
    .ident  "GCC: (GNU) 3.2.3"

PS2SDK latest:

    .file   1 "main.c"
    .section .mdebug.abiN32
    .previous
    .nan    legacy
    .module singlefloat
    .module oddspreg
    .text
    .align  2
    .globl  lerp
    .set    nomips16
    .set    nomicromips
    .ent    lerp
    .type   lerp, @function
lerp:
    .frame  $fp,32,$31      # vars= 16, regs= 1/0, args= 0, gp= 0
    .mask   0x40000000,-8
    .fmask  0x00000000,0
    addiu   $sp,$sp,-32
    sd  $fp,24($sp)
    move    $fp,$sp
    sw  $4,0($fp)
    sw  $5,4($fp)
    swc1    $f14,8($fp)
    lw  $2,0($fp)
    mtc1    $2,$f0
    cvt.s.w $f1,$f0
    lw  $3,4($fp)
    lw  $2,0($fp)
    subu    $2,$3,$2
    mtc1    $2,$f0
    cvt.s.w $f2,$f0
    lwc1    $f0,8($fp)
    mul.s   $f0,$f2,$f0
    add.s   $f0,$f1,$f0
    trunc.w.s $f0,$f0
    mfc1    $2,$f0
    move    $sp,$fp
    ld  $fp,24($sp)
    addiu   $sp,$sp,32
    jr  $31
    .end    lerp
    .size   lerp, .-lerp
    .align  2
    .globl  main
    .set    nomips16
    .set    nomicromips
    .ent    main
    .type   main, @function
main:
    .frame  $fp,16,$31      # vars= 0, regs= 2/0, args= 0, gp= 0
    .mask   0xc0000000,-8
    .fmask  0x00000000,0
    addiu   $sp,$sp,-16
    sd  $31,8($sp)
    sd  $fp,0($sp)
    move    $fp,$sp
    lwc1    $f0,%gp_rel(.LC0)($28)
    mov.s   $f14,$f0
    li  $5,10           # 0xa
    move    $4,$0
    jal lerp
    move    $sp,$fp
    ld  $31,8($sp)
    ld  $fp,0($sp)
    addiu   $sp,$sp,16
    jr  $31
    .end    main
    .size   main, .-main
    .section    .sdata,"aw"
    .align  2
.LC0:
    .word   1056964608
    .ident  "GCC: (GNU) 11.2.0"
uyjulian commented 2 years ago

The ABI has changed to n32 from o32 (by GCC maintainer recommendation).

In this case, I would recommend using inline assembly to call the function needing o32 calling convention.

Dnawrkshp commented 2 years ago

Ah I see. Thank you.

Dnawrkshp commented 2 years ago

I really appreciate the quick reply. With your direction I spent some time to research ABIs and calling conventions and found out that the old sdk was defaulting to eabi and the new sdk is, as you said, defaulting to n32. Using the flag -mabi=eabi on the latest sdk with gcc 11.2.0 produces function calls that will work with the game. However I am experiencing new issues that suggest that eabi support is bugged.

When converting integers to floats, the compiler (with optimizations off) will produce this error. Note that with optimizations on, this error goes away but not always. In my more complex project I will still get this error on some lines.

/src # cat main.c
int main(int argc, char * argv[])
{
        int b[] = {1,2,3};       
        float c = b[0];
        return 1;
}
/src # make
mips64r5900el-ps2-elf-gcc -D_EE -G0 -O0 -mabi=eabi -Wall  -I/usr/local/ps2dev/ps2sdk/ee/include -I/usr/local/ps2dev/ps2sdk/common/include -I.  -c main.c -o main.o
main.c: In function ‘main’:
main.c:4:15: warning: unused variable ‘c’ [-Wunused-variable]
    4 |         float c = b[0];
      |               ^
main.c:6:1: error: unable to find a register to spill
    6 | }
      | ^
main.c:6:1: error: this is the insn:
(insn 19 32 20 2 (set (reg:SF 203)
        (float:SF (subreg/s/u:SI (reg:DI 206 [orig:194 _1 ] [194]) 0))) "main.c":4:8 273 {floatsisf2}
     (expr_list:REG_DEAD (reg:DI 206 [orig:194 _1 ] [194])
        (nil)))
main.c:6: confused by earlier errors, bailing out
make: *** [/src/Makefile.eeglobal:44: main.o] Error 1

When I compile a second file and tell the linker to link it with main.o I get the following error:

/src # cat main.c
int main(int argc, char * argv[])
{
        int b[] = {1,2,3};
        //float c = b[0];
        return 1;
}
/src # cat other.c
int other(void)
{
        int b[] = {1,2,3};
        //float c = b[0];
        return 1;
}
/src # make
mips64r5900el-ps2-elf-gcc -D_EE -G0 -O0 -mabi=eabi -Wall  -I/usr/local/ps2dev/ps2sdk/ee/include -I/usr/local/ps2dev/ps2sdk/common/include -I.  -c main.c -o main.o
main.c: In function ‘main’:
main.c:3:13: warning: unused variable ‘b’ [-Wunused-variable]
    3 |         int b[] = {1,2,3};
      |             ^
mips64r5900el-ps2-elf-gcc -D_EE -G0 -O0 -mabi=eabi -Wall  -I/usr/local/ps2dev/ps2sdk/ee/include -I/usr/local/ps2dev/ps2sdk/common/include -I.  -c other.c -o other.o
other.c: In function ‘other’:
other.c:3:13: warning: unused variable ‘b’ [-Wunused-variable]
    3 |         int b[] = {1,2,3};
      |             ^
mips64r5900el-ps2-elf-gcc -T/usr/local/ps2dev/ps2sdk/ee/startup/linkfile -O0 -mabi=eabi -o main.elf main.o other.o   
during RTL pass: expand
ee/kernel/src/tlbfunc.c: In function ‘InitTLBFunctions’:
ee/kernel/src/tlbfunc.c:47:5: internal compiler error: in copy_to_mode_reg, at explow.c:651
0x11fa488 internal_error(char const*, ...)
        ???:0
0x586dee fancy_abort(char const*, int, char const*)
        ???:0
0x5ff9a3 expand_call(tree_node*, rtx_def*, int)
        ???:0
0x7162f6 expand_expr_real_1(tree_node*, rtx_def*, machine_mode, expand_modifier, rtx_def**, bool)
        ???:0
Please submit a full bug report,
with preprocessed source if appropriate.
Please include the complete backtrace with any bug report.
See <https://gcc.gnu.org/bugs/> for instructions.
lto-wrapper: fatal error: mips64r5900el-ps2-elf-gcc returned 1 exit status
compilation terminated.
/usr/local/ps2dev/ee/lib/gcc/mips64r5900el-ps2-elf/11.2.0/../../../../mips64r5900el-ps2-elf/bin/ld: error: lto-wrapper failed
collect2: error: ld returned 1 exit status
make: *** [/src/Makefile.eeglobal:59: main.elf] Error 1

Here are the temp files generated by the compiler. ps2sdk-eabi.zip

Again, I really appreciate the help. Please let me know if I can provide more information.

uyjulian commented 2 years ago

I believe this would be a GCC bug

sp193 commented 2 years ago

I don't think you are meant to just change the ABI like that? I recall it defines the expected lengths of the GPRs too? But it has been so long that I am out of touch.

All compilers that we had in the early 2000s (including the homebrew SDK's), were custom versions of some sort. The R5900 did not exactly fit into any ABI, so different strategies were created to maximize the use of its architecture, including its 128-bit GPRs. It was also a MIPS III with some MIPS IV instructions. All GCC versions anyone had, originally involved modifications to GCC.

When I worked on the 5.x version, I think we used n32 because it was a better fit. Support for 128-bit operations was recommended to be implemented using vector operations, which seemed to be a good fit because the R5900 lacks various basic arithmetic and logical operations in 128-bit mode.

Dnawrkshp commented 2 years ago

Well I guess I need to stick with the version that works. Appreciate the work you've done over the years, sp193. I remember your name from about a decade ago. Back then I think you were the only one left maintaining the project but now it seems that a few more people have stepped in to contribute. Really cool to see this is all still alive and well.

Cheers!

sp193 commented 2 years ago

Thanks. It is nice to see that people have stepped up to help maintain the toolchain for the favourite console.

Other than using a compiler that fits, what about writing an adaptor (adaptor pattern), in assembly? I'm not sure whether the new toolchain has matured enough to maximize the R5900's capabilities, but assuming it is complete - then is there really a reason to stay on 3.2.x?

Dnawrkshp commented 2 years ago

The challenge I'm facing at the moment is the amount of space I have available. I'm working in 0x000D0000 through 0x000FF000 because either the game or OPL (I want to maintain OPL compatibility) uses everything else. I'm splitting my project up into a bunch of modules so that I can have only what is needed loaded at any given time. In the worst case, I am using ~140kb/188kb. I expect these modules to grow in size over time so some of this extra space is reserved for future features.

The more I think about this idea, the more I think it might be worth it. Though I may need to use more space for the interop functions, I will likely save more space overall because of the improved optimizer.

I'll try this out when I have time and get back to you. Thanks for the suggestion.