this page的add
说明文档说明如下:
请注意我突出显示的两条说明。
我在NASM中尝试了以下代码(符合第一个突出显示的指令):
add WORD [myvar], BYTE 0xA5
但是我收到了以下错误:
警告:签名字节值超出界限
我做错了什么?
答案 0 :(得分:7)
8位立即数操作数(此处由imm8
表示)被符号扩展为16(或32)位,以匹配另一个操作数(r/m16
或r/m32
的大小,分别)。
因此,只能表示介于-128和127之间的值,这就是您从汇编程序收到此警告的原因。
对于值0xA5,您需要使用WORD
立即(imm16
):
add WORD [myvar], WORD 0xA5
(尽管WORD
在源操作数上是可选的,因为它是由常量的大小暗示的。)
答案 1 :(得分:2)
我不会重复@ fuz的回答,但我想补充一下:
如果您刚刚让汇编程序通过编写add word [myvar], 0xA5
来完成其工作,那么它将选择最小的编码。如果您的立即符合扩展名为imm8的版本,则会使用add r/m16, imm8
编码。通常不需要在非内存操作数上使用size-overrides。所有主要的x86汇编程序都优化了立即操作数的大小。有些人(例如NASM)甚至会将mov rax, 1
优化为相当但更短的mov eax, 1
,以及类似的东西,但其他人(YASM)不会。
但是,您可以强制汇编程序使用比填充/对齐所需更宽的立即数。例如add word [myvar], strict word 1
。会使用imm16
版本。 (没有strict
,它不会阻止汇编程序将其优化为较小的编码。)您还可以add word [rcx + strict dword 0], strict word 1
强制[base + disp32]
编码用于寻址模式。
如果可能,避免对mov
以外的指令执行16位立即操作数。在许多Intel CPU上,由于an LCP stall,该指令的解码速度很慢。对于具有解码uop缓存的较新CPU,这可能不是问题。但是在较旧的Intel CPU上,这可能会以划痕寄存器为代价运行得更快:
movzx eax, word [myvar]
add eax, 0xA5 # add ax, 0xa5 is 1B smaller, but has the same LCP stall.
mov [myvar], ax
add
/ sub
从左到右,因此较宽的添加的低部分始终与您从狭窄的add
获得的部分相同。避免寄存器操作数的LCP停顿通常很便宜(add eax,imm32
只需要额外的1B,因为它不需要操作数大小的前缀),但加载和存储是额外的。
这是一个更大的代码大小,因此在没有LCP停顿的CPU上可能会更慢。英特尔Sandybridge系列的前端只有1个uop(可以在单指令版本中微量融合负载+加入),执行单元/调度程序的uop数量相同。 (内存目标指令解码加载,ALU和存储uops。)