Почему компилятор GCC пропускает некоторый код?


9

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

Код C:

#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. Мне пришлось удалить один тег, поскольку существует ограничение (пять), и при задании вопроса, связанного с кодом, добавление имени языка в качестве тега - это замечательно, поскольку весь код (вопросы и ответы) выделяется.
Владимир Краверо

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*/);

Это должно делать свое дело. Изменчивая вещь заключается в том, чтобы сказать компилятору: «Я знаю, что это ничего не делает, просто держи это и поверь мне». Три ассемблерных «утверждения» вполне понятны, вы можете использовать любой регистр вместо r24, я считаю, что компилятору нравятся младшие регистры, поэтому вы можете захотеть использовать старшие. После первого :вы должны перечислить выходные (чтение и запись) переменные c, а их нет, после второго :вы должны перечислить входные (ronly) переменные c, опять же, их нет, а третий параметр - разделенный запятыми список модифицированных регистров. в данном случае р24. Я не уверен, стоит ли вам включать регистр статуса, поскольку ZEROфлаг, конечно, меняется, я его не включал.

отредактируйте отредактированный ответ как запрос OP Некоторые заметки.

"+rm"Перед тем (i)означает , что вы выпускающих компилятор решил место я в м Эмори или в г egister. В большинстве случаев это хорошо, так как компилятор может оптимизировать лучше, если он бесплатный. В вашем случае я считаю, что вы хотите оставить только ограничение r, чтобы заставить i быть регистром.


Похоже, это то, что мне действительно нужно. Но не могли бы вы изменить свой ответ так, чтобы он принимал любую cпеременную вместо литерала, который 10я упоминал в исходном ответе? Я пытаюсь прочитать руководства GCC, касающиеся правильного использования конструкции asm, но сейчас она для меня немного неясна. Я был бы очень признателен!
Роман Матвеев

1
@RomanMatveev отредактировал, как вы просили
Владимир Краверо

13

Вы можете попробовать сделать цикл на самом деле сделать что-то. В его нынешнем виде компилятор совершенно правильно говорит: «Этот цикл ничего не делает - я от него избавлюсь».

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

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

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


Ваше решение кажется намного более элегантным, чем мое ... может быть, мое лучше, когда требуется ТОЧНОЕ подсчет циклов? Я имею в виду, что целые вещи не гарантированно компилируются определенным образом, не так ли?
Владимир Краверо

8
Тот факт, что он использует C, означает, что вы не можете гарантировать циклы. Если вам нужен точный подсчет циклов, тогда ASM - единственный путь. Я имею в виду, что вы получите другое время с циклом C, если у вас включена опция -funroll-loops, чем если вы этого не сделаете и т. Д.
Majenko

Да, я так и думал. При выполнении задержек HW с достаточно высокими значениями i (100 или более), я думаю, ваше решение дает практически те же результаты, улучшая читабельность.
Владимир Краверо

6

Да, вы могли бы предположить это. Если вы объявляете переменную i как volatile, вы говорите компилятору не оптимизировать i.


1
Это не совсем так, по моему мнению.
Владимир Краверо

1
@VladimirCravero, что ты имеешь в виду, говоря это? Не могли бы вы сделать более ясным?
Роман Матвеев

2
Я имел в виду, что не уверен в том, что делает компилятор. Объявление переменной volatile говорит компилятору о том, что она может измениться где-то еще, поэтому она должна сделать это в то время.
Владимир Краверо

1
@ Роман Матвеев register unsigned char volatile i __asm__("r1");может быть?
a3f

2
Объявление iкак изменчивое решает все. Это гарантируется стандартом C 5.1.2.3. Соответствующий компилятор не должен оптимизировать эти циклы, если iон изменчив. К счастью, GCC - соответствующий компилятор. К сожалению, есть много потенциальных C-компиляторов, которые не соответствуют стандарту, но это не имеет отношения к этому конкретному вопросу. Абсолютно не нужен встроенный ассемблер.
Лундин

1

После первого цикла iэто константа. Инициализация iи цикл ничего не делают, кроме как выдают постоянное значение. Ничто в стандарте не указывает, что этот цикл должен быть скомпилирован как есть. Стандарт также ничего не говорит о сроках. Скомпилированный код должен вести себя так, как если бы цикл присутствовал, и это так. Вы не можете достоверно сказать, что эта оптимизация была выполнена по стандарту (время не учитывается).

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

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

использование

asm volatile ("nop");

чтобы заставить GCC поверить, что цикл что-то делает.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.