C (MIPS) - How to tell compiler load single-precision floats immidiates with GPRs?

Viewed 169

Recently, I am trying to write some utilities for n64 with gcc and have some problems with it's optimization strategy.

Please consider following example:

// cctest.c

extern struct {
    float x;
    float y;
    float z;
} var;

void *test() {
    float t;

    t = 5.0;
    var.x = var.x + t;
    var.y = 10.0;
    var.z = 60.0;
    return (void*)&var;
}

My except output was something like:

    lui $2, %hi(var)
    lui $1, 0x40A0
    addiu   $2,$2,%lo(var)
    mtc1 $1, $f2
    lwc1 $f0, 0x0($2)
    lui $3, 0x4120
    lui $4, 0x4270
    sw $3, 0x4($2)
    add.s $f0, $f0, $f2
    sw $4, 0x8($2)
    jr $31
    swc1 $f0, 0x0($2)

However, the compiler generates:

; cctest.s

; In .text
    lui $3,%hi(var)
    lui $2,%hi($LC0)
    lwc1    $f0,%lo(var)($3)
    lwc1    $f2,%lo($LC0)($2)
    lui $5,%hi($LC1)
    add.s   $f0,$f0,$f2
    addiu   $2,$3,%lo(var)
    lui $4,%hi($LC2)
    swc1    $f0,%lo(var)($3)
    lwc1    $f0,%lo($LC1)($5)
    swc1    $f0,4($2)
    lwc1    $f0,%lo($LC2)($4)
    jr  $31
    swc1    $f0,8($2)

; In .rodata
    .align  2
$LC0:
    .word   1084227584
    .align  2
$LC1:
    .word   1092616192
    .align  2
$LC2:
    .word   1114636288

with following flags given:

-G0 -fomit-frame-pointer -fno-PIC -mips3 -march=vr4300 -mtune=vr4300 -mabi=32 -mlong32 -mno-shared -mgp32 -mhard-float -mno-check-zero-division -fno-stack-protector -fno-common -fno-zero-initialized-in-bss -mno-abicalls -mno-memcpy -mbranch-likely -O3

I am not very experienced with mips3; but since the target machine (n64) has very limited RAM and DCache, I think that putting everything into memory does not appear to be a good idea.

I went to gcc's MIPS options page but did not found anything helpful.

The environment was mingw64(msys2) with gcc-10.2.0(mips64-elf), in which gcc was configured with

    --build=x86_64-w64-mingw32 \
    --host=x86_64-w64-mingw32 \
    --prefix="./" \
    --target=mips64-elf --with-arch=vr4300 \
    --enable-languages=c,c++ --without-headers --with-newlib \
    --with-gnu-as=./bin/mips64-elf-as.exe \
    --with-gnu-ld=./bin/mips64-elf-ld.exe \
    --enable-checking=release \
    --enable-shared \
    --enable-shared-libgcc \
    --disable-decimal-float \
    --disable-gold \
    --disable-libatomic \
    --disable-libgomp \
    --disable-libitm \
    --disable-libquadmath \
    --disable-libquadmath-support \
    --disable-libsanitizer \
    --disable-libssp \
    --disable-libunwind-exceptions \
    --disable-libvtv \
    --disable-multilib \
    --disable-nls \
    --disable-rpath \
    --disable-symvers \
    --disable-threads \
    --disable-win32-registry \
    --enable-lto \
    --enable-plugin \
    --enable-static \
    --without-included-gettext

Is there any way to tell gcc put such single precision floating-point constants in GPRs instead of memory, in case their lower 16-bits is zero?


EDIT1

Tried using clang (11.0) with flags

clang -S --target=mips-none-elf -O2 -G0 -ffreestanding -fomit-frame-pointer -ffast-math -mabi=o32 -mno-check-zero-division -mfp32

and the output was:

; cctest.llvm.s

; In .rodata ...
$CPI0_0:
    .4byte  0x40a00000

; In .text ...
    lui $1, %hi(var)
    addiu   $2, $1, %lo(var)
    lui $3, 17008
    sw  $3, 8($2)
    lui $3, 16672
    sw  $3, 4($2)
    lui $3, %hi($CPI0_0)
    lwc1    $f0, %lo($CPI0_0)($3)
    lwc1    $f1, %lo(var)($1)
    add.s   $f0, $f1, $f0
    jr  $ra
    swc1    $f0, %lo(var)($1)

Compared to gcc, 5.0 is still in memory but it use $3 to load 10.0 to var.y and 60.0 to var.z.

Please note that clang lacks support of o32 ABI on 64bit processors and fixes for specific processors.


EDIT2

I noticed that older version of gcc was able to optimize this tightly:

; egcs-mips-linux-1.1.2-4.i386
; binutils-mips-linux-2.9.5-3.i386
;
; cctest.egcs112.s
; -O2 -non_shared -mips3 -G 0 -mcpu=4300 

; .text
    .set    noreorder 
    .cpload $25 ; GPT with -G 0? no idea why
    .set    reorder ; Allow as to reorder instructions
    la  $2,var
    li.s    $f6,5.00000000000000000000e0 ; This pseudo op will expand to lui + mtc0
    l.s $f0,0($2)
    li.s    $f2,1.00000000000000000000e1
    li.s    $f4,6.00000000000000000000e1
    add.s   $f0,$f0,$f6
    s.s $f2,4($2)
    s.s $f4,8($2)
    .set    noreorder
    .set    nomacro
    j   $31
    s.s $f0,0($2)
    .set    macro
    .set    reorder

It turns out that some optimization was dropped at some point in 64 bits support added.

Currently, inside gcc source, the only way defined in mips.c and mips.md to transfer single immediate, is to load via memory; I am not sure whether this is a bug or intended, as some ancient builds of gcc was able generate way efficient code under certain scenarios.

In summary, it is not possible to perform such optimization with modern official releases of gcc; however, this can be done by switching back to 199x versions or make a custom build to add the support back manually.

0 Answers
Related