unsigned int fun1 ( unsigned int a, unsigned int b )
{
return(a+b);
}
unsigned char fun2 ( unsigned int a, unsigned int b )
{
return(a+b);
}
unsigned int fun3 ( unsigned char a, unsigned char b )
{
return(a+b);
}
unsigned char fun4 ( unsigned char a, unsigned char b )
{
return(a+b);
}
zgodnie z oczekiwaniami fun1 to wszystko ints, podobnie jak 16-bitowa matematyka
00000000 <fun1>:
0: 86 0f add r24, r22
2: 97 1f adc r25, r23
4: 08 95 ret
Chociaż technicznie niepoprawny, ponieważ jest 16-bitowym dodatkiem wywoływanym przez kod, nawet niezoptymalizowany ten kompilator usunął adc ze względu na rozmiar wyniku.
00000006 <fun2>:
6: 86 0f add r24, r22
8: 08 95 ret
tak naprawdę nie dziwi mnie promocja, kompilatory nie robiły tego, nie jestem pewien, która wersja sprawiła, że to się zaczęło, natrafiłem na to na początku mojej kariery i pomimo, że kompilatory promowały się nieczynnie (tak jak powyżej), robiłem promocję, mimo że ja kazał mi robić matematykę uchar, nie był zaskoczony.
0000000a <fun3>:
a: 70 e0 ldi r23, 0x00 ; 0
c: 26 2f mov r18, r22
e: 37 2f mov r19, r23
10: 28 0f add r18, r24
12: 31 1d adc r19, r1
14: 82 2f mov r24, r18
16: 93 2f mov r25, r19
18: 08 95 ret
i ideał, wiem, że to 8 bitów, chcę 8-bitowy wynik, więc po prostu powiedziałem, żeby wykonał 8 bitów przez całą drogę.
0000001a <fun4>:
1a: 86 0f add r24, r22
1c: 08 95 ret
Ogólnie rzecz biorąc, lepiej jest dążyć do rozmiaru rejestru, który idealnie jest wielkością (u) int, dla takiego 8-bitowego mcu autorzy kompilatora musieli pójść na kompromis ... Nie należy przyzwyczajać się do używając uchar do matematyki, o której wiesz, że nie potrzebuje więcej niż 8 bitów, tak jak podczas przenoszenia tego kodu lub pisania takiego kodu na procesorze z większymi rejestrami, teraz kompilator musi zacząć maskować i rozszerzać znaki, co niektórzy robią natywnie w niektórych instrukcjach, i inni nie.
00000000 <fun1>:
0: e0800001 add r0, r0, r1
4: e12fff1e bx lr
00000008 <fun2>:
8: e0800001 add r0, r0, r1
c: e20000ff and r0, r0, #255 ; 0xff
10: e12fff1e bx lr
zmuszanie 8 bitów kosztuje więcej. Oszukałem trochę / dużo, potrzebowałbym nieco bardziej skomplikowanych przykładów, aby zobaczyć więcej tego w uczciwy sposób.
EDYCJA na podstawie dyskusji na temat komentarzy
unsigned int fun ( unsigned char a, unsigned char b )
{
unsigned int c;
c = (a<<8)|b;
return(c);
}
00000000 <fun>:
0: 70 e0 ldi r23, 0x00 ; 0
2: 26 2f mov r18, r22
4: 37 2f mov r19, r23
6: 38 2b or r19, r24
8: 82 2f mov r24, r18
a: 93 2f mov r25, r19
c: 08 95 ret
00000000 <fun>:
0: e1810400 orr r0, r1, r0, lsl #8
4: e12fff1e bx lr
bez niespodzianki. Chociaż dlaczego optymalizator zostawił tę dodatkową instrukcję, czy nie możesz użyć ldi na r19? (Znałem odpowiedź, kiedy ją zadałem).
EDYCJA 2
dla avr
avr-gcc --version
avr-gcc (GCC) 4.9.2
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
aby uniknąć złego nawyku lub nie 8-bitowego porównania
arm-none-eabi-gcc --version
arm-none-eabi-gcc (GCC) 7.2.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
najwyraźniej optymalizacja była włączona zajmuje tylko sekundę, aby wypróbować własny kompilator, aby zobaczyć, jak wypada w porównaniu z moimi danymi wyjściowymi, ale w każdym razie:
whatever-gcc -O2 -c so.c -o so.o
whatever-objdump -D so.o
I tak, używanie bajtów do zmiennych wielkości bajtów, z pewnością na avr, pic itp., Pozwoli ci zaoszczędzić pamięć i naprawdę chcesz ją zachować ... jeśli tak naprawdę używasz, ale jak pokazano tutaj, jak najmniej będzie w pamięci, jak najwięcej w rejestrach, więc oszczędności flash wynikają z braku dodatkowych zmiennych, oszczędności pamięci RAM mogą, ale nie muszą być rzeczywiste.