Assembly 为什么编译器会在MIPS之后加上一条指令;j";从函数返回的指令?

Assembly 为什么编译器会在MIPS之后加上一条指令;j";从函数返回的指令?,assembly,mips,Assembly,Mips,我正在查看MIPS平台的一些编译器输出,努力理解函数如何返回以及什么是允许的 下面是一个简单的例子: int two_x_squared(int x) { return 2*x*x; } 如果我看到 好的,这里没什么大不了的,我猜j$31跳转到返回地址,nop可能是防止管道中投机执行所必需的 但是我用XC32在-O2下编译,得到 two_x_squared: mul $4,$4,$4 j $31 sll $2,$4,1 所以。。。跳转后执行j$31后的行

我正在查看MIPS平台的一些编译器输出,努力理解函数如何返回以及什么是允许的

下面是一个简单的例子:

int two_x_squared(int x)
{
    return 2*x*x;
}
如果我看到

好的,这里没什么大不了的,我猜
j$31
跳转到返回地址,
nop
可能是防止管道中投机执行所必需的

但是我用XC32在
-O2
下编译,得到

two_x_squared:
    mul $4,$4,$4
    j   $31
    sll $2,$4,1
所以。。。跳转后执行
j$31
后的行

这被称为。是的,分支实际上比您预期的要晚执行一条指令,并且编译器应该用有用的东西填充延迟槽——将分支之前逻辑上完成的东西移动到该槽中,或者将分支之后发生的东西移动到该槽中

这被引入原始MIPS体系结构(以及HP PA RISC等)以帮助流水线处理器,因为它们必须在执行的分支上排空和重新填充管道,这会浪费指令周期


该功能已在后来的MIPS处理器以及后续的开源RISC V硬件中删除。更现代的硬件使用其他方法来减少与管道重新填充相关的浪费周期,包括分支预测、一些无序执行、推测、在管道中更早地执行分支。

好的,为什么XC32会利用它,但用于MIPS的gcc 5.4没有?抱歉,我不知道@贾森:GCC通常会这样做;我认为,在这种情况下,这只是一个遗漏的优化。e、 g.如果您使用
gcc-march=mips32
,以便它可以使用
mul
指令而不是传统的
mult
,它将把它放入延迟槽中。您还可以使用
-fno delayed branch
告诉它始终使用NOP填充延迟槽,例如,这样您就可以轻松地将代码复制到MARS或默认情况下模拟MIPS变体而没有延迟槽的东西中。@JasonS:clang targeting
-march=mips3
将使用分支延迟槽;它最后一次轮班。IDK,如果有一些MIPS规则说您不能安全地将
mflo
放入分支延迟槽中。可能不会;clang为
-target mips-march=mips2
执行此操作。GCC仍然不知道,也许GCC只是不知道如何分割
mult
mflo
的“固定序列”。(我不得不使用
mips2
,因为mips3引入了64位寄存器宽度,而
clang-target mips
不知道32位ABI之类的东西?使用
-target mips64
我们得到了一个零移位来重做符号扩展),是的,
jr$ra
是mips的返回方式;间接跳转到链接寄存器。GCC/clang只是使用相同的
j
助记符进行直接和间接跳转,显然不是
jr
two_x_squared:
    mul $4,$4,$4
    j   $31
    sll $2,$4,1