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