|
Компилятор в IAR, ньюансы обращения к переменным |
|
|
|
Nov 22 2006, 02:23
|
Местный
  
Группа: Свой
Сообщений: 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 Спасибо заранее!
|
|
|
|
|
Nov 22 2006, 02:42
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
ИМХО потому что запись Код tmpByte <<= 1; на самом деле краткая и обозначает Код tmpByte = tmpByte << 1; Аналогично Код tmpByte |= tmp_bit; обозначает Код tmpByte = tmpByte | tmp_bit; Поскольку tmpByte является и источником-операндом и приемником результата, то компилятор вполне логично каждую команду раскладывает так, как ему предписано. А вот с включенной оптимизацией он возможно соптимизирует код к такому виду, который вы считаете нормальным.
|
|
|
|
|
Nov 22 2006, 11:57
|

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

|
Цитата Цитата(king2 @ Nov 22 2006, 02:26)  А почему оно в одном случае загружает переменную из памяти при помощи LDS, а в другом через LDI?
И по обьёму кода и по скорости варианты lds/sts и ldi/ldi/ld/st эквивалентны. Почти эквивалентны. Первый вариант все ж чуток быстрее, насколько помню. Но по объему если работа с объектом больше одного байта, второй начинает неслабо рулить.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Dec 8 2006, 12:16
|
Местный
  
Группа: Участник
Сообщений: 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, в противном случае компилятор будет работать в цикле не с самими переменными, а с их копиями в регистрах. И первый вариант корректнее второго, ибо по смыслу требуется ответная реакция и ответ должен посылаться после получения запроса а не до него.
|
|
|
|
|
Dec 8 2006, 13:24
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Цитата(_Bill @ Dec 8 2006, 12:16)  Все правильно. Вы должны были объявить переменные как volatile, в противном случае компилятор будет работать в цикле не с самими переменными, а с их копиями в регистрах. И первый вариант корректнее второго, ибо по смыслу требуется ответная реакция и ответ должен посылаться после получения запроса а не до него. Всем спасибо за ответы. С точки зрения логики работы программы - оба варианта для меня равнозначны. Так как ответ выдаётся ч/з паузу после приёма предыдущего пакета, а выход из цикла - после получения нового. Сама конструкция несколько тяжеловесней, но не буду об этом. Хочу спросить следующий вопрос. Таким образом после объявления переменной как volatile, код (в общем случае) должен быть эффективнее? Я это к тому, что наблюдаю как компилятор создаёт в ряде случаев локальные переменные и с ними работает, а не обращается напрямую. Вопрос не эффективности, а защиты данных.
|
|
|
|
|
Dec 8 2006, 17:31
|
Местный
  
Группа: Участник
Сообщений: 416
Регистрация: 18-04-06
Из: Челябинск
Пользователь №: 16 219

|
Цитата(SasaVitebsk @ Dec 8 2006, 13:24)  Всем спасибо за ответы. С точки зрения логики работы программы - оба варианта для меня равнозначны. Так как ответ выдаётся ч/з паузу после приёма предыдущего пакета, а выход из цикла - после получения нового. Сама конструкция несколько тяжеловесней, но не буду об этом.
Хочу спросить следующий вопрос. Таким образом после объявления переменной как volatile, код (в общем случае) должен быть эффективнее? Я это к тому, что наблюдаю как компилятор создаёт в ряде случаев локальные переменные и с ними работает, а не обращается напрямую. Вопрос не эффективности, а защиты данных. Он не будет эффективнее, скорее даже наоборот. Но код будет коррекктным, поскольку volatile запрещает компилятору оптимизацию (с его точки зрения) кода при обращении к этим переменным. Типичный пример оптимизации - работа с копией переменной в регистре а не с самой переменной в памяти. Компилятор считает, что если переменная в цикле не изменяется, то повторное обращение к памяти излишне. Т.е. компилятор выносит за пределы цикла все действия инвариантные данному циклу. В данном случае это неверно, поскольку значение переменной меняется иным, неизвестным компилятору, образом (в прерываниях). В случае же обращений к переменным volatile компилятор будет знать об этом и будет в цикле обращаться всегда к самой переменной, а не к ее копии.
Сообщение отредактировал _Bill - Dec 8 2006, 17:33
|
|
|
|
|
Dec 11 2006, 22:38
|

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

|
Цитата(SasaVitebsk @ Dec 11 2006, 20:34)  а существуют ли директивы компилятора позволяющие включать/отключать оптимизацию выборочно по тексту? Естественно. #pragma optimize= Подробности естественно в документации на компилятор. Но в Вашем конкретном случае это просто использование volatile - можете считать, что это "отключение оптимизации" для конкретной переменной.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Dec 18 2006, 16:58
|
Участник

Группа: Участник
Сообщений: 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 раза, однако! Никак оптимальным такой код не назовешь. Что скажете..?
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|