标签: riscv

RISCV无分支编码

在英特尔 AVX 上,存在无分支代码的可能性。您可以计算这两种情况,并根据条件混合结果,而不是针对 case0 或 case1 进行分支。

AVX 使用vblendps指令以 8 种方式实现浮动。

您还可以使用 x86 指令CMOVcc以标量方式(无需向量)执行此操作,该指令有条件地执行移动操作。

注意:ARM 有CSEL,NEON 有VBSL

RISCV64 可以做这样的标量移动吗,这样你就不必分支

a = c ? x : y;
Run Code Online (Sandbox Code Playgroud)

据我了解,RISCV 实现是有序的,因此在不需要分支时它比 x86 更有好处。(后者至少可以围绕一些指令进行洗牌,甚至可以推测性地分支以隐藏延迟。)

我能找到的最接近 riscv 的无分支操作是SLT(设置小于),但设置为 1 或 0,然后需要乘法?将 SLT 设置为 -1 或 0 不是更有用,这样我们就可以进行 AND 运算吗?

更新

做时:

int foo(int a, int b, int x, int y)
{
    return a < b ? x : y;
}
Run Code Online (Sandbox Code Playgroud)

我尝试了使用 SLT 的穷人版本的无分支。我不确定我是否完全正确,通过使用位掩码作为 0 - 条件(0|1),我想出了:

branchless: …
Run Code Online (Sandbox Code Playgroud)

assembly cpu-architecture riscv conditional-move branchless

7
推荐指数
2
解决办法
1657
查看次数

实际的RISC-V指令代码在哪里?

我已经下载了最新的...

RISC-V指令集手册,第1卷:用户级ISA

...这很有趣,但是它实际上从未提供操作码/ funct3和其他指令格式的值。例如,按名称列出了LOAD / STORE / BRANCH操作码,但没有提供它们代表的实际位值。

实际列出的所有代码在哪里?

riscv

6
推荐指数
2
解决办法
5747
查看次数

如何使用 LLVM/Clang 编译为 RISC-V 目标?

我想为 RISC-V 处理器编译一个简单的程序“int main(){return 0;}”。LLVM/Clang 版本是 9.0,我想用这样的 RISC-V 模拟器运行编译的程序https://github.com/riscv/riscv-tools

我的问题是,我无法仅列出带有这些命令的 LLC-s 支持的 clang 目标:

llc --version
llc -march=xxARCHTYPExx -mattr=help
Run Code Online (Sandbox Code Playgroud)

并且没有列出任何类型的 riscv 处理器。

所以我试图查看三重文件: llvm-project\llvm\include\llvm\ADT\Triple.h

并尝试以下命令: clang hello.c -target riscv32 -march=rv32imafd

但我收到以下错误:

错误:无法创建目标:'没有可用的目标与三重“riscv32”兼容'

有人可以帮助我如何获得有效的 RISC-V 目标吗?我只是简单的无法编译程序,但我知道 LLVM 有 RISC-V 支持。

compilation llvm clang riscv

6
推荐指数
1
解决办法
7091
查看次数

RISC-V ISA 中的 FENCE.TSO 是什么意思?

我不太明白RISC-V中普通FENCE之间的区别(已在这里回答:RISC-V指令集中的FENCE指令是什么意思?)和FENCE.TSO之间的区别。手册说:

\n\n

可选的 FENCE.TSO 指令被编码为 FENCE 指令,其中 fm=1000、前驱=RW、后继=RW。FENCE.TSO 将其前驱集中的所有加载操作排序在其后继集中的所有内存操作之前,并将其前驱集中的所有存储操作排序在其后继集中的所有存储操作之前。这会在 FENCE 中留下非 AMO 存储操作。 TSO\xe2\x80\x99s\n前驱集无序,其后继集中包含非 AMO 负载。

\n\n

好吧,这是我的猜测。我将仅根据我的理解展示我的草图。

\n\n

有两个集合(包括指令),被FENCE指令分隔开,即前驱集和后继集。

\n\n
Load Operation 1\nLoad Operation 2\nLoad Operation 3\nStore Operation 1\nStore Operation 2\nStore Operation 3\n**FENCE.TSO**\nMemory Operation 1\nMemory Operation 2\nMemory Operation 3\nStore Operation 4\nStore Operation 5\nStore Operation 6\n
Run Code Online (Sandbox Code Playgroud)\n\n

我是这样理解的。但我仍然对这句话感到困惑这使得 FENCE.TSO\xe2\x80\x99s\n前驱集中的非 AMO 存储操作无序,其后继集中的非 AMO 加载。\n什么是非 AMO 加载和非 AMO 存储操作?

\n\n

好吧,AMO 似乎代表“原子内存操作”。我仍然想知道为什么我不能只使用“普通”围栏。

\n

memory processor riscv

6
推荐指数
1
解决办法
1984
查看次数

为什么呼叫后GCC for Risc-V会产生nop指令

默认情况下,用于Risc-V的GCC会nop在生成指令后生成指令call

$ cat test.c
void g();
void f() {
        g();
}
$ riscv64-unknown-elf-gcc -S test.c -o -    
[...]
f:
        addi    sp,sp,-16
        sd      ra,8(sp)
        sd      s0,0(sp)
        addi    s0,sp,16
        call    g
        nop #### <-----------here
        ld      ra,8(sp)
        ld      s0,0(sp)
        addi    sp,sp,16
        jr      ra
        .size   f, .-f
        .ident  "GCC: (GNU) 8.3.0"
