以下所有说明都做同样的事情:设置%eax为零.哪种方式最佳(需要最少的机器周期)?
xorl %eax, %eax
mov $0, %eax
andl $0, %eax
Run Code Online (Sandbox Code Playgroud) 在x86-64 Tour of Intel Manuals中,我读到了
也许最令人惊讶的事实是,诸如
MOV EAX, EBX自动将指令的高32位归零的指令RAX.
同一来源引用的英特尔文档(3.4.1.1 64位手动基本架构中的通用寄存器)告诉我们:
- 64位操作数在目标通用寄存器中生成64位结果.
- 32位操作数生成32位结果,在目标通用寄存器中零扩展为64位结果.
- 8位和16位操作数生成8位或16位结果.目标通用寄存器的高56位或48位(分别)不会被操作修改.如果8位或16位操作的结果用于64位地址计算,则将寄存器显式符号扩展为完整的64位.
在x86-32和x86-64汇编中,16位指令如
mov ax, bx
Run Code Online (Sandbox Code Playgroud)
不要表现出这种"奇怪"的行为,即eax的上层词被归零.
因此:引入这种行为的原因是什么?乍一看似乎不合逻辑(但原因可能是我习惯了x86-32汇编的怪癖).
AMD CPU通过解码为两个128b操作来处理256b AVX指令.例如,vaddps ymm0, ymm1,ymm1在AMD上,Steamroller解码为2个宏操作,吞吐量的一半vaddps xmm0, xmm1,xmm1.
XOR归零是一种特殊情况(没有输入依赖性,并且在Jaguar上至少避免消耗物理寄存器文件条目,并且使得来自该寄存器的movdqa在发出/重命名时被消除,就像Bulldozer一直在做非零的REG)中. 但它是否足够vxorps ymm0,ymm0,ymm0早被检测到仍然只能解码为1个具有相同性能的宏操作 vxorps xmm0,xmm0,xmm0?(不像vxorps ymm3, ymm2,ymm1)
或者,在已经解码为两个uop之后,独立检测是否会发生?此外,AMD CPU上的向量xor-zeroing是否仍然使用执行端口?在Intel-CPU上,Nehalem需要一个端口,但Sandybridge系列在发布/重命名阶段处理它.
Agner Fog的指令表没有列出这个特例,他的微指南没有提到uop的数量.
这可能意味着vxorps xmm0,xmm0,xmm0更好的实施方式_mm256_setzero_ps().
对于AVX512 _mm512_setzero_ps(),如果可能的话,也只使用VEX编码的归零惯用语而不是EVEX来保存字节.(即对于zmm0-15. vxorps xmm31,xmm31,xmm31仍然需要EVEX).gcc/clang目前使用他们想要的任何寄存器宽度的xor-zeroing习语,而不是总是使用AVX-128.
报告为clang bug 32862和gcc bug 80636.MSVC已经使用了xmm.尚未向ICC报告,ICC也使用zmm regs进行AVX512归零.(虽然英特尔可能不会改变,因为目前任何英特尔CPU都没有任何好处,只有AMD.如果他们发布的低功耗CPU将矢量分成两半,他们可能.他们目前的低功耗设计(Silvermont)没有t支持AVX,只支持SSE4.)
我知道使用AVX-128指令清零256b寄存器唯一可能的缺点是它不会触发Intel CPU上256b执行单元的预热.可能会破坏试图加热它们的C或C++黑客攻击.
(在第一个256b指令之后的第一个~56k周期内,256b向量指令较慢.请参阅Agner Fog微格式pdf中的Skylake部分).如果调用noinline返回的函数_mm256_setzero_ps不是预热执行单元的可靠方法,那可能没问题.(一个仍然可以在没有AVX2的情况下工作,并且避免任何负载(可以缓存未命中)是__m128 onebits = _mm_castsi128_ps(_mm_set1_epi8(0xff));
return _mm256_insertf128_ps(_mm256_castps128_ps256(onebits), onebits)应该编译为pcmpeqd xmm0,xmm0,xmm0/ vinsertf128 ymm0,xmm0,1.对于你曾经呼叫一次预热(或保持)执行单元的事情,这仍然是非常微不足道的.关键循环.如果你想要内联的东西,你可能需要inline-asm.)
我没有AMD硬件所以我无法测试这个.
如果有人拥有AMD硬件但不知道如何测试,请使用perf计数器来计算周期(最好是m-ops或uops或AMD称之为的任何内容).
这是我用来测试短序列的NASM/YASM源:
section .text
global _start …Run Code Online (Sandbox Code Playgroud) 我是 x86_64 汇编编程的新手。我正在用 x86_64 程序集编写简单的“Hello World”程序。下面是我的代码,它运行得很好。
global _start
section .data
msg: db "Hello to the world of SLAE64", 0x0a
mlen equ $-msg
section .text
_start:
mov rax, 1
mov rdi, 1
mov rsi, msg
mov rdx, mlen
syscall
mov rax, 60
mov rdi, 4
syscall
Run Code Online (Sandbox Code Playgroud)
现在,当我在 gdb 中反汇编时,它会给出以下输出:
(gdb) disas
Dump of assembler code for function _start:
=> 0x00000000004000b0 <+0>: mov eax,0x1
0x00000000004000b5 <+5>: mov edi,0x1
0x00000000004000ba <+10>: movabs rsi,0x6000d8
0x00000000004000c4 <+20>: mov edx,0x1d
0x00000000004000c9 <+25>: syscall
0x00000000004000cb <+27>: mov …Run Code Online (Sandbox Code Playgroud) 看http://ref.x86asm.net/coder32.html我发现两个匹配语句的操作码
xor eax,eax
1)操作码31 XOR r/m16/32 r16/32
2)操作码33 XOR r16/32 r/m16/32
两者都指操作数1和操作数2的32位寄存器.那么,XORing两个32位寄存器的这种特殊情况有什么不同吗?
我正在深入研究 x86 ASM 中的左移和右移操作,例如shl eax, cl
来自 IA-32 英特尔架构软件开发人员\xe2\x80\x99s 手册 3
\n\n\n\n\n所有 IA-32\n 处理器(从 Intel 286 处理器开始)都会将移位计数屏蔽为 5 位,从而导致最大计数为 31。此屏蔽在所有操作模式\n 中完成(包括虚拟 8086模式)以减少指令的最大执行时间。
\n
我试图理解这个逻辑背后的原因。也许它以这种方式工作是因为在硬件级别上很难使用 1 个周期实现寄存器中所有 32(或 64)位的移位?
\n\n任何详细的解释都会有很大帮助!
\n如何为__m256值中的所有位设置值1?使用AVX或AVX2内在函数?
要获得全零,您可以使用_mm256_setzero_si256().
为了得到所有的,我正在使用_mm256_set1_epi64x(-1),但我怀疑这比全零情况慢.这里是否涉及内存访问或Salar/SSE/AVX切换?
我似乎无法在AVX中找到一个简单的按位NOT操作?如果可以,我可以简单地使用setzero,然后使用向量NOT.
assembly ×7
x86 ×6
x86-64 ×3
avx ×2
avx2 ×1
bit-shift ×1
intrinsics ×1
nasm ×1
opcode ×1
optimization ×1
performance ×1
shellcode ×1
x86-16 ×1
xor ×1