Nie rozumiem, dlaczego kompilator GCC wycina część mojego kodu, podczas gdy zachowuje absolutnie ten sam w sąsiedztwie?
Kod 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;
}
Odpowiednia część LSS (plik asemblera, stworzony przez kompilator):
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
Mogę założyć, że kompilator domyśli się, że taki kod jest fałszywy i wycina go, ale dlaczego zachowuje ten sam kod na końcu kodu?
Czy są jakieś instrukcje kompilatora, aby zapobiec takiej optymalizacji?
Odpowiedzi:
Ponieważ w jednym komentarzu stwierdzasz, że „każdy takt procesora jest godny”, sugeruję użycie wbudowanego zestawu, aby opóźnienia były zapętlone tak, jak chcesz. To rozwiązanie jest lepsze od różnych
volatile
lub-O0
ponieważ wyjaśnia , jakie masz zamiary.To powinno wystarczyć. Zmienna rzecz polega na tym, aby powiedzieć kompilatorowi: „Wiem, że to nic nie robi, po prostu zachowaj to i zaufaj mi”. Trzy „instrukcje” asm są dość zrozumiałe, możesz użyć dowolnego rejestru zamiast r24, myślę, że kompilator lubi niższe rejestry, więc możesz chcieć użyć wysokiego. Po pierwszym
:
powinieneś wypisać zmienne wyjściowe (odczyt i zapis) c, a nie ma żadnych, po drugim:
powinieneś wypisać zmienne wejściowe (ronly) c, znowu, nie ma żadnych, a trzecim parametrem jest lista oddzielonych przecinkami zmodyfikowanych rejestrów , w tym przypadku r24. Nie jestem pewien, czy powinieneś dołączyć także rejestr statusu, ponieważZERO
flaga oczywiście się zmienia, nie uwzględniłem go.edytuj zredagowaną odpowiedź zgodnie z żądaniem OP. Kilka notatek.
"+rm"
Przed(i)
środkami, które pozwalasz kompilator decydują się na miejsce, w m Emory lub w r egister. W większości przypadków to dobrze, ponieważ kompilator może lepiej zoptymalizować, jeśli jest bezpłatny. W twoim przypadku uważam, że chcesz zachować tylko ograniczenie r, aby zmusić i do bycia rejestrem.źródło
c
zmienną zamiast literału10
, o którym wspomniałem w pierwotnej odpowiedzi? Próbuję przeczytać podręczniki GCC dotyczące właściwego wykorzystania konstrukcji asm, ale dla mnie jest to teraz trochę zaciemnione. Byłbym bardzo wdzięczny!Możesz spróbować zrobić pętlę. W tej chwili kompilator słusznie mówi: „Ta pętla nic nie robi - pozbędę się tego”.
Abyś mógł wypróbować konstrukt, którego często używam:
Uwaga: nie wszystkie cele kompilatora gcc używają tej samej składni wbudowanej - może być konieczne dostosowanie go do celu.
źródło
Tak, możesz to założyć. Jeśli zadeklarujesz zmienną i jako zmienną, powiesz kompilatorowi, aby nie optymalizował na i.
źródło
register unsigned char volatile i __asm__("r1");
może?i
za niestabilne rozwiązuje wszystko. Gwarantuje to norma C 5.1.2.3. Kompilator zgodny nie może optymalizować tych pętli, jeślii
jest niestabilny. Na szczęście GCC jest zgodnym kompilatorem. Niestety istnieje wiele niedoszłych kompilatorów C, które nie są zgodne ze standardem, ale nie ma to znaczenia dla tego konkretnego pytania. Absolutnie nie ma potrzeby wbudowanego asemblera.Po pierwszej pętli
i
jest stała. Inicjalizacjai
i pętla robią tylko stałą wartość. Nic w tym standardzie nie określa, że ta pętla musi być skompilowana w obecnej postaci. Standard nie mówi też nic o czasie. Skompilowany kod musi zachowywać się tak, jakby pętla była obecna i tak jest. Nie można wiarygodnie stwierdzić, że ta optymalizacja została przeprowadzona zgodnie ze standardem (czas nie ma znaczenia).Druga pętla również powinna zostać usunięta. Uważam, że to błąd (lub brak optymalizacji), że tak nie jest. Po pętli
i
jest stała zero. Kod należy zastąpić ustawieniemi
na zero.Myślę, że GCC utrzymuje
i
się tylko z tego powodu, że może to mieć wpływ na (nieprzezroczysty) dostęp do portui
.Posługiwać się
oszukać GCC, aby uwierzył, że pętla coś robi.
źródło