При работе с инструкциями расширений SSE/AVX нам может потребоваться сохранить значения регистров XMM/YMM. Рассмотрим на примере программы на Linux сохранение первых 6 регистров XMM/YMM:
global _start section .data AVXSupport equ 0x10000000 ; проверяем бит 28 section .text _start: mov rax, 22 movq xmm0, rax ; для теста помещаем некоторое число в xmm0 ; Резервируем в стеке место для регистров AVX/SSE ; Для 6 регистров SSE необходимо только 96 байт ; Для 6 регистров AVX 196 байт ; Выделяем одно пространство для двух типов расширений - 196 байт ; Тогда SSE просто игнорируют дополнительные 64 байта sub rsp, 192 ; Определяем, доступны ли расширения AVX и соответственно регистры YMM mov rax, 1 cpuid and ecx, AVXSupport ; проверяем на поддержку AVX бит 28 jnz saveAVX ; если AVX доступно, переходим к метке preserveAVX ; Если поддержка расширений AVX отсутствует, сохраняет состояние регистров XMM0-XMM5 movdqu [rsp + 0], xmm0 movdqu [rsp + 16], xmm1 movdqu [rsp + 32], xmm2 movdqu [rsp + 48], xmm3 movdqu [rsp + 64], xmm4 movdqu [rsp + 80], xmm5 jmp afterSave ; сохраняем состояние регистров YMM0-YMM5 saveAVX: vmovdqu [rsp + 0], ymm0 vmovdqu [rsp + 32], ymm1 vmovdqu [rsp + 64], ymm2 vmovdqu [rsp + 96], ymm3 vmovdqu [rsp + 128], ymm4 vmovdqu [rsp + 160], ymm5 afterSave: ; здесь какие-нибудь действия в программе mov rax, 15 ; например, поместим в регистр RAX какое-нибудь число movq xmm0, rax ; и из регистра rax в xmm0, чтобы он изменился ; восстанавливаем значения регистров - в регистре ECX по прежнему значение для AVXSupport ; можно его использовать, чтобы заново не вызывать инструкцию cpuid and ecx, AVXSupport ; проверяем на поддержку AVX бит 28 jnz restoreAVX ; если AVX доступно, переходим к метке restoreAVX ; если не доступно AVX, восстанавливаем регистры XMM movdqu xmm0, [rsp] movdqu xmm1, [rsp + 16] movdqu xmm2, [rsp + 32] movdqu xmm3, [rsp + 48] movdqu xmm4, [rsp + 64] movdqu xmm5, [rsp + 80] jmp exit ; восстанавливаем регистры YMM restoreAVX: vmovdqu ymm0, [rsp + 0] vmovdqu ymm1, [rsp + 32] vmovdqu ymm2, [rsp + 64] vmovdqu ymm3, [rsp + 96] vmovdqu ymm4, [rsp + 128] vmovdqu ymm5, [rsp + 160] exit: add rsp, 192 movq rdi, xmm0 ; проверяем восстановленное значение - должно быть число 22 mov rax, 60 syscall
Как и в общем случае, проблема может заключаться в том, что мы можем не знать точно, какие именно расширения поддерживаются на текущей машине. Поэтому сначала с помощью инструкции
cpuid
проверяем наличие поддержки регистров YMM. Для этого к результату инструкции в регистре ECX применяем маску AVXSupport, которая выявляет установку 28 бита (который собственно и
говорит о поддержке AVX):
mov rax, 1 cpuid and ecx, AVXSupport ; проверяем на поддержку AVX бит 28 jnz saveAVX
Если бит установлен, соответственно после выполнения инструкции AND результат ненулевой, то переходим к метке saveAVX для сохранения состояния регистров YMM. Если бит не установлен, то сохраняем регистры XMM.
Для сохранения 6 регистров XMM нужно 16 байт * 6 = 96 байт. На сохранение 6 регистров YMM - 196 байт. В данном случае для простоты для обоих типов регистров в стеке выделяется 196 байт. Затем последовательно сохраняем регистры:
; Если поддержка расширений AVX отсутствует, сохраняет состояние регистров XMM0-XMM5 movdqu [rsp + 0], xmm0 movdqu [rsp + 16], xmm1 movdqu [rsp + 32], xmm2 movdqu [rsp + 48], xmm3 movdqu [rsp + 64], xmm4 movdqu [rsp + 80], xmm5 jmp afterSave ; сохраняем состояние регистров YMM0-YMM5 saveAVX: vmovdqu [rsp + 0], ymm0 vmovdqu [rsp + 32], ymm1 vmovdqu [rsp + 64], ymm2 vmovdqu [rsp + 96], ymm3 vmovdqu [rsp + 128], ymm4 vmovdqu [rsp + 160], ymm5
При завершении программы аналогично восстанавливаем регистры.
Аналогичная программа на Windows:
global _start section .data AVXSupport equ 0x10000000 ; проверяем бит 28 section .text _start: mov rax, 22 movq xmm0, rax ; для теста помещаем некоторое число в xmm0 ; Резервируем в стеке место для регистров AVX/SSE ; Для 6 регистров SSE необходимо только 96 байт ; Для 6 регистров AVX 196 байт ; Выделяем одно пространство для двух типов расширений - 196 байт ; Тогда SSE просто игнорируют дополнительные 64 байта sub rsp, 192 ; Определяем, доступны ли расширения AVX и соответственно регистры YMM mov rax, 1 cpuid and ecx, AVXSupport ; проверяем на поддержку AVX бит 28 jnz saveAVX ; если AVX доступно, переходим к метке preserveAVX ; Если поддержка расширений AVX отсутствует, сохраняет состояние регистров XMM0-XMM5 movdqu [rsp + 0], xmm0 movdqu [rsp + 16], xmm1 movdqu [rsp + 32], xmm2 movdqu [rsp + 48], xmm3 movdqu [rsp + 64], xmm4 movdqu [rsp + 80], xmm5 jmp afterSave ; сохраняем состояние регистров YMM0-YMM5 saveAVX: vmovdqu [rsp + 0], ymm0 vmovdqu [rsp + 32], ymm1 vmovdqu [rsp + 64], ymm2 vmovdqu [rsp + 96], ymm3 vmovdqu [rsp + 128], ymm4 vmovdqu [rsp + 160], ymm5 afterSave: ; здесь какие-нибудь действия в программе mov rax, 15 ; например, поместим в регистр RAX какое-нибудь число movq xmm0, rax ; и из регистра rax в xmm0, чтобы он изменился ; восстанавливаем значения регистров - в регистре ECX по прежнему значение для AVXSupport ; можно его использовать, чтобы заново не вызывать инструкцию cpuid and ecx, AVXSupport ; проверяем на поддержку AVX бит 28 jnz restoreAVX ; если AVX доступно, переходим к метке restoreAVX ; если не доступно AVX, восстанавливаем регистры XMM movdqu xmm0, [rsp] movdqu xmm1, [rsp + 16] movdqu xmm2, [rsp + 32] movdqu xmm3, [rsp + 48] movdqu xmm4, [rsp + 64] movdqu xmm5, [rsp + 80] jmp exit ; восстанавливаем регистры YMM restoreAVX: vmovdqu ymm0, [rsp + 0] vmovdqu ymm1, [rsp + 32] vmovdqu ymm2, [rsp + 64] vmovdqu ymm3, [rsp + 96] vmovdqu ymm4, [rsp + 128] vmovdqu ymm5, [rsp + 160] exit: add rsp, 192 movq rax, xmm0 ; проверяем восстановленное значение - должно быть число 22 ret