Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Организация стека в компиляторе WinAVR
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > MCS51, AVR, PIC, STM8, 8bit
Giekelberri
Добрый день всем!

Дано:
Компилятор WinAVR-20090313 в связке со средой разработки AVRStudio 4.
МК - ATMega1280, но это не важно.

Пусть есть программа на ЯВУ Си:

int SumEvaluate(int a, int b, int c)
{
int sum = 0;
sum = a + b + c;
return sum;

}
void main(void)
{
int i;
char ch1;
char ch2;
i = evaluate(5,4,10);
printf("%d",i);

}

Известно, что стек начинается с конца SRAM и растет в сторону уменьшения адреса.
Возникает вопрос где сохраняются переменные i, ch1, ch2? В соответствии с документацией на WinAVR, они могут сохраниться в двух местах:
Call-used registers (r18-r27)
или
Call-saved ergisters (r2-r17)

Если бы переменных было много (ch3, ch4, ch5, ch6, ...), места в регистрах РОН не хватило бы и тогда был бы задействован стек. В этом случае на вершину стека указывал бы указатель стека, хранящийся в Z-регистре или Y-регистре соответственно. А каким образом в этом случае известна глубина стека?

Повторю вопрос: а где на самом деле сохраняются эти переменные в Call-used registers или Call-saved ergisters? И как становиться известна глубина стека когда для размещения переменных не хватает регистров общего назначения ?

И второй вопрос, аналогичен первому - что происходит при вызове функции? Счетчик PC помещается в стек, это понятно. а куда помещаются локальные переменные для данной функции? Сначала в регистры общего назначения или сразу в стек? Куда помещается значение, возвращаемое функцией?
Какое максимальное количество параметров, которое возможно передать функции? Непанятна sm.gif
Сергей Борщ
QUOTE (Giekelberri @ Jan 28 2011, 15:41) *
Возникает вопрос где сохраняются переменные i, ch1, ch2?
Поскольку переменные локальные, они создаются на стеке. Однако, если в регистрах есть свободное место - оптимизатор выкинет сохранение на стек и восстановление, таким образом переменная будет жить в регистре. До тех пор, пока вы не попытаетесь взять и передать куда-то адрес переменной - в этом случае компилятор положит ее на стек.
QUOTE (Giekelberri @ Jan 28 2011, 15:41) *
Если бы переменных было много (ch3, ch4, ch5, ch6, ...), места в регистрах РОН не хватило бы и тогда был бы задействован стек. В этом случае на вершину стека указывал бы указатель стека, хранящийся в Z-регистре или Y-регистре соответственно.
Это у ИАРа указатель на стек данных хранится в Y. GCC использует один стек и на него указывает регистр спец. назначения (SFR) SP.

QUOTE (Giekelberri @ Jan 28 2011, 15:41) *
А каким образом в этом случае известна глубина стека?
Неизвестна. А зачем?
QUOTE (Giekelberri @ Jan 28 2011, 15:41) *
Повторю вопрос: а где на самом деле сохраняются эти переменные в Call-used registers или Call-saved ergisters?
Могут быть и в тех и в других. На время вызова функций переменные из call-used регистров временно выгружаются на стек, после возврата - вынимаются из стека обратно в регистры.
QUOTE (Giekelberri @ Jan 28 2011, 15:41) *
И как становиться известна глубина стека когда для размещения переменных не хватает регистров общего назначения ?
Компилятору она неизвестна.
QUOTE (Giekelberri @ Jan 28 2011, 15:41) *
Счетчик PC помещается в стек, это понятно. а куда помещаются локальные переменные для данной функции? Сначала в регистры общего назначения или сразу в стек?
Вы имеете ввиду параметры? В зависимости от типа и количества - могут передаваться или в регистрах, или на стеке или и там и там.
QUOTE (Giekelberri @ Jan 28 2011, 15:41) *
Куда помещается значение, возвращаемое функцией?
Аналогично. Какие-то типы возвращаются в регистрах, другие - через стек.
QUOTE (Giekelberri @ Jan 28 2011, 15:41) *
Какое максимальное количество параметров, которое возможно передать функции?
По стандарту их там столько, что вы устанете набирать исходник sm.gif
_Pasha
Цитата(Giekelberri @ Jan 28 2011, 16:41) *
Дано:
Компилятор WinAVR-20090313

Перейдите на крайнюю версию, она лучше.

Цитата
Если бы переменных было много
Непанятна sm.gif

