xref: /optee_os/lib/libutils/isoc/arch/arm/arm32_aeabi_ldivmod.c (revision 1bb929836182ecb96d2d9d268daa807c67596396)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (c) 2015, STMicroelectronics International N.V.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright notice,
10  * this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright notice,
13  * this list of conditions and the following disclaimer in the documentation
14  * and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /* struct lqr - stores qutient/remainder to handle divmod EABI interfaces. */
30 struct lqr {
31 	unsigned long long q;	/* computed quotient */
32 	unsigned long long r;	/* computed remainder */
33 	unsigned q_n;		/* specficies if quotient shall be negative */
34 	unsigned r_n;		/* specficies if remainder shall be negative */
35 };
36 
37 static void ul_div_qr(unsigned long long numerator,
38 		unsigned long long denominator, struct lqr *qr);
39 
40 
41 static void division_lqr(unsigned long long n, unsigned long long p,
42 		struct lqr *qr)
43 {
44 	unsigned long long i = 1, q = 0;
45 	if (p == 0) {
46 		qr->r = 0xFFFFFFFFFFFFFFFFULL;	/* division by 0 */
47 		return;
48 	}
49 
50 	while ((p >> 63) == 0) {
51 		i = i << 1;	/* count the max division steps */
52 		p = p << 1;     /* increase p until it has maximum size*/
53 	}
54 
55 	while (i > 0) {
56 		q = q << 1;	/* write bit in q at index (size-1) */
57 		if (n >= p) {
58 			n -= p;
59 			q++;
60 		}
61 		p = p >> 1;	/* decrease p */
62 		i = i >> 1;	/* decrease remaining size in q */
63 	}
64 	qr->r = n;
65 	qr->q = q;
66 }
67 
68 static void ul_div_qr(unsigned long long numerator,
69 		unsigned long long denominator, struct lqr *qr)
70 {
71 
72 	division_lqr(numerator, denominator, qr);
73 
74 	/* negate quotient and/or remainder according to requester */
75 	if (qr->q_n)
76 		qr->q = -qr->q;
77 	if (qr->r_n)
78 		qr->r = -qr->r;
79 }
80 
81 struct asm_ulqr {
82 	unsigned long long v0;
83 	unsigned long long v1;
84 };
85 
86 /* called from assembly function __aeabi_uldivmod */
87 void __ul_divmod(struct asm_ulqr *asm_ulqr);
88 void __ul_divmod(struct asm_ulqr *asm_ulqr)
89 {
90 	unsigned long long numerator = asm_ulqr->v0;
91 	unsigned long long denominator = asm_ulqr->v1;
92 	struct lqr qr = { .q_n = 0, .r_n = 0 };
93 
94 	ul_div_qr(numerator, denominator, &qr);
95 
96 	asm_ulqr->v0 = qr.q;
97 	asm_ulqr->v1 = qr.r;
98 }
99 
100 struct asm_lqr {
101 	long long v0;
102 	long long v1;
103 };
104 
105 /* called from assembly function __aeabi_ldivmod */
106 void __l_divmod(struct asm_lqr *asm_lqr);
107 void __l_divmod(struct asm_lqr *asm_lqr)
108 {
109 	long long numerator = asm_lqr->v0;
110 	long long denominator = asm_lqr->v1;
111 	struct lqr qr = { .q_n = 0, .r_n = 0 };
112 
113 	if (((numerator < 0) && (denominator > 0)) ||
114 	    ((numerator > 0) && (denominator < 0)))
115 		qr.q_n = 1;	/* quotient shall be negate */
116 	if (numerator < 0) {
117 		numerator = -numerator;
118 		qr.r_n = 1;	/* remainder shall be negate */
119 	}
120 	if (denominator < 0)
121 		denominator = -denominator;
122 
123 	ul_div_qr(numerator, denominator, &qr);
124 
125 	asm_lqr->v0 = qr.q;
126 	asm_lqr->v1 = qr.r;
127 }
128