Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: WinAVR-20081205 и тенденции
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Cредства разработки для МК > GNU/OpenSource средства разработки
ARV
Я знаю, что на этом форуме присутствуют люди, которые не только глубоко знают GCC и, в частности, WinAVR, но и участвуют в той или иной мере в совершенствовании этих продуктов. Поэтому в первую очередь мой вопрос к ним. Я понимаю, что мои вопросы почти риторические, но беспокойство от них у меня сильное...

Скачал WinAVR-20081205 (до этого был 20080610), провел тест на проекте для AT90CAN128. Было 18204 байт результрующего кода (WinAVR 20080610), стало 18350 байт. Исходник не менял, естественно, как и все параметры сборки проекта. Ранее при переходе с 20080411 так же было замечено увеличение итогового кода для проекта. То есть я делаю вывод, что от релиза к релизу GCC генерирует все менее и менее компактный код для AVR (про другие платформы не скажу, ибо не пользуюсь). Неоднократно были замечены всякие странные фокусы при сохранении регистров в прологе функций, некоторые "чудеса" оптимизации и "странности" в обработчиках прерываний (заглядываю на AVR Freaks)...

Что это? Целенаправленное стремление снизить качество GCC для AVR, косвенные следствия каких-то неведомых мне улучшений, непредвиденные последствия исправления багов или что-то еще? Казалось бы, добавилась поддержка новых микроконтроллкров - и на тебе: поддержка старых ухудшилась... Чего ждать в будущем? Единственный качественный бесплатный компилятор - неужели он обречен?!

Может быть, есть какие-то рекомендации по борьбе с такими особенностями? скажем, какие-то ключи компилятора или еще что-то, что позволит получить хотя бы прежний код с новым компилятором?

P.S. Пожалуйста, не надо ответов типа "хотите бесплатно - жрите, что дают!"... Ведь смысл бесплатного продукта не в том, чтобы унизить и заставить жрать помои...
_Pasha
Когда я "находил 10 отличий", дело было в более агрессивном разворачивании функций и небольших циклов в инлайны. Кроме того, сохранение регистров при входе в main() и их восстановление при выходе (т.е. никогда) для моих приложений не нужно. Это все отключаемые вещи. Видимо, набор настроек по умолчанию пока еще не устоялся.
ARV
Цитата(_Pasha @ Feb 12 2009, 10:54) *
Когда я "находил 10 отличий", дело было в более агрессивном разворачивании функций и небольших циклов в инлайны. Кроме того, сохранение регистров при входе в main() и их восстановление при выходе (т.е. никогда) для моих приложений не нужно. Это все отключаемые вещи. Видимо, набор настроек по умолчанию пока еще не устоялся.
с моей точки зрения, сохранение чего бы то ни было при входе в main() для AVR без ОС - это вообще никогда не нужно! логичнее было бы сделать опцию именно для включения сохранения контекста main() для всяких многозадачных ОС... все-таки для AVR это скорее исключение, чем правило. и, кстати, как пролог main() отключить?

а вот про циклы и инлайны - разговор другой... какие именно опции следует включать-выключать, чтобы избавиться от излишней агрессивности оптимизатора? или наоборот, перестроить его агрессивность в сторону компактности кода, а не скорости...
_Pasha
Цитата(ARV @ Feb 12 2009, 12:25) *
 и, кстати, как пролог main() отключить?

Я делаю так, как делать нельзя  
Код
int main (void) __attribute__((naked));
только потому, что main() никогда не содержит локальных переменных в своем теле. В более общем случае надо указывать атрибут OS_main  http://gcc.gnu.org/ml/gcc-patches/2008-04/msg00283.html 
Цитата
какие именно опции следует включать-выключать, чтобы избавиться от излишней агрессивности оптимизатора?


Была тема недавно, сейчас не могу найти:
1.Поиграться со значением штрафа --param inline-call-cost
2. Не брезговать квалификатором static для местных локальных функций
3. В особо мистических случаях - явно указывать __attribute__((noinline)) до выяснения причин излишней инлайнизации. 

