xref: /openbmc/linux/lib/muldi3.c (revision 3381df09)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  */
4 
5 #include <linux/export.h>
6 #include <linux/libgcc.h>
7 
8 #define W_TYPE_SIZE 32
9 
10 #define __ll_B ((unsigned long) 1 << (W_TYPE_SIZE / 2))
11 #define __ll_lowpart(t) ((unsigned long) (t) & (__ll_B - 1))
12 #define __ll_highpart(t) ((unsigned long) (t) >> (W_TYPE_SIZE / 2))
13 
14 /* If we still don't have umul_ppmm, define it using plain C.  */
15 #if !defined(umul_ppmm)
16 #define umul_ppmm(w1, w0, u, v)						\
17 	do {								\
18 		unsigned long __x0, __x1, __x2, __x3;			\
19 		unsigned short __ul, __vl, __uh, __vh;			\
20 									\
21 		__ul = __ll_lowpart(u);					\
22 		__uh = __ll_highpart(u);				\
23 		__vl = __ll_lowpart(v);					\
24 		__vh = __ll_highpart(v);				\
25 									\
26 		__x0 = (unsigned long) __ul * __vl;			\
27 		__x1 = (unsigned long) __ul * __vh;			\
28 		__x2 = (unsigned long) __uh * __vl;			\
29 		__x3 = (unsigned long) __uh * __vh;			\
30 									\
31 		__x1 += __ll_highpart(__x0); /* this can't give carry */\
32 		__x1 += __x2; /* but this indeed can */			\
33 		if (__x1 < __x2) /* did we get it? */			\
34 		__x3 += __ll_B; /* yes, add it in the proper pos */	\
35 									\
36 		(w1) = __x3 + __ll_highpart(__x1);			\
37 		(w0) = __ll_lowpart(__x1) * __ll_B + __ll_lowpart(__x0);\
38 	} while (0)
39 #endif
40 
41 #if !defined(__umulsidi3)
42 #define __umulsidi3(u, v) ({				\
43 	DWunion __w;					\
44 	umul_ppmm(__w.s.high, __w.s.low, u, v);		\
45 	__w.ll;						\
46 	})
47 #endif
48 
49 long long notrace __muldi3(long long u, long long v)
50 {
51 	const DWunion uu = {.ll = u};
52 	const DWunion vv = {.ll = v};
53 	DWunion w = {.ll = __umulsidi3(uu.s.low, vv.s.low)};
54 
55 	w.s.high += ((unsigned long) uu.s.low * (unsigned long) vv.s.high
56 		+ (unsigned long) uu.s.high * (unsigned long) vv.s.low);
57 
58 	return w.ll;
59 }
60 EXPORT_SYMBOL(__muldi3);
61