Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Про IAR компилятор С
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Cредства разработки для МК > IAR
Dopler
Здравствуйте.

Раньше никогда на C под железо не писал, только на ассемблере. Но жизнь показывает, что для ARM писать все на asm не актуально. Начал смотреть, какой код генерирует компилятор. Вот для примера две строки на C:

Код
      for(waiting_time = 1999; waiting_time >= 0; waiting_time--);

      for(waiting_time = 0; waiting_time <= 2000; waiting_time++);


Обе эти строки эквивалентны с точки зрения выполнения программы на С (т.е. будет 2000 шагов). Компилятор генерирует следующий код:

\ main:
\ ??main_0:
\ 00000000 CF00A0E3 MOV R0,#+207
\ 00000004 700E80E3 ORR R0,R0,#0x700
\ 00000008 000000EA B ??main_1
\ ??main_2:
\ 0000000C 010040E2 SUB R0,R0,#+1
\ ??main_1:
\ 00000010 000050E3 CMP R0,#+0
\ 00000014 FCFFFF5A BPL ??main_2
19
20 //AT91C_BASE_PIOB->PIO_CODR = 1 << 22;
21 for(waiting_time = 0; waiting_time < 2000; waiting_time++) ;
\ 00000018 0000A0E3 MOV R0,#+0
\ ??main_3:
\ 0000001C 7D0E50E3 CMP R0,#+2000
\ 00000020 F6FFFFAA BGE ??main_0
\ 00000024 010080E2 ADD R0,R0,#+1
\ 00000028 FBFFFFEA B ??main_3



Мне кажется, что выделенная строка явно лишняя, так как флаг нулевого значения выставляется и в предыдущей команде. Во-вторых, при декременте (в первом варианте) есть лишний переход. Таким образом, эквивалентность циклов на С достигнута искуственно. Возможно, это сделано специально (а возможно, человек пока способен генерировать все же лучший код)? Возможно, это где-то описанно или отключается?
Newegor
Поиграйтесь с настройками оптимизации. При различном уровне - будет различный код.
VAI
я, обычно, смотрю листинг, что у меня получилось, делаю несколько вариантов одного и того-же (как Вы в Вашем примере), и выбираю, с моей точки зрения, оптимальный.
И уровень оптимизации делаю максимальный или приближеный к нему.
scifi
Забавно, а у меня комилятор в обоих случаях сгенерил одно и то же:
Код
    for (i=0; i<2000; i++)
        __no_operation();
    for (i=1999; i>=0; i--)
        __no_operation();

    for (i=0; i<2000; i++)
  000195C4  E3A00E7D  MOV          R0, #0x7D0
        __no_operation();
  000195C8  E1A00000  NOP          
    for (i=0; i<2000; i++)
  000195CC  E2500001  SUBS         R0, R0, #0x1
    for (i=0; i<2000; i++)
  000195D0  1AFFFFFC  BNE          0x0195C8
    for (i=1999; i>=0; i--)
  000195D4  E3A00E7D  MOV          R0, #0x7D0
        __no_operation();
  000195D8  E1A00000  NOP          
    for (i=1999; i>=0; i--)
  000195DC  E2500001  SUBS         R0, R0, #0x1
    for (i=1999; i>=0; i--)
  000195E0  1AFFFFFC  BNE          0x0195D8


Это IAR 4.40, С++, максимальная оптимизация на скорость. Надо сказать, этот компилятор оставляет хорошее впечатление.
А вообще лучше не лезть в дела компилятора, а заниматься более интересными вещами, то есть написанием программ. Проверять сгенерированный код надо только тогда, когда не хватает памяти или скорости. Иначе не останется времени на отдых :-)
Dopler
Цитата(scifi @ Feb 8 2007, 15:26) *


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

Вопрос в том, что при желании сделать например, функцию
Код
SimpleDelay (long d)
{
     for (;d!=0;d--);

}


Этот самый Delay будет прямо зависить от режима оптимизации, и вообще улетит при максимальной.

