为什么rbp和rsp称为通用寄存器?

Den*_*nis 20 assembly x86-64 cpu-registers

根据英特尔在x64中,以下寄存器称为通用寄存器(RAX,RBX,RCX,RDX,RBP,RSI,RDI,RSP和R8-R15)https://software.intel.com/en-us/articles/介绍到x64组装.

在下面的文章中,写了RBP和RSP是专用寄存器(RBP指向当前堆栈帧的基础,RSP指向当前堆栈帧的顶部). https://www.recurse.com/blog/7-understanding-c-by-learning-assembly

现在我有两个相互矛盾的陈述.英特尔声明应该是值得信赖的,但是什么是正确的,为什么RBP和RSP被称为通用目的?

谢谢你的帮助.

jll*_*gre 17

通用意味着所有这些寄存器都可以与任何使用通用寄存器进行计算的指令一起使用,例如,您无法通过指令指针(RIP)或标志寄存器(RFLAGS)执行任何操作.

其中一些寄存器被设想用于特定用途,通常是.最关键的是RSP和RBP.

如果您需要将它们用于您自己的目的,您应该在将其他内容存储之前保存其内容,并在完成后将其恢复为原始值.

  • 值得注意的是,使用`rpb`作为帧指针基本上完全是_convention_并且实际上没有任何CPU支持(事实上,Windows 64 ABI允许你使用任何寄存器作为帧指针而不喜欢`rbp `).这与`rsp`非常不同,`rsp`在硬件级别与其功能紧密绑定,因为它被`push`,`pop`和朋友隐式使用. (8认同)
  • @BeeOnRope LEAVE和ENTER指令特别支持使用RBP作为帧指针.当用作基础的RBP也像SS一样相对于RSP,而不是像其他人一样的DS相对.在现代x86代码中不使用ENTER指令和单独的数据和堆栈段,但编译器仍然生成LEAVE指令.RBP不能用作没有位移的基础的事实也意味着它通常是用作帧指针的最佳寄存器.它不像RSP那样紧密绑定,但x86指令集倾向于使用RBP作为帧指针. (7认同)
  • 一些编译器可以选择不使用帧指针,在这种情况下,RBP 就成为通用计算机。 (3认同)
  • @BeeOnRope正如Peter Cordes所说,当RBP和RSP用作基础时,SS段仍然被使用,但它在实践中几乎没有差别.我所知道的唯一区别是,如果使用SS段而不是其他段寄存器之一,非规范地址将产生堆栈故障而不是一般保护错误. (3认同)
  • @BeeOnRope:是的,它仍然是真的,但几乎无关紧要.IIRC,x86-64要求SS和DS(以及ES和CS)具有base = 0.IDK在一些假想的操作系统中,你可以将其他东西放在一个重要的段描述符中.只有FS和GS仍然或多或少具有完整功能. (2认同)

Pet*_*des 8

如果寄存器可以是的操作数add,或者可以在寻址模式下使用,则它是“通用”,与FS段寄存器或RIP之类的寄存器相反。即使其他类型的寄存器也可以保存整数,GP寄存器也称为“整数寄存器”。

在计算机体系结构中,CPU内部处理整数寄存器/指令与FP / SIMD寄存器/指令分开是很常见的。例如,英特尔Sandybridge系列CPU具有单独的物理寄存器文件,用于重命名GP整数和FP /向量寄存器。这些简称为整数与FP寄存器文件。(在这里FP是内核不需要保存/恢复即可使用GP寄存器的所有事物的简写,而保留用户空间的FPU / SIMD状态不变。)FP寄存器文件中的每个条目都是256位宽(包含一个AVX ymm向量),但整数寄存器文件条目仅必须为64位宽。

在重命名段寄存器的CPU上(Skylake不会),我想那将是整数状态的一部分,RFLAGS + RIP也是如此。但是,当我们说“整数寄存器”时,通常是专门指通用寄存器。


每个寄存器对于某些指令都有一些特殊性,除了x86-64中添加的某些全新寄存器:R8-R15。这些不会将其视为通用 。原始8(低16)可追溯到8086,甚至在原始8086中也隐含使用它们。

