Почему avr-gcc беспокоится о сохранении состояния регистра при вызове main ()?


Функция main() в программе avr-gcc сохраняет состояние регистра в стеке, но когда среда выполнения вызывает его, я понимаю, что на микроконтроллере нет ничего, к чему можно было бы вернуться. Разве это пустая трата оперативной памяти? Как можно предотвратить эту государственную экономию?

5 2

5 ответов:

Скорее всего main просто компилируется так же, как и стандартная функция. В C это в значительной степени должно быть, потому что вы можете позвонить ему откуда-то.

Обратите внимание, что в C++ запрещено рекурсивно вызывать main, поэтому компилятор c++ может оптимизировать это еще больше. Но в C, как было указано в вашем вопросе, законно (если это плохая идея) вызывать main рекурсивно, поэтому он должен быть скомпилирован таким же образом, как и любая другая функция.

Как компилятор может быть уверен, что вы не собираетесь рекурсивно вызывать main()?

Все дело в C-стандарте.

Ничто не запрещает вам выходить из main в определенное время. Вы можете не делать этого в своей программе, но другие могут делать это.

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

Это может быть даже полезно сделать: Я не знаю о AVR, но другие микроконтроллеры могут перейти в состояние низкого энергопотребления, когда они закончат свою работу и ждут перезагрузки. Делать это из обработчика очистки может быть хорошей идеей, потому что этот обработчик вызывается, если вы выходите из main обычным способом и (насколько я сейчас), если ваша программа прерывается через сигнал kill.

Как можно предотвратить это сохранение состояния?

Единственное, что вы можете сделать, это написать собственную процедуру запуска C-Startup. Это означает возню с ассемблером, но вы можете перейти к своему main() вместо того, чтобы просто вызывать его.

В моих тестах с avr-gcc 4.3.5, он только сохраняет регистры, если не оптимизирует много. Нормальные уровни (- Os или-O2) вызывают оптимизацию инструкций push. Можно также указать в объявлении функции, что она не будет возвращаться с __attribute__((noreturn)). Также полезно выполнить полную оптимизацию программы с помощью-fwhole-program.

Исходный код в avr-libc использует вызов для перехода к main, поскольку указано, что main может вернуться, а затем переходит к exit (который объявлен noreturn и, таким образом, не генерирует вызова). Вы можете связать свой собственный вариант, если считаете, что это слишком много. exit() в свою очередь просто отключает прерывания и входит в бесконечный цикл, эффективно останавливая вашу программу, но не экономя энергию. Это четыре инструкции и два байта служебной памяти стека, если ваш main() никогда не возвращает или не вызывает exit ().