diff options
| -rw-r--r-- | ChangeLog | 15 | ||||
| -rw-r--r-- | sysdeps/alpha/Makefile | 4 | ||||
| -rw-r--r-- | sysdeps/alpha/div_libc.h | 113 | ||||
| -rw-r--r-- | sysdeps/alpha/divl.S | 79 | ||||
| -rw-r--r-- | sysdeps/alpha/divlu.S | 4 | ||||
| -rw-r--r-- | sysdeps/alpha/divq.S | 269 | ||||
| -rw-r--r-- | sysdeps/alpha/divqu.S | 244 | ||||
| -rw-r--r-- | sysdeps/alpha/divrem.h | 225 | ||||
| -rw-r--r-- | sysdeps/alpha/reml.S | 84 | ||||
| -rw-r--r-- | sysdeps/alpha/remlu.S | 4 | ||||
| -rw-r--r-- | sysdeps/alpha/remq.S | 265 | ||||
| -rw-r--r-- | sysdeps/alpha/remqu.S | 251 |
12 files changed, 1309 insertions, 248 deletions
@@ -1,3 +1,18 @@ +2004-03-26 Richard Henderson <rth@redhat.com> + + * sysdeps/alpha/Makefile <gnulib> (sysdep_routines): Merge divrem + variable, add unsigned variants. + * sysdeps/alpha/divrem.h: Remove file. + * sysdeps/alpha/div_libc.h: New file. + * sysdeps/alpha/divl.S: Rewrite from scratch. + * sysdeps/alpha/reml.S: Likewise. + * sysdeps/alpha/divq.S: Likewise. + * sysdeps/alpha/remq.S: Likewise. + * sysdeps/alpha/divlu.S: New file. + * sysdeps/alpha/remlu.S: New file. + * sysdeps/alpha/divqu.S: New file. + * sysdeps/alpha/remqu.S: New file. + 2004-03-26 Ulrich Drepper <drepper@redhat.com> * elf/dl-open.c (check_libc_caller): Fix typo. diff --git a/sysdeps/alpha/Makefile b/sysdeps/alpha/Makefile index ce8f9b3fef..1e74d82f58 100644 --- a/sysdeps/alpha/Makefile +++ b/sysdeps/alpha/Makefile @@ -26,7 +26,7 @@ sysdep_routines += _mcount endif ifeq ($(subdir),gnulib) -sysdep_routines += $(divrem) +sysdep_routines += divl divlu divq divqu reml remlu remq remqu endif ifeq ($(subdir),string) @@ -38,8 +38,6 @@ ifeq ($(subdir),elf) CFLAGS-rtld.c = -mbuild-constants endif -divrem := divl divq reml remq - # For now, build everything with full IEEE math support. # TODO: build separate libm and libm-ieee. sysdep-CFLAGS += -mieee diff --git a/sysdeps/alpha/div_libc.h b/sysdeps/alpha/div_libc.h new file mode 100644 index 0000000000..98566435ce --- /dev/null +++ b/sysdeps/alpha/div_libc.h @@ -0,0 +1,113 @@ +/* Copyright (C) 2004 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* Common bits for implementing software divide. */ + +#include <sysdep.h> +#ifdef __linux__ +# include <asm/gentrap.h> +# include <asm/pal.h> +#else +# include <machine/pal.h> +#endif + +/* These are not normal C functions. Argument registers are t10 and t11; + the result goes in t12; the return address is in t9. Only t12 and AT + may be clobbered. */ +#define X t10 +#define Y t11 +#define RV t12 +#define RA t9 + +/* None of these functions should use implicit anything. */ + .set nomacro + .set noat + +/* Code fragment to invoke _mcount for profiling. This should be invoked + directly after allocation of the stack frame. */ +.macro CALL_MCOUNT +#ifdef PROF + stq ra, 0(sp) + stq pv, 8(sp) + stq gp, 16(sp) + cfi_rel_offset (ra, 0) + cfi_rel_offset (pv, 8) + cfi_rel_offset (gp, 16) + br AT, 1f + .set macro +1: ldgp gp, 0(AT) + mov RA, ra + lda AT, _mcount + jsr AT, (AT), _mcount + .set nomacro + ldq ra, 0(sp) + ldq pv, 8(sp) + ldq gp, 16(sp) + cfi_restore (ra) + cfi_restore (pv) + cfi_restore (gp) + /* Realign subsequent code with what we'd have without this + macro at all. This means aligned with one arithmetic insn + used within the bundle. */ + .align 4 + nop +#endif +.endm + +/* In order to make the below work, all top-level divide routines must + use the same frame size. */ +#define FRAME 48 + +/* Code fragment to generate an integer divide-by-zero fault. When + building libc.so, we arrange for there to be one copy of this code + placed late in the dso, such that all branches are forward. When + building libc.a, we use multiple copies to avoid having an out of + range branch. Users should jump to DIVBYZERO. */ + +.macro DO_DIVBYZERO +#ifdef PIC +#define DIVBYZERO __divbyzero + .section .gnu.linkonce.t.divbyzero, "ax", @progbits + .globl __divbyzero + .type __divbyzero, @function + .usepv __divbyzero, no + .hidden __divbyzero +#else +#define DIVBYZERO $divbyzero +#endif + + .align 4 +DIVBYZERO: + cfi_startproc + cfi_return_column (RA) + cfi_def_cfa_offset (FRAME) + + mov a0, RV + unop + lda a0, GEN_INTDIV + call_pal PAL_gentrap + + mov RV, a0 + clr RV + lda sp, FRAME(sp) + cfi_def_cfa_offset (0) + ret $31, (RA), 1 + + cfi_endproc + .size DIVBYZERO, .-DIVBYZERO +.endm diff --git a/sysdeps/alpha/divl.S b/sysdeps/alpha/divl.S index fdf053fc25..33fa1187d9 100644 --- a/sysdeps/alpha/divl.S +++ b/sysdeps/alpha/divl.S @@ -1,6 +1,75 @@ -#define IS_REM 0 -#define SIZE 4 -#define UFUNC_NAME __divlu -#define SFUNC_NAME __divl +/* Copyright (C) 2004 Free Software Foundation, Inc. + This file is part of the GNU C Library. -#include "divrem.h" + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include "div_libc.h" + +/* 32-bit signed int divide. This is not a normal C function. Argument + registers are t10 and t11, the result goes in t12. Only t12 and AT may + be clobbered. + + The FPU can handle all input values except zero. Whee! */ + +#ifndef EXTEND +#define EXTEND(S,D) sextl S, D +#endif + + .text + .align 4 + .globl __divl + .type __divl, @function + .usepv __divl, no + + cfi_startproc + cfi_return_column (RA) +__divl: + lda sp, -FRAME(sp) + cfi_def_cfa_offset (FRAME) + CALL_MCOUNT + stt $f0, 0(sp) + stt $f1, 8(sp) + beq Y, DIVBYZERO + cfi_rel_offset ($f0, 0) + cfi_rel_offset ($f1, 8) + + EXTEND (X, RV) + EXTEND (Y, AT) + stq RV, 16(sp) + stq AT, 24(sp) + + ldt $f0, 16(sp) + ldt $f1, 24(sp) + cvtqt $f0, $f0 + cvtqt $f1, $f1 + + divt/c $f0, $f1, $f0 + cvttq/c $f0, $f0 + stt $f0, 16(sp) + ldt $f0, 0(sp) + + ldt $f1, 8(sp) + ldl RV, 16(sp) + lda sp, FRAME(sp) + cfi_restore ($f0) + cfi_restore ($f1) + cfi_def_cfa_offset (0) + ret $31, (RA), 1 + + cfi_endproc + .size __divl, .-__divl + + DO_DIVBYZERO diff --git a/sysdeps/alpha/divlu.S b/sysdeps/alpha/divlu.S new file mode 100644 index 0000000000..5c54bb54c0 --- /dev/null +++ b/sysdeps/alpha/divlu.S @@ -0,0 +1,4 @@ +#define UNSIGNED +#define EXTEND(S,D) zapnot S, 15, D +#define __divl __divlu +#include <divl.S> diff --git a/sysdeps/alpha/divq.S b/sysdeps/alpha/divq.S index 8c88af9736..464536db3d 100644 --- a/sysdeps/alpha/divq.S +++ b/sysdeps/alpha/divq.S @@ -1,6 +1,265 @@ -#define IS_REM 0 -#define SIZE 8 -#define UFUNC_NAME __divqu -#define SFUNC_NAME __divq +/* Copyright (C) 2004 Free Software Foundation, Inc. + This file is part of the GNU C Library. -#include "divrem.h" + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include "div_libc.h" + + +/* 64-bit signed long divide. These are not normal C functions. Argument + registers are t10 and t11, the result goes in t12. Only t12 and AT may + be clobbered. + + Theory of operation here is that we can use the FPU divider for virtually + all operands that we see: all dividend values between -2**53 and 2**53-1 + can be computed directly. Note that divisor values need not be checked + against that range because the rounded fp value will be close enough such + that the quotient is < 1, which will properly be truncated to zero when we + convert back to integer. + + When the dividend is outside the range for which we can compute exact + results, we use the fp quotent as an estimate from which we begin refining + an exact integral value. This reduces the number of iterations in the + shift-and-subtract loop significantly. */ + + .text + .align 4 + .globl __divq + .type __divq, @function + .usepv __divq, no + + cfi_startproc + cfi_return_column (RA) +__divq: + lda sp, -FRAME(sp) + cfi_def_cfa_offset (FRAME) + CALL_MCOUNT + + /* Get the fp divide insn issued as quickly as possible. After + that's done, we have at least 22 cycles until its results are + ready -- all the time in the world to figure out how we're + going to use the results. */ + stq X, 16(sp) + stq Y, 24(sp) + beq Y, DIVBYZERO + + stt $f0, 0(sp) + stt $f1, 8(sp) + cfi_rel_offset ($f0, 0) + cfi_rel_offset ($f1, 8) + ldt $f0, 16(sp) + ldt $f1, 24(sp) + + cvtqt $f0, $f0 + cvtqt $f1, $f1 + divt/c $f0, $f1, $f0 + + /* Check to see if X fit in the double as an exact value. */ + sll X, (64-53), AT + ldt $f1, 8(sp) + sra AT, (64-53), AT + cmpeq X, AT, AT + beq AT, $x_big + + /* If we get here, we're expecting exact results from the division. + Do nothing else besides convert and clean up. */ + cvttq/c $f0, $f0 + stt $f0, 16(sp) + + ldq RV, 16(sp) + ldt $f0, 0(sp) + cfi_restore ($f1) + cfi_remember_state + cfi_restore ($f0) + cfi_def_cfa_offset (0) + lda sp, FRAME(sp) + ret $31, (RA), 1 + + .align 4 + cfi_restore_state +$x_big: + /* If we get here, X is large enough that we don't expect exact + results, and neither X nor Y got mis-translated for the fp + division. Our task is to take the fp result, figure out how + far it's off from the correct result and compute a fixup. */ + stq t0, 16(sp) + stq t1, 24(sp) + stq t2, 32(sp) + stq t5, 40(sp) + cfi_rel_offset (t0, 16) + cfi_rel_offset (t1, 24) + cfi_rel_offset (t2, 32) + cfi_rel_offset (t5, 40) + +#define Q RV /* quotient */ +#define R t0 /* remainder */ +#define SY t1 /* scaled Y */ +#define S t2 /* scalar */ +#define QY t3 /* Q*Y */ + + /* The fixup code below can only handle unsigned values. */ + or X, Y, AT + mov $31, t5 + blt AT, $fix_sign_in +$fix_sign_in_ret1: + cvttq/c $f0, $f0 + + stt $f0, 8(sp) + ldq Q, 8(sp) +$fix_sign_in_ret2: + mulq Q, Y, QY + stq t4, 8(sp) + + ldt $f0, 0(sp) + unop + cfi_rel_offset (t4, 8) + cfi_restore ($f0) + stq t3, 0(sp) + unop + cfi_rel_offset (t3, 0) + + subq QY, X, R + mov Y, SY + mov 1, S + bgt R, $q_high + +$q_high_ret: + subq X, QY, R + mov Y, SY + mov 1, S + bgt R, $q_low + +$q_low_ret: + ldq t0, 16(sp) + ldq t1, 24(sp) + ldq t2, 32(sp) + bne t5, $fix_sign_out + +$fix_sign_out_ret: + ldq t3, 0(sp) + ldq t4, 8(sp) + ldq t5, 40(sp) + lda sp, FRAME(sp) + cfi_remember_state + cfi_restore (t0) + cfi_restore (t1) + cfi_restore (t2) + cfi_restore (t3) + cfi_restore (t4) + cfi_restore (t5) + cfi_def_cfa_offset (0) + ret $31, (RA), 1 + + .align 4 + cfi_restore_state + /* The quotient that we computed was too large. We need to reduce + it by S such that Y*S >= R. Obviously the closer we get to the + correct value the better, but overshooting high is ok, as we'll + fix that up later. */ +0: + addq SY, SY, SY + addq S, S, S +$q_high: + cmpult SY, R, AT + bne AT, 0b + + subq Q, S, Q + unop + subq QY, SY, QY + br $q_high_ret + + .align 4 + /* The quotient that we computed was too small. Divide Y by the + current remainder (R) and add that to the existing quotient (Q). + The expectation, of course, is that R is much smaller than X. */ + /* Begin with a shift-up loop. Compute S such that Y*S >= R. We + already have a copy of Y in SY and the value 1 in S. */ +0: + addq SY, SY, SY + addq S, S, S +$q_low: + cmpult SY, R, AT + bne AT, 0b + + /* Shift-down and subtract loop. Each iteration compares our scaled + Y (SY) with the remainder (R); if SY <= R then X is divisible by + Y's scalar (S) so add it to the quotient (Q). */ +2: addq Q, S, t3 + srl S, 1, S + cmpule SY, R, AT + subq R, SY, t4 + + cmovne AT, t3, Q + cmovne AT, t4, R + srl SY, 1, SY + bne S, 2b + + br $q_low_ret + + .align 4 +$fix_sign_in: + /* If we got here, then X|Y is negative. Need to adjust everything + such that we're doing unsigned division in the fixup loop. */ + /* T5 records the changes we had to make: + bit 0: set if result should be negative. + bit 2: set if X was negated. + bit 3: set if Y was negated. + */ + xor X, Y, AT + cmplt AT, 0, t5 + cmplt X, 0, AT + negq X, t0 + + s4addq AT, t5, t5 + cmovne AT, t0, X + cmplt Y, 0, AT + negq Y, t0 + + s8addq AT, t5, t5 + cmovne AT, t0, Y + unop + blbc t5, $fix_sign_in_ret1 + + cvttq/c $f0, $f0 + stt $f0, 8(sp) + ldq Q, 8(sp) + unop + + negq Q, Q + br $fix_sign_in_ret2 + + .align 4 +$fix_sign_out: + /* Now we get to undo what we did above. */ + /* ??? Is this really faster than just increasing the size of + the stack frame and storing X and Y in memory? */ + and t5, 8, AT + negq Y, t4 + cmovne AT, t4, Y + + and t5, 4, AT + negq X, t4 + cmovne AT, t4, X + + negq RV, t4 + cmovlbs t5, t4, RV + + br $fix_sign_out_ret + + cfi_endproc + .size __divq, .-__divq + + DO_DIVBYZERO diff --git a/sysdeps/alpha/divqu.S b/sysdeps/alpha/divqu.S new file mode 100644 index 0000000000..6ff6c035e2 --- /dev/null +++ b/sysdeps/alpha/divqu.S @@ -0,0 +1,244 @@ +/* Copyright (C) 2004 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include "div_libc.h" + + +/* 64-bit unsigned long divide. These are not normal C functions. Argument + registers are t10 and t11, the result goes in t12. Only t12 and AT may be + clobbered. + + Theory of operation here is that we can use the FPU divider for virtually + all operands that we see: all dividend values between -2**53 and 2**53-1 + can be computed directly. Note that divisor values need not be checked + against that range because the rounded fp value will be close enough such + that the quotient is < 1, which will properly be truncated to zero when we + convert back to integer. + + When the dividend is outside the range for which we can compute exact + results, we use the fp quotent as an estimate from which we begin refining + an exact integral value. This reduces the number of iterations in the + shift-and-subtract loop significantly. */ + + .text + .align 4 + .globl __divqu + .type __divqu, @function + .usepv __divqu, no + + cfi_startproc + cfi_return_column (RA) +__divqu: + lda sp, -FRAME(sp) + cfi_def_cfa_offset (FRAME) + CALL_MCOUNT + + /* Get the fp divide insn issued as quickly as possible. After + that's done, we have at least 22 cycles until its results are + ready -- all the time in the world to figure out how we're + going to use the results. */ + stq X, 16(sp) + stq Y, 24(sp) + beq Y, DIVBYZERO + + stt $f0, 0(sp) + stt $f1, 8(sp) + cfi_rel_offset ($f0, 0) + cfi_rel_offset ($f1, 8) + ldt $f0, 16(sp) + ldt $f1, 24(sp) + + cvtqt $f0, $f0 + cvtqt $f1, $f1 + blt X, $x_is_neg + divt/c $f0, $f1, $f0 + + /* Check to see if Y was mis-converted as signed value. */ + ldt $f1, 8(sp) + unop + nop + blt Y, $y_is_neg + + /* Check to see if X fit in the double as an exact value. */ + srl X, 53, AT + bne AT, $x_big + + /* If we get here, we're expecting exact results from the division. + Do nothing else besides convert and clean up. */ + cvttq/c $f0, $f0 + stt $f0, 16(sp) + + ldq RV, 16(sp) + ldt $f0, 0(sp) + cfi_remember_state + cfi_restore ($f0) + cfi_restore ($f1) + cfi_def_cfa_offset (0) + lda sp, FRAME(sp) + ret $31, (RA), 1 + + .align 4 + cfi_restore_state +$x_is_neg: + /* If we get here, X is so big that bit 63 is set, which made the + conversion come out negative. Fix it up lest we not even get + a good estimate. */ + ldah AT, 0x5f80 /* 2**64 as float. */ + stt $f2, 24(sp) + cfi_rel_offset ($f2, 24) + stl AT, 16(sp) + lds $f2, 16(sp) + + addt $f0, $f2, $f0 + unop + divt/c $f0, $f1, $f0 + unop + + /* Ok, we've now the divide issued. Continue with other checks. */ + ldt $f1, 8(sp) + unop + ldt $f2, 24(sp) + blt Y, $y_is_neg + cfi_restore ($f1) + cfi_restore ($f2) + cfi_remember_state /* for y_is_neg */ + + .align 4 +$x_big: + /* If we get here, X is large enough that we don't expect exact + results, and neither X nor Y got mis-translated for the fp + division. Our task is to take the fp result, figure out how + far it's off from the correct result and compute a fixup. */ + stq t0, 16(sp) + stq t1, 24(sp) + stq t2, 32(sp) + stq t3, 40(sp) + cfi_rel_offset (t0, 16) + cfi_rel_offset (t1, 24) + cfi_rel_offset (t2, 32) + cfi_rel_offset (t3, 40) + +#define Q RV /* quotient */ +#define R t0 /* remainder */ +#define SY t1 /* scaled Y */ +#define S t2 /* scalar */ +#define QY t3 /* Q*Y */ + + cvttq/c $f0, $f0 + stt $f0, 8(sp) + ldq Q, 8(sp) + mulq Q, Y, QY + + stq t4, 8(sp) + unop + ldt $f0, 0(sp) + unop + cfi_rel_offset (t4, 8) + cfi_restore ($f0) + + subq QY, X, R + mov Y, SY + mov 1, S + bgt R, $q_high + +$q_high_ret: + subq X, QY, R + mov Y, SY + mov 1, S + bgt R, $q_low + +$q_low_ret: + ldq t4, 8(sp) + ldq t0, 16(sp) + ldq t1, 24(sp) + ldq t2, 32(sp) + + ldq t3, 40(sp) + lda sp, FRAME(sp) + cfi_remember_state + cfi_restore (t0) + cfi_restore (t1) + cfi_restore (t2) + cfi_restore (t3) + cfi_restore (t4) + cfi_def_cfa_offset (0) + ret $31, (RA), 1 + + .align 4 + cfi_restore_state + /* The quotient that we computed was too large. We need to reduce + it by S such that Y*S >= R. Obviously the closer we get to the + correct value the better, but overshooting high is ok, as we'll + fix that up later. */ +0: + addq SY, SY, SY + addq S, S, S +$q_high: + cmpult SY, R, AT + bne AT, 0b + + subq Q, S, Q + unop + subq QY, SY, QY + br $q_high_ret + + .align 4 + /* The quotient that we computed was too small. Divide Y by the + current remainder (R) and add that to the existing quotient (Q). + The expectation, of course, is that R is much smaller than X. */ + /* Begin with a shift-up loop. |
