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

max*_*zig 6 assembly riscv

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)

怎么来的?

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

眠りネ*_*ネロク 5

当涉及到目标操作数源操作数时,我没有看到 RISC-V 汇编中真正的不一致:目标操作数——当它是指令编码的一部分时——总是对应于汇编语言中的第一个操作数

如果我们从六种不同指令格式中的四种中查看以下指令示例:

  • R型add t0, t1, t2
  • I型addi t0, t1, 11
  • J型jal ra, off
  • U型lui t0, 0x12345

在上面的汇编指令中,目标操作数是第一个操作数。显然,这个目的操作数对应于指令编码中的目的寄存器

现在,让我们专注于商店说明(S 型格式)。例如,考虑以下存储指令:

sw t0, 8(sp)
Run Code Online (Sandbox Code Playgroud)

我认为很清楚t0上面是一个源操作数,因为存储指令将其内容存储在内存中。

我们很容易认为这8(sp)是一个目标操作数。但是,通过仔细查看 S 型指令格式:

S型格式

我们可以看出8(sp)上面汇编指令中的部分实际上并不是单个操作数,而是两个操作数,即立即数8(即imm)和源寄存器 sp(即rs1)。如果指令可以表达为(类似于addi2):

sw t0, sp, 8
Run Code Online (Sandbox Code Playgroud)

很明显,这条指令需要三个操作数,而不仅仅是两个。

寄存器sp不修改,只读取;因此,它不能被视为目标寄存器。它也是一个源寄存器,就像t0是 - 存储指令将其内容存储在内存中的寄存器。内存是目标操作数,因为它是接收t0.

S 型指令格式不对目标操作数进行编码。指令对目标操作数的寻址信息进行编码。对于sw t0, 8(sp),目标操作数是内存中由存储指令计算有效地址指定的位置处sp8。该寄存器sp包含有关内存中该字(即目标操作数)的部分寻址信息。

概括

RISC-V 中编码目标操作数的汇编指令将此操作数作为第一个。但是,存储指令不会对目标操作数进行编码。它的目标操作数是内存中的一个位置,内存中这个位置的地址是根据指令源操作数的内容计算出来的。


1我们可能会争辩说,jal ra, off上面的指令有一个额外的目标操作数,即pc,因为pc以下列方式更新:pc? pc+符号扩展( off)。然而,执行任何其他指令也会导致修改pc,例如增加pc四(分支和 可能不同jalr)。无论如何,pc它没有编码在任何指令中,也不能作为寄存器直接供程序员访问。因此,对讨论没有兴趣。出于同样的原因,我还在本次讨论中省略了 B 型格式。

2或者正好相反:思考就好像你可以表达addi t0, t0, -1addi t0, -1(t0)。那么你会说这addi需要两个操作数(例如,t0-1(t0))吗?

  • 您提出了一个合理的论点,但指令编码细节不需要通知/对应于 asm 源级语法。我想,它确实使编写汇编器的解析变得更简单。目的地始终位于左侧是完全有效的,包括商店。CISC ISA 的汇编语言通常会这样做,并且心智模型是“内存目标操作数”,其中相关的内存块由寻址模式选择。(是的,存储确实有 2 个输入:数据和寻址模式。)您没有理由不能这样思考 RISC-V。 (2认同)
  • RISC ISA 的 asm 源语法不会将内存目标放在左侧,这基本上只是历史惯例。即使 ARM 也可以执行 `str reg、[reg, reg]` 等功能,但它并不像 MIPS 或 POWER 那样 RISCy。 (2认同)

old*_*mer 4

汇编语言是由汇编器(即程序)定义的。由作者选择语法。汇编器可以选择语法

bob pickle,(jar)
Run Code Online (Sandbox Code Playgroud)

这将是一个完全有效的语法,可以将一个寄存器存储到另一个寄存器定义的地址中。甚至可能在某些汇编语言语法中使用#define 的等效项。

为什么这个问题实际上意味着你想与可能不会在 Stack Overflow 上乱搞的实际开发人员交谈,尽管你可能很幸运,所以这个问题没有实际的答案。

