Если вы действительно хотите знать о значениях регистров , а не __m128i
C значениях переменных, я бы предложил использовать отладчик, такой как GDB. print /x $xmm0.v2_int64
при остановке в точке останова.
Захват регистра в верхней части функции - довольно ненадежная и ненадежная вещь, чтобы попытаться попытаться (пахнет так, как будто вы уже упали неправильный расчетный путь) 1 . Но вы на правильном пути с локальной переменной register-asm. Однако xmm0 не может соответствовать ограничению "=r"
, только "=x"
. См. Чтение значения регистра в переменную C для получения дополнительной информации об использовании пустого шаблона asm, чтобы сообщить компилятору, что вы хотите, чтобы переменная C была тем, что было в регистре.
Однако вам нужен оператор asm volatile("" : "=x"(var));
; Локальные переменные GNU C register-asm не имеют никаких гарантий, кроме случаев, когда они используются в качестве операндов для операторов asm
. (G CC часто все равно будет хранить ваш var в этом регистре, но IIR C clang не будет.)
Нет большой гарантии относительно того, где это будет заказано. другой код (asm volatile
может помочь некоторым, или для более сильного заказа также используйте "memory"
clobber). Также нет гарантии, что G CC сначала не будет использовать регистр для чего-то другого. (Особенно регистр с закрытым вызовом, такой как любой регистр xmm.) Но он, по крайней мере, работает в версии, которую я тестировал.
print a __m128i variable показывает, как напечатать __m128i
как две 64-битные половинки, если они у вас есть, или как элементы других размеров. Компилятор часто оптимизирует _mm_store_si128
/ перезагружает в случайном порядке, и это в любом случае для печати, так что не усложняйте.
Использование unsigned __int128 tmp;
также было бы вариантом в GNU C на x86-64.
#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
.
Он компилируется в asm мы хотим с G CC и clang ( Godbolt ), не наступая на 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
Footnote 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
аргументы), это может сработать. Это, конечно, Undefined Behavior в ISO C, но если вы действительно прекратите встраивание, эффекты будут зависеть от соглашения о вызовах. Пока вы убедитесь, что это noinline
, поэтому оптимизация времени компоновки не выполняет межфайловое встраивание.
Конечно, простой факт вставки вызова функции изменит распределение регистров в вызывающий, так что это помогает только для функции, которую вы все равно собирались вызвать.