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.