Компиляция следующего кода в MSVC 2013, сборка с 64-разрядной версией, /O2
Оптимизация:
while (*s == ' ' || *s == ',' || *s == '\r' || *s == '\n') {
++s;
}
Я получаю следующий код, который имеет очень крутую оптимизацию, используя 64-битный регистр в качестве таблицы поиска с инструкцией bt
(бит тест).
mov rcx, 17596481020928 ; 0000100100002400H
npad 5
[email protected]:
movzx eax, BYTE PTR [rsi]
cmp al, 44 ; 0000002cH
ja SHORT [email protected]
movsx rax, al
bt rcx, rax
jae SHORT [email protected]
inc rsi
jmp SHORT [email protected]
[email protected]:
; code after loop...
Но мой вопрос: какова цель movsx rax, al
после первой ветки?
Сначала мы загружаем байт из строки в rax
и нуль-расширяем его:
movzx eax, BYTE PTR [rsi]
Затем пара cmp
/ja
выполняет сравнение без знака между al
и 44
и разветвляется вперед, если al
больше.
Итак, теперь мы знаем 0 <= al <= 44
в неподписанных числах. Поэтому невозможно установить самый старший бит al
!
Тем не менее, следующая инструкция movsx rax, al
. Это расширенный шаг. Но так как:
-
al
- младший байтrax
- мы уже знаем, что остальные 7 байтов
rax
обнуляются - мы просто доказали, что
al
старший бит не может быть установлен
этот movsx
должен быть не-op.
Почему MSVC это делает? Я предполагаю, что это не для заполнения, так как в этом случае другой npad
сделает смысл более понятным. Является ли это сбросом данных или что-то?
(Кстати, эта оптимизация bt
действительно делает меня счастливой. Интересные факты: она работает в 0,6 раза от времени 4 cmp
/je
, которые вы могли бы ожидать, это способ быстрее, чем strspn
или std::string::find_first_not_of
, и это происходит только в 64-битных сборках, даже если интересующие символы имеют значения ниже 32.)