xref: /linux/arch/x86/include/asm/div64.h (revision 83bce9c2baa51e439480a713119a73d3c8b61083)
1 #ifndef _ASM_X86_DIV64_H
2 #define _ASM_X86_DIV64_H
3 
4 #ifdef CONFIG_X86_32
5 
6 #include <linux/types.h>
7 #include <linux/log2.h>
8 
9 /*
10  * do_div() is NOT a C function. It wants to return
11  * two values (the quotient and the remainder), but
12  * since that doesn't work very well in C, what it
13  * does is:
14  *
15  * - modifies the 64-bit dividend _in_place_
16  * - returns the 32-bit remainder
17  *
18  * This ends up being the most efficient "calling
19  * convention" on x86.
20  */
21 #define do_div(n, base)						\
22 ({								\
23 	unsigned long __upper, __low, __high, __mod, __base;	\
24 	__base = (base);					\
25 	if (__builtin_constant_p(__base) && is_power_of_2(__base)) { \
26 		__mod = n & (__base - 1);			\
27 		n >>= ilog2(__base);				\
28 	} else {						\
29 		asm("" : "=a" (__low), "=d" (__high) : "A" (n));\
30 		__upper = __high;				\
31 		if (__high) {					\
32 			__upper = __high % (__base);		\
33 			__high = __high / (__base);		\
34 		}						\
35 		asm("divl %2" : "=a" (__low), "=d" (__mod)	\
36 			: "rm" (__base), "0" (__low), "1" (__upper));	\
37 		asm("" : "=A" (n) : "a" (__low), "d" (__high));	\
38 	}							\
39 	__mod;							\
40 })
41 
42 static inline u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder)
43 {
44 	union {
45 		u64 v64;
46 		u32 v32[2];
47 	} d = { dividend };
48 	u32 upper;
49 
50 	upper = d.v32[1];
51 	d.v32[1] = 0;
52 	if (upper >= divisor) {
53 		d.v32[1] = upper / divisor;
54 		upper %= divisor;
55 	}
56 	asm ("divl %2" : "=a" (d.v32[0]), "=d" (*remainder) :
57 		"rm" (divisor), "0" (d.v32[0]), "1" (upper));
58 	return d.v64;
59 }
60 #define div_u64_rem	div_u64_rem
61 
62 static inline u64 mul_u32_u32(u32 a, u32 b)
63 {
64 	u32 high, low;
65 
66 	asm ("mull %[b]" : "=a" (low), "=d" (high)
67 			 : [a] "a" (a), [b] "rm" (b) );
68 
69 	return low | ((u64)high) << 32;
70 }
71 #define mul_u32_u32 mul_u32_u32
72 
73 #else
74 # include <asm-generic/div64.h>
75 #endif /* CONFIG_X86_32 */
76 
77 #endif /* _ASM_X86_DIV64_H */
78