对于RSP,它特别适用于push / pop / call / ret,因此大多数代码从不将其用于其他任何用途。(并且在内核模式下,异步用于中断,因此您真的无法像在用户空间代码中那样将其存储在某个地方以获取额外的GP寄存器:ESP是否像EAX一样通用?

但是在受控条件下(就像没有信号处理程序一样),您不必对堆栈指针使用RSP。例如,您可以使用它来读取带有pop的循环中的数组,例如此代码高尔夫球的答案。(我实际上使用的esp是32位代码,但有相同的区别:poplodsdSkylake 快,而两者均为1字节。)


每个寄存器的隐式用途和特殊性:

另请参见x86 Assembly-为什么在调用约定中保留[e] bx?以获得部分清单。

我主要将其限制为用户空间指令,特别是现代编译器实际上可能从C或C ++代码发出的指令。对于具有很多隐式用法的reg,我并不想穷尽所有。

  • rax:单操作数[i] mul / [i] div / cdq / cdqe,字符串指令(stos)cmpxchg等),以及许多直接指令(如2字节cmp al, 1或5字节)的特殊短编码add eax, 12345(无ModRM)字节)。另请参见codegolf.SE x86 / x64机器代码中的打高尔夫球技巧

    还有xchg-with-eax的0x90 nop来源(nop在x86-64中成为单独记录的指令之前,因为xchg eax,eaxeax零扩展到RAX中,因此无法使用0x90编码。但是仍然xchg rax,rax 可以汇编为REX.W = 1 0x90。)

  • rcx:移位计数,- rep字符串计数,慢速loop指令
  • rdxrdx:rax用于除法和乘法,并由cwd / cdq / cqo为其设置。 rdtscBMI2mulx
  • rbx:8086 xlatbcpuid使用EAX..EDX的全部四个。486 cmpxchg8bx86-64cmpxchg16b。大多数32位编译器都会发出cmpxchg8for std::atomic<long long>::compare_exchange_weak。(不过,如果目标是奔腾或更高版本,则纯负载/纯存储可以使用SSE MOVQ或x87 fild / fistp。)64位编译器将使用64位lock cmpxchg,而不是cmpxchg8b。

    一些64位编译器会发出cmpxchg16batomic<struct_16_bytes>。RBX具有原始8的最少隐式用法,但lock cmpxchg16b实际上是少数几个编译器之一。

  • rsi/ rdi:字符串操作,包括rep movsb某些编译器有时内联的字符串。(gcc rep cmpsb在某些情况下还会内联字符串文字,但这可能不是最佳选择)。
  • rbp:(leave仅比mov rsp, rbp/ 慢1uop。gcc pop rbp实际上不能在带有帧指针的函数中使用它pop rbp)。还有enter没人用过的可怕的慢。
  • rsp:堆栈操作:push / pop / call / ret和leave。(和enter)。并且在内核模式(不是用户空间)中,硬件异步使用它来保存中断上下文。这就是为什么内核代码不能有红色区域的原因。

  • r11syscall/ sysret用它来保存/恢复用户空间的RFLAGS。(与RCX一起保存/恢复用户空间的RIP)。

寻址模式编码的特殊情况:

(另请参见不允许将rbp作为SIB的基础?这只是关于寻址模式,我在其中复制了此答案的这一部分。)

rbp/ r13不能是没有位移的基址寄存器:该编码的意思是:(在ModRM中)rel32(相对于RIP),或者(在SIB中)disp32没有基址寄存器。(r13在ModRM / SIB中使用相同的3位,因此此选择通过不使指令长度解码器查看REX.B位以获得第4个基寄存器位来简化解码)。 [r13]组装到[r13 + disp8=0][r13+rdx]组装到[rdx+r13](如果可以的话,通过交换基数/索引来避免问题)。

rsp/ r12作为基址寄存器始终需要一个SIB字节。(base = RSP的ModR / M编码是转义码,用于向SIB字节发送信号,并且如果r12处理方式不同,则更多的解码器将不得不关心REX前缀)。

rsp不能是索引寄存器。这样就可以进行编码[rsp],它比有用[rsp + rsp]。(英特尔本可以为32位寻址模式(386中的新增功能)设计ModRM / SIB编码,因此只有在base = ESP时才可以使用没有索引的SIB。这将成为[eax + esp*4]可能,并且仅排除[esp + esp*1/2/4/8]。但这没有用,因此他们通过使index = ESP成为不带索引的代码来简化硬件,而不考虑基础,这允许两种冗余方式对任何基础或base + disp寻址模式进行编码:有或没有SIB。

r12 可以是索引寄存器。与其他情况不同,这不会影响指令长度解码。而且,不能像其他情况一样使用更长的编码来解决。AMD希望AMD64的寄存器组尽可能正交,因此在索引/无索引解码中,他们花了一些额外的晶体管来检查REX.X才有意义。例如,[rsp + r12*4]要求index = r12,因此r12没有完全通用的目的会使AMD64成为较差的编译器目标。

   0:   41 8b 03                mov    eax,DWORD PTR [r11]
   3:   41 8b 04 24             mov    eax,DWORD PTR [r12]      # needs a SIB like RSP
   7:   41 8b 45 00             mov    eax,DWORD PTR [r13+0x0]  # needs a disp8 like RBP
   b:   41 8b 06                mov    eax,DWORD PTR [r14]
   e:   41 8b 07                mov    eax,DWORD PTR [r15]
  11:   43 8b 04 e3             mov    eax,DWORD PTR [r11+r12*8] # *can* be an index
Run Code Online (Sandbox Code Playgroud)

当所有寄存器可以用于任何东西时,编译器会喜欢它,只限制一些特殊情况下的寄存器分配。这就是寄存器正交性的含义。