AVR-GCC: Совмещение C и ассемблера в одном проекте

Читать дальше
- +5
- 25 мая 2011, 22:55
- _YS_
В С/С++ связь отдельных файлов производится при линковке, компилируются они все независимо. Соответственно, .s файл сперва скармливается ассемблеру, затем полученный объектник (вместе с остальными) — линковщику. Но это на уровне тулчейна, а на уровне IDE над этим делают обертку — проект. По сути — список файлов, которые нужно откомпилировать/ассемблировать и затем слинковать, а также настройки этих процессов.
А вот в паскале (не уверен правда насчет mP, но он вообще кривой) модули связываются непосредственными ссылками — через кластер uses. Поэтому в Delphi (старых версий) проектом называли головной файл программы, а настройки хранили отдельно.
А вот в паскале (не уверен правда насчет mP, но он вообще кривой) модули связываются непосредственными ссылками — через кластер uses. Поэтому в Delphi (старых версий) проектом называли головной файл программы, а настройки хранили отдельно.
Объявление функции inline не избавит от вызова.
Чтобы сделать функцию по-настоящему inline всё её тело должно находиться в заголовочном файле. И ассемблерная вставка тут — единственный выход.
При этом надо понимать, что на AVR вызовы настолько легки, что в большинстве случаев инлайнить функцию было бы даже вредно.
Чтобы сделать функцию по-настоящему inline всё её тело должно находиться в заголовочном файле. И ассемблерная вставка тут — единственный выход.
При этом надо понимать, что на AVR вызовы настолько легки, что в большинстве случаев инлайнить функцию было бы даже вредно.
Инлайнить функци очень даже полезно. Когда компилятор встраивает функцию, то производит кучу оптимизаций, которые иначе были-бы невозможны. Маленькие функции, которые транслируются меньше, чем, скажем, в десяток комманд, и функции вызываемые всего один раз, встраивать всегда выгодно.
Или, например, что будет, если из обработчика прерывания вызвать функцию, которая не встроится? Правильно — все call-used регистры будут сохранены в стек.
И вообще решать встраивать функцию, или нет задача компиляторов, и они с ней вполне неплохо справляются. Главное им не мешать.
Кстати в последних версиях GCC (начиная с 4.5 с чем-то) появилась оптимазация во время линковки — LTO. И теперь он может заинлайнить даже ассемблерную функцию из отдельного модуля.
Или, например, что будет, если из обработчика прерывания вызвать функцию, которая не встроится? Правильно — все call-used регистры будут сохранены в стек.
И вообще решать встраивать функцию, или нет задача компиляторов, и они с ней вполне неплохо справляются. Главное им не мешать.
Кстати в последних версиях GCC (начиная с 4.5 с чем-то) появилась оптимазация во время линковки — LTO. И теперь он может заинлайнить даже ассемблерную функцию из отдельного модуля.
А вам посоветую
attribute(__force_inline__)
;). Тогда функция будет встроена, где бы ее тело не было.
Вот представь. Попросили компилятора C скомпилировать файл main.c. В ходе компиляции он добавил к исходнику все заголовочные файлы. В частности avr/io.h и asmroutine.h. Теперь ему не составит труда в выходном обьектном файл main.o инлайнить функции как ему нравится. При условии, что он знает, что в этих функциях содержится.
Но функция asm_func находиться в другом файле asmroutine.s. И вообще на другом языке написана. Компилятор C не из вредности не станет её инлайнить. А просто потому что для него эта функция — чёрный ящик расположенный вне досягаемости он него.
attribute(__force_inline__) просит компилятора инлайнить фунцию не взирая на мнение оптимизатора, если это возможно. В нашем случае это не возможно.
Но функция asm_func находиться в другом файле asmroutine.s. И вообще на другом языке написана. Компилятор C не из вредности не станет её инлайнить. А просто потому что для него эта функция — чёрный ящик расположенный вне досягаемости он него.
attribute(__force_inline__) просит компилятора инлайнить фунцию не взирая на мнение оптимизатора, если это возможно. В нашем случае это не возможно.
Мы говорили про С-функцию как противопоставление ассемблерной Объявление функции inline не избавит от вызова.
Чтобы сделать функцию по-настоящему inline всё её тело должно находиться в заголовочном файле. Если к ней добавить указанный атрибут, она будет заинлайнена, даже если занимает всю остальную программу, неважно где находится, вне зависимости от компилятора. И это не просьба, а требование :).
Чтобы сделать функцию по-настоящему inline всё её тело должно находиться в заголовочном файле. Если к ней добавить указанный атрибут, она будет заинлайнена, даже если занимает всю остальную программу, неважно где находится, вне зависимости от компилятора. И это не просьба, а требование :).
Ещё одно преимущество такого подхода перед ассемблерными вставками — кроссплатформенность. Если ассемблерная вставка правильно поймётся только avr-gcc, то объявление внешней функции закреплено стандартом C и для всех компиляторов всех архитектур можно писать один и тот-же исходник, заменяя только ассемблерный файл в проекте под конкретную архитектуру. Получится гораздо изящнее, чем километровые #ifdef. Атрибуты, кстати, тоже — штука весьма нестандартная.
Главное правильно понимать границы применимости. Если таким способом реализовать функцию __cli()… То очень скоро начнутся крики, что C — тормозное Г для ленивых.
Главное правильно понимать границы применимости. Если таким способом реализовать функцию __cli()… То очень скоро начнутся крики, что C — тормозное Г для ленивых.
Замечательная статья очень помогла!!!
Но прошу сильно не бить а если можно пояснить зачем нужна деректива __ASSEMBLER__
без неё у меня не запустилось!
Но прошу сильно не бить а если можно пояснить зачем нужна деректива __ASSEMBLER__
без неё у меня не запустилось!
Для включения заголовочных файлов надо файлам давать суффикс `.S` а не `.s`, тогда их будет можно компилять как `.c` файлы без явного указания, что надо сначала пропустить текст через C-препроцессор, а потом отдать as, `.s` означает что препроцессинг не требуется. Но это похоже важно только для тех кто пишет Makefile руками. Для AVR ещё возможно надо добавить обявление __SFR_OFFSET, если использовать libc заголовки.
А ничего, что Win-окружение нечувствительно (и слава Богу) к регистру имен? ;)
Да и тут разговор именно о самом заголовочнике. as никогда не поймет строчки типа void func(void), потому надо ее от него закрыть.
Да и тут разговор именно о самом заголовочнике. as никогда не поймет строчки типа void func(void), потому надо ее от него закрыть.
Я без этого дефайна __ASEMBLER__ пробовал и с расширением .s и .S при этом компилятор вываливал кучу одинаковых ошибок!
Не надо запутывания, я говорил о том, что gcc не запустит cpp для .s файла (как ведет себя порт для win не знаю) ели этого не указать явно.
$ cp entry.S entry.s
$ avr-gcc -c entry.S -mmcu=atmega16
$ avr-gcc -c entry.s -mmcu=atmega16
entry.s: Assembler messages:
entry.s:39: Error: constant value required
entry.s:41: Error: constant value required
$ avr-gcc -c -x assembler-with-cpp entry.s -mmcu=atmega16
$
Комментарии (35)
RSS свернуть / развернуть