Question

J'utilise Freescale Kinetis K60 et en utilisant le CodeWarrior IDE (que je crois que les utilisations du CCG pour la complier).

Je veux multiplier deux nombres 32 bits (ce qui se traduit par un nombre de 64 bits) et ne conserver que les 32 bits supérieurs.

Je pense que l'instruction de montage correct pour l'ARM Cortex-M4 est l'instruction SMMUL. Je préférerais accéder à cette instruction à partir du code C plutôt que de l'assemblage. Comment puis-je faire?

Je suppose que le code serait idéalement être quelque chose comme ceci:

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

Quand j'essaie cela dans CodeWarrior, je reçois le résultat correct pour c (536.870.912 = 0,25 en nombre D0 FP). Je ne vois pas où d'instructions SMMUL et est 3 multiplier les instructions (UMULL, député, et le député provincial - Je ne comprends pas pourquoi il utilise une multiplication non signée, mais cela est une autre question). J'ai aussi essayé un décalage vers la droite de 32 car cela pourrait faire plus de sens pour l'instruction SMMUL, mais qui ne fait pas quelque chose de différent.

Était-ce utile?

La solution

Le problème que vous obtenez avec l'optimisation de ce code est:

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

votre code ne marche pas faire quoi que ce soit de l'exécution afin que l'optimiseur peut simplement calculer la réponse finale.

:

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

appelé avec ceci:

c = mul_test02(0x40000000,0x40000000);

donne 0x10000000

UMULL donne le même résultat parce que vous utilisez des nombres positifs, les opérandes et les résultats sont tous positifs afin qu'il ne marche pas entrer dans les différences signées / non signées.

Hmm, bien tu me as sur celui-ci. Je lirais votre code comme dire au compilateur de favoriser la multiplication à un 64 bits. Smull est deux 32 bits opérandes donnant un résultat de 64 bits, ce qui est pas ce que votre code demande .... mais pour gcc et clang utilisé le Smull de toute façon, même si je suis parti comme une fonction non appelé, il ne savait pas à compilation que les opérandes avaient pas 32 chiffres significatifs ci-dessus, ils Smull encore utilisé.

Peut-être le changement a été la raison.

Eh oui, c'était-il ..

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

donne

gcc et clang clang (bien recyclages R0 et R1 au lieu d'utiliser r2 et 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

mais

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

donne cette

gcc:

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

clang:

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

Donc, avec le décalage de bits les compilateurs se rendent compte que vous ne souhaitez dans la partie supérieure du résultat afin qu'ils puissent éliminer la partie supérieure des opérandes moyens Smull peuvent être utilisés.

Maintenant, si vous faites ceci:

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

les deux compilateurs obtenir encore plus intelligente, en particulier clang:

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

Je peux voir que 0x40000000 considérée comme un flotteur où vous souhaitez conserver la trace de l'endroit décimal, et lieu est un emplacement fixe. 0x20000000 serait logique que la réponse. Je ne peux pas encore décider si ce changement 31 bits fonctionne universellement ou juste pour ce seul cas.

Un exemple complet utilisé pour ce qui précède est ici

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

et je ne l'exécuter sur un STM32F4 pour vérifier que cela fonctionne et les résultats.

EDIT:

Si vous passez les paramètres dans la fonction au lieu de les coder en dur dans la fonction:

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

Le compilateur est obligé de rendre le code d'exécution au lieu d'optimiser la réponse au moment de la compilation.

Maintenant, si vous appelez cette fonction d'une autre fonction avec des chiffres codés en dur:

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

Dans cette fonction appelant le compilateur peut choisir de calculer la réponse et il suffit de placer là au moment de la compilation. Si la fonction myfun () est globale (non déclarée comme statique) le compilateur ne marche pas savoir si un autre code à lier plus tard l'utilisera donc même à proximité du point d'appel dans ce fichier, il permet d'optimiser une réponse, il doit encore produire la fonction réelle et le laisser dans l'objet pour tout autre code dans d'autres fichiers à l'appel, afin que vous puissiez examiner ce que le compilateur / optimiseur fait avec ce code C. À moins que vous utilisez LLVM par exemple où vous pouvez optimiser l'ensemble du projet (dans les fichiers) code externe appelant cette fonction utilisera la fonction réelle et non un temps de réponse calculée compilation.

gcc et clang fait ce que je décris, le code d'exécution gauche pour la fonction en tant que fonction globale, mais dans le fichier, il calcule la réponse au moment de la compilation et placé la réponse hardcoded dans le code au lieu d'appeler la fonction:

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

dans une autre fonction dans le même fichier:

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

La fonction elle-même est mis en œuvre dans le code:

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

mais où il est appelé, ils ont hardcoded la réponse parce qu'ils avaient toutes les informations nécessaires pour le faire:

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

Si vous ne voulez pas la réponse hardcoded vous devez utiliser une fonction qui est pas dans la même passe d'optimisation.

le compilateur et Manipuler optimiseur vient down à beaucoup de pratique et ce n'est pas une science exacte comme les compilateurs et les optimiseurs sont en constante évolution (pour le meilleur ou le pire).
En isolant un petit morceau de code dans une fonction que vous causez des problèmes d'une autre manière, les fonctions plus importantes sont plus susceptibles d'avoir besoin d'un cadre de pile et variables EVICT des registres à la pile comme il va, de plus petites fonctions pourraient ne pas besoin de le faire et la optimiseurs peuvent changer la façon dont le code est mis en œuvre en conséquence. Vous testez le fragment de code d'une façon de voir ce que le compilateur fait ensuite utiliser dans une fonction plus grande et obtenir le résultat DonT que vous voulez. S'il y a une instruction exacte ou une séquence d'instructions que vous souhaitez mettre en œuvre les mises en œuvre .... en assembleur. Si vous ciblez un ensemble spécifique d'instructions dans un jeu d'instructions spécifiques / processeur, puis éviter le jeu, éviter que votre changement de code lorsque vous changez d'ordinateur / compilateurs / etc, et assembleur simplement d'utiliser pour cette cible. si nécessaire ifdef ou utiliser des options de compilation conditionnelles à construire pour les différentes cibles sans l'assembleur.

Autres conseils

GCC supporte les types à virgule fixe réels: http://gcc.gnu.org/ onlinedocs / gcc / Fixed_002dPoint.html

Je ne suis pas sûr de ce que l'instruction qu'il utilisera, mais il pourrait vous faciliter la vie.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top