为什么 RV32I 包含 ADDI 和 XORI 之类的指令而不是 BLTI?
Why does RV32I include instructions like ADDI and XORI but not BLTI?
我没有 ISA 设计经验。我一直在阅读 https://riscv.org/specifications/ 第 2 章第 21 页。
谁能解释一下为什么 RISC-V 有使用立即数的算术和逻辑指令,例如 ADDI 和 XORI,但没有类似的条件分支指令,例如 BLTI、BEQI 等
(其中 B
ranch L
ess T
han I
mmediate 会将寄存器与常量进行比较,如果较小则分支。)
我不知情的观点是,BLTI 会经常用于 C 中的固定长度循环,例如:
for (int i = 0; i < 16; i++) {
...
}
为什么算术和逻辑指令比分支指令更值得立即变体?
分支已经需要将分支目标偏移量编码为立即数,在其中安装两个立即数操作数会更难。让它们都小得多可以使它们适合,但也会降低指令的有用性。
这样的分支偶尔会有用,但在我看来你高估了它的用处:在典型的循环中,没有必要直接将循环计数器与其边界值进行比较,事实上大多数循环变量甚至都没有做到这一点进入编译后的代码。
作为一个小例子(使用更高的计数来避免循环的完全展开),
int test(int *data) {
int sum = 0;
for (int i = 0; i < 255; i++)
sum += data[i];
return sum;
}
被Clang编译成:
test(int*): # @test(int*)
addi a2, zero, 1020
mv a3, zero
mv a1, zero
.LBB0_1: # =>This Inner Loop Header: Depth=1
add a4, a0, a3
lw a4, 0(a4)
add a1, a4, a1
addi a3, a3, 4
bne a3, a2, .LBB0_1
mv a0, a1
ret
Clang 在这里所做的是计算最终的 地址 然后循环直到到达该地址,从存在中删除循环计数器。
这是一个有点特殊的情况,但也有其他技巧。例如,在很多情况下,循环退出测试可以转化为当寄存器递减为零时退出的循环,这很容易测试,因为RISCV有bnez
。如果有必要,原始循环计数器可以与它共存(不参与循环退出测试),或者如果可能,它可以再次消失。
我没有 ISA 设计经验。我一直在阅读 https://riscv.org/specifications/ 第 2 章第 21 页。
谁能解释一下为什么 RISC-V 有使用立即数的算术和逻辑指令,例如 ADDI 和 XORI,但没有类似的条件分支指令,例如 BLTI、BEQI 等
(其中 B
ranch L
ess T
han I
mmediate 会将寄存器与常量进行比较,如果较小则分支。)
我不知情的观点是,BLTI 会经常用于 C 中的固定长度循环,例如:
for (int i = 0; i < 16; i++) {
...
}
为什么算术和逻辑指令比分支指令更值得立即变体?
分支已经需要将分支目标偏移量编码为立即数,在其中安装两个立即数操作数会更难。让它们都小得多可以使它们适合,但也会降低指令的有用性。
这样的分支偶尔会有用,但在我看来你高估了它的用处:在典型的循环中,没有必要直接将循环计数器与其边界值进行比较,事实上大多数循环变量甚至都没有做到这一点进入编译后的代码。
作为一个小例子(使用更高的计数来避免循环的完全展开),
int test(int *data) {
int sum = 0;
for (int i = 0; i < 255; i++)
sum += data[i];
return sum;
}
被Clang编译成:
test(int*): # @test(int*)
addi a2, zero, 1020
mv a3, zero
mv a1, zero
.LBB0_1: # =>This Inner Loop Header: Depth=1
add a4, a0, a3
lw a4, 0(a4)
add a1, a4, a1
addi a3, a3, 4
bne a3, a2, .LBB0_1
mv a0, a1
ret
Clang 在这里所做的是计算最终的 地址 然后循环直到到达该地址,从存在中删除循环计数器。
这是一个有点特殊的情况,但也有其他技巧。例如,在很多情况下,循环退出测试可以转化为当寄存器递减为零时退出的循环,这很容易测试,因为RISCV有bnez
。如果有必要,原始循环计数器可以与它共存(不参与循环退出测试),或者如果可能,它可以再次消失。