Следующий вопрос, а как-нибудь вообще можно объявить функцию на asm как inline? Или сделать макрос на asm для использования в C-коде?
Сергей Борщ
Цитата(Dopler @ Feb 8 2007, 15:02) *
Если убрать пустую операцию, то весь цикл попадает под нож оптимизатора при максимальной оптимизации
Если писать правильно, то не попадает.
Цитата(Dopler @ Feb 8 2007, 15:02) *
Вопрос в том, что при желании сделать например, функцию
Код
SimpleDelay (long d)
{
     for (;d!=0;d--);
}

Этот самый Delay будет прямо зависить от режима оптимизации, и вообще улетит при максимальной.
А если так:
Код
SimpleDelay (long volatile d)
{
     for (;d!=0;d--);
}
При разном уровне действительно возможно различное время выполнения, но можно ведь для этой конкретной функции выставить желаемый уровень оптимизации с помощью #pragma.
Цитата(Dopler @ Feb 8 2007, 15:02) *
Следующий вопрос, а как-нибудь вообще можно объявить функцию на asm как inline? Или сделать макрос на asm для использования в C-коде?
В gcc вроде да, в IAR - точно нет. Про остальные не знаю.
scifi
Цитата
Следующий вопрос, а как-нибудь вообще можно объявить функцию на asm как inline? Или сделать макрос на asm для использования в C-коде?


Попробуйте так:

#define DELAY(N) __asm("mov R0,#" #N "\nSUBS R0,R0,#1\nBNE .-4");
Dopler
Цитата(scifi @ Feb 8 2007, 20:49) *
Попробуйте так:

#define DELAY(N) __asm("mov R0,#" #N "\nSUBS R0,R0,#1\nBNE .-4");



И при использовании такого макроса компилятор гарантированно не будет использовать R0 в своих целях?
Что-то я ничего внятного в документации IAR по поводу INLINE ASSEMBLER не нашел, кроме того, что его лучше избегать.
zltigo
Цитата(Dopler @ Feb 8 2007, 21:21) *
И при использовании такого макроса компилятор гарантированно не будет использовать R0 в своих целях?

Не будет sad.gif и много чего другого не будет sad.gif Верный способ угробить оптимизацию sad.gif - посему действительно не стоит использовать такие вставки....
Dopler
Похоже самый надежный способ, это тот, который рекомендовал Сергей Борщ, это принудительно задавать уровень оптимизации для некоторых функций, используя при этом volatile.

Еще вопрос, как нибудь в IAR можно узнать, за сколько тактов процессора выполнится кусок кода? А то в некоторых случаях это не совсем очевидно (некоторые команды asm заменяются двумя, обнуление конвеера и т.д.)
scifi
Если я правильно понял, нужно получить малые задержки. Предлагаю для этих целей написать функцию на ассемблере и вызывать её из Си. Если пытаться добиться этого при помощи компилятора, то результат будет зависеть от опций компилятора и, возможно, его версии, что не очень приятно (проапгрейдили компилятор - будьте готовы к тому, что старый код перестанет работать).
Время выполнения кода зависит от модели процессора и задержек при доступе к памяти. Для процессора ARM7TDMI времена выполнения инструкций указаны в "ARM7TDMI Technical Reference Manual":
http://www.arm.com/pdfs/DDI0210C_7tdmi_r4p1_trm.pdf (2 MB)
О задержках при доступе к памяти: см. документацию на используемый микроконтроллер.
Dopler
Цитата(scifi @ Feb 9 2007, 11:37) *
Если я правильно понял, нужно получить малые задержки. Предлагаю для этих целей написать функцию на ассемблере и вызывать её из Си.


Просто для совсем маленьких функций накладные расходы по вызову и передаче параметров неприятны. Если бы можно было бы функцию на asm сделать inline, было бы здорово.
Dopler
Так #pragma optimize не позволяет повысить уровень оптимизации, что это за лажа такая?
zltigo
Цитата(scifi @ Feb 9 2007, 10:37) *
Время выполнения кода зависит от модели процессора и задержек при доступе к памяти. Для процессора ARM7TDMI времена выполнения инструкций указаны в "ARM7TDMI Technical Reference Manual":

