如何在asm()中定义指向XMM寄存器的指针? 就像在循环中访问数组元素一样,如何使用计数器访问asm中的寄存器? 我尝试在以下代码中执行此操作:
float *f=(float*)_aligned_malloc(64,16);
for(int i=0;i<4;i++)
asm volatile
(
"movaps (%1),%%xmm%0"
:
:"r"(i),"r"(f+4*i)
:"%xmm%0"
);
但是编译器给了我这个错误:
unknown register name '%xmm%0' in 'asm'
答案 0 :(得分:3)
与使用汇编程序宏或实际手动展开相比,这听起来像是一个可怕的想法。如果gcc决定不完全展开循环,你的代码将完全破解,因为它只能用于编译时常量索引。
此外,没有办法告诉编译器你将结果放在哪个寄存器中,所以这基本上没用了。我只是回答使用GNU C inline-asm语法时的愚蠢行为,不是因为这个答案在任何项目中都可能有用。
也就是说,您可以使用"i"
约束和a c
operand modifier将此格式设置为裸号格式,例如1
而不是$1
。
void *_aligned_malloc(int, int);
void foo()
{
float *f=(float*)_aligned_malloc(64,16);
for(int i=0;i<4;i++) {
asm volatile (
"movaps %[input],%%xmm%c[regnum]"
:
// only compiles with optimization enabled.
:[regnum] "i"(i), [input] "m"(f[4*i])
:"%xmm0", "%xmm1", "%xmm2", "%xmm3"
);
}
}
gcc和clang,-O3
,能够完全展开并为每次迭代生成i
一个可以匹配"i"
约束的编译时常量。 This compiles on Godbolt
# gcc7.3 -O3
foo():
subq $8, %rsp
movl $16, %esi
movl $64, %edi
call _aligned_malloc(int, int) # from a dummy prototype so it compiles
movaps (%rax),%xmm0
movaps 16(%rax),%xmm1 # compiler can use addressing modes because I switched to an "m" constraint
movaps 32(%rax),%xmm2
movaps 48(%rax),%xmm3
vzeroupper # XMM clobbers also include YMM, and I guess gcc assumes you might have dirtied the upper lanes.
addq $8, %rsp
ret
请注意,我只告诉编译器读取每组4的第一个float
。
ICC -O3
即使catastrophic error: Cannot match asm operand constraint
也说-O3
。当然,在禁用优化的情况下,gcc和clang会遇到同样的问题。例如,gcc -O0会说:
<source>: In function 'void foo()':
<source>:11:10: warning: asm operand 0 probably doesn't match constraints
);
^
<source>:11:10: error: impossible constraint in 'asm'
Compiler returned: 1
因为没有优化,i
不是编译时常量,不能匹配"i"
(立即)约束。
显然你不能使用"r"
约束;如果编译器选择%xmm%eax
,那将填充asm模板eax
。
无论如何,这是无用的,因为你不能使用目的地寄存器。您所能做的就是告诉编译器所有可能的目标寄存器都被破坏了。在一个asm语句中写入被破坏的寄存器是不安全的,然后假设该值仍然存在于稍后的asm语句中。
与所有其他体系结构一样,x86无法使用运行时值索引体系结构寄存器。注册号必须硬编码到指令流中。
(有些微控制器,比如AVR,有内存映射寄存器,所以你可以通过索引寄存器文件别名的内存来索引它们。但这种情况很少见,而且x86不会这样做。它会干扰掉 - 按照与自修改代码类似的方式执行顺序执行.BTW,SMC(或分支到16个不同版本的指令之一)是运行时索引寄存器文件的唯一选项。)
答案 1 :(得分:2)
你不能 - 没有办法索引寄存器文件。
如果要按顺序使用多个寄存器,则需要展开循环并明确命名每个寄存器。