4. -fno-unroll-loops это запретить разворачивать циклы
ARV
в конкретно моем случае я перебрал все опции оптимизации, затрагивающие циклы, включая и выключая их - в лучшем случае изменения размеров не обнаружено, в худшем, естественно, увеличение... -fcall-prologues помогает здорово, но ведь по отношению к предыдущей версии это как-то не честно smile.gif

static, атрибуты и прочее - это все понятно, речь идет только о том, как компилятор воспринимает уже имеющийся исходник. т.е. оптимизация методом правки исходника не затрагивается в данном контексте.

про OS_main уже встречал где-то здесь, однако, в официальный релиз этот патч не вошел? в документации нет ни слова...
ReAl
Вот что интересно - на небольшом проекте с scmRTOS (около 10килобайт) 20081205 выигрывает у 20071221, а этот выигрывает у более ранних - байт по 100-150. И при этом наблюдается небольшое уменьшение потребления стека.

А на старых добрых С-шных проектах - устойчивый рост.

Похоже, дело в изменениях в самом gcc - в процессоронезависимой части.

Кстати, avreal при компиляции 3.4.2 и 4.2.0 тоже растёт в объёме. Но там 100 килобайт или 110 - никого не волнует.
MrYuran
Цитата(ARV @ Feb 12 2009, 10:19) *
Было 18204 байт результрующего кода (WinAVR 20080610), стало 18350 байт.

Итого:
(18350-18204)/18204 = 0,8%
Стоит ли паниковать?
Как говорится, тяжело менять, ничего не меняя, нельзя приготовить омлет, не разбив яйца, на всех не угодишь и т.д.
В конце концов, никто не запрещает Вам пользоваться старой версией или собрать новую под свои личные потребности.
ARV
Цитата(ReAl @ Feb 12 2009, 12:45) *
Похоже, дело в изменениях в самом gcc - в процессоронезависимой части.
перенос основного акцента в сторону С++ ?
MrYuran
Цитата(ReAl @ Feb 12 2009, 12:45) *
Похоже, дело в изменениях в самом gcc - в процессоронезависимой части.

Кстати да, в отличие от MSPGCC, который навсегда остался в 3-й версии GCC, avr-gcc развивается синхронно с общим проектом. Что, по-моему, является большим плюсом.
demiurg_spb
Кстати, на младших контроллерах очень полезно включать такую вот опцию:
Код
CFLAGS += -mshort-calls
ARV
Цитата(MrYuran @ Feb 12 2009, 12:47) *
Итого:
(18350-18204)/18204 = 0,8%
Стоит ли паниковать?
Как говорится, тяжело менять, ничего не меняя, нельзя приготовить омлет, не разбив яйца, на всех не угодишь и т.д.
В конце концов, никто не запрещает Вам пользоваться старой версией или собрать новую под свои личные потребности.

если каждый раз для забивания гвоздя отливать для себя удобный молоток - это немножко отдает идиотизмом... а на счет паники - дело-то в другом! для attiny2313 или тем более attiny13 каждые 100 байт могут стать смертельными! какие 100 - даже десять... я устанавливаю новую версию с надеждой. что в ней меньше ошибок, багов и т.п., да и поддержку новых процессоров хотелось бы иметь под рукой... и не хочется при этом лишаться старых заделов или разводить кучу подборок компиляторов под разные цели... поймите меня правильно.

P.S. не надо про ассемблер для тини - я в курсе smile.gif
_Pasha
Я добавил последний AVR-libc  в 20071221. Держу на всякий случай, т.к. к нему вообще никаких претензий не было.



Цитата(demiurg_spb @ Feb 12 2009, 12:52) *
Кстати, на младших контроллерах очень полезно включать такую вот опцию:
Код
CFLAGS += -mshort-calls


