Frage

Ich benutze Freescale Kinetis K60 und die Codewarrior -IDE (von denen ich glaube, dass sie GCC für das Komplier verwendet).

Ich möchte zwei 32 -Bit -Zahlen multiplizieren (was zu einer 64 -Bit -Zahl führt) und nur die oberen 32 Bit beibehalten.

Ich denke, die korrekte Montageanweisung für den Arm Cortex-M4 ist die SMMUL-Anweisung. Ich würde es vorziehen, auf diese Anweisung aus C -Code als auf Assembly zuzugreifen. Wie mache ich das?

Ich stelle mir vor, der Code würde idealerweise so etwas sein:

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

Wenn ich dies in Codewarrior versuche, erhalte ich das richtige Ergebnis für C (536870912 = 0,25 als D0 -FP -Zahl). Ich sehe die SMMUL -Anweisung nirgendwo und der Multiplikum ist 3 Anweisungen (Umull, MLA und MLA - ich verstehe nicht, warum er einen nicht signierten Multiplizieren verwendet, aber das ist eine andere Frage). Ich habe auch eine richtige Verschiebung von 32 ausprobiert, da dies für die SMMUL -Anweisung sinnvoller sein könnte, aber das macht nichts anderes.

War es hilfreich?

Lösung

Das Problem, das Sie bei der Optimierung dieses Code erhalten, ist:

08000328 <mul_test01>:
 8000328:   f04f 5000   mov.w   r0, #536870912  ; 0x20000000
 800032c:   4770        bx  lr
 800032e:   bf00        nop

Ihr Code macht keine Laufzeit, sodass der Optimierer einfach die endgültige Antwort berechnen kann.

Dies:

.thumb_func
.globl mul_test02
mul_test02:
    smull r2,r3,r0,r1
    mov r0,r3
    bx lr

mit diesem angerufen:

c = mul_test02(0x40000000,0x40000000);

gibt 0x10000000

Umull gibt das gleiche Ergebnis, da Sie positive Zahlen verwenden, die Operanden und die Ergebnisse alle positiv sind, sodass sie nicht in die signierten/nicht signierten Unterschiede eingehen.

Hmm, na ja, du hast mich auf diesen bekommen. Ich würde Ihren Code lesen, um dem Compiler mitzuteilen, er solle das Multiplizieren mit einem 64 -Bit bewerben. Smull ist zwei 32 -Bit -Operanden, die ein 64 -Bit -Ergebnis erzielen, was nicht das ist, wonach Ihr Code fragt ... aber sowohl GCC als auch Clang benutzten den Smull sowieso, selbst wenn ich ihn als unangebaute Funktion verlassen habe, wusste es nicht bei Kompilierungszeit, in der die Operanden keine signifikanten Ziffern über 32 hatten, verwendeten sie immer noch Smull.

Vielleicht war die Verschiebung der Grund.

Ja, das war es ..

int mul_test04 ( int a, int b )
{
    int c;
    c = ((long long)a*b) >> 31; 
    return(c);
}

gibt

