1 /* Copyright (C) 1996, 1997 Free Software Foundation, Inc.
2    Contributed by David Mosberger (davidm@cs.arizona.edu).
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <http://www.gnu.org/licenses/>.  */
18 
19 /* The current Alpha chips don't provide hardware for integer
20    division.  The C compiler expects the functions
21 
22 	__divqu: 64-bit unsigned long divide
23 	__remqu: 64-bit unsigned long remainder
24 	__divqs/__remqs: signed 64-bit
25 	__divlu/__remlu: unsigned 32-bit
26 	__divls/__remls: signed 32-bit
27 
28    These are not normal C functions: instead of the normal calling
29    sequence, these expect their arguments in registers t10 and t11, and
30    return the result in t12 (aka pv).  Register AT may be clobbered
31    (assembly temporary), anything else must be saved.  */
32 
33 #include <features.h>
34 
35 #include <sys/regdef.h>
36 #ifdef __linux__
37 # include <asm/gentrap.h>
38 # include <asm/pal.h>
39 #else
40 # include <machine/pal.h>
41 #endif
42 
43 #define mask			v0
44 #define divisor			t0
45 #define compare			AT
46 #define tmp1			t2
47 #define tmp2			t3
48 #define retaddr			t9
49 #define arg1			t10
50 #define arg2			t11
51 #define result			t12
52 
53 
54 #if IS_REM
55 # define DIV_ONLY(x,y...)
56 # define REM_ONLY(x,y...)	x,##y
57 # define modulus		result
58 # define quotient		t1
59 # define GETSIGN(x)		mov arg1, x
60 # define STACK			32
61 #else
62 # define DIV_ONLY(x,y...)	x,##y
63 # define REM_ONLY(x,y...)
64 # define modulus		t1
65 # define quotient		result
66 # define GETSIGN(x)		xor arg1, arg2, x
67 # define STACK			48
68 #endif
69 
70 #if SIZE == 8
71 # define LONGIFY(x,y)		mov x,y
72 # define SLONGIFY(x,y)		mov x,y
73 # define _SLONGIFY(x)
74 # define NEG(x,y)		negq x,y
75 #else
76 # define LONGIFY(x,y)		zapnot x,15,y
77 # define SLONGIFY(x,y)		sextl x,y
78 # define _SLONGIFY(x)		sextl x,x
79 # define NEG(x,y)		negl x,y
80 #endif
81 
82 	.set noreorder
83 	.set noat
84 
85 	.ent UFUNC_NAME
86 	.globl UFUNC_NAME
87 #ifndef IS_IN_rtld
88 	.hidden UFUNC_NAME
89 #endif
90 
91 	.align 3
92 UFUNC_NAME:
93 	lda	sp, -STACK(sp)
94 	.frame	sp, STACK, retaddr, 0
95 	.prologue 0
96 
97 $udiv:
98 	stq	t0, 0(sp)
99 	LONGIFY	(arg2, divisor)
100 	stq	t1, 8(sp)
101 	LONGIFY	(arg1, modulus)
102 	stq	v0, 16(sp)
103 	clr	quotient
104 	stq	tmp1, 24(sp)
105 	ldiq	mask, 1
106 	DIV_ONLY(stq tmp2,32(sp))
107 
108 	beq	divisor, $divbyzero
109 
110 	.align 3
111 #if SIZE == 8
112 	/* Shift divisor left.  */
113 1:	cmpult	divisor, modulus, compare
114 	blt	divisor, 2f
115 	addq	divisor, divisor, divisor
116 	addq	mask, mask, mask
117 	bne	compare, 1b
118 	unop
119 2:
120 #else
121 	/* Shift divisor left using 3-bit shifts as we can't overflow.
122 	   This results in looping three times less here, but up to
123 	   two more times later.  Thus using a large shift isn't worth it.  */
124 1:	cmpult	divisor, modulus, compare
125 	s8addq	divisor, zero, divisor
126 	s8addq	mask, zero, mask
127 	bne	compare, 1b
128 #endif
129 
130 	/* Now go back to the right.  */
131 3:	DIV_ONLY(addq quotient, mask, tmp2)
132 	srl	mask, 1, mask
133 	cmpule	divisor, modulus, compare
134 	subq	modulus, divisor, tmp1
135 	DIV_ONLY(cmovne compare, tmp2, quotient)
136 	srl	divisor, 1, divisor
137 	cmovne	compare, tmp1, modulus
138 	bne	mask, 3b
139 
140 $done:	ldq	t0, 0(sp)
141 	ldq	t1, 8(sp)
142 	ldq	v0, 16(sp)
143 	ldq	tmp1, 24(sp)
144 	DIV_ONLY(ldq tmp2, 32(sp))
145 	lda	sp, STACK(sp)
146 	ret	zero, (retaddr), 1
147 
148 $divbyzero:
149 	mov	a0, tmp1
150 	ldiq	a0, GEN_INTDIV
151 	call_pal PAL_gentrap
152 	mov	tmp1, a0
153 	clr	result			/* If trap returns, return zero.  */
154 	br	$done
155 
156 	.end UFUNC_NAME
157 
158 	.ent SFUNC_NAME
159 	.globl SFUNC_NAME
160 
161 	.align 3
162 SFUNC_NAME:
163 	lda	sp, -STACK(sp)
164 	.frame	sp, STACK, retaddr, 0
165 	.prologue 0
166 
167 	or	arg1, arg2, AT
168 	_SLONGIFY(AT)
169 	bge	AT, $udiv		/* don't need to mess with signs */
170 
171 	/* Save originals and find absolute values.  */
172 	stq	arg1, 0(sp)
173 	NEG	(arg1, AT)
174 	stq	arg2, 8(sp)
175 	cmovge	AT, AT, arg1
176 	stq	retaddr, 16(sp)
177 	NEG	(arg2, AT)
178 	stq	tmp1, 24(sp)
179 	cmovge	AT, AT, arg2
180 
181 	/* Do the unsigned division.  */
182 	bsr	retaddr, UFUNC_NAME
183 
184 	/* Restore originals and adjust the sign of the result.  */
185 	ldq	arg1, 0(sp)
186 	ldq	arg2, 8(sp)
187 	GETSIGN	(AT)
188 	NEG	(result, tmp1)
189 	_SLONGIFY(AT)
190 	ldq	retaddr, 16(sp)
191 	cmovlt	AT, tmp1, result
192 	ldq	tmp1, 24(sp)
193 
194 	lda	sp, STACK(sp)
195 	ret	zero, (retaddr), 1
196 
197 	.end	SFUNC_NAME
198