Отключите оптимизацию - все поймете сразу, либо устройте веселый микс из локальных переменных и локальных volatile переменных, т.к. последние будут на стеке.
Возврат значений и передача параметров - есть порядок назначения регистров, начинается с R24:R25 а дальше не помню sm.gif и то в командной строке этот порядок можно изменить, - при написании функций на ассмеблере так и стараюсь дальше R24:R25 не заплывать. Баюс sm.gif
alexeyv
Цитата
Возникает вопрос где сохраняются переменные i, ch1, ch2

i - резервируется место в стеке
ch1, ch2 - скорее всего выкинутся компилятором после оптимизации т.к. не используются

Цитата
Куда помещается значение, возвращаемое функцией?

Return values: 8-bit in r24 (not r25!), 16-bit in r25:r24, up to 32 bits in r22-r25, up to 64 bits in r18-r25.
Перевода надеюсь не надо.

Цитата
Call-used registers (r18-r27)

Call-used registers (r18-r27, r30-r31):
May be allocated by gcc for local data. You may use them freely in assembler subroutines. Calling C subroutines can clobber any of them - the caller is responsible for saving and restoring.
Могут быть распределены gcc для локальных переменных. Вы МОЖЕТЕ их использовать в ассемблерных подпрограммах. Вызываемая Си-подпрограмма МОЖЕТ ЗАТЕРЕТЬ любое содержимое - вызывающая программа ответственна за сохранение и воссстановление.

Цитата
Call-saved registers (r2-r17)

Call-saved registers (r2-r17, r28-r29):
May be allocated by gcc for local data. Calling C subroutines leaves them unchanged. Assembler subroutines are responsible for saving and restoring these registers, if changed.
Могут быть распределены gcc для локальных переменных. Вызываемая Си-подпрограмма ДОЛЖНА их вернуть НЕИЗМЕНЕННЫМИ. Ассемблерная подпрограмма ответственна за сохранение и восстановление, если их изменяет.

Цитата
Повторю вопрос: а где на самом деле сохраняются эти переменные в Call-used registers или Call-saved ergisters?

И те и другие сохраняются в стеке, только Call-used registers сохраняте вызывающая подпрограмма, а Call-saved ergisters ДОЛЖНА сохранять вызываемая подпрограмма (если, тем более, она написана на асме)

Если хотите узнать как компилятор СИ (WinAVR) работает со стеком, то посмотрите оттранслированный листинг любой функции. В кратце при вызове функции это происходит так:
1. Вызывающая функция сохраняет в стеке (если надо) Call-used registers, и параметры некоторых функций (например printf)
2. Процессор аппаратно (в команде CALL или FCALL) сохраняет в стеке адресс возврата
3. Вызываемая программа сохраняет в стеке используемые Call-saved registers, в том числе и указатель стека (r28-r29)=(Y).
4. Вызываемая программа играется со стеком как душе угодно, в том числе размещает там свои локальные переменные.
5. Вызываемая программа восстанавливает Call-saved registers из стека
6. Процеесор аппаратно (в команде RET или FRET), воостанавливает PC из стека
7. Вызывающая функция восстанавливает из стека Сall-used registers, и также очищает стек от параметров некоторых функций (например printf)

Если Вы пишите на Си, то замарачиваться по этому поводу не надо. Если только на Асме, то также замарачиваться не надо. И если только миксуете код - то надо знать правила вызовов Си-функций
demiurg_spb
Цитата(alexeyv @ Jan 31 2011, 07:56) *
3. Вызываемая программа сохраняет в стеке используемые Call-saved registers, в том числе и указатель стека (r28-r29)=(Y).
Не про avr-gcc (WinAVR). Не использует он программный стек вовсе, только аппаратный (SPL,SPH).
alexeyv
Цитата
Не про avr-gcc (WinAVR). Не использует он программный стек вовсе, только аппаратный (SPL,SPH).

А как же фраза из WinAVR-хелпа:
r29:r28 (Y pointer) is used as a frame pointer (points to local data on stack) if necessary.

Во-вторых, я описал общую работу со стеком при вызове функции на Си. О реализации данного механизма в WinAVR можно говорить долго, и я не считаю нужным начинать спор из-за этого. Топик-стартер задал конкретный вопрос - я на него постарался ответить
demiurg_spb
Цитата(alexeyv @ Jan 31 2011, 11:44) *
А как же фраза из WinAVR-хелпа:
r29:r28 (Y pointer) is used as a frame pointer (points to local data on stack) if necessary.

Вы её не так понимаете. Указатель стека в avr-gcc один - аппаратный. И точка.

