Чому компілятор GCC пропускає якийсь код?


9

Я не можу зрозуміти, чому компілятор GCC вирізає частину мого коду, хоча він зберігає абсолютно той самий у сусідньому?

Код С:

#define setb_SYNCO do{(PORTA|= (1<<0));} while(0);

ISR(INT0_vect){
    unsigned char i;

    i = 10;
    while(i>0)i--;   // first pause - omitted

    setb_SYNCO;
    setb_GATE;
    i=30;
    clrb_SYNCO;
    while(i>0)i--;  // second pause - preserved
    clrb_GATE;
}

Відповідна частина LSS (файл асемблера, створений компілятором):

ISR(INT0_vect){
  a4:   1f 92           push    r1
  a6:   0f 92           push    r0
  a8:   0f b6           in  r0, 0x3f    ; 63
  aa:   0f 92           push    r0
  ac:   11 24           eor r1, r1
  ae:   8f 93           push    r24
    unsigned char i;

    i = 10;
    while(i>0)i--;

    setb_SYNCO;
  b0:   d8 9a           sbi 0x1b, 0 ; 27
    setb_GATE;
  b2:   d9 9a           sbi 0x1b, 1 ; 27
    i=30;
    clrb_SYNCO;
  b4:   d8 98           cbi 0x1b, 0 ; 27
  b6:   8e e1           ldi r24, 0x1E   ; 30
  b8:   81 50           subi    r24, 0x01   ; 1
    while(i>0)i--;
  ba:   f1 f7           brne    .-4         ; 0xb8 <__vector_1+0x14>
    clrb_GATE;
  bc:   d9 98           cbi 0x1b, 1 ; 27
}
  be:   8f 91           pop r24
  c0:   0f 90           pop r0
  c2:   0f be           out 0x3f, r0    ; 63
  c4:   0f 90           pop r0
  c6:   1f 90           pop r1
  c8:   18 95           reti

Я можу припустити, що компілятор з'ясував, що такий код є фіктивним і вирізає його, але чому він зберігає той самий у кінці коду?

Чи є якісь інструкції компілятора, щоб запобігти такій оптимізації?


1
Ви також можете сказати компілятору не оптимізувати жодну функцію, можливо, варто спробувати цей метод зі своїм ISR. Дивіться це запитання про stackoverflow.
Володимир Крейвер

2
Привіт Романе, я додав тег "c" до вашого питання про видалення atmega. Мені довелося видалити один тег, оскільки існує обмеження (п’ять), і коли ви задаєте питання щодо коду, додаючи назву мови як тег, це чудово, оскільки весь код (Q & As) виділяється.
Володимир Креверо

5
Взагалі кажучи, мови вищого рівня (наприклад, C) явно розроблені так, щоб вони не були пов'язані у взаємозв'язку 1: 1 з отриманою ними збіркою. Якщо вам потрібно порахувати інструкції, щоб отримати правильний час, завжди потрібно покладатися на збірку (як це робилось у деяких відповідях). Вся суть мов високого рівня полягає в тому, що компілятор має деяку свободу, щоб зробити ваш код швидше-міцнішим-кращим, ніж раніше. Деталі, такі як розподіл реєстру та передбачення гілок, набагато краще залишити компілятору ... за винятком таких випадків, коли ви, програміст, точно знаєте інструкції, які ви хочете.
Корт Аммон

5
Питання краще, чому GCC не оптимізує обидві ці петлі?
Ільмарі Каронен

5
Gcc спочатку розгортає цикл і лише потім помічає, що відповідний код марний. Якщо розмір циклу становить 30, розгортання було б нерозумним, і gcc не робить цього. На більш високому рівні оптимізації обидва оптимізовані у відсутності.
Марк Глісс

Відповіді:


9

Оскільки в одному коментарі ви заявляєте, що "кожна галочка процесора гідна", я пропоную використовувати деякі вбудовані збори, щоб зробити петлю затримок так, як вам потрібно. Це рішення перевершує різні volatileабо -O0тому, що дає зрозуміти, у чому полягає ваш намір.

unsigned char i = 10;
__asm__ volatile ( "loop: subi    %0, 0x01\n\t"
                   "      brne    loop"
                   : "+rm" (i)
                   : /* no inputs */
                   : /* no dirty registers to decleare*/);

