RISCV:如何计算分支指令?
RISCV: how the branch intstructions are calculated?
我想了解现代 CPU 的工作原理。我专注于 RISC-V。分支类型很少:
BEQ
BNE
BLT
BGE
BLTU
BGEU
我使用 venus 模拟器来测试这个,我也在尝试模拟它,到目前为止效果很好,但我不明白,分支是如何计算的。
据我所知,ALU 单元只有一个信号输出 - ZERO
(除了它的数学输出)只要输出为零,它就会激活。但是,我如何仅根据 ZERO
输出来确定是否应该采用分支?它们是如何计算的?
示例代码:
addi t0, zero, 9
addi t1, zero, 10
blt t0, t1, end
end:
分支示例:
BEQ
- 减去 2 个数字,如果 ZERO
有效,分支
BNE
- 减去2个数,如果ZERO
不活跃,分支
BLT
- 在这里我有点困惑;我应该减去然后查看符号位,还是什么?
BGE
/ BGEU
- 如何区分这些?我应该使用什么数学指令?
谢谢
您不必通过减法来比较两个(有符号或无符号)数字。
例如,您可以使用级联 7485 chip。
使用此芯片,您可以在不做任何减法的情况下进行所有分支计算。
是的,零输出给你等于/不等于。如果 XOR 运行得更快(在部分时钟周期中更早准备好),您也可以使用 XOR 而不是 SUB 进行相等比较 and/or 使用更少的功率(更少的晶体管开关)。
有趣的事实:MIPS 只有 eq / ne 和带符号的零比较条件,所有这些都可以在没有进位传播或任何其他级联位的情况下快速测试。这很重要,因为它在解码的同一阶段检查分支条件,从而减少了分支延迟。 (所以 1 个分支延迟槽隐藏了延迟。)
为什么要使用只有零输出的 ALU?这使得它无法用于除完全相等之外的比较。
您需要其他输出来从减法结果中确定 GT / GE / LE / LT(及其无符号等价物)。
对于无符号条件,您只需要零和一个 carry/borrow(无符号溢出)标志.
结果的符号位本身不足以满足有符号条件,因为有符号溢出是可能的:(-1) - (-2)
= +1
: -1 > -2
(符号位清除)但是(8 -位环绕)0x80 - 0x7F = +1
(符号位也清除)但 -128 < 127
。数字的符号位仅在比较 与零 .
时才有用
如果扩大结果(通过对输入进行符号扩展并再执行 add/sub 的一位),则不可能出现有符号溢出,因此第 33 位直接是小于符号的结果。
您还可以从 signed_overflow XOR signbit
得到一个小于符号的结果,而不是实际加宽 + 添加。如果 RISC-V 有任何架构方式让软件检查有符号整数溢出,您可能还需要一个 ALU 输出用于有符号溢出。
可以通过查看 MSB(符号位)的进位和进位来计算有符号溢出。如果这些不同,你就会溢出。即 SF = 这两个进位的 XOR。另请参阅 http://teaching.idallen.com/dat2343/10f/notes/040_overflow.txt,详细了解 2 位和 4 位示例的无符号进位与有符号溢出。
在带有 FLAGS 寄存器(例如 x86 和 ARM)的 CPU 中,这些 ALU 输出实际上进入了一个带有命名位的特殊寄存器。您可以查看 x86 manual for conditional-jump instructions 以了解条件名称如 l
(符号小于)或 b
(下面无符号)如何映射到这些标志:
签约条件:
jl
(又名 RISC-V blt
):如果小于则跳转(SF≠ OF
)。这是输出符号位不等于溢出标志,来自减法/cmp
jle
:如果小于或等于 (ZF=1 or SF≠ OF
) 则跳转。
jge
(又名 RISC-V bge
):如果大于或等于 (SF=OF
) 则跳转。
jg
(又名 RISC-V bgt
):如果大于 (ZF=0 and SF=OF
),则跳转较短。
如果你决定让你的 ALU 只产生一个 "signed-less-than" 输出而不是单独的 SF 和 OF 输出,那很好。 SF==OF
只是 !(SF != OF)
.
(x86 也有一些相同操作码的助记同义词,如 jl
= jnge
。有 "only" 16 个 FLAGS 谓词,包括单独的 OF=0
(测试溢出,而不是比较结果)和奇偶校验标志。您只关心实际的 signed/unsigned 比较条件。)
如果您仔细思考一些示例案例,例如测试 INT_MAX > INT_MIN
,您就会明白为什么这些条件有意义,就像我在上面展示的 8 位数字示例一样。
未签名:
jb
(又名 RISC-V bltu
):如果低于(CF=1
)则跳转。那只是测试进位标志。
jae
(又名 RISC-V bgeu
):如果高于或等于(CF=0
)则跳空。
ja
(又名 RISC-V bgtu
):如果高于(CF=0 and ZF=0
)则跳空。
(请注意,x86 减法设置 CF = 借位输出,因此 1 - 2
设置 CF=1。其他一些 ISA(例如 ARM)会反转减法的进位标志。在实现 RISC-V 时,这都是CPU 的内部,在架构上对软件不可见。)
我不知道 RISC-V 是否真的有所有这些不同的分支条件,但 x86 有。
可能有比减法更简单的方法来实现有符号或无符号比较器。
但是如果您已经有一个 add/subtract ALU 并且想要搭载它,那么您可能只希望它生成进位和有符号小于输出以及零。
这样你就不需要单独的符号标志输出,也不需要获取整数结果的 MSB。它只是 ALU 内部一个额外的 XOR 门,用于将这两件事结合起来。
我想了解现代 CPU 的工作原理。我专注于 RISC-V。分支类型很少:
BEQ
BNE
BLT
BGE
BLTU
BGEU
我使用 venus 模拟器来测试这个,我也在尝试模拟它,到目前为止效果很好,但我不明白,分支是如何计算的。
据我所知,ALU 单元只有一个信号输出 - ZERO
(除了它的数学输出)只要输出为零,它就会激活。但是,我如何仅根据 ZERO
输出来确定是否应该采用分支?它们是如何计算的?
示例代码:
addi t0, zero, 9
addi t1, zero, 10
blt t0, t1, end
end:
分支示例:
BEQ
- 减去 2 个数字,如果 ZERO
有效,分支
BNE
- 减去2个数,如果ZERO
不活跃,分支
BLT
- 在这里我有点困惑;我应该减去然后查看符号位,还是什么?
BGE
/ BGEU
- 如何区分这些?我应该使用什么数学指令?
谢谢
您不必通过减法来比较两个(有符号或无符号)数字。 例如,您可以使用级联 7485 chip。 使用此芯片,您可以在不做任何减法的情况下进行所有分支计算。
是的,零输出给你等于/不等于。如果 XOR 运行得更快(在部分时钟周期中更早准备好),您也可以使用 XOR 而不是 SUB 进行相等比较 and/or 使用更少的功率(更少的晶体管开关)。
有趣的事实:MIPS 只有 eq / ne 和带符号的零比较条件,所有这些都可以在没有进位传播或任何其他级联位的情况下快速测试。这很重要,因为它在解码的同一阶段检查分支条件,从而减少了分支延迟。 (所以 1 个分支延迟槽隐藏了延迟。)
为什么要使用只有零输出的 ALU?这使得它无法用于除完全相等之外的比较。
您需要其他输出来从减法结果中确定 GT / GE / LE / LT(及其无符号等价物)。
对于无符号条件,您只需要零和一个 carry/borrow(无符号溢出)标志.
结果的符号位本身不足以满足有符号条件,因为有符号溢出是可能的:(-1) - (-2)
= +1
: -1 > -2
(符号位清除)但是(8 -位环绕)0x80 - 0x7F = +1
(符号位也清除)但 -128 < 127
。数字的符号位仅在比较 与零 .
如果扩大结果(通过对输入进行符号扩展并再执行 add/sub 的一位),则不可能出现有符号溢出,因此第 33 位直接是小于符号的结果。
您还可以从 signed_overflow XOR signbit
得到一个小于符号的结果,而不是实际加宽 + 添加。如果 RISC-V 有任何架构方式让软件检查有符号整数溢出,您可能还需要一个 ALU 输出用于有符号溢出。
可以通过查看 MSB(符号位)的进位和进位来计算有符号溢出。如果这些不同,你就会溢出。即 SF = 这两个进位的 XOR。另请参阅 http://teaching.idallen.com/dat2343/10f/notes/040_overflow.txt,详细了解 2 位和 4 位示例的无符号进位与有符号溢出。
在带有 FLAGS 寄存器(例如 x86 和 ARM)的 CPU 中,这些 ALU 输出实际上进入了一个带有命名位的特殊寄存器。您可以查看 x86 manual for conditional-jump instructions 以了解条件名称如 l
(符号小于)或 b
(下面无符号)如何映射到这些标志:
签约条件:
jl
(又名 RISC-Vblt
):如果小于则跳转(SF≠ OF
)。这是输出符号位不等于溢出标志,来自减法/cmpjle
:如果小于或等于 (ZF=1 or SF≠ OF
) 则跳转。jge
(又名 RISC-Vbge
):如果大于或等于 (SF=OF
) 则跳转。jg
(又名 RISC-Vbgt
):如果大于 (ZF=0 and SF=OF
),则跳转较短。
如果你决定让你的 ALU 只产生一个 "signed-less-than" 输出而不是单独的 SF 和 OF 输出,那很好。 SF==OF
只是 !(SF != OF)
.
(x86 也有一些相同操作码的助记同义词,如 jl
= jnge
。有 "only" 16 个 FLAGS 谓词,包括单独的 OF=0
(测试溢出,而不是比较结果)和奇偶校验标志。您只关心实际的 signed/unsigned 比较条件。)
如果您仔细思考一些示例案例,例如测试 INT_MAX > INT_MIN
,您就会明白为什么这些条件有意义,就像我在上面展示的 8 位数字示例一样。
未签名:
jb
(又名 RISC-Vbltu
):如果低于(CF=1
)则跳转。那只是测试进位标志。jae
(又名 RISC-Vbgeu
):如果高于或等于(CF=0
)则跳空。ja
(又名 RISC-Vbgtu
):如果高于(CF=0 and ZF=0
)则跳空。
(请注意,x86 减法设置 CF = 借位输出,因此 1 - 2
设置 CF=1。其他一些 ISA(例如 ARM)会反转减法的进位标志。在实现 RISC-V 时,这都是CPU 的内部,在架构上对软件不可见。)
我不知道 RISC-V 是否真的有所有这些不同的分支条件,但 x86 有。
可能有比减法更简单的方法来实现有符号或无符号比较器。
但是如果您已经有一个 add/subtract ALU 并且想要搭载它,那么您可能只希望它生成进位和有符号小于输出以及零。
这样你就不需要单独的符号标志输出,也不需要获取整数结果的 MSB。它只是 ALU 内部一个额外的 XOR 门,用于将这两件事结合起来。