2011-12-03 27 views
8

Używam programu Freescale Kinetis K60 i używam CodeWarrior IDE (który, jak sądzę, używa GCC dla kompilatora).Naprawiono matematykę punktową z ARM Cortex-M4 i kompilatorem gcc

Chcę pomnożyć dwie liczby 32-bitowe (co daje liczbę 64-bitową) i zachować tylko 32 górne bity.

Myślę, że prawidłową instrukcją montażu dla ARM Cortex-M4 jest instrukcja SMMUL. Wolałbym uzyskać dostęp do tej instrukcji z kodu C zamiast z zestawu. Jak mam to zrobic?

Wyobrażam sobie, że kod będzie idealnie być coś takiego:

int a,b,c; 

a = 1073741824; // 0x40000000 = 0.5 as a D0 fixed point number 
b = 1073741824; // 0x40000000 = 0.5 as a D0 fixed point number 

c = ((long long)a*b) >> 31; // 31 because there are two sign bits after the multiplication 
          // so I can throw away the most significant bit 

Kiedy próbuję to w CodeWarrior, uzyskać poprawny wynik dla C (536870912 = 0,25 jako liczba D0 FP). Nie widzę instrukcji SMMUL w dowolnym miejscu, a mnożenie to 3 instrukcje (UMULL, MLA i MLA - nie rozumiem, dlaczego używa on unsigned multiply, ale to jest kolejne pytanie). Próbowałem również przesunięcia w prawo o 32, ponieważ może to mieć więcej sensu dla instrukcji SMMUL, ale to nie robi nic innego.

+0

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

Odpowiedz

6

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.

+0

Użyłem aktualnego wcześniej znanego-as-codesourcery kompilatora gcc i clang z llvm wersji 3.0. –

+2

Wygląda na to, że się myliłem. CodeWarrior wydaje się nie używać GCC. Odwołuje się do pliku wykonywalnego o nazwie mwccarm, który wydaje się być kompilatorem specyficznym dla Freescale. Próbowałem zdefiniować mnożenie w funkcji i nadal nie mogę sprawić, by kompilator używał komendy smull - w rzeczywistości polecenie umuli nadal tam jest. Spróbuję pobrać gcc lub clang i zobaczyć, co się stanie. – EpicAdv

+1

Nie sądzę, że z powodzeniem mówię kompilatorowi, aby skompilował autonomiczną funkcję. Wydaje się, że spoglądam wstecz tam, gdzie (i jeśli) jest ono wywoływane i optymalizowane. Jak mogę wymusić na nim kompilację ogólnej funkcji, tak jak tu robisz? – EpicAdv

-1

GCC obsługuje rzeczywiste typy stałym punkcie: http://gcc.gnu.org/onlinedocs/gcc/Fixed_002dPoint.html

Nie jestem pewien, co instrukcja użyje, ale może sprawić, że życie staje się łatwiejsze.

+1

Z podlinkowanej strony: "Nie wszystkie cele obsługują typy o stałym punkcie." Z mojego doświadczenia wynika, że ​​bardzo niewiele celów obsługuje typy stałe. –