Компиляция следующего кода в 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.)