Це повинно зробити трюк. Мінлива річ є, щоб сказати компілятору "Я знаю, що це нічого не робить, просто зберіг це і довіри мені". Три твердження "ASM" досить зрозумілі, ви можете використовувати будь-який регістр замість r24, я вважаю, що компілятору подобаються нижчі регістри, тож ви можете використовувати високий. Після першого :ви повинні перерахувати вихідні (читання та записування) c змінних, а таких немає, після другого :слід перерахувати вхідні (ronly) c змінні, знову ж таки, немає жодних, а третій параметр - це розділений комами список модифікованих регістрів , у цьому випадку r24. Я не впевнений, чи слід включати також реєстр статусу, оскільки ZEROпрапор, звичайно, змінюється, я не включав його.

відредагувати відредаговану відповідь на вимогу ОП. Деякі примітки.

"+rm"Перед тим (i)означає , що ви випускають компілятор вирішив місце я в м Еморі або в г egister. Це в хорошій справі в більшості випадків, оскільки компілятор може оптимізувати краще, якщо він безкоштовний. У вашому випадку я вважаю, що ви хочете зберегти лише обмеження r, щоб змусити i бути реєстром.


Схоже, це річ, яка мені справді потрібна. Але чи можете ви змінити свою відповідь, щоб прийняти будь-яку cзмінну замість буквальної, про яку 10я згадував у початковій відповіді? Я намагаюсь прочитати посібники GCC щодо правильного використання конструкції ASM, але зараз це для мене трохи затьмарено. Буду дуже вдячний!
Роман Матвєєв

1
@RomanMatveev редагував, як ви просили
Vladimir Cravero

13

Ви можете спробувати змусити цикл насправді щось зробити. Як стверджує, компілятор цілком слушно говорить: "Цей цикл нічого не робить - я його позбудусь".

Тож ви можете спробувати конструкцію, яку я часто використовую:

int i;
for (i = 0; i < 10; i++) {
    asm volatile ("nop");
}

Примітка: не всі цілі компілятора gcc використовують один і той же синтаксис вбудованого вбудованого тексту - можливо, вам знадобиться налаштувати його для своєї цілі.


Ваше рішення здається набагато більш елегантним, ніж моє ... можливо, моє краще, коли потрібна кількість циклів PRECISE? Я маю на увазі, ціле річ не гарантується певним чином складено, чи не так?
Володимир Крейвер

8
Простий факт використання C означає, що ви не можете гарантувати цикли. Якщо вам потрібен точний підрахунок циклу, то єдиний шлях - ASM. Я маю на увазі, що у вас є різні терміни з циклом C, якщо у вас включено -ролик циклу, ніж якщо ви цього не зробите, і т. Д.
Majenko

Так, я думав. Коли ви робите затримки HW з досить високими значеннями i (100 або більше), я думаю, що ваше рішення дає практично однакові результати, підвищуючи читабельність.
Володимир Крейвер

6

Так, ви можете це припустити. Якщо ви оголосите змінну i як мінливу, ви скажете компілятору не робити оптимізацію на i.


1
На мою думку, це не зовсім вірно.
Володимир Крейвер

1
@VladimirCravero, що ти маєш на увазі під цим? Чи можете ви зрозуміти більше?
Роман Матвєєв

2
Що я мав на увазі, що я не був би таким впевненим у тому, що робить компілятор. Оголошення змінної мінливою говорить компілятору, що він може змінитися десь в іншому місці, тому він дійсно повинен зробити це в той час, як.
Володимир Крейвер

1
@ Роман Матвєєв, register unsigned char volatile i __asm__("r1");можливо?
a3f

2
Оголошення iмінливим вирішує все. Це гарантується стандартом C 5.1.2.3. Відповідний компілятор не повинен оптимізувати ці петлі, якщо він iє нестабільним. На щастя, GCC є відповідним компілятором. На жаль, існує багато потенційних компіляторів С, які не відповідають стандарту, але це не має значення для цього конкретного питання. Вбудований асемблер абсолютно не потрібен.
Лундін

1

Після першої петлі i- константа. Ініціалізація iта цикл нічого не роблять, однак створюють постійне значення. Ніщо в стандарті не вказує, що цей цикл повинен бути складений як є. Стандарт також нічого не говорить про терміни. Скомпільований код повинен вести себе так, ніби цикл був присутній, і він це робить. Ви не можете достовірно сказати, що ця оптимізація проводилася за стандартом (терміни не враховуються).

Другу петлю також слід видалити. Я вважаю, що це помилка (або відсутня оптимізація), що це не так. Після циклу iпостійний нуль. Код слід замінити iна нуль.

Я думаю, що GCC тримається iнавколо виключно з тієї причини, що ви виконуєте (непрозорий) доступ до порту, може вплинути i.

Використовуйте

asm volatile ("nop");

щоб підманути GCC повірити, що цикл щось робить.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.