2013-06-20 3 views
8

Скажем, я объявил в глобальном масштабе:Почему некоторые компиляторы оптимизируют if (a> 0), а не if (* (& a)> 0)?

const int a =0x93191; 

И в главной функции у меня есть следующее условие:

if(a>0) 
    do_something 

неловкая вещь, которую я заметил, что RVDS компилятор будет падать оператор if и в объектном файле нет ветки/jmp.

но Если я пишу:

if(*(&a)>0) 
    do_something 

КРП (cmp и branch) будет в скомпилированный файл объекта.


В отличие от этого, GCC сделать оптимизирует как с (-O1 или -O2 или -O3):

#include <stdio.h> 
const a = 3333; 

int main() 
{ 
    if (a >333) 
     printf("first\n"); 

return 0; 
} 

скомпилирован с -O3:

(gdb) disassemble main 
Dump of assembler code for function main: 
0x0000000100000f10 <main+0>: push %rbp 
0x0000000100000f11 <main+1>: mov %rsp,%rbp 
0x0000000100000f14 <main+4>: lea 0x3d(%rip),%rdi  # 0x100000f58 
0x0000000100000f1b <main+11>: callq 0x100000f2a <dyld_stub_puts> 
0x0000000100000f20 <main+16>: xor %eax,%eax 
0x0000000100000f22 <main+18>: pop %rbp 
0x0000000100000f23 <main+19>: retq 
End of assembler dump. 

И для

#include <stdio.h> 
const a = 3333; 

int main() 
{ 
     if (*(&a) >333) 
       printf("first\n"); 

return 0; 
} 

даст:

(gdb) disassemble main 
Dump of assembler code for function main: 
0x0000000100000f10 <main+0>: push %rbp 
0x0000000100000f11 <main+1>: mov %rsp,%rbp 
0x0000000100000f14 <main+4>: lea 0x3d(%rip),%rdi  # 0x100000f58 
0x0000000100000f1b <main+11>: callq 0x100000f2a <dyld_stub_puts> 
0x0000000100000f20 <main+16>: xor %eax,%eax 
0x0000000100000f22 <main+18>: pop %rbp 
0x0000000100000f23 <main+19>: retq 
End of assembler dump. 

GCC рассматривать и как же (как и должно быть) и RVDS не делает?


Я попытался изучить аффект использования volatile и в RVDS она уронить на if(a>333) но GCC не сделал:

#include <stdio.h> 
volatile const a = 3333; 

int main() 
{ 
    if (a >333) 
     printf("first\n"); 

return 0; 
} 

(gdb) disassemble main 
Dump of assembler code for function main: 
0x0000000100000f10 <main+0>: push %rbp 
0x0000000100000f11 <main+1>: mov %rsp,%rbp 
0x0000000100000f14 <main+4>: cmpl $0x14e,0x12a(%rip)  # 0x100001048 <a> 
0x0000000100000f1e <main+14>: jl  0x100000f2c <main+28> 
0x0000000100000f20 <main+16>: lea 0x39(%rip),%rdi  # 0x100000f60 
0x0000000100000f27 <main+23>: callq 0x100000f36 <dyld_stub_puts> 
0x0000000100000f2c <main+28>: xor %eax,%eax 
0x0000000100000f2e <main+30>: pop %rbp 
0x0000000100000f2f <main+31>: retq 
End of assembler dump. 

Вероятно, есть некоторые ошибки в версии компилятора Я использовал RVDS.

+4

Какой компилятор и какие параметры компилятора? –

+1

@ 0x90 'armvct' Я еще не слышал об этом компиляторе? Можете ли вы опубликовать ссылку, чтобы мы могли высмеять их? – Mikhail

+0

GCC 4.5.3 оптимизирует ветку в обоих случаях на '-O1' и выше для меня. –

ответ

4

Оптимизация - это детали реализации компиляторов. Требуется время и силы для их реализации, а авторы компиляторов обычно сосредотачиваются на общем использовании языка (т. Е. Возврат инвестиций оптимизирующего кода, который очень редки, близок к нулю).

Это означает, что в обеих частях кода существует важное различие, в первом случае a не используется в качестве odr, используется только как rvalue, а это означает, что его можно обрабатывать как постоянную времени компиляции. То есть, когда a используется напрямую (без адреса, ссылки не привязаны к нему), компиляторы немедленно заменяют значение. Значение должно быть известно компилятору без доступа к переменной, поскольку оно может использоваться в контекстах, где константные выражения (т. е. определение размера массива).

Во втором случае используется значение a, адрес берется и считывается значение в этом месте. Компилятор должен создать код, который выполняет эти шаги перед передачей результата оптимизатору. Оптимизатор, в свою очередь, может обнаружить, что он является константой, и заменить всю операцию на значение, но это немного больше, чем в предыдущем случае, когда сам компилятор заполнил значение.

11

Уровень сложности, который должен выполнить компилятор, чтобы узнать, «это то, что я могу понять, что такое фактическое значение», не является неограниченным. Если вы напишете достаточно сложный оператор, компилятор просто скажет: «Я не знаю, что это за значение, я буду генерировать код для его вычисления».

Это совершенно возможно для компилятора, чтобы понять, что он не изменится. Но также возможно, что некоторые компиляторы «сдаются» в процессе - это также может зависеть от того, где в цепочке компиляции этот анализ сделан.

Это, вероятно, довольно типичный пример правила «как-если» - компилятору разрешено выполнять любую оптимизацию, которая генерирует результат «как есть», который был выполнен.

Сказав все это, это должно быть довольно тривиальным (и, согласно комментариям, компилятор должен консмер *(&a) такой же, как a), поэтому кажется странным, что он не избавится от сравнения.

+6

Несомненно, это неспособность компилятора распознать возможность оптимизации, но это не так просто, как скрещивание некоторого порога сложности в компиляторе. '* (& a)' не только очень простое выражение, но оно явно вызывается в сноске в стандарте C как равное 'a' (примечание 83 в стандарте 1999 года, 84 в 1999 году проект TC2 n1124, 102 в 2011). Мы знаем, что этот компилятор оптимизирует 'a> 0', учитывая видимое значение' a'. Таким образом, тот факт, что он не может оптимизировать '* (& a)> 0', говорит о том, что он пропустил ясный и четко сформулированный аспект семантики C. –

+0

Спасибо за разъяснение, Эрик! –

+1

Я голосую за этот пост, потому что происходит что-то еще. Никакой разумный компилятор не уйдет. – Mikhail