Комментарии 31
Ещё вместо флага O0 можно попробовать Os — будет сгенерирован максимально короткий код без лишних инструкций.
можете взять связку из актуального gcc, OllyDbg и NASM.
GCC вполне умеет сам сохранять asm код (кажись ключ -S) Диалект по умолчанию AT&T но можно переключить на masm (не помню ключ). В коде тоже можно асм использовать (правда только AT&T вроде)
Ну а визуальный отладчик есть в CodeBlocks (который интегрирован с GCC), что вполне удобно. Под вындой вообще достаточно поставить CodeBlocks и получить сразу всё комплектом.
Потому что уже достаточно случаев, когда заявлялись циклы статей, но после первой/реже второй все останавливалось.
так что, надеюсь, что автор не бросит писать )
Суть статьи — «постигаем глубже конкретную версию компилятора gcc с конкретными флагами компиляции, используя ассемблер».
a
окажется в стеке, несмотря на указание register
; а в примере с умножением на два вместо сложения будет сдвиг влево.idiv
.При -O1 и больше имеем:
foo(int): # @foo(int)
mov eax, edi
shr eax, 31
lea eax, [rax + rdi]
sar eax
ret
хочется видеть интерпретацию нашего кода в asm, а не оптимизированного.
Подозреваю, что автор считает, что для каждого фрагмента кода на Си есть некая «каноническая» трансляция в машкод x86, и с отключённой оптимизацией все компиляторы будут выдавать примерно одну и ту же «каноническую трансляцию».
Так вот, это не так.
В том же clang-е большое число проходов оптимизации, и общее количество изменений, которые они могут внести в код в разных обстоятельствах, достигает астрономических величин.
Может быть интересно сравнивать разные реализации одной и той же функции, чтобы понять, какая из них более оптимальна, но пытаться выявить и запомнить все комбинации ассемблерных команд на выходе компилятора — бесполезное дело, имхо.
(Если что, в gcc тоже не одна сотня проходов трансляции/оптимизации, с самыми неожиданными взаимосвязями между проходами. Одной из целей создания llvm/clang как раз и было заменить эту кастрюлю спагетти чем-то более удобным в обслуживании.)
Подозреваю, что автор считает, что для каждого фрагмента кода на Си есть некая «каноническая» трансляция в машкод x86, и с отключённой оптимизацией все компиляторы будут выдавать примерно одну и ту же «каноническую трансляцию».
нет, автор так не считает. Но рассматривать все и со всех сторон, еще и в одной статье, просто перебор.
x==4?3:2
) в код без ветвлений.Там разница между компиляторами, действительно, будет куда интереснее, чем общие места.
int a = 1;
int main(void)
{
return a;
}
переменная будет и не регистровая и не стековая
Так же нам нужен декомпилятор
Простите за занудство, но вы используете дизассемблер, а не декомпилятор.
А про то, что можно сразу генерировать ассемблерные листинги при компиляции, выше уже написали.
Надеюсь, хоть какое-то количество программистов благодаря таким статьям узнает, какие вообще соглашения бывают у компиляторов — calling conventions, stack frames, вот это всё. А то люди считают stack trace какой-то магией.
Конечно, лучше изучать на примере своего рабочего компилятора (поэтому странно, что вы 80x86 32 bit за основу взяли, а не 64), но и так пригодится.
P.S. У самого на мониторе наклеена бумажка с перечнем регистров в Objective C. При отладке в глубинах библиотечных функций — очень выручает. Но Objective C тут попроще обычного — есть полноценная рефлексия и т.п.
А то новичок из этого материала мог получить впечатление, что единственная разница между ними двумя — это «меньше регистров, больше внимания к сути».
push ebp
mov ebp, esp
push ebx
mov ebx, 1
mov eax, ebx
pop ebx
pop ebp
ret
Первые две строчки соответствую прологу функции, и мы их разберем в статье о функциях.
Все-таки сохранение на стек волатильных регистров (ebx) является частью пролога. Его восстановление вы отнесли к эпилогу.
Слово — это 16 бит.
Стоило сделать ремарку что размер слова рознится в зависимости от платформы.
Термин получил распространение в эпоху 16-ти битных процессоров, тогда в регистр помещалось ровно 16 бит. Такой объем информации стали называть словом (word).
Но действительно стоило упомянуть, что на других процессорах традиции другие. Например, на ARM (даже 64-битных) словом считаются 32 бита.
Удивительно, но многие до сих пор считают, что программа на чистом C в общем случае дает более легкий и быстрый код, нежели функционально эквивалентная ей программа на C++. :)
Постигаем Си глубже, используя ассемблер