И наличия MAM и наличия Кэш и эффективности работы оных а еще DMA захватит шину smile.gif. Короче, лучше навсегда выкиньте из головы желание узнать для более-менее современых контроллеров "точное" время исполнения чего-либо.

Цитата(Dopler @ Feb 9 2007, 12:02) *
Так #pragma optimize не позволяет повысить уровень оптимизации, что это за лажа такая?

Изъясняйтесь конкретнее.
Dopler
Цитата(zltigo @ Feb 9 2007, 13:13) *
Цитата(Dopler @ Feb 9 2007, 12:02) *

Так #pragma optimize не позволяет повысить уровень оптимизации, что это за лажа такая?

Изъясняйтесь конкретнее.


Я имел в виду, что любимой избранной функции нельзя задать жестко уровень оптимизации. Т.е. нельзя написать какую-нибудь простенькую функцию, определить для нее максимальную оптимизацию и наслаждаться, при условии, что остальной проект выполнен без оптимизации.
zltigo
Цитата(Dopler @ Feb 9 2007, 13:40) *
Я имел в виду, что любимой избранной функции нельзя задать жестко уровень оптимизации. Т.е. нельзя написать какую-нибудь простенькую функцию, определить для нее максимальную оптимизацию и наслаждаться, при условии, что остальной проект выполнен без оптимизации.

1. Никто не мешает для этого конкретного файла задавать собственные критерии оптимизации
( или вообще не задавать) и все разруливать прагмами.
2. А вообще все "простенькие" функции в отдельный файл и тогда как угодно и как нужно и без прагм.
3. А какого черта проекты вообще без оптимизации собирать - для того что-бы написанный левой ногой код как-то работал? За десятки лет на добром десятке компиляторов никогда не делал ничего без оптимизации. Обычно отдельные куски тупо зажимаются по размеру. Большая часть тупо по максимальной скорости и некоторые критические функции - тонкой индивидуальной подстройкой по скорости. Куски кода с разными требованиями к оптимизации в один файл не сваливаются.
Dopler
Вернемся к поиску оптимального кода. Приведенный выше цикл с различными уровнями оптимизации:
Код
// Исходный цикл
for(d = 2000; d !=0; d--)
        __no_operation();


Код
  Оптимизации нет
     22                for(d = 2000; d !=0; d--)
   \   0000000A   FA21               MOVS     R1,#+250
   \   0000000C   C900               LSLS     R1,R1,#+3       ;; #+2000
   \   0000000E   0800               MOVS     R0,R1
   \                     ??main_2:
   \   00000010   0028               CMP      R0,#+0
   \   00000012   02D0               BEQ      ??main_3
     23                  __no_operation();
   \   00000014   C046               Nop      
   \   00000016   401E               SUBS     R0,R0,#+1
   \   00000018   FAE7               B        ??main_2


Код
  С оптимизацией
      22                for(d = 2000; d !=0; d--)
   \   00000010   0C00               MOVS     R4,R1
     23                  __no_operation();
   \                     ??main_2:
   \   00000012   C046               Nop      
   \   00000014   641E               SUBS     R4,R4,#+1
   \   00000016   FCD1               BNE      ??main_2


Очевидно, что второй код предпочтительнее, причем я на asm написал бы именно так. Единственное, что напрягает - это явно лишний Nop, попробуем от него избавится, сохраняя уровень оптимизации.
Код
Посто закоментировал Nop, и весь цикл улетел, попав под нож оптимизатора.
for(d = 2000; d !=0; d--);
//       __no_operation();


