summaryrefslogtreecommitdiffstats
path: root/contrib/gcc/config/alpha/lib1funcs.asm
blob: e63180ade916202565ccb6634aa139f6a3b7e724 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
/* DEC Alpha division and remainder support.
   Copyright (C) 1994, 1999 Free Software Foundation, Inc.

This file is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.

In addition to the permissions in the GNU General Public License, the
Free Software Foundation gives you unlimited permission to link the
compiled version of this file with other programs, and to distribute
those programs without any restriction coming from the use of this
file.  (The General Public License restrictions do apply in other
respects; for example, they cover modification of the file, and
distribution when not linked into another program.)

This file is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

/* As a special exception, if you link this library with other files,
   some of which are compiled with GCC, to produce an executable,
   this library does not by itself cause the resulting executable
   to be covered by the GNU General Public License.
   This exception does not however invalidate any other reasons why
   the executable file might be covered by the GNU General Public License.  */

/* This had to be written in assembler because the division functions 
   use a non-standard calling convention. 

   This file provides an implementation of __divqu, __divq, __divlu, 
   __divl, __remqu, __remq, __remlu and __reml.  CPP macros control
   the exact operation.

   Operation performed: $27 := $24 o $25, clobber $28, return address to
   caller in $23, where o one of the operations.

   The following macros need to be defined: 

	SIZE, the number of bits, 32 or 64.

	TYPE, either UNSIGNED or SIGNED

	OPERATION, either DIVISION or REMAINDER
   
	SPECIAL_CALLING_CONVENTION, 0 or 1.  It is useful for debugging to
	define this to 0.  That removes the `__' prefix to make the function
	name not collide with the existing libc.a names, and uses the
	standard Alpha procedure calling convention.
*/

#ifndef SPECIAL_CALLING_CONVENTION
#define SPECIAL_CALLING_CONVENTION 1
#endif

#ifdef L_divl
#if SPECIAL_CALLING_CONVENTION
#define FUNCTION_NAME __divl
#else
#define FUNCTION_NAME divl
#endif
#define SIZE 32
#define TYPE SIGNED
#define OPERATION DIVISION
#endif

#ifdef L_divlu
#if SPECIAL_CALLING_CONVENTION
#define FUNCTION_NAME __divlu
#else
#define FUNCTION_NAME divlu
#endif
#define SIZE 32
#define TYPE UNSIGNED
#define OPERATION DIVISION
#endif

#ifdef L_divq
#if SPECIAL_CALLING_CONVENTION
#define FUNCTION_NAME __divq
#else
#define FUNCTION_NAME divq
#endif
#define SIZE 64
#define TYPE SIGNED
#define OPERATION DIVISION
#endif

#ifdef L_divqu
#if SPECIAL_CALLING_CONVENTION
#define FUNCTION_NAME __divqu
#else
#define FUNCTION_NAME divqu
#endif
#define SIZE 64
#define TYPE UNSIGNED
#define OPERATION DIVISION
#endif

#ifdef L_reml
#if SPECIAL_CALLING_CONVENTION
#define FUNCTION_NAME __reml
#else
#define FUNCTION_NAME reml
#endif
#define SIZE 32
#define TYPE SIGNED
#define OPERATION REMAINDER
#endif

#ifdef L_remlu
#if SPECIAL_CALLING_CONVENTION
#define FUNCTION_NAME __remlu
#else
#define FUNCTION_NAME remlu
#endif
#define SIZE 32
#define TYPE UNSIGNED
#define OPERATION REMAINDER
#endif

#ifdef L_remq
#if SPECIAL_CALLING_CONVENTION
#define FUNCTION_NAME __remq
#else
#define FUNCTION_NAME remq
#endif
#define SIZE 64
#define TYPE SIGNED
#define OPERATION REMAINDER
#endif

#ifdef L_remqu
#if SPECIAL_CALLING_CONVENTION
#define FUNCTION_NAME __remqu
#else
#define FUNCTION_NAME remqu
#endif
#define SIZE 64
#define TYPE UNSIGNED
#define OPERATION REMAINDER
#endif

#define tmp0 $3
#define tmp1 $28
#define cnt $1
#define result_sign $2

#if SPECIAL_CALLING_CONVENTION
#define N $24
#define D $25
#define Q RETREG
#define RETREG $27
#else
#define N $16
#define D $17
#define Q RETREG
#define RETREG $0
#endif

/* Misc symbols to make alpha assembler easier to read.  */
#define zero $31
#define sp $30

