可能这甚至都不是微观但纳米优化,但主题让我感兴趣,我想知道在长模式下使用非本机寄存器大小时是否存在任何惩罚?
我从各种来源了解到,部分寄存器更新(比如ax代替eax)会导致eflags停顿并降低性能.但我不确定长模式.对于此处理器操作模式,哪个寄存器大小被视为原生?x86-64仍然是x86架构的扩展,因此我相信32位仍然是原生的.还是我错了?
例如,像
sub eax, r14d
Run Code Online (Sandbox Code Playgroud)
要么
sub rax, r14
Run Code Online (Sandbox Code Playgroud)
具有相同的尺寸,但在使用其中任何一种时可能会有任何处罚吗?在如下连续指令中混合寄存器大小时可能会有任何处罚吗?(假设高dword在所有情况下均为零)
sub ecx, eax
sub r14, rax
Run Code Online (Sandbox Code Playgroud) 除了其他方面,x86-64 SysV ABI指定了如何在寄存器中传递函数参数(第一个参数in rdi,then rsi等等),以及如何传回整数返回值(in rax和then rdx表示非常大的值).
然而,我找不到的是当传递小于64位的类型时,参数或返回值寄存器的高位应该是什么.
例如,对于以下功能:
void foo(unsigned x, unsigned y);
Run Code Online (Sandbox Code Playgroud)
... x将被传入rdi和y在rsi,但他们只是32位.不要的高32位rdi和rsi必须为零?直观地说,我会假设是,但是所有gcc,clang和icc 生成的代码mov在开始时都有特定的指令将高位清零,所以看起来编译器假定不然.
类似地,编译器似乎假设rax如果返回值小于64 位,则返回值的高位可能具有垃圾位.例如,以下代码中的循环:
unsigned gives32();
unsigned short gives16();
long sum32_64() {
long total = 0;
for (int i=1000; i--; ) {
total += gives32();
}
return total;
}
long sum16_64() {
long total = 0;
for (int i=1000; i--; ) {
total += …Run Code Online (Sandbox Code Playgroud) 我正在关注Linux 64系统中的《开始x64汇编编程》一书。我正在使用 NASM 和 gcc。
在关于浮点运算的章节中,本书指定了以下用于添加 2 个浮点数的代码。在本书和其他在线资源中,我读到寄存器 RAX 根据调用约定指定要使用的 XMM 寄存器的数量。
书中的代码如下:
extern printf
section .data
num1 dq 9.0
num2 dq 73.0
fmt db "The numbers are %f and %f",10,0
f_sum db "%f + %f = %f",10,0
section .text
global main
main:
push rbp
mov rbp, rsp
printn:
movsd xmm0, [num1]
movsd xmm1, [num2]
mov rdi, fmt
mov rax, 2 ;for printf rax specifies amount of xmm registers
call printf
sum:
movsd xmm2, [num1]
addsd xmm2, [num2]
printsum:
movsd …Run Code Online (Sandbox Code Playgroud) 我想替换整数中的最低字节。在 x86 上这确实是这样mov al, [mem],但我似乎无法让编译器输出它。我是否遗漏了一个明显的可识别代码模式,我是否误解了某些内容,或者这只是一个错过的优化?
unsigned insert_1(const unsigned* a, const unsigned char* b)
{
return (*a & ~255) | *b;
}
unsigned insert_2(const unsigned* a, const unsigned char* b)
{
return *a >> 8 << 8 | *b;
}
Run Code Online (Sandbox Code Playgroud)
GCC 实际上使用al但只是为了归零。
mov eax, DWORD PTR [rdi]
movzx edx, BYTE PTR [rsi]
xor al, al
or eax, edx
ret
Run Code Online (Sandbox Code Playgroud)
Clang 几乎逐字编译两者
mov ecx, -256
and ecx, dword ptr [rdi]
movzx eax, byte ptr [rsi]
or eax, ecx …Run Code Online (Sandbox Code Playgroud) 大多数汇编程序利用4个通用寄存器eax ebx ecx edx,但我发现,很多时候我需要使用超过4个寄存器来轻松地完成我的任务,而不必push和pop从堆栈得多.由于我的程序无意使用FPU或MMX寄存器进行浮点计算或"预期用途",在程序中使用这些额外的寄存器是否可以接受?
例如.使用xmm0一个循环递增计数器腾出ecx寄存器做其他事情.
有时gcc使用32位寄存器,当我希望它使用64位寄存器时.例如以下C代码:
unsigned long long
div(unsigned long long a, unsigned long long b){
return a/b;
}
Run Code Online (Sandbox Code Playgroud)
使用-O2选项编译(省略一些样板文件):
div:
movq %rdi, %rax
xorl %edx, %edx
divq %rsi
ret
Run Code Online (Sandbox Code Playgroud)
对于无符号除法,寄存器%rdx需要0.这可以通过xorq %rdx, %rdx但xorl %edx, %edx似乎具有相同的效果来实现.
至少在我的机器上没有性能提升(即加速)进行xorl了xorq.
我实际上不只是一个问题:
xorl并且不使用xorw?xorl比这更快的机器xorq?为什么没有特定的寄存器来访问寄存器的其他部分(16-32)?
像啊或al一样访问ax寄存器的8位部分.

我想问一下CMPXCHG在8位内存字段上使用指令是否比在32位字段上使用指令更糟糕。
我正在使用C11 stdatomic.h来实现一些同步方法。
我正在尝试针对特定的 Kaby Lake CPU (i5-7300HQ) 优化以下子例程,理想情况下,与原始形式相比,代码速度至少快 10 倍。该代码在 16 位实模式下作为软盘式引导加载程序运行。它在屏幕上显示一个十位数的十进制计数器,从 0 - 9999999999 计数然后停止。
我查看了 Agner 的微体系结构和汇编优化指南、 指令性能表和英特尔的优化参考手册。
到目前为止,我能够做的唯一明智的优化是将loop指令交换为dec + jnz,在此处进行解释。
另一种可能的优化可能是交换lodsbfor mov + dec,但我发现的关于它的信息一直存在冲突,有些人说它有一点帮助,而另一些人则认为它实际上可能会损害现代 CPU 的性能。
我还尝试切换到 32 位模式并将整个计数器保留在一个未使用的寄存器对中以消除任何内存访问,但在读入一点后我意识到这十位将立即被缓存,并且 L1 缓存之间的延迟差异和寄存器只有大约三倍,所以绝对不值得以这种格式使用计数器的额外开销。
(编者注:add reg延迟为 1 个周期,add [mem]延迟约为 6 个周期,包括 5 个周期的存储转发延迟。如果[mem]像视频 RAM 那样不可缓存,则更糟。)
org 7c00h
pos equ 2*(2*80-2) ;address on screen
;init
cli
mov ax,3
int 10h
mov …Run Code Online (Sandbox Code Playgroud) 对于 64 位寄存器,有CMOV cc A, B指令,仅在满足条件时B写入:Acc
; Do rax <- rdx iff rcx == 0
test rcx, rcx
cmove rax, rdx
Run Code Online (Sandbox Code Playgroud)
但是,我找不到任何与 AVX 等效的东西。我仍然想根据 的值移动RFLAGS,只是使用更大的操作数:
; Do ymm1 <- ymm2 iff rcx == 0
test rcx, rcx
cmove ymm1, ymm2 (invalid)
Run Code Online (Sandbox Code Playgroud)
有 AVX 等效项吗cmov?如果没有,我怎样才能以无分支的方式实现这个操作?