logo elektroda
logo elektroda
X
logo elektroda
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

Jak wymusić użycie INLINE w AVR-GCC zamiast wywołań CALL?

GienekS 22 Sty 2005 17:20 4307 19
  • #1 1161090
    GienekS
    Poziom 32  
    Posty: 1971
    Pomógł: 139
    Ocena: 15
    Jak zmusić kompilator AVR-GCC do wykonania dyrektywy języka C inline Jak do tej pory wszystkie procedury są wywoływane przez CALL
  • #3 1162156
    GienekS
    Poziom 32  
    Posty: 1971
    Pomógł: 139
    Ocena: 15
    Nie chodzi mi o wstawki asemblerowe tylko o dyrektywę inline która ma daną proderę "wstawić" do programu a nie wywołać żeby zaoszczędzić na czasie wywoływania (call) i powrotu (ret).
  • #4 1162184
    BoskiDialer
    Poziom 34  
    Posty: 1530
    Pomógł: 353
    Ocena: 42
    hm.. "Jak zmusić kompilator AVR-GCC do wykonania dyrektywy języka C inline"
    nie do końca to rozumiem.. w C pisze aktualnie mały kernel dla komputera.. (cel-zabawa) i teraz jestem w temacie inline.. zgodnie ze standardem ansi C (czy jak mu tam): avr-gcc powinien również używać tej samej składni..

    aby funkcja była inline i była przez kompilator wstawiona pisze się np:
    
    [b]inline[/b] void Funkcja(void)
    {
      ...
    }
    
    void main(void)
    {
      Funkcja();
    }
    
  • #5 1162275
    Zbych_
    Poziom 25  
    Posty: 456
    Pomógł: 81
    Ocena: 6
    BoskiDialer napisał:
    aby funkcja była inline i była przez kompilator wstawiona pisze się np:
    
    [b]inline[/b] void Funkcja(void)
    {
      ...
    }
    
    void main(void)
    {
      Funkcja();
    }
    


    Dodat tylko, że funkcja taka musi być w tym samy pliku źródłowym. Przynajmniej mi nie udało się zmusic gcc, żeby wstawił funkcję inline gdy znajdowała się ona w innym pliku.
  • #6 1162393
    GienekS
    Poziom 32  
    Posty: 1971
    Pomógł: 139
    Ocena: 15
    Mnie nawet jek się znajduje w tym samym pliku też zawsze jest tylko "wywoływana". Czy może w GCC trzeba inaczej zadeklarować ?
  • #7 1164211
    Zbych_
    Poziom 25  
    Posty: 456
    Pomógł: 81
    Ocena: 6
    GienekS napisał:
    Mnie nawet jek się znajduje w tym samym pliku też zawsze jest tylko "wywoływana"


    Ja to zrobiłem tak:
    #include <avr/io.h>
    
    
    inline void Send(uint8_t data){
    
    	PORTA=data+10;
    
    }
    
    
    int main(void){
    uint8_t a;
    
    	a=10;
    	Send(a);
    	while(1);
    }


    I efekt kompilacji jest taki:
    
    int main(void){
      d0:	cf ef       	ldi	r28, 0xFF	; 255
      d2:	d0 e1       	ldi	r29, 0x10	; 16
      d4:	de bf       	out	0x3e, r29	; 62 
      d6:	cd bf       	out	0x3d, r28	; 61
    
    
      d8:	84 e1       	ldi	r24, 0x14	; 20  <- tu jest inline !!!
      da:	8b bb       	out	0x1b, r24	; 27  <-
    
    uint8_t a;
    
    	a=10;
    	Send(a);
    	while(1);
      dc:	ff cf       	rjmp	.-2      	; 0xdc
    

    Czyli wszystko działa (nawet zoptymalizowane zostało dodawanie 10+10 :-)). Do testów użyłem gcc z pakietu WinAvr z połowy zeszłego roku.
  • #8 1165114
    GienekS
    Poziom 32  
    Posty: 1971
    Pomógł: 139
    Ocena: 15
    Ja mam coś takiego:
    #include "io.h"
    
    /* Ustawienie lini portów do komunikacji
    */
    void Init_IO (void)
    {
     DDRC = 0x0f;
    }
    /* Procedura nadaje bajt do linijki */
    
    void SendBajt (u08 bajt)
    {u08 j = 1;
    	do {
    	   if (bajt & j)
    	   	  Q0 = 1;
    	   else
    	   	   Q0 = 0;
    	   CLK = 1;	  
    	   j <<= 1;
    	   CLK = 0;
    	} while (j>0);
    }
    /* Procedura nadaje blok do linijki */
    
    void SendBlok (u08* adres, u08 ilosc)
    {
     while (ilosc) {
     	   SendBajt (*adres++);
     	   ilosc--;
     }
     Strob ();
    }
    /* Strob przepisuje dane w 4094 */
    
    inline void Strob (void)
    {
     	 STR = 1;
     	 asm ("nop\n\t"
    	  	  "nop\n\t");
     	 STR = 0;
     	 OE = 1; 		 // włączenie 4094
    }

    a wynik jest taki:
    /* Procedura nadaje blok do linijki */
    
    void SendBlok (u08* adres, u08 ilosc)
    {
      b2:	0f 93       	push	r16
      b4:	1f 93       	push	r17
      b6:	cf 93       	push	r28
      b8:	8c 01       	movw	r16, r24
      ba:	c6 2f       	mov	r28, r22
     while (ilosc) {
      bc:	66 23       	and	r22, r22
      be:	39 f0       	breq	.+14     	; 0xce
     	   SendBajt (*adres++);
      c0:	f8 01       	movw	r30, r16
      c2:	81 91       	ld	r24, Z+
      c4:	8f 01       	movw	r16, r30
      c6:	0e 94 4c 00 	call	0x98
     	   ilosc--;
      ca:	c1 50       	subi	r28, 0x01	; 1
      cc:	c9 f7       	brne	.-14     	; 0xc0
     }
     Strob ();
      ce:	0e 94 6d 00 	call	0xda
    }
      d2:	cf 91       	pop	r28
      d4:	1f 91       	pop	r17
      d6:	0f 91       	pop	r16
      d8:	08 95       	ret
    
    

    Czyli cały czas jest ta procedura wywoływana a nie inline
    Dlaczego !!!
  • #9 1165221
    BoskiDialer
    Poziom 34  
    Posty: 1530
    Pomógł: 353
    Ocena: 42
    hm.. a zamiast:
    
    inline void Strob (void) 
    { 
         STR = 1; 
         asm ("nop\n\t" 
              "nop\n\t"); 
         STR = 0; 
         OE = 1;        // włączenie 4094 
    }

    wpisz:
    
    #define Strob() { STR = 1; \
                      asm ("nop\n\t" \
                           "nop\n\t"); \
                      STR = 0; \
                      OE = 1; }
    

    to musi być wstawione.. :)

    możliwe że kompilator stwierdza iż funkcja jest inline, ale jest zadługa więc ją optymalizuje i robi nie inline... inaczej jest z #define.. to jest zadanie dla preprocesora a więc kompilator otrzyma już wstawiony kod :)
  • #10 1168772
    Zbych_
    Poziom 25  
    Posty: 456
    Pomógł: 81
    Ocena: 6
    GienekS napisał:
    Czyli cały czas jest ta procedura wywoływana a nie inline
    Dlaczego !!!


    Dziwne, że to w ogóle się kompiluje. Bo jeśli nie masz prototypu funkcji Strob to powinieneś przynajmniej umieścić ją nad funkcją, która jako pierwsza wywołuje Strob.
  • #11 1170267
    BoskiDialer
    Poziom 34  
    Posty: 1530
    Pomógł: 353
    Ocena: 42
    Zbych_: prototyp funkcji robi się wtedy kiedy do funkcji odwołujemy się nad jej właściwą implementacją.. np kod:
    
    void Sprawdz(void)
    {
      PobierzDane();
    }
    
    void PobierzDane(void)
    {
    }
    

    niezadziała bo kompilator podczas przetważania funkcji Sprawdz() niezna jeszcze funkcji PobierzDane.. prototyp informuje kompilator że taka funkcja istnieje, ale niebyła jeszcze przetwożona.. jeśli w wyższym kodzie zamienić miejscami obie funkcje to całość sie skompiluje bez prototypu..
  • #12 1170776
    GienekS
    Poziom 32  
    Posty: 1971
    Pomógł: 139
    Ocena: 15
    Prototyp jest umieściłem w pliku nagłówkowym "io.h" i mimo to też nie jest inline
  • #13 1171999
    Zbych_
    Poziom 25  
    Posty: 456
    Pomógł: 81
    Ocena: 6
    BoskiDialer napisał:
    Zbych_: prototyp funkcji robi się wtedy kiedy do funkcji odwołujemy się nad jej właściwą implementacją.. np kod:

    Przypatrz się programowi powyżej mojej odpowiedzi (24 Sty 2005 13:40). Właśnie taka sytuacja tam występuje.
  • #14 5953688
    Freddie Chopin
    Specjalista - Mikrokontrolery
    Posty: 13336
    Pomógł: 1712
    Ocena: 870
    da sie wymusic inline funkcji, ktora jest najnormalniej w swiecie zdefiniowana w INNYM pliku? jesli deklaracje i definicje funkcje wzbogace o 'inline', to kompilator stwierdza przy kompilacji modulu wywolujacego, ze:

    Cytat:
    undefined reference to `io_init'


    pomaga umieszczenie ciala funkcji w pliku naglowkowym, ale... ale to kiepskie rozwiazanie, wolalbym gdyby funkcja taka mogla byc po prostu w innym pliku.

    4\/3!!
  • #15 5953857
    BoskiDialer
    Poziom 34  
    Posty: 1530
    Pomógł: 353
    Ocena: 42
    Freddie Chopin: Funkcja inline jest wstawiana tylko w obrębie aktualnego modułu (kiedy to kompilator posiada funkcję w postaci nie skompilowanej i może ją w miejscu zoptymalizować do funkcji wywołującej). Jeśli funkcja nie jest statyczna, to mimo to pojawi się w ciele modułu jedna kopia ogólna, którą można wywoływać z zewnętrznych modułów (w nagłówku można pominąć inline, gdyż i tak zostało by zignorowane) lub na potrzeby aktualnego modułu (jeśli uzyskujemy adres funkcji, to pojawi się kod nawet w przypadku statycznej). Moduły są łączone przez linker, kiedy funkcje już są zbudowane, tak więc jedyna możliwość, to dostarczyć do każdego modułu przez nagłówek ciało funkcji, do której dodano atrybut always_inline (lub inline+static). Nie powinno się pobierać adresu tej funkcji w żadnym module (dopuszczalne maksimum to 1 moduł), gdyż inaczej musi zostać wkompilowana postać ogólna, co spowoduje konflikt nazw, dodanie static spowoduje posiadanie przez każdy moduł osobnej kopii funkcji, co prowadzi do nierówności wskaźników uzyskanych przez różne moduły oraz do wzrostu rozmiaru kodu.

    Umieszczenie ciała funkcji w nagłówku jest stosowane np przez util/delay.h dla avr'ów - funkcja jest rozwijana i optymalizowana w miejscu.
  • #17 5954107
    bobbyAIR
    Poziom 20  
    Posty: 267
    Pomógł: 41
    Ocena: 6
    Pamiętajcie że inline to zalecenie dla kompilatora mówiące, że jeśli nie ma przeciwskazań to powinien rozwinąć ją w linii. Jeśli chce się do tego zmusić kompilator należy jeszcze dodatkowo o tym powiedzieć
    
    inline int f(int) __attribute__((always_inline))
    {
    .......
    }
    

    Oczywiście wspomniane przez kolegów ograniczenia co do modułu dalej mają tu zastosowanie.

    EDIT: Widzę, że BoskiDialer już o tym wspomaniał - ehhh niedoczytanie
  • #18 5954682
    Dr.Vee
    VIP Zasłużony dla elektroda
    Posty: 1784
    Pomógł: 307
    Ocena: 76
    http://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html

    Opcja -finline-limit=n zmienia liczbę abstrakcyjnych instrukcji, którą może mieć funkcja, żeby wciąż była uznawana przez kompilator jako kandydat do operacji rozwinięcia w miejscu wywołania.

    Poza tym jest kilka parametrów które kontrolują ten proces - wszystkie są wymienione na powyższej stronie.

    Pozdrawiam,
    Dr.Vee
  • #19 5954968
    Zbych_
    Poziom 25  
    Posty: 456
    Pomógł: 81
    Ocena: 6
    Dr.Vee napisał:
    Poza tym jest kilka parametrów które kontrolują ten proces - wszystkie są wymienione na powyższej stronie.

    Któraś z poprzednich wersji gcc (chyba 4.2) miała jakieś straszne opory przed inline'owaniem funkcji jeśli wybrano optymalizację na rozmiar. Manipulowanie parametrami niewiele pomagało. W wersji 4.3 było to już poprawione.
  • #20 5955528
    Dr.Vee
    VIP Zasłużony dla elektroda
    Posty: 1784
    Pomógł: 307
    Ocena: 76
    Dokumentacja wyraźnie mówi, że znaczenie parametrów do optymalizacji może się zmienić miedzy wersjami gcc. Jak już ktoś optymalizuje projekt do tak niskiego poziomu jak wybranie długości funkcji do rozwiajania w miejscu wykonania, to 2x się zastanowi przed instalacją nowej wersji kompilatora :)

    Pozdrawiam,
    Dr.Vee

Podsumowanie tematu

✨ Dyskusja dotyczy wymuszenia przez kompilator AVR-GCC rozwinięcia funkcji jako inline zamiast standardowego wywołania CALL. Standardowa dyrektywa inline w C powinna sugerować kompilatorowi wstawienie kodu funkcji bez wywołania, co przyspiesza wykonanie. Jednak w praktyce AVR-GCC inline działa tylko, gdy funkcja jest zdefiniowana w tym samym pliku źródłowym, a nawet wtedy kompilator może nie wstawić jej inline, jeśli uzna to za nieoptymalne. Aby wymusić inline, można użyć atrybutu __attribute__((always_inline)) wraz z deklaracją inline i statyczną funkcją. Funkcje inline zdefiniowane w osobnych plikach są problematyczne, ponieważ kompilator nie ma dostępu do ich ciała podczas kompilacji modułu wywołującego. Alternatywnie, można stosować makra (#define) do wstawiania kodu bezpośrednio przez preprocesor. Optymalizacja inline jest również zależna od ustawień kompilatora, takich jak -finline-limit=n, oraz wersji GCC, które różnie traktują inline przy optymalizacji na rozmiar. Prototypy funkcji inline powinny być umieszczone w plikach nagłówkowych, ale sama deklaracja inline musi być obecna w miejscu definicji funkcji. Linker łączy moduły po kompilacji, więc inline działa tylko w obrębie pojedynczego modułu.
Wygenerowane przez model językowy.
REKLAMA