Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Локалные переменные в регистры
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Cредства разработки для МК > IAR
Student2
Не тайна что через использование ключевое слово register можно указать компилятору поставит переменную в регистр. В проекте можно тоже указать компилятору не использовать определенные регистры и потом создать глобальные регистровые переменные.

Вопрос идет об локальных переменных в какой то процедуре - если в процедуре много переменных оптимизация часто не принимает во внимание мои инструкции поставит переменные в регистры.

Например:
Код


register uint8_t var1, var2, var3, var4; // переменные в регистры

var1 = 15; // установка значения
var2 = 61;
var3 = 45;
var4 = 1;

DDRA = var1;
PORTC = var2;
DDRA = var3;
DDRB = var4;


После компиляции часто можно увидеть что в листинге находиться что то вроде:

Код

// DDRA = var1;
0000017A E07F       LDI   R23, 15    // load R23 with 15
0000017C B97D       OUT   0x0D, R23 // out to DDRA


вместо ожидаемого:


Код

// DDRA = var1;
0000017C B97D       OUT   0x0D, R21 // out directly var1 to DDRA


Как можно обмануть компилятору и поставить локальные переменные воистину в регистры (без использование глобальных переменных в регистров).
Rst7
Совершенно непонятно, почему Вы ожидаете от компилятора опустить инициализацию переменных (а больше отличий между реальным и желаемым листингом нет).
Student2
Конечно компилятор надо инициализировать переменные, но я говорю об что то совершенно другое - компилятор видит что переменная все время имеет одна и та же стойность и делает что то неладное

- вместо чтобы загрузит регистр и хранит этот регистр только для этой переменной он использует один и тот регистр для все переменные var1..var4

- перед каждой установке PORTA, DDRA и т.д. он загружает константу в регистр и только потом загружает PORTA, DDRA. В итоге это одна инструкция больше!
MALLOY2
Цитата
ключевое слово register


Не указать, а рекомендовать, компилятор сам решает куда и как ее засунуть если есть возможность ее в регистре разместить он ее и сам разместит. В современных компиляторах абсолютно бесполезная директива осталась только для совместимости со стандартом.

Цитата
В итоге это одна инструкция больше!



Какая риазница ?
Код
LDI R0,  15;  // установка значения
LDI R1,  61;
LDI R2,  45;
LDI R3,  1;  

OUT DDRA,  R0;
OUT PORTC, R1;
OUT DDRA,  R2;
OUT DDRB,  R3;


или

Код
LDI R0,  15;  
OUT DDRA,  R0;
LDI R0,  61;  
OUT PORTC, R0;
LDI R0,  45;  
OUT DDRA,  R0;
LDI R0,  1;  
OUT DDRB,  R0;
Student2
Цитата
Какая риазница ?


Если установка PORTA DDRA произходить в цикле разница получается доволно большая и заметная.

Код
LDI R0,  15;  // установка значения
LDI R1,  61;
LDI R2,  45;
LDI R3,  1;  

....
for (uint16_t ii = 0; ii < 1000; ii++)
{
OUT DDRA,  R0;
OUT PORTC, R1;
OUT DDRA,  R2;
OUT DDRB,  R3;
}


намного быстрее (1000 clocks!!!!) чем :

Код
for (uint16_t ii = 0; ii < 1000; ii++)
{
LDI R0,  15;  
OUT DDRA,  R0;
LDI R0,  61;  
OUT PORTC, R0;
LDI R0,  45;  
OUT DDRA,  R0;
LDI R0,  1;  
OUT DDRB,  R0;
}
Rst7
Посмотрел внимательнее. Судя по всему, компилятор - IAR. Действительно, иногда у него так бывает, жадность до регистров (т.е. не портить лишнего) пересиливает CSE.

Я в таких случаях делаю код вот такого плана:
Код
static void fooxx_stage2(uint8_t var1, uint8_t var2, uint8_t var3, uint8_t var4);

void fooxx(void)
{
  fooxx_stage2(15,61,45,1);
}

static void fooxx_stage2(uint8_t var1, uint8_t var2, uint8_t var3, uint8_t var4)
{
  for(uint16_t ii=0; ii<1000; ii++)
  {
    DDRD = var1;
    PORTC = var2;
    DDRD = var3;
    DDRB = var4;
  }
}

CODE

