2016-09-01 10 views
4

if(!A && !B) кажется, что она должна составить доПочему не удается (! A &&! B) оптимизировать одну инструкцию TEST?

mov eax, dword ptr[esp + A_offset] 
test eax, dword ptr[esp + B_offset] 
jne ~~~~~~~~~~ 

компилятор на самом деле генерирует

mov eax, dword ptr[esp + A_offset] 
test eax, eax 
jne ~~~~~~~~~~ 
mov eax, dword ptr[esp + B_offset] 
test eax, eax 
jne ~~~~~~~~~~ 

См свалка здесь

8B 45 F8    mov   eax,dword ptr [b] 
83 7D FC 00   cmp   dword ptr [a],0 
75 04    jne   main+32h (0A71072h) 
85 C0    test  eax,eax 
75 00    jne   main+32h (0A71072h) 

Почему не использовать одну команду TEST сохранить филиалы и инструкции?

+2

Что делать, если A = 1 и B = 2? Тогда тест даст неправильный ответ. –

+1

@Yuval Это не дубликат (по крайней мере, не тот конкретный вопрос). Проблема здесь: '(a! = 0) & (b! = 0' vs' (a & b)! = 0', а не ленивая оценка. –

+0

@MarcGlisse В вопросе говорится '&&', а не '&'. – molbdnilo

ответ

2

Из-за short-circuit evaluation.

if(!A && !B) 

Обращаем внимание на приведенный выше код.

Если A является правдивым (не 0),!A && !B становится 0(FALSE). Право, Вам не нужно проверять значение B. Он должен пропустить (перепрыгнуть) код-блок для оператора if.

mov eax, dword ptr[esp + A_offset] 
test eax, eax ; If `A & A` 
jne ~~~~~~~~~~ ; is not 0(If A is not 0), skip this if-codeblock. 
mov eax, dword ptr[esp + B_offset] ; Otherwise, 
test eax, eax ; If `B & B` 
jne ~~~~~~~~~~ ; is not 0(If B is not 0), skip this if-codeblock. 
......   ; Both A and B are 0, and `!A && !B` is `1(TRUE)`! Run the if-codeblock. 

Plus:

Кажется, что ваш код не так ..?

mov eax, dword ptr[esp + A_offset] 
mov ebx, dword ptr[esp + B_offset] 
test eax, ebx ; `A & B` 
jne ~~~~~~~~~~ ; If `A & B != 0`, skip this code-block for the if statement. 
...... ; In other words, this code-block will be run when `A & B == 0`, 
     ; which will be `TRUE` when A is 1(0b00000001) and B is 2(0b00000010). 
+0

Если бы был эффективный способ протестировать оба сразу, компилятор сделает это. Обратите внимание, что код OP уже загружал 'b' в регистр перед тестированием' a', поэтому принятие решения компилятором не имеет ничего общего с оценкой избегания работы или короткого замыкания, и все, что связано с тем, что 'bool (A & B) '! =' A && B', когда A и B отличны от нуля с непересекающимися битовыми шаблонами. Также обратите внимание на комментарий от gcc-разработчика Marc Glisse, что gcc превращает '! A &&! B' в'! (A | b) ', когда он знает, что' b' безопасен для чтения. –

+0

@PeterCordes Ну, честно говоря, причина, почему GCC не делает эту оптимизацию, когда 'b' небезопасно читать, потому что оценка короткого замыкания означает, что она никогда не открывается, если' a' истинно. –

+0

@RossRidge: право, очевидно, это предпосылка для сохранения семантики источника C. Но мой вопрос заключался в выводе компилятора * real * в вопросе (последний блок кода), который загружает до первого теста, доказывая, что они безопасны для доступа и что он не пытается его избежать, поэтому первая гипотеза этого ответа неправильно. –

2

№ Инструкция test выполняет побитовое И из операндов и устанавливает флаги в соответствии с результатом, см. https://en.wikipedia.org/wiki/TEST_(x86_instruction).

Поэтому код, сгенерированный компилятором, верен.

+0

Спасибо. Я был просто смущен –