реклама на сайте
подробности

 
 
3 страниц V   1 2 3 >  
Reply to this topicStart new topic
> Компилятор в IAR, ньюансы обращения к переменным
king2
сообщение Nov 22 2006, 02:23
Сообщение #1


Местный
***

Группа: Свой
Сообщений: 255
Регистрация: 17-08-06
Из: Москва
Пользователь №: 19 646



Снова не пойму как IAR работает с переменными...
Оптимизация вся выключена, дабы не влиять излишне.

Имеем такой вот код:

Код
    111                    tmpByte <<= 1;
   \   00000464   9100....           LDS     R16, tmpByte
   \   00000468   0F00               LSL     R16
   \   0000046A   9300....           STS     tmpByte, R16
    112                    tmpByte |= tmp_bit;
   \   0000046E   8108               LD      R16, Y
   \   00000470   ....               LDI     R30, LOW(tmpByte)
   \   00000472   ....               LDI     R31, (tmpByte) >> 8
   \   00000474   8110               LD      R17, Z
   \   00000476   2B10               OR      R17, R16
   \   00000478   8310               ST      Z, R17


Почему компилятор, имея в регистре значение переменной, и следующую операцию с ней же, тем не менее сначала сохраняет ее, а потом загружает? Переменная не volatile.
Почему в первом и втором случае обращение к одной и той же переменной делается по разному??
Как избежать такого извращенного обращения к переменным?

Эквивалентный (нормальный) код:
Код
    114                    tmpByte = (tmpByte<<1) | tmp_bit;
   \   0000047A   9100....           LDS     R16, tmpByte
   \   0000047E   0F00               LSL     R16
   \   00000480   8118               LD      R17, Y
   \   00000482   2B10               OR      R17, R16
   \   00000484   9310....           STS     tmpByte, R17


Спасибо заранее!
Go to the top of the page
 
+Quote Post
rezident
сообщение Nov 22 2006, 02:42
Сообщение #2


Гуру
******

Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882



ИМХО потому что запись
Код
tmpByte <<= 1;

на самом деле краткая и обозначает
Код
tmpByte = tmpByte << 1;

Аналогично
Код
tmpByte |= tmp_bit;

обозначает
Код
tmpByte = tmpByte | tmp_bit;

Поскольку tmpByte является и источником-операндом и приемником результата, то компилятор вполне логично каждую команду раскладывает так, как ему предписано. А вот с включенной оптимизацией он возможно соптимизирует код к такому виду, который вы считаете нормальным.
Go to the top of the page
 
+Quote Post
king2
сообщение Nov 22 2006, 03:26
Сообщение #3


Местный
***

Группа: Свой
Сообщений: 255
Регистрация: 17-08-06
Из: Москва
Пользователь №: 19 646



А почему оно в одном случае загружает переменную из памяти при помощи LDS, а в другом через LDI?
Go to the top of the page
 
+Quote Post
IgorKossak
сообщение Nov 22 2006, 10:50
Сообщение #4


Шаман
******

Группа: Модераторы
Сообщений: 3 064
Регистрация: 30-06-04
Из: Киев, Украина
Пользователь №: 221



Цитата(king2 @ Nov 22 2006, 02:26) *
А почему оно в одном случае загружает переменную из памяти при помощи LDS, а в другом через LDI?

И по обьёму кода и по скорости варианты lds/sts и ldi/ldi/ld/st эквивалентны.
Второй вариант начинает иметь преимущество если переменная длиннее одного байта или когда она участвует в цепочечных операциях.
Go to the top of the page
 
+Quote Post
dxp
сообщение Nov 22 2006, 11:57
Сообщение #5


Adept
******

Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343



Цитата
Цитата(king2 @ Nov 22 2006, 02:26) *

А почему оно в одном случае загружает переменную из памяти при помощи LDS, а в другом через LDI?

И по обьёму кода и по скорости варианты lds/sts и ldi/ldi/ld/st эквивалентны.

Почти эквивалентны. Первый вариант все ж чуток быстрее, насколько помню. Но по объему если работа с объектом больше одного байта, второй начинает неслабо рулить.


--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
Go to the top of the page
 
+Quote Post
SasaVitebsk
сообщение Dec 7 2006, 23:06
Сообщение #6


Гуру
******

Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521



На этот раз для меня не понятный момент. До этого - не встречался с такой ерундой.
Переменная объявлена так (начало файла)

int16_t static HeadInBuf=0, EndInBuf=0; // RS232. Указатели буфера. Указатель на голову и хвост достоверных данных

