gcc -O3 optimize:: xmm0 зарегистрироваться?

(Я плохо говорю по-английски.. пожалуйста, поймите ТТ)

Я писал функцию vsprintf для использования моего ядра 64-битной ОС (написано на C) и проверял, хорошо ли она работает в Visual Studio и Cygwin gcc. Затем я установил ядро ​​и запустил... но ядро ​​не работает

Я отладил и выяснил проблему: vsprintf содержит следующий код сборки

movdqa xmm0,XMMWORD PTR [rip+0x0]

Настоящая проблема в том, что я НИКОГДА не использую плавающую точку!

Я думаю, что это была оптимизация GCC, и это кажется правильным, потому что он работает хорошо без оптимизации

Есть ли, так сказать, вариант gcc, который отключает оптимизацию с помощью регистров xmm?

2 ответа

Решение

Команды перемещения регистра XMM генерируются, потому что в System V AMD64 ABI аргументы с плавающей запятой хранятся в XMM0–XMM7.

Поскольку мы не знаем, используются ли числа с плавающей запятой, просто взглянув на функцию с переменным числом, компилятору необходимо сгенерировать инструкции для передачи значений с плавающей запятой va_list также.


Вы могли бы использовать -mno-sse флаг для отключения SSE. Например,

__attribute__((noinline))
void f(const char* x, ...) {
    va_list va;
    va_start(va, x);
    vprintf(x, va);
    va_end(va);
}

Без -mno-sse флаг:

subq    $0x000000d8,%rsp
testb   %al,%al
movq    %rsi,0x28(%rsp)
movq    %rdx,0x30(%rsp)
movq    %rcx,0x38(%rsp)
movq    %r8,0x40(%rsp)
movq    %r9,0x48(%rsp)
je  0x100000f1b
movaps  %xmm0,0x50(%rsp)
movaps  %xmm1,0x60(%rsp)
movaps  %xmm2,0x70(%rsp)
movaps  %xmm3,0x00000080(%rsp)
movaps  %xmm4,0x00000090(%rsp)
movaps  %xmm5,0x000000a0(%rsp)
movaps  %xmm6,0x000000b0(%rsp)
movaps  %xmm7,0x000000c0(%rsp)
0x100000f1b:
leaq    0x000000e0(%rsp),%rax
movl    $0x00000008,0x08(%rsp)
movq    %rax,0x10(%rsp)
leaq    0x08(%rsp),%rsi
leaq    0x20(%rsp),%rax
movl    $0x00000030,0x0c(%rsp)
movq    %rax,0x18(%rsp)
callq   0x100000f6a ; symbol stub for: _vprintf
addq    $0x000000d8,%rsp
ret

С -mno-sse флаг:

subq    $0x58,%rsp
leaq    0x60(%rsp),%rax
movq    %rsi,0x28(%rsp)
movq    %rax,0x10(%rsp)
leaq    0x08(%rsp),%rsi
leaq    0x20(%rsp),%rax
movq    %rdx,0x30(%rsp)
movq    %rcx,0x38(%rsp)
movq    %r8,0x40(%rsp)
movq    %r9,0x48(%rsp)
movl    $0x00000008,0x08(%rsp)
movq    %rax,0x18(%rsp)
callq   0x100000f6a ; symbol stub for: _vprintf
addq    $0x58,%rsp
ret

Вы также можете использовать target атрибут для отключения SSE только для этой функции, например

__attribute__((noinline, target("no-sse")))
//                       ^^^^^^^^^^^^^^^^
void f(const char* x, ...) {
    va_list va;
    va_start(va, x);
    vprintf(x, va);
    va_end(va);
}

Но имейте в виду, что другие функции с поддержкой SSE не будут знать f не использует SSE, и, следовательно, вызов их с числами с плавающей точкой вызовет неопределенное поведение:

int main() {
    f("%g %g", 1.0, 2.0);  // 1.0 and 2.0 are stored in XMM0–1
                           // So this will print garbage e.g. `0 6.95326e-310`
}

Используйте -O2 вместо -O3 это будет работать.

Другие вопросы по тегам