Как выполнить целочисленное (знаковое или беззнаковое) деление в ARM?
-
27-10-2019 - |
Вопрос
Я работаю, в частности, над Cortex-A8 и Cortex-A9.Я знаю, что некоторые архитектуры не поддерживают целочисленное деление, но каков наилучший способ сделать это, кроме преобразования в float, divide, convert в integer?Или это действительно лучшее решение?
Ваше здоровье!= )
Решение
Компилятор обычно включает в себя разрыв в своей библиотеке, GCClib, например, я извлек их из GCC и использую их напрямую:
https://github.com/dwelch67/stm32vld/ затем STM32F4D/Adventure/GCClib
Собираться плавать и обратно, вероятно, не лучшее решение. Вы можете попробовать это и посмотреть, как быстро это ... это умножение, но с таким же успехом может сделать его разделением:
https://github.com/dwelch67/stm32vld/ Тогда STM32F4D/float01/vectors.s
Я не сделал время, хотя, чтобы увидеть, как быстро/медленно. Понятно, что я использую кору-м выше, и вы говорите о коре-а, разных концах спектра, аналогичных инструкциях по плаванию, и материал GCC LIB похожи Так же легко построить для руки. На самом деле с GCC все должно работать автоматически, вам не нужно делать это так, как я это сделал. Другие компиляторы также не должны делать это так, как я делал это в приключенческой игре выше.
Другие советы
Разделение на постоянное значение выполняется быстро, выполняя 64-битный и сдвиг, например, вправо, например, это:
LDR R3, =0xA151C331
UMULL R3, R2, R1, R3
MOV R0, R2,LSR#10
Здесь R1 делится на 1625. Расчет выполняется таким: 64bitreg (r2: r3) = r1*0xa151c331, то результат - верхний 32 -битный справа, сдвинутый на 10:
R1*0xA151C331/2^(32+10) = R1*0.00061538461545751488 = R1/1624.99999980
Вы можете рассчитать свои собственные константы из этой формулы:
x / N == (x*A)/2^(32+n) --> A = 2^(32+n)/N
Выберите самый большой n, для которого <2^32
Некоторые копии-паста из других мест для целочисленного разрыва: в основном 3 инструкции за бит. Из это Веб -сайт, хотя я видел это и многие другие места.Этот У сайта также есть хорошая версия, которая может быть быстрее в целом.
@ 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)
Я написал свою собственную процедуру для выполнения неподписанного разделения, поскольку не смог найти неподписанную версию в Интернете.Мне нужно было разделить 64-битное значение на 32-битное, чтобы получить 32-битный результат.
Внутренний цикл не так эффективен, как решение со знаком, представленное выше, но оно поддерживает арифметику без знака.Эта процедура выполняет 32-разрядное деление, если старшая часть числителя (hi) меньше знаменателя (den), в противном случае выполняется полное 64-разрядное деление (hi: lo /den).Результат находится в 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
Может быть добавлена дополнительная проверка граничных условий и степени 2.С полной информацией можно ознакомиться по адресу http://www.idwiz.co.za/Tips%20and%20Tricks/Divide.htm
Я написал следующие функции для ARM GNU
ассемблер. Если у вас нет процессора с udiv/sdiv
Поддержка машины, просто выровняйте первые несколько линий до метки «0:» в любой функции.
.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
Есть две функции, udiv
для беспигнированного целочисленного подразделения и sdiv
Для подписанного целочисленного подразделения. Они оба ожидают 64-раз r1
(Высокое слово) и r0
(низкое слово) и 32-битный делитель в r2
. Анкет Они возвращают коэффициент в r0
и остаток в r1
, таким образом, вы можете определить их в C header
в качестве extern
Вернув 64-разрядное целое число и замаскируйте коэффициент и остаток после этого. Ошибка (разделение на 0 или переполнение) обозначена оставшейся, имеющей абсолютное значение, превышающее или равное абсолютное значение делителя. Алгоритм подписанного разделения использует различие для случая по признакам как дивидендов, так и дивизора; Сначала он не превращается в положительные целые числа, поскольку это не обнаружит все условия переполнения должным образом.