СтоИт в аврстудии по умолчанию smile.gif
ARV
Цитата(demiurg_spb @ Feb 12 2009, 12:52) *
Кстати, на младших контроллерах очень полезно включать такую вот опцию:
Код
CFLAGS += -mshort-calls
а в чем смысл? в младших контроллерах все вызовы и так короткие (rcall)... -Wl,-relax помогает не хуже smile.gif
demiurg_spb
Цитата(ARV @ Feb 12 2009, 13:00) *
а в чем смысл? в младших контроллерах все вызовы и так короткие (rcall)... -Wl,-relax помогает не хуже smile.gif
Хорошо. Перефразирую. На контроллерах где по-умолчанию не rcall бывает полезно и причём весьма. Вот только сейчас попробовал на мега16 размер программы уменьшился на 120 байт.
Прикол. А если одновременно и -mshort-calls и -Wl,-relax, то ld падает с сообщением "ld.exe application error"
ARV
на моем проекте -mshort-calls вообще не сработала - кучу ошибок при линковке выдало... а вот -relax действует, причем более аккуратно: на маленьких МК не вредит, на больших - помогает, причем существенно (и по скорости тоже выигрыш будет!)
_Pasha
Цитата(ARV @ Feb 12 2009, 13:24) *
на моем проекте -mshort-calls вообще не сработала

Потому что должен быть один модуль с длинными вызовами и большинство с включенным -mshort-calls , но это неудобно, конечно.
alx2
Цитата(ARV @ Feb 12 2009, 12:19) *
Было 18204 байт результрующего кода (WinAVR 20080610), стало 18350 байт.
Хочу заметить, что размер кода - не единственно возможный критерий качества оптимизации. Может может там с оптимизацией по скорости выполнения стало лучше?
Цитата(ARV @ Feb 12 2009, 13:25) *
с моей точки зрения, сохранение чего бы то ни было при входе в main() для AVR без ОС - это вообще никогда не нужно!
логичнее было бы сделать опцию именно для включения сохранения контекста main()
Не согласен. Во-первых, для указания компилятору, что из функции (любой, а не только main!) никогда не выполняется возврат, существует специальный атрибут. во-вторых, сохранение регистров, которые потом не будут нужны, не нарушает работу программы, а лишь делает код не таким оптимальным, как мог бы. Обратное же (несохранение регистров, которые впоследствии нужны) нарушит работу программы. Поэтому дефолтным поведением должно быть именно сохранение регистров (если специально не указано, что функция noreturn).
Цитата(ARV @ Feb 12 2009, 14:33) *
в конкретно моем случае я перебрал все опции оптимизации, затрагивающие циклы, включая и выключая их - в лучшем случае изменения размеров не обнаружено
Предлагаю гвоворить более конкретно: вот пример кода, вот результат компиляции gcc такой-то версии с такими-то опциями, а вот - другой версии. Или с другими опциями. Вот анализ арботы оптимизатора. А так - совершенно беспредметный разговор получается...
demiurg_spb
У меня странная проблема на большом проекте 60КБ и mega1281 опция -Wl,-relax приводит к краху линковки...
avr-gcc (WinAVR 20081205) 4.3.2
Код
#---------------- Linker Options ----------------
#  -Wl,...:     tell GCC to pass this to linker.
#    -Map:      create map file
#    --cref:    add cross reference to  map file
LDFLAGS =
LDFLAGS += -Wl,-Map=$(TARGET).map,--cref
LDFLAGS += -Wl,-relax
LDFLAGS += $(EXTMEMOPTS)
LDFLAGS += $(patsubst %,-L%,$(EXTRALIBDIRS))
LDFLAGS += $(PRINTF_LIB) $(SCANF_LIB) $(MATH_LIB)

Linking: ../bin/Paragraph4/Paragraph.elf
avr-gcc -mmcu=atmega1281 -I. -gdwarf-2 -DF_CPU=14745600UL -Os
-funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes
-Wa,-adhlns=..............
--output ../bin/Paragraph4/Paragraph.elf -Wl,-Map=../bin/Paragraph4/Paragraph.map,--cref -Wl,-relax
-Wl,-u,vfprintf -lprintf_flt -lm
make: *** [../bin/Paragraph4/Paragraph.elf] Error 1

