Problem optymalizacji można uzyskać z tego kodu jest:
08000328 <mul_test01>:
8000328: f04f 5000 mov.w r0, #536870912 ; 0x20000000
800032c: 4770 bx lr
800032e: bf00 nop
kod robi coś zrobić wykonawczego więc optymalizator może po prostu obliczyć ostateczną odpowiedź.
to:
.thumb_func
.globl mul_test02
mul_test02:
smull r2,r3,r0,r1
mov r0,r3
bx lr
nazywa się to:
c = mul_test02(0x40000000,0x40000000);
daje 0x10000000
UMULL daje ten sam efekt, ponieważ używasz liczb dodatnich argumentów, a wyniki są pozytywne, tak nie dostaje się do podpisanych/nie podpisanych różnic.
Hmm, dobrze, masz mnie na tym. Przeczytałbym twój kod jako informujący kompilator, aby promować mnożenie do 64 bitów. smull to dwa 32-bitowe operandy dające 64-bitowy wynik, który nie jest tym, o co prosi twój kod ... ale zarówno gcc, jak i clang używają smulla i tak, nawet jeśli zostawiłem to jako niesłuszną funkcję, więc nie wiedziałem o czas kompilacji, w którym operandy nie miały cyfr znaczących powyżej 32, nadal używały smull.
Być może zmiana była powodem.
Tak, to było ..
int mul_test04 (int a, int b)
{
int c;
c = ((long long)a*b) >> 31;
return(c);
}
daje
zarówno gcc szczęk (również szczęk przetwarza r0, a R1, zamiast korzystania z R2 i R3)
08000340 <mul_test04>:
8000340: fb81 2300 smull r2, r3, r1, r0
8000344: 0fd0 lsrs r0, r2, #31
8000346: ea40 0043 orr.w r0, r0, r3, lsl #1
800034a: 4770 bx lr
ale
int mul_test04 (int a, int b)
{
int c;
c = ((long long)a*b);
return(c);
}
daje ten
gcc:
08000340 <mul_test04>:
8000340: fb00 f001 mul.w r0, r0, r1
8000344: 4770 bx lr
8000346: bf00 nop
dzyń:
0800048c <mul_test04>:
800048c: 4348 muls r0, r1
800048e: 4770 bx lr
Tak z nieco przesunąć kompilatory sobie sprawę, że jesteś zainteresowany tylko w górnej części wyniku więc mogą wyrzucić górną część argumentów co oznacza smull puszka być użytym.
Teraz, jeśli to zrobić:
int mul_test04 (int a, int b)
{
int c;
c = ((long long)a*b) >> 32;
return(c);
}
oba kompilatory się jeszcze bardziej inteligentne, w szczególności brzękiem:
0800048c <mul_test04>:
800048c: fb81 1000 smull r1, r0, r1, r0
8000490: 4770 bx lr
gcc:
08000340 <mul_test04>:
8000340: fb81 0100 smull r0, r1, r1, r0
8000344: 4608 mov r0, r1
8000346: 4770 bx lr
widzę, że 0x40000000 uważane za float, w którym śledzisz miejsce dziesiętne, a to miejsce jest stałą lokalizacją. 0x20000000 miałoby sens jako odpowiedź. Nie mogę jeszcze zdecydować, czy ta 31-bitowa zmiana działa uniwersalnie, czy tylko w tym przypadku.
Kompletny przykład wykorzystywane do powyższego jest tutaj
https://github.com/dwelch67/stm32vld/tree/master/stm32f4d/sample01
i nie uruchamiać go na stm32f4 aby zweryfikować to działa i wyniki.
EDIT:
Jeśli przekazać parametry do funkcji zamiast kodować je w ramach funkcji:
int myfun (int a, int b)
{
return(a+b);
}
Kompilator jest zmuszony do kodu wykonawczego zamiast Optymalizacja odpowiedź w czasie kompilacji.
Teraz, jeśli wywołanie tej funkcji z innej funkcji z numerów zakodowanych:
...
c=myfun(0x1234,0x5678);
...
W tej funkcji wywołującego kompilator może zdecydować, aby obliczyć odpowiedź i po prostu umieścić go tam w czasie kompilacji. Jeśli funkcja myfun() ma charakter globalny (nie jest zadeklarowana jako statyczna), kompilator nie wie, czy jakiś inny kod, który ma być później połączony, użyje go, więc nawet w pobliżu punktu wywołania w tym pliku optymalizuje odpowiedź, ale nadal musi tworzyć rzeczywistą funkcję i pozostaw go w obiekcie dla innego kodu w innych plikach do wywołania, abyś mógł sprawdzić, co kompilator/optymalizator robi z tym kodem C. Jeśli nie użyjesz llvm na przykład tam, gdzie możesz zoptymalizować cały projekt (między plikami) zewnętrzny kod wywołujący tę funkcję użyje prawdziwej funkcji, a nie obliczonej w czasie kompilacji odpowiedzi.
zarówno gcc, jak i clang zrobiły to, co opisuję, lewy kod środowiska wykonawczego dla funkcji jako funkcja globalna, ale w pliku obliczyłem odpowiedź w czasie kompilacji i umieściłem odpowiedź zakodowaną w kodzie zamiast wywoływania funkcji:
int mul_test04 (int a, int b)
{
int c;
c = ((long long)a*b) >> 31;
return(c);
}
w innej funkcji w tym samym pliku:
hexstring(mul_test04(0x40000000,0x40000000),1);
funkcja sam jest zaimplementowana w kodzie:
0800048c <mul_test04>:
800048c: fb81 1000 smull r1, r0, r1, r0
8000490: 0fc9 lsrs r1, r1, #31
8000492: ea41 0040 orr.w r0, r1, r0, lsl #1
8000496: 4770 bx lr
ale gdzie nazywa się ich sztywno odpowiedź, ponieważ posiada wszystkie informacje potrzebne, aby to zrobić:
8000520: f04f 5000 mov.w r0, #536870912 ; 0x20000000
8000524: 2101 movs r1, #1
8000526: f7ff fe73 bl 8000210 <hexstring>
Jeśli nie chcesz odpowiedź Hardcoded trzeba użyć funkcji, która nie jest w ten sam przebieg optymalizacji.
Manipulowanie kompilatorem i optymalizatorem sprowadza się do wielu ćwiczeń i nie jest to nauka ścisła, ponieważ kompilatory i optymalizatory nieustannie ewoluują (na lepsze lub na gorsze).
Poprzez izolowanie małego fragmentu kodu w funkcji powoduje się problemy w inny sposób, większe funkcje będą prawdopodobnie wymagać ramki stosu i eksmisji zmiennych z rejestrów do stosu, ponieważ mniejsze funkcje mogą tego nie robić. a optymalizatorzy mogą zmieniać sposób implementacji kodu. Testujesz fragment kodu w jeden sposób, aby zobaczyć, co robi kompilator, a następnie użyj go w większej funkcji i nie uzyskaj pożądanego rezultatu. Jeśli istnieje dokładna instrukcja lub sekwencja instrukcji, które chcesz zaimplementować ... Zaimplementuj je w asemblerze. Jeśli celujesz w konkretny zestaw instrukcji w określonym zestawie/procesorze instrukcji, to unikaj gry, unikaj zmiany kodu podczas zmiany komputerów/kompilatorów/etc, i po prostu użyj asemblera dla tego celu. w razie potrzeby ifdef lub w inny sposób użyj warunkowych opcji kompilacji do budowania dla różnych celów bez asemblera.
Czy kompilujesz z optymalizacją? Jeśli twój kompilator nie generuje optymalnego kodu, możesz napisać małą funkcję złożenia lub użyć jakiegoś wbudowanego zestawu z C. – TJD