为什么 Branch Target Buffer 会影响函数调用的 return? [WinMIPS64]

Why does Branch Target Buffer affect return from function calls? [WinMIPS64]

我正在为大学写一些 MIPS 代码,看看函数中的函数是如何工作的,一开始一切都很好。我正在使用 WinMIP64 simulator.

然后,在我打开 BTB 后,一切都坏了(它在第二个函数中陷入了无限循环)。

我快要疯了,直到意识到这是因为 BTB(其中一个函数中有一个 b,我想减少一些因此出现的 Branch Taken Stalls)。当我关闭它时,一切又恢复正常了。

我在下面包含了一些代码。

.data
tabla:  .byte 1,4,5
res:    .space 3 
cont:   .word 3
num:    .word 0

.text

       daddi $a0, [=11=], tabla  # offset element table
       daddi $a1, [=11=], res    # offset results table
       lb $a2, cont([=11=])      # $a2 = 3 (array size)
       daddi $sp, [=11=], 0x400  # $sp = 0x400
       jal dobles            # $ra = 0x14
       sd $v1, num([=11=])       # offset element count
       halt

dobles:                      #first function 

       daddi $sp, $sp, -8    # make space in stack $sp = 0x3f8
       sd $ra, 0($sp)        # 0x3f8 = $ra (0x14) 

loop:
       lb $s0, 0($a0)        # saving element from table in $s0
       daddi $a0, $a0, 1     # add 1 byte displacement to $a0  

       daddi $sp, $sp, -8    # $sp = 0x3f0
       sd $s0, 0($sp)        # 0x3f0 = tabla element 

       jal multi             # $ra = 0x38 
       sb $v0, 0($a1)        # saving result to res
       daddi $a1, $a1, 1     # displacement + 1 byte 

       daddi $a2, $a2, -1    # counter -1 
       bnez $a2, loop        # loop till counter is 0 

       ld $ra, 0($sp)        # load $ra from stack
       daddi $sp, $sp, 8     
       jr $ra 

multi:                       # second function
       ld $t0, 0($sp)        # load element from stack
       daddi $sp, $sp, 8     
       daddi $v1, $v1, 1     # count numer of elements
       dadd $v0, $t0, $t0    # element * 2
       jr $ra 

为什么会这样?对函数的调用是否对缓冲区有某种影响(我认为这只是针对分支)?如果我打开了 BTB,是否可以在函数内调用函数而不会出现问题? 如果我想在函数中使用 BTB 和函数调用,我需要更改什么?

我们的节目中没有涉及到这个,所以我在这里问一下。

BTB 是一个分支预测结构。它对正确性的影响为零,仅对性能有影响。它在建筑上不可见。

我的猜测与 Jester 的猜测相同:您实际上(也?)启用了架构分支 delay 插槽:bXX 或 [=11= 之后的指令] 指令 运行s 是否采用分支,hiding branch latency 在早期 MIPS(短有序流水线,非超标量)上。

但实际上我在您的代码中没有看到任何会在有或没有分支延迟槽的情况下中断的内容。 Jester 测试发现在第二次执行时 jal multi$ra 设置为 multi;那是一个模拟器错误。没有正确执行你的代码可以设置 $ra 这样,有或没有分支延迟槽。


根据 WinMIPS64 page

A delay slot can be implemented if desired. With V1.30 a simple branch-target-buffer can also be simulated. A << in the code window beside a jump or branch instruction indicates that it is predicted as being taken.

也许 GUI 将 BTB 和延迟槽选项联系在一起?

一如既往,在调试器中单步执行您的代码以查看其执行情况。

如果您确定 WinMIP64 正在使用 BTB 进行模拟但没有分支延迟槽,那么您可能已经在 WinMIP64 本身中发现了一个错误。由于 BTB(和一般的分支预测)在架构上不可见1,你的代码必须运行相同,无论是否有它.

(除非你做了 MIPS ISA 允许导致 "unpredictable behaviour" 的事情,比如将两个分支背靠背放置,或者在执行后的几条指令内修改 mult 指令的输入. 或者对于经典的 MIPS I,在加载延迟槽中过早地使用加载结果。)

脚注 1:在 Spectre 之外:使用侧通道使微架构状态在架构上可见。