Run Code Online (Sandbox Code Playgroud)

我希望当针对具有分支延迟时隙的架构时,但是我的理解是Risc-V不是这样的架构。实际上,nop使用-O1或更高版本进行编译时消失。

只是GCC中的“错误”会nop从具有延迟插槽的体系结构中遗留下来,还是有此nop指令的实际原因?

gcc riscv

6
推荐指数
1
解决办法
132
查看次数

RISC-V 中 JAL 和 JALR 指令的偏移地址

在 RISC-V 规范中,写到 JAL 和 JALR 指令中的立即数被转换为跳转偏移量:

  1. 将给定的立即数符号扩展到 XLEN 位。

  2. 将 LSB 设置为零。

关于这个,我有几个问题。

问题 1

对于 JAL,这给出了一个范围:

000000000000 to 111111111110
Run Code Online (Sandbox Code Playgroud)

也就是说,4KiB。

在这里,如果 LSB 必须始终为零,为什么不将立即数视为地址的强制零 LSB 之前的 12 位,从而将地址范围增加到:

[000000000000]0 to [111111111111]0      
Run Code Online (Sandbox Code Playgroud)

[ ] 表示给定的立即数偏移量,内部在给定的立即数偏移量的末尾添加一个零。那是,

  1. 左移一点点给出地址。

  2. 将结果符号扩展到 XLEN 位。

问题2

如何区分正偏移和负偏移?是否使用了给定偏移量的 MSB?

riscv

6
推荐指数
1
解决办法
2194
查看次数

为什么 JALR 对偏移量的 LSB 进行编码?

我们知道jal指定了一个 21 位的偏移量。但是,它不编码 21 位偏移量而是编码 20 位偏移量。原因是地址的最低有效位始终为零,因为最小可能的 RISC-V 指令是 2 个字节,因此该位未在指令中编码。

通过以这种方式对偏移进行编码,它可以提供 ±1MiB 的跳跃范围。如果jal确实对 LSB 进行编码,它将仅提供 ±512KiB 的跳跃范围。

但是,jalr指定 12 位偏移量的指令确实对 LSB 进行了编码。这将跳跃范围减少到 ±2kiB(而不是 ±4kiB)。我知道它jalr使用 I 型格式,它与addi此类指令的立即数的 LSB 必须编码相同。但是,我认为没有理由必须对jalr.

assembly instruction-set riscv instruction-encoding

6
推荐指数
1
解决办法
213
查看次数

PK/Linux 上的 RISC-V ecall 系统调用约定

在 RISC-V 伪内核 (pk) 或 Linux 下运行的程序中系统调用的调用约定是什么?

查看由 riscv-gnu-toolchain 生成的代码,规则似乎是:

  • 系统调用号被传入 a7
  • 系统调用参数被传递a0a5
  • 未使用的参数设置为 0
  • 返回值在 a0

是这个吗?

真的有必要将未使用的参数归零吗?

注册a6呢?这可以用于另一个 sycall 参数吗?

调用exit()系统调用的示例:

li    a0, 1               # argument that is used by the syscall
li    a1, 0               # unused arguments
li    a2, 0
li    a3, 0
li    a4, 0
li    a5, 0
li    a7, 93              # exit syscall number
Run Code Online (Sandbox Code Playgroud)

system-calls calling-convention riscv

6
推荐指数
1
解决办法
1898
查看次数

RISC-V 汇编语法中的混合目标/源操作数顺序

RISC-V 汇编程序中的大多数指令在源操作数之前对目标操作数进行排序,例如:

li  t0, 22        # destination, source
li  t1, 1         # destination, source
add t2, t0, t1    # destination, source
Run Code Online (Sandbox Code Playgroud)

但是商店说明的顺序颠倒了:

sb    t0, (sp)    # source, destination
lw    t1, (a0)    # destination, source
vlb.v v4, (a1)    # destination, source
vsb.v v5, (a2)    # source, destination
Run Code Online (Sandbox Code Playgroud)

怎么来的?

这种(可以说是)非对称汇编器语法设计的动机是什么?

assembly riscv

6
推荐指数
2
解决办法
568
查看次数

为什么 rv32gc 针对 RISC-V 优化带有分支的无分支代码?

让我们尝试定义一个返回两个值 x 和 y 中的最大值的函数。这些公式有效的充分条件是,对于有符号整数,\xe2\x80\x932^30 <= x, y <= 2^30 \xe2\x80\x93 1,对于无符号整数,0 <= x, y <= 2^31 \xe2\x80\x93 1即,只需要处理有效减少一位的缩小整数范围)。最通用(且最简单)的实现是:

\n
int max(int x, int y) {\n    return (x > y) ? x : y\n}\n
Run Code Online (Sandbox Code Playgroud)\n

在 Pentium Pro 之前,这将是 x86 中生成的程序集:

\n
max:  # GCC -O3 -march=pentium -m32\n  mov eax, DWORD PTR [esp+8]\n  cmp eax, DWORD PTR [esp+4]\n  jge .L4\n  mov eax, DWORD PTR [esp+4]\n.L4:\n  ret\n
Run Code Online (Sandbox Code Playgroud)\n

但从 Pentium Pro 开始,CMOVcc r32,r/m引入了一条新指令,它根据某些指定的状态标志执行条件移动: …

gcc clang compiler-optimization branch-prediction riscv

6
推荐指数
1
解决办法
460
查看次数