Sowohl GCC als auch Clang (Well Clang recycles r0 und r1 anstatt R2 und 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

aber dieses

int mul_test04 ( int a, int b )
{
    int c;
    c = ((long long)a*b); 
    return(c);
}

gibt das

GCC:

08000340 <mul_test04>:
 8000340:   fb00 f001   mul.w   r0, r0, r1
 8000344:   4770        bx  lr
 8000346:   bf00        nop

Klang:

0800048c <mul_test04>:
 800048c:   4348        muls    r0, r1
 800048e:   4770        bx  lr

Mit der Bitverschiebung erkennen die Compiler, dass Sie sich nur am oberen Teil des Ergebnisses interessieren, damit sie den oberen Teil der Operanden verwerfen können, was bedeutet, dass Smull verwendet werden kann.

Nun, wenn Sie dies tun:

int mul_test04 ( int a, int b )
{
    int c;
    c = ((long long)a*b) >> 32; 
    return(c);
}

Beide Compiler werden noch schlauer, insbesondere Klang:

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

Ich kann sehen, dass 0x40000000 als Schwimmer angesehen wird, an dem Sie den Dezimalplatz verfolgen, und dieser Ort ist ein fester Ort. 0x20000000 wäre sinnvoll wie die Antwort. Ich kann noch nicht entscheiden, ob diese 31 -Bit -Verschiebung universell oder nur für diesen einen Fall funktioniert.

Ein vollständiges Beispiel, das für das obige verwendet wird, ist hier

https://github.com/dwelch67/stm32vld/tree/master/stm32f4d/sample01

Und ich habe es auf einem STM32F4 ausgeführt, um zu überprüfen, ob es funktioniert und die Ergebnisse.

BEARBEITEN:

Wenn Sie die Parameter in die Funktion übergeben, anstatt sie innerhalb der Funktion festzuhalten:

int myfun ( int a, int b )
{
     return(a+b);
}

Der Compiler ist gezwungen, Laufzeitcode zu erstellen, anstatt die Antwort zur Kompilierungszeit zu optimieren.

Wenn Sie diese Funktion nun von einer anderen Funktion mit hartcodierten Zahlen aufrufen:

...
c=myfun(0x1234,0x5678);
...

In dieser Aufruffunktion kann der Compiler die Antwort berechnen und dort zur Kompilierzeit dort platzieren. Wenn die Funktion myfun () global (nicht als statisch deklariert) ist, weiß der Compiler nicht, ob ein späterer Code, der später verknüpft ist Lassen Sie es im Objekt für andere Code in anderen Dateien aufrufen, sodass Sie weiterhin untersuchen können, was der Compiler/Optimierer mit diesem C -Code macht. Sofern Sie nicht LLVM verwenden, beispielsweise wenn Sie das gesamte Projekt (über Dateien) optimieren können, verwendet der externe Code, den diese Funktion aufruft, die reale Funktion und nicht eine kompile Zeit berechnete Antwort.

Sowohl GCC als auch Clang haben das getan, was ich beschreibe, linke Laufzeitcode für die Funktion als globale Funktion, aber innerhalb der Datei wurde die Antwort zur Kompilierungszeit berechnet und die festcodierte Antwort im Code platziert, anstatt die Funktion aufzurufen:

int mul_test04 ( int a, int b )
{
    int c;
    c = ((long long)a*b) >> 31;
    return(c);
}

In einer anderen Funktion in derselben Datei:

hexstring(mul_test04(0x40000000,0x40000000),1);

Die Funktion selbst wird im Code implementiert:

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

Aber wo es genannt wird, haben sie die Antwort fest codiert, weil sie alle Informationen hatten, die dies benötigten:

 8000520:   f04f 5000   mov.w   r0, #536870912  ; 0x20000000
 8000524:   2101        movs    r1, #1
 8000526:   f7ff fe73   bl  8000210 <hexstring>

Wenn Sie nicht die hartcodierte Antwort möchten, müssen Sie eine Funktion verwenden, die sich nicht im gleichen Optimierungspass befindet.

Die Manipulation des Compilers und des Optimierers hängt von viel Übung ab und es ist keine genaue Wissenschaft, da sich die Compiler und Optimierer ständig weiterentwickeln (zum Guten oder Schlechten).
Durch das Isolieren eines kleinen Stücks Code in einer Funktion, die Sie auf andere Weise Probleme verursachen, benötigen größere Funktionen eher einen Stapelrahmen und räumen Variablen von Registern bis zum Stapel, so dass kleinere Funktionen dies möglicherweise nicht tun müssen, und die Optimierer können sich ändern, wie der Code als Ergebnis implementiert wird. Sie testen das Codefragment auf eine Möglichkeit, um zu sehen, was der Compiler tut, dann in einer größeren Funktion und nicht das gewünschte Ergebnis erhalten. Wenn es eine genaue Anweisung oder eine Abfolge von Anweisungen gibt, die Sie implementieren möchten, implementieren Sie sie in Assembler. Wenn Sie auf einen bestimmten Satz von Anweisungen in einem bestimmten Befehlssatz/-prozessor abzielen, vermeiden Sie das Spiel, vermeiden Sie Ihren Code, wenn Sie Computer/Compiler/usw. ändern, und verwenden Sie Assembler einfach für dieses Ziel. Bei Bedarf IFDEF oder auf andere Weise bedingte Kompilierungsoptionen zum Erstellen verschiedener Ziele ohne Assembler.

Andere Tipps

GCC unterstützt tatsächliche Festpunkttypen: http://gcc.gnu.org/onlinedocs/gcc/fixed_002dpoint.html

Ich bin mir nicht sicher, welche Anweisung er verwenden wird, aber es könnte Sie das Leben leichter machen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top