root/arch/arm/lib/div64.S

/* [<][>][^][v][top][bottom][index][help] */
   1 /* SPDX-License-Identifier: GPL-2.0-only */
   2 /*
   3  *  linux/arch/arm/lib/div64.S
   4  *
   5  *  Optimized computation of 64-bit dividend / 32-bit divisor
   6  *
   7  *  Author:     Nicolas Pitre
   8  *  Created:    Oct 5, 2003
   9  *  Copyright:  Monta Vista Software, Inc.
  10  */
  11 
  12 #include <linux/linkage.h>
  13 #include <asm/assembler.h>
  14 #include <asm/unwind.h>
  15 
  16 #ifdef __ARMEB__
  17 #define xh r0
  18 #define xl r1
  19 #define yh r2
  20 #define yl r3
  21 #else
  22 #define xl r0
  23 #define xh r1
  24 #define yl r2
  25 #define yh r3
  26 #endif
  27 
  28 /*
  29  * __do_div64: perform a division with 64-bit dividend and 32-bit divisor.
  30  *
  31  * Note: Calling convention is totally non standard for optimal code.
  32  *       This is meant to be used by do_div() from include/asm/div64.h only.
  33  *
  34  * Input parameters:
  35  *      xh-xl   = dividend (clobbered)
  36  *      r4      = divisor (preserved)
  37  *
  38  * Output values:
  39  *      yh-yl   = result
  40  *      xh      = remainder
  41  *
  42  * Clobbered regs: xl, ip
  43  */
  44 
  45 ENTRY(__do_div64)
  46 UNWIND(.fnstart)
  47 
  48         @ Test for easy paths first.
  49         subs    ip, r4, #1
  50         bls     9f                      @ divisor is 0 or 1
  51         tst     ip, r4
  52         beq     8f                      @ divisor is power of 2
  53 
  54         @ See if we need to handle upper 32-bit result.
  55         cmp     xh, r4
  56         mov     yh, #0
  57         blo     3f
  58 
  59         @ Align divisor with upper part of dividend.
  60         @ The aligned divisor is stored in yl preserving the original.
  61         @ The bit position is stored in ip.
  62 
  63 #if __LINUX_ARM_ARCH__ >= 5
  64 
  65         clz     yl, r4
  66         clz     ip, xh
  67         sub     yl, yl, ip
  68         mov     ip, #1
  69         mov     ip, ip, lsl yl
  70         mov     yl, r4, lsl yl
  71 
  72 #else
  73 
  74         mov     yl, r4
  75         mov     ip, #1
  76 1:      cmp     yl, #0x80000000
  77         cmpcc   yl, xh
  78         movcc   yl, yl, lsl #1
  79         movcc   ip, ip, lsl #1
  80         bcc     1b
  81 
  82 #endif
  83 
  84         @ The division loop for needed upper bit positions.
  85         @ Break out early if dividend reaches 0.
  86 2:      cmp     xh, yl
  87         orrcs   yh, yh, ip
  88         subscs  xh, xh, yl
  89         movsne  ip, ip, lsr #1
  90         mov     yl, yl, lsr #1
  91         bne     2b
  92 
  93         @ See if we need to handle lower 32-bit result.
  94 3:      cmp     xh, #0
  95         mov     yl, #0
  96         cmpeq   xl, r4
  97         movlo   xh, xl
  98         retlo   lr
  99 
 100         @ The division loop for lower bit positions.
 101         @ Here we shift remainer bits leftwards rather than moving the
 102         @ divisor for comparisons, considering the carry-out bit as well.
 103         mov     ip, #0x80000000
 104 4:      movs    xl, xl, lsl #1
 105         adcs    xh, xh, xh
 106         beq     6f
 107         cmpcc   xh, r4
 108 5:      orrcs   yl, yl, ip
 109         subcs   xh, xh, r4
 110         movs    ip, ip, lsr #1
 111         bne     4b
 112         ret     lr
 113 
 114         @ The top part of remainder became zero.  If carry is set
 115         @ (the 33th bit) this is a false positive so resume the loop.
 116         @ Otherwise, if lower part is also null then we are done.
 117 6:      bcs     5b
 118         cmp     xl, #0
 119         reteq   lr
 120 
 121         @ We still have remainer bits in the low part.  Bring them up.
 122 
 123 #if __LINUX_ARM_ARCH__ >= 5
 124 
 125         clz     xh, xl                  @ we know xh is zero here so...
 126         add     xh, xh, #1
 127         mov     xl, xl, lsl xh
 128         mov     ip, ip, lsr xh
 129 
 130 #else
 131 
 132 7:      movs    xl, xl, lsl #1
 133         mov     ip, ip, lsr #1
 134         bcc     7b
 135 
 136 #endif
 137 
 138         @ Current remainder is now 1.  It is worthless to compare with
 139         @ divisor at this point since divisor can not be smaller than 3 here.
 140         @ If possible, branch for another shift in the division loop.
 141         @ If no bit position left then we are done.
 142         movs    ip, ip, lsr #1
 143         mov     xh, #1
 144         bne     4b
 145         ret     lr
 146 
 147 8:      @ Division by a power of 2: determine what that divisor order is
 148         @ then simply shift values around
 149 
 150 #if __LINUX_ARM_ARCH__ >= 5
 151 
 152         clz     ip, r4
 153         rsb     ip, ip, #31
 154 
 155 #else
 156 
 157         mov     yl, r4
 158         cmp     r4, #(1 << 16)
 159         mov     ip, #0
 160         movhs   yl, yl, lsr #16
 161         movhs   ip, #16
 162 
 163         cmp     yl, #(1 << 8)
 164         movhs   yl, yl, lsr #8
 165         addhs   ip, ip, #8
 166 
 167         cmp     yl, #(1 << 4)
 168         movhs   yl, yl, lsr #4
 169         addhs   ip, ip, #4
 170 
 171         cmp     yl, #(1 << 2)
 172         addhi   ip, ip, #3
 173         addls   ip, ip, yl, lsr #1
 174 
 175 #endif
 176 
 177         mov     yh, xh, lsr ip
 178         mov     yl, xl, lsr ip
 179         rsb     ip, ip, #32
 180  ARM(   orr     yl, yl, xh, lsl ip      )
 181  THUMB( lsl     xh, xh, ip              )
 182  THUMB( orr     yl, yl, xh              )
 183         mov     xh, xl, lsl ip
 184         mov     xh, xh, lsr ip
 185         ret     lr
 186 
 187         @ eq -> division by 1: obvious enough...
 188 9:      moveq   yl, xl
 189         moveq   yh, xh
 190         moveq   xh, #0
 191         reteq   lr
 192 UNWIND(.fnend)
 193 
 194 UNWIND(.fnstart)
 195 UNWIND(.pad #4)
 196 UNWIND(.save {lr})
 197 Ldiv0_64:
 198         @ Division by 0:
 199         str     lr, [sp, #-8]!
 200         bl      __div0
 201 
 202         @ as wrong as it could be...
 203         mov     yl, #0
 204         mov     yh, #0
 205         mov     xh, #0
 206         ldr     pc, [sp], #8
 207 
 208 UNWIND(.fnend)
 209 ENDPROC(__do_div64)

/* [<][>][^][v][top][bottom][index][help] */