avr-gcc i strasznie długie pętle :/

Dec 27, 2005 12 Replies

Witam!



Niby prosta rzecz:



void test1(void) { while( bit_is_clear (PIND, 4 ) ); }



void test2(void) { while( true ) { if(bit_is_set (PIND, 4 )) break; } }



void test3(void) { for(;;) { if(bit_is_set (PIND, 4 )) break; } }



A efekty co najmniej tragiczne:


00000084 <_Z5test1v>: 84: 21 e0 ldi r18, 0x01 ; 1 86: 30 e0 ldi r19, 0x00 ; 0 88: 80 b3 in r24, 0x10 ; 16 8a: 99 27 eor r25, r25 8c: 92 95 swap r25 8e: 82 95 swap r24 90: 8f 70 andi r24, 0x0F ; 15 92: 89 27 eor r24, r25 94: 9f 70 andi r25, 0x0F ; 15 96: 89 27 eor r24, r25 98: 82 27 eor r24, r18 9a: 93 27 eor r25, r19 9c: 80 fd sbrc r24, 0 9e: f4 cf rjmp .-24 ; 0x88 a0: 08 95 ret


000000a2 <_Z5test2v>: a2: 80 b3 in r24, 0x10 ; 16 a4: 99 27 eor r25, r25 a6: 92 95 swap r25 a8: 82 95 swap r24 aa: 8f 70 andi r24, 0x0F ; 15 ac: 89 27 eor r24, r25 ae: 9f 70 andi r25, 0x0F ; 15 b0: 89 27 eor r24, r25 b2: 81 70 andi r24, 0x01 ; 1 b4: 90 70 andi r25, 0x00 ; 0 b6: 88 23 and r24, r24 b8: a1 f3 breq .-24 ; 0xa2 ba: 08 95 ret


000000bc <_Z5test3v>: bc: 80 b3 in r24, 0x10 ; 16 be: 99 27 eor r25, r25 c0: 92 95 swap r25 c2: 82 95 swap r24 c4: 8f 70 andi r24, 0x0F ; 15 c6: 89 27 eor r24, r25 c8: 9f 70 andi r25, 0x0F ; 15 ca: 89 27 eor r24, r25 cc: 81 70 andi r24, 0x01 ; 1 ce: 90 70 andi r25, 0x00 ; 0 d0: 88 23 and r24, r24 d2: a1 f3 breq .-24 ; 0xbc d4: 08 95 ret


Po co te wszystkie swapy/etc ?



Spodziewałbym się prymitywnej pętli z testowaniem bitu. A dostaje stertę kodu, który nic sensownego nie wnosi ...



gcc 3.4.3, kompilacja z -O2 i -O3 daje takie same efekty.



Zaznaczam, że samo testowanie bitów kompiluje się do pojedynczej instrukcji. Sama pętla while(true); kompiluje sie do prostackiego rjmp .-2. Dopiero zbitek z warunkiem opuszczenia produkuje takie nieoptymale fukcje.



Czy to wina GCC czy może moja ?


A co to sa owe bit_is_set/clear?

Sebastian Bialy przemówił ludzkim głosem:

[...]
[...]

U mnie przy kompilacji z optymalizacją rozmiaru (-Os) wychodzi:

while( 1 ) { if(bit_is_set (PIND, 4 )) break; 96: 84 9b sbis 0x10, 4 ; 16 98: fe cf rjmp .-4 ; 0x96 }

avr-libc-user-manual

IO register bit manipulation #define bit_is_set(sfr, bit) (_SFR_BYTE(sfr) & _BV(bit)) #define bit_is_clear(sfr, bit) (!(_SFR_BYTE(sfr) & _BV(bit))) #define loop_until_bit_is_set(sfr, bit) do { } while (bit_is_clear(sfr, bit)) #define loop_until_bit_is_clear(sfr, bit) do { } while (bit_is_set(sfr, bit))

W zasadzie kompiluje się do tego samego co PORT&(1<<BIT) etc.

Damn!

Jaką masz wersję gcc ? Jakie bym -Ox nie podawał, zawsze jest źle (choć nie zawsze tak samo). -Os też nic nie daje, dlaej kod jest strasznie rozwlekły.

Sebastian Bialy przemówił ludzkim głosem:

Configured with: ../gcc-3.4.3/configure --prefix=m:/WinAVR

--build=mingw32 --host=mingw32 --target=avr --enable-languages=c,c++

--with-dwarf2 Thread model: single gcc version 3.4.3

Dziwne ....

Czy możesz zrobić dla mnie test: ?

compile.bat:

@avr-gcc -Os -Wall -mmcu=atmega8 -Wall -o test.elf main.c @avr-objdump -h -S test.elf >test.lst

main.c:

#include <avr/interrupt.h>

#include <avr/io.h>

int main(void) { while ( bit_is_set(PORTD, 6) ); return 0; }

Sebastian Bialy przemówił ludzkim głosem:

Wyszło to samo co u ciebie. Dziś mi się już nie chce dochodzić jakie opcje decydują o takim efekcie, proponuję żebyś skorzystał ze standardowego makefile'a (/winavr/sample). Wystarczy jak zmodyfikujesz w nim dwie linie: TARGET i SRC.

Ok, ale to dało mi wiele do myślenia. Poszukam wobec tego co to za brakujące opcje i dziękuje za pomoc !

Coś kręcisz. U mnie wyszło z tego chyba to co powinno (wlepiłem tą funkcję do większego programu dla testu):

0001013e <test1>: 1013e: 84 9b sbis 0x10, 4 ; 16 10140: fe cf rjmp .-4 ; 0x1013e 10142: 08 95 ret

Super optymalnie i tak trzymać. Użyłem avr-gcc 3.4.3 z pakietu WinAVR. Opcje kompilacji (takich standardowo używam):

-Os -std=gnu99 -funsigned-char -funsigned-bitfields -fpack-struct \

-finline -fomit-frame-pointer -fshort-enums -Wall -Werror \

-Wstrict-prototypes -Wformat=2

Podobnie dobre wyniki dały kolejne testy:

0001013e <test2>: 1013e: 84 9b sbis 0x10, 4 ; 16 10140: fe cf rjmp .-4 ; 0x1013e 10142: 08 95 ret
0001013e <test3>: 1013e: 84 9b sbis 0x10, 4 ; 16 10140: fe cf rjmp .-4 ; 0x1013e 10142: 08 95 ret

Czyli wszystkie trzy przykłady robią to samo i to samo dają po kompilacji.

Zerknij na odpowiedź Zbycha, a ja zobacze które parametry kompilacji mogą powodować ten głupi efekt.

Głupia sprawa ....

Okazuje się, że pliki xxx.cpp kompiluja się bez optymalizacji, a pliki xxx.c z optymalizacją.

Poz mianie mojego z .cpp na .c wszystko zaczęło działać jak powinno.

Niestety ja chce mieć cpp bo ja _chce_ c++ (ze względu na szablony). Może avr-g++ ignoruje -Ox ?

Heh, jak miło pisać w 100% pure C, bez żadnych ++. :-) Trzeba spróbować powalczyć z optymalizerem lub explicite kazać kompilatorowi tylko wykonać fazę pośrednią, a po nim zapuścić optymalizer. Jeżeli avr-gcc przewiduje takie sztuczki (podobne do rozdzielenia preprocesora od właściwego kompilatora a potem asemblera).

Join the Discussion

Have something to add? Share your thoughts — no account required.

Didn't find your answer?

Ask the community — no account required