C (MIPS) - 如何告诉编译器加载单精度浮点数与 GPR 直接相关?

ima*_*kak 6 c floating-point gcc mips

最近,我正在尝试使用 gcc 为 n64 编写一些实用程序,但它的优化策略存在一些问题。

请考虑以下示例:

// 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;
}
Run Code Online (Sandbox Code Playgroud)

我的 except 输出类似于:

    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)
Run Code Online (Sandbox Code Playgroud)

然而,编译器生成:

; 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
Run Code Online (Sandbox Code Playgroud)

给出以下标志:

-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
Run Code Online (Sandbox Code Playgroud)

我对 mips3 不太有经验;但由于目标机器(n64)的 RAM 和 DCache 非常有限,我认为将所有内容放入内存似乎不是一个好主意。

我访问了 gcc 的 MIPS 选项页面,但没有发现任何有用的信息。

环境为mingw64(msys2),搭配gcc-10.2.0(mips64-elf),其中gcc配置为

    --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
Run Code Online (Sandbox Code Playgroud)

有没有办法告诉 gcc 将此类单精度浮点常量放入 GPR 而不是内存中,以防它们的低 16 位为零?


编辑1

尝试使用带有标志的 clang (11.0)

clang -S --target=mips-none-elf -O2 -G0 -ffreestanding -fomit-frame-pointer -ffast-math -mabi=o32 -mno-check-zero-division -mfp32
Run Code Online (Sandbox Code Playgroud)

输出是:

; 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)
Run Code Online (Sandbox Code Playgroud)

与 gcc 相比,5.0仍然在内存中,但它用于$3加载10.0var.y和。60.0var.z

请注意,clang 缺乏对 64 位处理器上的 o32 ABI 的支持以及针对特定处理器的修复。


编辑2

我注意到旧版本的 gcc 能够对此进行紧密优化:

; 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
Run Code Online (Sandbox Code Playgroud)

事实证明,在添加 64 位支持时,某些优化被放弃了。

目前,在 gcc 源代码中,mips.c 和 mips.md 中定义的传输单个立即数的唯一方法是通过内存加载;我不确定这是否是一个错误或有意为之,因为 gcc 的一些古老版本能够在某些情况下生成高效的代码。

总之,使用现代官方版本的 gcc 不可能执行此类优化但是,这可以通过切换回 199x 版本或进行自定义构建以手动添加回支持来完成。