Внизу в колове имеется такой оператор.
...
while (HeadInBuf == EndInBuf)
AnswerGo(); // Ждать прихода символа с RS485
....

Сижу в отладчике - вижу, что переменные HeadInBuf и EndInBuf - разные (пришёл пакет данных), но условие не проверяется, непрерывно выполняется AnswerGo(). И из цикла не выходит.

Скорее всего надо чего-то указать компилятору при инициализации переменных. Вопрос - что?
Go to the top of the page
 
+Quote Post
rezident
сообщение Dec 8 2006, 01:01
Сообщение #7


Гуру
******

Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882



Цитата(SasaVitebsk @ Dec 8 2006, 01:06) *
Скорее всего надо чего-то указать компилятору при инициализации переменных. Вопрос - что?

Добавить volatile в объявлении переменных. Или даже заменить static на volatile, т.к. я не уверен, что компилятор съест их оба враз.
Go to the top of the page
 
+Quote Post
zltigo
сообщение Dec 8 2006, 02:52
Сообщение #8


Гуру
******

Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244



Цитата(rezident @ Dec 8 2006, 00:01) *
заменить static на volatile, т.к. я не уверен, что компилятор съест их оба враз.

Cовершенно разные ни коим образом не противоречащие друг-другу вещи - не может не скушать.
Правда следует отметить, что static ни каким боком к проблеме не относится.


--------------------
Feci, quod potui, faciant meliora potentes
Go to the top of the page
 
+Quote Post
SasaVitebsk
сообщение Dec 8 2006, 03:12
Сообщение #9


Гуру
******

Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521



Информация к размышлению. Объявление не менял. Поменял конструкцию на вот такую.

do
AnswerGo();
while (HeadInBuf == EndInBuf); // Ждать прихода символа с RS485

И всё заработало.

В обоих случаях смотрел откомпилированный текст.

В первом случае было типа

rcall ...
brne ...
rcall ...
rjmp PC-1 ( должно было компильнутся в PC-3)

Во втором случае придраться не к чему. Надо сказать что эта конструкция в тексте встречается 3 раза, и возможно он пытался к общему знаменателю придти. ???

Опции по оптимизации выставлены такие.
speed high
все птички
3 прохода
птичка
Go to the top of the page
 
+Quote Post
_Bill
сообщение Dec 8 2006, 12:16
Сообщение #10


Местный
***

Группа: Участник
Сообщений: 416
Регистрация: 18-04-06
Из: Челябинск
Пользователь №: 16 219



Цитата(SasaVitebsk @ Dec 8 2006, 03:12) *
Информация к размышлению. Объявление не менял. Поменял конструкцию на вот такую.

do
AnswerGo();
while (HeadInBuf == EndInBuf); // Ждать прихода символа с RS485

И всё заработало.

В обоих случаях смотрел откомпилированный текст.

В первом случае было типа

rcall ...
brne ...
rcall ...
rjmp PC-1 ( должно было компильнутся в PC-3)

Во втором случае придраться не к чему. Надо сказать что эта конструкция в тексте встречается 3 раза, и возможно он пытался к общему знаменателю придти. ???

Опции по оптимизации выставлены такие.
speed high
все птички
3 прохода
птичка

Все правильно. Вы должны были объявить переменные как volatile, в противном случае компилятор будет работать в цикле не с самими переменными, а с их копиями в регистрах. И первый вариант корректнее второго, ибо по смыслу требуется ответная реакция и ответ должен посылаться после получения запроса а не до него.
Go to the top of the page
 
+Quote Post
SasaVitebsk
сообщение Dec 8 2006, 13:24
Сообщение #11


Гуру
******

Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521



Цитата(_Bill @ Dec 8 2006, 12:16) *
Все правильно. Вы должны были объявить переменные как volatile, в противном случае компилятор будет работать в цикле не с самими переменными, а с их копиями в регистрах. И первый вариант корректнее второго, ибо по смыслу требуется ответная реакция и ответ должен посылаться после получения запроса а не до него.


Всем спасибо за ответы.
С точки зрения логики работы программы - оба варианта для меня равнозначны. Так как ответ выдаётся ч/з паузу после приёма предыдущего пакета, а выход из цикла - после получения нового. Сама конструкция несколько тяжеловесней, но не буду об этом.

Хочу спросить следующий вопрос. Таким образом после объявления переменной как volatile, код (в общем случае) должен быть эффективнее? Я это к тому, что наблюдаю как компилятор создаёт в ряде случаев локальные переменные и с ними работает, а не обращается напрямую. Вопрос не эффективности, а защиты данных.
Go to the top of the page
 