Проверил ещё на нескольких проектах при добавлении LDFLAGS += -Wl,--relax на некоторых из них происходит крах линкера
(см. картинку во вложенном файле).

Что это? И как быть?
ARV
1. в настоящее время меня интересует качество оптимизации именно по размеру, т.е. -Os. до сего момента никогда -O3 не давало в итоге меньший размер кода, чем -Os. в частности, упомянутый проект выдал 30254 байт при -O3.

2. если бы main() не стояла особняком среди прочих функций Си-программы, я был бы готов с вами согласиться. но это особая функция, потому для нее вполне можно по умолчанию использовать особые режимы генерации кода. тем более для AVR, для которых наличие ОС, вызывающей main(), все же скорее исключение, чем правило.

3. к сожалению, я установил новый WinAVR поверх старого, и пока что не спешу делать откат - возможно, все решится меньшей кровью. потому могу сравнивать только то, о чем уже сказал: есть результат компиляции проекта с опцией -Os без других специальных мер старым WinAVR, и есть новый WinAVR, дающий с теми же опциями проекта худший результат. теперь идет поиск опций, позволяющих добиться прежних результатов с новым компилятором.

пока что наибольший эффект дает применение --relax для линкера и -fcall-prologues для компилятора. однако, я очень сомневаюсь, что эти же опции помогут, например, для atmega8 (--relax уж точно не поможет)... а хочется найти универсальное решение проблемы.
aesok
Цитата(ARV @ Feb 12 2009, 10:19) *
Скачал WinAVR-20081205 (до этого был 20080610),
провел тест на проекте для AT90CAN128. Было 18204 байт результрующего кода (WinAVR 20080610),
стало 18350 байт.



Проверте, как влияет на размер кода -fno-split-wide-types.

Анатолий.
ARV
Цитата(aesok @ Feb 12 2009, 14:17) *
Проверте, как влияет на размер кода -fno-split-wide-types.

Анатолий.
влияет: 18278 байт стало. но до стартовых 18204 еще есть резерв smile.gif этот параметр действует одинаково на всех контроллерах?

demiurg_spb, подправьте свой пост, пожалуйста: ну невозможно пользоваться форумом - все разъехалось вширь донельзя!
demiurg_spb
Цитата(ARV @ Feb 12 2009, 14:27) *
влияет: 18278 байт стало. но до стартовых 18204 еще есть резерв smile.gif этот параметр действует одинаково на всех контроллерах?

demiurg_spb, подправьте свой пост, пожалуйста: ну невозможно пользоваться форумом - все разъехалось вширь донельзя!

Поправил. У меня в фаерфоксе ничего не разъезжалось..
И у меня на 102 байта уменьшилось на 60КБ проекте.
А что этот параметр означает-то? В доке тишина (в WinAVR\doc нет файлов, содержащих этот параметр)...
aesok
Цитата(ARV @ Feb 12 2009, 14:27) *
влияет: 18278 байт стало. но до стартовых 18204 еще есть резерв smile.gif этот параметр действует одинаково на всех контроллерах?


да.

В этом проекте используются функции для работы с EEPROM из avr-libc?

Цитата(demiurg_spb @ Feb 12 2009, 14:38) *
А что этот параметр означает-то? В доке тишина (в WinAVR\doc нет файлов, содержащих этот параметр)...


это паремет GCC и задокументираван в GCC 4.3.3 Manual.

Цитата
-fsplit-wide-types
When using a type that occupies multiple registers, such as long long on a 32-bit system, split the registers apart and allocate them independently. This normally generates better code for those types, but may make debugging more difficult.
Enabled at levels -O, -O2, -O3, -Os.
_Pasha
Цитата(demiurg_spb @ Feb 12 2009, 14:38) *
 В доке тишина (в WinAVR\doc нет файлов, содержащих этот параметр)...

