对于一个小程序或几个功能的一次性使用,你不需要在本地安装任何东西。
使用 Matt Godbolt 的编译器资源管理器站点 https://godbolt.org/,该站点具有适用于各种 ISA 的 GCC 和 clang,包括 MIPS 和 x86-64 以及一些其他编译器。
请注意,默认情况下编译器资源管理器会过滤指令,因此您可以仅查看指令,而忽略对齐、节、.globl 等内容。 (对于没有全局/静态数据的函数,这实际上很好,尤其是当您只想使用编译器为您制作示例时。无论如何,如果您不使用任何指令,默认部分是.text。 )
大多数想要 MIPS asm 做家庭作业的人都在使用 SPIM 或 MARS,通常没有分支延迟槽。 (与真正的 MIPS 不同,因此您需要调整编译器以不在无条件运行的分支之后利用下一条指令,即使它已被占用。)对于 GCC,选项是 -fno-delayed-branch - 那将用 NOP 填充每个延迟槽,因此代码仍将在真正的 MIPS 上运行。您可以手动删除所有 NOP。
可能需要进行其他调整,例如 MARS 可能要求您使用 jr $31 而不是 j $31、Tweak mips-gcc output to work with MARS。当然,I/O 代码必须使用MARS's toy system calls 来实现,而不是jal 调用标准库函数,如printf 或std::ostream::operator<<。不过,您可以有效地编译(和手动调整)asm 以处理数据,例如乘以整数或求和或反转数组。
不幸的是,GCC 没有选择使用$a0 等寄存器名称来代替$r。对于 PowerPC,有 -mregnames 使用 r1 而不是 1,但 MIPS 没有类似的选项来使用“更具符号性”的 reg 名称。
int maybe_square(int num) {
if (num>0)
return num;
return num * num;
}
On Godbolt 与 GCC 5.4 -xc -O3 -march=mips32r2 -Wall -fverbose-asm -fno-delayed-branch
-xc 编译为 C,而不是 C++,因为我发现这比在下拉列表中切换 C 和 C++ 语言并让网站删除我的源代码更方便。
-fverbose-asm 用 C 变量名称为目标和源创建 asm。 (在优化的代码中,通常是临时发明的,但并非总是如此。)
-O3 启用完全优化,因为默认的-O0 调试模式对于人类来说是一团糟。如果您想手动查看代码并了解它如何实现源代码,请始终至少使用-Og。 How to remove "noise" from GCC/clang assembly output?。如果为带有 SIMD 指令的 ISA 进行编译,您还可以使用 -fno-unroll-loops 和 -fno-tree-vectorize。
这使用 mul 而不是经典的 MIPS mult + mflo,这要归功于 -march= 选项告诉 GCC 我们正在为以后的 MIPS ISA 进行编译,而不是默认基线是什么。 (也许是 MIPS I aka R2000,-march=mips1)
另请参阅MIPS target options 上的 GCC 手册部分。
# gcc 5.4 -O3
square:
blez $4,$L5
nop
move $2,$4 # D.1492, num # retval = num
j $31 # jr $ra = return
nop
$L5:
mul $2,$4,$4 # D.1492, num, num # retval = num * num
j $31 # jr $ra = return
nop
或者使用 clang,使用 -target mips 告诉它为 MIPS 编译。 您可以在桌面上执行此操作;与 GCC 不同,clang 通常在构建时启用了多个后端。
来自同一个 Godbolt 链接,clang 10.1 -xc -O3 -target mips -Wall -fverbose-asm -fomit-frame-pointer。默认目标显然是 MIPS32 或类似于 clang 的目标。此外,clang 默认为 MIPS 启用帧指针,从而使 asm 嘈杂。
请注意,它选择了无分支汇编,将 if 转换为条件移动以在原始输入和 mul 结果之间进行选择。不幸的是,clang 不支持-fno-delayed-branch;也许它有相同选项的另一个名称,或者可能没有希望。
maybe_square:
slti $1, $4, 1
addiu $2, $zero, 1
movn $2, $4, $1 # conditional move based on $1
jr $ra
mul $2, $2, $4 # in the branch delay slot
在这种情况下,我们可以简单地将mul 放在jr 之前,但在其他情况下,转换为无分支延迟 asm 并不是一件容易的事。例如在循环计数器 before 上进行分支,它不能通过将减量放在首位来撤消;那会改变意思。
注册名称:
编译器使用寄存器编号,而不是名称。对于人类使用,您通常需要翻译回来。网上很多地方都有 MIPS 注册表,显示 $4..$7 是 $a0..$a3,$8..$15 是 $t0..$t7 等。例如this one。