Цитата
Во-вторых, я описал общую работу со стеком при вызове функции на Си. О реализации данного механизма в WinAVR можно говорить долго, и я не считаю нужным начинать спор из-за этого. Топик-стартер задал конкретный вопрос - я на него постарался ответить
Прочитайте название топика внимательно. Вы сами себе противоречите. Его не интересовали общие принципы. Вопрос конкретно сформулирован.
alexeyv
Цитата
Вы её не так понимаете. Указатель стека в avr-gcc один - аппаратный. И точка.


А как правильно понимать эту фразу? Помогите немощному!

То есть, хватит меня критиковать - если знаете попробуйте объяснить сами, а не докапываться до чужих слов.

Скомпилировал простую программку и посмотрел ее листинг
пролог функции:
Код
push    r29
push    r28
in    r28, 0x3d; 61
in    r29, 0x3e; 62
sbiw    r28, 0x1f; 31
in    r0, 0x3f; 63
cli
out    0x3e, r29; 62
out    0x3f, r0; 63
out    0x3d, r28; 61


эпилог функции:
Код
adiw    r28, 0x1f; 31
in    r0, 0x3f; 63
cli
out    0x3e, r29; 62
out    0x3f, r0; 63
out    0x3d, r28; 61
pop    r28
pop    r29
ret


Из кода видно что компилятор копирует указатель стека в Y, и поэтому Y - указывает на стек (это так сказать алиас стека). И работает c локальными переменными в функции через указатель Y. Так что мои слова
Цитата
в том числе и указатель стека (r28-r29)=(Y).

не являются ложными , а являются слегка неточными.
Про один аппаратный стек в WinAVR, я полностью согласен. Про наличие двух стеков, я не говорил!
Сергей Борщ
QUOTE (alexeyv @ Jan 31 2011, 12:39) *
А как правильно понимать эту фразу? Помогите немощному!
Этот указатель заводится временно внутри функции для доступа к данным на стеке.
ReAl
Цитата(Сергей Борщ @ Jan 31 2011, 13:05) *
Этот указатель заводится временно внутри функции для доступа к данным на стеке.
Дополню: и только втом случае, если это нужно для данной функции (не все аргументы или локальные переменные влезли в регистры либо куда-то передаётся адрес локальной переменной).
frame pointer — указатель на кадр в стеке. А сам стек живёт по SP

Совершенно аналогично использованию регистра BP в x86/16 - в той системе команд относительно SP адресоваться было невозможно.
Giekelberri
Всем спасибо за ответы! Частично разобрался с организацией стека. Для этого воспользовался советами посмотреть откомпилированный код без оптимизации.

Код
int evaluate(int a, int b, int c)
{
    int sum = 0;
    sum = a + b + c;
    return sum;
}
int main(void)
{
    int i;
    char ch1;
    char ch2;
    i = evaluate(5,4,10);
    return 0;
}


Даже без оптимизации компилятор выкинул ch1 и ch2.
Параметры функции сначала сохранил в РОН. Войдя в функцию evaluate() выделил для них место в стеке и сохранил их в стеке.
Далее вычисление суммы. Тут заметна избыточность кода. Так как переменных всего три можно было их вычислить и без использования стека.
Результат, как вы правильно сказали, и в соответствии с документацией, был помещен в r24:r25

Вывод я сделал следующий:
1. для навигации внутри стека используется указатель Y.
2. глубину стека компилятор определяет заранее, поэтому при вызове функции указатель Y, а за ним и указатель стека SP прыгают на необходимую величину.

А количество параметров, передаваемых функции, как я понял ограничивается только размером стека, т. е. стремиться к Internal SRAM.
demiurg_spb
Цитата(alexeyv @ Jan 31 2011, 13:39) *
То есть, хватит меня критиковать - если знаете попробуйте объяснить сами, а не докапываться до чужих слов.
Не стоит так кипятиться:-)
У Вас всего-навсего была лишь проблема в терминологии. А в остальном Вы большой молодец!

Цитата(Giekelberri @ Jan 31 2011, 17:29) *
1. для навигации внутри стека используется указатель Y.
Для индексации стекового фрейма...используется указатель Y.
MaxiMuz
Цитата(Сергей Борщ @ Jan 28 2011, 19:19) *
... Это у ИАРа указатель на стек данных хранится в Y. GCC использует один стек и на него указывает регистр спец. назначения (SFR) SP.
...
Всегда считал что для организации стека в AVR используется указ.стека SP.
А РОН Y - в качестве указателя стека - это уже самодеятельность я был лучшего мнения о ИАР )
zltigo
QUOTE (MaxiMuz @ Jun 29 2011, 14:40) *
А РОН Y - в качестве указателя стека - это уже самодеятельность я был лучшего мнения о ИАР )

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