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 45ENTRY(__do_div64) 46UNWIND(.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 761: 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. 862: 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. 943: 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 1044: movs xl, xl, lsl #1 105 adcs xh, xh, xh 106 beq 6f 107 cmpcc xh, r4 1085: 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. 1176: 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 1327: 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 1478: @ 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... 1889: moveq yl, xl 189 moveq yh, xh 190 moveq xh, #0 191 reteq lr 192UNWIND(.fnend) 193 194UNWIND(.fnstart) 195UNWIND(.pad #4) 196UNWIND(.save {lr}) 197Ldiv0_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 208UNWIND(.fnend) 209ENDPROC(__do_div64) 210