64位架构下的汇编寄存器

19

在关于汇编寄存器大小的答案之后:

  • 首先,在64位架构中,eaxaxah及其对应的寄存器大小是多少?如何访问单个寄存器的字节以及如何访问所有64位寄存器的八个字节?

    我希望包括x86-64(x64)Itanium处理器。

  • 其次,在新的调用约定中,使用四个寄存器作为函数调用中前四个参数的正确方法是什么?


1
eax == 32bits ax == 16bits ah|al == 8bits,这一点从未改变。x64添加了新的寄存器,在16位时代我们只有ax & al & ah,当32位寻址出现时,它以一种不会影响如何寻址16位或8位寄存器的方式被添加。x64中的新寄存器(重叠的64位寄存器,例如覆盖ax的eax等)以“r”开头,因此有raxrbx等等。更多信息请参见这里 - James
1个回答

55

使用旧名称时,所有寄存器的大小保持不变,就像从x86-16扩展到x86-32一样。要访问64位整数寄存器,您可以使用带有R-prefix的新名称,例如rax,rbx...

寄存器名称不会改变,因此您仍然可以像以前一样使用字节寄存器(al,bl,cl,dl,ah,bh,ch,dh)来表示ax,bx,cx,dx的LSB和MSB。(AX,AH,AL如何映射到EAX?)

还有8个新的寄存器,分别是r8-r15。你可以通过添加后缀b来访问它们的最低有效位(如果你使用AMD,则为l)。例如,r8b、r9b、r10l、r11l...你还可以使用esi、edi、esp、ebp的最低有效位,分别为sil、dil、spl、bpl,并配合新的REX前缀使用,但不能与ah、bh、ch或dh同时使用。
同样,新寄存器的最低字或双字可以通过后缀"w"或"d"进行访问。写入32位寄存器时,会将其零扩展为完整的64位寄存器,与写入低8位、高8位或低16位部分寄存器的情况不同,8086/386的语义仍然适用。
更新:Intel刚刚推出了一个名为APX的新扩展,为x86-64增加了16个名为r16-r31的寄存器。
因此,通用寄存器的列表如下:
64位寄存器 低32位 低16位 低8位 rax eax ax al rbx ebx bx bl rcx ecx cx cl rdx edx dx dl rsi esi si sil rdi edi di dil rbp ebp bp bpl rsp esp sp spl r8 r8d r8w r8b (r8l) r9 r9d r9w r9b (r9l) r10 r10d r10w r10b (r10l) r11 r11d r11w r11b (r11l) r12 r12d r12w r12b (r12l) r13 r13d r13w r13b (r13l) r14 r14d r14w r14b (r14l) r15 r15d r15w r15b (r15l) r16 (with APX) r16d r16w r16b (r16l) r17 (with APX) r17d r17w r17b (r17l) ... r31 (with APX) r31d r31w r31b (r31l)
当然,还有其他类型的寄存器,如控制寄存器调试寄存器标志寄存器,浮点寄存器,向量寄存器,段寄存器,测试寄存器等等。更多详细信息请查看https://wiki.osdev.org/CPU_Registers_x86。另请参阅新的X86_64处理器寄存器的名称是什么?

调用约定

关于调用约定,在每个特定的系统上只有一种约定1。请点击链接了解有关整数和向量寄存器的详细信息,包括被调用破坏 vs. 被调用保留,以及在call之前RSP的16字节对齐和可变参数函数的特殊情况。

  • 在Windows上

    • 如果参数是整数或指针,前四个参数使用RCX、RDX、R8、R9寄存器
    • 浮点数参数使用XMM0、XMM1、XMM2、XMM3寄存器
    • 调用者在返回地址上方保留32字节的“阴影空间”。(如果有任何堆栈参数,它们位于阴影空间之上。)


    1自从MSVC 2013以来,在Windows上还有一个名为__vectorcall新扩展约定,因此“单一约定策略”不再适用。

  • 在Linux和其他遵循System V AMD64 ABI的系统上,可以将更多参数传递到寄存器中,并且堆栈下方有一个128字节的红区,这可能使叶子函数更快。

    • 前六个整数或指针参数使用RDI、RSI、RDX、RCX、R8和R9寄存器传递
    • 浮点数参数使用XMM0到XMM7寄存器传递

要获取更多信息,请阅读x86-64x86-64调用约定

Plan 9中还有一种惯例

  • 所有寄存器都由调用者保存
  • 所有参数都通过堆栈传递
  • 返回值也在堆栈上返回,在参数下方(栈方式;在amd64上是较高的地址)保留空间。

Golang遵循Plan 9的调用约定, 但自从go 1.17+以来,他们逐渐引入了基于寄存器的调用约定以提高性能。调用约定可能会在将来发生变化,编译器可以生成存根来自动调用旧约定的汇编函数。目前ABI规定

  • 使用9个通用寄存器传递整数参数:RAX,RBX,RCX,RDI,RSI,R8,R9,R10,R11
  • 使用15个寄存器XMM0-XMM14传递浮点参数
实际上,Plan 9一直都是一个怪胎。例如,在没有硬件零寄存器的RISC架构上,它强制一个寄存器为0。它还在x86架构上使用一致的寄存器名称,无论是16位、32位还是64位,操作数大小由助记符后缀指示。这意味着ax寄存器可以是16位、32位或64位,取决于指令的后缀。如果你对此感兴趣,请阅读以下链接:
- [Plan 9汇编器手册](link1) - [Go/plan9的汇编器很奇怪](link2)
OTOH Itanium是一种完全不同的架构,与x86-64毫无关系。它是一种纯64位架构,因此所有普通寄存器都是64位的,没有32位或更小版本可用。它有很多寄存器:
  • 128个通用整数寄存器r0到r127,每个寄存器携带64个值位和一个陷阱位。关于陷阱位的更多信息我们稍后会了解。
  • 128个浮点寄存器f0到f127。
  • 64个谓词寄存器p0到p63。
  • 8个分支寄存器b0到b7。
  • 一个指令指针,Windows调试引擎不知何故称之为iip。(多出来的“i”是为了“疯狂”吗?)
  • 128个特殊用途寄存器,其中并非所有寄存器都有特定含义。由于某种原因,它们被称为“应用寄存器”(ar)。在讨论过程中,我将介绍一些选择的寄存器。
  • 其他我们在本系列中不涉及的杂项寄存器。

阅读更多关于Itanium处理器,第1部分:热身的内容。

了解更多关于x64和IA-64之间的区别是什么?的信息。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接