Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: gcc для AVR
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > MCS51, AVR, PIC, STM8, 8bit
609
Здравствуйте. Я программирую на языке C для микроконтроллеров AVR при помощи компилятора gcc. Нашел такую интересную особенность. В приведенном ниже коде программа занимает место порядка 200 байт. Если раскомментировать закомментированную строку с объявлением функции delay_ms, программа резко полнеет до 350 байт. Никак не могу разобраться, почему. Не могли бы вы мне помочь. Упорное чтение Кернигана и Ричи не помогает. Куда гуглить просто не знаю. Заранее спасибо за ответ.

Код
#define WDR        asm("wdr")
//void delay_ms(int time);

void main(void)
{
    delay_ms(1);
    delay_ms(1);
}


void delay_ms(int time)
{
    volatile int Timer, Timer1;

    for (Timer=0; Timer<time; Timer++)
        for (Timer1=0; Timer1<51; Timer1++)
            WDR;
}



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

Код
-Wall
-gdwarf-2
-Os
-std=gnu99
-funsigned-char
-funsigned-bitfields
-fpack-struct
-fshort-enums
777777
Цитата(609 @ Oct 5 2010, 10:41) *
Здравствуйте. Я программирую на языке C для микроконтроллеров AVR при помощи компилятора gcc. Нашел такую интересную особенность. В приведенном ниже коде программа занимает место порядка 200 байт. Если раскомментировать закомментированную строку с объявлением функции delay_ms, программа резко полнеет до 350 байт. Никак не могу разобраться, почему. Не могли бы вы мне помочь. Упорное чтение Кернигана и Ричи не помогает. Куда гуглить просто не знаю.

А какой варнинг он выдает когда строка закомментирована?
609
Цитата(777777 @ Oct 5 2010, 10:56) *
А какой варнинг он выдает когда строка закомментирована?

Build succeeded with 0 Warnings...
777777
Цитата(609 @ Oct 5 2010, 10:59) *
Build succeeded with 0 Warnings...

Стало жутко интересно. Сделал проект и получил 230 байт в обоих случаях, только при закомментированном прототипе выдаются варнинги:

implicit declaration of function 'delay_ms'
conflicting types of function 'delay_ms'

С этим, надеюсь, понятно? При неявном использовании функции они считается возвращающей int, а когда компилятор дошел до ее определения, она оказалась void
609
Компилирую при помощи WinAVR-20081205, прикрученном к AVR Studio 4.16.

Цитата
При неявном использовании функции они считается возвращающей int, а когда компилятор дошел до ее определения, она оказалась void


Про это я читал. Пробовал вместо void ставить int, но ситуация не меняется.

Если точно, то с закомментированной строчкой получается 220 байт, а если я ее раскомментирую, то 364. Не могли бы Вы сказать, какой конкретно версией gcc пользуетесь.
777777
Цитата(609 @ Oct 5 2010, 11:24) *
Компилирую при помощи WinAVR-20081205, прикрученном к AVR Studio 4.16.

Про это я читал. Пробовал вместо void ставить int, но ситуация не меняется.

Если точно, то с закомментированной строчкой получается 220 байт, а если я ее раскомментирую, то 364. Не могли бы Вы сказать, какой конкретно версией gcc пользуетесь.


4.18 b700 WinAVR 20100110
Еще имеет значение чип, я выбрал ATmega168
dimka76
Не понятно для чего вы используете volatile для локальной переменной. Если без volatile, то тогда компилятор скорее всего разместит ее в регистре.

Вот еще один путь снижения объема кода

Код
void delay_ms(int time)
{
    int Timer1 = 51;

    while(time--)
       while(Timer1--)
           WDR;
}


Здесь переменная Timer вообще не нужна.
Операция сравнения с нулем выполняется быстрее, чем с каким-либо другим числом.
MrYuran
Цитата(dimka76 @ Oct 5 2010, 11:53) *
Код
void delay_ms(int time)
{
    int Timer1 = 51;

    while(time--)
       while(Timer1--)
           WDR;
}

