xref: /linux/arch/xtensa/lib/udivsi3.S (revision 24bce201d79807b668bf9d9e0aca801c5c0d5f78)
1/* SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 */
2#include <linux/linkage.h>
3#include <asm/asmmacro.h>
4#include <asm/core.h>
5
6ENTRY(__udivsi3)
7
8	abi_entry_default
9#if XCHAL_HAVE_DIV32
10	quou	a2, a2, a3
11#else
12	bltui	a3, 2, .Lle_one	/* check if the divisor <= 1 */
13
14	mov	a6, a2		/* keep dividend in a6 */
15	do_nsau	a5, a6, a2, a7	/* dividend_shift = nsau (dividend) */
16	do_nsau	a4, a3, a2, a7	/* divisor_shift = nsau (divisor) */
17	bgeu	a5, a4, .Lspecial
18
19	sub	a4, a4, a5	/* count = divisor_shift - dividend_shift */
20	ssl	a4
21	sll	a3, a3		/* divisor <<= count */
22	movi	a2, 0		/* quotient = 0 */
23
24	/* test-subtract-and-shift loop; one quotient bit on each iteration */
25#if XCHAL_HAVE_LOOPS
26	loopnez	a4, .Lloopend
27#endif /* XCHAL_HAVE_LOOPS */
28.Lloop:
29	bltu	a6, a3, .Lzerobit
30	sub	a6, a6, a3
31	addi	a2, a2, 1
32.Lzerobit:
33	slli	a2, a2, 1
34	srli	a3, a3, 1
35#if !XCHAL_HAVE_LOOPS
36	addi	a4, a4, -1
37	bnez	a4, .Lloop
38#endif /* !XCHAL_HAVE_LOOPS */
39.Lloopend:
40
41	bltu	a6, a3, .Lreturn
42	addi	a2, a2, 1	/* increment quotient if dividend >= divisor */
43.Lreturn:
44	abi_ret_default
45
46.Lle_one:
47	beqz	a3, .Lerror	/* if divisor == 1, return the dividend */
48	abi_ret_default
49
50.Lspecial:
51	/* return dividend >= divisor */
52	bltu	a6, a3, .Lreturn0
53	movi	a2, 1
54	abi_ret_default
55
56.Lerror:
57	/* Divide by zero: Use an illegal instruction to force an exception.
58	   The subsequent "DIV0" string can be recognized by the exception
59	   handler to identify the real cause of the exception.  */
60	ill
61	.ascii	"DIV0"
62
63.Lreturn0:
64	movi	a2, 0
65#endif /* XCHAL_HAVE_DIV32 */
66	abi_ret_default
67
68ENDPROC(__udivsi3)
69