64位汇编,何时使用较小尺寸的寄存器 [英] 64 bit assembly, when to use smaller size registers
问题描述
我知道在x86_64汇编中有(64位)rax寄存器,但也可以作为32位寄存器eax,16位,ax和8位等来访问它.在什么情况下我不仅会使用完整的64位,为什么,会有什么优势?
I understand in x86_64 assembly there is for example the (64 bit) rax register, but it can also be accessed as a 32 bit register, eax, 16 bit, ax, and 8 bit, al. In what situation would I not just use the full 64 bits, and why, what advantage would there be?
作为示例,使用这个简单的hello world程序:
As an example, with this simple hello world program:
section .data
msg: db "Hello World!", 0x0a, 0x00
len: equ $-msg
section .text
global start
start:
mov rax, 0x2000004 ; System call write = 4
mov rdi, 1 ; Write to standard out = 1
mov rsi, msg ; The address of hello_world string
mov rdx, len ; The size to write
syscall ; Invoke the kernel
mov rax, 0x2000001 ; System call number for exit = 1
mov rdi, 0 ; Exit success = 0
syscall ; Invoke the kernel
rdi和rdx至少只需要8位而不是64位,对吗?但是,如果我分别将它们更改为dil和dl(它们的低8位等效项),则该程序将进行汇编和链接,但不会输出任何内容.
rdi and rdx, at least, only need 8 bits and not 64, right? But if I change them to dil and dl, respectively (their lower 8-bit equivalents), the program assembles and links but doesn't output anything.
但是,如果我使用eax,edi和edx,它仍然可以工作,那么我应该使用那些而不是完整的64位吗?为什么或为什么不呢?
However, it still works if I use eax, edi and edx, so should I use those rather than the full 64-bits? Why or why not?
推荐答案
首先要从内存中加载较小的值(例如8位)(读取char,处理数据结构,反序列化网络数据包) ,等等.)进入寄存器.
First and foremost would be when loading a smaller (e.g. 8-bit) value from memory (reading a char, working on a data structure, deserialising a network packet, etc.) into a register.
MOV AL, [0x1234]
与
MOV RAX, [0x1234]
SHR RAX, 56
# assuming there are actually 8 accessible bytes at 0x1234,
# and they're the right endianness; otherwise you'd need
# AND RAX, 0xFF or similar...
或者,当然,将所述值写回内存中.
Or, of course, writing said value back to memory.
(如6年后一样进行编辑):
由于这种情况不断出现:
Since this keeps coming up:
MOV AL, [0x1234]
- 仅读取0x1234的单个字节的内存(反之只会覆盖单个字节的内存)
- 保留RAX其他56位中的任何内容
- 这会在RAX的过去值和将来值之间建立依赖关系,因此CPU无法使用
- only reads a single byte of memory at 0x1234 (the inverse would only overwrite a single byte of memory)
- keeps whatever was in the other 56 bits of RAX
- This creates a dependency between the past and future values of RAX, so the CPU can't optimise the instruction using register renaming.
对比:
MOV RAX, [0x1234]
- 从0x1234开始读取8个字节的内存(反之会覆盖8个字节的内存)
- 覆盖RAX的全部
- 假定内存中的字节与CPU具有相同的字节序(在网络数据包中通常不正确,因此几年前我的
SHR
指令) - reads 8 bytes of memory starting at 0x1234 (the inverse would overwrite 8 bytes of memory)
- overwrites all of RAX
- assumes the bytes in memory have the same endianness as the CPU (often not true in network packets, hence my
SHR
instruction years ago)
还要注意的重要事项:
MOV EAX, [0x1234]
- 从0x1234开始读取4个字节的内存(反之会覆盖4个字节的内存)
- 覆盖所有RAX ,但高位将全部为零
- 请参阅:为什么大多数x64指令将32位寄存器的高位归零
- reads 4 bytes of memory starting at 0x1234 (the inverse would overwrite 4 bytes of memory)
- overwrites all of RAX, but the high bits will all be zero
- see: Why do most x64 instructions zero the upper part of a 32 bit register
然后,如评论中所述,有:
Then, as mentioned in the comments, there is:
MOVZX EAX, byte [0x1234]
- 仅读取0x1234的单个字节的内存
- 扩展该值,以用零填充所有EAX(并因此填充RAX)(消除依赖性并允许寄存器重命名优化).
在所有这些情况下,如果要从'A'寄存器中写入到内存中,则必须选择宽度:
In all of these cases, if you want to write from the 'A' register into memory you'd have to pick your width:
MOV [0x1234], AL ; write a byte (8 bits) MOV [0x1234], AX ; write a word (16 bits) MOV [0x1234], EAX ; write a dword (32 bits) MOV [0x1234], RAX ; write a qword (64 bits)
这篇关于64位汇编,何时使用较小尺寸的寄存器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
- 这会在RAX的过去值和将来值之间建立依赖关系,因此CPU无法使用