Если эти переменные используются в рамках одной функции, то их можно завести на стеке. Так и делает большинство С-компиляторов. По-другому (статическое дерево «скомпилированного стека» aka «оверлеев данных») делают только тогда, когда стек невозможно или неудобно (накладно по времени выполнения) сделать.
В применении к AVR есть два подхода (коротко, не углубляясь в детали):
«IAR» -- Пара Y выделена под указатель стека данных, в начале программы настраивается на выделенную область ОЗУ.
В подпрограмме:
; пусть нужны 8-битная и 16-битная переменные, которые не влазят в регистры
foo:
sbiw Y, 3
; заносим начальные значения
clr r16
st Y, r16
ldi r16, lo8(12345)
std Y+1, r16
ldi r16, hi8(12345)
std Y+2, r16
; ...
; вот так со смещениями и пользуемся, выходит даже компактнее, чем STS/LDS
; ...
; если ей тоже нужно, она сама сделает sbiw/adiw с Y и будет иметь
; свои локальные переменные
call foo2
; ...
; восстанавливаем указатель стека и возвращаемся
adiw Y, 3
ret
AVR-GCC -- стек один, общий для управления (call/ret) и для данных. Если в подпрограмме нужны локальные переменные, то она уменьшает значение SPH:SPL (да-да, IN, модификация, OUT, это нужно делать атомарно, поэтому для небольших значений изворачиваются комбинациями call / push / pop), новое значение остаётся в Y для аналогичного использования.
В минусах лишняя работа по организации стекового кадра, в плюсах -- свободная пара Y для тех функций, которым стековый кадр не нужен.