http://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
Упс, опередили.
Т.е. насколько я понял, по умолчанию включено, и это добавляет количество команд mov  вместо movw
aesok
Цитата(_Pasha @ Feb 12 2009, 14:45) *
Т.е. насколько я понял, по умолчанию включено, и это добавляет количество команд mov  вместо movw


Да. Как сторонний эффект.
ARV
Цитата(aesok @ Feb 12 2009, 14:38) *
да.

В этом проекте используются функции для работы с EEPROM из avr-libc?
eeprom.h подключается, но его функции пока не используются.

чем еще можно усушить размер? wink.gif
demiurg_spb
На этой странице не встречается "fno-split-wide-types"
Цитата(_Pasha @ Feb 12 2009, 14:45) *
Ступил... Нашёл что надо. Спасибо.
Есть дока по "fsplit-wide-types". Теперь совсем неясно стало.
Если разделять по несвязанным регистрам то код должен уменьшаться а тут мы запрещаем split и он уменьшается. Непонятно....
А на мелком проекте 5КБ код вырос на 4 байтаsmile.gif
aesok
Цитата(ARV @ Feb 12 2009, 14:53) *
eeprom.h подключается, но его функции пока не используются.

чем еще можно усушить размер? wink.gif


С --param inline-call-cost=4,5,6 игрались?
ARV
--param -inline-call-cost

=0.........17902
=1,2,3.....17820
=4,5.......17988
=6,7,8.....18002
=9..........18036
=10,11.....18292
=12.........исходные 18350
при дальнейшем росте значения идет так же рост размера smile.gif

при этом никакие другие параметры не использовал! комбинация -mcall-prologues -fno-split-wide-types --param -inline-call-cost=1 -Wl,-relax дает 17112 байт! более чем на килобайт меньше отправной точки! (правда, в железе не проверил - работает оно или нет?) :D

это предел? еще хачу! smile.gif

P.S. интересно, а со старым компилятором сколько бы вышло? wink.gif проверю обязательно!
_Pasha
-fsplit-wide-types - совершенно очевидно, что в качестве умолчания оно не подходит.

Остальное - так и думал, инлайны

2ARV: -fno-unroll-loops стоИт?
ARV
Цитата(_Pasha @ Feb 12 2009, 15:25) *
2ARV: -fno-unroll-loops стоИт?
раньше я уже писал: все *loops* опции я пробовал... но проверил еще раз: в комплекте с предыдущим набором 0 байт изменения.
aesok
Цитата(ARV @ Feb 12 2009, 15:17) *
при этом никакие другие параметры не использовал! комбинация -mcall-prologues -fno-split-wide-types --param -inline-call-cost=1 -Wl,-relax дает 17112 байт! более чем на килобайт меньше отправной точки! (правда, в железе не проверил - работает оно или нет?) :D


По моим наблюдениям GCC 4.3 лучше чем 4.2 процента на 3. GCC 4.4 будет посередине между ними.

Про -fsplit-wide-types постораюсь вечером расказать.

Анатолий.
Rst7
Кстати, для тех, кто пользуется максимальной оптимизацией по скорости, рекомендую отключить векторизацию. Не так давно обсуждали этот вопрос.
Сергей Борщ
Цитата(alx2 @ Feb 12 2009, 12:32) *
Поэтому дефолтным поведением должно быть именно сохранение регистров (если специально не указано, что функция noreturn).
Буквально сегодня добавил в mspgcc проверку, есть ли выход из функции. Если выхода нет - не сохраняются регистры и не генерится эпилог. Т.е. этакий автоматический __attribute__((noreturn)). Судя по листингу - получается. Даже для main(). Не знаю насколько все изменилось в gcc 4.x по сравнению с 3.х, но можно предположить, что возможность такой проверки осталась - ведь компилятор как-то умеет генерить предупреждения об отсутствии return в функции, возвращающей значения и о наличии точек выхода в функции с атрибутом noreturn. Остается дописать по одному условию в функции генерации пролога и эпилога.

