Dlaczego kompilator nie używa bezpośrednio LSR

10

Cześć. Pracowałem nad projektem przy użyciu Arduino Uno (więc ATmega328p), w którym czas jest dość ważny, dlatego chciałem sprawdzić, w które instrukcje kompilator konwertuje mój kod. I tam mam uint8_tprzesunięcie o jeden bit w prawo przy każdej iteracji data >>= 1i wydaje się, że kompilator przetłumaczył to na 5 instrukcji ( datajest w r24):

mov     r18, r24
ldi     r19, 0x00
asr     r19
ror     r18
mov     r24, r18

Ale jeśli zajrzę do dokumentacji zestawu instrukcji, zobaczę instrukcję, która robi dokładnie to: lsr r24

Czy coś pomijam lub dlaczego kompilator też tego nie używa? Rejestry r18i r19nie są używane nigdzie indziej.

Używam Ardunio, ale jeśli mam rację, używa zwykłego avr-gcckompilatora. To jest kod (przycięty), który generuje sekwencję:

ISR(PCINT0_vect) {
    uint8_t data = 0;
    for (uint8_t i = 8; i > 0; --i) {
//        asm volatile ("lsr %0": "+w" (data));
        data >>= 1;
        if (PINB & (1 << PB0))
            data |= 0x80;
    }
    host_data = data;
}

O ile widzę, Ardunino IDE korzysta z kompilatora gcc AVR dostarczonego przez system w wersji 6.2.0-1.fc24. Oba są instalowane za pomocą menedżera pakietów, więc powinny być aktualne.

xZise
źródło
1
Zespół wydaje się nie odpowiadać kodowi C.
Eugene Sh.
Cóż, skompilowałem go za pomocą Ardunio IDE, a następnie użyłem avr-objdumpw pliku elfa… Co to wydaje się nie odpowiadać?
xZise,
1
@Eugene Sh .: to nie zgadza się z kodem C. Odpowiada tylko liniidata >>= 1;
Curd
1
Jest to jeden z przypadków, w których „użycie zmiany zamiast podziału” jest niewłaściwą radą. Jeśli zrobisz / = 2 zamiast tego kompilator wygeneruje lsr r24; (wskazówka: wypróbuj eksploratora gcc, aby pobawić się generowaniem kodu asm)
PlasmaHH
Jaki kompilator? Jaki procesor? Naprawdę powinno być oczywiste, że jest to niezbędna informacja, aby pytanie miało sens.
Olin Lathrop,

Odpowiedzi:

18

Zgodnie ze specyfikacją języka C każda wartość, której rozmiar jest mniejszy niż rozmiar int(zależy od konkretnego kompilatora; w twoim przypadku intma 16 bitów szerokości), zaangażowana w dowolną operację (w twoim przypadku >>), jest przekazywana do wartości intprzed operacją.
Takie zachowanie kompilatora nazywa się promocją liczb całkowitych .

I właśnie to zrobił kompilator:

  • r19 = 0 to MSByte promowanej liczby całkowitej wartości data.
  • (r19, r18) reprezentuje całkowitą promowaną liczbę całkowitą, dataktóra jest następnie przesuwana w prawo o jeden bit o asr r19i ror 18.
  • Wynik jest następnie domyślnie rzutowany z powrotem na twoją uint8_tzmienną data:
    mov r24, r18tj. MSByte w r19 jest wyrzucany.

Edycja:
Oczywiście kompilator może zoptymalizować kod.
Próbując odtworzyć problem, stwierdziłem, że przynajmniej w przypadku avr-gcc w wersji 4.9.2 problem nie występuje. Tworzy bardzo wydajny kod, tzn. Linia C data >>= 1;jest kompilowana do jednej lsr r24instrukcji. Więc może używasz bardzo starej wersji kompilatora.

Twaróg
źródło
2
Nie jest to całkowite marnotrawstwo, ponieważ czasami potrzebny jest niezoptymalizowany kod do debugowania na poziomie asemblera. Jesteś bardzo zadowolony, jeśli masz niezoptymalizowany kod.
Curd
3
Jeśli dobrze pamiętam, -mint8 jest flagą, aby liczby całkowite były 8-bitowe. Ma to jednak wiele niepożądanych efektów ubocznych. Przepraszam, nie mogę sobie przypomnieć, jakie były teraz, ale nigdy nie użyłem flagi z ich powodu. Wiele lat temu spędziłem dużo czasu porównując avr-gcc z komercyjnym kompilatorem.
Jon
1
Och, zgadza się, standard C wymaga liczb całkowitych co najmniej 16-bitowych, więc użycie -mint8 psuje wszystkie biblioteki.
Jon
9
Nigel Jones powiedział w „Efektywnym kodzie C dla 8-bitowych mikrokontrolerów” coś w stylu: „... Reguły promocji liczby całkowitej C są prawdopodobnie najbardziej ohydną zbrodnią popełnianą przeciwko tym z nas, którzy pracują w 8-bitowym świecie” ...
Dirceu Rodrigues Jr
1
@Jonas Wielicki: najlepszym rozwiązaniem tego problemu jest użycie lepszego kompilatora. Np. Z avr-gcc w wersji 4.9.2 nie mogę odtworzyć problemu: dla linii kodu C d >>= 1;otrzymuję tylko jedną lsr r24instrukcję. Może xZise używa bardzo starej wersji kompilatora.
Curd