Comment peut-on faire entier (signé ou non signé) division sur ARM?
-
27-10-2019 - |
Question
Je travaille sur Cortex-A8 et Cortex-A9 en particulier. Je sais que certaines architectures ne viennent pas avec la division entière, mais quelle est la meilleure façon de le faire autre que converti à flotteur, diviser, convertir en entier? Ou est-ce bien la meilleure solution?
Vive! =)
La solution
Le compilateur comprend normalement une division dans sa bibliothèque, gcclib par exemple, je les ai extrait de gcc et de les utiliser directement:
https://github.com/dwelch67/stm32vld/ puis stm32f4d / aventure / gcclib
va flotter et probablement pas en arrière la meilleure solution. vous pouvez l'essayer et de voir à quelle vitesse il est ... Ceci est un make se multiplient, mais pourrait aussi facilement une fracture:
https://github.com/dwelch67/stm32vld/ puis stm32f4d / float01 / vecteurs. s
Je na pas le temps, il se voir à quelle vitesse / lent. ENTENDU J'utilise un cortex m au-dessus et vous parlez d'un cortex a, différentes extrémités du spectre, des instructions de flotteur similaires et les choses lib gcc est similaire, pour le cortex m je dois construire pour le pouce, mais vous pouvez tout comme construire facilement pour le bras. En fait, avec gcc il devrait tout simplement travailler automagiquement vous ne devriez pas besoin de le faire comme je l'ai fait. D'autres compilateurs ainsi vous ne devriez pas avoir besoin de le faire comme je l'ai fait dans le jeu d'aventure ci-dessus.
Autres conseils
Division par une valeur constante se fait rapidement en faisant une 64bit-multiplier et de décalage à droite, par exemple, comme ceci:
LDR R3, =0xA151C331
UMULL R3, R2, R1, R3
MOV R0, R2,LSR#10
ici R1 est divisée par 1625. Le calcul se fait comme suit: 64bitreg (R2: R3) = R1 * 0xA151C331, alors le résultat est le 32 bits en haut à droite décalée de 10:
R1*0xA151C331/2^(32+10) = R1*0.00061538461545751488 = R1/1624.99999980
Vous pouvez calculer vos propres constantes de cette formule:
x / N == (x*A)/2^(32+n) --> A = 2^(32+n)/N
sélectionner le plus grand n, pour lesquels A <2 ^ 32
Certains copie pâtes d'ailleurs pour une division entière: En gros, 3 instructions par bit. De ce site , bien que je l'ai vu beaucoup d'autres endroits. Ce site dispose également d'une version agréable qui peut être plus rapide en général .
@ Entry r0: numerator (lo) must be signed positive
@ r2: deniminator (den) must be non-zero and signed negative
idiv:
lo .req r0; hi .req r1; den .req r2
mov hi, #0 @ hi = 0
adds lo, lo, lo
.rept 32 @ repeat 32 times
adcs hi, den, hi, lsl #1
subcc hi, hi, den
adcs lo, lo, lo
.endr
mov pc, lr @ return
@ Exit r0: quotient (lo)
@ r1: remainder (hi)
J'ai écrit ma propre routine pour effectuer une division non signée que je ne pouvais pas trouver une version non signée sur le web. Je avais besoin de diviser une valeur 64 bits avec une valeur de 32 bits pour obtenir un résultat de 32 bits.
La boucle intérieure n'est pas aussi efficace que la solution signée fournie ci-dessus, mais pris en charge par l'arithmétique non signé. Cette routine exécute une division de 32 bits si la partie haute du numérateur (salut) est plus petit que le dénominateur (den), autrement une pleine division de 64 bits est effectué (salut: lo / den). Le résultat est lo.
cmp hi, den // if hi < den do 32 bits, else 64 bits
bpl do64bits
REPT 32
adds lo, lo, lo // shift numerator through carry
adcs hi, hi, hi
subscc work, hi, den // if carry not set, compare
subcs hi, hi, den // if carry set, subtract
addcs lo, lo, #1 // if carry set, and 1 to quotient
ENDR
mov r0, lo // move result into R0
mov pc, lr // return
do64bits:
mov top, #0
REPT 64
adds lo, lo, lo // shift numerator through carry
adcs hi, hi, hi
adcs top, top, top
subscc work, top, den // if carry not set, compare
subcs top, top, den // if carry set, subtract
addcs lo, lo, #1 // if carry set, and 1 to quotient
ENDR
mov r0, lo // move result into R0
mov pc, lr // return
vérification supplémentaire pour les conditions aux limites et puissance de 2 peut être ajouté. Vous trouverez tous les détails http://www.idwiz.co.za /Tips%20and%20Tricks/Divide.htm
J'ai écrit les fonctions suivantes pour l'assembleur ARM GNU
. Si vous ne disposez pas d'une unité centrale de traitement avec le soutien de la machine à udiv/sdiv
, il suffit de couper les premières lignes à la « 0 ». Étiquette soit fonction
.arm
.cpu cortex-a7
.syntax unified
.type udiv,%function
.globl udiv
udiv: tst r1,r1
bne 0f
udiv r3,r0,r2
mls r1,r2,r3,r0
mov r0,r3
bx lr
0: cmp r1,r2
movhs r1,r2
bxhs lr
mvn r3,0
1: adds r0,r0
adcs r1,r1
cmpcc r1,r2
subcs r1,r2
orrcs r0,1
lsls r3,1
bne 1b
bx lr
.size udiv,.-udiv
.type sdiv,%function
.globl sdiv
sdiv: teq r1,r0,ASR 31
bne 0f
sdiv r3,r0,r2
mls r1,r2,r3,r0
mov r0,r3
bx lr
0: mov r3,2
adds r0,r0
and r3,r3,r1,LSR 30
adcs r1,r1
orr r3,r3,r2,LSR 31
movvs r1,r2
ldrvc pc,[pc,r3,LSL 2]
bx lr
.int 1f
.int 3f
.int 5f
.int 11f
1: cmp r1,r2
movge r1,r2
bxge lr
mvn r3,1
2: adds r0,r0
adcs r1,r1
cmpvc r1,r2
subge r1,r2
orrge r0,1
lsls r3,1
bne 2b
bx lr
3: cmn r1,r2
movge r1,r2
bxge lr
mvn r3,1
4: adds r0,r0
adcs r1,r1
cmnvc r1,r2
addge r1,r2
orrge r0,1
lsls r3,1
bne 4b
rsb r0,0
bx lr
5: cmn r1,r2
blt 6f
tsteq r0,r0
bne 7f
6: mov r1,r2
bx lr
7: mvn r3,1
8: adds r0,r0
adcs r1,r1
cmnvc r1,r2
blt 9f
tsteq r0,r3
bne 10f
9: add r1,r2
orr r0,1
10: lsls r3,1
bne 8b
rsb r0,0
bx lr
11: cmp r1,r2
blt 12f
tsteq r0,r0
bne 13f
12: mov r1,r2
bx lr
13: mvn r3,1
14: adds r0,r0
adcs r1,r1
cmpvc r1,r2
blt 15f
tsteq r0,r3
bne 16f
15: sub r1,r2
orr r0,1
16: lsls r3,1
bne 14b
bx lr
Il y a deux fonctions, udiv
pour la division entier non signé et sdiv
pour la division entière signée. Ils comptent tous deux un dividende de 64 bits (soit signé ou non signé) dans r1
(mot haut) et r0
(mot faible), et un diviseur 32 bits dans r2
. Ils reviennent le quotient dans r0
et le reste dans r1
, ainsi vous pouvez les définir dans un C header
comme extern
retournant un entier de 64 bits et masquer le quotient et le reste par la suite. Une erreur (division par 0 ou de débordement) est indiqué par un reste ayant une valeur absolue supérieure ou égale à la valeur absolue du diviseur. L'algorithme de division signé utilise la distinction de cas par les signes des deux dividende et le diviseur; il ne convertit pas en nombres entiers positifs d'abord, car cela ne détecte pas toutes les conditions de débordement correctement.