如果你真的想知道 register 值,而不是__m128i C 变量值,我建议使用像 GDB 这样的调试器。 print /x $xmm0.v2_int64 在断点处停止时。
在函数顶部捕获寄存器是一种非常不稳定且不可靠的尝试尝试(闻起来就像您已经走错了设计路径)1支持>。但是您使用 register-asm local var 走在正确的轨道上。但是,xmm0 不能匹配 "=r" 约束,只能匹配 "=x"。有关使用空 asm 模板告诉编译器您希望 C 变量成为寄存器中的内容的更多信息,请参阅Reading a register value into a C variable。
不过,您确实需要 asm volatile("" : "=x"(var)); 语句; GNU C register-asm local vars 没有任何保证,除非用作asm 语句的操作数。 (无论如何,GCC 通常会将您的 var 保存在该寄存器中,但 IIRC clang 不会。)
对于将在何处订购此产品并没有太多保证。其他代码(asm volatile 可能会有所帮助,或者对于更强的排序也可以使用"memory"clobber)。也不能保证 GCC 不会首先将寄存器用于其他用途。 (特别是像任何 xmm reg 一样的调用破坏寄存器。)但它至少在我测试的版本中确实可以工作。
print a __m128i variable 展示了如何将 __m128i 打印为两个 64 位的一半,或者作为其他元素大小。编译器通常会优化_mm_store_si128 / 重新加载到随机播放中,无论如何这都是为了打印所以保持简单。
在 x86-64 上的 GNU C 中,使用 unsigned __int128 tmp; 也是一个选项。
#include <immintrin.h>
#include <stdint.h>
#include <stdio.h>
#ifndef __cplusplus
#include <stdalign.h>
#endif
// If you need this, you're probably doing something wrong.
// There's no guarantee about what a compiler will have in XMM0 at any point
void foo() {
register __m128i xmm0 __asm__("xmm0");
__asm__ volatile ("" :"=x"(xmm0));
alignas(16) uint64_t buf[2];
_mm_store_si128((__m128i*)buf, xmm0);
printf("%llu %llu\n", buf[1], buf[0]); // I'd normally use hex, like %#llx
}
这会首先打印高半部分(最重要的部分),因此从左到右读取两个元素,我们会按内存地址的降序排列buf 中的每个字节。
它可以使用 GCC 和 clang (Godbolt) 编译成我们想要的 asm,在阅读之前不会踩到 xmm0。
# GCC10.2 -O3
foo:
movhlps xmm1, xmm0
movq rdx, xmm0 # low half -> RDX
mov edi, OFFSET FLAT:.LC0
xor eax, eax
movq rsi, xmm1 # high half -> RSI
jmp printf
脚注 1:
如果您确定您的函数没有内联,您可以利用调用约定来获取 xmm0..7(对于 x86-64 System V)或 xmm0..3(如果您没有)的传入值整数参数 (Windows x64)。
__attribute__((noinline))
void foo(__m128i xmm0, __m128i xmm1, __m128i xmm2, etc.) {
// do whatever you want with the xmm0..7 args
}
如果您想为调用者使用的函数提供不同的原型(省略 __m128i 参数),那可能可行。这当然是 ISO C 中的未定义行为,但如果你真的停止内联,效果取决于调用约定。只要您确保它是noinline,那么链接时优化就不会进行跨文件内联。
当然,插入函数调用这一事实会改变调用者中的寄存器分配,因此这仅对您将要调用的函数有所帮助。