RSEG CODE:CODE:NOROOT(1)
// 7 void fooxx(void)
fooxx:
// 8 {
// 9 fooxx_stage2(15,61,45,1);
LDI R19, 1
LDI R18, 45
LDI R17, 61
LDI R16, 15
REQUIRE fooxx_stage2
; // Fall through to label fooxx_stage2
// 10 }
// 11

RSEG CODE:CODE:NOROOT(1)
// 12 static void fooxx_stage2(uint8_t var1, uint8_t var2, uint8_t var3, uint8_t var4)
fooxx_stage2:
// 13 {
MOVW R21:R20, R25:R24
// 14 for(uint16_t ii=0; ii<1000; ii++)
LDI R24, 232
LDI R25, 3
// 15 {
// 16 DDRD = var1;
??fooxx_stage2_0:
OUT 0x0A, R16
// 17 PORTC = var2;
OUT 0x08, R17
// 18 DDRD = var3;
OUT 0x0A, R18
// 19 DDRB = var4;
OUT 0x04, R19
// 20 }
SBIW R25:R24, 1
BRNE ??fooxx_stage2_0
// 21 }
MOVW R25:R24, R21:R20
RET


Но лучше так делать только при сильной необходимости.
Student2
Спасибо - это точно что хотел узнать.

Вопрос - а что будет если использовать #pragma inline=forced перед fooxx(). Мне все таки не хочется вызывать процедуру где можно и не вызывать (каждый CALL может сопровождаться громоздким сегментом сохранения - восстановления регистров).
Rst7
Цитата
Вопрос - а что будет если использовать #pragma inline=forced перед fooxx().


Ничего хорошего. Вам просто надо сделать так - разбить Ваш код (который был в виде одной процедуры) на две части, первая из которых лежит в процедуре fooxx, например, подготавливая все исходные данные, а вторая - fooxx_stage2, где и происходят необходимые действия.
_Pasha
Цитата(Rst7 @ Oct 27 2009, 17:05) *
Ничего хорошего.

В гцц такие объявления:
Код
static inline void fooxx_stage2(const uint8_t var1, const uint8_t var2, const  uint8_t var3, const uint8_t var4);

не вызывают указанных проблем с оверхедом. Как по-иарски, не знаю...
Rst7
Цитата
не вызывают указанных проблем с оверхедом. Как по-иарски, не знаю...


Так оно и так инлайнится почти. Поглядите на листинг. А инлайнить stage2 - это тоже самое, что написать все вместе и получить унылый результат.
_Bill
Цитата(Student2 @ Oct 27 2009, 07:37) *
Не тайна...

Компилятор вообще игнорирует ключевое слово register. Если хорошенько подумаете, то поймете почему.
sKWO
Интерессно, а переменную типа флоат компилятор используя соглашения предлагает разместить поочерёдно в регистрах с 16- го по 19й при инициализ. ф-ции fooxx.
В fooxx_stage2 при попытке доступа к регистрам они сначала размещаются на стеке а потом меняются местами тоесть обратно перегружаются в обратном порядке (19 равен 16му и тд).
Почему компилятор поступает так с рабочими регистрами остаётся загадкой. Пробовал с максимальной оптимизацией по скорости.
Если в цикле например увеличивать её значение (переменной) то порядок проинициализированных рабочих регистров в ф-ции fooxx в fooxx_stage2 не изменяется.
Ето нужно учитывать при написании асм функций.
_Bill
Цитата(Student2 @ Oct 27 2009, 07:37) *
Не тайна что через использование ключевое слово register можно указать компилятору поставит переменную в регистр. В проекте можно тоже указать компилятору не использовать определенные регистры и потом создать глобальные регистровые переменные.

Вопрос идет об локальных переменных в какой то процедуре - если в процедуре много переменных оптимизация часто не принимает во внимание мои инструкции поставит переменные в регистры.

Например:
Код


register uint8_t var1, var2, var3, var4; // переменные в регистры

var1 = 15; // установка значения
var2 = 61;
var3 = 45;
var4 = 1;

DDRA = var1;
PORTC = var2;
DDRA = var3;
DDRB = var4;


После компиляции часто можно увидеть что в листинге находиться что то вроде:

Код

// DDRA = var1;
0000017A E07F       LDI   R23, 15    // load R23 with 15
0000017C B97D       OUT   0x0D, R23 // out to DDRA


вместо ожидаемого:


Код

// DDRA = var1;
0000017C B97D       OUT   0x0D, R21 // out directly var1 to DDRA


Как можно обмануть компилятору и поставить локальные переменные воистину в регистры (без использование глобальных переменных в регистров).

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