Цитата(ARV @ Feb 12 2009, 14:17) *
это предел? еще хачу! smile.gif
smile.gif http://electronix.ru/forum/index.php?s=&am...st&p=393663
ReAl
Цитата(Сергей Борщ @ Feb 12 2009, 17:27) *
Т.е. этакий автоматический __attribute__((noreturn)).
Автоматический это хорошо, конечно, но пренебрагать явным не стоит - он предназачен не толко для компиляции данной функции, но и для вызывающих её.
Простейший пример (в жизни врядли такой будет, но это первое, что взбрело в голову для - форсирования размещения аргументов на стеке).

Код
__attribute__((__noreturn__)) void foo(char *p, ...);

void moo(char *p, ...);

char cc[10];

void baa(unsigned char a)
{
    if(a) foo(cc, 1313);
    moo(cc, 169);
}

Код
    .type    kwa, @function
kwa:
    ldi r18,lo8(cc)
    ldi r19,hi8(cc)
    tst r24
    breq .L2
    ldi r24,lo8(1313)
    ldi r25,hi8(1313)
    push r25
    push r24
    push r19
    push r18
    rcall foo     ; -----------
.L2:
    ldi r24,lo8(169)
    ldi r25,hi8(169)
    push r25
    push r24
    push r19
    push r18
    rcall moo
    pop __tmp_reg__
    pop __tmp_reg__
    pop __tmp_reg__
    pop __tmp_reg__
    ret
из foo "не должно быть" возврата, поэтому нет смысла после неё восстанавливать стек (собственно, можно было и rjmp нарисовать...)

Аналогично - если у вызывающей функции набралось что-то в caller-save-регистрах, то перед вызовом noreturn-функции она не будет эти регистры сохранять.
ARV
а вот еще вопрос (не совсем в тему, но касающийся оптимизации размера): каким образом убрать из кода таблицу векторов прерываний, если прерывания не используются, чтобы "полезный" код начинался с адреса 0х000? актуально для tiny-проектов.
MrYuran
Цитата(ARV @ Feb 13 2009, 10:38) *
каким образом убрать из кода таблицу векторов прерываний, если прерывания не используются, чтобы "полезный" код начинался с адреса 0х000? актуально для tiny-проектов.

Наверно, подрихтовать скрипт линкера, выкинуть секцию векторов и растянуть кодовую
aesok
Цитата(ARV @ Feb 13 2009, 10:38) *
а вот еще вопрос (не совсем в тему, но касающийся оптимизации размера): каким образом убрать из кода таблицу векторов прерываний, если прерывания не используются, чтобы "полезный" код начинался с адреса 0х000? актуально для tiny-проектов.


Я так ни когда не делал, но можете попробовать этот вариант. Не знаю получиться ли так в WinAVR.

1. качаете исходники avr-libc и распаковываете их;

2. находите заголовычный файл для вашего контроллера avr-libc\include\avr\io<нужный_контроллер>.h, и правите макрос _VECTORS_SIZE, в соттветствии с необходимым количеством векторов. Учтите _VECTORS_SIZE размер таблицы векторов в байтах;

3. компилируете avr-libc;
Код
mkdir build
cd build
../avr-libc/configure -v --host=avr --prefix=/tools/
make


make install - не делаете;

4. ищете statr-up файл для вашего контроллера build\avr\lib\avr<2,3,....51>\<нужный_контроллер>\gcrt1.o

5. линкуете этот gcrt1.o в ваш проект вместо стандартного.

Анатолий.
ARV
увы, такой сложный и, опять-же, индивидуально затачиваемый под конкретный контроллер вариант явно не подходит... sad.gif
aesok
Цитата(ARV @ Feb 13 2009, 11:27) *
увы, такой сложный и, опять-же, индивидуально затачиваемый под конкретный контроллер вариант явно не подходит... sad.gif


Пока только так, изменить таблицу векторов можно только в стртап файле.


Анатолий.
ARV
Цитата(aesok @ Feb 13 2009, 11:31) *
Пока только так...
слово "пока" зарождает надежду...
а нельзя ли принципиально удалять таблицу векторов, если имеется опция -mno-interrupts ? по-моему, очень логично...
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.