Пользуемся советом Сергея, делаем d volatile
Код
       17                int volatile d;
       20                for(d = 2000; d !=0; d--);
   \   00000012   0091               STR      R1,[SP, #+0]
   \   00000014   02E0               B        ??main_2
   \                     ??main_3:
   \   00000016   009C               LDR      R4,[SP, #+0]
   \   00000018   641E               SUBS     R4,R4,#+1
   \   0000001A   0094               STR      R4,[SP, #+0]
   \                     ??main_2:
   \   0000001C   009C               LDR      R4,[SP, #+0]
   \   0000001E   002C               CMP      R4,#+0
   \   00000020   F9D1               BNE      ??main_3


И видим, что весь цикл, где используется d перестает оптимизироваться, более того, d из регистров попадает прямиком в стек, что еще больше раздувает код, отсюда делаю вывод, что без явных причин использовать volatile не стоит.

Поэкспериментировав с различными галочками в оптимизаторе, убедился что без Code Motion цикл не пропадает, но все равно получить код как во втором примере, только без Nop не удалось.
Dopler
А вообще, компилятор действительно не плох, он цепляется за любую мелочь. Например, если 2000 выходит за ранг MOV, он понимает, что можно загрузить 250 и сдвинуть на 4. А вот если пользователь захочет 1999, тогда уж лучше завести константу в памяти. Конечно, про расчет времени выполнения программы по тактам можно забыть.
MALLOY2
Вот что у меня получилось IAR 4.41 оптимизция полная по скорости

Код
void delay(long tmp)
{
while(--tmp);
}



результат
Код
   In segment CODE, align 4, keep-with-next
                void delay(long tmp)
                {
                 while(--tmp);
                  delay:
                  ??delay_0:
                   00000000   010050E2           SUBS     R0,R0,#+1
                   00000004   FDFFFF1A            BNE      ??delay_0
                 }
                   00000008   1EFF2FE1            BX       LR              ;; return



при этом он пытается ее инлайнить всегда, но от этого можно легко избавится при желании
aspID
дабы не открывать новую ветку...

Помогите понять, зачем так сложно:
(time описана как unsigned long)

Код
//   80   time++;
        LDI     R30, time
        LDI     R31, 0
        LD      R16, Z
        LDD     R17, Z+1
        LDD     R18, Z+2
        LDD     R19, Z+3
        SUBI    R16, 255
        SBCI    R17, 255
        SBCI    R18, 255
        SBCI    R19, 255
        ST      Z, R16
        STD     Z+1, R17
        STD     Z+2, R18
        STD     Z+3, R19
IgorKossak
Цитата(aspID @ Mar 19 2008, 09:03) *
дабы не открывать новую ветку...

Помогите понять, зачем так сложно:
(time описана как unsigned long)

Наоборот, это наиболее простой формат машинного представления времени и даты.
unsigned long содержит в себе количество секунд от некоторого базового момента. Из него можно получить текущие время и дату в человеческом формате путём несложных преобразований.
Или Вам не понтравилось как компилятор сделал инкремент?
aspID
Цитата(IgorKossak @ Mar 19 2008, 14:03) *
Наоборот, это наиболее простой формат машинного представления времени и даты.
unsigned long содержит в себе количество секунд от некоторого базового момента. Из него можно получить текущие время и дату в человеческом формате путём несложных преобразований.
Или Вам не понтравилось как компилятор сделал инкремент?

Да, именно это и реализовывал smile.gif
Вот только почему так "широка страна моя..." в смысле почему в такую длинную цепочку развернулся обычный инк...
IgorKossak
Цитата(aspID @ Mar 19 2008, 10:08) *
Да, именно это и реализовывал smile.gif
Вот только почему так "широка страна моя..." в смысле почему в такую длинную цепочку развернулся обычный инк...

А как Вы предполагали проще?
Приведите ассемблерный кусок.
Ну можно было разве что сэкономить регистры и не грузить весь лонг, а обрабатывать по одному байту, но экономии ни по длине кода ни по скорости в этом случае всё равно не будет.
MrYuran
Цитата(Dopler @ Feb 9 2007, 15:36) *
Просто для совсем маленьких функций накладные расходы по вызову и передаче параметров неприятны. Если бы можно было бы функцию на asm сделать inline, было бы здорово.

А функцию на си можно инлайн объявить?
Если да, то что мешает внутри написать
asm("something asm text");

Я вот чё-то только __no_inline нашёл, то есть наоборот, запрет инлайна
IgorKossak
Цитата(MrYuran @ Mar 19 2008, 10:37) *
А функцию на си можно инлайн объявить?
Если да, то что мешает внутри написать
asm("something asm text");

Я вот чё-то только __no_inline нашёл, то есть наоборот, запрет инлайна

#pragma inline=forced
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.