/* Symbols to make interface nicer.  */
#define UNSIGNED 0
#define SIGNED 1
#define DIVISION 0
#define REMAINDER 1

	.set noreorder
	.set noat
.text
	.align 3
	.globl FUNCTION_NAME
	.ent FUNCTION_NAME
FUNCTION_NAME:

	.frame	$30,0,$26,0
	.prologue 0

/* Under the special calling convention, we have to preserve all register
   values but $23 and $28.  */
#if SPECIAL_CALLING_CONVENTION
	lda	sp,-64(sp)
#if OPERATION == DIVISION
	stq	N,0(sp)
#endif
	stq	D,8(sp)
	stq	cnt,16(sp)
	stq	result_sign,24(sp)
	stq	tmp0,32(sp)
#endif

/* If we are computing the remainder, move N to the register that is used
   for the return value, and redefine what register is used for N.  */
#if OPERATION == REMAINDER
	bis	N,N,RETREG
#undef N
#define N RETREG
#endif

/* Perform conversion from 32 bit types to 64 bit types.  */
#if SIZE == 32
#if TYPE == SIGNED
	/* If there are problems with the signed case, add these instructions.
	   The caller should already have done this.
	addl	N,0,N		# sign extend N
	addl	D,0,D		# sign extend D
	*/
#else /* UNSIGNED */
	zap	N,0xf0,N	# zero extend N (caller required to sign extend)
	zap	D,0xf0,D	# zero extend D
#endif
#endif

/* Check for divide by zero.  */
	bne	D,$34
	lda	$16,-2(zero)
	call_pal 0xaa
$34:

#if TYPE == SIGNED
#if OPERATION == DIVISION
	xor	N,D,result_sign
#else
	bis	N,N,result_sign
#endif
/* Get the absolute values of N and D.  */
	subq	zero,N,tmp0
	cmovlt	N,tmp0,N
	subq	zero,D,tmp0
	cmovlt	D,tmp0,D
#endif

/* Compute CNT = ceil(log2(N)) - ceil(log2(D)).  This is the number of
   divide iterations we will have to perform.  Should you wish to optimize
   this, check a few bits at a time, preferably using zap/zapnot.  Be
   careful though, this code runs fast fro the most common cases, when the
   quotient is small.  */
	bge	N,$35
	bis	zero,1,cnt
	blt	D,$40
	.align	3
$39:	addq	D,D,D
	addl	cnt,1,cnt
	bge	D,$39
	br	zero,$40
$35:	cmpult	N,D,tmp0
	bis	zero,zero,cnt
	bne	tmp0,$42
	.align	3
$44:	addq	D,D,D
	cmpult	N,D,tmp0
	addl	cnt,1,cnt
	beq	tmp0,$44
$42:	srl	D,1,D
$40:
	subl	cnt,1,cnt


/* Actual divide.  Could be optimized with unrolling.  */
#if OPERATION == DIVISION
	bis	zero,zero,Q
#endif
	blt	cnt,$46
	.align	3
$49:	cmpule	D,N,tmp1
	subq	N,D,tmp0
	srl	D,1,D
	subl	cnt,1,cnt
	cmovne	tmp1,tmp0,N
#if OPERATION == DIVISION
	addq	Q,Q,Q
	bis	Q,tmp1,Q
#endif
	bge	cnt,$49
$46:


/* The result is now in RETREG.  NOTE!  It was written to RETREG using
   either N or Q as a synonym!  */


/* Change the sign of the result as needed.  */
#if TYPE == SIGNED
	subq	zero,RETREG,tmp0
	cmovlt	result_sign,tmp0,RETREG
#endif


/* Restore clobbered registers.  */
#if SPECIAL_CALLING_CONVENTION
#if OPERATION == DIVISION
	ldq	N,0(sp)
#endif
	ldq	D,8(sp)
	ldq	cnt,16(sp)
	ldq	result_sign,24(sp)
	ldq	tmp0,32(sp)

	lda	sp,64(sp)
#endif


/* Sign extend an *unsigned* 32 bit result, as required by the Alpha
   conventions.  */
#if TYPE == UNSIGNED && SIZE == 32
	/* This could be avoided by adding some CPP hair to the divide loop.
	   It is probably not worth the added complexity.    */
	addl	RETREG,0,RETREG
#endif


#if SPECIAL_CALLING_CONVENTION
	ret	zero,($23),1
#else
	ret	zero,($26),1
#endif
	.end	FUNCTION_NAME
OpenPOWER on IntegriCloud