Перейти к содержанию
    

Локалные переменные в регистры

Не тайна что через использование ключевое слово 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

 

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

Изменено пользователем 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;

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Какая риазница ?

 

Если установка 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;
}

Изменено пользователем Student2

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Посмотрел внимательнее. Судя по всему, компилятор - 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;
  }
}

       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

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо - это точно что хотел узнать.

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вопрос - а что будет если использовать #pragma inline=forced перед fooxx().

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ничего хорошего.

В гцц такие объявления:

static inline void fooxx_stage2(const uint8_t var1, const uint8_t var2, const  uint8_t var3, const uint8_t var4);

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

 

Так оно и так инлайнится почти. Поглядите на листинг. А инлайнить stage2 - это тоже самое, что написать все вместе и получить унылый результат.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Не тайна...

Компилятор вообще игнорирует ключевое слово register. Если хорошенько подумаете, то поймете почему.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Интерессно, а переменную типа флоат компилятор используя соглашения предлагает разместить поочерёдно в регистрах с 16- го по 19й при инициализ. ф-ции fooxx.

В fooxx_stage2 при попытке доступа к регистрам они сначала размещаются на стеке а потом меняются местами тоесть обратно перегружаются в обратном порядке (19 равен 16му и тд).

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

Если в цикле например увеличивать её значение (переменной) то порядок проинициализированных рабочих регистров в ф-ции fooxx в fooxx_stage2 не изменяется.

Ето нужно учитывать при написании асм функций.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Не тайна что через использование ключевое слово 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

 

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

Я думаю, обманывать тут никого не нужно. Если судить по приведенному фрагменту кода, то необходимости отводить регистры под однократно используемые константы абсолютно нет. И компилятор это учел. Если бы эти константы использовались не один раЗ, то компилятор возможно и разместил бы их регистрах, но... Тут все зависит от количества локальных переменных. Если их число велико, то всех их в регистрах не разместишь. Поэтому для подобных операций используются рабочие регистры.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...