Не знаю как себя поведёт avr-gcc, но в аннотации к mspgcc про такую конструкцию написано, что при удачном стечении обстоятельств ждать будем вечно...
609
Цитата(777777 @ Oct 5 2010, 11:30) *
4.18 b700 WinAVR 20100110
Еще имеет значение чип, я выбрал ATmega168

Спасибо! Завтра попробую скачать эту версию, посмотрю что получится.




Цитата(dimka76 @ Oct 5 2010, 11:53) *
Не понятно для чего вы используете volatile для локальной переменной. Если без volatile, то тогда компилятор скорее всего разместит ее в регистре.


Если не использовать volatile, то компилятор может оптимизировать задержку, т.е. просто выполнить конечный результат функции.

Цитата(dimka76 @ Oct 5 2010, 11:53) *
Вот еще один путь снижения объема кода


Вопрос не в том как сделать код задержки меньше, а почему при объявлении функции код увеличивается?
MrYuran
Цитата(609 @ Oct 5 2010, 12:26) *
Вопрос не в том как сделать код задержки меньше, а почему при объявлении функции код увеличивается?

Посмотрите листинги для каждого случая, возможно, станет более понятно.
Сергей Борщ
Цитата(609 @ Oct 5 2010, 11:26) *
Если не использовать volatile, то компилятор может оптимизировать задержку, т.е. просто выполнить конечный результат функции.
А если напишете #define WDR asm volatile ("wdr"), то не сможет.
dimka76
Цитата(MrYuran @ Oct 5 2010, 12:14) *
Не знаю как себя поведёт avr-gcc, но в аннотации к mspgcc про такую конструкцию написано, что при удачном стечении обстоятельств ждать будем вечно...


А там случайно эти обстоятельства не конкретизируются?
zhevak
Цитата(609 @ Oct 5 2010, 14:26) *
Вопрос не в том как сделать код задержки меньше, а почему при объявлении функции код увеличивается?

Смею предположить, что оптимизирующий компилятор в одном случае может вставлять вызов функции, а в другом -- осуществлять in-line подстановку тела этой функции в месте ее вызова. Попробуйте отключить оптимизацию и, как абсолютно правильно сказал MrYuran, посмотрите листинги. Вы не только ответите на свой вопрос, но и существенно придвинетесь в сторону профи.
MrYuran
Цитата(dimka76 @ Oct 5 2010, 14:39) *
А там случайно эти обстоятельства не конкретизируются?


Цитата
18. If you execute
while ((long) a & 0x80000l);
the program will hang, unless ’a’ is declared volatile. So, do it!


Цитата
19. Delay loops are very sophisticated routines. Normally, users do something like:
int i = 1234;
while (i--);
or
int i;
for (i = 0; i < 1234; i++);
NEITHER WILL WORK AS YOU EXPECT when optimisation is switched on!!! The optimizer will detect
dead code in both examples and will eliminate it. It might even eliminate the loop completely. Adding the
volatile attribute to the definition of ’i’ might help, but don’t count on it if ’i’ is a local variable. The
compiler can still detect the calculations are wasteful, and eliminate them.
Regardless of these optimisation issues, this type of delay loop is poor programming style - you have no
idea how long or short a delay it might produce (although there is an obvious minimum bound!). It would be
better, and more reliable to define something like:
static void __inline__ brief_pause(register unsigned int n)
{
__asm__ __volatile__ (
"1: \n"
" dec %[n] \n"
" jne 1b \n"
: [n] "+r"(n));
}
and call this routine where needed. This is simple, compact, and predictable.

Хотя, я пробовал - иногда прокатывало...
Но тут уж как повезёт.

Основной смысл последней цитаты - задержку лучше определить самому (с точностью до такта), чем отдавать на откуп компилятору
Dx!
А чем макросы _delay_ms() и _delay_us() что идут с WinAVR плохи?
XVR
Цитата(MrYuran @ Oct 5 2010, 15:10) *
Цитата
Не знаю как себя поведёт avr-gcc, но в аннотации к mspgcc про такую конструкцию написано, что при удачном стечении обстоятельств ждать будем вечно...
Цитата
А там случайно эти обстоятельства не конкретизируются?