为了有成功的机会,处理器开发人员的最佳利益是创建或雇用某人最初创建汇编程序,然后为新处理器创建工具链,其中包括有人坐下来检查机器代码并从那。对于目标的第三方汇编器来说,成功的机会在于使用类似于原始指令的语法,但如果您不打算将其混淆,为什么还要费心制作新的指令呢?指令语法只是汇编器定义的整个语言的一部分,您会发现 mips、arm 等有很大的变化,并且随着时间的推移,RISC-V 也会发生变化,尽管制造新工具的愿望在过去急剧下降几十年了。

成功的汇编程序必须遵循的唯一规则是逻辑定义的规则,语法可以是他们出于任何原因选择的任何规则。因此,如果您想知道,您必须询问每个作者/团队,甚至不确定 Bugzilla 能否帮助您实现这一目标。

一个相关的原因问题是,我们早年的大部分时间都在目的地在左边度过

y = mx + b
Run Code Online (Sandbox Code Playgroud)

并不是

mx + b = y
Run Code Online (Sandbox Code Playgroud)

哪个理智的人会设计一种汇编语言,其中指令部分的目标位于右侧,即使高级语言也不会这样做。

您的问题的一个可能的答案是,很久以前的某个人很懒,使用相同的代码进行加载/存储,或者剪切和粘贴它。随后的至少 RISC 人员也遵循了这一惯例。

不仅对于英特尔,对于所有主要/次要指令集,您都会发现跨工具、x86、arm、mips、msp430、avr、8051、6502、z80 等的语法不兼容,最终还有 risc-v(如果还没有)。向 gnu 汇编器添加目标的人们必须为制作不兼容的汇编语言而感到自豪,因为他们经常这样做。

指令内的位置通常与汇编语言无关。作者要么在目的地第一个营地,要么在目的地最后一个营地开始。

add r0,r1,r2  ; r0 = r1 + r2 

add r0,r0,r2  ; r0 + r1 -> r2
Run Code Online (Sandbox Code Playgroud)

然后寄存器的名称是自由形式的,有时会有所不同。斧头,%斧头。r0, $0

我认为最近的(可怕的)时尚来自 mips 及其在 v0、a0、t0 等学校中的使用......并且感染了其他不相关的指令集。如今,不同指令集习惯的混杂经常发生。

他们选择如何指示间接@r1, (r1), [r1]...

在执行说明时如何指示前/后增量/修改等。

有些人选择 4(r1),而另一些人则使用 [r1,#4]

首先汇编语言或个人大量使用的语言在他们如何处理他人方面发挥着作用,有些人只需要制作自己的工具来避免学习另一种语言或处理他们不喜欢另一种语言的东西,从而AT&T 的事情,可能是 gnu 汇编器的选择。绝对是 MIPS 处理调用约定的方式,以及这个概念、功能?如何感染其他工具,甚至可能感染教室。

特别关注 x86 汇编语言随时间的演变(AT&T 与 Intel 的对比与我所说的无关)。

应该如此,您只需学习汇编程序使用的语言并继续前进,或者您编写自己的汇编程序来匹配您喜欢的语言,如果您发布它和其他类似的语言,那么它就可以进入规范,您就可以了看到这种情况发生。

简短的回答,因为其他汇编语言也这样做。因为您可以在设计中看到 RISC-V 和 MIPS 之间的明显联系,毫无疑问,文档的作者也遵循了他们用于引导 RISC-V 的 MIPS 风格。规则的例外情况也会发生,而始终保留目的地将是更纯粹的解决方案。正如您所指出的,更重要的是一致性。不要以一种方式存储一种口味,而以另一种方式存储另一种口味。看一下典型的 ARM 语法中的 MRS/MSR,目标/源位于中间,在同一个位置。

就 gnu 汇编器而言,binutils 是开源的,您可以完全自由地切换它,同样,您可以根据需要自由地使用顺序和语法创建自己的汇编器。如果您希望它成为链的一部分,那么与当前的工具链一样,您需要创建/更改编译器以匹配汇编器和链接器。

如果这严格来说是一个“为什么”问题,那么它主要是基于意见的,应该关闭。文档的作者和汇编程序(后端)的作者可以自由选择,这就是选择。