+Quote Post
_Bill
сообщение Dec 8 2006, 17:31
Сообщение #12


Местный
***

Группа: Участник
Сообщений: 416
Регистрация: 18-04-06
Из: Челябинск
Пользователь №: 16 219



Цитата(SasaVitebsk @ Dec 8 2006, 13:24) *
Всем спасибо за ответы.
С точки зрения логики работы программы - оба варианта для меня равнозначны. Так как ответ выдаётся ч/з паузу после приёма предыдущего пакета, а выход из цикла - после получения нового. Сама конструкция несколько тяжеловесней, но не буду об этом.

Хочу спросить следующий вопрос. Таким образом после объявления переменной как volatile, код (в общем случае) должен быть эффективнее? Я это к тому, что наблюдаю как компилятор создаёт в ряде случаев локальные переменные и с ними работает, а не обращается напрямую. Вопрос не эффективности, а защиты данных.

Он не будет эффективнее, скорее даже наоборот. Но код будет коррекктным, поскольку volatile запрещает компилятору оптимизацию (с его точки зрения) кода при обращении к этим переменным. Типичный пример оптимизации - работа с копией переменной в регистре а не с самой переменной в памяти. Компилятор считает, что если переменная в цикле не изменяется, то повторное обращение к памяти излишне. Т.е. компилятор выносит за пределы цикла все действия инвариантные данному циклу. В данном случае это неверно, поскольку значение переменной меняется иным, неизвестным компилятору, образом (в прерываниях). В случае же обращений к переменным volatile компилятор будет знать об этом и будет в цикле обращаться всегда к самой переменной, а не к ее копии.

Сообщение отредактировал _Bill - Dec 8 2006, 17:33
Go to the top of the page
 
+Quote Post
SasaVitebsk
сообщение Dec 11 2006, 21:34
Сообщение #13


Гуру
******

Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521



Спасибо, разъяснили доступно. Извините за наглость, но чтобы окончательно закрыть данный вопрос, - а существуют ли директивы компилятора позволяющие включать/отключать оптимизацию выборочно по тексту? У меня, к примеру, это первый случай возникновения такой ситуации и не хочется глобально отключать оптимизацию переменной по той или иной причине.

Заранее спасибо за ответ.
Go to the top of the page
 
+Quote Post
zltigo
сообщение Dec 11 2006, 22:38
Сообщение #14


Гуру
******

Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244



Цитата(SasaVitebsk @ Dec 11 2006, 20:34) *
а существуют ли директивы компилятора позволяющие включать/отключать оптимизацию выборочно по тексту?

Естественно.
#pragma optimize=
Подробности естественно в документации на компилятор.
Но в Вашем конкретном случае это просто использование volatile - можете считать, что это "отключение оптимизации" для конкретной переменной.


--------------------
Feci, quod potui, faciant meliora potentes
Go to the top of the page
 
+Quote Post
Perepic
сообщение Dec 18 2006, 16:58
Сообщение #15


Участник
*

Группа: Участник
Сообщений: 23
Регистрация: 14-08-06
Пользователь №: 19 528



Цитата(rezident @ Nov 22 2006, 02:42) *
ИМХО потому что запись
Код
tmpByte <<= 1;

на самом деле краткая и обозначает
Код
tmpByte = tmpByte << 1;


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


Интересно мыслите, однако! Как вам такой пример, я честно говоря до сих пор не понимаю отчего IAR 4.11a так себя ведет. Оптимизация включена на максимум, окромя "cross-call"
Код
UCHAR TimeOut;            // глобальная переменная

__interrupt void TimerInt()
{
    if(TimeOut) TimeOut--;
};

Все это компилится в ледующий код:
Код
     LDS     R16, TimeOut
     TST     R16
     BREQ    0x392
  
    LDI     R30,0x9F
    LDI     R31,0x02
    LD      R16,Z
    DEC     R16
    ST      Z,R16

Сохраниение и восстановление стека я опустил.
Что мы видим? Грузим переменную, проверяем ее, если условие выполняется, опять ее же грузим (ЗАЧЕМ?), причем засоряя адресные регистры, модифицируем и сохраняем. В результате имеем 3 лишних комманды + 4 для сохраниения-восстановления адресных регистров. Более чем в 2 раза, однако! Никак оптимальным такой код не назовешь. Что скажете..?
Go to the top of the page
 
+Quote Post

3 страниц V   1 2 3 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 20th July 2025 - 18:51
Рейтинг@Mail.ru


Страница сгенерированна за 0.01585 секунд с 7
ELECTRONIX ©2004-2016