...
Вывод неправильный - не 'ждать вечно', а 'не ждать вообще'

Цитата
Основной смысл последней цитаты - задержку лучше определить самому (с точностью до такта), чем отдавать на откуп компилятору
Это да, тут поспорить трудно (хотя и можно при большом желании и абсолютном взаимопонимании с компилятором) rolleyes.gif
609
Цитата(609 @ Oct 5 2010, 12:26) *
4.18 b700 WinAVR 20100110
Еще имеет значение чип, я выбрал ATmega168

Поставил эту версию, проблема исчезла, и уменьшился код: при компиляции в обоих случаях выдает 168 байт.


Цитата(Сергей Борщ @ Oct 5 2010, 14:23) *
А если напишете #define WDR asm volatile ("wdr"), то не сможет.

Спасибо за подсказку, так действительно получается более оптимально.

Цитата(zhevak @ Oct 5 2010, 14:50) *
Смею предположить, что оптимизирующий компилятор в одном случае может вставлять вызов функции, а в другом -- осуществлять in-line подстановку тела этой функции в месте ее вызова. Попробуйте отключить оптимизацию и, как абсолютно правильно сказал MrYuran, посмотрите листинги. Вы не только ответите на свой вопрос, но и существенно придвинетесь в сторону профи.

Листинг смотрел, там действительно в одном случае функция вызывается, а в другом подставляется.
Только я так и не понял, почему компилятор подставляет 100 байт тела функции, вместо того чтобы вызвать функцию 3 байтами.




MrYuran
Цитата(609 @ Oct 6 2010, 14:03) *
Листинг смотрел, там действительно в одном случае функция вызывается, а в другом подставляется.
Только я так и не понял, почему компилятор подставляет 100 байт тела функции, вместо того чтобы вызвать функцию 3 байтами.

Это зависит от того, какие остальные оптимизирующие ключи выставлены и от того, сколько раз эта функция вызывается.
Если один, то зачем тратить 3 байта на её вызов?
609
Цитата(MrYuran @ Oct 6 2010, 14:37) *
Если один, то зачем тратить 3 байта на её вызов?

В примере задержка вызывается два раза, и код подставляется дважды. Получается, вместо того чтобы потратить 6 байт на вызов, и 100 байт на тело функции, компилятор подставляет 200 байт.


MrYuran
Вот ключи для более тонкой настройки инлайна и анролла циклов:
Код
CFLAGS += --param inline-call-cost=10
CFLAGS += --param max-unroll-times=100

#CFLAGS += -ftree-loop-ivcanon
#CFLAGS += -mcall-prologues
CFLAGS += -finline-small-functions
CFLAGS += -fearly-inlining
CFLAGS += -finline-limit=100
CFLAGS += -fno-unit-at-a-time

#CFLAGS += -fno-inline-small-functions
#CFLAGS += -fno-inline-functions
#CFLAGS += -fno-unroll-loops
#CFLAGS += -fno-unroll-all-loops

Может, ещё другие есть.
609
Цитата(MrYuran @ Oct 6 2010, 15:42) *
Вот ключи для более тонкой настройки инлайна и анролла циклов:
Код
CFLAGS += --param inline-call-cost=10
CFLAGS += --param max-unroll-times=100

#CFLAGS += -ftree-loop-ivcanon
#CFLAGS += -mcall-prologues
CFLAGS += -finline-small-functions
CFLAGS += -fearly-inlining
CFLAGS += -finline-limit=100
CFLAGS += -fno-unit-at-a-time

#CFLAGS += -fno-inline-small-functions
#CFLAGS += -fno-inline-functions
#CFLAGS += -fno-unroll-loops
#CFLAGS += -fno-unroll-all-loops

Может, ещё другие есть.


Спасибо!
Попробовал первый ключ из Вашего списка, все стало компилироваться одинаково.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.