【发布时间】:2016-03-11 14:14:07
【问题描述】:
考虑以下将使用 GCC 编译的 C 和 (ARM) 程序集 sn-p:
__asm__ __volatile__ (
"vldmia.64 %[data_addr]!, {d0-d1}\n\t"
"vmov.f32 q12, #0.0\n\t"
: [data_addr] "+r" (data_addr)
: : "q0", "q12");
for(int n=0; n<10; ++n){
__asm__ __volatile__ (
"vadd.f32 q12, q12, q0\n\t"
"vldmia.64 %[data_addr]!, {d0-d1}\n\t"
: [data_addr] "+r" (data_addr),
:: "q0", "q12");
}
在这个例子中,我在循环外初始化一些 SIMD 寄存器,然后让 C 处理循环逻辑,这些初始化的寄存器在循环内使用。
这适用于一些测试代码,但我担心编译器会破坏 sn-ps 之间的寄存器的风险。有什么方法可以确保不会发生这种情况?我可以推断出关于将在 sn-p 中使用的寄存器类型的任何保证(在这种情况下,不会破坏任何 SIMD 寄存器)?
【问题讨论】:
-
如果代码这么简单,为什么不把它全部变成asm呢?还是只是一个例子?
-
这是一个简化的示例,但我当然可以将其全部设为 asm。保留只是它使整个代码更难阅读和修改,并且编译器非常擅长输出循环。
-
无法保证编译器会保留 C 代码中的寄存器。使它成为一个单一语句。并添加一个内存破坏器。
-
您谨慎考虑这种可能性。最安全的选择是不假设在您的程序集 sn-ps 之间 或周围 使用寄存器。不仅汇编段之间的 C 代码可能会弄乱寄存器,而且您对寄存器的操作也可能会破坏 GCC 的寄存器使用。
-
x86 tag wiki 中有一些关于内联汇编的好东西。例如使用 C 代码从不接触的输出操作数是让 gcc 选择哪些暂存寄存器的好方法,而不是硬编码一些。并且@Olaf:如果代码使用内存操作数而不是在寄存器中请求地址,这可以避免使用内存破坏器。然后编译器会知道哪个内存被触动了。 IIRC,要求使用后递增寻址模式是有限制的。