diff options
Diffstat (limited to 'gcc/config/pa')
-rwxr-xr-x | gcc/config/pa/ee.asm | 261 | ||||
-rwxr-xr-x | gcc/config/pa/ee_fp.asm | 274 | ||||
-rwxr-xr-x | gcc/config/pa/lib1funcs.asm | 1146 | ||||
-rwxr-xr-x | gcc/config/pa/lib2funcs.asm | 74 | ||||
-rwxr-xr-x | gcc/config/pa/pa-gas.h | 22 | ||||
-rwxr-xr-x | gcc/config/pa/pa-hiux.h | 26 | ||||
-rwxr-xr-x | gcc/config/pa/pa-hpux.h | 49 | ||||
-rwxr-xr-x | gcc/config/pa/pa-hpux10.h | 89 | ||||
-rwxr-xr-x | gcc/config/pa/pa-hpux11.h | 98 | ||||
-rwxr-xr-x | gcc/config/pa/pa-hpux7.h | 37 | ||||
-rwxr-xr-x | gcc/config/pa/pa-hpux9.h | 31 | ||||
-rwxr-xr-x | gcc/config/pa/pa-oldas.h | 22 | ||||
-rwxr-xr-x | gcc/config/pa/pa-osf.h | 42 | ||||
-rwxr-xr-x | gcc/config/pa/pa-pro-end.h | 42 | ||||
-rwxr-xr-x | gcc/config/pa/pa-pro.h | 79 | ||||
-rwxr-xr-x | gcc/config/pa/pa.c | 6491 | ||||
-rwxr-xr-x | gcc/config/pa/pa.h | 2601 | ||||
-rwxr-xr-x | gcc/config/pa/pa.md | 5729 | ||||
-rwxr-xr-x | gcc/config/pa/pa1.h | 28 | ||||
-rwxr-xr-x | gcc/config/pa/rtems.h | 31 | ||||
-rwxr-xr-x | gcc/config/pa/t-dce-thr | 5 | ||||
-rwxr-xr-x | gcc/config/pa/t-pa | 18 | ||||
-rwxr-xr-x | gcc/config/pa/t-pro | 38 | ||||
-rwxr-xr-x | gcc/config/pa/x-pa | 3 | ||||
-rwxr-xr-x | gcc/config/pa/x-pa-hpux | 4 | ||||
-rwxr-xr-x | gcc/config/pa/xm-pa.h | 62 | ||||
-rwxr-xr-x | gcc/config/pa/xm-pahpux.h | 61 | ||||
-rwxr-xr-x | gcc/config/pa/xm-papro.h | 58 |
28 files changed, 17421 insertions, 0 deletions
diff --git a/gcc/config/pa/ee.asm b/gcc/config/pa/ee.asm new file mode 100755 index 0000000..787bda7 --- /dev/null +++ b/gcc/config/pa/ee.asm @@ -0,0 +1,261 @@ +; Subroutines for out of line prologues and epilogues on for the HPPA +; Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc. + +; This file is part of GNU CC. + +; GNU CC 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. + +; GNU CC 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 GNU CC; see the file COPYING. If not, write to +; the Free Software Foundation, 59 Temple Place - Suite 330, +; Boston, MA 02111-1307, USA. + + .SPACE $PRIVATE$ + .SUBSPA $DATA$,QUAD=1,ALIGN=8,ACCESS=31 + .SUBSPA $BSS$,QUAD=1,ALIGN=8,ACCESS=31,ZERO,SORT=82 + .SPACE $TEXT$ + .SUBSPA $LIT$,QUAD=0,ALIGN=8,ACCESS=44 + .SUBSPA $CODE$,QUAD=0,ALIGN=8,ACCESS=44,CODE_ONLY + .SUBSPA $MILLICODE$,QUAD=0,ALIGN=8,ACCESS=44,SORT=8 + +; This is an out-of-line prologue. +; +; It performs the following operations: +; +; * Saves the return pointer at sp - 20 +; +; * Creates a new stack frame (sp'), size of the frame is passed in %r21 +; +; * The old stack pointer is saved at sp (frame pointer version only). +; +; * Saves grs (passed in low 16 bits of %r22 into the stack frame +; at sp' + local_fsize (passed in %r19). +; +; * Saves frs (passed in high 16 bits of %r22) into the stack +; frame at sp' + local_fsize (passed in %r19). +; +; * Sets up a frame pointer (in %r3) (frame pointer version only). +; +; * Returns to the instruction _immediately_ after the call to +; this function. + + .SPACE $TEXT$ + .SUBSPA $MILLICODE$ + .EXPORT __outline_prologue,MILLICODE + .align 32 +__outline_prologue + .PROC + .CALLINFO FRAME=0,NO_CALLS + .ENTRY + copy %r30,%r20 + + ; Subtract 4 from our return pointer so that we return to + ; the right location. + ldo -4(%r31),%r31 + + ; Save off %r2 + stw %r2,-20(0,%r30) + + ; Make our new frame. + add %r21,%r30,%r30 + + ; Add in local_fsize to our frame pointer so we do register + ; saves into the right place + add %r20,%r19,%r20 + + ; %r22 tells us what registers we need to save. The upper half + ; is for fp registers, the lower half for integer registers. + ; We put the lower half in %r1 and the upper half into %r22 + ; for later use. + extru %r22,31,16,%r1 + extrs %r22,15,16,%r22 + + ; %r1 now olds a value 0-18 which corresponds to the number + ; of grs we need to save. We need to reverse that value so + ; we can just into the table and straight-line execute to the + ; end of the gr saves. + comb,= %r0,%r1,L$0000 + subi 18,%r1,%r1 + blr,n %r1,%r0 + b,n L$0000 + stws,ma %r18,4(0,%r20) + nop + stws,ma %r17,4(0,%r20) + nop + stws,ma %r16,4(0,%r20) + nop + stws,ma %r15,4(0,%r20) + nop + stws,ma %r14,4(0,%r20) + nop + stws,ma %r13,4(0,%r20) + nop + stws,ma %r12,4(0,%r20) + nop + stws,ma %r11,4(0,%r20) + nop + stws,ma %r10,4(0,%r20) + nop + stws,ma %r9,4(0,%r20) + nop + stws,ma %r8,4(0,%r20) + nop + stws,ma %r7,4(0,%r20) + nop + stws,ma %r6,4(0,%r20) + nop + stws,ma %r5,4(0,%r20) + nop + stws,ma %r4,4(0,%r20) + nop + stws,ma %r3,4(0,%r20) + nop +L$0000 + ; All gr saves are done. Align the temporary frame pointer and + ; do the fr saves. + ldo 7(%r20),%r20 + depi 0,31,3,%r20 + + comb,= %r0,%r22,L$0001 + subi 21,%r22,%r22 + blr,n %r22,%r0 + b,n L$0001 + fstws,ma %fr21,8(0,%r20) + nop + fstws,ma %fr20,8(0,%r20) + nop + fstws,ma %fr19,8(0,%r20) + nop + fstws,ma %fr18,8(0,%r20) + nop + fstws,ma %fr17,8(0,%r20) + nop + fstws,ma %fr16,8(0,%r20) + nop + fstws,ma %fr15,8(0,%r20) + nop + fstws,ma %fr14,8(0,%r20) + nop + fstws,ma %fr13,8(0,%r20) + nop + fstws,ma %fr12,8(0,%r20) + nop +L$0001 + ; Return + bv,n 0(%r31) + .EXIT + .PROCEND + + + + .EXPORT __outline_epilogue,MILLICODE + .align 32 +__outline_epilogue + .PROC + .CALLINFO FRAME=0,NO_CALLS + .ENTRY + ; Get our original stack pointer and put it in %r20 + sub %r30,%r21,%r20 + + ; Subtract 4 from our return pointer so that we return to + ; the right location. + ldo -4(%r31),%r31 + + ; Reload %r2 + ldw -20(0,%r20),%r2 + + ; Add in local_fsize (%r19) to the frame pointer to find + ; the saved registers. + add %r20,%r19,%r20 + + ; %r22 tells us what registers we need to restore. The upper half + ; is for fp registers, the lower half for integer registers. + ; We put the lower half in %r1 and the upper half into %r22 + ; for later use. + extru %r22,31,16,%r1 + extrs %r22,15,16,%r22 + + ; %r1 now olds a value 0-18 which corresponds to the number + ; of grs we need to restore. We need to reverse that value so + ; we can just into the table and straight-line execute to the + ; end of the gr restore. + comb,= %r0,%r1,L$0004 + subi 18,%r1,%r1 + blr,n %r1,%r0 + b,n L$0004 + ldws,ma 4(0,%r20),%r18 + nop + ldws,ma 4(0,%r20),%r17 + nop + ldws,ma 4(0,%r20),%r16 + nop + ldws,ma 4(0,%r20),%r15 + nop + ldws,ma 4(0,%r20),%r14 + nop + ldws,ma 4(0,%r20),%r13 + nop + ldws,ma 4(0,%r20),%r12 + nop + ldws,ma 4(0,%r20),%r11 + nop + ldws,ma 4(0,%r20),%r10 + nop + ldws,ma 4(0,%r20),%r9 + nop + ldws,ma 4(0,%r20),%r8 + nop + ldws,ma 4(0,%r20),%r7 + nop + ldws,ma 4(0,%r20),%r6 + nop + ldws,ma 4(0,%r20),%r5 + nop + ldws,ma 4(0,%r20),%r4 + nop + ldws,ma 4(0,%r20),%r3 + nop +L$0004 + ; All gr restore are done. Align the temporary frame pointer and + ; do the fr restore. + ldo 7(%r20),%r20 + depi 0,31,3,%r20 + + comb,= %r0,%r22,L$0005 + subi 21,%r22,%r22 + blr,n %r22,%r0 + b,n L$0005 + fldws,ma 8(0,%r20),%fr21 + nop + fldws,ma 8(0,%r20),%fr20 + nop + fldws,ma 8(0,%r20),%fr19 + nop + fldws,ma 8(0,%r20),%fr18 + nop + fldws,ma 8(0,%r20),%fr17 + nop + fldws,ma 8(0,%r20),%fr16 + nop + fldws,ma 8(0,%r20),%fr15 + nop + fldws,ma 8(0,%r20),%fr14 + nop + fldws,ma 8(0,%r20),%fr13 + nop + fldws,ma 8(0,%r20),%fr12 + nop +L$0005 + ; Return and deallocate our frame. + bv 0(%r31) + sub %r30,%r21,%r30 + .EXIT + .PROCEND diff --git a/gcc/config/pa/ee_fp.asm b/gcc/config/pa/ee_fp.asm new file mode 100755 index 0000000..ef040cf --- /dev/null +++ b/gcc/config/pa/ee_fp.asm @@ -0,0 +1,274 @@ +; Subroutines for out of line prologues and epilogues on for the HPPA +; Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc. + +; This file is part of GNU CC. + +; GNU CC 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. + +; GNU CC 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 GNU CC; see the file COPYING. If not, write to +; the Free Software Foundation, 59 Temple Place - Suite 330, +; Boston, MA 02111-1307, USA. + + .SPACE $PRIVATE$ + .SUBSPA $DATA$,QUAD=1,ALIGN=8,ACCESS=31 + .SUBSPA $BSS$,QUAD=1,ALIGN=8,ACCESS=31,ZERO,SORT=82 + .SPACE $TEXT$ + .SUBSPA $LIT$,QUAD=0,ALIGN=8,ACCESS=44 + .SUBSPA $CODE$,QUAD=0,ALIGN=8,ACCESS=44,CODE_ONLY + .SUBSPA $MILLICODE$,QUAD=0,ALIGN=8,ACCESS=44,SORT=8 + + +; This is an out-of-line prologue. +; +; It performs the following operations: +; +; * Saves the return pointer at sp - 20 +; +; * Creates a new stack frame (sp'), size of the frame is passed in %r21 +; +; * The old stack pointer is saved at sp (frame pointer version only). +; +; * Saves grs (passed in low 16 bits of %r22 into the stack frame +; at sp' + local_fsize (passed in %r19). +; +; * Saves frs (passed in high 16 bits of %r22) into the stack +; frame at sp' + local_fsize (passed in %r19). +; +; * Sets up a frame pointer (in %r3) (frame pointer version only). +; +; * Returns to the instruction _immediately_ after the call to +; this function. + + .SPACE $TEXT$ + .SUBSPA $MILLICODE$ + .EXPORT __outline_prologue_fp,MILLICODE + .align 32 +__outline_prologue_fp + .PROC + .CALLINFO FRAME=0,NO_CALLS + .ENTRY + copy %r30,%r20 + + ; Subtract 4 from our return pointer so that we return to + ; the right location. + ldo -4(%r31),%r31 + + ; Save off %r2 + stw %r2,-20(0,%r30) + + ; Make our new frame. + add %r21,%r30,%r30 + + ; Save our old stack pointer. + stw %r20,0(0,%r20) + + ; Add in local_fsize to our frame pointer so we do register + ; saves into the right place + add %r20,%r19,%r20 + + ; %r22 tells us what registers we need to save. The upper half + ; is for fp registers, the lower half for integer registers. + ; We put the lower half in %r1 and the upper half into %r22 + ; for later use. + extru %r22,31,16,%r1 + extrs %r22,15,16,%r22 + + ; %r1 now olds a value 0-18 which corresponds to the number + ; of grs we need to save. We need to reverse that value so + ; we can just into the table and straight-line execute to the + ; end of the gr saves. + comb,= %r0,%r1,L$0002 + subi 18,%r1,%r1 + blr,n %r1,%r0 + b,n L$0002 + stws,ma %r18,4(0,%r20) + nop + stws,ma %r17,4(0,%r20) + nop + stws,ma %r16,4(0,%r20) + nop + stws,ma %r15,4(0,%r20) + nop + stws,ma %r14,4(0,%r20) + nop + stws,ma %r13,4(0,%r20) + nop + stws,ma %r12,4(0,%r20) + nop + stws,ma %r11,4(0,%r20) + nop + stws,ma %r10,4(0,%r20) + nop + stws,ma %r9,4(0,%r20) + nop + stws,ma %r8,4(0,%r20) + nop + stws,ma %r7,4(0,%r20) + nop + stws,ma %r6,4(0,%r20) + nop + stws,ma %r5,4(0,%r20) + nop + stws,ma %r4,4(0,%r20) + nop + stws,ma %r3,4(0,%r20) + nop +L$0002 + ; All gr saves are done. Align the temporary frame pointer and + ; do the fr saves. + ldo 7(%r20),%r20 + depi 0,31,3,%r20 + + comb,= %r0,%r22,L$0003 + subi 21,%r22,%r22 + blr,n %r22,%r0 + b,n L$0003 + fstws,ma %fr21,8(0,%r20) + nop + fstws,ma %fr20,8(0,%r20) + nop + fstws,ma %fr19,8(0,%r20) + nop + fstws,ma %fr18,8(0,%r20) + nop + fstws,ma %fr17,8(0,%r20) + nop + fstws,ma %fr16,8(0,%r20) + nop + fstws,ma %fr15,8(0,%r20) + nop + fstws,ma %fr14,8(0,%r20) + nop + fstws,ma %fr13,8(0,%r20) + nop + fstws,ma %fr12,8(0,%r20) + nop +L$0003 + ; Return, setting up a frame pointer in the delay slot + bv 0(%r31) + sub %r30,%r21,%r3 + .EXIT + .PROCEND + + +; This is an out-of-line epilogue. It's operation is basically the reverse +; of the out-of-line prologue. + + .EXPORT __outline_epilogue_fp,MILLICODE + .align 32 +__outline_epilogue_fp + .PROC + .CALLINFO FRAME=0,NO_CALLS + .ENTRY + ; Make a copy of our frame pointer into %r20 + copy %r3,%r20 + + ; Subtract 4 from our return pointer so that we return to + ; the right location. + ldo -4(%r31),%r31 + + ; Reload %r2 + ; First save off %r2 + ldw -20(0,%r20),%r2 + + ; Load our old stack pointer, save it in %r21. + ldw 0(0,%r20),%r21 + + ; Add in local_fsize (%r19) to the frame pointer to find + ; the saved registers. + add %r20,%r19,%r20 + + ; %r22 tells us what registers we need to restore. The upper half + ; is for fp registers, the lower half for integer registers. + ; We put the lower half in %r1 and the upper half into %r22 + ; for later use. + extru %r22,31,16,%r1 + extrs %r22,15,16,%r22 + + ; %r1 now olds a value 0-18 which corresponds to the number + ; of grs we need to restore. We need to reverse that value so + ; we can just into the table and straight-line execute to the + ; end of the gr restore. + comb,= %r0,%r1,L$0006 + subi 18,%r1,%r1 + blr,n %r1,%r0 + b,n L$0006 + ldws,ma 4(0,%r20),%r18 + nop + ldws,ma 4(0,%r20),%r17 + nop + ldws,ma 4(0,%r20),%r16 + nop + ldws,ma 4(0,%r20),%r15 + nop + ldws,ma 4(0,%r20),%r14 + nop + ldws,ma 4(0,%r20),%r13 + nop + ldws,ma 4(0,%r20),%r12 + nop + ldws,ma 4(0,%r20),%r11 + nop + ldws,ma 4(0,%r20),%r10 + nop + ldws,ma 4(0,%r20),%r9 + nop + ldws,ma 4(0,%r20),%r8 + nop + ldws,ma 4(0,%r20),%r7 + nop + ldws,ma 4(0,%r20),%r6 + nop + ldws,ma 4(0,%r20),%r5 + nop + ldws,ma 4(0,%r20),%r4 + nop + ldws,ma 4(0,%r20),%r3 + nop +L$0006 + ; All gr restore are done. Align the temporary frame pointer and + ; do the fr restore. + ldo 7(%r20),%r20 + depi 0,31,3,%r20 + + comb,= %r0,%r22,L$0007 + subi 21,%r22,%r22 + blr,n %r22,%r0 + b,n L$0007 + fldws,ma 8(0,%r20),%fr21 + nop + fldws,ma 8(0,%r20),%fr20 + nop + fldws,ma 8(0,%r20),%fr19 + nop + fldws,ma 8(0,%r20),%fr18 + nop + fldws,ma 8(0,%r20),%fr17 + nop + fldws,ma 8(0,%r20),%fr16 + nop + fldws,ma 8(0,%r20),%fr15 + nop + fldws,ma 8(0,%r20),%fr14 + nop + fldws,ma 8(0,%r20),%fr13 + nop + fldws,ma 8(0,%r20),%fr12 + nop +L$0007 + ; Return and deallocate our frame. + bv 0(%r31) + copy %r21,%r30 + .EXIT + .PROCEND + + diff --git a/gcc/config/pa/lib1funcs.asm b/gcc/config/pa/lib1funcs.asm new file mode 100755 index 0000000..95eb75e --- /dev/null +++ b/gcc/config/pa/lib1funcs.asm @@ -0,0 +1,1146 @@ +; Low level integer divide, multiply, remainder, etc routines for the HPPA. +; Copyright (C) 1995 Free Software Foundation, Inc. + +; This file is part of GNU CC. + +; GNU CC 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.) + +; GNU CC 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 GNU CC; see the file COPYING. If not, write to +; the Free Software Foundation, 59 Temple Place - Suite 330, +; Boston, MA 02111-1307, USA. + +#ifdef L_dyncall + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .export $$dyncall +$$dyncall + .proc + .callinfo frame=0,no_calls + .entry + bb,>=,n %r22,30,L$1 ; branch if not plabel address + depi 0,31,2,%r22 ; clear the two least significant bits + ldw 4(%sr0,%r22),%r19 ; load new LTP value + ldw 0(%sr0,%r22),%r22 ; load address of target +L$1 ldsid (%sr0,%r22),%r1 ; get the "space ident" selected by r22 + mtsp %r1,%sr0 ; move that space identifier into sr0 + be 0(%sr0,%r22) ; branch to the real target + stw %r2,-24(%sr0,%r30) ; save return address into frame marker + .exit + .procend +#endif + + +#ifdef L_multiply +#define op0 %r26 +#define op1 %r25 +#define res %r29 +#define ret %r31 +#define tmp %r1 + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .align 4 + .export $$mulU + .export $$mulI +$$mulU +$$mulI + .proc + .callinfo frame=0,no_calls + .entry + addi,tr 0,%r0,res ; clear out res, skip next insn +L$loop zdep op1,26,27,op1 ; shift up op1 by 5 +L$lo zdep op0,30,5,tmp ; extract next 5 bits and shift up + blr tmp,%r0 + extru op0,26,27,op0 ; shift down op0 by 5 +L$0 comib,<> 0,op0,L$lo + zdep op1,26,27,op1 ; shift up op1 by 5 + bv %r0(ret) + nop +L$1 b L$loop + addl op1,res,res + nop + nop +L$2 b L$loop + sh1addl op1,res,res + nop + nop +L$3 sh1addl op1,op1,tmp ; 3x + b L$loop + addl tmp,res,res + nop +L$4 b L$loop + sh2addl op1,res,res + nop + nop +L$5 sh2addl op1,op1,tmp ; 5x + b L$loop + addl tmp,res,res + nop +L$6 sh1addl op1,op1,tmp ; 3x + b L$loop + sh1addl tmp,res,res + nop +L$7 zdep op1,28,29,tmp ; 8x + sub tmp,op1,tmp ; 7x + b L$loop + addl tmp,res,res +L$8 b L$loop + sh3addl op1,res,res + nop + nop +L$9 sh3addl op1,op1,tmp ; 9x + b L$loop + addl tmp,res,res + nop +L$10 sh2addl op1,op1,tmp ; 5x + b L$loop + sh1addl tmp,res,res + nop +L$11 sh2addl op1,op1,tmp ; 5x + sh1addl tmp,op1,tmp ; 11x + b L$loop + addl tmp,res,res +L$12 sh1addl op1,op1,tmp ; 3x + b L$loop + sh2addl tmp,res,res + nop +L$13 sh1addl op1,op1,tmp ; 3x + sh2addl tmp,op1,tmp ; 13x + b L$loop + addl tmp,res,res +L$14 zdep op1,28,29,tmp ; 8x + sub tmp,op1,tmp ; 7x + b L$loop + sh1addl tmp,res,res +L$15 zdep op1,27,28,tmp ; 16x + sub tmp,op1,tmp ; 15x + b L$loop + addl tmp,res,res +L$16 zdep op1,27,28,tmp ; 16x + b L$loop + addl tmp,res,res + nop +L$17 zdep op1,27,28,tmp ; 16x + addl tmp,op1,tmp ; 17x + b L$loop + addl tmp,res,res +L$18 sh3addl op1,op1,tmp ; 9x + b L$loop + sh1addl tmp,res,res + nop +L$19 sh3addl op1,op1,tmp ; 9x + sh1addl tmp,op1,tmp ; 19x + b L$loop + addl tmp,res,res +L$20 sh2addl op1,op1,tmp ; 5x + b L$loop + sh2addl tmp,res,res + nop +L$21 sh2addl op1,op1,tmp ; 5x + sh2addl tmp,op1,tmp ; 21x + b L$loop + addl tmp,res,res +L$22 sh2addl op1,op1,tmp ; 5x + sh1addl tmp,op1,tmp ; 11x + b L$loop + sh1addl tmp,res,res +L$23 sh1addl op1,op1,tmp ; 3x + sh3addl tmp,res,res ; += 8x3 + b L$loop + sub res,op1,res ; -= x +L$24 sh1addl op1,op1,tmp ; 3x + b L$loop + sh3addl tmp,res,res ; += 8x3 + nop +L$25 sh2addl op1,op1,tmp ; 5x + sh2addl tmp,tmp,tmp ; 25x + b L$loop + addl tmp,res,res +L$26 sh1addl op1,op1,tmp ; 3x + sh2addl tmp,op1,tmp ; 13x + b L$loop + sh1addl tmp,res,res ; += 2x13 +L$27 sh1addl op1,op1,tmp ; 3x + sh3addl tmp,tmp,tmp ; 27x + b L$loop + addl tmp,res,res +L$28 zdep op1,28,29,tmp ; 8x + sub tmp,op1,tmp ; 7x + b L$loop + sh2addl tmp,res,res ; += 4x7 +L$29 sh1addl op1,op1,tmp ; 3x + sub res,tmp,res ; -= 3x + b L$foo + zdep op1,26,27,tmp ; 32x +L$30 zdep op1,27,28,tmp ; 16x + sub tmp,op1,tmp ; 15x + b L$loop + sh1addl tmp,res,res ; += 2x15 +L$31 zdep op1,26,27,tmp ; 32x + sub tmp,op1,tmp ; 31x +L$foo b L$loop + addl tmp,res,res + .exit + .procend +#endif + + +#ifdef L_divU +#define dividend %r26 +#define divisor %r25 +#define tmp %r1 +#define quotient %r29 +#define ret %r31 + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .align 4 + .export $$divU +$$divU + .proc + .callinfo frame=0,no_calls + .entry + comb,< divisor,0,L$largedivisor + sub %r0,divisor,%r1 ; clear cy as side-effect + ds %r0,%r1,%r0 + addc dividend,dividend,dividend + ds %r0,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,quotient + ds %r1,divisor,%r1 + bv 0(ret) + addc quotient,quotient,quotient +L$largedivisor + comclr,<< dividend,divisor,quotient + ldi 1,quotient + bv,n 0(ret) + .exit + .procend +#endif + + +#ifdef L_remU +#define dividend %r26 +#define divisor %r25 +#define quotient %r29 +#define tmp %r1 +#define ret %r31 + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .align 4 + .export $$remU +$$remU + .proc + .callinfo frame=0,no_calls + .entry + comb,< divisor,0,L$largedivisor + sub %r0,divisor,%r1 ; clear cy as side-effect + ds %r0,%r1,%r0 + addc dividend,dividend,dividend + ds %r0,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,quotient + ds %r1,divisor,%r1 + comclr,>= %r1,%r0,%r0 + addl %r1,divisor,%r1 + bv 0(ret) + copy %r1,quotient +L$largedivisor + sub,>>= dividend,divisor,quotient + copy dividend,quotient + bv,n 0(ret) + .exit + .procend +#endif + + +#ifdef L_divI +#define dividend %r26 +#define divisor %r25 +#define quotient %r29 +#define tmp %r1 +#define ret %r31 + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .align 4 + .export $$divI +$$divI + .proc + .callinfo frame=0,no_calls + .entry + xor dividend,divisor,quotient ; result sign + comclr,>= divisor,%r0,%r0 ; get absolute values + sub %r0,divisor,divisor + comclr,>= dividend,%r0,%r0 + sub %r0,dividend,dividend + + comb,< divisor,0,L$largedivisor + sub %r0,divisor,%r1 ; clear cy as side-effect + ds %r0,%r1,%r0 + addc dividend,dividend,dividend + ds %r0,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + comclr,>= %r1,%r0,%r0 + addl %r1,divisor,%r1 + comclr,>= quotient,%r0,%r0 ; skip of no need to negate + sub %r0,dividend,dividend + bv 0(ret) + copy dividend,quotient +L$largedivisor + comclr,<< dividend,divisor,quotient + ldi 1,quotient + bv,n 0(ret) + .exit + .procend +#endif + + +#ifdef L_remI +#define dividend %r26 +#define divisor %r25 +#define quotient %r29 +#define tmp %r1 +#define ret %r31 + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .align 4 + .export $$remI +$$remI + .proc + .callinfo frame=0,no_calls + .entry + xor dividend,%r0,quotient ; result sign + comclr,>= divisor,%r0,%r0 ; get absolute values + sub %r0,divisor,divisor + comclr,>= dividend,%r0,%r0 + sub %r0,dividend,dividend + + comb,< divisor,0,L$largedivisor + sub %r0,divisor,%r1 ; clear cy as side-effect + ds %r0,%r1,%r0 + addc dividend,dividend,dividend + ds %r0,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + ds %r1,divisor,%r1 + addc dividend,dividend,dividend + comclr,>= %r1,%r0,%r0 + addl %r1,divisor,%r1 + comclr,>= quotient,%r0,%r0 ; skip of no need to negate + sub %r0,%r1,%r1 + bv 0(ret) + copy %r1,quotient +L$largedivisor + sub,>>= dividend,divisor,quotient + copy dividend,quotient + bv,n 0(ret) + .exit + .procend +#endif + + +#if defined (L_divU_3) && !defined (SMALL_LIB) +#undef L_divU_3 +#define dividend %r26 +#define divisor %r25 +#define tmp %r1 +#define result %r29 +#define ret %r31 + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .align 4 + .export $$divU_3 +$$divU_3 + .proc + .callinfo frame=0,no_calls + .entry + sh2add %r26,%r26,%r29 ; r29 = lo(101 x r) + shd %r0,%r26,30,%r1 ; r1 = hi(100 x r) + addc %r1,%r0,%r1 ; r1 = hi(101 x r) +; r in r1,,r29 + zdep %r29,27,28,%r25 ; r25 = lo(10000 x r) + add %r25,%r29,%r25 ; r25 = lo(10001 x r) + shd %r1,%r29,28,%r29 ; r29 = hi(10000 x r) + addc %r29,%r1,%r29 ; r29 = hi(10001 x r) +; r in r29,,r25 + zdep %r25,23,24,%r1 ; r1 = lo(100000000 x r) + add %r1,%r25,%r1 ; r1 = lo(100000001 x r) + shd %r29,%r25,24,%r25 ; r25 = hi(100000000 x r) + addc %r25,%r29,%r25 ; r25 = hi(100000001 x r) +; r in r25,,r1 + zdep %r1,15,16,%r29 + add %r29,%r1,%r29 + shd %r25,%r1,16,%r1 + addc %r1,%r25,%r1 +; r in r1,,r29 + sh1add %r29,%r26,%r0 ; r0 = lo(10 x r) + dividend + shd %r1,%r29,31,%r29 ; r29 = hi(10 x r) + addc %r29,%r0,%r29 + bv 0(ret) + extru %r29,30,31,result + .exit + .procend +#endif + + +#if defined (L_divU_5) && !defined (SMALL_LIB) +#undef L_divU_5 +#define dividend %r26 +#define divisor %r25 +#define tmp %r1 +#define result %r29 +#define ret %r31 + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .align 4 + .export $$divU_5 +$$divU_5 + .proc + .callinfo frame=0,no_calls + .entry + sh1add %r26,%r26,%r29 ; r29 = lo(11 x r) + shd %r0,%r26,31,%r1 ; r1 = hi(10 x r) + addc %r1,%r0,%r1 ; r1 = hi(11 x r) +; r in r1,,r29 + zdep %r29,27,28,%r25 ; r25 = lo(10000 x r) + add %r25,%r29,%r25 ; r25 = lo(10001 x r) + shd %r1,%r29,28,%r29 ; r29 = hi(10000 x r) + addc %r29,%r1,%r29 ; r29 = hi(10001 x r) +; r in r29,,r25 + zdep %r25,23,24,%r1 ; r1 = lo(100000000 x r) + add %r1,%r25,%r1 ; r1 = lo(100000001 x r) + shd %r29,%r25,24,%r25 ; r25 = hi(100000000 x r) + addc %r25,%r29,%r25 ; r25 = hi(100000001 x r) +; r in r25,,r1 + zdep %r1,15,16,%r29 + add %r29,%r1,%r29 + shd %r25,%r1,16,%r1 + addc %r1,%r25,%r1 +; r in r1,,r29 + sh2add %r29,%r26,%r0 ; r0 = lo(1000 x r) + dividend + shd %r1,%r29,30,%r29 ; r29 = hi(1000 x r) + addc %r29,%r0,%r29 + bv 0(ret) + extru %r29,29,30,result + .exit + .procend +#endif + + +#if defined (L_divU_6) && !defined (SMALL_LIB) +#undef L_divU_6 +#define dividend %r26 +#define divisor %r25 +#define tmp %r1 +#define result %r29 +#define ret %r31 + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .align 4 + .export $$divU_6 +$$divU_6 + .proc + .callinfo frame=0,no_calls + .entry + sh2add %r26,%r26,%r29 ; r29 = lo(101 x r) + shd %r0,%r26,30,%r1 ; r1 = hi(100 x r) + addc %r1,%r0,%r1 ; r1 = hi(101 x r) +; r in r1,,r29 + zdep %r29,27,28,%r25 ; r25 = lo(10000 x r) + add %r25,%r29,%r25 ; r25 = lo(10001 x r) + shd %r1,%r29,28,%r29 ; r29 = hi(10000 x r) + addc %r29,%r1,%r29 ; r29 = hi(10001 x r) +; r in r29,,r25 + zdep %r25,23,24,%r1 ; r1 = lo(100000000 x r) + add %r1,%r25,%r1 ; r1 = lo(100000001 x r) + shd %r29,%r25,24,%r25 ; r25 = hi(100000000 x r) + addc %r25,%r29,%r25 ; r25 = hi(100000001 x r) +; r in r25,,r1 + zdep %r1,15,16,%r29 + add %r29,%r1,%r29 + shd %r25,%r1,16,%r1 + addc %r1,%r25,%r1 +; r in r1,,r29 + sh1add %r29,%r26,%r0 ; r0 = lo(10 x r) + dividend + shd %r1,%r29,31,%r29 ; r29 = hi(10 x r) + addc %r29,%r0,%r29 + bv 0(ret) + extru %r29,29,30,result + .exit + .procend +#endif + + +#if defined (L_divU_9) && !defined (SMALL_LIB) +#undef L_divU_9 +#define dividend %r26 +#define divisor %r25 +#define tmp %r1 +#define result %r29 +#define ret %r31 + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .align 4 + .export $$divU_9 +$$divU_9 + .proc + .callinfo frame=0,no_calls + .entry + zdep %r26,28,29,%r29 + sub %r29,%r26,%r29 + shd 0,%r26,29,%r1 + subb %r1,0,%r1 /* 111 */ + + zdep %r29,25,26,%r25 + add %r25,%r29,%r25 + shd %r1,%r29,26,%r29 + addc %r29,%r1,%r29 /* 111000111 */ + + sh3add %r25,%r26,%r1 + shd %r29,%r25,29,%r25 + addc %r25,0,%r25 /* 111000111001 */ + + zdep %r1,16,17,%r29 + sub %r29,%r1,%r29 + shd %r25,%r1,17,%r1 + subb %r1,%r25,%r1 /* 111000111000111000111000111 */ + + sh3add %r29,%r26,%r0 + shd %r1,%r29,29,%r29 + addc %r29,0,%r29 /* 111000111000111000111000111001 */ + bv 0(ret) + extru %r29,30,31,result + .exit + .procend +#endif + + +#if defined (L_divU_10) && !defined (SMALL_LIB) +#undef L_divU_10 +#define dividend %r26 +#define divisor %r25 +#define tmp %r1 +#define result %r29 +#define ret %r31 + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .align 4 + .export $$divU_10 +$$divU_10 + .proc + .callinfo frame=0,no_calls + .entry + sh1add %r26,%r26,%r29 ; r29 = lo(11 x r) + shd %r0,%r26,31,%r1 ; r1 = hi(10 x r) + addc %r1,%r0,%r1 ; r1 = hi(11 x r) +; r in r1,,r29 + zdep %r29,27,28,%r25 ; r25 = lo(10000 x r) + add %r25,%r29,%r25 ; r25 = lo(10001 x r) + shd %r1,%r29,28,%r29 ; r29 = hi(10000 x r) + addc %r29,%r1,%r29 ; r29 = hi(10001 x r) +; r in r29,,r25 + zdep %r25,23,24,%r1 ; r1 = lo(100000000 x r) + add %r1,%r25,%r1 ; r1 = lo(100000001 x r) + shd %r29,%r25,24,%r25 ; r25 = hi(100000000 x r) + addc %r25,%r29,%r25 ; r25 = hi(100000001 x r) +; r in r25,,r1 + zdep %r1,15,16,%r29 + add %r29,%r1,%r29 + shd %r25,%r1,16,%r1 + addc %r1,%r25,%r1 +; r in r1,,r29 + sh2add %r29,%r26,%r0 ; r0 = lo(1000 x r) + dividend + shd %r1,%r29,30,%r29 ; r29 = hi(1000 x r) + addc %r29,%r0,%r29 + bv 0(ret) + extru %r29,28,29,result + .exit + .procend +#endif + + +#if defined (L_divU_12) && !defined (SMALL_LIB) +#undef L_divU_12 +#define dividend %r26 +#define divisor %r25 +#define tmp %r1 +#define result %r29 +#define ret %r31 + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .align 4 + .export $$divU_12 +$$divU_12 + .proc + .callinfo frame=0,no_calls + .entry + sh2add %r26,%r26,%r29 ; r29 = lo(101 x r) + shd %r0,%r26,30,%r1 ; r1 = hi(100 x r) + addc %r1,%r0,%r1 ; r1 = hi(101 x r) +; r in r1,,r29 + zdep %r29,27,28,%r25 ; r25 = lo(10000 x r) + add %r25,%r29,%r25 ; r25 = lo(10001 x r) + shd %r1,%r29,28,%r29 ; r29 = hi(10000 x r) + addc %r29,%r1,%r29 ; r29 = hi(10001 x r) +; r in r29,,r25 + zdep %r25,23,24,%r1 ; r1 = lo(100000000 x r) + add %r1,%r25,%r1 ; r1 = lo(100000001 x r) + shd %r29,%r25,24,%r25 ; r25 = hi(100000000 x r) + addc %r25,%r29,%r25 ; r25 = hi(100000001 x r) +; r in r25,,r1 + zdep %r1,15,16,%r29 + add %r29,%r1,%r29 + shd %r25,%r1,16,%r1 + addc %r1,%r25,%r1 +; r in r1,,r29 + sh1add %r29,%r26,%r0 ; r0 = lo(10 x r) + dividend + shd %r1,%r29,31,%r29 ; r29 = hi(10 x r) + addc %r29,%r0,%r29 + bv 0(ret) + extru %r29,28,29,result + .exit + .procend +#endif + + +#ifdef L_divU_3 + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .align 4 + .export $$divU_3 +$$divU_3 + .proc + .callinfo frame=0,no_calls + .entry + b $$divU + ldi 3,%r25 + .exit + .procend + .import $$divU,MILLICODE +#endif + +#ifdef L_divU_5 + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .align 4 + .export $$divU_5 +$$divU_5 + .proc + .callinfo frame=0,no_calls + .entry + b $$divU + ldi 5,%r25 + .exit + .procend + .import $$divU,MILLICODE +#endif + +#ifdef L_divU_6 + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .align 4 + .export $$divU_6 +$$divU_6 + .proc + .callinfo frame=0,no_calls + .entry + b $$divU + ldi 6,%r25 + .exit + .procend + .import $$divU,MILLICODE +#endif + +#ifdef L_divU_7 + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .align 4 + .export $$divU_7 +$$divU_7 + .proc + .callinfo frame=0,no_calls + .entry + b $$divU + ldi 7,%r25 + .exit + .procend + .import $$divU,MILLICODE +#endif + +#ifdef L_divU_9 + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .align 4 + .export $$divU_9 +$$divU_9 + .proc + .callinfo frame=0,no_calls + .entry + b $$divU + ldi 9,%r25 + .exit + .procend + .import $$divU,MILLICODE +#endif + +#ifdef L_divU_10 + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .align 4 + .export $$divU_10 +$$divU_10 + .proc + .callinfo frame=0,no_calls + .entry + b $$divU + ldi 10,%r25 + .exit + .procend + .import $$divU,MILLICODE +#endif + +#ifdef L_divU_12 + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .align 4 + .export $$divU_12 +$$divU_12 + .proc + .callinfo frame=0,no_calls + .entry + b $$divU + ldi 12,%r25 + .exit + .procend + .import $$divU,MILLICODE +#endif + +#ifdef L_divU_14 + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .align 4 + .export $$divU_14 +$$divU_14 + .proc + .callinfo frame=0,no_calls + .entry + b $$divU + ldi 14,%r25 + .exit + .procend + .import $$divU,MILLICODE +#endif + +#ifdef L_divU_15 + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .align 4 + .export $$divU_15 +$$divU_15 + .proc + .callinfo frame=0,no_calls + .entry + b $$divU + ldi 15,%r25 + .exit + .procend + .import $$divU,MILLICODE +#endif + +#ifdef L_divI_3 + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .align 4 + .export $$divI_3 +$$divI_3 + .proc + .callinfo frame=0,no_calls + .entry + b $$divI + ldi 3,%r25 + .exit + .procend + .import $$divI,MILLICODE +#endif + +#ifdef L_divI_5 + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .align 4 + .export $$divI_5 +$$divI_5 + .proc + .callinfo frame=0,no_calls + .entry + b $$divI + ldi 5,%r25 + .exit + .procend + .import $$divI,MILLICODE +#endif + +#ifdef L_divI_6 + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .align 4 + .export $$divI_6 +$$divI_6 + .proc + .callinfo frame=0,no_calls + .entry + b $$divI + ldi 6,%r25 + .exit + .procend + .import $$divI,MILLICODE +#endif + +#ifdef L_divI_7 + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .align 4 + .export $$divI_7 +$$divI_7 + .proc + .callinfo frame=0,no_calls + .entry + b $$divI + ldi 7,%r25 + .exit + .procend + .import $$divI,MILLICODE +#endif + +#ifdef L_divI_9 + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .align 4 + .export $$divI_9 +$$divI_9 + .proc + .callinfo frame=0,no_calls + .entry + b $$divI + ldi 9,%r25 + .exit + .procend + .import $$divI,MILLICODE +#endif + +#ifdef L_divI_10 + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .align 4 + .export $$divI_10 +$$divI_10 + .proc + .callinfo frame=0,no_calls + .entry + b $$divI + ldi 10,%r25 + .exit + .procend + .import $$divI,MILLICODE +#endif + +#ifdef L_divI_12 + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .align 4 + .export $$divI_12 +$$divI_12 + .proc + .callinfo frame=0,no_calls + .entry + b $$divI + ldi 12,%r25 + .exit + .procend + .import $$divI,MILLICODE +#endif + +#ifdef L_divI_14 + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .align 4 + .export $$divI_14 +$$divI_14 + .proc + .callinfo frame=0,no_calls + .entry + b $$divI + ldi 14,%r25 + .exit + .procend + .import $$divI,MILLICODE +#endif + +#ifdef L_divI_15 + .space $TEXT$ + .subspa $MILLICODE$,quad=0,align=8,access=0x2c,sort=8 + .align 4 + .export $$divI_15 +$$divI_15 + .proc + .callinfo frame=0,no_calls + .entry + b $$divI + ldi 15,%r25 + .exit + .procend + .import $$divI,MILLICODE +#endif diff --git a/gcc/config/pa/lib2funcs.asm b/gcc/config/pa/lib2funcs.asm new file mode 100755 index 0000000..cf57cbb --- /dev/null +++ b/gcc/config/pa/lib2funcs.asm @@ -0,0 +1,74 @@ +; Subroutines for calling unbound dynamic functions from within GDB for HPPA. +; Subroutines for out of line prologues and epilogues on for the HPPA +; Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc. + +; This file is part of GNU CC. + +; GNU CC 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. + +; GNU CC 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. + +; 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.) + +; You should have received a copy of the GNU General Public License +; along with GNU CC; see the file COPYING. If not, write to +; the Free Software Foundation, 59 Temple Place - Suite 330, +; Boston, MA 02111-1307, USA. + + .SPACE $PRIVATE$ + .SUBSPA $DATA$,QUAD=1,ALIGN=8,ACCESS=31 + .SUBSPA $BSS$,QUAD=1,ALIGN=8,ACCESS=31,ZERO,SORT=82 + .SPACE $TEXT$ + .SUBSPA $LIT$,QUAD=0,ALIGN=8,ACCESS=44 + .SUBSPA $CODE$,QUAD=0,ALIGN=8,ACCESS=44,CODE_ONLY + .SUBSPA $MILLICODE$,QUAD=0,ALIGN=8,ACCESS=44,SORT=8 + + .IMPORT $$dyncall,MILLICODE +; gcc_compiled.: + .SPACE $TEXT$ + .SUBSPA $CODE$ + +; Simply call with the address of the desired import stub in %r22 and +; arguments in the normal place (%r26-%r23 and stack slots). +; + .align 4 + .EXPORT __gcc_plt_call,ENTRY,PRIV_LEV=3,RTNVAL=GR +__gcc_plt_call + .PROC + .CALLINFO + .ENTRY + ; Our return address comes in %r31, not %r2! + stw %r31,-8(0,%r30) + + ; An inline version of dyncall so we don't have to worry + ; about long calls to millicode, PIC and other complexities. + bb,>=,n %r22,30,L$foo + depi 0,31,2,%r22 + ldw 4(%r22),%r19 + ldw 0(%r22),%r22 +L$foo + ldsid (%r22),%r1 + mtsp %r1,%sr0 + ble 0(%sr0,%r22) + copy %r31,%r2 + ldw -8(0,%r30),%r2 + + ; We're going to be returning to a stack address, so we + ; need to do an intra-space return. + ldsid (%rp),%r1 + mtsp %r1,%sr0 + be,n 0(%sr0,%rp) + .EXIT + .PROCEND diff --git a/gcc/config/pa/pa-gas.h b/gcc/config/pa/pa-gas.h new file mode 100755 index 0000000..4106b70 --- /dev/null +++ b/gcc/config/pa/pa-gas.h @@ -0,0 +1,22 @@ +/* Definitions of target machine for GNU compiler, for HP-UX using GNU as. + Copyright (C) 1996 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC 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. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#undef TARGET_DEFAULT +#define TARGET_DEFAULT 0x88 /* TARGET_GAS + TARGET_JUMP_IN_DELAY */ diff --git a/gcc/config/pa/pa-hiux.h b/gcc/config/pa/pa-hiux.h new file mode 100755 index 0000000..f4be95b --- /dev/null +++ b/gcc/config/pa/pa-hiux.h @@ -0,0 +1,26 @@ +/* Definitions of target machine for GNU compiler, for HI-UX. + Copyright (C) 1993, 1995, 1996 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC 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. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* HIUX is just a HPUX variant. We can simply use the HPUX configuration + for just about everything. */ + +/* Predefines are the one noteworthy difference between HPUX and HIUX. */ +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dhppa -DPWB -Dunix -D__H3050R -D__H3050RX -Asystem(unix) -Asystem(hiux) -Acpu(hppa) -Amachine(hppa)" diff --git a/gcc/config/pa/pa-hpux.h b/gcc/config/pa/pa-hpux.h new file mode 100755 index 0000000..e001ebe --- /dev/null +++ b/gcc/config/pa/pa-hpux.h @@ -0,0 +1,49 @@ +/* Definitions of target machine for GNU compiler, for HP-UX. + Copyright (C) 1991, 1995, 1996 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC 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. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#undef TARGET_DEFAULT +#define TARGET_DEFAULT 0 + +/* Make GCC agree with types.h. */ +#undef SIZE_TYPE +#undef PTRDIFF_TYPE + +#define SIZE_TYPE "unsigned int" +#define PTRDIFF_TYPE "int" + +/* Like the default, except no -lg. */ +#undef LIB_SPEC +#define LIB_SPEC "%{!shared:%{!p:%{!pg:-lc}}%{p: -L/lib/libp/ -lc}%{pg: -L/lib/libp/ -lc}}" + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dhppa -Dhp9000s800 -D__hp9000s800 -Dhp9k8 -DPWB -Dhpux -Dunix -Asystem(unix) -Asystem(hpux) -Acpu(hppa) -Amachine(hppa)" + +#undef LINK_SPEC +#if ((TARGET_DEFAULT | TARGET_CPU_DEFAULT) & 1) +#define LINK_SPEC \ + "%{!mpa-risc-1-0:%{!shared:-L/lib/pa1.1 -L/usr/lib/pa1.1 }}%{mlinker-opt:-O} %{!shared:-u main} %{static:-a archive} %{g*:-a archive} %{shared:-b}" +#else +#define LINK_SPEC \ + "%{mlinker-opt:-O} %{!shared:-u main} %{static:-a archive} %{g*:-a archive} %{shared:-b}" +#endif + +/* hpux8 and later have C++ compatible include files, so do not + pretend they are `extern "C"'. */ +#define NO_IMPLICIT_EXTERN_C diff --git a/gcc/config/pa/pa-hpux10.h b/gcc/config/pa/pa-hpux10.h new file mode 100755 index 0000000..e00c107 --- /dev/null +++ b/gcc/config/pa/pa-hpux10.h @@ -0,0 +1,89 @@ +/* Definitions of target machine for GNU compiler, for HP PA-RISC 1.1 + Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc. + Contributed by Tim Moore (moore@defmacro.cs.utah.edu) + +This file is part of GNU CC. + +GNU CC 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. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* We can debug dynamically linked executables on hpux9; we also want + dereferencing of a NULL pointer to cause a SEGV. */ +#undef LINK_SPEC +#if ((TARGET_DEFAULT | TARGET_CPU_DEFAULT) & 1) +#define LINK_SPEC \ + "%{!mpa-risc-1-0:%{!shared:-L/lib/pa1.1 -L/usr/lib/pa1.1 }} -z %{mlinker-opt:-O} %{!shared:-u main} %{static:-a archive} %{shared:-b}" +#else +#define LINK_SPEC \ + "-z %{mlinker-opt:-O} %{!shared:-u main} %{static:-a archive} %{shared:-b}" +#endif + +/* Like the default, except no -lg. */ +#undef LIB_SPEC +#define LIB_SPEC \ + "%{!shared:\ + %{!p:\ + %{!pg:\ + %{!threads:-lc}\ + %{threads:-lcma -lc_r}}\ + %{p: -L/lib/libp/ -lc}\ + %{pg: -L/lib/libp/ -lc}}}" + +/* The hpux10 assembler requires a .LEVEL pseudo-op at the start of + the assembly file. */ +#undef ASM_FILE_START +#define ASM_FILE_START(FILE) \ +do { \ + /* CYGNUS LOCAL pa8000/law */ \ + if (TARGET_PARISC_2_0) \ + fputs("\t.LEVEL 2.0\n", FILE); \ + else if (TARGET_SNAKE) \ + fputs("\t.LEVEL 1.1\n", FILE); \ + else \ + fputs("\t.LEVEL 1.0\n", FILE); \ + /* END CYGNUS LOCAL */ \ + fputs ("\t.SPACE $PRIVATE$\n\ +\t.SUBSPA $DATA$,QUAD=1,ALIGN=8,ACCESS=31\n\ +\t.SUBSPA $BSS$,QUAD=1,ALIGN=8,ACCESS=31,ZERO,SORT=82\n\ +\t.SPACE $TEXT$\n\ +\t.SUBSPA $LIT$,QUAD=0,ALIGN=8,ACCESS=44\n\ +\t.SUBSPA $CODE$,QUAD=0,ALIGN=8,ACCESS=44,CODE_ONLY\n\ +\t.IMPORT $global$,DATA\n\ +\t.IMPORT $$dyncall,MILLICODE\n", FILE);\ + if (profile_flag)\ + fprintf (FILE, "\t.IMPORT _mcount, CODE\n");\ + if (write_symbols != NO_DEBUG) \ + output_file_directive ((FILE), main_input_filename); \ + } while (0) + +/* Under hpux10, the normal location of the `ld' and `as' programs is the + /usr/ccs/bin directory. */ + +#ifndef CROSS_COMPILE +#undef MD_EXEC_PREFIX +#define MD_EXEC_PREFIX "/usr/ccs/bin/" +#endif + +/* Under hpux10, the normal location of the various *crt*.o files is the + /usr/ccs/lib directory. */ + +#ifndef CROSS_COMPILE +#undef MD_STARTFILE_PREFIX +#define MD_STARTFILE_PREFIX "/usr/ccs/lib/" +#endif + +/* hpux10 has the new HP assembler. It's still lousy, but it's a whole lot + better than the assembler shipped with older versions of hpux. */ +#define NEW_HP_ASSEMBLER diff --git a/gcc/config/pa/pa-hpux11.h b/gcc/config/pa/pa-hpux11.h new file mode 100755 index 0000000..2959c5c --- /dev/null +++ b/gcc/config/pa/pa-hpux11.h @@ -0,0 +1,98 @@ +/* CYGNUS LOCAL entire file hpux11/law */ +/* Definitions of target machine for GNU compiler, for HP PA-RISC 1.1 + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC 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. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* We can debug dynamically linked executables on hpux11; we also + want dereferencing of a NULL pointer to cause a SEGV. */ +#undef LINK_SPEC +#if ((TARGET_DEFAULT | TARGET_CPU_DEFAULT) & 1) +#define LINK_SPEC \ + "%{!mpa-risc-1-0:%{!shared:-L/lib/pa1.1 -L/usr/lib/pa1.1 }} -z %{mlinker-opt:-O} %{!shared:-u main} %{static:-a archive} %{shared:-b}" +#else +#define LINK_SPEC \ + "-z %{mlinker-opt:-O} %{!shared:-u main} %{static:-a archive} %{shared:-b}" +#endif + +/* Like the default, except no -lg. */ +#undef LIB_SPEC +#define LIB_SPEC \ + "%{!shared:\ + %{!p:\ + %{!pg:\ + %{!threads:-lc}\ + %{threads:-lcma -lc_r}}\ + %{p: -L/lib/libp/ -lc}\ + %{pg: -L/lib/libp/ -lc}}}" + +/* The hpux11 assembler requires a .LEVEL pseudo-op at the start of the + assembly file. */ +#undef ASM_FILE_START +#define ASM_FILE_START(FILE) \ +do { \ + /* CYGNUS LOCAL pa8000/law */ \ + if (TARGET_PARISC_2_0) \ + fputs("\t.LEVEL 2.0\n", FILE); \ + else if (TARGET_SNAKE) \ + fputs("\t.LEVEL 1.1\n", FILE); \ + else \ + fputs("\t.LEVEL 1.0\n", FILE); \ + /* END CYGNUS LOCAL */ \ + fputs ("\t.SPACE $PRIVATE$\n\ +\t.SUBSPA $DATA$,QUAD=1,ALIGN=8,ACCESS=31\n\ +\t.SUBSPA $BSS$,QUAD=1,ALIGN=8,ACCESS=31,ZERO,SORT=82\n\ +\t.SPACE $TEXT$\n\ +\t.SUBSPA $LIT$,QUAD=0,ALIGN=8,ACCESS=44\n\ +\t.SUBSPA $CODE$,QUAD=0,ALIGN=8,ACCESS=44,CODE_ONLY\n\ +\t.IMPORT $global$,DATA\n\ +\t.IMPORT $$dyncall,MILLICODE\n", FILE);\ + if (profile_flag)\ + fprintf (FILE, "\t.IMPORT _mcount, CODE\n");\ + if (write_symbols != NO_DEBUG) \ + output_file_directive ((FILE), main_input_filename); \ + } while (0) + +/* Under hpux11, the normal location of the `ld' and `as' programs is the + /usr/ccs/bin directory. */ + +#ifndef CROSS_COMPILE +#undef MD_EXEC_PREFIX +#define MD_EXEC_PREFIX "/usr/ccs/bin/" +#endif + +/* Under hpux11 the normal location of the various *crt*.o files is the + /usr/ccs/lib directory. */ + +#ifndef CROSS_COMPILE +#undef MD_STARTFILE_PREFIX +#define MD_STARTFILE_PREFIX "/usr/ccs/lib/" +#endif + +/* hpux11 has the new HP assembler. It's still lousy, but it's a whole lot + better than the assembler shipped with older versions of hpux. */ +#define NEW_HP_ASSEMBLER + +/* Make GCC agree with types.h. */ +#undef SIZE_TYPE +#undef PTRDIFF_TYPE + +#define SIZE_TYPE "long unsigned int" +#define PTRDIFF_TYPE "long int" + +/* END CYGNUS LOCAL */ diff --git a/gcc/config/pa/pa-hpux7.h b/gcc/config/pa/pa-hpux7.h new file mode 100755 index 0000000..dc75ec2 --- /dev/null +++ b/gcc/config/pa/pa-hpux7.h @@ -0,0 +1,37 @@ +/* Definitions of target machine for GNU compiler, for HP-UX. + Copyright (C) 1991, 1995, 1996 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC 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. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#ifndef TARGET_DEFAULT +#define TARGET_DEFAULT 0 +#endif + +/* Make GCC agree with types.h. */ +#undef SIZE_TYPE +#undef PTRDIFF_TYPE + +#define SIZE_TYPE "unsigned int" +#define PTRDIFF_TYPE "int" + +/* Like the default, except no -lg. */ +#undef LIB_SPEC +#define LIB_SPEC "%{!p:%{!pg:-lc}}%{p: -L/lib/libp/ -lc}%{pg: -L/lib/libp/ -lc}" + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dhppa -Dhp9000s800 -D__hp9000s800 -Dhp9k8 -DPWB -Dhpux -Dunix -Asystem(unix) -Asystem(hpux) -Acpu(hppa) -Amachine(hppa)" diff --git a/gcc/config/pa/pa-hpux9.h b/gcc/config/pa/pa-hpux9.h new file mode 100755 index 0000000..8d039d2 --- /dev/null +++ b/gcc/config/pa/pa-hpux9.h @@ -0,0 +1,31 @@ +/* Definitions of target machine for GNU compiler, for HP PA-RISC 1.1 + Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc. + Contributed by Tim Moore (moore@defmacro.cs.utah.edu) + +This file is part of GNU CC. + +GNU CC 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. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* We can debug dynamically linked executables on hpux9; we also want + dereferencing of a NULL pointer to cause a SEGV. */ +#undef LINK_SPEC +#if ((TARGET_DEFAULT | TARGET_CPU_DEFAULT) & 1) +#define LINK_SPEC \ + "%{!mpa-risc-1-0:%{!shared:-L/lib/pa1.1 -L/usr/lib/pa1.1 }} -z %{mlinker-opt:-O} %{!shared:-u main} %{static:-a archive} %{shared:-b}" +#else +#define LINK_SPEC \ + "-z %{mlinker-opt:-O} %{!shared:-u main} %{static:-a archive} %{shared:-b}" +#endif diff --git a/gcc/config/pa/pa-oldas.h b/gcc/config/pa/pa-oldas.h new file mode 100755 index 0000000..8ff741f --- /dev/null +++ b/gcc/config/pa/pa-oldas.h @@ -0,0 +1,22 @@ +/* Definitions of target machine for GNU compiler, for HP PA-RISC 1.1 + Copyright (C) 1991, 1996 Free Software Foundation, Inc. + Contributed by Tim Moore (moore@defmacro.cs.utah.edu) + +This file is part of GNU CC. + +GNU CC 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. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#define HP_FP_ARG_DESCRIPTOR_REVERSED diff --git a/gcc/config/pa/pa-osf.h b/gcc/config/pa/pa-osf.h new file mode 100755 index 0000000..047d20e --- /dev/null +++ b/gcc/config/pa/pa-osf.h @@ -0,0 +1,42 @@ +/* Definitions of target machine for GNU compiler, for HP PA-RISC 1.1 + Copyright (C) 1991, 1995, 1996 Free Software Foundation, Inc. + Contributed by Tim Moore (moore@defmacro.cs.utah.edu) + +This file is part of GNU CC. + +GNU CC 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. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#undef CPP_PREDEFINES +#if ((TARGET_DEFAULT | TARGET_CPU_DEFAULT) & 1) +#define CPP_PREDEFINES "-Dhppa -Dunix -Dhp9000 -Dspectrum -DREVARGV -Dhp700 -DHP700 -Dparisc -D__pa_risc -DPARISC -DBYTE_MSF -DBIT_MSF -Asystem(unix) -Asystem(mach) -Acpu(hppa) -Amachine(hppa)" +#else +#define CPP_PREDEFINES "-Dhppa -Dhp9000s800 -D__hp9000s800 -Dhp9k8 -Dunix -Dhp9000 -Dhp800 -Dspectrum -DREVARGV -Dparisc -D__pa_risc -DPARISC -DBYTE_MSF -DBIT_MSF -Asystem(unix) -Asystem(mach) -Acpu(hppa) -Amachine(hppa)" +#endif + +/* Don't default to pcc-struct-return, because gcc is the only compiler, and + we want to retain compatibility with older gcc versions. */ +#define DEFAULT_PCC_STRUCT_RETURN 0 + +/* OSF1 on the PA still uses 16bit wchar_t. */ +#undef WCHAR_TYPE +#undef WCHAR_TYPE_SIZE + +#define WCHAR_TYPE "short unsigned int" +#define WCHAR_TYPE_SIZE 16 + +/* OSF1 wants to be different and use unsigned long as size_t. */ +#undef SIZE_TYPE +#define SIZE_TYPE "long unsigned int" diff --git a/gcc/config/pa/pa-pro-end.h b/gcc/config/pa/pa-pro-end.h new file mode 100755 index 0000000..de88036 --- /dev/null +++ b/gcc/config/pa/pa-pro-end.h @@ -0,0 +1,42 @@ +/* Definitions of target machine for GNU compiler, for PRO. + Copyright (C) 1996, 1997 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC 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. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Make GCC agree with types.h. */ +#undef SIZE_TYPE +#undef PTRDIFF_TYPE + +#define SIZE_TYPE "unsigned int" +#define PTRDIFF_TYPE "int" + +/* Like the default, except no -lg. */ +#undef LIB_SPEC +#define LIB_SPEC "%{!p:%{!pg:-lc}}%{p: -L/lib/libp/ -lc}%{pg: -L/lib/libp/ -lc}" + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dhppa -DPWB -Acpu(hppa) -Amachine(hppa)" + +/* hpux8 and later have C++ compatible include files, so do not + pretend they are `extern "C"'. */ +#define NO_IMPLICIT_EXTERN_C + +/* We don't want a crt0.o to get linked in automatically, we want the + linker script to pull it in. + */ +#define STARTFILE_SPEC "" diff --git a/gcc/config/pa/pa-pro.h b/gcc/config/pa/pa-pro.h new file mode 100755 index 0000000..2ec832b --- /dev/null +++ b/gcc/config/pa/pa-pro.h @@ -0,0 +1,79 @@ +/* Definitions of target machine for GNU compiler, for PRO. + Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC 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. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Global constructor and destructor support. */ +/* Define the pseudo-ops used to switch to the .ctors and .dtors sections. + + Note that we want to give these sections the SHF_WRITE attribute + because these sections will actually contain data (i.e. tables of + addresses of functions in the current root executable or shared library + file) and, in the case of a shared library, the relocatable addresses + will have to be properly resolved/relocated (and then written into) by + the dynamic linker when it actually attaches the given shared library + to the executing process. */ + +#define CTORS_SECTION_ASM_OP "\t.section\t\".ctors\",#alloc,#write" +#define DTORS_SECTION_ASM_OP "\t.section\t\".dtors\",#alloc,#write" + +#define CTORS_SECTION_FUNCTION \ +void \ +ctors_section () \ +{ \ + if (in_section != in_ctors) \ + { \ + fprintf (asm_out_file, "%s\n", CTORS_SECTION_ASM_OP); \ + in_section = in_ctors; \ + } \ +} + +#define DTORS_SECTION_FUNCTION \ +void \ +dtors_section () \ +{ \ + if (in_section != in_dtors) \ + { \ + fprintf (asm_out_file, "%s\n", DTORS_SECTION_ASM_OP); \ + in_section = in_dtors; \ + } \ +} + + +/* A C statement (sans semicolon) to output an element in the table of + global destructors. */ +#define ASM_OUTPUT_DESTRUCTOR(FILE,NAME) \ + do { \ + dtors_section (); \ + fputs ("\t.word\t ", FILE); \ + assemble_name (FILE, NAME); \ + fputs ("\n", FILE); \ + } while (0) + +/* A C statement (sans semicolon) to output an element in the table of + global constructors. */ +#define ASM_OUTPUT_CONSTRUCTOR(FILE,NAME) \ + do { \ + ctors_section (); \ + fputs ("\t.word\t ", FILE); \ + assemble_name (FILE, NAME); \ + fputs ("\n", FILE); \ + } while (0) + +/* JUMP_IN_DELAY + PORTABLE_RUNTIME + GAS + NO_SPACE_REGS + SOFT_FLOAT */ +#define TARGET_DEFAULT (4 + 8 + 64 + 128 + 256) diff --git a/gcc/config/pa/pa.c b/gcc/config/pa/pa.c new file mode 100755 index 0000000..de7f698 --- /dev/null +++ b/gcc/config/pa/pa.c @@ -0,0 +1,6491 @@ +/* Subroutines for insn-output.c for HPPA. + Copyright (C) 1992, 93-98, 1999 Free Software Foundation, Inc. + Contributed by Tim Moore (moore@cs.utah.edu), based on sparc.c + +This file is part of GNU CC. + +GNU CC 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. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#include "config.h" +#include "system.h" + +#include "rtl.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "real.h" +#include "insn-config.h" +#include "conditions.h" +#include "insn-flags.h" +#include "output.h" +#include "insn-attr.h" +#include "flags.h" +#include "tree.h" +#include "reload.h" +#include "c-tree.h" +#include "expr.h" +#include "obstack.h" +#include "toplev.h" + +static void restore_unscaled_index_insn_codes PROTO((rtx)); +static void record_unscaled_index_insn_codes PROTO((rtx)); +static void pa_combine_instructions PROTO((rtx)); +static int pa_can_combine_p PROTO((rtx, rtx, rtx, int, rtx, rtx, rtx)); +static int forward_branch_p PROTO((rtx)); +static int shadd_constant_p PROTO((int)); + +/* Save the operands last given to a compare for use when we + generate a scc or bcc insn. */ + +rtx hppa_compare_op0, hppa_compare_op1; +enum cmp_type hppa_branch_type; + +/* Which cpu we are scheduling for. */ +enum processor_type pa_cpu; + +/* String to hold which cpu we are scheduling for. */ +char *pa_cpu_string; + +/* Set by the FUNCTION_PROFILER macro. */ +int hp_profile_labelno; + +/* Counts for the number of callee-saved general and floating point + registers which were saved by the current function's prologue. */ +static int gr_saved, fr_saved; + +/* Whether or not the current function uses an out-of-line prologue + and epilogue. */ +static int out_of_line_prologue_epilogue; + +static rtx find_addr_reg (); + +/* Keep track of the number of bytes we have output in the CODE subspaces + during this compilation so we'll know when to emit inline long-calls. */ + +unsigned int total_code_bytes; + +/* Variables to handle plabels that we discover are necessary at assembly + output time. They are output after the current function. */ + +struct deferred_plabel +{ + rtx internal_label; + char *name; +} *deferred_plabels = 0; +int n_deferred_plabels = 0; + +/* Array indexed by INSN_UIDs holding the INSN_CODE of an insn which + uses an unscaled indexed address before delay slot scheduling. */ +static int *unscaled_index_insn_codes; + +/* Upper bound for the array. */ +static int max_unscaled_index_insn_codes_uid; + +void +override_options () +{ + /* Default to 7100 scheduling. If the 7100LC scheduling ever + gets reasonably tuned, it should be the default since that + what most PAs sold now are. */ + if (pa_cpu_string == NULL + || ! strcmp (pa_cpu_string, "7100")) + { + pa_cpu_string = "7100"; + pa_cpu = PROCESSOR_7100; + } + else if (! strcmp (pa_cpu_string, "700")) + { + pa_cpu_string = "700"; + pa_cpu = PROCESSOR_700; + } + else if (! strcmp (pa_cpu_string, "7100LC")) + { + pa_cpu_string = "7100LC"; + pa_cpu = PROCESSOR_7100LC; + } + else if (! strcmp (pa_cpu_string, "7200")) + { + pa_cpu_string = "7200"; + pa_cpu = PROCESSOR_7200; + } + /* CYGNUS LOCAL PA8000/law */ + else if (! strcmp (pa_cpu_string, "8000")) + { + pa_cpu_string = "8000"; + pa_cpu = PROCESSOR_8000; + } + else + { + warning ("Unknown -mschedule= option (%s).\nValid options are 700, 7100 and 7100LC, 7200 and 8000\n", pa_cpu_string); + } + /* END CYGNUS LOCAL */ + + if (flag_pic && TARGET_PORTABLE_RUNTIME) + { + warning ("PIC code generation is not supported in the portable runtime model\n"); + } + + if (flag_pic && (TARGET_NO_SPACE_REGS || TARGET_FAST_INDIRECT_CALLS)) + { + warning ("PIC code generation is not compatible with fast indirect calls\n"); + } + + if (flag_pic && profile_flag) + { + warning ("PIC code generation is not compatible with profiling\n"); + } + + if (TARGET_SPACE && (flag_pic || profile_flag)) + { + warning ("Out of line entry/exit sequences are not compatible\n"); + warning ("with PIC or profiling\n"); + } + + if (! TARGET_GAS && write_symbols != NO_DEBUG) + { + warning ("-g is only supported when using GAS on this processor,"); + warning ("-g option disabled."); + write_symbols = NO_DEBUG; + } +} + + +/* Return non-zero only if OP is a register of mode MODE, + or CONST0_RTX. */ +int +reg_or_0_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (op == CONST0_RTX (mode) || register_operand (op, mode)); +} + +/* Return non-zero if OP is suitable for use in a call to a named + function. + + (???) For 2.5 try to eliminate either call_operand_address or + function_label_operand, they perform very similar functions. */ +int +call_operand_address (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return (CONSTANT_P (op) && ! TARGET_PORTABLE_RUNTIME); +} + +/* Return 1 if X contains a symbolic expression. We know these + expressions will have one of a few well defined forms, so + we need only check those forms. */ +int +symbolic_expression_p (x) + register rtx x; +{ + + /* Strip off any HIGH. */ + if (GET_CODE (x) == HIGH) + x = XEXP (x, 0); + + return (symbolic_operand (x, VOIDmode)); +} + +int +symbolic_operand (op, mode) + register rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + switch (GET_CODE (op)) + { + case SYMBOL_REF: + case LABEL_REF: + return 1; + case CONST: + op = XEXP (op, 0); + return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF + || GET_CODE (XEXP (op, 0)) == LABEL_REF) + && GET_CODE (XEXP (op, 1)) == CONST_INT); + default: + return 0; + } +} + +/* Return truth value of statement that OP is a symbolic memory + operand of mode MODE. */ + +int +symbolic_memory_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + if (GET_CODE (op) != MEM) + return 0; + op = XEXP (op, 0); + return (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == CONST + || GET_CODE (op) == HIGH || GET_CODE (op) == LABEL_REF); +} + +/* Return 1 if the operand is either a register or a memory operand that is + not symbolic. */ + +int +reg_or_nonsymb_mem_operand (op, mode) + register rtx op; + enum machine_mode mode; +{ + if (register_operand (op, mode)) + return 1; + + if (memory_operand (op, mode) && ! symbolic_memory_operand (op, mode)) + return 1; + + return 0; +} + +/* Return 1 if the operand is either a register, zero, or a memory operand + that is not symbolic. */ + +int +reg_or_0_or_nonsymb_mem_operand (op, mode) + register rtx op; + enum machine_mode mode; +{ + if (register_operand (op, mode)) + return 1; + + if (op == CONST0_RTX (mode)) + return 1; + + if (memory_operand (op, mode) && ! symbolic_memory_operand (op, mode)) + return 1; + + return 0; +} + +/* Accept any constant that can be moved in one instructions into a + general register. */ +int +cint_ok_for_move (intval) + HOST_WIDE_INT intval; +{ + /* OK if ldo, ldil, or zdepi, can be used. */ + return (VAL_14_BITS_P (intval) || (intval & 0x7ff) == 0 + || zdepi_cint_p (intval)); +} + +/* Accept anything that can be moved in one instruction into a general + register. */ +int +move_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (register_operand (op, mode)) + return 1; + + if (GET_CODE (op) == CONSTANT_P_RTX) + return 1; + + if (GET_CODE (op) == CONST_INT) + return cint_ok_for_move (INTVAL (op)); + + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + if (GET_CODE (op) != MEM) + return 0; + + op = XEXP (op, 0); + if (GET_CODE (op) == LO_SUM) + return (register_operand (XEXP (op, 0), Pmode) + && CONSTANT_P (XEXP (op, 1))); + + /* Since move_operand is only used for source operands, we can always + allow scaled indexing! */ + if (! TARGET_DISABLE_INDEXING + && GET_CODE (op) == PLUS + && ((GET_CODE (XEXP (op, 0)) == MULT + && GET_CODE (XEXP (XEXP (op, 0), 0)) == REG + && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT + && INTVAL (XEXP (XEXP (op, 0), 1)) == GET_MODE_SIZE (mode) + && GET_CODE (XEXP (op, 1)) == REG) + || (GET_CODE (XEXP (op, 1)) == MULT + &&GET_CODE (XEXP (XEXP (op, 1), 0)) == REG + && GET_CODE (XEXP (XEXP (op, 1), 1)) == CONST_INT + && INTVAL (XEXP (XEXP (op, 1), 1)) == GET_MODE_SIZE (mode) + && GET_CODE (XEXP (op, 0)) == REG))) + return 1; + + return memory_address_p (mode, op); +} + +/* Accept REG and any CONST_INT that can be moved in one instruction into a + general register. */ +int +reg_or_cint_move_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (register_operand (op, mode)) + return 1; + + if (GET_CODE (op) == CONST_INT) + return cint_ok_for_move (INTVAL (op)); + + return 0; +} + +int +pic_label_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + if (!flag_pic) + return 0; + + switch (GET_CODE (op)) + { + case LABEL_REF: + return 1; + case CONST: + op = XEXP (op, 0); + return (GET_CODE (XEXP (op, 0)) == LABEL_REF + && GET_CODE (XEXP (op, 1)) == CONST_INT); + default: + return 0; + } +} + +int +fp_reg_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return reg_renumber && FP_REG_P (op); +} + + + +/* Return truth value of whether OP can be used as an operand in a + three operand arithmetic insn that accepts registers of mode MODE + or 14-bit signed integers. */ +int +arith_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (register_operand (op, mode) + || (GET_CODE (op) == CONST_INT && INT_14_BITS (op))); +} + +/* Return truth value of whether OP can be used as an operand in a + three operand arithmetic insn that accepts registers of mode MODE + or 11-bit signed integers. */ +int +arith11_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (register_operand (op, mode) + || (GET_CODE (op) == CONST_INT && INT_11_BITS (op))); +} + +/* A constant integer suitable for use in a PRE_MODIFY memory + reference. */ +int +pre_cint_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return (GET_CODE (op) == CONST_INT + && INTVAL (op) >= -0x2000 && INTVAL (op) < 0x10); +} + +/* A constant integer suitable for use in a POST_MODIFY memory + reference. */ +int +post_cint_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return (GET_CODE (op) == CONST_INT + && INTVAL (op) < 0x2000 && INTVAL (op) >= -0x10); +} + +int +arith_double_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (register_operand (op, mode) + || (GET_CODE (op) == CONST_DOUBLE + && GET_MODE (op) == mode + && VAL_14_BITS_P (CONST_DOUBLE_LOW (op)) + && ((CONST_DOUBLE_HIGH (op) >= 0) + == ((CONST_DOUBLE_LOW (op) & 0x1000) == 0)))); +} + +/* Return truth value of whether OP is a integer which fits the + range constraining immediate operands in three-address insns, or + is an integer register. */ + +int +ireg_or_int5_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return ((GET_CODE (op) == CONST_INT && INT_5_BITS (op)) + || (GET_CODE (op) == REG && REGNO (op) > 0 && REGNO (op) < 32)); +} + +/* Return truth value of whether OP is a integer which fits the + range constraining immediate operands in three-address insns. */ + +int +int5_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return (GET_CODE (op) == CONST_INT && INT_5_BITS (op)); +} + +int +uint5_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return (GET_CODE (op) == CONST_INT && INT_U5_BITS (op)); +} + +int +int11_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return (GET_CODE (op) == CONST_INT && INT_11_BITS (op)); +} + +int +uint32_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ +#if HOST_BITS_PER_WIDE_INT > 32 + /* All allowed constants will fit a CONST_INT. */ + return (GET_CODE (op) == CONST_INT + && (INTVAL (op) >= 0 && INTVAL (op) < 0x100000000L)); +#else + return (GET_CODE (op) == CONST_INT + || (GET_CODE (op) == CONST_DOUBLE + && CONST_DOUBLE_HIGH (op) == 0)); +#endif +} + +int +arith5_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return register_operand (op, mode) || int5_operand (op, mode); +} + +/* True iff zdepi can be used to generate this CONST_INT. */ +int +zdepi_cint_p (x) + unsigned HOST_WIDE_INT x; +{ + unsigned HOST_WIDE_INT lsb_mask, t; + + /* This might not be obvious, but it's at least fast. + This function is critical; we don't have the time loops would take. */ + lsb_mask = x & -x; + t = ((x >> 4) + lsb_mask) & ~(lsb_mask - 1); + /* Return true iff t is a power of two. */ + return ((t & (t - 1)) == 0); +} + +/* True iff depi or extru can be used to compute (reg & mask). + Accept bit pattern like these: + 0....01....1 + 1....10....0 + 1..10..01..1 */ +int +and_mask_p (mask) + unsigned HOST_WIDE_INT mask; +{ + mask = ~mask; + mask += mask & -mask; + return (mask & (mask - 1)) == 0; +} + +/* True iff depi or extru can be used to compute (reg & OP). */ +int +and_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (register_operand (op, mode) + || (GET_CODE (op) == CONST_INT && and_mask_p (INTVAL (op)))); +} + +/* True iff depi can be used to compute (reg | MASK). */ +int +ior_mask_p (mask) + unsigned HOST_WIDE_INT mask; +{ + mask += mask & -mask; + return (mask & (mask - 1)) == 0; +} + +/* True iff depi can be used to compute (reg | OP). */ +int +ior_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return (GET_CODE (op) == CONST_INT && ior_mask_p (INTVAL (op))); +} + +int +lhs_lshift_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return register_operand (op, mode) || lhs_lshift_cint_operand (op, mode); +} + +/* True iff OP is a CONST_INT of the forms 0...0xxxx or 0...01...1xxxx. + Such values can be the left hand side x in (x << r), using the zvdepi + instruction. */ +int +lhs_lshift_cint_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + unsigned HOST_WIDE_INT x; + if (GET_CODE (op) != CONST_INT) + return 0; + x = INTVAL (op) >> 4; + return (x & (x + 1)) == 0; +} + +int +arith32_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return register_operand (op, mode) || GET_CODE (op) == CONST_INT; +} + +int +pc_or_label_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return (GET_CODE (op) == PC || GET_CODE (op) == LABEL_REF); +} + +/* Legitimize PIC addresses. If the address is already + position-independent, we return ORIG. Newly generated + position-independent addresses go to REG. If we need more + than one register, we lose. */ + +rtx +legitimize_pic_address (orig, mode, reg) + rtx orig, reg; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + rtx pic_ref = orig; + + /* Labels need special handling. */ + if (pic_label_operand (orig)) + { + emit_insn (gen_pic_load_label (reg, orig)); + current_function_uses_pic_offset_table = 1; + return reg; + } + if (GET_CODE (orig) == SYMBOL_REF) + { + if (reg == 0) + abort (); + + if (flag_pic == 2) + { + emit_insn (gen_pic2_highpart (reg, pic_offset_table_rtx, orig)); + pic_ref + = gen_rtx_MEM (Pmode, + gen_rtx_LO_SUM (Pmode, reg, + gen_rtx_UNSPEC (SImode, + gen_rtvec (1, orig), + 0))); + } + else + pic_ref = gen_rtx_MEM (Pmode, + gen_rtx_PLUS (Pmode, + pic_offset_table_rtx, orig)); + current_function_uses_pic_offset_table = 1; + RTX_UNCHANGING_P (pic_ref) = 1; + emit_move_insn (reg, pic_ref); + return reg; + } + else if (GET_CODE (orig) == CONST) + { + rtx base; + + if (GET_CODE (XEXP (orig, 0)) == PLUS + && XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx) + return orig; + + if (reg == 0) + abort (); + + if (GET_CODE (XEXP (orig, 0)) == PLUS) + { + base = legitimize_pic_address (XEXP (XEXP (orig, 0), 0), Pmode, reg); + orig = legitimize_pic_address (XEXP (XEXP (orig, 0), 1), Pmode, + base == reg ? 0 : reg); + } + else abort (); + if (GET_CODE (orig) == CONST_INT) + { + if (INT_14_BITS (orig)) + return plus_constant_for_output (base, INTVAL (orig)); + orig = force_reg (Pmode, orig); + } + pic_ref = gen_rtx_PLUS (Pmode, base, orig); + /* Likewise, should we set special REG_NOTEs here? */ + } + return pic_ref; +} + +/* Try machine-dependent ways of modifying an illegitimate address + to be legitimate. If we find one, return the new, valid address. + This macro is used in only one place: `memory_address' in explow.c. + + OLDX is the address as it was before break_out_memory_refs was called. + In some cases it is useful to look at this to decide what needs to be done. + + MODE and WIN are passed so that this macro can use + GO_IF_LEGITIMATE_ADDRESS. + + It is always safe for this macro to do nothing. It exists to recognize + opportunities to optimize the output. + + For the PA, transform: + + memory(X + <large int>) + + into: + + if (<large int> & mask) >= 16 + Y = (<large int> & ~mask) + mask + 1 Round up. + else + Y = (<large int> & ~mask) Round down. + Z = X + Y + memory (Z + (<large int> - Y)); + + This is for CSE to find several similar references, and only use one Z. + + X can either be a SYMBOL_REF or REG, but because combine can not + perform a 4->2 combination we do nothing for SYMBOL_REF + D where + D will not fit in 14 bits. + + MODE_FLOAT references allow displacements which fit in 5 bits, so use + 0x1f as the mask. + + MODE_INT references allow displacements which fit in 14 bits, so use + 0x3fff as the mask. + + This relies on the fact that most mode MODE_FLOAT references will use FP + registers and most mode MODE_INT references will use integer registers. + (In the rare case of an FP register used in an integer MODE, we depend + on secondary reloads to clean things up.) + + + It is also beneficial to handle (plus (mult (X) (Y)) (Z)) in a special + manner if Y is 2, 4, or 8. (allows more shadd insns and shifted indexed + addressing modes to be used). + + Put X and Z into registers. Then put the entire expression into + a register. */ + +rtx +hppa_legitimize_address (x, oldx, mode) + rtx x, oldx ATTRIBUTE_UNUSED; + enum machine_mode mode; +{ + rtx orig = x; + + if (flag_pic) + return legitimize_pic_address (x, mode, gen_reg_rtx (Pmode)); + + /* Strip off CONST. */ + if (GET_CODE (x) == CONST) + x = XEXP (x, 0); + + /* Special case. Get the SYMBOL_REF into a register and use indexing. + That should always be safe. */ + if (GET_CODE (x) == PLUS + && GET_CODE (XEXP (x, 0)) == REG + && GET_CODE (XEXP (x, 1)) == SYMBOL_REF) + { + rtx reg = force_reg (SImode, XEXP (x, 1)); + return force_reg (SImode, gen_rtx_PLUS (SImode, reg, XEXP (x, 0))); + } + + /* Note we must reject symbols which represent function addresses + since the assembler/linker can't handle arithmetic on plabels. */ + if (GET_CODE (x) == PLUS + && GET_CODE (XEXP (x, 1)) == CONST_INT + && ((GET_CODE (XEXP (x, 0)) == SYMBOL_REF + && !FUNCTION_NAME_P (XSTR (XEXP (x, 0), 0))) + || GET_CODE (XEXP (x, 0)) == REG)) + { + rtx int_part, ptr_reg; + int newoffset; + int offset = INTVAL (XEXP (x, 1)); + int mask = GET_MODE_CLASS (mode) == MODE_FLOAT ? 0x1f : 0x3fff; + + /* CYGNUS LOCAL pa8000/law */ + mask = (GET_MODE_CLASS (mode) == MODE_FLOAT + ? (TARGET_PARISC_2_0 ? 0x3fff : 0x1f) : 0x3fff); + /* END CYGNUS LOCAL */ + + /* Choose which way to round the offset. Round up if we + are >= halfway to the next boundary. */ + if ((offset & mask) >= ((mask + 1) / 2)) + newoffset = (offset & ~ mask) + mask + 1; + else + newoffset = (offset & ~ mask); + + /* If the newoffset will not fit in 14 bits (ldo), then + handling this would take 4 or 5 instructions (2 to load + the SYMBOL_REF + 1 or 2 to load the newoffset + 1 to + add the new offset and the SYMBOL_REF.) Combine can + not handle 4->2 or 5->2 combinations, so do not create + them. */ + if (! VAL_14_BITS_P (newoffset) + && GET_CODE (XEXP (x, 0)) == SYMBOL_REF) + { + rtx const_part + = gen_rtx_CONST (VOIDmode, gen_rtx_PLUS (Pmode, + XEXP (x, 0), + GEN_INT (newoffset))); + rtx tmp_reg + = force_reg (Pmode, + gen_rtx_HIGH (Pmode, const_part)); + ptr_reg + = force_reg (Pmode, + gen_rtx_LO_SUM (Pmode, tmp_reg, const_part)); + } + else + { + if (! VAL_14_BITS_P (newoffset)) + int_part = force_reg (Pmode, GEN_INT (newoffset)); + else + int_part = GEN_INT (newoffset); + + ptr_reg = force_reg (Pmode, + gen_rtx_PLUS (Pmode, + force_reg (Pmode, XEXP (x, 0)), + int_part)); + } + return plus_constant (ptr_reg, offset - newoffset); + } + + /* Handle (plus (mult (a) (shadd_constant)) (b)). */ + + if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == MULT + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && shadd_constant_p (INTVAL (XEXP (XEXP (x, 0), 1))) + && (GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == 'o' + || GET_CODE (XEXP (x, 1)) == SUBREG) + && GET_CODE (XEXP (x, 1)) != CONST) + { + int val = INTVAL (XEXP (XEXP (x, 0), 1)); + rtx reg1, reg2; + + reg1 = XEXP (x, 1); + if (GET_CODE (reg1) != REG) + reg1 = force_reg (Pmode, force_operand (reg1, 0)); + + reg2 = XEXP (XEXP (x, 0), 0); + if (GET_CODE (reg2) != REG) + reg2 = force_reg (Pmode, force_operand (reg2, 0)); + + return force_reg (Pmode, gen_rtx_PLUS (Pmode, + gen_rtx_MULT (Pmode, reg2, + GEN_INT (val)), + reg1)); + } + + /* Similarly for (plus (plus (mult (a) (shadd_constant)) (b)) (c)). + + Only do so for floating point modes since this is more speculative + and we lose if it's an integer store. */ + if (GET_CODE (x) == PLUS + && GET_CODE (XEXP (x, 0)) == PLUS + && GET_CODE (XEXP (XEXP (x, 0), 0)) == MULT + && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)) == CONST_INT + && shadd_constant_p (INTVAL (XEXP (XEXP (XEXP (x, 0), 0), 1))) + && (mode == SFmode || mode == DFmode)) + { + + /* First, try and figure out what to use as a base register. */ + rtx reg1, reg2, base, idx, orig_base; + + reg1 = XEXP (XEXP (x, 0), 1); + reg2 = XEXP (x, 1); + base = NULL_RTX; + idx = NULL_RTX; + + /* Make sure they're both regs. If one was a SYMBOL_REF [+ const], + then emit_move_sequence will turn on REGNO_POINTER_FLAG so we'll + know it's a base register below. */ + if (GET_CODE (reg1) != REG) + reg1 = force_reg (Pmode, force_operand (reg1, 0)); + + if (GET_CODE (reg2) != REG) + reg2 = force_reg (Pmode, force_operand (reg2, 0)); + + /* Figure out what the base and index are. */ + + if (GET_CODE (reg1) == REG + && REGNO_POINTER_FLAG (REGNO (reg1))) + { + base = reg1; + orig_base = XEXP (XEXP (x, 0), 1); + idx = gen_rtx_PLUS (Pmode, + gen_rtx_MULT (Pmode, + XEXP (XEXP (XEXP (x, 0), 0), 0), + XEXP (XEXP (XEXP (x, 0), 0), 1)), + XEXP (x, 1)); + } + else if (GET_CODE (reg2) == REG + && REGNO_POINTER_FLAG (REGNO (reg2))) + { + base = reg2; + orig_base = XEXP (x, 1); + idx = XEXP (x, 0); + } + + if (base == 0) + return orig; + + /* If the index adds a large constant, try to scale the + constant so that it can be loaded with only one insn. */ + if (GET_CODE (XEXP (idx, 1)) == CONST_INT + && VAL_14_BITS_P (INTVAL (XEXP (idx, 1)) + / INTVAL (XEXP (XEXP (idx, 0), 1))) + && INTVAL (XEXP (idx, 1)) % INTVAL (XEXP (XEXP (idx, 0), 1)) == 0) + { + /* Divide the CONST_INT by the scale factor, then add it to A. */ + int val = INTVAL (XEXP (idx, 1)); + + val /= INTVAL (XEXP (XEXP (idx, 0), 1)); + reg1 = XEXP (XEXP (idx, 0), 0); + if (GET_CODE (reg1) != REG) + reg1 = force_reg (Pmode, force_operand (reg1, 0)); + + reg1 = force_reg (Pmode, gen_rtx_PLUS (Pmode, reg1, GEN_INT (val))); + + /* We can now generate a simple scaled indexed address. */ + return force_reg (Pmode, + gen_rtx_PLUS (Pmode, + gen_rtx_MULT (Pmode, reg1, + XEXP (XEXP (idx, 0), 1)), + base)); + } + + /* If B + C is still a valid base register, then add them. */ + if (GET_CODE (XEXP (idx, 1)) == CONST_INT + && INTVAL (XEXP (idx, 1)) <= 4096 + && INTVAL (XEXP (idx, 1)) >= -4096) + { + int val = INTVAL (XEXP (XEXP (idx, 0), 1)); + rtx reg1, reg2; + + reg1 = force_reg (Pmode, gen_rtx_PLUS (Pmode, base, XEXP (idx, 1))); + + reg2 = XEXP (XEXP (idx, 0), 0); + if (GET_CODE (reg2) != CONST_INT) + reg2 = force_reg (Pmode, force_operand (reg2, 0)); + + return force_reg (Pmode, gen_rtx_PLUS (Pmode, + gen_rtx_MULT (Pmode, reg2, + GEN_INT (val)), + reg1)); + } + + /* Get the index into a register, then add the base + index and + return a register holding the result. */ + + /* First get A into a register. */ + reg1 = XEXP (XEXP (idx, 0), 0); + if (GET_CODE (reg1) != REG) + reg1 = force_reg (Pmode, force_operand (reg1, 0)); + + /* And get B into a register. */ + reg2 = XEXP (idx, 1); + if (GET_CODE (reg2) != REG) + reg2 = force_reg (Pmode, force_operand (reg2, 0)); + + reg1 = force_reg (Pmode, + gen_rtx_PLUS (Pmode, + gen_rtx_MULT (Pmode, reg1, + XEXP (XEXP (idx, 0), 1)), + reg2)); + + /* Add the result to our base register and return. */ + return force_reg (Pmode, gen_rtx_PLUS (Pmode, base, reg1)); + + } + + /* Uh-oh. We might have an address for x[n-100000]. This needs + special handling to avoid creating an indexed memory address + with x-100000 as the base. + + If the constant part is small enough, then it's still safe because + there is a guard page at the beginning and end of the data segment. + + Scaled references are common enough that we want to try and rearrange the + terms so that we can use indexing for these addresses too. Only + do the optimization for floatint point modes. */ + + if (GET_CODE (x) == PLUS + && symbolic_expression_p (XEXP (x, 1))) + { + /* Ugly. We modify things here so that the address offset specified + by the index expression is computed first, then added to x to form + the entire address. */ + + rtx regx1, regx2, regy1, regy2, y; + + /* Strip off any CONST. */ + y = XEXP (x, 1); + if (GET_CODE (y) == CONST) + y = XEXP (y, 0); + + if (GET_CODE (y) == PLUS || GET_CODE (y) == MINUS) + { + /* See if this looks like + (plus (mult (reg) (shadd_const)) + (const (plus (symbol_ref) (const_int)))) + + Where const_int is small. In that case the const + expression is a valid pointer for indexing. + + If const_int is big, but can be divided evenly by shadd_const + and added to (reg). This allows more scaled indexed addresses. */ + if (GET_CODE (XEXP (y, 0)) == SYMBOL_REF + && GET_CODE (XEXP (x, 0)) == MULT + && GET_CODE (XEXP (y, 1)) == CONST_INT + && INTVAL (XEXP (y, 1)) >= -4096 + && INTVAL (XEXP (y, 1)) <= 4095 + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && shadd_constant_p (INTVAL (XEXP (XEXP (x, 0), 1)))) + { + int val = INTVAL (XEXP (XEXP (x, 0), 1)); + rtx reg1, reg2; + + reg1 = XEXP (x, 1); + if (GET_CODE (reg1) != REG) + reg1 = force_reg (Pmode, force_operand (reg1, 0)); + + reg2 = XEXP (XEXP (x, 0), 0); + if (GET_CODE (reg2) != REG) + reg2 = force_reg (Pmode, force_operand (reg2, 0)); + + return force_reg (Pmode, + gen_rtx_PLUS (Pmode, + gen_rtx_MULT (Pmode, reg2, + GEN_INT (val)), + reg1)); + } + else if ((mode == DFmode || mode == SFmode) + && GET_CODE (XEXP (y, 0)) == SYMBOL_REF + && GET_CODE (XEXP (x, 0)) == MULT + && GET_CODE (XEXP (y, 1)) == CONST_INT + && INTVAL (XEXP (y, 1)) % INTVAL (XEXP (XEXP (x, 0), 1)) == 0 + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && shadd_constant_p (INTVAL (XEXP (XEXP (x, 0), 1)))) + { + regx1 + = force_reg (Pmode, GEN_INT (INTVAL (XEXP (y, 1)) + / INTVAL (XEXP (XEXP (x, 0), 1)))); + regx2 = XEXP (XEXP (x, 0), 0); + if (GET_CODE (regx2) != REG) + regx2 = force_reg (Pmode, force_operand (regx2, 0)); + regx2 = force_reg (Pmode, gen_rtx_fmt_ee (GET_CODE (y), Pmode, + regx2, regx1)); + return force_reg (Pmode, + gen_rtx_PLUS (Pmode, + gen_rtx_MULT (Pmode, regx2, + XEXP (XEXP (x, 0), + 1)), + force_reg (Pmode, XEXP (y, 0)))); + } + else if (GET_CODE (XEXP (y, 1)) == CONST_INT + && INTVAL (XEXP (y, 1)) >= -4096 + && INTVAL (XEXP (y, 1)) <= 4095) + { + /* This is safe because of the guard page at the + beginning and end of the data space. Just + return the original address. */ + return orig; + } + else + { + /* Doesn't look like one we can optimize. */ + regx1 = force_reg (Pmode, force_operand (XEXP (x, 0), 0)); + regy1 = force_reg (Pmode, force_operand (XEXP (y, 0), 0)); + regy2 = force_reg (Pmode, force_operand (XEXP (y, 1), 0)); + regx1 = force_reg (Pmode, + gen_rtx_fmt_ee (GET_CODE (y), Pmode, + regx1, regy2)); + return force_reg (Pmode, gen_rtx_PLUS (Pmode, regx1, regy1)); + } + } + } + + return orig; +} + +/* For the HPPA, REG and REG+CONST is cost 0 + and addresses involving symbolic constants are cost 2. + + PIC addresses are very expensive. + + It is no coincidence that this has the same structure + as GO_IF_LEGITIMATE_ADDRESS. */ +int +hppa_address_cost (X) + rtx X; +{ + if (GET_CODE (X) == PLUS) + return 1; + else if (GET_CODE (X) == LO_SUM) + return 1; + else if (GET_CODE (X) == HIGH) + return 2; + return 4; +} + +/* Emit insns to move operands[1] into operands[0]. + + Return 1 if we have written out everything that needs to be done to + do the move. Otherwise, return 0 and the caller will emit the move + normally. */ + +int +emit_move_sequence (operands, mode, scratch_reg) + rtx *operands; + enum machine_mode mode; + rtx scratch_reg; +{ + register rtx operand0 = operands[0]; + register rtx operand1 = operands[1]; + register rtx tem; + + if (scratch_reg + && reload_in_progress && GET_CODE (operand0) == REG + && REGNO (operand0) >= FIRST_PSEUDO_REGISTER) + operand0 = reg_equiv_mem[REGNO (operand0)]; + else if (scratch_reg + && reload_in_progress && GET_CODE (operand0) == SUBREG + && GET_CODE (SUBREG_REG (operand0)) == REG + && REGNO (SUBREG_REG (operand0)) >= FIRST_PSEUDO_REGISTER) + { + SUBREG_REG (operand0) = reg_equiv_mem[REGNO (SUBREG_REG (operand0))]; + operand0 = alter_subreg (operand0); + } + + if (scratch_reg + && reload_in_progress && GET_CODE (operand1) == REG + && REGNO (operand1) >= FIRST_PSEUDO_REGISTER) + operand1 = reg_equiv_mem[REGNO (operand1)]; + else if (scratch_reg + && reload_in_progress && GET_CODE (operand1) == SUBREG + && GET_CODE (SUBREG_REG (operand1)) == REG + && REGNO (SUBREG_REG (operand1)) >= FIRST_PSEUDO_REGISTER) + { + SUBREG_REG (operand1) = reg_equiv_mem[REGNO (SUBREG_REG (operand1))]; + operand1 = alter_subreg (operand1); + } + + if (scratch_reg && reload_in_progress && GET_CODE (operand0) == MEM + && ((tem = find_replacement (&XEXP (operand0, 0))) + != XEXP (operand0, 0))) + operand0 = gen_rtx_MEM (GET_MODE (operand0), tem); + if (scratch_reg && reload_in_progress && GET_CODE (operand1) == MEM + && ((tem = find_replacement (&XEXP (operand1, 0))) + != XEXP (operand1, 0))) + operand1 = gen_rtx_MEM (GET_MODE (operand1), tem); + + /* Handle secondary reloads for loads/stores of FP registers from + REG+D addresses where D does not fit in 5 bits, including + (subreg (mem (addr))) cases. */ + if (fp_reg_operand (operand0, mode) + && ((GET_CODE (operand1) == MEM + && ! memory_address_p (DFmode, XEXP (operand1, 0))) + || ((GET_CODE (operand1) == SUBREG + && GET_CODE (XEXP (operand1, 0)) == MEM + && !memory_address_p (DFmode, XEXP (XEXP (operand1, 0), 0))))) + && scratch_reg) + { + if (GET_CODE (operand1) == SUBREG) + operand1 = XEXP (operand1, 0); + + scratch_reg = gen_rtx_REG (SImode, REGNO (scratch_reg)); + + /* D might not fit in 14 bits either; for such cases load D into + scratch reg. */ + if (!memory_address_p (SImode, XEXP (operand1, 0))) + { + emit_move_insn (scratch_reg, XEXP (XEXP (operand1, 0), 1)); + emit_move_insn (scratch_reg, gen_rtx_fmt_ee (GET_CODE (XEXP (operand1, 0)), + SImode, + XEXP (XEXP (operand1, 0), 0), + scratch_reg)); + } + else + emit_move_insn (scratch_reg, XEXP (operand1, 0)); + emit_insn (gen_rtx_SET (VOIDmode, operand0, gen_rtx_MEM (mode, + scratch_reg))); + return 1; + } + else if (fp_reg_operand (operand1, mode) + && ((GET_CODE (operand0) == MEM + && ! memory_address_p (DFmode, XEXP (operand0, 0))) + || ((GET_CODE (operand0) == SUBREG) + && GET_CODE (XEXP (operand0, 0)) == MEM + && !memory_address_p (DFmode, XEXP (XEXP (operand0, 0), 0)))) + && scratch_reg) + { + if (GET_CODE (operand0) == SUBREG) + operand0 = XEXP (operand0, 0); + + scratch_reg = gen_rtx_REG (SImode, REGNO (scratch_reg)); + /* D might not fit in 14 bits either; for such cases load D into + scratch reg. */ + if (!memory_address_p (SImode, XEXP (operand0, 0))) + { + emit_move_insn (scratch_reg, XEXP (XEXP (operand0, 0), 1)); + emit_move_insn (scratch_reg, gen_rtx_fmt_ee (GET_CODE (XEXP (operand0, + 0)), + SImode, + XEXP (XEXP (operand0, 0), + 0), + scratch_reg)); + } + else + emit_move_insn (scratch_reg, XEXP (operand0, 0)); + emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_MEM (mode, scratch_reg), + operand1)); + return 1; + } + /* Handle secondary reloads for loads of FP registers from constant + expressions by forcing the constant into memory. + + use scratch_reg to hold the address of the memory location. + + ??? The proper fix is to change PREFERRED_RELOAD_CLASS to return + NO_REGS when presented with a const_int and an register class + containing only FP registers. Doing so unfortunately creates + more problems than it solves. Fix this for 2.5. */ + else if (fp_reg_operand (operand0, mode) + && CONSTANT_P (operand1) + && scratch_reg) + { + rtx xoperands[2]; + + /* Force the constant into memory and put the address of the + memory location into scratch_reg. */ + xoperands[0] = scratch_reg; + xoperands[1] = XEXP (force_const_mem (mode, operand1), 0); + emit_move_sequence (xoperands, Pmode, 0); + + /* Now load the destination register. */ + emit_insn (gen_rtx_SET (mode, operand0, gen_rtx_MEM (mode, scratch_reg))); + return 1; + } + /* Handle secondary reloads for SAR. These occur when trying to load + the SAR from memory a FP register, or with a constant. */ + else if (GET_CODE (operand0) == REG + && REGNO_REG_CLASS (REGNO (operand0)) == SHIFT_REGS + && (GET_CODE (operand1) == MEM + || GET_CODE (operand1) == CONST_INT + || (GET_CODE (operand1) == REG + && FP_REG_CLASS_P (REGNO_REG_CLASS (REGNO (operand1))))) + && scratch_reg) + { + /* D might not fit in 14 bits either; for such cases load D into + scratch reg. */ + if (GET_CODE (operand1) == MEM + && !memory_address_p (SImode, XEXP (operand1, 0))) + { + emit_move_insn (scratch_reg, XEXP (XEXP (operand1, 0), 1)); + emit_move_insn (scratch_reg, gen_rtx_fmt_ee (GET_CODE (XEXP (operand1, + 0)), + SImode, + XEXP (XEXP (operand1, 0), + 0), + scratch_reg)); + emit_move_insn (scratch_reg, gen_rtx_MEM (GET_MODE (operand1), + scratch_reg)); + } + else + emit_move_insn (scratch_reg, operand1); + emit_move_insn (operand0, scratch_reg); + return 1; + } + /* Handle most common case: storing into a register. */ + else if (register_operand (operand0, mode)) + { + if (register_operand (operand1, mode) + || (GET_CODE (operand1) == CONST_INT && INT_14_BITS (operand1)) + || (operand1 == CONST0_RTX (mode)) + || (GET_CODE (operand1) == HIGH + && !symbolic_operand (XEXP (operand1, 0), VOIDmode)) + /* Only `general_operands' can come here, so MEM is ok. */ + || GET_CODE (operand1) == MEM) + { + /* Run this case quickly. */ + emit_insn (gen_rtx_SET (VOIDmode, operand0, operand1)); + return 1; + } + } + else if (GET_CODE (operand0) == MEM) + { + if (mode == DFmode && operand1 == CONST0_RTX (mode) + && !(reload_in_progress || reload_completed)) + { + rtx temp = gen_reg_rtx (DFmode); + + emit_insn (gen_rtx_SET (VOIDmode, temp, operand1)); + emit_insn (gen_rtx_SET (VOIDmode, operand0, temp)); + return 1; + } + if (register_operand (operand1, mode) || operand1 == CONST0_RTX (mode)) + { + /* Run this case quickly. */ + emit_insn (gen_rtx_SET (VOIDmode, operand0, operand1)); + return 1; + } + if (! (reload_in_progress || reload_completed)) + { + operands[0] = validize_mem (operand0); + operands[1] = operand1 = force_reg (mode, operand1); + } + } + + /* Simplify the source if we need to. + Note we do have to handle function labels here, even though we do + not consider them legitimate constants. Loop optimizations can + call the emit_move_xxx with one as a source. */ + if ((GET_CODE (operand1) != HIGH && immediate_operand (operand1, mode)) + || function_label_operand (operand1, mode) + || (GET_CODE (operand1) == HIGH + && symbolic_operand (XEXP (operand1, 0), mode))) + { + int ishighonly = 0; + + if (GET_CODE (operand1) == HIGH) + { + ishighonly = 1; + operand1 = XEXP (operand1, 0); + } + if (symbolic_operand (operand1, mode)) + { + /* Argh. The assembler and linker can't handle arithmetic + involving plabels. + + So we force the plabel into memory, load operand0 from + the memory location, then add in the constant part. */ + if ((GET_CODE (operand1) == CONST + && GET_CODE (XEXP (operand1, 0)) == PLUS + && function_label_operand (XEXP (XEXP (operand1, 0), 0), Pmode)) + || function_label_operand (operand1, mode)) + { + rtx temp, const_part; + + /* Figure out what (if any) scratch register to use. */ + if (reload_in_progress || reload_completed) + scratch_reg = scratch_reg ? scratch_reg : operand0; + else if (flag_pic) + scratch_reg = gen_reg_rtx (Pmode); + + if (GET_CODE (operand1) == CONST) + { + /* Save away the constant part of the expression. */ + const_part = XEXP (XEXP (operand1, 0), 1); + if (GET_CODE (const_part) != CONST_INT) + abort (); + + /* Force the function label into memory. */ + temp = force_const_mem (mode, XEXP (XEXP (operand1, 0), 0)); + } + else + { + /* No constant part. */ + const_part = NULL_RTX; + + /* Force the function label into memory. */ + temp = force_const_mem (mode, operand1); + } + + + /* Get the address of the memory location. PIC-ify it if + necessary. */ + temp = XEXP (temp, 0); + if (flag_pic) + temp = legitimize_pic_address (temp, mode, scratch_reg); + + /* Put the address of the memory location into our destination + register. */ + operands[1] = temp; + emit_move_sequence (operands, mode, scratch_reg); + + /* Now load from the memory location into our destination + register. */ + operands[1] = gen_rtx_MEM (Pmode, operands[0]); + emit_move_sequence (operands, mode, scratch_reg); + + /* And add back in the constant part. */ + if (const_part != NULL_RTX) + expand_inc (operand0, const_part); + + return 1; + } + + if (flag_pic) + { + rtx temp; + + if (reload_in_progress || reload_completed) + temp = scratch_reg ? scratch_reg : operand0; + else + temp = gen_reg_rtx (Pmode); + + /* (const (plus (symbol) (const_int))) must be forced to + memory during/after reload if the const_int will not fit + in 14 bits. */ + if (GET_CODE (operand1) == CONST + && GET_CODE (XEXP (operand1, 0)) == PLUS + && GET_CODE (XEXP (XEXP (operand1, 0), 1)) == CONST_INT + && !INT_14_BITS (XEXP (XEXP (operand1, 0), 1)) + && (reload_completed || reload_in_progress) + && flag_pic) + { + operands[1] = force_const_mem (mode, operand1); + operands[1] = legitimize_pic_address (XEXP (operands[1], 0), + mode, temp); + emit_move_sequence (operands, mode, temp); + } + else + { + operands[1] = legitimize_pic_address (operand1, mode, temp); + emit_insn (gen_rtx_SET (VOIDmode, operand0, operands[1])); + } + } + /* On the HPPA, references to data space are supposed to use dp, + register 27, but showing it in the RTL inhibits various cse + and loop optimizations. */ + else + { + rtx temp, set; + + if (reload_in_progress || reload_completed) + temp = scratch_reg ? scratch_reg : operand0; + else + temp = gen_reg_rtx (mode); + + /* Loading a SYMBOL_REF into a register makes that register + safe to be used as the base in an indexed address. + + Don't mark hard registers though. That loses. */ + if (GET_CODE (operand0) == REG + && REGNO (operand0) >= FIRST_PSEUDO_REGISTER) + REGNO_POINTER_FLAG (REGNO (operand0)) = 1; + if (REGNO (temp) >= FIRST_PSEUDO_REGISTER) + REGNO_POINTER_FLAG (REGNO (temp)) = 1; + if (ishighonly) + set = gen_rtx_SET (mode, operand0, temp); + else + set = gen_rtx_SET (VOIDmode, operand0, + gen_rtx_LO_SUM (mode, temp, operand1)); + + emit_insn (gen_rtx_SET (VOIDmode, + temp, + gen_rtx_HIGH (mode, operand1))); + emit_insn (set); + + } + return 1; + } + else if (GET_CODE (operand1) != CONST_INT + || ! cint_ok_for_move (INTVAL (operand1))) + { + rtx temp; + + if (reload_in_progress || reload_completed) + temp = operand0; + else + temp = gen_reg_rtx (mode); + + emit_insn (gen_rtx_SET (VOIDmode, temp, + gen_rtx_HIGH (mode, operand1))); + operands[1] = gen_rtx_LO_SUM (mode, temp, operand1); + } + } + /* Now have insn-emit do whatever it normally does. */ + return 0; +} + +/* Examine EXP and return nonzero if it contains an ADDR_EXPR (meaning + it will need a link/runtime reloc). */ + +int +reloc_needed (exp) + tree exp; +{ + int reloc = 0; + + switch (TREE_CODE (exp)) + { + case ADDR_EXPR: + return 1; + + case PLUS_EXPR: + case MINUS_EXPR: + reloc = reloc_needed (TREE_OPERAND (exp, 0)); + reloc |= reloc_needed (TREE_OPERAND (exp, 1)); + break; + + case NOP_EXPR: + case CONVERT_EXPR: + case NON_LVALUE_EXPR: + reloc = reloc_needed (TREE_OPERAND (exp, 0)); + break; + + case CONSTRUCTOR: + { + register tree link; + for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link)) + if (TREE_VALUE (link) != 0) + reloc |= reloc_needed (TREE_VALUE (link)); + } + break; + + case ERROR_MARK: + break; + + default: + break; + } + return reloc; +} + +/* Does operand (which is a symbolic_operand) live in text space? If + so SYMBOL_REF_FLAG, which is set by ENCODE_SECTION_INFO, will be true. */ + +int +read_only_operand (operand) + rtx operand; +{ + if (GET_CODE (operand) == CONST) + operand = XEXP (XEXP (operand, 0), 0); + if (flag_pic) + { + if (GET_CODE (operand) == SYMBOL_REF) + return SYMBOL_REF_FLAG (operand) && !CONSTANT_POOL_ADDRESS_P (operand); + } + else + { + if (GET_CODE (operand) == SYMBOL_REF) + return SYMBOL_REF_FLAG (operand) || CONSTANT_POOL_ADDRESS_P (operand); + } + return 1; +} + + +/* Return the best assembler insn template + for moving operands[1] into operands[0] as a fullword. */ +char * +singlemove_string (operands) + rtx *operands; +{ + HOST_WIDE_INT intval; + + if (GET_CODE (operands[0]) == MEM) + return "stw %r1,%0"; + if (GET_CODE (operands[1]) == MEM) + return "ldw %1,%0"; + if (GET_CODE (operands[1]) == CONST_DOUBLE) + { + long i; + REAL_VALUE_TYPE d; + + if (GET_MODE (operands[1]) != SFmode) + abort (); + + /* Translate the CONST_DOUBLE to a CONST_INT with the same target + bit pattern. */ + REAL_VALUE_FROM_CONST_DOUBLE (d, operands[1]); + REAL_VALUE_TO_TARGET_SINGLE (d, i); + + operands[1] = GEN_INT (i); + /* Fall through to CONST_INT case. */ + } + if (GET_CODE (operands[1]) == CONST_INT) + { + intval = INTVAL (operands[1]); + + if (VAL_14_BITS_P (intval)) + return "ldi %1,%0"; + else if ((intval & 0x7ff) == 0) + return "ldil L'%1,%0"; + else if (zdepi_cint_p (intval)) + return "zdepi %Z1,%0"; + else + return "ldil L'%1,%0\n\tldo R'%1(%0),%0"; + } + return "copy %1,%0"; +} + + +/* Compute position (in OP[1]) and width (in OP[2]) + useful for copying IMM to a register using the zdepi + instructions. Store the immediate value to insert in OP[0]. */ +void +compute_zdepi_operands (imm, op) + unsigned HOST_WIDE_INT imm; + unsigned *op; +{ + int lsb, len; + + /* Find the least significant set bit in IMM. */ + for (lsb = 0; lsb < 32; lsb++) + { + if ((imm & 1) != 0) + break; + imm >>= 1; + } + + /* Choose variants based on *sign* of the 5-bit field. */ + if ((imm & 0x10) == 0) + len = (lsb <= 28) ? 4 : 32 - lsb; + else + { + /* Find the width of the bitstring in IMM. */ + for (len = 5; len < 32; len++) + { + if ((imm & (1 << len)) == 0) + break; + } + + /* Sign extend IMM as a 5-bit value. */ + imm = (imm & 0xf) - 0x10; + } + + op[0] = imm; + op[1] = 31 - lsb; + op[2] = len; +} + +/* Output assembler code to perform a doubleword move insn + with operands OPERANDS. */ + +char * +output_move_double (operands) + rtx *operands; +{ + enum { REGOP, OFFSOP, MEMOP, CNSTOP, RNDOP } optype0, optype1; + rtx latehalf[2]; + rtx addreg0 = 0, addreg1 = 0; + + /* First classify both operands. */ + + if (REG_P (operands[0])) + optype0 = REGOP; + else if (offsettable_memref_p (operands[0])) + optype0 = OFFSOP; + else if (GET_CODE (operands[0]) == MEM) + optype0 = MEMOP; + else + optype0 = RNDOP; + + if (REG_P (operands[1])) + optype1 = REGOP; + else if (CONSTANT_P (operands[1])) + optype1 = CNSTOP; + else if (offsettable_memref_p (operands[1])) + optype1 = OFFSOP; + else if (GET_CODE (operands[1]) == MEM) + optype1 = MEMOP; + else + optype1 = RNDOP; + + /* Check for the cases that the operand constraints are not + supposed to allow to happen. Abort if we get one, + because generating code for these cases is painful. */ + + if (optype0 != REGOP && optype1 != REGOP) + abort (); + + /* Handle auto decrementing and incrementing loads and stores + specifically, since the structure of the function doesn't work + for them without major modification. Do it better when we learn + this port about the general inc/dec addressing of PA. + (This was written by tege. Chide him if it doesn't work.) */ + + if (optype0 == MEMOP) + { + /* We have to output the address syntax ourselves, since print_operand + doesn't deal with the addresses we want to use. Fix this later. */ + + rtx addr = XEXP (operands[0], 0); + if (GET_CODE (addr) == POST_INC || GET_CODE (addr) == POST_DEC) + { + rtx high_reg = gen_rtx_SUBREG (SImode, operands[1], 0); + + operands[0] = XEXP (addr, 0); + if (GET_CODE (operands[1]) != REG || GET_CODE (operands[0]) != REG) + abort (); + + if (!reg_overlap_mentioned_p (high_reg, addr)) + { + /* No overlap between high target register and address + register. (We do this in a non-obvious way to + save a register file writeback) */ + if (GET_CODE (addr) == POST_INC) + return "stws,ma %1,8(0,%0)\n\tstw %R1,-4(0,%0)"; + return "stws,ma %1,-8(0,%0)\n\tstw %R1,12(0,%0)"; + } + else + abort(); + } + else if (GET_CODE (addr) == PRE_INC || GET_CODE (addr) == PRE_DEC) + { + rtx high_reg = gen_rtx_SUBREG (SImode, operands[1], 0); + + operands[0] = XEXP (addr, 0); + if (GET_CODE (operands[1]) != REG || GET_CODE (operands[0]) != REG) + abort (); + + if (!reg_overlap_mentioned_p (high_reg, addr)) + { + /* No overlap between high target register and address + register. (We do this in a non-obvious way to + save a register file writeback) */ + if (GET_CODE (addr) == PRE_INC) + return "stws,mb %1,8(0,%0)\n\tstw %R1,4(0,%0)"; + return "stws,mb %1,-8(0,%0)\n\tstw %R1,4(0,%0)"; + } + else + abort(); + } + } + if (optype1 == MEMOP) + { + /* We have to output the address syntax ourselves, since print_operand + doesn't deal with the addresses we want to use. Fix this later. */ + + rtx addr = XEXP (operands[1], 0); + if (GET_CODE (addr) == POST_INC || GET_CODE (addr) == POST_DEC) + { + rtx high_reg = gen_rtx_SUBREG (SImode, operands[0], 0); + + operands[1] = XEXP (addr, 0); + if (GET_CODE (operands[0]) != REG || GET_CODE (operands[1]) != REG) + abort (); + + if (!reg_overlap_mentioned_p (high_reg, addr)) + { + /* No overlap between high target register and address + register. (We do this in a non-obvious way to + save a register file writeback) */ + if (GET_CODE (addr) == POST_INC) + return "ldws,ma 8(0,%1),%0\n\tldw -4(0,%1),%R0"; + return "ldws,ma -8(0,%1),%0\n\tldw 12(0,%1),%R0"; + } + else + { + /* This is an undefined situation. We should load into the + address register *and* update that register. Probably + we don't need to handle this at all. */ + if (GET_CODE (addr) == POST_INC) + return "ldw 4(0,%1),%R0\n\tldws,ma 8(0,%1),%0"; + return "ldw 4(0,%1),%R0\n\tldws,ma -8(0,%1),%0"; + } + } + else if (GET_CODE (addr) == PRE_INC || GET_CODE (addr) == PRE_DEC) + { + rtx high_reg = gen_rtx_SUBREG (SImode, operands[0], 0); + + operands[1] = XEXP (addr, 0); + if (GET_CODE (operands[0]) != REG || GET_CODE (operands[1]) != REG) + abort (); + + if (!reg_overlap_mentioned_p (high_reg, addr)) + { + /* No overlap between high target register and address + register. (We do this in a non-obvious way to + save a register file writeback) */ + if (GET_CODE (addr) == PRE_INC) + return "ldws,mb 8(0,%1),%0\n\tldw 4(0,%1),%R0"; + return "ldws,mb -8(0,%1),%0\n\tldw 4(0,%1),%R0"; + } + else + { + /* This is an undefined situation. We should load into the + address register *and* update that register. Probably + we don't need to handle this at all. */ + if (GET_CODE (addr) == PRE_INC) + return "ldw 12(0,%1),%R0\n\tldws,mb 8(0,%1),%0"; + return "ldw -4(0,%1),%R0\n\tldws,mb -8(0,%1),%0"; + } + } + else if (GET_CODE (addr) == PLUS + && GET_CODE (XEXP (addr, 0)) == MULT) + { + rtx high_reg = gen_rtx_SUBREG (SImode, operands[0], 0); + + if (!reg_overlap_mentioned_p (high_reg, addr)) + { + rtx xoperands[3]; + + xoperands[0] = high_reg; + xoperands[1] = XEXP (addr, 1); + xoperands[2] = XEXP (XEXP (addr, 0), 0); + xoperands[3] = XEXP (XEXP (addr, 0), 1); + output_asm_insn ("sh%O3addl %2,%1,%0", xoperands); + return "ldw 4(0,%0),%R0\n\tldw 0(0,%0),%0"; + } + else + { + rtx xoperands[3]; + + xoperands[0] = high_reg; + xoperands[1] = XEXP (addr, 1); + xoperands[2] = XEXP (XEXP (addr, 0), 0); + xoperands[3] = XEXP (XEXP (addr, 0), 1); + output_asm_insn ("sh%O3addl %2,%1,%R0", xoperands); + return "ldw 0(0,%R0),%0\n\tldw 4(0,%R0),%R0"; + } + + } + } + + /* If an operand is an unoffsettable memory ref, find a register + we can increment temporarily to make it refer to the second word. */ + + if (optype0 == MEMOP) + addreg0 = find_addr_reg (XEXP (operands[0], 0)); + + if (optype1 == MEMOP) + addreg1 = find_addr_reg (XEXP (operands[1], 0)); + + /* Ok, we can do one word at a time. + Normally we do the low-numbered word first. + + In either case, set up in LATEHALF the operands to use + for the high-numbered word and in some cases alter the + operands in OPERANDS to be suitable for the low-numbered word. */ + + if (optype0 == REGOP) + latehalf[0] = gen_rtx_REG (SImode, REGNO (operands[0]) + 1); + else if (optype0 == OFFSOP) + latehalf[0] = adj_offsettable_operand (operands[0], 4); + else + latehalf[0] = operands[0]; + + if (optype1 == REGOP) + latehalf[1] = gen_rtx_REG (SImode, REGNO (operands[1]) + 1); + else if (optype1 == OFFSOP) + latehalf[1] = adj_offsettable_operand (operands[1], 4); + else if (optype1 == CNSTOP) + split_double (operands[1], &operands[1], &latehalf[1]); + else + latehalf[1] = operands[1]; + + /* If the first move would clobber the source of the second one, + do them in the other order. + + This can happen in two cases: + + mem -> register where the first half of the destination register + is the same register used in the memory's address. Reload + can create such insns. + + mem in this case will be either register indirect or register + indirect plus a valid offset. + + register -> register move where REGNO(dst) == REGNO(src + 1) + someone (Tim/Tege?) claimed this can happen for parameter loads. + + Handle mem -> register case first. */ + if (optype0 == REGOP + && (optype1 == MEMOP || optype1 == OFFSOP) + && refers_to_regno_p (REGNO (operands[0]), REGNO (operands[0]) + 1, + operands[1], 0)) + { + /* Do the late half first. */ + if (addreg1) + output_asm_insn ("ldo 4(%0),%0", &addreg1); + output_asm_insn (singlemove_string (latehalf), latehalf); + + /* Then clobber. */ + if (addreg1) + output_asm_insn ("ldo -4(%0),%0", &addreg1); + return singlemove_string (operands); + } + + /* Now handle register -> register case. */ + if (optype0 == REGOP && optype1 == REGOP + && REGNO (operands[0]) == REGNO (operands[1]) + 1) + { + output_asm_insn (singlemove_string (latehalf), latehalf); + return singlemove_string (operands); + } + + /* Normal case: do the two words, low-numbered first. */ + + output_asm_insn (singlemove_string (operands), operands); + + /* Make any unoffsettable addresses point at high-numbered word. */ + if (addreg0) + output_asm_insn ("ldo 4(%0),%0", &addreg0); + if (addreg1) + output_asm_insn ("ldo 4(%0),%0", &addreg1); + + /* Do that word. */ + output_asm_insn (singlemove_string (latehalf), latehalf); + + /* Undo the adds we just did. */ + if (addreg0) + output_asm_insn ("ldo -4(%0),%0", &addreg0); + if (addreg1) + output_asm_insn ("ldo -4(%0),%0", &addreg1); + + return ""; +} + +char * +output_fp_move_double (operands) + rtx *operands; +{ + if (FP_REG_P (operands[0])) + { + if (FP_REG_P (operands[1]) + || operands[1] == CONST0_RTX (GET_MODE (operands[0]))) + output_asm_insn ("fcpy,dbl %r1,%0", operands); + else + output_asm_insn ("fldd%F1 %1,%0", operands); + } + else if (FP_REG_P (operands[1])) + { + output_asm_insn ("fstd%F0 %1,%0", operands); + } + else if (operands[1] == CONST0_RTX (GET_MODE (operands[0]))) + { + if (GET_CODE (operands[0]) == REG) + { + rtx xoperands[2]; + xoperands[1] = gen_rtx_REG (SImode, REGNO (operands[0]) + 1); + xoperands[0] = operands[0]; + output_asm_insn ("copy %%r0,%0\n\tcopy %%r0,%1", xoperands); + } + /* This is a pain. You have to be prepared to deal with an + arbitrary address here including pre/post increment/decrement. + + so avoid this in the MD. */ + else + abort (); + } + else abort (); + return ""; +} + +/* Return a REG that occurs in ADDR with coefficient 1. + ADDR can be effectively incremented by incrementing REG. */ + +static rtx +find_addr_reg (addr) + rtx addr; +{ + while (GET_CODE (addr) == PLUS) + { + if (GET_CODE (XEXP (addr, 0)) == REG) + addr = XEXP (addr, 0); + else if (GET_CODE (XEXP (addr, 1)) == REG) + addr = XEXP (addr, 1); + else if (CONSTANT_P (XEXP (addr, 0))) + addr = XEXP (addr, 1); + else if (CONSTANT_P (XEXP (addr, 1))) + addr = XEXP (addr, 0); + else + abort (); + } + if (GET_CODE (addr) == REG) + return addr; + abort (); +} + +/* Emit code to perform a block move. + + OPERANDS[0] is the destination pointer as a REG, clobbered. + OPERANDS[1] is the source pointer as a REG, clobbered. + OPERANDS[2] is a register for temporary storage. + OPERANDS[4] is the size as a CONST_INT + OPERANDS[3] is a register for temporary storage. + OPERANDS[5] is the alignment safe to use, as a CONST_INT. + OPERANDS[6] is another temporary register. */ + +char * +output_block_move (operands, size_is_constant) + rtx *operands; + int size_is_constant ATTRIBUTE_UNUSED; +{ + int align = INTVAL (operands[5]); + unsigned long n_bytes = INTVAL (operands[4]); + + /* We can't move more than four bytes at a time because the PA + has no longer integer move insns. (Could use fp mem ops?) */ + if (align > 4) + align = 4; + + /* Note that we know each loop below will execute at least twice + (else we would have open-coded the copy). */ + switch (align) + { + case 4: + /* Pre-adjust the loop counter. */ + operands[4] = GEN_INT (n_bytes - 8); + output_asm_insn ("ldi %4,%2", operands); + + /* Copying loop. */ + output_asm_insn ("ldws,ma 4(0,%1),%3", operands); + output_asm_insn ("ldws,ma 4(0,%1),%6", operands); + output_asm_insn ("stws,ma %3,4(0,%0)", operands); + output_asm_insn ("addib,>= -8,%2,.-12", operands); + output_asm_insn ("stws,ma %6,4(0,%0)", operands); + + /* Handle the residual. There could be up to 7 bytes of + residual to copy! */ + if (n_bytes % 8 != 0) + { + operands[4] = GEN_INT (n_bytes % 4); + if (n_bytes % 8 >= 4) + output_asm_insn ("ldws,ma 4(0,%1),%3", operands); + if (n_bytes % 4 != 0) + output_asm_insn ("ldw 0(0,%1),%6", operands); + if (n_bytes % 8 >= 4) + output_asm_insn ("stws,ma %3,4(0,%0)", operands); + if (n_bytes % 4 != 0) + output_asm_insn ("stbys,e %6,%4(0,%0)", operands); + } + return ""; + + case 2: + /* Pre-adjust the loop counter. */ + operands[4] = GEN_INT (n_bytes - 4); + output_asm_insn ("ldi %4,%2", operands); + + /* Copying loop. */ + output_asm_insn ("ldhs,ma 2(0,%1),%3", operands); + output_asm_insn ("ldhs,ma 2(0,%1),%6", operands); + output_asm_insn ("sths,ma %3,2(0,%0)", operands); + output_asm_insn ("addib,>= -4,%2,.-12", operands); + output_asm_insn ("sths,ma %6,2(0,%0)", operands); + + /* Handle the residual. */ + if (n_bytes % 4 != 0) + { + if (n_bytes % 4 >= 2) + output_asm_insn ("ldhs,ma 2(0,%1),%3", operands); + if (n_bytes % 2 != 0) + output_asm_insn ("ldb 0(0,%1),%6", operands); + if (n_bytes % 4 >= 2) + output_asm_insn ("sths,ma %3,2(0,%0)", operands); + if (n_bytes % 2 != 0) + output_asm_insn ("stb %6,0(0,%0)", operands); + } + return ""; + + case 1: + /* Pre-adjust the loop counter. */ + operands[4] = GEN_INT (n_bytes - 2); + output_asm_insn ("ldi %4,%2", operands); + + /* Copying loop. */ + output_asm_insn ("ldbs,ma 1(0,%1),%3", operands); + output_asm_insn ("ldbs,ma 1(0,%1),%6", operands); + output_asm_insn ("stbs,ma %3,1(0,%0)", operands); + output_asm_insn ("addib,>= -2,%2,.-12", operands); + output_asm_insn ("stbs,ma %6,1(0,%0)", operands); + + /* Handle the residual. */ + if (n_bytes % 2 != 0) + { + output_asm_insn ("ldb 0(0,%1),%3", operands); + output_asm_insn ("stb %3,0(0,%0)", operands); + } + return ""; + + default: + abort (); + } +} + +/* Count the number of insns necessary to handle this block move. + + Basic structure is the same as emit_block_move, except that we + count insns rather than emit them. */ + +int +compute_movstrsi_length (insn) + rtx insn; +{ + rtx pat = PATTERN (insn); + int align = INTVAL (XEXP (XVECEXP (pat, 0, 6), 0)); + unsigned long n_bytes = INTVAL (XEXP (XVECEXP (pat, 0, 5), 0)); + unsigned int n_insns = 0; + + /* We can't move more than four bytes at a time because the PA + has no longer integer move insns. (Could use fp mem ops?) */ + if (align > 4) + align = 4; + + /* The basic copying loop. */ + n_insns = 6; + + /* Residuals. */ + if (n_bytes % (2 * align) != 0) + { + if ((n_bytes % (2 * align)) >= align) + n_insns += 2; + + if ((n_bytes % align) != 0) + n_insns += 2; + } + + /* Lengths are expressed in bytes now; each insn is 4 bytes. */ + return n_insns * 4; +} + + +char * +output_and (operands) + rtx *operands; +{ + if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) != 0) + { + unsigned HOST_WIDE_INT mask = INTVAL (operands[2]); + int ls0, ls1, ms0, p, len; + + for (ls0 = 0; ls0 < 32; ls0++) + if ((mask & (1 << ls0)) == 0) + break; + + for (ls1 = ls0; ls1 < 32; ls1++) + if ((mask & (1 << ls1)) != 0) + break; + + for (ms0 = ls1; ms0 < 32; ms0++) + if ((mask & (1 << ms0)) == 0) + break; + + if (ms0 != 32) + abort(); + + if (ls1 == 32) + { + len = ls0; + + if (len == 0) + abort (); + + operands[2] = GEN_INT (len); + return "extru %1,31,%2,%0"; + } + else + { + /* We could use this `depi' for the case above as well, but `depi' + requires one more register file access than an `extru'. */ + + p = 31 - ls0; + len = ls1 - ls0; + + operands[2] = GEN_INT (p); + operands[3] = GEN_INT (len); + return "depi 0,%2,%3,%0"; + } + } + else + return "and %1,%2,%0"; +} + +char * +output_ior (operands) + rtx *operands; +{ + unsigned HOST_WIDE_INT mask = INTVAL (operands[2]); + int bs0, bs1, p, len; + + if (INTVAL (operands[2]) == 0) + return "copy %1,%0"; + + for (bs0 = 0; bs0 < 32; bs0++) + if ((mask & (1 << bs0)) != 0) + break; + + for (bs1 = bs0; bs1 < 32; bs1++) + if ((mask & (1 << bs1)) == 0) + break; + + if (bs1 != 32 && ((unsigned HOST_WIDE_INT) 1 << bs1) <= mask) + abort(); + + p = 31 - bs0; + len = bs1 - bs0; + + operands[2] = GEN_INT (p); + operands[3] = GEN_INT (len); + return "depi -1,%2,%3,%0"; +} + +/* Output an ascii string. */ +void +output_ascii (file, p, size) + FILE *file; + unsigned char *p; + int size; +{ + int i; + int chars_output; + unsigned char partial_output[16]; /* Max space 4 chars can occupy. */ + + /* The HP assembler can only take strings of 256 characters at one + time. This is a limitation on input line length, *not* the + length of the string. Sigh. Even worse, it seems that the + restriction is in number of input characters (see \xnn & + \whatever). So we have to do this very carefully. */ + + fputs ("\t.STRING \"", file); + + chars_output = 0; + for (i = 0; i < size; i += 4) + { + int co = 0; + int io = 0; + for (io = 0, co = 0; io < MIN (4, size - i); io++) + { + register unsigned int c = p[i + io]; + + if (c == '\"' || c == '\\') + partial_output[co++] = '\\'; + if (c >= ' ' && c < 0177) + partial_output[co++] = c; + else + { + unsigned int hexd; + partial_output[co++] = '\\'; + partial_output[co++] = 'x'; + hexd = c / 16 - 0 + '0'; + if (hexd > '9') + hexd -= '9' - 'a' + 1; + partial_output[co++] = hexd; + hexd = c % 16 - 0 + '0'; + if (hexd > '9') + hexd -= '9' - 'a' + 1; + partial_output[co++] = hexd; + } + } + if (chars_output + co > 243) + { + fputs ("\"\n\t.STRING \"", file); + chars_output = 0; + } + fwrite (partial_output, 1, co, file); + chars_output += co; + co = 0; + } + fputs ("\"\n", file); +} + +/* Try to rewrite floating point comparisons & branches to avoid + useless add,tr insns. + + CHECK_NOTES is nonzero if we should examine REG_DEAD notes + to see if FPCC is dead. CHECK_NOTES is nonzero for the + first attempt to remove useless add,tr insns. It is zero + for the second pass as reorg sometimes leaves bogus REG_DEAD + notes lying around. + + When CHECK_NOTES is zero we can only eliminate add,tr insns + when there's a 1:1 correspondence between fcmp and ftest/fbranch + instructions. */ +void +remove_useless_addtr_insns (insns, check_notes) + rtx insns; + int check_notes; +{ + rtx insn; + static int pass = 0; + + /* This is fairly cheap, so always run it when optimizing. */ + if (optimize > 0) + { + int fcmp_count = 0; + int fbranch_count = 0; + + /* Walk all the insns in this function looking for fcmp & fbranch + instructions. Keep track of how many of each we find. */ + insns = get_insns (); + for (insn = insns; insn; insn = next_insn (insn)) + { + rtx tmp; + + /* Ignore anything that isn't an INSN or a JUMP_INSN. */ + if (GET_CODE (insn) != INSN && GET_CODE (insn) != JUMP_INSN) + continue; + + tmp = PATTERN (insn); + + /* It must be a set. */ + if (GET_CODE (tmp) != SET) + continue; + + /* If the destination is CCFP, then we've found an fcmp insn. */ + tmp = SET_DEST (tmp); + if (GET_CODE (tmp) == REG && REGNO (tmp) == 0) + { + fcmp_count++; + continue; + } + + tmp = PATTERN (insn); + /* If this is an fbranch instruction, bump the fbranch counter. */ + if (GET_CODE (tmp) == SET + && SET_DEST (tmp) == pc_rtx + && GET_CODE (SET_SRC (tmp)) == IF_THEN_ELSE + && GET_CODE (XEXP (SET_SRC (tmp), 0)) == NE + && GET_CODE (XEXP (XEXP (SET_SRC (tmp), 0), 0)) == REG + && REGNO (XEXP (XEXP (SET_SRC (tmp), 0), 0)) == 0) + { + fbranch_count++; + continue; + } + } + + + /* Find all floating point compare + branch insns. If possible, + reverse the comparison & the branch to avoid add,tr insns. */ + for (insn = insns; insn; insn = next_insn (insn)) + { + rtx tmp, next; + + /* Ignore anything that isn't an INSN. */ + if (GET_CODE (insn) != INSN) + continue; + + tmp = PATTERN (insn); + + /* It must be a set. */ + if (GET_CODE (tmp) != SET) + continue; + + /* The destination must be CCFP, which is register zero. */ + tmp = SET_DEST (tmp); + if (GET_CODE (tmp) != REG || REGNO (tmp) != 0) + continue; + + /* INSN should be a set of CCFP. + + See if the result of this insn is used in a reversed FP + conditional branch. If so, reverse our condition and + the branch. Doing so avoids useless add,tr insns. */ + next = next_insn (insn); + while (next) + { + /* Jumps, calls and labels stop our search. */ + if (GET_CODE (next) == JUMP_INSN + || GET_CODE (next) == CALL_INSN + || GET_CODE (next) == CODE_LABEL) + break; + + /* As does another fcmp insn. */ + if (GET_CODE (next) == INSN + && GET_CODE (PATTERN (next)) == SET + && GET_CODE (SET_DEST (PATTERN (next))) == REG + && REGNO (SET_DEST (PATTERN (next))) == 0) + break; + + next = next_insn (next); + } + + /* Is NEXT_INSN a branch? */ + if (next + && GET_CODE (next) == JUMP_INSN) + { + rtx pattern = PATTERN (next); + + /* If it a reversed fp conditional branch (eg uses add,tr) + and CCFP dies, then reverse our conditional and the branch + to avoid the add,tr. */ + if (GET_CODE (pattern) == SET + && SET_DEST (pattern) == pc_rtx + && GET_CODE (SET_SRC (pattern)) == IF_THEN_ELSE + && GET_CODE (XEXP (SET_SRC (pattern), 0)) == NE + && GET_CODE (XEXP (XEXP (SET_SRC (pattern), 0), 0)) == REG + && REGNO (XEXP (XEXP (SET_SRC (pattern), 0), 0)) == 0 + && GET_CODE (XEXP (SET_SRC (pattern), 1)) == PC + && (fcmp_count == fbranch_count + || (check_notes + && find_regno_note (next, REG_DEAD, 0)))) + { + /* Reverse the branch. */ + tmp = XEXP (SET_SRC (pattern), 1); + XEXP (SET_SRC (pattern), 1) = XEXP (SET_SRC (pattern), 2); + XEXP (SET_SRC (pattern), 2) = tmp; + INSN_CODE (next) = -1; + + /* Reverse our condition. */ + tmp = PATTERN (insn); + PUT_CODE (XEXP (tmp, 1), + reverse_condition (GET_CODE (XEXP (tmp, 1)))); + } + } + } + } + + pass = !pass; + +} + +/* You may have trouble believing this, but this is the HP-PA stack + layout. Wow. + + Offset Contents + + Variable arguments (optional; any number may be allocated) + + SP-(4*(N+9)) arg word N + : : + SP-56 arg word 5 + SP-52 arg word 4 + + Fixed arguments (must be allocated; may remain unused) + + SP-48 arg word 3 + SP-44 arg word 2 + SP-40 arg word 1 + SP-36 arg word 0 + + Frame Marker + + SP-32 External Data Pointer (DP) + SP-28 External sr4 + SP-24 External/stub RP (RP') + SP-20 Current RP + SP-16 Static Link + SP-12 Clean up + SP-8 Calling Stub RP (RP'') + SP-4 Previous SP + + Top of Frame + + SP-0 Stack Pointer (points to next available address) + +*/ + +/* This function saves registers as follows. Registers marked with ' are + this function's registers (as opposed to the previous function's). + If a frame_pointer isn't needed, r4 is saved as a general register; + the space for the frame pointer is still allocated, though, to keep + things simple. + + + Top of Frame + + SP (FP') Previous FP + SP + 4 Alignment filler (sigh) + SP + 8 Space for locals reserved here. + . + . + . + SP + n All call saved register used. + . + . + . + SP + o All call saved fp registers used. + . + . + . + SP + p (SP') points to next available address. + +*/ + +/* Emit RTL to store REG at the memory location specified by BASE+DISP. + Handle case where DISP > 8k by using the add_high_const pattern. + + Note in DISP > 8k case, we will leave the high part of the address + in %r1. There is code in expand_hppa_{prologue,epilogue} that knows this.*/ +static void +store_reg (reg, disp, base) + int reg, disp, base; +{ + if (VAL_14_BITS_P (disp)) + { + emit_move_insn (gen_rtx_MEM (SImode, + gen_rtx_PLUS (SImode, + gen_rtx_REG (SImode, base), + GEN_INT (disp))), + gen_rtx_REG (SImode, reg)); + } + else + { + emit_insn (gen_add_high_const (gen_rtx_REG (SImode, 1), + gen_rtx_REG (SImode, base), + GEN_INT (disp))); + emit_move_insn (gen_rtx_MEM (SImode, + gen_rtx_LO_SUM (SImode, + gen_rtx_REG (SImode, 1), + GEN_INT (disp))), + gen_rtx_REG (SImode, reg)); + } +} + +/* Emit RTL to load REG from the memory location specified by BASE+DISP. + Handle case where DISP > 8k by using the add_high_const pattern. + + Note in DISP > 8k case, we will leave the high part of the address + in %r1. There is code in expand_hppa_{prologue,epilogue} that knows this.*/ +static void +load_reg (reg, disp, base) + int reg, disp, base; +{ + if (VAL_14_BITS_P (disp)) + { + emit_move_insn (gen_rtx_REG (SImode, reg), + gen_rtx_MEM (SImode, + gen_rtx_PLUS (SImode, + gen_rtx_REG (SImode, base), + GEN_INT (disp)))); + } + else + { + emit_insn (gen_add_high_const (gen_rtx_REG (SImode, 1), + gen_rtx_REG (SImode, base), + GEN_INT (disp))); + emit_move_insn (gen_rtx_REG (SImode, reg), + gen_rtx_MEM (SImode, + gen_rtx_LO_SUM (SImode, + gen_rtx_REG (SImode, 1), + GEN_INT (disp)))); + } +} + +/* Emit RTL to set REG to the value specified by BASE+DISP. + Handle case where DISP > 8k by using the add_high_const pattern. + + Note in DISP > 8k case, we will leave the high part of the address + in %r1. There is code in expand_hppa_{prologue,epilogue} that knows this.*/ +static void +set_reg_plus_d(reg, base, disp) + int reg, base, disp; +{ + if (VAL_14_BITS_P (disp)) + { + emit_move_insn (gen_rtx_REG (SImode, reg), + gen_rtx_PLUS (SImode, + gen_rtx_REG (SImode, base), + GEN_INT (disp))); + } + else + { + emit_insn (gen_add_high_const (gen_rtx_REG (SImode, 1), + gen_rtx_REG (SImode, base), + GEN_INT (disp))); + emit_move_insn (gen_rtx_REG (SImode, reg), + gen_rtx_LO_SUM (SImode, + gen_rtx_REG (SImode, 1), + GEN_INT (disp))); + } +} + +/* Global variables set by FUNCTION_PROLOGUE. */ +/* Size of frame. Need to know this to emit return insns from + leaf procedures. */ +static int actual_fsize; +static int local_fsize, save_fregs; + +int +compute_frame_size (size, fregs_live) + int size; + int *fregs_live; +{ + extern int current_function_outgoing_args_size; + int i, fsize; + + /* 8 is space for frame pointer + filler. If any frame is allocated + we need to add this in because of STARTING_FRAME_OFFSET. */ + fsize = size + (size || frame_pointer_needed ? 8 : 0); + + /* We must leave enough space for all the callee saved registers + from 3 .. highest used callee save register since we don't + know if we're going to have an inline or out of line prologue + and epilogue. */ + for (i = 18; i >= 3; i--) + if (regs_ever_live[i]) + { + fsize += 4 * (i - 2); + break; + } + + /* Round the stack. */ + fsize = (fsize + 7) & ~7; + + /* We must leave enough space for all the callee saved registers + from 3 .. highest used callee save register since we don't + know if we're going to have an inline or out of line prologue + and epilogue. */ + for (i = 66; i >= 48; i -= 2) + if (regs_ever_live[i] || regs_ever_live[i + 1]) + { + if (fregs_live) + *fregs_live = 1; + + fsize += 4 * (i - 46); + break; + } + + fsize += current_function_outgoing_args_size; + if (! leaf_function_p () || fsize) + fsize += 32; + return (fsize + 63) & ~63; +} + +rtx hp_profile_label_rtx; +static char hp_profile_label_name[8]; +void +output_function_prologue (file, size) + FILE *file; + int size ATTRIBUTE_UNUSED; +{ + /* The function's label and associated .PROC must never be + separated and must be output *after* any profiling declarations + to avoid changing spaces/subspaces within a procedure. */ + ASM_OUTPUT_LABEL (file, XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0)); + fputs ("\t.PROC\n", file); + + /* hppa_expand_prologue does the dirty work now. We just need + to output the assembler directives which denote the start + of a function. */ + fprintf (file, "\t.CALLINFO FRAME=%d", actual_fsize); + if (regs_ever_live[2] || profile_flag) + fputs (",CALLS,SAVE_RP", file); + else + fputs (",NO_CALLS", file); + + if (frame_pointer_needed) + fputs (",SAVE_SP", file); + + /* Pass on information about the number of callee register saves + performed in the prologue. + + The compiler is supposed to pass the highest register number + saved, the assembler then has to adjust that number before + entering it into the unwind descriptor (to account for any + caller saved registers with lower register numbers than the + first callee saved register). */ + if (gr_saved) + fprintf (file, ",ENTRY_GR=%d", gr_saved + 2); + + if (fr_saved) + fprintf (file, ",ENTRY_FR=%d", fr_saved + 11); + + fputs ("\n\t.ENTRY\n", file); + + /* Horrid hack. emit_function_prologue will modify this RTL in + place to get the expected results. */ + if (profile_flag) + ASM_GENERATE_INTERNAL_LABEL (hp_profile_label_name, "LP", + hp_profile_labelno); + + /* If we're using GAS and not using the portable runtime model, then + we don't need to accumulate the total number of code bytes. */ + if (TARGET_GAS && ! TARGET_PORTABLE_RUNTIME) + total_code_bytes = 0; + else if (insn_addresses) + { + unsigned int old_total = total_code_bytes; + + total_code_bytes += insn_addresses[INSN_UID (get_last_insn())]; + total_code_bytes += FUNCTION_BOUNDARY / BITS_PER_UNIT; + + /* Be prepared to handle overflows. */ + total_code_bytes = old_total > total_code_bytes ? -1 : total_code_bytes; + } + else + total_code_bytes = -1; + + remove_useless_addtr_insns (get_insns (), 0); + + /* Restore INSN_CODEs for insn which use unscaled indexed addresses. */ + restore_unscaled_index_insn_codes (get_insns ()); +} + +void +hppa_expand_prologue() +{ + extern char call_used_regs[]; + int size = get_frame_size (); + int merge_sp_adjust_with_store = 0; + int i, offset; + rtx tmpreg, size_rtx; + + gr_saved = 0; + fr_saved = 0; + save_fregs = 0; + local_fsize = size + (size || frame_pointer_needed ? 8 : 0); + actual_fsize = compute_frame_size (size, &save_fregs); + + /* Compute a few things we will use often. */ + tmpreg = gen_rtx_REG (SImode, 1); + size_rtx = GEN_INT (actual_fsize); + + /* Handle out of line prologues and epilogues. */ + if (TARGET_SPACE) + { + rtx operands[2]; + int saves = 0; + int outline_insn_count = 0; + int inline_insn_count = 0; + + /* Count the number of insns for the inline and out of line + variants so we can choose one appropriately. + + No need to screw with counting actual_fsize operations -- they're + done for both inline and out of line prologues. */ + if (regs_ever_live[2]) + inline_insn_count += 1; + + if (! cint_ok_for_move (local_fsize)) + outline_insn_count += 2; + else + outline_insn_count += 1; + + /* Put the register save info into %r22. */ + for (i = 18; i >= 3; i--) + if (regs_ever_live[i] && ! call_used_regs[i]) + { + /* -1 because the stack adjustment is normally done in + the same insn as a register save. */ + inline_insn_count += (i - 2) - 1; + saves = i; + break; + } + + for (i = 66; i >= 48; i -= 2) + if (regs_ever_live[i] || regs_ever_live[i + 1]) + { + /* +1 needed as we load %r1 with the start of the freg + save area. */ + inline_insn_count += (i/2 - 23) + 1; + saves |= ((i/2 - 12 ) << 16); + break; + } + + if (frame_pointer_needed) + inline_insn_count += 3; + + if (! cint_ok_for_move (saves)) + outline_insn_count += 2; + else + outline_insn_count += 1; + + if (TARGET_PORTABLE_RUNTIME) + outline_insn_count += 2; + else + outline_insn_count += 1; + + /* If there's a lot of insns in the prologue, then do it as + an out-of-line sequence. */ + if (inline_insn_count > outline_insn_count) + { + /* Put the local_fisze into %r19. */ + operands[0] = gen_rtx_REG (SImode, 19); + operands[1] = GEN_INT (local_fsize); + emit_move_insn (operands[0], operands[1]); + + /* Put the stack size into %r21. */ + operands[0] = gen_rtx_REG (SImode, 21); + operands[1] = size_rtx; + emit_move_insn (operands[0], operands[1]); + + operands[0] = gen_rtx_REG (SImode, 22); + operands[1] = GEN_INT (saves); + emit_move_insn (operands[0], operands[1]); + + /* Now call the out-of-line prologue. */ + emit_insn (gen_outline_prologue_call ()); + emit_insn (gen_blockage ()); + + /* Note that we're using an out-of-line prologue. */ + out_of_line_prologue_epilogue = 1; + return; + } + } + + out_of_line_prologue_epilogue = 0; + + /* Save RP first. The calling conventions manual states RP will + always be stored into the caller's frame at sp-20. */ + if (regs_ever_live[2] || profile_flag) + store_reg (2, -20, STACK_POINTER_REGNUM); + + /* Allocate the local frame and set up the frame pointer if needed. */ + if (actual_fsize) + { + if (frame_pointer_needed) + { + /* Copy the old frame pointer temporarily into %r1. Set up the + new stack pointer, then store away the saved old frame pointer + into the stack at sp+actual_fsize and at the same time update + the stack pointer by actual_fsize bytes. Two versions, first + handles small (<8k) frames. The second handles large (>8k) + frames. */ + emit_move_insn (tmpreg, frame_pointer_rtx); + emit_move_insn (frame_pointer_rtx, stack_pointer_rtx); + if (VAL_14_BITS_P (actual_fsize)) + emit_insn (gen_post_stwm (stack_pointer_rtx, tmpreg, size_rtx)); + else + { + /* It is incorrect to store the saved frame pointer at *sp, + then increment sp (writes beyond the current stack boundary). + + So instead use stwm to store at *sp and post-increment the + stack pointer as an atomic operation. Then increment sp to + finish allocating the new frame. */ + emit_insn (gen_post_stwm (stack_pointer_rtx, tmpreg, GEN_INT (64))); + set_reg_plus_d (STACK_POINTER_REGNUM, + STACK_POINTER_REGNUM, + actual_fsize - 64); + } + } + /* no frame pointer needed. */ + else + { + /* In some cases we can perform the first callee register save + and allocating the stack frame at the same time. If so, just + make a note of it and defer allocating the frame until saving + the callee registers. */ + if (VAL_14_BITS_P (-actual_fsize) + && local_fsize == 0 + && ! profile_flag + && ! flag_pic) + merge_sp_adjust_with_store = 1; + /* Can not optimize. Adjust the stack frame by actual_fsize bytes. */ + else if (actual_fsize != 0) + set_reg_plus_d (STACK_POINTER_REGNUM, + STACK_POINTER_REGNUM, + actual_fsize); + } + } + + /* The hppa calling conventions say that %r19, the pic offset + register, is saved at sp - 32 (in this function's frame) when + generating PIC code. FIXME: What is the correct thing to do + for functions which make no calls and allocate no frame? Do + we need to allocate a frame, or can we just omit the save? For + now we'll just omit the save. */ + if (actual_fsize != 0 && flag_pic) + store_reg (PIC_OFFSET_TABLE_REGNUM, -32, STACK_POINTER_REGNUM); + + /* Profiling code. + + Instead of taking one argument, the counter label, as most normal + mcounts do, _mcount appears to behave differently on the HPPA. It + takes the return address of the caller, the address of this routine, + and the address of the label. Also, it isn't magic, so + argument registers have to be preserved. */ + if (profile_flag) + { + int pc_offset, i, arg_offset, basereg, offsetadj; + + pc_offset = 4 + (frame_pointer_needed + ? (VAL_14_BITS_P (actual_fsize) ? 12 : 20) + : (VAL_14_BITS_P (actual_fsize) ? 4 : 8)); + + /* When the function has a frame pointer, use it as the base + register for saving/restore registers. Else use the stack + pointer. Adjust the offset according to the frame size if + this function does not have a frame pointer. */ + + basereg = frame_pointer_needed ? FRAME_POINTER_REGNUM + : STACK_POINTER_REGNUM; + offsetadj = frame_pointer_needed ? 0 : actual_fsize; + + /* Horrid hack. emit_function_prologue will modify this RTL in + place to get the expected results. sprintf here is just to + put something in the name. */ + sprintf(hp_profile_label_name, "LP$%04d", -1); + hp_profile_label_rtx = gen_rtx_SYMBOL_REF (SImode, hp_profile_label_name); + if (current_function_returns_struct) + store_reg (STRUCT_VALUE_REGNUM, - 12 - offsetadj, basereg); + + for (i = 26, arg_offset = -36 - offsetadj; i >= 23; i--, arg_offset -= 4) + if (regs_ever_live [i]) + { + store_reg (i, arg_offset, basereg); + /* Deal with arg_offset not fitting in 14 bits. */ + pc_offset += VAL_14_BITS_P (arg_offset) ? 4 : 8; + } + + emit_move_insn (gen_rtx_REG (SImode, 26), gen_rtx_REG (SImode, 2)); + emit_move_insn (tmpreg, gen_rtx_HIGH (SImode, hp_profile_label_rtx)); + emit_move_insn (gen_rtx_REG (SImode, 24), + gen_rtx_LO_SUM (SImode, tmpreg, hp_profile_label_rtx)); + /* %r25 is set from within the output pattern. */ + emit_insn (gen_call_profiler (GEN_INT (- pc_offset - 20))); + + /* Restore argument registers. */ + for (i = 26, arg_offset = -36 - offsetadj; i >= 23; i--, arg_offset -= 4) + if (regs_ever_live [i]) + load_reg (i, arg_offset, basereg); + + if (current_function_returns_struct) + load_reg (STRUCT_VALUE_REGNUM, -12 - offsetadj, basereg); + + } + + /* Normal register save. + + Do not save the frame pointer in the frame_pointer_needed case. It + was done earlier. */ + if (frame_pointer_needed) + { + for (i = 18, offset = local_fsize; i >= 4; i--) + if (regs_ever_live[i] && ! call_used_regs[i]) + { + store_reg (i, offset, FRAME_POINTER_REGNUM); + offset += 4; + gr_saved++; + } + /* Account for %r3 which is saved in a special place. */ + gr_saved++; + } + /* No frame pointer needed. */ + else + { + for (i = 18, offset = local_fsize - actual_fsize; i >= 3; i--) + if (regs_ever_live[i] && ! call_used_regs[i]) + { + /* If merge_sp_adjust_with_store is nonzero, then we can + optimize the first GR save. */ + if (merge_sp_adjust_with_store) + { + merge_sp_adjust_with_store = 0; + emit_insn (gen_post_stwm (stack_pointer_rtx, + gen_rtx_REG (SImode, i), + GEN_INT (-offset))); + } + else + store_reg (i, offset, STACK_POINTER_REGNUM); + offset += 4; + gr_saved++; + } + + /* If we wanted to merge the SP adjustment with a GR save, but we never + did any GR saves, then just emit the adjustment here. */ + if (merge_sp_adjust_with_store) + set_reg_plus_d (STACK_POINTER_REGNUM, + STACK_POINTER_REGNUM, + actual_fsize); + } + + /* Align pointer properly (doubleword boundary). */ + offset = (offset + 7) & ~7; + + /* Floating point register store. */ + if (save_fregs) + { + /* First get the frame or stack pointer to the start of the FP register + save area. */ + if (frame_pointer_needed) + set_reg_plus_d (1, FRAME_POINTER_REGNUM, offset); + else + set_reg_plus_d (1, STACK_POINTER_REGNUM, offset); + + /* Now actually save the FP registers. */ + for (i = 66; i >= 48; i -= 2) + { + if (regs_ever_live[i] || regs_ever_live[i + 1]) + { + emit_move_insn (gen_rtx_MEM (DFmode, + gen_rtx_POST_INC (DFmode, tmpreg)), + gen_rtx_REG (DFmode, i)); + fr_saved++; + } + } + } + + /* When generating PIC code it is necessary to save/restore the + PIC register around each function call. We used to do this + in the call patterns themselves, but that implementation + made incorrect assumptions about using global variables to hold + per-function rtl code generated in the backend. + + So instead, we copy the PIC register into a reserved callee saved + register in the prologue. Then after each call we reload the PIC + register from the callee saved register. We also reload the PIC + register from the callee saved register in the epilogue ensure the + PIC register is valid at function exit. + + This may (depending on the exact characteristics of the function) + even be more efficient. + + Avoid this if the callee saved register wasn't used (these are + leaf functions). */ + if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM_SAVED]) + emit_move_insn (gen_rtx_REG (SImode, PIC_OFFSET_TABLE_REGNUM_SAVED), + gen_rtx_REG (SImode, PIC_OFFSET_TABLE_REGNUM)); +} + + +void +output_function_epilogue (file, size) + FILE *file; + int size ATTRIBUTE_UNUSED; +{ + rtx insn = get_last_insn (); + + /* hppa_expand_epilogue does the dirty work now. We just need + to output the assembler directives which denote the end + of a function. + + To make debuggers happy, emit a nop if the epilogue was completely + eliminated due to a volatile call as the last insn in the + current function. That way the return address (in %r2) will + always point to a valid instruction in the current function. */ + + /* Get the last real insn. */ + if (GET_CODE (insn) == NOTE) + insn = prev_real_insn (insn); + + /* If it is a sequence, then look inside. */ + if (insn && GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SEQUENCE) + insn = XVECEXP (PATTERN (insn), 0, 0); + + /* If insn is a CALL_INSN, then it must be a call to a volatile + function (otherwise there would be epilogue insns). */ + if (insn && GET_CODE (insn) == CALL_INSN) + fputs ("\tnop\n", file); + + fputs ("\t.EXIT\n\t.PROCEND\n", file); + + /* Free up stuff we don't need anymore. */ + if (unscaled_index_insn_codes) + free (unscaled_index_insn_codes); + max_unscaled_index_insn_codes_uid = 0; +} + +void +hppa_expand_epilogue () +{ + rtx tmpreg; + int offset,i; + int merge_sp_adjust_with_load = 0; + + /* Handle out of line prologues and epilogues. */ + if (TARGET_SPACE && out_of_line_prologue_epilogue) + { + int saves = 0; + rtx operands[2]; + + /* Put the register save info into %r22. */ + for (i = 18; i >= 3; i--) + if (regs_ever_live[i] && ! call_used_regs[i]) + { + saves = i; + break; + } + + for (i = 66; i >= 48; i -= 2) + if (regs_ever_live[i] || regs_ever_live[i + 1]) + { + saves |= ((i/2 - 12 ) << 16); + break; + } + + emit_insn (gen_blockage ()); + + /* Put the local_fisze into %r19. */ + operands[0] = gen_rtx_REG (SImode, 19); + operands[1] = GEN_INT (local_fsize); + emit_move_insn (operands[0], operands[1]); + + /* Put the stack size into %r21. */ + operands[0] = gen_rtx_REG (SImode, 21); + operands[1] = GEN_INT (actual_fsize); + emit_move_insn (operands[0], operands[1]); + + operands[0] = gen_rtx_REG (SImode, 22); + operands[1] = GEN_INT (saves); + emit_move_insn (operands[0], operands[1]); + + /* Now call the out-of-line epilogue. */ + emit_insn (gen_outline_epilogue_call ()); + return; + } + + /* We will use this often. */ + tmpreg = gen_rtx_REG (SImode, 1); + + /* Try to restore RP early to avoid load/use interlocks when + RP gets used in the return (bv) instruction. This appears to still + be necessary even when we schedule the prologue and epilogue. */ + if (frame_pointer_needed + && (regs_ever_live [2] || profile_flag)) + load_reg (2, -20, FRAME_POINTER_REGNUM); + + /* No frame pointer, and stack is smaller than 8k. */ + else if (! frame_pointer_needed + && VAL_14_BITS_P (actual_fsize + 20) + && (regs_ever_live[2] || profile_flag)) + load_reg (2, - (actual_fsize + 20), STACK_POINTER_REGNUM); + + /* General register restores. */ + if (frame_pointer_needed) + { + for (i = 18, offset = local_fsize; i >= 4; i--) + if (regs_ever_live[i] && ! call_used_regs[i]) + { + load_reg (i, offset, FRAME_POINTER_REGNUM); + offset += 4; + } + } + else + { + for (i = 18, offset = local_fsize - actual_fsize; i >= 3; i--) + { + if (regs_ever_live[i] && ! call_used_regs[i]) + { + /* Only for the first load. + merge_sp_adjust_with_load holds the register load + with which we will merge the sp adjustment. */ + if (VAL_14_BITS_P (actual_fsize + 20) + && local_fsize == 0 + && ! merge_sp_adjust_with_load) + merge_sp_adjust_with_load = i; + else + load_reg (i, offset, STACK_POINTER_REGNUM); + offset += 4; + } + } + } + + /* Align pointer properly (doubleword boundary). */ + offset = (offset + 7) & ~7; + + /* FP register restores. */ + if (save_fregs) + { + /* Adjust the register to index off of. */ + if (frame_pointer_needed) + set_reg_plus_d (1, FRAME_POINTER_REGNUM, offset); + else + set_reg_plus_d (1, STACK_POINTER_REGNUM, offset); + + /* Actually do the restores now. */ + for (i = 66; i >= 48; i -= 2) + { + if (regs_ever_live[i] || regs_ever_live[i + 1]) + { + emit_move_insn (gen_rtx_REG (DFmode, i), + gen_rtx_MEM (DFmode, + gen_rtx_POST_INC (DFmode, tmpreg))); + } + } + } + + /* Emit a blockage insn here to keep these insns from being moved to + an earlier spot in the epilogue, or into the main instruction stream. + + This is necessary as we must not cut the stack back before all the + restores are finished. */ + emit_insn (gen_blockage ()); + /* No frame pointer, but we have a stack greater than 8k. We restore + %r2 very late in this case. (All other cases are restored as early + as possible.) */ + if (! frame_pointer_needed + && ! VAL_14_BITS_P (actual_fsize + 20) + && (regs_ever_live[2] || profile_flag)) + { + set_reg_plus_d (STACK_POINTER_REGNUM, + STACK_POINTER_REGNUM, + - actual_fsize); + + /* This used to try and be clever by not depending on the value in + %r30 and instead use the value held in %r1 (so that the 2nd insn + which sets %r30 could be put in the delay slot of the return insn). + + That won't work since if the stack is exactly 8k set_reg_plus_d + doesn't set %r1, just %r30. */ + load_reg (2, - 20, STACK_POINTER_REGNUM); + } + + /* Reset stack pointer (and possibly frame pointer). The stack + pointer is initially set to fp + 64 to avoid a race condition. */ + else if (frame_pointer_needed) + { + set_reg_plus_d (STACK_POINTER_REGNUM, FRAME_POINTER_REGNUM, 64); + emit_insn (gen_pre_ldwm (frame_pointer_rtx, + stack_pointer_rtx, + GEN_INT (-64))); + } + /* If we were deferring a callee register restore, do it now. */ + else if (! frame_pointer_needed && merge_sp_adjust_with_load) + emit_insn (gen_pre_ldwm (gen_rtx_REG (SImode, merge_sp_adjust_with_load), + stack_pointer_rtx, + GEN_INT (- actual_fsize))); + else if (actual_fsize != 0) + set_reg_plus_d (STACK_POINTER_REGNUM, + STACK_POINTER_REGNUM, + - actual_fsize); +} + +/* Fetch the return address for the frame COUNT steps up from + the current frame, after the prologue. FRAMEADDR is the + frame pointer of the COUNT frame. + + We want to ignore any export stub remnants here. + + The value returned is used in two different ways: + + 1. To find a function's caller. + + 2. To change the return address for a function. + + This function handles most instances of case 1; however, it will + fail if there are two levels of stubs to execute on the return + path. The only way I believe that can happen is if the return value + needs a parameter relocation, which never happens for C code. + + This function handles most instances of case 2; however, it will + fail if we did not originally have stub code on the return path + but will need code on the new return path. This can happen if + the caller & callee are both in the main program, but the new + return location is in a shared library. + + To handle this correctly we need to set the return pointer at + frame-20 to point to a return stub frame-24 to point to the + location we wish to return to. */ + +rtx +return_addr_rtx (count, frameaddr) + int count ATTRIBUTE_UNUSED; + rtx frameaddr; +{ + rtx label; + rtx saved_rp; + rtx ins; + + saved_rp = gen_reg_rtx (Pmode); + + /* First, we start off with the normal return address pointer from + -20[frameaddr]. */ + + emit_move_insn (saved_rp, plus_constant (frameaddr, -5 * UNITS_PER_WORD)); + + /* Get pointer to the instruction stream. We have to mask out the + privilege level from the two low order bits of the return address + pointer here so that ins will point to the start of the first + instruction that would have been executed if we returned. */ + ins = copy_to_reg (gen_rtx_AND (Pmode, + copy_to_reg (gen_rtx_MEM (Pmode, saved_rp)), + MASK_RETURN_ADDR)); + label = gen_label_rtx (); + + /* Check the instruction stream at the normal return address for the + export stub: + + 0x4bc23fd1 | stub+8: ldw -18(sr0,sp),rp + 0x004010a1 | stub+12: ldsid (sr0,rp),r1 + 0x00011820 | stub+16: mtsp r1,sr0 + 0xe0400002 | stub+20: be,n 0(sr0,rp) + + If it is an export stub, than our return address is really in + -24[frameaddr]. */ + + emit_cmp_insn (gen_rtx_MEM (SImode, ins), + GEN_INT (0x4bc23fd1), + NE, NULL_RTX, SImode, 1, 0); + emit_jump_insn (gen_bne (label)); + + emit_cmp_insn (gen_rtx_MEM (SImode, plus_constant (ins, 4)), + GEN_INT (0x004010a1), + NE, NULL_RTX, SImode, 1, 0); + emit_jump_insn (gen_bne (label)); + + emit_cmp_insn (gen_rtx_MEM (SImode, plus_constant (ins, 8)), + GEN_INT (0x00011820), + NE, NULL_RTX, SImode, 1, 0); + emit_jump_insn (gen_bne (label)); + + emit_cmp_insn (gen_rtx_MEM (SImode, plus_constant (ins, 12)), + GEN_INT (0xe0400002), + NE, NULL_RTX, SImode, 1, 0); + + /* If there is no export stub then just use our initial guess of + -20[frameaddr]. */ + + emit_jump_insn (gen_bne (label)); + + /* Here we know that our return address pointer points to an export + stub. We don't want to return the address of the export stub, + but rather the return address that leads back into user code. + That return address is stored at -24[frameaddr]. */ + + emit_move_insn (saved_rp, plus_constant (frameaddr, -6 * UNITS_PER_WORD)); + + emit_label (label); + return gen_rtx_MEM (Pmode, memory_address (Pmode, saved_rp)); +} + +/* This is only valid once reload has completed because it depends on + knowing exactly how much (if any) frame there is and... + + It's only valid if there is no frame marker to de-allocate and... + + It's only valid if %r2 hasn't been saved into the caller's frame + (we're not profiling and %r2 isn't live anywhere). */ +int +hppa_can_use_return_insn_p () +{ + return (reload_completed + && (compute_frame_size (get_frame_size (), 0) ? 0 : 1) + && ! profile_flag + && ! regs_ever_live[2] + && ! frame_pointer_needed); +} + +void +emit_bcond_fp (code, operand0) + enum rtx_code code; + rtx operand0; +{ + emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, + gen_rtx_IF_THEN_ELSE (VOIDmode, + gen_rtx_fmt_ee (code, + VOIDmode, + gen_rtx_REG (CCFPmode, 0), + const0_rtx), + gen_rtx_LABEL_REF (VOIDmode, operand0), + pc_rtx))); + +} + +rtx +gen_cmp_fp (code, operand0, operand1) + enum rtx_code code; + rtx operand0, operand1; +{ + return gen_rtx_SET (VOIDmode, gen_rtx_REG (CCFPmode, 0), + gen_rtx_fmt_ee (code, CCFPmode, operand0, operand1)); +} + +/* Adjust the cost of a scheduling dependency. Return the new cost of + a dependency LINK or INSN on DEP_INSN. COST is the current cost. */ + +int +pa_adjust_cost (insn, link, dep_insn, cost) + rtx insn; + rtx link; + rtx dep_insn; + int cost; +{ + enum attr_type attr_type; + + if (! recog_memoized (insn)) + return 0; + + /* CYGNUS LOCAL PA8000/law */ + /* No cost adjustments are needed for the PA8000 */ + if (pa_cpu == PROCESSOR_8000) + return 0; + /* END CYGNUS LOCAL */ + + attr_type = get_attr_type (insn); + + if (REG_NOTE_KIND (link) == 0) + { + /* Data dependency; DEP_INSN writes a register that INSN reads some + cycles later. */ + + if (attr_type == TYPE_FPSTORE) + { + rtx pat = PATTERN (insn); + rtx dep_pat = PATTERN (dep_insn); + if (GET_CODE (pat) == PARALLEL) + { + /* This happens for the fstXs,mb patterns. */ + pat = XVECEXP (pat, 0, 0); + } + if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET) + /* If this happens, we have to extend this to schedule + optimally. Return 0 for now. */ + return 0; + + if (rtx_equal_p (SET_DEST (dep_pat), SET_SRC (pat))) + { + if (! recog_memoized (dep_insn)) + return 0; + /* DEP_INSN is writing its result to the register + being stored in the fpstore INSN. */ + switch (get_attr_type (dep_insn)) + { + case TYPE_FPLOAD: + /* This cost 3 cycles, not 2 as the md says for the + 700 and 7100. */ + return cost + 1; + + case TYPE_FPALU: + case TYPE_FPMULSGL: + case TYPE_FPMULDBL: + case TYPE_FPDIVSGL: + case TYPE_FPDIVDBL: + case TYPE_FPSQRTSGL: + case TYPE_FPSQRTDBL: + /* In these important cases, we save one cycle compared to + when flop instruction feed each other. */ + return cost - 1; + + default: + return cost; + } + } + } + + /* For other data dependencies, the default cost specified in the + md is correct. */ + return cost; + } + else if (REG_NOTE_KIND (link) == REG_DEP_ANTI) + { + /* Anti dependency; DEP_INSN reads a register that INSN writes some + cycles later. */ + + if (attr_type == TYPE_FPLOAD) + { + rtx pat = PATTERN (insn); + rtx dep_pat = PATTERN (dep_insn); + if (GET_CODE (pat) == PARALLEL) + { + /* This happens for the fldXs,mb patterns. */ + pat = XVECEXP (pat, 0, 0); + } + if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET) + /* If this happens, we have to extend this to schedule + optimally. Return 0 for now. */ + return 0; + + if (reg_mentioned_p (SET_DEST (pat), SET_SRC (dep_pat))) + { + if (! recog_memoized (dep_insn)) + return 0; + switch (get_attr_type (dep_insn)) + { + case TYPE_FPALU: + case TYPE_FPMULSGL: + case TYPE_FPMULDBL: + case TYPE_FPDIVSGL: + case TYPE_FPDIVDBL: + case TYPE_FPSQRTSGL: + case TYPE_FPSQRTDBL: + /* A fpload can't be issued until one cycle before a + preceding arithmetic operation has finished if + the target of the fpload is any of the sources + (or destination) of the arithmetic operation. */ + return cost - 1; + + default: + return 0; + } + } + } + else if (attr_type == TYPE_FPALU) + { + rtx pat = PATTERN (insn); + rtx dep_pat = PATTERN (dep_insn); + if (GET_CODE (pat) == PARALLEL) + { + /* This happens for the fldXs,mb patterns. */ + pat = XVECEXP (pat, 0, 0); + } + if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET) + /* If this happens, we have to extend this to schedule + optimally. Return 0 for now. */ + return 0; + + if (reg_mentioned_p (SET_DEST (pat), SET_SRC (dep_pat))) + { + if (! recog_memoized (dep_insn)) + return 0; + switch (get_attr_type (dep_insn)) + { + case TYPE_FPDIVSGL: + case TYPE_FPDIVDBL: + case TYPE_FPSQRTSGL: + case TYPE_FPSQRTDBL: + /* An ALU flop can't be issued until two cycles before a + preceding divide or sqrt operation has finished if + the target of the ALU flop is any of the sources + (or destination) of the divide or sqrt operation. */ + return cost - 2; + + default: + return 0; + } + } + } + + /* For other anti dependencies, the cost is 0. */ + return 0; + } + else if (REG_NOTE_KIND (link) == REG_DEP_OUTPUT) + { + /* Output dependency; DEP_INSN writes a register that INSN writes some + cycles later. */ + if (attr_type == TYPE_FPLOAD) + { + rtx pat = PATTERN (insn); + rtx dep_pat = PATTERN (dep_insn); + if (GET_CODE (pat) == PARALLEL) + { + /* This happens for the fldXs,mb patterns. */ + pat = XVECEXP (pat, 0, 0); + } + if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET) + /* If this happens, we have to extend this to schedule + optimally. Return 0 for now. */ + return 0; + + if (reg_mentioned_p (SET_DEST (pat), SET_DEST (dep_pat))) + { + if (! recog_memoized (dep_insn)) + return 0; + switch (get_attr_type (dep_insn)) + { + case TYPE_FPALU: + case TYPE_FPMULSGL: + case TYPE_FPMULDBL: + case TYPE_FPDIVSGL: + case TYPE_FPDIVDBL: + case TYPE_FPSQRTSGL: + case TYPE_FPSQRTDBL: + /* A fpload can't be issued until one cycle before a + preceding arithmetic operation has finished if + the target of the fpload is the destination of the + arithmetic operation. */ + return cost - 1; + + default: + return 0; + } + } + } + else if (attr_type == TYPE_FPALU) + { + rtx pat = PATTERN (insn); + rtx dep_pat = PATTERN (dep_insn); + if (GET_CODE (pat) == PARALLEL) + { + /* This happens for the fldXs,mb patterns. */ + pat = XVECEXP (pat, 0, 0); + } + if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET) + /* If this happens, we have to extend this to schedule + optimally. Return 0 for now. */ + return 0; + + if (reg_mentioned_p (SET_DEST (pat), SET_DEST (dep_pat))) + { + if (! recog_memoized (dep_insn)) + return 0; + switch (get_attr_type (dep_insn)) + { + case TYPE_FPDIVSGL: + case TYPE_FPDIVDBL: + case TYPE_FPSQRTSGL: + case TYPE_FPSQRTDBL: + /* An ALU flop can't be issued until two cycles before a + preceding divide or sqrt operation has finished if + the target of the ALU flop is also the target of + the divide or sqrt operation. */ + return cost - 2; + + default: + return 0; + } + } + } + + /* For other output dependencies, the cost is 0. */ + return 0; + } + else + abort (); +} + +/* Return any length adjustment needed by INSN which already has its length + computed as LENGTH. Return zero if no adjustment is necessary. + + For the PA: function calls, millicode calls, and backwards short + conditional branches with unfilled delay slots need an adjustment by +1 + (to account for the NOP which will be inserted into the instruction stream). + + Also compute the length of an inline block move here as it is too + complicated to express as a length attribute in pa.md. */ +int +pa_adjust_insn_length (insn, length) + rtx insn; + int length; +{ + rtx pat = PATTERN (insn); + + /* Call insns which are *not* indirect and have unfilled delay slots. */ + if (GET_CODE (insn) == CALL_INSN) + { + + if (GET_CODE (XVECEXP (pat, 0, 0)) == CALL + && GET_CODE (XEXP (XEXP (XVECEXP (pat, 0, 0), 0), 0)) == SYMBOL_REF) + return 4; + else if (GET_CODE (XVECEXP (pat, 0, 0)) == SET + && GET_CODE (XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0), 0)) + == SYMBOL_REF) + return 4; + else + return 0; + } + /* Jumps inside switch tables which have unfilled delay slots + also need adjustment. */ + else if (GET_CODE (insn) == JUMP_INSN + && simplejump_p (insn) + && GET_MODE (insn) == SImode) + return 4; + /* Millicode insn with an unfilled delay slot. */ + else if (GET_CODE (insn) == INSN + && GET_CODE (pat) != SEQUENCE + && GET_CODE (pat) != USE + && GET_CODE (pat) != CLOBBER + && get_attr_type (insn) == TYPE_MILLI) + return 4; + /* Block move pattern. */ + else if (GET_CODE (insn) == INSN + && GET_CODE (pat) == PARALLEL + && GET_CODE (XEXP (XVECEXP (pat, 0, 0), 0)) == MEM + && GET_CODE (XEXP (XVECEXP (pat, 0, 0), 1)) == MEM + && GET_MODE (XEXP (XVECEXP (pat, 0, 0), 0)) == BLKmode + && GET_MODE (XEXP (XVECEXP (pat, 0, 0), 1)) == BLKmode) + return compute_movstrsi_length (insn) - 4; + /* Conditional branch with an unfilled delay slot. */ + else if (GET_CODE (insn) == JUMP_INSN && ! simplejump_p (insn)) + { + /* Adjust a short backwards conditional with an unfilled delay slot. */ + if (GET_CODE (pat) == SET + && length == 4 + && ! forward_branch_p (insn)) + return 4; + else if (GET_CODE (pat) == PARALLEL + && get_attr_type (insn) == TYPE_PARALLEL_BRANCH + && length == 4) + return 4; + /* Adjust dbra insn with short backwards conditional branch with + unfilled delay slot -- only for case where counter is in a + general register register. */ + else if (GET_CODE (pat) == PARALLEL + && GET_CODE (XVECEXP (pat, 0, 1)) == SET + && GET_CODE (XEXP (XVECEXP (pat, 0, 1), 0)) == REG + && ! FP_REG_P (XEXP (XVECEXP (pat, 0, 1), 0)) + && length == 4 + && ! forward_branch_p (insn)) + return 4; + else + return 0; + } + return 0; +} + +/* Print operand X (an rtx) in assembler syntax to file FILE. + CODE is a letter or dot (`z' in `%z0') or 0 if no letter was specified. + For `%' followed by punctuation, CODE is the punctuation and X is null. */ + +void +print_operand (file, x, code) + FILE *file; + rtx x; + int code; +{ + switch (code) + { + case '#': + /* Output a 'nop' if there's nothing for the delay slot. */ + if (dbr_sequence_length () == 0) + fputs ("\n\tnop", file); + return; + case '*': + /* Output an nullification completer if there's nothing for the */ + /* delay slot or nullification is requested. */ + if (dbr_sequence_length () == 0 || + (final_sequence && + INSN_ANNULLED_BRANCH_P (XVECEXP (final_sequence, 0, 0)))) + fputs (",n", file); + return; + case 'R': + /* Print out the second register name of a register pair. + I.e., R (6) => 7. */ + fputs (reg_names[REGNO (x)+1], file); + return; + case 'r': + /* A register or zero. */ + if (x == const0_rtx + || (x == CONST0_RTX (DFmode)) + || (x == CONST0_RTX (SFmode))) + { + fputs ("0", file); + return; + } + else + break; + case 'C': /* Plain (C)ondition */ + case 'X': + switch (GET_CODE (x)) + { + case EQ: + fputs ("=", file); break; + case NE: + fputs ("<>", file); break; + case GT: + fputs (">", file); break; + case GE: + fputs (">=", file); break; + case GEU: + fputs (">>=", file); break; + case GTU: + fputs (">>", file); break; + case LT: + fputs ("<", file); break; + case LE: + fputs ("<=", file); break; + case LEU: + fputs ("<<=", file); break; + case LTU: + fputs ("<<", file); break; + default: + abort (); + } + return; + case 'N': /* Condition, (N)egated */ + switch (GET_CODE (x)) + { + case EQ: + fputs ("<>", file); break; + case NE: + fputs ("=", file); break; + case GT: + fputs ("<=", file); break; + case GE: + fputs ("<", file); break; + case GEU: + fputs ("<<", file); break; + case GTU: + fputs ("<<=", file); break; + case LT: + fputs (">=", file); break; + case LE: + fputs (">", file); break; + case LEU: + fputs (">>", file); break; + case LTU: + fputs (">>=", file); break; + default: + abort (); + } + return; + /* For floating point comparisons. Need special conditions to deal + with NaNs properly. */ + case 'Y': + switch (GET_CODE (x)) + { + case EQ: + fputs ("!=", file); break; + case NE: + fputs ("=", file); break; + case GT: + fputs ("<=", file); break; + case GE: + fputs ("<", file); break; + case LT: + fputs (">=", file); break; + case LE: + fputs (">", file); break; + default: + abort (); + } + return; + case 'S': /* Condition, operands are (S)wapped. */ + switch (GET_CODE (x)) + { + case EQ: + fputs ("=", file); break; + case NE: + fputs ("<>", file); break; + case GT: + fputs ("<", file); break; + case GE: + fputs ("<=", file); break; + case GEU: + fputs ("<<=", file); break; + case GTU: + fputs ("<<", file); break; + case LT: + fputs (">", file); break; + case LE: + fputs (">=", file); break; + case LEU: + fputs (">>=", file); break; + case LTU: + fputs (">>", file); break; + default: + abort (); + } + return; + case 'B': /* Condition, (B)oth swapped and negate. */ + switch (GET_CODE (x)) + { + case EQ: + fputs ("<>", file); break; + case NE: + fputs ("=", file); break; + case GT: + fputs (">=", file); break; + case GE: + fputs (">", file); break; + case GEU: + fputs (">>", file); break; + case GTU: + fputs (">>=", file); break; + case LT: + fputs ("<=", file); break; + case LE: + fputs ("<", file); break; + case LEU: + fputs ("<<", file); break; + case LTU: + fputs ("<<=", file); break; + default: + abort (); + } + return; + case 'k': + if (GET_CODE (x) == CONST_INT) + { + fprintf (file, "%d", ~INTVAL (x)); + return; + } + abort(); + case 'L': + if (GET_CODE (x) == CONST_INT) + { + fprintf (file, "%d", 32 - (INTVAL (x) & 31)); + return; + } + abort(); + case 'O': + if (GET_CODE (x) == CONST_INT && exact_log2 (INTVAL (x)) >= 0) + { + fprintf (file, "%d", exact_log2 (INTVAL (x))); + return; + } + abort(); + case 'P': + if (GET_CODE (x) == CONST_INT) + { + fprintf (file, "%d", 31 - (INTVAL (x) & 31)); + return; + } + abort(); + case 'I': + if (GET_CODE (x) == CONST_INT) + fputs ("i", file); + return; + case 'M': + case 'F': + switch (GET_CODE (XEXP (x, 0))) + { + case PRE_DEC: + case PRE_INC: + fputs ("s,mb", file); + break; + case POST_DEC: + case POST_INC: + fputs ("s,ma", file); + break; + case PLUS: + if (GET_CODE (XEXP (XEXP (x, 0), 0)) == MULT + || GET_CODE (XEXP (XEXP (x, 0), 1)) == MULT) + fputs ("x,s", file); + else if (code == 'F') + fputs ("s", file); + break; + default: + if (code == 'F') + fputs ("s", file); + break; + } + return; + case 'G': + output_global_address (file, x, 0); + return; + case 'H': + output_global_address (file, x, 1); + return; + case 0: /* Don't do anything special */ + break; + case 'Z': + { + unsigned op[3]; + compute_zdepi_operands (INTVAL (x), op); + fprintf (file, "%d,%d,%d", op[0], op[1], op[2]); + return; + } + default: + abort (); + } + if (GET_CODE (x) == REG) + { + fputs (reg_names [REGNO (x)], file); + if (FP_REG_P (x) && GET_MODE_SIZE (GET_MODE (x)) <= 4 && (REGNO (x) & 1) == 0) + fputs ("L", file); + } + else if (GET_CODE (x) == MEM) + { + int size = GET_MODE_SIZE (GET_MODE (x)); + rtx base = XEXP (XEXP (x, 0), 0); + switch (GET_CODE (XEXP (x, 0))) + { + case PRE_DEC: + case POST_DEC: + fprintf (file, "-%d(0,%s)", size, reg_names [REGNO (base)]); + break; + case PRE_INC: + case POST_INC: + fprintf (file, "%d(0,%s)", size, reg_names [REGNO (base)]); + break; + default: + if (GET_CODE (XEXP (x, 0)) == PLUS + && GET_CODE (XEXP (XEXP (x, 0), 0)) == MULT) + fprintf (file, "%s(0,%s)", + reg_names [REGNO (XEXP (XEXP (XEXP (x, 0), 0), 0))], + reg_names [REGNO (XEXP (XEXP (x, 0), 1))]); + else if (GET_CODE (XEXP (x, 0)) == PLUS + && GET_CODE (XEXP (XEXP (x, 0), 1)) == MULT) + fprintf (file, "%s(0,%s)", + reg_names [REGNO (XEXP (XEXP (XEXP (x, 0), 1), 0))], + reg_names [REGNO (XEXP (XEXP (x, 0), 0))]); + else + output_address (XEXP (x, 0)); + break; + } + } + else + output_addr_const (file, x); +} + +/* output a SYMBOL_REF or a CONST expression involving a SYMBOL_REF. */ + +void +output_global_address (file, x, round_constant) + FILE *file; + rtx x; + int round_constant; +{ + + /* Imagine (high (const (plus ...))). */ + if (GET_CODE (x) == HIGH) + x = XEXP (x, 0); + + if (GET_CODE (x) == SYMBOL_REF && read_only_operand (x)) + assemble_name (file, XSTR (x, 0)); + else if (GET_CODE (x) == SYMBOL_REF && !flag_pic) + { + assemble_name (file, XSTR (x, 0)); + fputs ("-$global$", file); + } + else if (GET_CODE (x) == CONST) + { + char *sep = ""; + int offset = 0; /* assembler wants -$global$ at end */ + rtx base = NULL_RTX; + + if (GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF) + { + base = XEXP (XEXP (x, 0), 0); + output_addr_const (file, base); + } + else if (GET_CODE (XEXP (XEXP (x, 0), 0)) == CONST_INT) + offset = INTVAL (XEXP (XEXP (x, 0), 0)); + else abort (); + + if (GET_CODE (XEXP (XEXP (x, 0), 1)) == SYMBOL_REF) + { + base = XEXP (XEXP (x, 0), 1); + output_addr_const (file, base); + } + else if (GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT) + offset = INTVAL (XEXP (XEXP (x, 0),1)); + else abort (); + + /* How bogus. The compiler is apparently responsible for + rounding the constant if it uses an LR field selector. + + The linker and/or assembler seem a better place since + they have to do this kind of thing already. + + If we fail to do this, HP's optimizing linker may eliminate + an addil, but not update the ldw/stw/ldo instruction that + uses the result of the addil. */ + if (round_constant) + offset = ((offset + 0x1000) & ~0x1fff); + + if (GET_CODE (XEXP (x, 0)) == PLUS) + { + if (offset < 0) + { + offset = -offset; + sep = "-"; + } + else + sep = "+"; + } + else if (GET_CODE (XEXP (x, 0)) == MINUS + && (GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF)) + sep = "-"; + else abort (); + + if (!read_only_operand (base) && !flag_pic) + fputs ("-$global$", file); + if (offset) + fprintf (file,"%s%d", sep, offset); + } + else + output_addr_const (file, x); +} + +void +output_deferred_plabels (file) + FILE *file; +{ + int i; + /* If we have deferred plabels, then we need to switch into the data + section and align it to a 4 byte boundary before we output the + deferred plabels. */ + if (n_deferred_plabels) + { + data_section (); + ASM_OUTPUT_ALIGN (file, 2); + } + + /* Now output the deferred plabels. */ + for (i = 0; i < n_deferred_plabels; i++) + { + ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (deferred_plabels[i].internal_label)); + assemble_integer (gen_rtx_SYMBOL_REF (VOIDmode, + deferred_plabels[i].name), 4, 1); + } +} + +/* HP's millicode routines mean something special to the assembler. + Keep track of which ones we have used. */ + +enum millicodes { remI, remU, divI, divU, mulI, mulU, end1000 }; +static char imported[(int)end1000]; +static char *milli_names[] = {"remI", "remU", "divI", "divU", "mulI", "mulU"}; +static char import_string[] = ".IMPORT $$....,MILLICODE"; +#define MILLI_START 10 + +static void +import_milli (code) + enum millicodes code; +{ + char str[sizeof (import_string)]; + + if (!imported[(int)code]) + { + imported[(int)code] = 1; + strcpy (str, import_string); + strncpy (str + MILLI_START, milli_names[(int)code], 4); + output_asm_insn (str, 0); + } +} + +/* The register constraints have put the operands and return value in + the proper registers. */ + +char * +output_mul_insn (unsignedp, insn) + int unsignedp ATTRIBUTE_UNUSED; + rtx insn; +{ + import_milli (mulI); + return output_millicode_call (insn, gen_rtx_SYMBOL_REF (SImode, "$$mulI")); +} + +/* Emit the rtl for doing a division by a constant. */ + +/* Do magic division millicodes exist for this value? */ +static int magic_milli[]= {0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, + 1, 1}; + +/* We'll use an array to keep track of the magic millicodes and + whether or not we've used them already. [n][0] is signed, [n][1] is + unsigned. */ + +static int div_milli[16][2]; + +int +div_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (mode == SImode + && ((GET_CODE (op) == REG && REGNO (op) == 25) + || (GET_CODE (op) == CONST_INT && INTVAL (op) > 0 + && INTVAL (op) < 16 && magic_milli[INTVAL (op)]))); +} + +int +emit_hpdiv_const (operands, unsignedp) + rtx *operands; + int unsignedp; +{ + if (GET_CODE (operands[2]) == CONST_INT + && INTVAL (operands[2]) > 0 + && INTVAL (operands[2]) < 16 + && magic_milli[INTVAL (operands[2])]) + { + emit_move_insn (gen_rtx_REG (SImode, 26), operands[1]); + emit + (gen_rtx + (PARALLEL, VOIDmode, + gen_rtvec (5, gen_rtx_SET (VOIDmode, gen_rtx_REG (SImode, 29), + gen_rtx_fmt_ee (unsignedp ? UDIV : DIV, + SImode, + gen_rtx_REG (SImode, 26), + operands[2])), + gen_rtx_CLOBBER (VOIDmode, operands[3]), + gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (SImode, 26)), + gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (SImode, 25)), + gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (SImode, 31))))); + emit_move_insn (operands[0], gen_rtx_REG (SImode, 29)); + return 1; + } + return 0; +} + +char * +output_div_insn (operands, unsignedp, insn) + rtx *operands; + int unsignedp; + rtx insn; +{ + int divisor; + + /* If the divisor is a constant, try to use one of the special + opcodes .*/ + if (GET_CODE (operands[0]) == CONST_INT) + { + static char buf[100]; + divisor = INTVAL (operands[0]); + if (!div_milli[divisor][unsignedp]) + { + div_milli[divisor][unsignedp] = 1; + if (unsignedp) + output_asm_insn (".IMPORT $$divU_%0,MILLICODE", operands); + else + output_asm_insn (".IMPORT $$divI_%0,MILLICODE", operands); + } + if (unsignedp) + { + sprintf (buf, "$$divU_%d", INTVAL (operands[0])); + return output_millicode_call (insn, + gen_rtx_SYMBOL_REF (SImode, buf)); + } + else + { + sprintf (buf, "$$divI_%d", INTVAL (operands[0])); + return output_millicode_call (insn, + gen_rtx_SYMBOL_REF (SImode, buf)); + } + } + /* Divisor isn't a special constant. */ + else + { + if (unsignedp) + { + import_milli (divU); + return output_millicode_call (insn, + gen_rtx_SYMBOL_REF (SImode, "$$divU")); + } + else + { + import_milli (divI); + return output_millicode_call (insn, + gen_rtx_SYMBOL_REF (SImode, "$$divI")); + } + } +} + +/* Output a $$rem millicode to do mod. */ + +char * +output_mod_insn (unsignedp, insn) + int unsignedp; + rtx insn; +{ + if (unsignedp) + { + import_milli (remU); + return output_millicode_call (insn, + gen_rtx_SYMBOL_REF (SImode, "$$remU")); + } + else + { + import_milli (remI); + return output_millicode_call (insn, + gen_rtx_SYMBOL_REF (SImode, "$$remI")); + } +} + +void +output_arg_descriptor (call_insn) + rtx call_insn; +{ + char *arg_regs[4]; + enum machine_mode arg_mode; + rtx link; + int i, output_flag = 0; + int regno; + + for (i = 0; i < 4; i++) + arg_regs[i] = 0; + + /* Specify explicitly that no argument relocations should take place + if using the portable runtime calling conventions. */ + if (TARGET_PORTABLE_RUNTIME) + { + fputs ("\t.CALL ARGW0=NO,ARGW1=NO,ARGW2=NO,ARGW3=NO,RETVAL=NO\n", + asm_out_file); + return; + } + + if (GET_CODE (call_insn) != CALL_INSN) + abort (); + for (link = CALL_INSN_FUNCTION_USAGE (call_insn); link; link = XEXP (link, 1)) + { + rtx use = XEXP (link, 0); + + if (! (GET_CODE (use) == USE + && GET_CODE (XEXP (use, 0)) == REG + && FUNCTION_ARG_REGNO_P (REGNO (XEXP (use, 0))))) + continue; + + arg_mode = GET_MODE (XEXP (use, 0)); + regno = REGNO (XEXP (use, 0)); + if (regno >= 23 && regno <= 26) + { + arg_regs[26 - regno] = "GR"; + if (arg_mode == DImode) + arg_regs[25 - regno] = "GR"; + } + else if (regno >= 32 && regno <= 39) + { + if (arg_mode == SFmode) + arg_regs[(regno - 32) / 2] = "FR"; + else + { +#ifndef HP_FP_ARG_DESCRIPTOR_REVERSED + arg_regs[(regno - 34) / 2] = "FR"; + arg_regs[(regno - 34) / 2 + 1] = "FU"; +#else + arg_regs[(regno - 34) / 2] = "FU"; + arg_regs[(regno - 34) / 2 + 1] = "FR"; +#endif + } + } + } + fputs ("\t.CALL ", asm_out_file); + for (i = 0; i < 4; i++) + { + if (arg_regs[i]) + { + if (output_flag++) + fputc (',', asm_out_file); + fprintf (asm_out_file, "ARGW%d=%s", i, arg_regs[i]); + } + } + fputc ('\n', asm_out_file); +} + +/* Return the class of any secondary reload register that is needed to + move IN into a register in class CLASS using mode MODE. + + Profiling has showed this routine and its descendants account for + a significant amount of compile time (~7%). So it has been + optimized to reduce redundant computations and eliminate useless + function calls. + + It might be worthwhile to try and make this a leaf function too. */ + +enum reg_class +secondary_reload_class (class, mode, in) + enum reg_class class; + enum machine_mode mode; + rtx in; +{ + int regno, is_symbolic; + + /* Trying to load a constant into a FP register during PIC code + generation will require %r1 as a scratch register. */ + if (flag_pic == 2 + && GET_MODE_CLASS (mode) == MODE_INT + && FP_REG_CLASS_P (class) + && (GET_CODE (in) == CONST_INT || GET_CODE (in) == CONST_DOUBLE)) + return R1_REGS; + + /* Profiling showed the PA port spends about 1.3% of its compilation + time in true_regnum from calls inside secondary_reload_class. */ + + if (GET_CODE (in) == REG) + { + regno = REGNO (in); + if (regno >= FIRST_PSEUDO_REGISTER) + regno = true_regnum (in); + } + else if (GET_CODE (in) == SUBREG) + regno = true_regnum (in); + else + regno = -1; + + /* If we have something like (mem (mem (...)), we can safely assume the + inner MEM will end up in a general register after reloading, so there's + no need for a secondary reload. */ + if (GET_CODE (in) == MEM + && GET_CODE (XEXP (in, 0)) == MEM) + return NO_REGS; + + /* Handle out of range displacement for integer mode loads/stores of + FP registers. */ + if (((regno >= FIRST_PSEUDO_REGISTER || regno == -1) + && GET_MODE_CLASS (mode) == MODE_INT + && FP_REG_CLASS_P (class)) + || (class == SHIFT_REGS && (regno <= 0 || regno >= 32))) + return GENERAL_REGS; + + if (GET_CODE (in) == HIGH) + in = XEXP (in, 0); + + /* Profiling has showed GCC spends about 2.6% of its compilation + time in symbolic_operand from calls inside secondary_reload_class. + + We use an inline copy and only compute its return value once to avoid + useless work. */ + switch (GET_CODE (in)) + { + rtx tmp; + + case SYMBOL_REF: + case LABEL_REF: + is_symbolic = 1; + break; + case CONST: + tmp = XEXP (in, 0); + is_symbolic = ((GET_CODE (XEXP (tmp, 0)) == SYMBOL_REF + || GET_CODE (XEXP (tmp, 0)) == LABEL_REF) + && GET_CODE (XEXP (tmp, 1)) == CONST_INT); + break; + + default: + is_symbolic = 0; + break; + } + + if (!flag_pic + && is_symbolic + && read_only_operand (in)) + return NO_REGS; + + if (class != R1_REGS && is_symbolic) + return R1_REGS; + + return NO_REGS; +} + +enum direction +function_arg_padding (mode, type) + enum machine_mode mode; + tree type; +{ + int size; + + if (mode == BLKmode) + { + if (type && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST) + size = int_size_in_bytes (type) * BITS_PER_UNIT; + else + return upward; /* Don't know if this is right, but */ + /* same as old definition. */ + } + else + size = GET_MODE_BITSIZE (mode); + if (size < PARM_BOUNDARY) + return downward; + else if (size % PARM_BOUNDARY) + return upward; + else + return none; +} + + +/* Do what is necessary for `va_start'. The argument is ignored; + We look at the current function to determine if stdargs or varargs + is used and fill in an initial va_list. A pointer to this constructor + is returned. */ + +struct rtx_def * +hppa_builtin_saveregs (arglist) + tree arglist ATTRIBUTE_UNUSED; +{ + rtx offset, dest; + tree fntype = TREE_TYPE (current_function_decl); + int argadj = ((!(TYPE_ARG_TYPES (fntype) != 0 + && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) + != void_type_node))) + ? UNITS_PER_WORD : 0); + + if (argadj) + offset = plus_constant (current_function_arg_offset_rtx, argadj); + else + offset = current_function_arg_offset_rtx; + + /* Store general registers on the stack. */ + dest = gen_rtx_MEM (BLKmode, + plus_constant (current_function_internal_arg_pointer, + -16)); + move_block_from_reg (23, dest, 4, 4 * UNITS_PER_WORD); + + /* move_block_from_reg will emit code to store the argument registers + individually as scalar stores. + + However, other insns may later load from the same addresses for + a structure load (passing a struct to a varargs routine). + + The alias code assumes that such aliasing can never happen, so we + have to keep memory referencing insns from moving up beyond the + last argument register store. So we emit a blockage insn here. */ + emit_insn (gen_blockage ()); + + if (current_function_check_memory_usage) + emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3, + dest, ptr_mode, + GEN_INT (4 * UNITS_PER_WORD), TYPE_MODE (sizetype), + GEN_INT (MEMORY_USE_RW), + TYPE_MODE (integer_type_node)); + + return copy_to_reg (expand_binop (Pmode, add_optab, + current_function_internal_arg_pointer, + offset, 0, 0, OPTAB_LIB_WIDEN)); +} + +/* This routine handles all the normal conditional branch sequences we + might need to generate. It handles compare immediate vs compare + register, nullification of delay slots, varying length branches, + negated branches, and all combinations of the above. It returns the + output appropriate to emit the branch corresponding to all given + parameters. */ + +char * +output_cbranch (operands, nullify, length, negated, insn) + rtx *operands; + int nullify, length, negated; + rtx insn; +{ + static char buf[100]; + int useskip = 0; + + /* A conditional branch to the following instruction (eg the delay slot) is + asking for a disaster. This can happen when not optimizing. + + In such cases it is safe to emit nothing. */ + + if (next_active_insn (JUMP_LABEL (insn)) == next_active_insn (insn)) + return ""; + + /* If this is a long branch with its delay slot unfilled, set `nullify' + as it can nullify the delay slot and save a nop. */ + if (length == 8 && dbr_sequence_length () == 0) + nullify = 1; + + /* If this is a short forward conditional branch which did not get + its delay slot filled, the delay slot can still be nullified. */ + if (! nullify && length == 4 && dbr_sequence_length () == 0) + nullify = forward_branch_p (insn); + + /* A forward branch over a single nullified insn can be done with a + comclr instruction. This avoids a single cycle penalty due to + mis-predicted branch if we fall through (branch not taken). */ + if (length == 4 + && next_real_insn (insn) != 0 + && get_attr_length (next_real_insn (insn)) == 4 + && JUMP_LABEL (insn) == next_nonnote_insn (next_real_insn (insn)) + && nullify) + useskip = 1; + + switch (length) + { + /* All short conditional branches except backwards with an unfilled + delay slot. */ + case 4: + if (useskip) + strcpy (buf, "com%I2clr,"); + else + strcpy (buf, "com%I2b,"); + if (negated) + strcat (buf, "%B3"); + else + strcat (buf, "%S3"); + if (useskip) + strcat (buf, " %2,%r1,0"); + else if (nullify) + strcat (buf, ",n %2,%r1,%0"); + else + strcat (buf, " %2,%r1,%0"); + break; + + /* All long conditionals. Note an short backward branch with an + unfilled delay slot is treated just like a long backward branch + with an unfilled delay slot. */ + case 8: + /* Handle weird backwards branch with a filled delay slot + with is nullified. */ + if (dbr_sequence_length () != 0 + && ! forward_branch_p (insn) + && nullify) + { + strcpy (buf, "com%I2b,"); + if (negated) + strcat (buf, "%S3"); + else + strcat (buf, "%B3"); + strcat (buf, ",n %2,%r1,.+12\n\tbl %0,0"); + } + /* Handle short backwards branch with an unfilled delay slot. + Using a comb;nop rather than comiclr;bl saves 1 cycle for both + taken and untaken branches. */ + else if (dbr_sequence_length () == 0 + && ! forward_branch_p (insn) + && insn_addresses + && VAL_14_BITS_P (insn_addresses[INSN_UID (JUMP_LABEL (insn))] + - insn_addresses[INSN_UID (insn)] - 8)) + { + strcpy (buf, "com%I2b,"); + if (negated) + strcat (buf, "%B3 %2,%r1,%0%#"); + else + strcat (buf, "%S3 %2,%r1,%0%#"); + } + else + { + strcpy (buf, "com%I2clr,"); + if (negated) + strcat (buf, "%S3"); + else + strcat (buf, "%B3"); + if (nullify) + strcat (buf, " %2,%r1,0\n\tbl,n %0,0"); + else + strcat (buf, " %2,%r1,0\n\tbl %0,0"); + } + break; + + case 20: + /* Very long branch. Right now we only handle these when not + optimizing. See "jump" pattern in pa.md for details. */ + if (optimize) + abort (); + + /* Create a reversed conditional branch which branches around + the following insns. */ + if (negated) + strcpy (buf, "com%I2b,%S3,n %2,%r1,.+20"); + else + strcpy (buf, "com%I2b,%B3,n %2,%r1,.+20"); + output_asm_insn (buf, operands); + + /* Output an insn to save %r1. */ + output_asm_insn ("stw %%r1,-16(%%r30)", operands); + + /* Now output a very long branch to the original target. */ + output_asm_insn ("ldil L'%l0,%%r1\n\tbe R'%l0(%%sr4,%%r1)", operands); + + /* Now restore the value of %r1 in the delay slot. We're not + optimizing so we know nothing else can be in the delay slot. */ + return "ldw -16(%%r30),%%r1"; + + case 28: + /* Very long branch when generating PIC code. Right now we only + handle these when not optimizing. See "jump" pattern in pa.md + for details. */ + if (optimize) + abort (); + + /* Create a reversed conditional branch which branches around + the following insns. */ + if (negated) + strcpy (buf, "com%I2b,%S3,n %2,%r1,.+28"); + else + strcpy (buf, "com%I2b,%B3,n %2,%r1,.+28"); + output_asm_insn (buf, operands); + + /* Output an insn to save %r1. */ + output_asm_insn ("stw %%r1,-16(%%r30)", operands); + + /* Now output a very long PIC branch to the original target. */ + { + rtx xoperands[5]; + + xoperands[0] = operands[0]; + xoperands[1] = operands[1]; + xoperands[2] = operands[2]; + xoperands[3] = operands[3]; + xoperands[4] = gen_label_rtx (); + + output_asm_insn ("bl .+8,%%r1\n\taddil L'%l0-%l4,%%r1", xoperands); + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", + CODE_LABEL_NUMBER (xoperands[4])); + output_asm_insn ("ldo R'%l0-%l4(%%r1),%%r1\n\tbv 0(%%r1)", xoperands); + } + + /* Now restore the value of %r1 in the delay slot. We're not + optimizing so we know nothing else can be in the delay slot. */ + return "ldw -16(%%r30),%%r1"; + + default: + abort(); + } + return buf; +} + +/* This routine handles all the branch-on-bit conditional branch sequences we + might need to generate. It handles nullification of delay slots, + varying length branches, negated branches and all combinations of the + above. it returns the appropriate output template to emit the branch. */ + +char * +output_bb (operands, nullify, length, negated, insn, which) + rtx *operands ATTRIBUTE_UNUSED; + int nullify, length, negated; + rtx insn; + int which; +{ + static char buf[100]; + int useskip = 0; + + /* A conditional branch to the following instruction (eg the delay slot) is + asking for a disaster. I do not think this can happen as this pattern + is only used when optimizing; jump optimization should eliminate the + jump. But be prepared just in case. */ + + if (next_active_insn (JUMP_LABEL (insn)) == next_active_insn (insn)) + return ""; + + /* If this is a long branch with its delay slot unfilled, set `nullify' + as it can nullify the delay slot and save a nop. */ + if (length == 8 && dbr_sequence_length () == 0) + nullify = 1; + + /* If this is a short forward conditional branch which did not get + its delay slot filled, the delay slot can still be nullified. */ + if (! nullify && length == 4 && dbr_sequence_length () == 0) + nullify = forward_branch_p (insn); + + /* A forward branch over a single nullified insn can be done with a + extrs instruction. This avoids a single cycle penalty due to + mis-predicted branch if we fall through (branch not taken). */ + + if (length == 4 + && next_real_insn (insn) != 0 + && get_attr_length (next_real_insn (insn)) == 4 + && JUMP_LABEL (insn) == next_nonnote_insn (next_real_insn (insn)) + && nullify) + useskip = 1; + + switch (length) + { + + /* All short conditional branches except backwards with an unfilled + delay slot. */ + case 4: + if (useskip) + strcpy (buf, "extrs,"); + else + strcpy (buf, "bb,"); + if ((which == 0 && negated) + || (which == 1 && ! negated)) + strcat (buf, ">="); + else + strcat (buf, "<"); + if (useskip) + strcat (buf, " %0,%1,1,0"); + else if (nullify && negated) + strcat (buf, ",n %0,%1,%3"); + else if (nullify && ! negated) + strcat (buf, ",n %0,%1,%2"); + else if (! nullify && negated) + strcat (buf, "%0,%1,%3"); + else if (! nullify && ! negated) + strcat (buf, " %0,%1,%2"); + break; + + /* All long conditionals. Note an short backward branch with an + unfilled delay slot is treated just like a long backward branch + with an unfilled delay slot. */ + case 8: + /* Handle weird backwards branch with a filled delay slot + with is nullified. */ + if (dbr_sequence_length () != 0 + && ! forward_branch_p (insn) + && nullify) + { + strcpy (buf, "bb,"); + if ((which == 0 && negated) + || (which == 1 && ! negated)) + strcat (buf, "<"); + else + strcat (buf, ">="); + if (negated) + strcat (buf, ",n %0,%1,.+12\n\tbl %3,0"); + else + strcat (buf, ",n %0,%1,.+12\n\tbl %2,0"); + } + /* Handle short backwards branch with an unfilled delay slot. + Using a bb;nop rather than extrs;bl saves 1 cycle for both + taken and untaken branches. */ + else if (dbr_sequence_length () == 0 + && ! forward_branch_p (insn) + && insn_addresses + && VAL_14_BITS_P (insn_addresses[INSN_UID (JUMP_LABEL (insn))] + - insn_addresses[INSN_UID (insn)] - 8)) + { + strcpy (buf, "bb,"); + if ((which == 0 && negated) + || (which == 1 && ! negated)) + strcat (buf, ">="); + else + strcat (buf, "<"); + if (negated) + strcat (buf, " %0,%1,%3%#"); + else + strcat (buf, " %0,%1,%2%#"); + } + else + { + strcpy (buf, "extrs,"); + if ((which == 0 && negated) + || (which == 1 && ! negated)) + strcat (buf, "<"); + else + strcat (buf, ">="); + if (nullify && negated) + strcat (buf, " %0,%1,1,0\n\tbl,n %3,0"); + else if (nullify && ! negated) + strcat (buf, " %0,%1,1,0\n\tbl,n %2,0"); + else if (negated) + strcat (buf, " %0,%1,1,0\n\tbl %3,0"); + else + strcat (buf, " %0,%1,1,0\n\tbl %2,0"); + } + break; + + default: + abort(); + } + return buf; +} + +/* This routine handles all the branch-on-variable-bit conditional branch + sequences we might need to generate. It handles nullification of delay + slots, varying length branches, negated branches and all combinations + of the above. it returns the appropriate output template to emit the + branch. */ + +char * +output_bvb (operands, nullify, length, negated, insn, which) + rtx *operands ATTRIBUTE_UNUSED; + int nullify, length, negated; + rtx insn; + int which; +{ + static char buf[100]; + int useskip = 0; + + /* A conditional branch to the following instruction (eg the delay slot) is + asking for a disaster. I do not think this can happen as this pattern + is only used when optimizing; jump optimization should eliminate the + jump. But be prepared just in case. */ + + if (next_active_insn (JUMP_LABEL (insn)) == next_active_insn (insn)) + return ""; + + /* If this is a long branch with its delay slot unfilled, set `nullify' + as it can nullify the delay slot and save a nop. */ + if (length == 8 && dbr_sequence_length () == 0) + nullify = 1; + + /* If this is a short forward conditional branch which did not get + its delay slot filled, the delay slot can still be nullified. */ + if (! nullify && length == 4 && dbr_sequence_length () == 0) + nullify = forward_branch_p (insn); + + /* A forward branch over a single nullified insn can be done with a + extrs instruction. This avoids a single cycle penalty due to + mis-predicted branch if we fall through (branch not taken). */ + + if (length == 4 + && next_real_insn (insn) != 0 + && get_attr_length (next_real_insn (insn)) == 4 + && JUMP_LABEL (insn) == next_nonnote_insn (next_real_insn (insn)) + && nullify) + useskip = 1; + + switch (length) + { + + /* All short conditional branches except backwards with an unfilled + delay slot. */ + case 4: + if (useskip) + strcpy (buf, "vextrs,"); + else + strcpy (buf, "bvb,"); + if ((which == 0 && negated) + || (which == 1 && ! negated)) + strcat (buf, ">="); + else + strcat (buf, "<"); + if (useskip) + strcat (buf, " %0,1,0"); + else if (nullify && negated) + strcat (buf, ",n %0,%3"); + else if (nullify && ! negated) + strcat (buf, ",n %0,%2"); + else if (! nullify && negated) + strcat (buf, "%0,%3"); + else if (! nullify && ! negated) + strcat (buf, " %0,%2"); + break; + + /* All long conditionals. Note an short backward branch with an + unfilled delay slot is treated just like a long backward branch + with an unfilled delay slot. */ + case 8: + /* Handle weird backwards branch with a filled delay slot + with is nullified. */ + if (dbr_sequence_length () != 0 + && ! forward_branch_p (insn) + && nullify) + { + strcpy (buf, "bvb,"); + if ((which == 0 && negated) + || (which == 1 && ! negated)) + strcat (buf, "<"); + else + strcat (buf, ">="); + if (negated) + strcat (buf, ",n %0,.+12\n\tbl %3,0"); + else + strcat (buf, ",n %0,.+12\n\tbl %2,0"); + } + /* Handle short backwards branch with an unfilled delay slot. + Using a bb;nop rather than extrs;bl saves 1 cycle for both + taken and untaken branches. */ + else if (dbr_sequence_length () == 0 + && ! forward_branch_p (insn) + && insn_addresses + && VAL_14_BITS_P (insn_addresses[INSN_UID (JUMP_LABEL (insn))] + - insn_addresses[INSN_UID (insn)] - 8)) + { + strcpy (buf, "bvb,"); + if ((which == 0 && negated) + || (which == 1 && ! negated)) + strcat (buf, ">="); + else + strcat (buf, "<"); + if (negated) + strcat (buf, " %0,%3%#"); + else + strcat (buf, " %0,%2%#"); + } + else + { + strcpy (buf, "vextrs,"); + if ((which == 0 && negated) + || (which == 1 && ! negated)) + strcat (buf, "<"); + else + strcat (buf, ">="); + if (nullify && negated) + strcat (buf, " %0,1,0\n\tbl,n %3,0"); + else if (nullify && ! negated) + strcat (buf, " %0,1,0\n\tbl,n %2,0"); + else if (negated) + strcat (buf, " %0,1,0\n\tbl %3,0"); + else + strcat (buf, " %0,1,0\n\tbl %2,0"); + } + break; + + default: + abort(); + } + return buf; +} + +/* Return the output template for emitting a dbra type insn. + + Note it may perform some output operations on its own before + returning the final output string. */ +char * +output_dbra (operands, insn, which_alternative) + rtx *operands; + rtx insn; + int which_alternative; +{ + + /* A conditional branch to the following instruction (eg the delay slot) is + asking for a disaster. Be prepared! */ + + if (next_active_insn (JUMP_LABEL (insn)) == next_active_insn (insn)) + { + if (which_alternative == 0) + return "ldo %1(%0),%0"; + else if (which_alternative == 1) + { + output_asm_insn ("fstws %0,-16(0,%%r30)",operands); + output_asm_insn ("ldw -16(0,%%r30),%4",operands); + output_asm_insn ("ldo %1(%4),%4\n\tstw %4,-16(0,%%r30)", operands); + return "fldws -16(0,%%r30),%0"; + } + else + { + output_asm_insn ("ldw %0,%4", operands); + return "ldo %1(%4),%4\n\tstw %4,%0"; + } + } + + if (which_alternative == 0) + { + int nullify = INSN_ANNULLED_BRANCH_P (insn); + int length = get_attr_length (insn); + + /* If this is a long branch with its delay slot unfilled, set `nullify' + as it can nullify the delay slot and save a nop. */ + if (length == 8 && dbr_sequence_length () == 0) + nullify = 1; + + /* If this is a short forward conditional branch which did not get + its delay slot filled, the delay slot can still be nullified. */ + if (! nullify && length == 4 && dbr_sequence_length () == 0) + nullify = forward_branch_p (insn); + + /* Handle short versions first. */ + if (length == 4 && nullify) + return "addib,%C2,n %1,%0,%3"; + else if (length == 4 && ! nullify) + return "addib,%C2 %1,%0,%3"; + else if (length == 8) + { + /* Handle weird backwards branch with a fulled delay slot + which is nullified. */ + if (dbr_sequence_length () != 0 + && ! forward_branch_p (insn) + && nullify) + return "addib,%N2,n %1,%0,.+12\n\tbl %3,0"; + /* Handle short backwards branch with an unfilled delay slot. + Using a addb;nop rather than addi;bl saves 1 cycle for both + taken and untaken branches. */ + else if (dbr_sequence_length () == 0 + && ! forward_branch_p (insn) + && insn_addresses + && VAL_14_BITS_P (insn_addresses[INSN_UID (JUMP_LABEL (insn))] + - insn_addresses[INSN_UID (insn)] - 8)) + return "addib,%C2 %1,%0,%3%#"; + + /* Handle normal cases. */ + if (nullify) + return "addi,%N2 %1,%0,%0\n\tbl,n %3,0"; + else + return "addi,%N2 %1,%0,%0\n\tbl %3,0"; + } + else + abort(); + } + /* Deal with gross reload from FP register case. */ + else if (which_alternative == 1) + { + /* Move loop counter from FP register to MEM then into a GR, + increment the GR, store the GR into MEM, and finally reload + the FP register from MEM from within the branch's delay slot. */ + output_asm_insn ("fstws %0,-16(0,%%r30)\n\tldw -16(0,%%r30),%4",operands); + output_asm_insn ("ldo %1(%4),%4\n\tstw %4,-16(0,%%r30)", operands); + if (get_attr_length (insn) == 24) + return "comb,%S2 0,%4,%3\n\tfldws -16(0,%%r30),%0"; + else + return "comclr,%B2 0,%4,0\n\tbl %3,0\n\tfldws -16(0,%%r30),%0"; + } + /* Deal with gross reload from memory case. */ + else + { + /* Reload loop counter from memory, the store back to memory + happens in the branch's delay slot. */ + output_asm_insn ("ldw %0,%4", operands); + if (get_attr_length (insn) == 12) + return "addib,%C2 %1,%4,%3\n\tstw %4,%0"; + else + return "addi,%N2 %1,%4,%4\n\tbl %3,0\n\tstw %4,%0"; + } +} + +/* Return the output template for emitting a dbra type insn. + + Note it may perform some output operations on its own before + returning the final output string. */ +char * +output_movb (operands, insn, which_alternative, reverse_comparison) + rtx *operands; + rtx insn; + int which_alternative; + int reverse_comparison; +{ + + /* A conditional branch to the following instruction (eg the delay slot) is + asking for a disaster. Be prepared! */ + + if (next_active_insn (JUMP_LABEL (insn)) == next_active_insn (insn)) + { + if (which_alternative == 0) + return "copy %1,%0"; + else if (which_alternative == 1) + { + output_asm_insn ("stw %1,-16(0,%%r30)",operands); + return "fldws -16(0,%%r30),%0"; + } + else if (which_alternative == 2) + return "stw %1,%0"; + else + return "mtsar %r1"; + } + + /* Support the second variant. */ + if (reverse_comparison) + PUT_CODE (operands[2], reverse_condition (GET_CODE (operands[2]))); + + if (which_alternative == 0) + { + int nullify = INSN_ANNULLED_BRANCH_P (insn); + int length = get_attr_length (insn); + + /* If this is a long branch with its delay slot unfilled, set `nullify' + as it can nullify the delay slot and save a nop. */ + if (length == 8 && dbr_sequence_length () == 0) + nullify = 1; + + /* If this is a short forward conditional branch which did not get + its delay slot filled, the delay slot can still be nullified. */ + if (! nullify && length == 4 && dbr_sequence_length () == 0) + nullify = forward_branch_p (insn); + + /* Handle short versions first. */ + if (length == 4 && nullify) + return "movb,%C2,n %1,%0,%3"; + else if (length == 4 && ! nullify) + return "movb,%C2 %1,%0,%3"; + else if (length == 8) + { + /* Handle weird backwards branch with a filled delay slot + which is nullified. */ + if (dbr_sequence_length () != 0 + && ! forward_branch_p (insn) + && nullify) + return "movb,%N2,n %1,%0,.+12\n\tbl %3,0"; + + /* Handle short backwards branch with an unfilled delay slot. + Using a movb;nop rather than or;bl saves 1 cycle for both + taken and untaken branches. */ + else if (dbr_sequence_length () == 0 + && ! forward_branch_p (insn) + && insn_addresses + && VAL_14_BITS_P (insn_addresses[INSN_UID (JUMP_LABEL (insn))] + - insn_addresses[INSN_UID (insn)] - 8)) + return "movb,%C2 %1,%0,%3%#"; + /* Handle normal cases. */ + if (nullify) + return "or,%N2 %1,%%r0,%0\n\tbl,n %3,0"; + else + return "or,%N2 %1,%%r0,%0\n\tbl %3,0"; + } + else + abort(); + } + /* Deal with gross reload from FP register case. */ + else if (which_alternative == 1) + { + /* Move loop counter from FP register to MEM then into a GR, + increment the GR, store the GR into MEM, and finally reload + the FP register from MEM from within the branch's delay slot. */ + output_asm_insn ("stw %1,-16(0,%%r30)",operands); + if (get_attr_length (insn) == 12) + return "comb,%S2 0,%1,%3\n\tfldws -16(0,%%r30),%0"; + else + return "comclr,%B2 0,%1,0\n\tbl %3,0\n\tfldws -16(0,%%r30),%0"; + } + /* Deal with gross reload from memory case. */ + else if (which_alternative == 2) + { + /* Reload loop counter from memory, the store back to memory + happens in the branch's delay slot. */ + if (get_attr_length (insn) == 8) + return "comb,%S2 0,%1,%3\n\tstw %1,%0"; + else + return "comclr,%B2 0,%1,0\n\tbl %3,0\n\tstw %1,%0"; + } + /* Handle SAR as a destination. */ + else + { + if (get_attr_length (insn) == 8) + return "comb,%S2 0,%1,%3\n\tmtsar %r1"; + else + return "comclr,%B2 0,%1,0\n\tbl %3,0\n\tmtsar %r1"; + } +} + + +/* INSN is a millicode call. It may have an unconditional jump in its delay + slot. + + CALL_DEST is the routine we are calling. */ + +char * +output_millicode_call (insn, call_dest) + rtx insn; + rtx call_dest; +{ + int distance; + rtx xoperands[4]; + rtx seq_insn; + + /* Handle common case -- empty delay slot or no jump in the delay slot, + and we're sure that the branch will reach the beginning of the $CODE$ + subspace. */ + if ((dbr_sequence_length () == 0 + && (get_attr_length (insn) == 8 || get_attr_length (insn) == 28)) + || (dbr_sequence_length () != 0 + && GET_CODE (NEXT_INSN (insn)) != JUMP_INSN + && get_attr_length (insn) == 4)) + { + xoperands[0] = call_dest; + output_asm_insn ("bl %0,%%r31%#", xoperands); + return ""; + } + + /* This call may not reach the beginning of the $CODE$ subspace. */ + if (get_attr_length (insn) > 4) + { + int delay_insn_deleted = 0; + rtx xoperands[2]; + + /* We need to emit an inline long-call branch. */ + if (dbr_sequence_length () != 0 + && GET_CODE (NEXT_INSN (insn)) != JUMP_INSN) + { + /* A non-jump insn in the delay slot. By definition we can + emit this insn before the call. */ + final_scan_insn (NEXT_INSN (insn), asm_out_file, optimize, 0, 0); + + /* Now delete the delay insn. */ + PUT_CODE (NEXT_INSN (insn), NOTE); + NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0; + delay_insn_deleted = 1; + } + + /* If we're allowed to use be/ble instructions, then this is the + best sequence to use for a long millicode call. */ + if (TARGET_NO_SPACE_REGS || TARGET_FAST_INDIRECT_CALLS + || ! (flag_pic || TARGET_PORTABLE_RUNTIME)) + { + xoperands[0] = call_dest; + output_asm_insn ("ldil L%%%0,%%r31", xoperands); + output_asm_insn ("ble R%%%0(%%sr4,%%r31)", xoperands); + output_asm_insn ("nop", xoperands); + } + /* Pure portable runtime doesn't allow be/ble; we also don't have + PIC support int he assembler/linker, so this sequence is needed. */ + else if (TARGET_PORTABLE_RUNTIME) + { + xoperands[0] = call_dest; + /* Get the address of our target into %r29. */ + output_asm_insn ("ldil L%%%0,%%r29", xoperands); + output_asm_insn ("ldo R%%%0(%%r29),%%r29", xoperands); + + /* Get our return address into %r31. */ + output_asm_insn ("blr 0,%%r31", xoperands); + + /* Jump to our target address in %r29. */ + output_asm_insn ("bv,n 0(%%r29)", xoperands); + + /* Empty delay slot. Note this insn gets fetched twice and + executed once. To be safe we use a nop. */ + output_asm_insn ("nop", xoperands); + return ""; + } + /* PIC long millicode call sequence. */ + else + { + xoperands[0] = call_dest; + xoperands[1] = gen_label_rtx (); + /* Get our address + 8 into %r1. */ + output_asm_insn ("bl .+8,%%r1", xoperands); + + /* Add %r1 to the offset of our target from the next insn. */ + output_asm_insn ("addil L%%%0-%1,%%r1", xoperands); + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", + CODE_LABEL_NUMBER (xoperands[1])); + output_asm_insn ("ldo R%%%0-%1(%%r1),%%r1", xoperands); + + /* Get the return address into %r31. */ + output_asm_insn ("blr 0,%%r31", xoperands); + + /* Branch to our target which is in %r1. */ + output_asm_insn ("bv,n 0(%%r1)", xoperands); + + /* Empty delay slot. Note this insn gets fetched twice and + executed once. To be safe we use a nop. */ + output_asm_insn ("nop", xoperands); + } + + /* If we had a jump in the call's delay slot, output it now. */ + if (dbr_sequence_length () != 0 + && !delay_insn_deleted) + { + xoperands[0] = XEXP (PATTERN (NEXT_INSN (insn)), 1); + output_asm_insn ("b,n %0", xoperands); + + /* Now delete the delay insn. */ + PUT_CODE (NEXT_INSN (insn), NOTE); + NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0; + } + return ""; + } + + /* This call has an unconditional jump in its delay slot and the + call is known to reach its target or the beginning of the current + subspace. */ + + /* Use the containing sequence insn's address. */ + seq_insn = NEXT_INSN (PREV_INSN (XVECEXP (final_sequence, 0, 0))); + + distance = insn_addresses[INSN_UID (JUMP_LABEL (NEXT_INSN (insn)))] + - insn_addresses[INSN_UID (seq_insn)] - 8; + + /* If the branch was too far away, emit a normal call followed + by a nop, followed by the unconditional branch. + + If the branch is close, then adjust %r2 from within the + call's delay slot. */ + + xoperands[0] = call_dest; + xoperands[1] = XEXP (PATTERN (NEXT_INSN (insn)), 1); + if (! VAL_14_BITS_P (distance)) + output_asm_insn ("bl %0,%%r31\n\tnop\n\tbl,n %1,%%r0", xoperands); + else + { + xoperands[3] = gen_label_rtx (); + output_asm_insn ("\n\tbl %0,%%r31\n\tldo %1-%3(%%r31),%%r31", xoperands); + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", + CODE_LABEL_NUMBER (xoperands[3])); + } + + /* Delete the jump. */ + PUT_CODE (NEXT_INSN (insn), NOTE); + NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0; + return ""; +} + +extern struct obstack permanent_obstack; +extern struct obstack *saveable_obstack; +extern struct obstack *rtl_obstack; +extern struct obstack *current_obstack; + +/* INSN is either a function call. It may have an unconditional jump + in its delay slot. + + CALL_DEST is the routine we are calling. */ + +char * +output_call (insn, call_dest) + rtx insn; + rtx call_dest; +{ + int distance; + rtx xoperands[4]; + rtx seq_insn; + + /* Handle common case -- empty delay slot or no jump in the delay slot, + and we're sure that the branch will reach the beginning of the $CODE$ + subspace. */ + if ((dbr_sequence_length () == 0 + && get_attr_length (insn) == 8) + || (dbr_sequence_length () != 0 + && GET_CODE (NEXT_INSN (insn)) != JUMP_INSN + && get_attr_length (insn) == 4)) + { + xoperands[0] = call_dest; + output_asm_insn ("bl %0,%%r2%#", xoperands); + return ""; + } + + /* This call may not reach the beginning of the $CODE$ subspace. */ + if (get_attr_length (insn) > 8) + { + int delay_insn_deleted = 0; + rtx xoperands[2]; + rtx link; + + /* We need to emit an inline long-call branch. Furthermore, + because we're changing a named function call into an indirect + function call well after the parameters have been set up, we + need to make sure any FP args appear in both the integer + and FP registers. Also, we need move any delay slot insn + out of the delay slot. And finally, we can't rely on the linker + being able to fix the call to $$dyncall! -- Yuk!. */ + if (dbr_sequence_length () != 0 + && GET_CODE (NEXT_INSN (insn)) != JUMP_INSN) + { + /* A non-jump insn in the delay slot. By definition we can + emit this insn before the call (and in fact before argument + relocating. */ + final_scan_insn (NEXT_INSN (insn), asm_out_file, optimize, 0, 0); + + /* Now delete the delay insn. */ + PUT_CODE (NEXT_INSN (insn), NOTE); + NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0; + delay_insn_deleted = 1; + } + + /* Now copy any FP arguments into integer registers. */ + for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1)) + { + int arg_mode, regno; + rtx use = XEXP (link, 0); + if (! (GET_CODE (use) == USE + && GET_CODE (XEXP (use, 0)) == REG + && FUNCTION_ARG_REGNO_P (REGNO (XEXP (use, 0))))) + continue; + + arg_mode = GET_MODE (XEXP (use, 0)); + regno = REGNO (XEXP (use, 0)); + /* Is it a floating point register? */ + if (regno >= 32 && regno <= 39) + { + /* Copy from the FP register into an integer register + (via memory). */ + if (arg_mode == SFmode) + { + xoperands[0] = XEXP (use, 0); + xoperands[1] = gen_rtx_REG (SImode, 26 - (regno - 32) / 2); + output_asm_insn ("fstws %0,-16(%%sr0,%%r30)", xoperands); + output_asm_insn ("ldw -16(%%sr0,%%r30),%1", xoperands); + } + else + { + xoperands[0] = XEXP (use, 0); + xoperands[1] = gen_rtx_REG (DImode, 25 - (regno - 34) / 2); + output_asm_insn ("fstds %0,-16(%%sr0,%%r30)", xoperands); + output_asm_insn ("ldw -12(%%sr0,%%r30),%R1", xoperands); + output_asm_insn ("ldw -16(%%sr0,%%r30),%1", xoperands); + } + } + } + + /* Don't have to worry about TARGET_PORTABLE_RUNTIME here since + we don't have any direct calls in that case. */ + { + int i; + char *name = XSTR (call_dest, 0); + + /* See if we have already put this function on the list + of deferred plabels. This list is generally small, + so a liner search is not too ugly. If it proves too + slow replace it with something faster. */ + for (i = 0; i < n_deferred_plabels; i++) + if (strcmp (name, deferred_plabels[i].name) == 0) + break; + + /* If the deferred plabel list is empty, or this entry was + not found on the list, create a new entry on the list. */ + if (deferred_plabels == NULL || i == n_deferred_plabels) + { + struct obstack *ambient_obstack = current_obstack; + struct obstack *ambient_rtl_obstack = rtl_obstack; + char *real_name; + + /* Any RTL we create here needs to live until the end of + the compilation unit and therefore must live on the + permanent obstack. */ + current_obstack = &permanent_obstack; + rtl_obstack = &permanent_obstack; + + if (deferred_plabels == 0) + deferred_plabels = (struct deferred_plabel *) + xmalloc (1 * sizeof (struct deferred_plabel)); + else + deferred_plabels = (struct deferred_plabel *) + xrealloc (deferred_plabels, + ((n_deferred_plabels + 1) + * sizeof (struct deferred_plabel))); + + i = n_deferred_plabels++; + deferred_plabels[i].internal_label = gen_label_rtx (); + deferred_plabels[i].name = obstack_alloc (&permanent_obstack, + strlen (name) + 1); + strcpy (deferred_plabels[i].name, name); + + /* Switch back to normal obstack allocation. */ + current_obstack = ambient_obstack; + rtl_obstack = ambient_rtl_obstack; + + /* Gross. We have just implicitly taken the address of this + function, mark it as such. */ + STRIP_NAME_ENCODING (real_name, name); + TREE_SYMBOL_REFERENCED (get_identifier (real_name)) = 1; + } + + /* We have to load the address of the function using a procedure + label (plabel). Inline plabels can lose for PIC and other + cases, so avoid them by creating a 32bit plabel in the data + segment. */ + if (flag_pic) + { + xoperands[0] = deferred_plabels[i].internal_label; + xoperands[1] = gen_label_rtx (); + + output_asm_insn ("addil LT%%%0,%%r19", xoperands); + output_asm_insn ("ldw RT%%%0(%%r1),%%r22", xoperands); + output_asm_insn ("ldw 0(0,%%r22),%%r22", xoperands); + + /* Get our address + 8 into %r1. */ + output_asm_insn ("bl .+8,%%r1", xoperands); + + /* Add %r1 to the offset of dyncall from the next insn. */ + output_asm_insn ("addil L%%$$dyncall-%1,%%r1", xoperands); + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", + CODE_LABEL_NUMBER (xoperands[1])); + output_asm_insn ("ldo R%%$$dyncall-%1(%%r1),%%r1", xoperands); + + /* Get the return address into %r31. */ + output_asm_insn ("blr 0,%%r31", xoperands); + + /* Branch to our target which is in %r1. */ + output_asm_insn ("bv 0(%%r1)", xoperands); + + /* Copy the return address into %r2 also. */ + output_asm_insn ("copy %%r31,%%r2", xoperands); + } + else + { + xoperands[0] = deferred_plabels[i].internal_label; + + /* Get the address of our target into %r22. */ + output_asm_insn ("addil LR%%%0-$global$,%%r27", xoperands); + output_asm_insn ("ldw RR%%%0-$global$(%%r1),%%r22", xoperands); + + /* Get the high part of the address of $dyncall into %r2, then + add in the low part in the branch instruction. */ + output_asm_insn ("ldil L%%$$dyncall,%%r2", xoperands); + output_asm_insn ("ble R%%$$dyncall(%%sr4,%%r2)", xoperands); + + /* Copy the return pointer into both %r31 and %r2. */ + output_asm_insn ("copy %%r31,%%r2", xoperands); + } + } + + /* If we had a jump in the call's delay slot, output it now. */ + if (dbr_sequence_length () != 0 + && !delay_insn_deleted) + { + xoperands[0] = XEXP (PATTERN (NEXT_INSN (insn)), 1); + output_asm_insn ("b,n %0", xoperands); + + /* Now delete the delay insn. */ + PUT_CODE (NEXT_INSN (insn), NOTE); + NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0; + } + return ""; + } + + /* This call has an unconditional jump in its delay slot and the + call is known to reach its target or the beginning of the current + subspace. */ + + /* Use the containing sequence insn's address. */ + seq_insn = NEXT_INSN (PREV_INSN (XVECEXP (final_sequence, 0, 0))); + + distance = insn_addresses[INSN_UID (JUMP_LABEL (NEXT_INSN (insn)))] + - insn_addresses[INSN_UID (seq_insn)] - 8; + + /* If the branch was too far away, emit a normal call followed + by a nop, followed by the unconditional branch. + + If the branch is close, then adjust %r2 from within the + call's delay slot. */ + + xoperands[0] = call_dest; + xoperands[1] = XEXP (PATTERN (NEXT_INSN (insn)), 1); + if (! VAL_14_BITS_P (distance)) + output_asm_insn ("bl %0,%%r2\n\tnop\n\tbl,n %1,%%r0", xoperands); + else + { + xoperands[3] = gen_label_rtx (); + output_asm_insn ("\n\tbl %0,%%r2\n\tldo %1-%3(%%r2),%%r2", xoperands); + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", + CODE_LABEL_NUMBER (xoperands[3])); + } + + /* Delete the jump. */ + PUT_CODE (NEXT_INSN (insn), NOTE); + NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0; + return ""; +} + +/* In HPUX 8.0's shared library scheme, special relocations are needed + for function labels if they might be passed to a function + in a shared library (because shared libraries don't live in code + space), and special magic is needed to construct their address. + + For reasons too disgusting to describe storage for the new name + is allocated either on the saveable_obstack (released at function + exit) or on the permanent_obstack for things that can never change + (libcall names for example). */ + +void +hppa_encode_label (sym, permanent) + rtx sym; + int permanent; +{ + char *str = XSTR (sym, 0); + int len = strlen (str); + char *newstr; + + newstr = obstack_alloc ((permanent ? &permanent_obstack : saveable_obstack), + len + 2); + + if (str[0] == '*') + *newstr++ = *str++; + strcpy (newstr + 1, str); + *newstr = '@'; + XSTR (sym,0) = newstr; +} + +int +function_label_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return GET_CODE (op) == SYMBOL_REF && FUNCTION_NAME_P (XSTR (op, 0)); +} + +/* Returns 1 if OP is a function label involved in a simple addition + with a constant. Used to keep certain patterns from matching + during instruction combination. */ +int +is_function_label_plus_const (op) + rtx op; +{ + /* Strip off any CONST. */ + if (GET_CODE (op) == CONST) + op = XEXP (op, 0); + + return (GET_CODE (op) == PLUS + && function_label_operand (XEXP (op, 0), Pmode) + && GET_CODE (XEXP (op, 1)) == CONST_INT); +} + +/* Returns 1 if the 6 operands specified in OPERANDS are suitable for + use in fmpyadd instructions. */ +int +fmpyaddoperands (operands) + rtx *operands; +{ + enum machine_mode mode = GET_MODE (operands[0]); + + /* Must be a floating point mode. */ + if (mode != SFmode && mode != DFmode) + return 0; + + /* All modes must be the same. */ + if (! (mode == GET_MODE (operands[1]) + && mode == GET_MODE (operands[2]) + && mode == GET_MODE (operands[3]) + && mode == GET_MODE (operands[4]) + && mode == GET_MODE (operands[5]))) + return 0; + + /* All operands must be registers. */ + if (! (GET_CODE (operands[1]) == REG + && GET_CODE (operands[2]) == REG + && GET_CODE (operands[3]) == REG + && GET_CODE (operands[4]) == REG + && GET_CODE (operands[5]) == REG)) + return 0; + + /* Only 2 real operands to the addition. One of the input operands must + be the same as the output operand. */ + if (! rtx_equal_p (operands[3], operands[4]) + && ! rtx_equal_p (operands[3], operands[5])) + return 0; + + /* Inout operand of add can not conflict with any operands from multiply. */ + if (rtx_equal_p (operands[3], operands[0]) + || rtx_equal_p (operands[3], operands[1]) + || rtx_equal_p (operands[3], operands[2])) + return 0; + + /* multiply can not feed into addition operands. */ + if (rtx_equal_p (operands[4], operands[0]) + || rtx_equal_p (operands[5], operands[0])) + return 0; + + /* SFmode limits the registers to the upper 32 of the 32bit FP regs. */ + if (mode == SFmode + && (REGNO (operands[0]) < 57 + || REGNO (operands[1]) < 57 + || REGNO (operands[2]) < 57 + || REGNO (operands[3]) < 57 + || REGNO (operands[4]) < 57 + || REGNO (operands[5]) < 57)) + return 0; + + /* Passed. Operands are suitable for fmpyadd. */ + return 1; +} + +/* Returns 1 if the 6 operands specified in OPERANDS are suitable for + use in fmpysub instructions. */ +int +fmpysuboperands (operands) + rtx *operands; +{ + enum machine_mode mode = GET_MODE (operands[0]); + + /* Must be a floating point mode. */ + if (mode != SFmode && mode != DFmode) + return 0; + + /* All modes must be the same. */ + if (! (mode == GET_MODE (operands[1]) + && mode == GET_MODE (operands[2]) + && mode == GET_MODE (operands[3]) + && mode == GET_MODE (operands[4]) + && mode == GET_MODE (operands[5]))) + return 0; + + /* All operands must be registers. */ + if (! (GET_CODE (operands[1]) == REG + && GET_CODE (operands[2]) == REG + && GET_CODE (operands[3]) == REG + && GET_CODE (operands[4]) == REG + && GET_CODE (operands[5]) == REG)) + return 0; + + /* Only 2 real operands to the subtraction. Subtraction is not a commutative + operation, so operands[4] must be the same as operand[3]. */ + if (! rtx_equal_p (operands[3], operands[4])) + return 0; + + /* multiply can not feed into subtraction. */ + if (rtx_equal_p (operands[5], operands[0])) + return 0; + + /* Inout operand of sub can not conflict with any operands from multiply. */ + if (rtx_equal_p (operands[3], operands[0]) + || rtx_equal_p (operands[3], operands[1]) + || rtx_equal_p (operands[3], operands[2])) + return 0; + + /* SFmode limits the registers to the upper 32 of the 32bit FP regs. */ + if (mode == SFmode + && (REGNO (operands[0]) < 57 + || REGNO (operands[1]) < 57 + || REGNO (operands[2]) < 57 + || REGNO (operands[3]) < 57 + || REGNO (operands[4]) < 57 + || REGNO (operands[5]) < 57)) + return 0; + + /* Passed. Operands are suitable for fmpysub. */ + return 1; +} + +int +plus_xor_ior_operator (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return (GET_CODE (op) == PLUS || GET_CODE (op) == XOR + || GET_CODE (op) == IOR); +} + +/* Return 1 if the given constant is 2, 4, or 8. These are the valid + constants for shadd instructions. */ +static int +shadd_constant_p (val) + int val; +{ + if (val == 2 || val == 4 || val == 8) + return 1; + else + return 0; +} + +/* Return 1 if OP is a CONST_INT with the value 2, 4, or 8. These are + the valid constant for shadd instructions. */ +int +shadd_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return (GET_CODE (op) == CONST_INT && shadd_constant_p (INTVAL (op))); +} + +/* Return 1 if OP is valid as a base register in a reg + reg address. */ + +int +basereg_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + /* cse will create some unscaled indexed addresses, however; it + generally isn't a win on the PA, so avoid creating unscaled + indexed addresses until after cse is finished. */ + if (!cse_not_expected) + return 0; + + /* Once reload has started everything is considered valid. Reload should + only create indexed addresses using the stack/frame pointer, and any + others were checked for validity when created by the combine pass. + + Also allow any register when TARGET_NO_SPACE_REGS is in effect since + we don't have to worry about the braindamaged implicit space register + selection using the basereg only (rather than effective address) + screwing us over. */ + if (TARGET_NO_SPACE_REGS || reload_in_progress || reload_completed) + return (GET_CODE (op) == REG); + + /* Stack is always OK for indexing. */ + if (op == stack_pointer_rtx) + return 1; + + /* While it's always safe to index off the frame pointer, it's not + always profitable, particularly when the frame pointer is being + eliminated. */ + if (! flag_omit_frame_pointer && op == frame_pointer_rtx) + return 1; + + /* The only other valid OPs are pseudo registers with + REGNO_POINTER_FLAG set. */ + if (GET_CODE (op) != REG + || REGNO (op) < FIRST_PSEUDO_REGISTER + || ! register_operand (op, mode)) + return 0; + + return REGNO_POINTER_FLAG (REGNO (op)); +} + +/* Return 1 if this operand is anything other than a hard register. */ + +int +non_hard_reg_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return ! (GET_CODE (op) == REG && REGNO (op) < FIRST_PSEUDO_REGISTER); +} + +/* Return 1 if INSN branches forward. Should be using insn_addresses + to avoid walking through all the insns... */ +static int +forward_branch_p (insn) + rtx insn; +{ + rtx label = JUMP_LABEL (insn); + + while (insn) + { + if (insn == label) + break; + else + insn = NEXT_INSN (insn); + } + + return (insn == label); +} + +/* Return 1 if OP is an equality comparison, else return 0. */ +int +eq_neq_comparison_operator (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return (GET_CODE (op) == EQ || GET_CODE (op) == NE); +} + +/* Return 1 if OP is an operator suitable for use in a movb instruction. */ +int +movb_comparison_operator (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return (GET_CODE (op) == EQ || GET_CODE (op) == NE + || GET_CODE (op) == LT || GET_CODE (op) == GE); +} + +/* Return 1 if INSN is in the delay slot of a call instruction. */ +int +jump_in_call_delay (insn) + rtx insn; +{ + + if (GET_CODE (insn) != JUMP_INSN) + return 0; + + if (PREV_INSN (insn) + && PREV_INSN (PREV_INSN (insn)) + && GET_CODE (next_active_insn (PREV_INSN (PREV_INSN (insn)))) == INSN) + { + rtx test_insn = next_active_insn (PREV_INSN (PREV_INSN (insn))); + + return (GET_CODE (PATTERN (test_insn)) == SEQUENCE + && XVECEXP (PATTERN (test_insn), 0, 1) == insn); + + } + else + return 0; +} + +/* Output an unconditional move and branch insn. */ + +char * +output_parallel_movb (operands, length) + rtx *operands; + int length; +{ + /* These are the cases in which we win. */ + if (length == 4) + return "mov%I1b,tr %1,%0,%2"; + + /* None of these cases wins, but they don't lose either. */ + if (dbr_sequence_length () == 0) + { + /* Nothing in the delay slot, fake it by putting the combined + insn (the copy or add) in the delay slot of a bl. */ + if (GET_CODE (operands[1]) == CONST_INT) + return "bl %2,0\n\tldi %1,%0"; + else + return "bl %2,0\n\tcopy %1,%0"; + } + else + { + /* Something in the delay slot, but we've got a long branch. */ + if (GET_CODE (operands[1]) == CONST_INT) + return "ldi %1,%0\n\tbl %2,0"; + else + return "copy %1,%0\n\tbl %2,0"; + } +} + +/* Output an unconditional add and branch insn. */ + +char * +output_parallel_addb (operands, length) + rtx *operands; + int length; +{ + /* To make life easy we want operand0 to be the shared input/output + operand and operand1 to be the readonly operand. */ + if (operands[0] == operands[1]) + operands[1] = operands[2]; + + /* These are the cases in which we win. */ + if (length == 4) + return "add%I1b,tr %1,%0,%3"; + + /* None of these cases win, but they don't lose either. */ + if (dbr_sequence_length () == 0) + { + /* Nothing in the delay slot, fake it by putting the combined + insn (the copy or add) in the delay slot of a bl. */ + return "bl %3,0\n\tadd%I1 %1,%0,%0"; + } + else + { + /* Something in the delay slot, but we've got a long branch. */ + return "add%I1 %1,%0,%0\n\tbl %3,0"; + } +} + +/* Return nonzero if INSN (a jump insn) immediately follows a call to + a named function. This is used to discourage creating parallel movb/addb + insns since a jump which immediately follows a call can execute in the + delay slot of the call. + + It is also used to avoid filling the delay slot of a jump which + immediately follows a call since the jump can usually be eliminated + completely by modifying RP in the delay slot of the call. */ + +int +following_call (insn) + rtx insn; +{ + /* CYGNUS LOCAL PA8000/law */ + /* We do not parallel movb,addb or place jumps into call delay slots when + optimizing for the PA8000. */ + if (pa_cpu != PROCESSOR_8000) + return 0; + /* END CYGNUS LOCAL */ + + /* Find the previous real insn, skipping NOTEs. */ + insn = PREV_INSN (insn); + while (insn && GET_CODE (insn) == NOTE) + insn = PREV_INSN (insn); + + /* Check for CALL_INSNs and millicode calls. */ + if (insn + && ((GET_CODE (insn) == CALL_INSN + && get_attr_type (insn) != TYPE_DYNCALL) + || (GET_CODE (insn) == INSN + && GET_CODE (PATTERN (insn)) != SEQUENCE + && GET_CODE (PATTERN (insn)) != USE + && GET_CODE (PATTERN (insn)) != CLOBBER + && get_attr_type (insn) == TYPE_MILLI))) + return 1; + + return 0; +} + +/* Restore any INSN_CODEs for insns with unscaled indexed addresses since + the INSN_CODE might be clobberd by rerecognition triggered by reorg. */ + +static void +restore_unscaled_index_insn_codes (insns) + rtx insns; +{ + rtx insn; + + for (insn = insns; insn; insn = NEXT_INSN (insn)) + { + if (INSN_UID (insn) < max_unscaled_index_insn_codes_uid + && unscaled_index_insn_codes[INSN_UID (insn)] != -1) + INSN_CODE (insn) = unscaled_index_insn_codes[INSN_UID (insn)]; + } +} + +/* Severe braindamage: + + On the PA, address computations within MEM expressions are not + commutative because of the implicit space register selection + from the base register (instead of the entire effective address). + + Because of this mis-feature we have to know which register in a reg+reg + address is the base and which is the index. + + Before reload, the base can be identified by REGNO_POINTER_FLAG. We use + this to force base + index addresses to match a different insn than + index + base addresses. + + We assume that no pass during or after reload creates new unscaled indexed + addresses, so any unscaled indexed address we find after reload must have + at one time been recognized a base + index or index + base and we accept + any register as a base register. + + This scheme assumes that no pass during/after reload will rerecognize an + insn with an unscaled indexed address. This failed due to a reorg call + to rerecognize certain insns. + + So, we record if an insn uses an unscaled indexed address and which + register is the base (via recording of the INSN_CODE for such insns). + + Just before we output code for the function, we make sure all the insns + using unscaled indexed addresses have the same INSN_CODE as they did + immediately before delay slot scheduling. + + This is extremely gross. Long term, I'd like to be able to look at + REG_POINTER_FLAG to handle these kinds of problems. */ + +static void +record_unscaled_index_insn_codes (insns) + rtx insns; +{ + rtx insn; + + max_unscaled_index_insn_codes_uid = get_max_uid (); + unscaled_index_insn_codes + = (int *)xmalloc (max_unscaled_index_insn_codes_uid * sizeof (int)); + memset (unscaled_index_insn_codes, -1, + max_unscaled_index_insn_codes_uid * sizeof (int)); + + for (insn = insns; insn; insn = NEXT_INSN (insn)) + { + rtx set = single_set (insn); + rtx mem = NULL_RTX; + + /* Ignore anything that isn't a normal SET. */ + if (set == NULL_RTX) + continue; + + /* No insns can have more than one MEM. */ + if (GET_CODE (SET_SRC (set)) == MEM) + mem = SET_SRC (set); + + if (GET_CODE (SET_DEST (set)) == MEM) + mem = SET_DEST (set); + + /* If neither operand is a mem, then there's nothing to do. */ + if (mem == NULL_RTX) + continue; + + if (GET_CODE (XEXP (mem, 0)) != PLUS) + continue; + + /* If both are REGs (or SUBREGs), then record the insn code for + this insn. */ + if (REG_P (XEXP (XEXP (mem, 0), 0)) && REG_P (XEXP (XEXP (mem, 0), 1))) + unscaled_index_insn_codes[INSN_UID (insn)] = INSN_CODE (insn); + } +} + +/* We use this hook to perform a PA specific optimization which is difficult + to do in earlier passes. + + We want the delay slots of branches within jump tables to be filled. + None of the compiler passes at the moment even has the notion that a + PA jump table doesn't contain addresses, but instead contains actual + instructions! + + Because we actually jump into the table, the addresses of each entry + must stay constant in relation to the beginning of the table (which + itself must stay constant relative to the instruction to jump into + it). I don't believe we can guarantee earlier passes of the compiler + will adhere to those rules. + + So, late in the compilation process we find all the jump tables, and + expand them into real code -- eg each entry in the jump table vector + will get an appropriate label followed by a jump to the final target. + + Reorg and the final jump pass can then optimize these branches and + fill their delay slots. We end up with smaller, more efficient code. + + The jump instructions within the table are special; we must be able + to identify them during assembly output (if the jumps don't get filled + we need to emit a nop rather than nullifying the delay slot)). We + identify jumps in switch tables by marking the SET with DImode. + + We also surround the jump table itself with BEGIN_BRTAB and END_BRTAB + insns. This serves two purposes, first it prevents jump.c from + noticing that the last N entries in the table jump to the instruction + immediately after the table and deleting the jumps. Second, those + insns mark where we should emit .begin_brtab and .end_brtab directives + when using GAS (allows for better link time optimizations). */ + +void +pa_reorg (insns) + rtx insns; +{ + rtx insn; + + /* Keep track of which insns have unscaled indexed addresses, and which + register is the base address in such insns. */ + record_unscaled_index_insn_codes (insns); + + remove_useless_addtr_insns (insns, 1); + + /* CYGNUS LOCAL PA8000/law */ + /* These optimizations hurt PA8000 performance. */ + if (pa_cpu != PROCESSOR_8000) + pa_combine_instructions (get_insns ()); + /* END CYGNUS LOCAL */ + + /* This is fairly cheap, so always run it if optimizing. */ + if (optimize > 0 && !TARGET_BIG_SWITCH) + { + /* Find and explode all ADDR_VEC or ADDR_DIFF_VEC insns. */ + insns = get_insns (); + for (insn = insns; insn; insn = NEXT_INSN (insn)) + { + rtx pattern, tmp, location; + unsigned int length, i; + + /* Find an ADDR_VEC or ADDR_DIFF_VEC insn to explode. */ + if (GET_CODE (insn) != JUMP_INSN + || (GET_CODE (PATTERN (insn)) != ADDR_VEC + && GET_CODE (PATTERN (insn)) != ADDR_DIFF_VEC)) + continue; + + /* Emit marker for the beginning of the branch table. */ + emit_insn_before (gen_begin_brtab (), insn); + + pattern = PATTERN (insn); + location = PREV_INSN (insn); + length = XVECLEN (pattern, GET_CODE (pattern) == ADDR_DIFF_VEC); + + for (i = 0; i < length; i++) + { + /* Emit a label before each jump to keep jump.c from + removing this code. */ + tmp = gen_label_rtx (); + LABEL_NUSES (tmp) = 1; + emit_label_after (tmp, location); + location = NEXT_INSN (location); + + if (GET_CODE (pattern) == ADDR_VEC) + { + /* Emit the jump itself. */ + tmp = gen_jump (XEXP (XVECEXP (pattern, 0, i), 0)); + tmp = emit_jump_insn_after (tmp, location); + JUMP_LABEL (tmp) = XEXP (XVECEXP (pattern, 0, i), 0); + /* It is easy to rely on the branch table markers + during assembly output to trigger the correct code + for a switch table jump with an unfilled delay slot, + + However, that requires state and assumes that we look + at insns in order. + + We can't make such assumptions when computing the length + of instructions. Ugh. We could walk the insn chain to + determine if this instruction is in a branch table, but + that can get rather expensive, particularly during the + branch shortening phase of the compiler. + + So instead we mark this jump as being special. This is + far from ideal and knows that no code after this will + muck around with the mode of the JUMP_INSN itself. */ + PUT_MODE (tmp, SImode); + LABEL_NUSES (JUMP_LABEL (tmp))++; + location = NEXT_INSN (location); + } + else + { + /* Emit the jump itself. */ + tmp = gen_jump (XEXP (XVECEXP (pattern, 1, i), 0)); + tmp = emit_jump_insn_after (tmp, location); + JUMP_LABEL (tmp) = XEXP (XVECEXP (pattern, 1, i), 0); + /* It is easy to rely on the branch table markers + during assembly output to trigger the correct code + for a switch table jump with an unfilled delay slot, + + However, that requires state and assumes that we look + at insns in order. + + We can't make such assumptions when computing the length + of instructions. Ugh. We could walk the insn chain to + determine if this instruction is in a branch table, but + that can get rather expensive, particularly during the + branch shortening phase of the compiler. + + So instead we mark this jump as being special. This is + far from ideal and knows that no code after this will + muck around with the mode of the JUMP_INSN itself. */ + PUT_MODE (tmp, SImode); + LABEL_NUSES (JUMP_LABEL (tmp))++; + location = NEXT_INSN (location); + } + + /* Emit a BARRIER after the jump. */ + emit_barrier_after (location); + location = NEXT_INSN (location); + } + + /* Emit marker for the end of the branch table. */ + emit_insn_before (gen_end_brtab (), location); + location = NEXT_INSN (location); + emit_barrier_after (location); + + /* Delete the ADDR_VEC or ADDR_DIFF_VEC. */ + delete_insn (insn); + } + } + else + { + /* Sill need an end_brtab insn. */ + insns = get_insns (); + for (insn = insns; insn; insn = NEXT_INSN (insn)) + { + /* Find an ADDR_VEC insn. */ + if (GET_CODE (insn) != JUMP_INSN + || (GET_CODE (PATTERN (insn)) != ADDR_VEC + && GET_CODE (PATTERN (insn)) != ADDR_DIFF_VEC)) + continue; + + /* Now generate markers for the beginning and end of the + branch table. */ + emit_insn_before (gen_begin_brtab (), insn); + emit_insn_after (gen_end_brtab (), insn); + } + } +} + +/* The PA has a number of odd instructions which can perform multiple + tasks at once. On first generation PA machines (PA1.0 and PA1.1) + it may be profitable to combine two instructions into one instruction + with two outputs. It's not profitable PA2.0 machines because the + two outputs would take two slots in the reorder buffers. + + This routine finds instructions which can be combined and combines + them. We only support some of the potential combinations, and we + only try common ways to find suitable instructions. + + * addb can add two registers or a register and a small integer + and jump to a nearby (+-8k) location. Normally the jump to the + nearby location is conditional on the result of the add, but by + using the "true" condition we can make the jump unconditional. + Thus addb can perform two independent operations in one insn. + + * movb is similar to addb in that it can perform a reg->reg + or small immediate->reg copy and jump to a nearby (+-8k location). + + * fmpyadd and fmpysub can perform a FP multiply and either an + FP add or FP sub if the operands of the multiply and add/sub are + independent (there are other minor restrictions). Note both + the fmpy and fadd/fsub can in theory move to better spots according + to data dependencies, but for now we require the fmpy stay at a + fixed location. + + * Many of the memory operations can perform pre & post updates + of index registers. GCC's pre/post increment/decrement addressing + is far too simple to take advantage of all the possibilities. This + pass may not be suitable since those insns may not be independent. + + * comclr can compare two ints or an int and a register, nullify + the following instruction and zero some other register. This + is more difficult to use as it's harder to find an insn which + will generate a comclr than finding something like an unconditional + branch. (conditional moves & long branches create comclr insns). + + * Most arithmetic operations can conditionally skip the next + instruction. They can be viewed as "perform this operation + and conditionally jump to this nearby location" (where nearby + is an insns away). These are difficult to use due to the + branch length restrictions. */ + +static void +pa_combine_instructions (insns) + rtx insns ATTRIBUTE_UNUSED; +{ + rtx anchor, new; + + /* This can get expensive since the basic algorithm is on the + order of O(n^2) (or worse). Only do it for -O2 or higher + levels of optimization. */ + if (optimize < 2) + return; + + /* Walk down the list of insns looking for "anchor" insns which + may be combined with "floating" insns. As the name implies, + "anchor" instructions don't move, while "floating" insns may + move around. */ + new = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, NULL_RTX, NULL_RTX)); + new = make_insn_raw (new); + + for (anchor = get_insns (); anchor; anchor = NEXT_INSN (anchor)) + { + enum attr_pa_combine_type anchor_attr; + enum attr_pa_combine_type floater_attr; + + /* We only care about INSNs, JUMP_INSNs, and CALL_INSNs. + Also ignore any special USE insns. */ + if ((GET_CODE (anchor) != INSN + && GET_CODE (anchor) != JUMP_INSN + && GET_CODE (anchor) != CALL_INSN) + || GET_CODE (PATTERN (anchor)) == USE + || GET_CODE (PATTERN (anchor)) == CLOBBER + || GET_CODE (PATTERN (anchor)) == ADDR_VEC + || GET_CODE (PATTERN (anchor)) == ADDR_DIFF_VEC) + continue; + + anchor_attr = get_attr_pa_combine_type (anchor); + /* See if anchor is an insn suitable for combination. */ + if (anchor_attr == PA_COMBINE_TYPE_FMPY + || anchor_attr == PA_COMBINE_TYPE_FADDSUB + || (anchor_attr == PA_COMBINE_TYPE_UNCOND_BRANCH + && ! forward_branch_p (anchor))) + { + rtx floater; + + for (floater = PREV_INSN (anchor); + floater; + floater = PREV_INSN (floater)) + { + if (GET_CODE (floater) == NOTE + || (GET_CODE (floater) == INSN + && (GET_CODE (PATTERN (floater)) == USE + || GET_CODE (PATTERN (floater)) == CLOBBER))) + continue; + + /* Anything except a regular INSN will stop our search. */ + if (GET_CODE (floater) != INSN + || GET_CODE (PATTERN (floater)) == ADDR_VEC + || GET_CODE (PATTERN (floater)) == ADDR_DIFF_VEC) + { + floater = NULL_RTX; + break; + } + + /* See if FLOATER is suitable for combination with the + anchor. */ + floater_attr = get_attr_pa_combine_type (floater); + if ((anchor_attr == PA_COMBINE_TYPE_FMPY + && floater_attr == PA_COMBINE_TYPE_FADDSUB) + || (anchor_attr == PA_COMBINE_TYPE_FADDSUB + && floater_attr == PA_COMBINE_TYPE_FMPY)) + { + /* If ANCHOR and FLOATER can be combined, then we're + done with this pass. */ + if (pa_can_combine_p (new, anchor, floater, 0, + SET_DEST (PATTERN (floater)), + XEXP (SET_SRC (PATTERN (floater)), 0), + XEXP (SET_SRC (PATTERN (floater)), 1))) + break; + } + + else if (anchor_attr == PA_COMBINE_TYPE_UNCOND_BRANCH + && floater_attr == PA_COMBINE_TYPE_ADDMOVE) + { + if (GET_CODE (SET_SRC (PATTERN (floater))) == PLUS) + { + if (pa_can_combine_p (new, anchor, floater, 0, + SET_DEST (PATTERN (floater)), + XEXP (SET_SRC (PATTERN (floater)), 0), + XEXP (SET_SRC (PATTERN (floater)), 1))) + break; + } + else + { + if (pa_can_combine_p (new, anchor, floater, 0, + SET_DEST (PATTERN (floater)), + SET_SRC (PATTERN (floater)), + SET_SRC (PATTERN (floater)))) + break; + } + } + } + + /* If we didn't find anything on the backwards scan try forwards. */ + if (!floater + && (anchor_attr == PA_COMBINE_TYPE_FMPY + || anchor_attr == PA_COMBINE_TYPE_FADDSUB)) + { + for (floater = anchor; floater; floater = NEXT_INSN (floater)) + { + if (GET_CODE (floater) == NOTE + || (GET_CODE (floater) == INSN + && (GET_CODE (PATTERN (floater)) == USE + || GET_CODE (PATTERN (floater)) == CLOBBER))) + + continue; + + /* Anything except a regular INSN will stop our search. */ + if (GET_CODE (floater) != INSN + || GET_CODE (PATTERN (floater)) == ADDR_VEC + || GET_CODE (PATTERN (floater)) == ADDR_DIFF_VEC) + { + floater = NULL_RTX; + break; + } + + /* See if FLOATER is suitable for combination with the + anchor. */ + floater_attr = get_attr_pa_combine_type (floater); + if ((anchor_attr == PA_COMBINE_TYPE_FMPY + && floater_attr == PA_COMBINE_TYPE_FADDSUB) + || (anchor_attr == PA_COMBINE_TYPE_FADDSUB + && floater_attr == PA_COMBINE_TYPE_FMPY)) + { + /* If ANCHOR and FLOATER can be combined, then we're + done with this pass. */ + if (pa_can_combine_p (new, anchor, floater, 1, + SET_DEST (PATTERN (floater)), + XEXP (SET_SRC (PATTERN(floater)),0), + XEXP(SET_SRC(PATTERN(floater)),1))) + break; + } + } + } + + /* FLOATER will be nonzero if we found a suitable floating + insn for combination with ANCHOR. */ + if (floater + && (anchor_attr == PA_COMBINE_TYPE_FADDSUB + || anchor_attr == PA_COMBINE_TYPE_FMPY)) + { + /* Emit the new instruction and delete the old anchor. */ + emit_insn_before (gen_rtx_PARALLEL (VOIDmode, + gen_rtvec (2, + PATTERN (anchor), + PATTERN (floater))), + anchor); + PUT_CODE (anchor, NOTE); + NOTE_LINE_NUMBER (anchor) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (anchor) = 0; + + /* Emit a special USE insn for FLOATER, then delete + the floating insn. */ + emit_insn_before (gen_rtx_USE (VOIDmode, floater), floater); + delete_insn (floater); + + continue; + } + else if (floater + && anchor_attr == PA_COMBINE_TYPE_UNCOND_BRANCH) + { + rtx temp; + /* Emit the new_jump instruction and delete the old anchor. */ + temp = emit_jump_insn_before (gen_rtx_PARALLEL (VOIDmode, + gen_rtvec (2, PATTERN (anchor), + PATTERN (floater))), + anchor); + JUMP_LABEL (temp) = JUMP_LABEL (anchor); + PUT_CODE (anchor, NOTE); + NOTE_LINE_NUMBER (anchor) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (anchor) = 0; + + /* Emit a special USE insn for FLOATER, then delete + the floating insn. */ + emit_insn_before (gen_rtx_USE (VOIDmode, floater), floater); + delete_insn (floater); + continue; + } + } + } +} + +int +pa_can_combine_p (new, anchor, floater, reversed, dest, src1, src2) + rtx new, anchor, floater; + int reversed; + rtx dest, src1, src2; +{ + int insn_code_number; + rtx start, end; + + /* Create a PARALLEL with the patterns of ANCHOR and + FLOATER, try to recognize it, then test constraints + for the resulting pattern. + + If the pattern doesn't match or the constraints + aren't met keep searching for a suitable floater + insn. */ + XVECEXP (PATTERN (new), 0, 0) = PATTERN (anchor); + XVECEXP (PATTERN (new), 0, 1) = PATTERN (floater); + INSN_CODE (new) = -1; + insn_code_number = recog_memoized (new); + if (insn_code_number < 0 + || !constrain_operands (insn_code_number, 1)) + return 0; + + if (reversed) + { + start = anchor; + end = floater; + } + else + { + start = floater; + end = anchor; + } + + /* There's up to three operands to consider. One + output and two inputs. + + The output must not be used between FLOATER & ANCHOR + exclusive. The inputs must not be set between + FLOATER and ANCHOR exclusive. */ + + if (reg_used_between_p (dest, start, end)) + return 0; + + if (reg_set_between_p (src1, start, end)) + return 0; + + if (reg_set_between_p (src2, start, end)) + return 0; + + /* If we get here, then everything is good. */ + return 1; +} + +/* Return nonzero if sets and references for INSN are delayed. + + Millicode insns are actually function calls with some special + constraints on arguments and register usage. + + Millicode calls always expect their arguments in the integer argument + registers, and always return their result in %r29 (ret1). They + are expected to clobber their arguments, %r1, %r29, and %r31 and + nothing else. + + By considering this effects delayed reorg reorg can put insns + which set the argument registers into the delay slot of the millicode + call -- thus they act more like traditional CALL_INSNs. + + get_attr_type will try to recognize the given insn, so make sure to + filter out things it will not accept -- SEQUENCE, USE and CLOBBER insns + in particular. */ +int +insn_sets_and_refs_are_delayed (insn) + rtx insn; +{ + return ((GET_CODE (insn) == INSN + && GET_CODE (PATTERN (insn)) != SEQUENCE + && GET_CODE (PATTERN (insn)) != USE + && GET_CODE (PATTERN (insn)) != CLOBBER + && get_attr_type (insn) == TYPE_MILLI)); +} diff --git a/gcc/config/pa/pa.h b/gcc/config/pa/pa.h new file mode 100755 index 0000000..a63d955 --- /dev/null +++ b/gcc/config/pa/pa.h @@ -0,0 +1,2601 @@ +/* Definitions of target machine for GNU compiler, for the HP Spectrum. + Copyright (C) 1992, 93-98, 1999 Free Software Foundation, Inc. + Contributed by Michael Tiemann (tiemann@cygnus.com) of Cygnus Support + and Tim Moore (moore@defmacro.cs.utah.edu) of the Center for + Software Science at the University of Utah. + +This file is part of GNU CC. + +GNU CC 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 1, or (at your option) +any later version. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +enum cmp_type /* comparison type */ +{ + CMP_SI, /* compare integers */ + CMP_SF, /* compare single precision floats */ + CMP_DF, /* compare double precision floats */ + CMP_MAX /* max comparison type */ +}; + +/* For long call handling. */ +extern unsigned int total_code_bytes; + +/* Which processor to schedule for. */ + +enum processor_type +{ + PROCESSOR_700, + PROCESSOR_7100, + PROCESSOR_7100LC, + PROCESSOR_7200, + /* CYGNUS LOCAL PA8000/law */ + PROCESSOR_8000 + /* END CYGNUS LCOAL */ +}; + +/* For -mschedule= option. */ +extern char *pa_cpu_string; +extern enum processor_type pa_cpu; + +#define pa_cpu_attr ((enum attr_cpu)pa_cpu) + +/* CYGNUS LOCAL PA8000/law */ +/* The 700 can only issue a single insn at a time. + The 7XXX processors can issue two insns at a time. */ +#define ISSUE_RATE \ + (pa_cpu == PROCESSOR_700 ? 1 : pa_cpu == PROCESSOR_8000 ? 4 : 2) +/* END CYGNUS LOCAL */ + +/* Print subsidiary information on the compiler version in use. */ + +#define TARGET_VERSION fputs (" (hppa)", stderr); + +/* Run-time compilation parameters selecting different hardware subsets. */ + +extern int target_flags; + +/* compile code for HP-PA 1.1 ("Snake") */ + +#define TARGET_SNAKE (target_flags & 1) + +/* Disable all FP registers (they all become fixed). This may be necessary + for compiling kernels which perform lazy context switching of FP regs. + Note if you use this option and try to perform floating point operations + the compiler will abort! */ + +#define TARGET_DISABLE_FPREGS (target_flags & 2) + +/* Generate code which assumes that calls through function pointers will + never cross a space boundary. Such assumptions are generally safe for + building kernels and statically linked executables. Code compiled with + this option will fail miserably if the executable is dynamically linked + or uses nested functions! + + This is also used to trigger aggressive unscaled index addressing. */ +#define TARGET_NO_SPACE_REGS (target_flags & 4) + +/* Allow unconditional jumps in the delay slots of call instructions. */ +#define TARGET_JUMP_IN_DELAY (target_flags & 8) + +/* Optimize for space. Currently this only turns on out of line + prologues and epilogues. */ +#define TARGET_SPACE (target_flags & 16) + +/* Disable indexed addressing modes. */ + +#define TARGET_DISABLE_INDEXING (target_flags & 32) + +/* Emit code which follows the new portable runtime calling conventions + HP wants everyone to use for ELF objects. If at all possible you want + to avoid this since it's a performance loss for non-prototyped code. + + Note TARGET_PORTABLE_RUNTIME also forces all calls to use inline + long-call stubs which is quite expensive. */ + +#define TARGET_PORTABLE_RUNTIME (target_flags & 64) + +/* Emit directives only understood by GAS. This allows parameter + relocations to work for static functions. There is no way + to make them work the HP assembler at this time. */ + +#define TARGET_GAS (target_flags & 128) + +/* Emit code for processors which do not have an FPU. */ + +#define TARGET_SOFT_FLOAT (target_flags & 256) + +/* Use 3-insn load/store sequences for access to large data segments + in shared libraries on hpux10. */ +#define TARGET_LONG_LOAD_STORE (target_flags & 512) + +/* Use a faster sequence for indirect calls. */ +#define TARGET_FAST_INDIRECT_CALLS (target_flags & 1024) + +/* Generate code with big switch statements to avoid out of range branches + occurring within the switch table. */ +#define TARGET_BIG_SWITCH (target_flags & 2048) + +/* CYGNUS LOCAL pa8000/law */ +/* Generate PA2.0 instructions. */ +#define TARGET_PARISC_2_0 (target_flags & 4096) +/* END CYGNUS LOCAL */ + +/* Macro to define tables used to set the flags. + This is a list in braces of pairs in braces, + each pair being { "NAME", VALUE } + where VALUE is the bits to set or minus the bits to clear. + An empty string NAME is used to identify the default VALUE. */ + +#define TARGET_SWITCHES \ + {{"snake", 1}, \ + {"nosnake", -1}, \ + {"pa-risc-1-0", -1}, \ + {"pa-risc-1-1", 1}, \ + {"disable-fpregs", 2}, \ + {"no-disable-fpregs", -2}, \ + {"no-space-regs", 4}, \ + {"space-regs", -4}, \ + {"jump-in-delay", 8}, \ + {"no-jump-in-delay", -8}, \ + {"space", 16}, \ + {"no-space", -16}, \ + {"disable-indexing", 32}, \ + {"no-disable-indexing", -32},\ + {"portable-runtime", 64}, \ + {"no-portable-runtime", -64},\ + {"gas", 128}, \ + {"no-gas", -128}, \ + {"soft-float", 256}, \ + {"no-soft-float", -256}, \ + {"long-load-store", 512}, \ + {"no-long-load-store", -512},\ + {"fast-indirect-calls", 1024},\ + {"no-fast-indirect-calls", -1024},\ + {"big-switch", 2048}, \ + {"no-big-switch", -2048}, \ + /* CYGNUS LOCAL pa8000/law */\ + {"pa-risc-2-0", 4097}, \ + {"no-pa-risc-2-0", -4096}, \ + /* END CYGNUS LOCAL */ \ + {"linker-opt", 0}, \ + { "", TARGET_DEFAULT | TARGET_CPU_DEFAULT}} + +#ifndef TARGET_DEFAULT +#define TARGET_DEFAULT 0x88 /* TARGET_GAS + TARGET_JUMP_IN_DELAY */ +#endif + +#ifndef TARGET_CPU_DEFAULT +#define TARGET_CPU_DEFAULT 0 +#endif + +#define TARGET_OPTIONS \ +{ \ + { "schedule=", &pa_cpu_string }\ +} + +#define OVERRIDE_OPTIONS override_options () + +#define DBX_DEBUGGING_INFO +#define DEFAULT_GDB_EXTENSIONS 1 + +/* This is the way other stabs-in-XXX tools do things. We will be + compatible. */ +#define DBX_BLOCKS_FUNCTION_RELATIVE 1 + +/* Likewise for linenos. + + We make the first line stab special to avoid adding several + gross hacks to GAS. */ +#undef ASM_OUTPUT_SOURCE_LINE +#define ASM_OUTPUT_SOURCE_LINE(file, line) \ + { static int sym_lineno = 1; \ + static tree last_function_decl = NULL; \ + if (current_function_decl == last_function_decl) \ + fprintf (file, "\t.stabn 68,0,%d,L$M%d-%s\nL$M%d:\n", \ + line, sym_lineno, \ + XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0) + 1, \ + sym_lineno); \ + else \ + fprintf (file, "\t.stabn 68,0,%d,0\n", line); \ + last_function_decl = current_function_decl; \ + sym_lineno += 1; } + +/* But, to make this work, we have to output the stabs for the function + name *first*... */ +#define DBX_FUNCTION_FIRST + +/* Only labels should ever begin in column zero. */ +#define ASM_STABS_OP "\t.stabs" +#define ASM_STABN_OP "\t.stabn" + +/* GDB always assumes the current function's frame begins at the value + of the stack pointer upon entry to the current function. Accessing + local variables and parameters passed on the stack is done using the + base of the frame + an offset provided by GCC. + + For functions which have frame pointers this method works fine; + the (frame pointer) == (stack pointer at function entry) and GCC provides + an offset relative to the frame pointer. + + This loses for functions without a frame pointer; GCC provides an offset + which is relative to the stack pointer after adjusting for the function's + frame size. GDB would prefer the offset to be relative to the value of + the stack pointer at the function's entry. Yuk! */ +#define DEBUGGER_AUTO_OFFSET(X) \ + ((GET_CODE (X) == PLUS ? INTVAL (XEXP (X, 1)) : 0) \ + + (frame_pointer_needed ? 0 : compute_frame_size (get_frame_size (), 0))) + +#define DEBUGGER_ARG_OFFSET(OFFSET, X) \ + ((GET_CODE (X) == PLUS ? OFFSET : 0) \ + + (frame_pointer_needed ? 0 : compute_frame_size (get_frame_size (), 0))) + +/* gdb needs a null N_SO at the end of each file for scattered loading. */ + +#undef DBX_OUTPUT_MAIN_SOURCE_FILE_END +#define DBX_OUTPUT_MAIN_SOURCE_FILE_END(FILE, FILENAME) \ + text_section (); \ + if (!TARGET_PORTABLE_RUNTIME) \ + fputs ("\t.SPACE $TEXT$\n\t.NSUBSPA $CODE$,QUAD=0,ALIGN=8,ACCESS=44,CODE_ONLY\n", FILE); \ + else \ + fprintf (FILE, "%s\n", TEXT_SECTION_ASM_OP); \ + fprintf (FILE, \ + "\t.stabs \"\",%d,0,0,L$text_end0000\nL$text_end0000:\n", N_SO) + +/* CYGNUS LOCAL hpux11/law */ +#if ((TARGET_DEFAULT | TARGET_CPU_DEFAULT) & 1) == 0 +#define CPP_SPEC "%{msnake:-D__hp9000s700 -D_PA_RISC1_1}\ + %{mpa-risc-1-1:-D__hp9000s700 -D_PA_RISC1_1}\ + %{!ansi: -D_HPUX_SOURCE -D_HIUX_SOURCE -D__STDC_EXT__}\ + %{threads:-D_REENTRANT -D_DCE_THREADS}" +#else +#define CPP_SPEC "%{!mpa-risc-1-0:%{!mnosnake:%{!msoft-float:-D__hp9000s700 -D_PA_RISC1_1}}} \ + %{!ansi: -D_HPUX_SOURCE -D_HIUX_SOURCE -D__STDC_EXT__}\ + %{threads:-D_REENTRANT -D_DCE_THREADS}" +#endif +/* END CYGNUS LOCAL */ + +/* Defines for a K&R CC */ + +#define CC1_SPEC "%{pg:} %{p:}" + +#define LINK_SPEC "%{mlinker-opt:-O} %{!shared:-u main} %{shared:-b}" + +/* We don't want -lg. */ +#ifndef LIB_SPEC +#define LIB_SPEC "%{!p:%{!pg:-lc}}%{p:-lc_p}%{pg:-lc_p}" +#endif + +/* Make gcc agree with <machine/ansi.h> */ + +#define SIZE_TYPE "unsigned int" +#define PTRDIFF_TYPE "int" +#define WCHAR_TYPE "unsigned int" +#define WCHAR_TYPE_SIZE 32 + +/* Show we can debug even without a frame pointer. */ +#define CAN_DEBUG_WITHOUT_FP + +/* Machine dependent reorg pass. */ +#define MACHINE_DEPENDENT_REORG(X) pa_reorg(X) + +/* Prototype function used in MACHINE_DEPENDENT_REORG macro. */ +void pa_reorg (); + +/* Prototype function used in various macros. */ +int symbolic_operand (); + +/* Used in insn-*.c. */ +int following_call (); +int function_label_operand (); +int lhs_lshift_cint_operand (); + +/* Names to predefine in the preprocessor for this target machine. */ + +#define CPP_PREDEFINES "-Dhppa -Dhp9000s800 -D__hp9000s800 -Dhp9k8 -Dunix -Dhp9000 -Dhp800 -Dspectrum -DREVARGV -Asystem(unix) -Asystem(bsd) -Acpu(hppa) -Amachine(hppa)" + +/* HPUX has a program 'chatr' to list the dependencies of dynamically + linked executables and shared libraries. */ +#define LDD_SUFFIX "chatr" +/* Look for lines like "dynamic /usr/lib/X11R5/libX11.sl" + or "static /usr/lib/X11R5/libX11.sl". + + HPUX 10.20 also has lines like "static branch prediction ..." + so we filter that out explicitly. + + We also try to bound our search for libraries with marker + lines. What a pain. */ +#define PARSE_LDD_OUTPUT(PTR) \ +do { \ + static int in_shlib_list = 0; \ + while (*PTR == ' ') PTR++; \ + if (strncmp (PTR, "shared library list:", \ + sizeof ("shared library list:") - 1) == 0) \ + { \ + PTR = 0; \ + in_shlib_list = 1; \ + } \ + else if (strncmp (PTR, "shared library binding:", \ + sizeof ("shared library binding:") - 1) == 0)\ + { \ + PTR = 0; \ + in_shlib_list = 0; \ + } \ + else if (strncmp (PTR, "static branch prediction disabled", \ + sizeof ("static branch prediction disabled") - 1) == 0)\ + { \ + PTR = 0; \ + in_shlib_list = 0; \ + } \ + else if (in_shlib_list \ + && strncmp (PTR, "dynamic", sizeof ("dynamic") - 1) == 0) \ + { \ + PTR += sizeof ("dynamic") - 1; \ + while (*p == ' ') PTR++; \ + } \ + else if (in_shlib_list \ + && strncmp (PTR, "static", sizeof ("static") - 1) == 0) \ + { \ + PTR += sizeof ("static") - 1; \ + while (*p == ' ') PTR++; \ + } \ + else \ + PTR = 0; \ +} while (0) + +/* target machine storage layout */ + +/* Define for cross-compilation from a host with a different float format + or endianness (e.g. VAX, x86). */ +#define REAL_ARITHMETIC + +/* Define this macro if it is advisable to hold scalars in registers + in a wider mode than that declared by the program. In such cases, + the value is constrained to be within the bounds of the declared + type, but kept valid in the wider mode. The signedness of the + extension may differ from that of the type. */ + +#define PROMOTE_MODE(MODE,UNSIGNEDP,TYPE) \ + if (GET_MODE_CLASS (MODE) == MODE_INT \ + && GET_MODE_SIZE (MODE) < 4) \ + (MODE) = SImode; + +/* Define this if most significant bit is lowest numbered + in instructions that operate on numbered bit-fields. */ +#define BITS_BIG_ENDIAN 1 + +/* Define this if most significant byte of a word is the lowest numbered. */ +/* That is true on the HP-PA. */ +#define BYTES_BIG_ENDIAN 1 + +/* Define this if most significant word of a multiword number is lowest + numbered. */ +#define WORDS_BIG_ENDIAN 1 + +/* number of bits in an addressable storage unit */ +#define BITS_PER_UNIT 8 + +/* Width in bits of a "word", which is the contents of a machine register. + Note that this is not necessarily the width of data type `int'; + if using 16-bit ints on a 68000, this would still be 32. + But on a machine with 16-bit registers, this would be 16. */ +#define BITS_PER_WORD 32 + +/* Width of a word, in units (bytes). */ +#define UNITS_PER_WORD 4 + +/* Width in bits of a pointer. + See also the macro `Pmode' defined below. */ +#define POINTER_SIZE 32 + +/* Allocation boundary (in *bits*) for storing arguments in argument list. */ +#define PARM_BOUNDARY 32 + +/* Largest alignment required for any stack parameter, in bits. + Don't define this if it is equal to PARM_BOUNDARY */ +#define MAX_PARM_BOUNDARY 64 + +/* Boundary (in *bits*) on which stack pointer is always aligned; + certain optimizations in combine depend on this. + + GCC for the PA always rounds its stacks to a 512bit boundary, + but that happens late in the compilation process. */ +#define STACK_BOUNDARY 64 + +/* Allocation boundary (in *bits*) for the code of a function. */ +#define FUNCTION_BOUNDARY 32 + +/* Alignment of field after `int : 0' in a structure. */ +#define EMPTY_FIELD_BOUNDARY 32 + +/* Every structure's size must be a multiple of this. */ +#define STRUCTURE_SIZE_BOUNDARY 8 + +/* A bitfield declared as `int' forces `int' alignment for the struct. */ +#define PCC_BITFIELD_TYPE_MATTERS 1 + +/* No data type wants to be aligned rounder than this. */ +#define BIGGEST_ALIGNMENT 64 + +/* The .align directive in the HP assembler allows up to a 32 alignment. */ +#define MAX_OFILE_ALIGNMENT 32768 + +/* Get around hp-ux assembler bug, and make strcpy of constants fast. */ +#define CONSTANT_ALIGNMENT(CODE, TYPEALIGN) \ + ((TYPEALIGN) < 32 ? 32 : (TYPEALIGN)) + +/* Make arrays of chars word-aligned for the same reasons. */ +#define DATA_ALIGNMENT(TYPE, ALIGN) \ + (TREE_CODE (TYPE) == ARRAY_TYPE \ + && TYPE_MODE (TREE_TYPE (TYPE)) == QImode \ + && (ALIGN) < BITS_PER_WORD ? BITS_PER_WORD : (ALIGN)) + + +/* Set this nonzero if move instructions will actually fail to work + when given unaligned data. */ +#define STRICT_ALIGNMENT 1 + +/* Generate calls to memcpy, memcmp and memset. */ +#define TARGET_MEM_FUNCTIONS + +/* Standard register usage. */ + +/* Number of actual hardware registers. + The hardware registers are assigned numbers for the compiler + from 0 to just below FIRST_PSEUDO_REGISTER. + All registers that the compiler knows about must be given numbers, + even those that are not normally considered general registers. + + HP-PA 1.0 has 32 fullword registers and 16 floating point + registers. The floating point registers hold either word or double + word values. + + 16 additional registers are reserved. + + HP-PA 1.1 has 32 fullword registers and 32 floating point + registers. However, the floating point registers behave + differently: the left and right halves of registers are addressable + as 32 bit registers. So, we will set things up like the 68k which + has different fp units: define separate register sets for the 1.0 + and 1.1 fp units. */ + +#define FIRST_PSEUDO_REGISTER 89 /* 32 general regs + 56 fp regs + + + 1 shift reg */ + +/* 1 for registers that have pervasive standard uses + and are not available for the register allocator. + + On the HP-PA, these are: + Reg 0 = 0 (hardware). However, 0 is used for condition code, + so is not fixed. + Reg 1 = ADDIL target/Temporary (hardware). + Reg 2 = Return Pointer + Reg 3 = Frame Pointer + Reg 4 = Frame Pointer (>8k varying frame with HP compilers only) + Reg 4-18 = Preserved Registers + Reg 19 = Linkage Table Register in HPUX 8.0 shared library scheme. + Reg 20-22 = Temporary Registers + Reg 23-26 = Temporary/Parameter Registers + Reg 27 = Global Data Pointer (hp) + Reg 28 = Temporary/???/Return Value register + Reg 29 = Temporary/Static Chain/Return Value register #2 + Reg 30 = stack pointer + Reg 31 = Temporary/Millicode Return Pointer (hp) + + Freg 0-3 = Status Registers -- Not known to the compiler. + Freg 4-7 = Arguments/Return Value + Freg 8-11 = Temporary Registers + Freg 12-15 = Preserved Registers + + Freg 16-31 = Reserved + + On the Snake, fp regs are + + Freg 0-3 = Status Registers -- Not known to the compiler. + Freg 4L-7R = Arguments/Return Value + Freg 8L-11R = Temporary Registers + Freg 12L-21R = Preserved Registers + Freg 22L-31R = Temporary Registers + +*/ + +#define FIXED_REGISTERS \ + {0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 1, 0, 0, 1, 0, \ + /* fp registers */ \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0} + +/* 1 for registers not available across function calls. + These must include the FIXED_REGISTERS and also any + registers that can be used without being saved. + The latter must include the registers where values are returned + and the register where structure-value addresses are passed. + Aside from that, you can include as many other registers as you like. */ +#define CALL_USED_REGISTERS \ + {1, 1, 1, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + /* fp registers */ \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 1} + +#define CONDITIONAL_REGISTER_USAGE \ +{ \ + if (!TARGET_SNAKE) \ + { \ + for (i = 56; i < 88; i++) \ + fixed_regs[i] = call_used_regs[i] = 1; \ + for (i = 33; i < 88; i += 2) \ + fixed_regs[i] = call_used_regs[i] = 1; \ + } \ + if (TARGET_DISABLE_FPREGS || TARGET_SOFT_FLOAT)\ + { \ + for (i = 32; i < 88; i++) \ + fixed_regs[i] = call_used_regs[i] = 1; \ + } \ + if (flag_pic) \ + { \ + fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1; \ + fixed_regs[PIC_OFFSET_TABLE_REGNUM_SAVED] = 1;\ + } \ +} + +/* CYGNUS LOCAL PA8000/law */ +/* Allocate the call used registers first. This should minimize + the number of registers that need to be saved (as call used + registers will generally not be allocated across a call). + + Experimentation has shown slightly better results by allocating + FP registers first. + + The PA8000 triggers a depedency stall if we use both halves of a + floating point register in the same bundle. So we separate out the + halves to discourage using them in nearby insns. This should be + fixed in the register allocator/scheduler. */ + +#define REG_ALLOC_ORDER \ + { \ + /* caller-saved fp regs. */ \ + 68, 70, 72, 74, 76, 78, 80, 82, \ + 84, 86, 40, 42, 44, 46, 32, 34, \ + 36, 38, \ + 69, 71, 73, 75, 77, 79, 81, 83, \ + 85, 87, 41, 43, 45, 47, 33, 35, \ + 37, 39, \ + /* caller-saved general regs. */ \ + 19, 20, 21, 22, 23, 24, 25, 26, \ + 27, 28, 29, 31, 2, \ + /* callee-saved fp regs. */ \ + 48, 50, 52, 54, 56, 58, 60, 62, \ + 64, 66, \ + 49, 51, 53, 55, 57, 59, 61, 63, \ + 65, 67, \ + /* callee-saved general regs. */ \ + 3, 4, 5, 6, 7, 8, 9, 10, \ + 11, 12, 13, 14, 15, 16, 17, 18, \ + /* special registers. */ \ + 1, 30, 0, 88} +/* END CYGNUS LOCAL */ + + +/* True if register is floating-point. */ +#define FP_REGNO_P(N) ((N) >= 32 && (N) <= 87) + +/* Return number of consecutive hard regs needed starting at reg REGNO + to hold something of mode MODE. + This is ordinarily the length in words of a value of mode MODE + but can be less for certain modes in special long registers. + + On the HP-PA, ordinary registers hold 32 bits worth; + The floating point registers are 64 bits wide. Snake fp regs are 32 + bits wide */ +#define HARD_REGNO_NREGS(REGNO, MODE) \ + (!TARGET_SNAKE && FP_REGNO_P (REGNO) ? 1 \ + : ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)) + +/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE. + On the HP-PA, the cpu registers can hold any mode. We + force this to be an even register is it cannot hold the full mode. */ +#define HARD_REGNO_MODE_OK(REGNO, MODE) \ + ((REGNO) == 0 ? (MODE) == CCmode || (MODE) == CCFPmode \ + /* On 1.0 machines, don't allow wide non-fp modes in fp regs. */ \ + : !TARGET_SNAKE && FP_REGNO_P (REGNO) \ + ? GET_MODE_SIZE (MODE) <= 4 || GET_MODE_CLASS (MODE) == MODE_FLOAT \ + /* Make wide modes be in aligned registers. */ \ + : GET_MODE_SIZE (MODE) <= 4 || ((REGNO) & 1) == 0) + +/* Value is 1 if it is a good idea to tie two pseudo registers + when one has mode MODE1 and one has mode MODE2. + If HARD_REGNO_MODE_OK could produce different values for MODE1 and MODE2, + for any hard reg, then this must be 0 for correct output. */ +#define MODES_TIEABLE_P(MODE1, MODE2) \ + (GET_MODE_CLASS (MODE1) == GET_MODE_CLASS (MODE2)) + +/* Specify the registers used for certain standard purposes. + The values of these macros are register numbers. */ + +/* The HP-PA pc isn't overloaded on a register that the compiler knows about. */ +/* #define PC_REGNUM */ + +/* Register to use for pushing function arguments. */ +#define STACK_POINTER_REGNUM 30 + +/* Base register for access to local variables of the function. */ +#define FRAME_POINTER_REGNUM 3 + +/* Value should be nonzero if functions must have frame pointers. */ +#define FRAME_POINTER_REQUIRED \ + (current_function_calls_alloca) + +/* C statement to store the difference between the frame pointer + and the stack pointer values immediately after the function prologue. + + Note, we always pretend that this is a leaf function because if + it's not, there's no point in trying to eliminate the + frame pointer. If it is a leaf function, we guessed right! */ +#define INITIAL_FRAME_POINTER_OFFSET(VAR) \ + do {(VAR) = - compute_frame_size (get_frame_size (), 0);} while (0) + +/* Base register for access to arguments of the function. */ +#define ARG_POINTER_REGNUM 3 + +/* Register in which static-chain is passed to a function. */ +/* ??? */ +#define STATIC_CHAIN_REGNUM 29 + +/* Register which holds offset table for position-independent + data references. */ + +#define PIC_OFFSET_TABLE_REGNUM 19 +#define PIC_OFFSET_TABLE_REG_CALL_CLOBBERED 1 + +/* Register into which we save the PIC_OFFEST_TABLE_REGNUM so that it + can be restore across function calls. */ +#define PIC_OFFSET_TABLE_REGNUM_SAVED 4 + +/* SOM ABI says that objects larger than 64 bits are returned in memory. */ +#define DEFAULT_PCC_STRUCT_RETURN 0 +#define RETURN_IN_MEMORY(TYPE) \ + (int_size_in_bytes (TYPE) > 8) + +/* Register in which address to store a structure value + is passed to a function. */ +#define STRUCT_VALUE_REGNUM 28 + +/* Define the classes of registers for register constraints in the + machine description. Also define ranges of constants. + + One of the classes must always be named ALL_REGS and include all hard regs. + If there is more than one class, another class must be named NO_REGS + and contain no registers. + + The name GENERAL_REGS must be the name of a class (or an alias for + another name such as ALL_REGS). This is the class of registers + that is allowed by "g" or "r" in a register constraint. + Also, registers outside this class are allocated only when + instructions express preferences for them. + + The classes must be numbered in nondecreasing order; that is, + a larger-numbered class must never be contained completely + in a smaller-numbered class. + + For any two classes, it is very desirable that there be another + class that represents their union. */ + + /* The HP-PA has four kinds of registers: general regs, 1.0 fp regs, + 1.1 fp regs, and the high 1.1 fp regs, to which the operands of + fmpyadd and fmpysub are restricted. */ + +enum reg_class { NO_REGS, R1_REGS, GENERAL_REGS, FPUPPER_REGS, FP_REGS, GENERAL_OR_FP_REGS, + SHIFT_REGS, ALL_REGS, LIM_REG_CLASSES}; + +#define N_REG_CLASSES (int) LIM_REG_CLASSES + +/* Give names of register classes as strings for dump file. */ + +#define REG_CLASS_NAMES \ + {"NO_REGS", "R1_REGS", "GENERAL_REGS", "FPUPPER_REGS", "FP_REGS", \ + "GENERAL_OR_FP_REGS", "SHIFT_REGS", "ALL_REGS"} + +/* Define which registers fit in which classes. + This is an initializer for a vector of HARD_REG_SET + of length N_REG_CLASSES. Register 0, the "condition code" register, + is in no class. */ + +#define REG_CLASS_CONTENTS \ + {{0x00000000, 0x00000000, 0x00000000}, /* NO_REGS */ \ + {0x00000002, 0x00000000, 0x00000000}, /* R1_REGS */ \ + {0xfffffffe, 0x00000000, 0x00000000}, /* GENERAL_REGS */ \ + {0x00000000, 0xff000000, 0x00ffffff}, /* FPUPPER_REGS */ \ + {0x00000000, 0xffffffff, 0x00ffffff}, /* FP_REGS */ \ + {0xfffffffe, 0xffffffff, 0x00ffffff}, /* GENERAL_OR_FP_REGS */ \ + {0x00000000, 0x00000000, 0x01000000}, /* SHIFT_REGS */ \ + {0xfffffffe, 0xffffffff, 0x01ffffff}} /* ALL_REGS */ + +/* The same information, inverted: + Return the class number of the smallest class containing + reg number REGNO. This could be a conditional expression + or could index an array. */ + +#define REGNO_REG_CLASS(REGNO) \ + ((REGNO) == 0 ? NO_REGS \ + : (REGNO) == 1 ? R1_REGS \ + : (REGNO) < 32 ? GENERAL_REGS \ + : (REGNO) < 56 ? FP_REGS \ + : (REGNO) < 88 ? FPUPPER_REGS \ + : SHIFT_REGS) + +/* The class value for index registers, and the one for base regs. */ +#define INDEX_REG_CLASS GENERAL_REGS +#define BASE_REG_CLASS GENERAL_REGS + +#define FP_REG_CLASS_P(CLASS) \ + ((CLASS) == FP_REGS || (CLASS) == FPUPPER_REGS) + +/* Get reg_class from a letter such as appears in the machine description. */ +/* Keep 'x' for backward compatibility with user asm. */ +#define REG_CLASS_FROM_LETTER(C) \ + ((C) == 'f' ? FP_REGS : \ + (C) == 'y' ? FPUPPER_REGS : \ + (C) == 'x' ? FP_REGS : \ + (C) == 'q' ? SHIFT_REGS : \ + (C) == 'a' ? R1_REGS : \ + (C) == 'Z' ? ALL_REGS : NO_REGS) + +/* The letters I, J, K, L and M in a register constraint string + can be used to stand for particular ranges of immediate operands. + This macro defines what the ranges are. + C is the letter, and VALUE is a constant value. + Return 1 if VALUE is in the range specified by C. + + `I' is used for the 11 bit constants. + `J' is used for the 14 bit constants. + `K' is used for values that can be moved with a zdepi insn. + `L' is used for the 5 bit constants. + `M' is used for 0. + `N' is used for values with the least significant 11 bits equal to zero. + `O' is used for numbers n such that n+1 is a power of 2. + */ + +#define CONST_OK_FOR_LETTER_P(VALUE, C) \ + ((C) == 'I' ? VAL_11_BITS_P (VALUE) \ + : (C) == 'J' ? VAL_14_BITS_P (VALUE) \ + : (C) == 'K' ? zdepi_cint_p (VALUE) \ + : (C) == 'L' ? VAL_5_BITS_P (VALUE) \ + : (C) == 'M' ? (VALUE) == 0 \ + : (C) == 'N' ? ((VALUE) & 0x7ff) == 0 \ + : (C) == 'O' ? (((VALUE) & ((VALUE) + 1)) == 0) \ + : (C) == 'P' ? and_mask_p (VALUE) \ + : 0) + +/* Prototype function used in macro CONST_OK_FOR_LETTER_P. */ +int zdepi_cint_p (); + +/* Similar, but for floating or large integer constants, and defining letters + G and H. Here VALUE is the CONST_DOUBLE rtx itself. + + For PA, `G' is the floating-point constant zero. `H' is undefined. */ + +#define CONST_DOUBLE_OK_FOR_LETTER_P(VALUE, C) \ + ((C) == 'G' ? (GET_MODE_CLASS (GET_MODE (VALUE)) == MODE_FLOAT \ + && (VALUE) == CONST0_RTX (GET_MODE (VALUE))) \ + : 0) + +/* Given an rtx X being reloaded into a reg required to be + in class CLASS, return the class of reg to actually use. + In general this is just CLASS; but on some machines + in some cases it is preferable to use a more restrictive class. */ +#define PREFERRED_RELOAD_CLASS(X,CLASS) (CLASS) + +/* Return the register class of a scratch register needed to copy IN into + or out of a register in CLASS in MODE. If it can be done directly + NO_REGS is returned. + + Avoid doing any work for the common case calls. */ + +#define SECONDARY_RELOAD_CLASS(CLASS,MODE,IN) \ + ((CLASS == BASE_REG_CLASS && GET_CODE (IN) == REG \ + && REGNO (IN) < FIRST_PSEUDO_REGISTER) \ + ? NO_REGS : secondary_reload_class (CLASS, MODE, IN)) + +/* On the PA it is not possible to directly move data between + GENERAL_REGS and FP_REGS. */ +#define SECONDARY_MEMORY_NEEDED(CLASS1, CLASS2, MODE) \ + (FP_REG_CLASS_P (CLASS1) != FP_REG_CLASS_P (CLASS2)) + +/* Return the stack location to use for secondary memory needed reloads. */ +#define SECONDARY_MEMORY_NEEDED_RTX(MODE) \ + gen_rtx_MEM (MODE, gen_rtx_PLUS (Pmode, stack_pointer_rtx, GEN_INT (-16))) + +/* Return the maximum number of consecutive registers + needed to represent mode MODE in a register of class CLASS. */ +#define CLASS_MAX_NREGS(CLASS, MODE) \ + (!TARGET_SNAKE && ((CLASS) == FP_REGS || (CLASS) == FPUPPER_REGS) ? 1 : \ + ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)) + +/* Stack layout; function entry, exit and calling. */ + +/* Define this if pushing a word on the stack + makes the stack pointer a smaller address. */ +/* #define STACK_GROWS_DOWNWARD */ + +/* Believe it or not. */ +#define ARGS_GROW_DOWNWARD + +/* Define this if the nominal address of the stack frame + is at the high-address end of the local variables; + that is, each additional local variable allocated + goes at a more negative offset in the frame. */ +/* #define FRAME_GROWS_DOWNWARD */ + +/* Offset within stack frame to start allocating local variables at. + If FRAME_GROWS_DOWNWARD, this is the offset to the END of the + first local allocated. Otherwise, it is the offset to the BEGINNING + of the first local allocated. */ +#define STARTING_FRAME_OFFSET 8 + +/* If we generate an insn to push BYTES bytes, + this says how many the stack pointer really advances by. + On the HP-PA, don't define this because there are no push insns. */ +/* #define PUSH_ROUNDING(BYTES) */ + +/* Offset of first parameter from the argument pointer register value. + This value will be negated because the arguments grow down. + Also note that on STACK_GROWS_UPWARD machines (such as this one) + this is the distance from the frame pointer to the end of the first + argument, not it's beginning. To get the real offset of the first + argument, the size of the argument must be added. + + ??? Have to check on this.*/ + +#define FIRST_PARM_OFFSET(FNDECL) -32 + +/* Absolute value of offset from top-of-stack address to location to store the + function parameter if it can't go in a register. + Addresses for following parameters are computed relative to this one. */ +#define FIRST_PARM_CALLER_OFFSET(FNDECL) -32 + + +/* When a parameter is passed in a register, stack space is still + allocated for it. */ +#define REG_PARM_STACK_SPACE(DECL) 16 + +/* Define this if the above stack space is to be considered part of the + space allocated by the caller. */ +#define OUTGOING_REG_PARM_STACK_SPACE + +/* Keep the stack pointer constant throughout the function. + This is both an optimization and a necessity: longjmp + doesn't behave itself when the stack pointer moves within + the function! */ +#define ACCUMULATE_OUTGOING_ARGS + +/* The weird HPPA calling conventions require a minimum of 48 bytes on + the stack: 16 bytes for register saves, and 32 bytes for magic. + This is the difference between the logical top of stack and the + actual sp. */ +#define STACK_POINTER_OFFSET -32 + +#define STACK_DYNAMIC_OFFSET(FNDECL) \ + ((STACK_POINTER_OFFSET) - current_function_outgoing_args_size) + +/* Value is 1 if returning from a function call automatically + pops the arguments described by the number-of-args field in the call. + FUNDECL is the declaration node of the function (as a tree), + FUNTYPE is the data type of the function (as a tree), + or for a library call it is an identifier node for the subroutine name. */ + +#define RETURN_POPS_ARGS(FUNDECL,FUNTYPE,SIZE) 0 + +/* Define how to find the value returned by a function. + VALTYPE is the data type of the value (as a tree). + If the precise function being called is known, FUNC is its FUNCTION_DECL; + otherwise, FUNC is 0. */ + +/* On the HP-PA the value is found in register(s) 28(-29), unless + the mode is SF or DF. Then the value is returned in fr4 (32, ) */ + + +#define FUNCTION_VALUE(VALTYPE, FUNC) \ + gen_rtx_REG (TYPE_MODE (VALTYPE), ((! TARGET_SOFT_FLOAT \ + && (TYPE_MODE (VALTYPE) == SFmode || \ + TYPE_MODE (VALTYPE) == DFmode)) ? \ + 32 : 28)) + +/* Define how to find the value returned by a library function + assuming the value has mode MODE. */ + +#define LIBCALL_VALUE(MODE) \ + gen_rtx_REG (MODE, \ + (! TARGET_SOFT_FLOAT \ + && ((MODE) == SFmode || (MODE) == DFmode) ? 32 : 28)) + +/* 1 if N is a possible register number for a function value + as seen by the caller. */ + +#define FUNCTION_VALUE_REGNO_P(N) \ + ((N) == 28 || (! TARGET_SOFT_FLOAT && (N) == 32)) + +/* 1 if N is a possible register number for function argument passing. */ + +#define FUNCTION_ARG_REGNO_P(N) \ + (((N) >= 23 && (N) <= 26) || (! TARGET_SOFT_FLOAT && (N) >= 32 && (N) <= 39)) + +/* Define a data type for recording info about an argument list + during the scan of that argument list. This data type should + hold all necessary information about the function itself + and about the args processed so far, enough to enable macros + such as FUNCTION_ARG to determine where the next arg should go. + + On the HP-PA, this is a single integer, which is a number of words + of arguments scanned so far (including the invisible argument, + if any, which holds the structure-value-address). + Thus 4 or more means all following args should go on the stack. */ + +struct hppa_args {int words, nargs_prototype, indirect; }; + +#define CUMULATIVE_ARGS struct hppa_args + +/* Initialize a variable CUM of type CUMULATIVE_ARGS + for a call to a function whose data type is FNTYPE. + For a library call, FNTYPE is 0. */ + +#define INIT_CUMULATIVE_ARGS(CUM,FNTYPE,LIBNAME,INDIRECT) \ + (CUM).words = 0, \ + (CUM).indirect = INDIRECT, \ + (CUM).nargs_prototype = (FNTYPE && TYPE_ARG_TYPES (FNTYPE) \ + ? (list_length (TYPE_ARG_TYPES (FNTYPE)) - 1 \ + + (TYPE_MODE (TREE_TYPE (FNTYPE)) == BLKmode \ + || RETURN_IN_MEMORY (TREE_TYPE (FNTYPE)))) \ + : 0) + + + +/* Similar, but when scanning the definition of a procedure. We always + set NARGS_PROTOTYPE large so we never return a PARALLEL. */ + +#define INIT_CUMULATIVE_INCOMING_ARGS(CUM,FNTYPE,IGNORE) \ + (CUM).words = 0, \ + (CUM).indirect = 0, \ + (CUM).nargs_prototype = 1000 + +/* Figure out the size in words of the function argument. */ + +#define FUNCTION_ARG_SIZE(MODE, TYPE) \ + ((((MODE) != BLKmode ? GET_MODE_SIZE (MODE) : int_size_in_bytes (TYPE))+3)/4) + +/* Update the data in CUM to advance over an argument + of mode MODE and data type TYPE. + (TYPE is null for libcalls where that information may not be available.) */ + +#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \ +{ (CUM).nargs_prototype--; \ + ((((CUM).words & 01) && (TYPE) != 0 \ + && FUNCTION_ARG_SIZE(MODE, TYPE) > 1) \ + && (CUM).words++), \ + (CUM).words += FUNCTION_ARG_SIZE(MODE, TYPE); \ +} + +/* Determine where to put an argument to a function. + Value is zero to push the argument on the stack, + or a hard register in which to store the argument. + + MODE is the argument's machine mode. + TYPE is the data type of the argument (as a tree). + This is null for libcalls where that information may + not be available. + CUM is a variable of type CUMULATIVE_ARGS which gives info about + the preceding args and about the function being called. + NAMED is nonzero if this argument is a named parameter + (otherwise it is an extra parameter matching an ellipsis). + + On the HP-PA the first four words of args are normally in registers + and the rest are pushed. But any arg that won't entirely fit in regs + is pushed. + + Arguments passed in registers are either 1 or 2 words long. + + The caller must make a distinction between calls to explicitly named + functions and calls through pointers to functions -- the conventions + are different! Calls through pointers to functions only use general + registers for the first four argument words. + + Of course all this is different for the portable runtime model + HP wants everyone to use for ELF. Ugh. Here's a quick description + of how it's supposed to work. + + 1) callee side remains unchanged. It expects integer args to be + in the integer registers, float args in the float registers and + unnamed args in integer registers. + + 2) caller side now depends on if the function being called has + a prototype in scope (rather than if it's being called indirectly). + + 2a) If there is a prototype in scope, then arguments are passed + according to their type (ints in integer registers, floats in float + registers, unnamed args in integer registers. + + 2b) If there is no prototype in scope, then floating point arguments + are passed in both integer and float registers. egad. + + FYI: The portable parameter passing conventions are almost exactly like + the standard parameter passing conventions on the RS6000. That's why + you'll see lots of similar code in rs6000.h. */ + +#define FUNCTION_ARG_PADDING(MODE, TYPE) function_arg_padding ((MODE), (TYPE)) + +/* Do not expect to understand this without reading it several times. I'm + tempted to try and simply it, but I worry about breaking something. */ + +#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \ + (4 >= ((CUM).words + FUNCTION_ARG_SIZE ((MODE), (TYPE))) \ + ? (!TARGET_PORTABLE_RUNTIME || (TYPE) == 0 \ + || !FLOAT_MODE_P (MODE) || TARGET_SOFT_FLOAT \ + || (CUM).nargs_prototype > 0) \ + ? gen_rtx_REG ((MODE), \ + (FUNCTION_ARG_SIZE ((MODE), (TYPE)) > 1 \ + ? (((!(CUM).indirect \ + || TARGET_PORTABLE_RUNTIME) \ + && (MODE) == DFmode \ + && ! TARGET_SOFT_FLOAT) \ + ? ((CUM).words ? 38 : 34) \ + : ((CUM).words ? 23 : 25)) \ + : (((!(CUM).indirect \ + || TARGET_PORTABLE_RUNTIME) \ + && (MODE) == SFmode \ + && ! TARGET_SOFT_FLOAT) \ + ? (32 + 2 * (CUM).words) \ + : (27 - (CUM).words - FUNCTION_ARG_SIZE ((MODE), \ + (TYPE))))))\ + /* We are calling a non-prototyped function with floating point \ + arguments using the portable conventions. */ \ + : gen_rtx_PARALLEL ((MODE), \ + gen_rtvec \ + (2, \ + gen_rtx_EXPR_LIST (VOIDmode, \ + gen_rtx_REG ((MODE), \ + (FUNCTION_ARG_SIZE ((MODE), (TYPE)) > 1 \ + ? ((CUM).words ? 38 : 34) \ + : (32 + 2 * (CUM).words))), \ + const0_rtx), \ + gen_rtx_EXPR_LIST (VOIDmode, \ + gen_rtx_REG ((MODE), \ + (FUNCTION_ARG_SIZE ((MODE), (TYPE)) > 1 \ + ? ((CUM).words ? 23 : 25) \ + : (27 - (CUM).words - \ + FUNCTION_ARG_SIZE ((MODE), \ + (TYPE))))), \ + const0_rtx))) \ + /* Pass this parameter in the stack. */ \ + : 0) + +/* For an arg passed partly in registers and partly in memory, + this is the number of registers used. + For args passed entirely in registers or entirely in memory, zero. */ + +#define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) 0 + +/* If defined, a C expression that gives the alignment boundary, in + bits, of an argument with the specified mode and type. If it is + not defined, `PARM_BOUNDARY' is used for all arguments. */ + +#define FUNCTION_ARG_BOUNDARY(MODE, TYPE) \ + (((TYPE) != 0) \ + ? (((int_size_in_bytes (TYPE)) + 3) / 4) * BITS_PER_WORD \ + : ((GET_MODE_ALIGNMENT(MODE) <= PARM_BOUNDARY) \ + ? PARM_BOUNDARY \ + : GET_MODE_ALIGNMENT(MODE))) + +/* Arguments larger than eight bytes are passed by invisible reference */ + +#define FUNCTION_ARG_PASS_BY_REFERENCE(CUM, MODE, TYPE, NAMED) \ + ((TYPE) && int_size_in_bytes (TYPE) > 8) + +#define FUNCTION_ARG_CALLEE_COPIES(CUM, MODE, TYPE, NAMED) \ + ((TYPE) && int_size_in_bytes (TYPE) > 8) + + +extern struct rtx_def *hppa_compare_op0, *hppa_compare_op1; +extern enum cmp_type hppa_branch_type; + +/* Output the label for a function definition. */ +#ifndef HP_FP_ARG_DESCRIPTOR_REVERSED +#define ASM_DOUBLE_ARG_DESCRIPTORS(FILE, ARG0, ARG1) \ + do { fprintf (FILE, ",ARGW%d=FR", (ARG0)); \ + fprintf (FILE, ",ARGW%d=FU", (ARG1));} while (0) +#define DFMODE_RETURN_STRING ",RTNVAL=FU" +#define SFMODE_RETURN_STRING ",RTNVAL=FR" +#else +#define ASM_DOUBLE_ARG_DESCRIPTORS(FILE, ARG0, ARG1) \ + do { fprintf (FILE, ",ARGW%d=FU", (ARG0)); \ + fprintf (FILE, ",ARGW%d=FR", (ARG1));} while (0) +#define DFMODE_RETURN_STRING ",RTNVAL=FR" +#define SFMODE_RETURN_STRING ",RTNVAL=FU" +#endif + +#define ASM_OUTPUT_MI_THUNK(FILE, THUNK_FNDECL, DELTA, FUNCTION) \ +{ char *target_name = XSTR (XEXP (DECL_RTL (FUNCTION), 0), 0); \ + STRIP_NAME_ENCODING (target_name, target_name); \ + output_function_prologue (FILE, 0); \ + if (VAL_14_BITS_P (DELTA)) \ + fprintf (FILE, "\tb %s\n\tldo %d(%%r26),%%r26\n", target_name, DELTA); \ + else \ + fprintf (FILE, "\taddil L%%%d,%%r26\n\tb %s\n\tldo R%%%d(%%r1),%%r26\n", \ + DELTA, target_name, DELTA); \ + fprintf (FILE, "\n\t.EXIT\n\t.PROCEND\n"); \ +} + +/* NAME refers to the function's name. If we are placing each function into + its own section, we need to switch to the section for this function. Note + that the section name will have a "." prefix. */ +#define ASM_OUTPUT_FUNCTION_PREFIX(FILE, NAME) \ + { \ + char *name; \ + STRIP_NAME_ENCODING (name, NAME); \ + if (!TARGET_PORTABLE_RUNTIME && TARGET_GAS && in_section == in_text) \ + fputs ("\t.NSUBSPA $CODE$,QUAD=0,ALIGN=8,ACCESS=44,CODE_ONLY\n", FILE); \ + else if (! TARGET_PORTABLE_RUNTIME && TARGET_GAS) \ + fprintf (FILE, \ + "\t.SUBSPA .%s\n", name); \ + } + +#define ASM_DECLARE_FUNCTION_NAME(FILE, NAME, DECL) \ + do { tree fntype = TREE_TYPE (TREE_TYPE (DECL)); \ + tree tree_type = TREE_TYPE (DECL); \ + tree parm; \ + int i; \ + if (TREE_PUBLIC (DECL) || TARGET_GAS) \ + { extern int current_function_varargs; \ + if (TREE_PUBLIC (DECL)) \ + { \ + fputs ("\t.EXPORT ", FILE); \ + assemble_name (FILE, NAME); \ + fputs (",ENTRY,PRIV_LEV=3", FILE); \ + } \ + else \ + { \ + fputs ("\t.PARAM ", FILE); \ + assemble_name (FILE, NAME); \ + } \ + if (TARGET_PORTABLE_RUNTIME) \ + { \ + fputs (",ARGW0=NO,ARGW1=NO,ARGW2=NO,ARGW3=NO,", FILE); \ + fputs ("RTNVAL=NO\n", FILE); \ + break; \ + } \ + for (parm = DECL_ARGUMENTS (DECL), i = 0; parm && i < 4; \ + parm = TREE_CHAIN (parm)) \ + { \ + if (TYPE_MODE (DECL_ARG_TYPE (parm)) == SFmode \ + && ! TARGET_SOFT_FLOAT) \ + fprintf (FILE, ",ARGW%d=FR", i++); \ + else if (TYPE_MODE (DECL_ARG_TYPE (parm)) == DFmode \ + && ! TARGET_SOFT_FLOAT) \ + { \ + if (i <= 2) \ + { \ + if (i == 1) i++; \ + ASM_DOUBLE_ARG_DESCRIPTORS (FILE, i++, i++); \ + } \ + else \ + break; \ + } \ + else \ + { \ + int arg_size = \ + FUNCTION_ARG_SIZE (TYPE_MODE (DECL_ARG_TYPE (parm)),\ + DECL_ARG_TYPE (parm)); \ + /* Passing structs by invisible reference uses \ + one general register. */ \ + if (arg_size > 2 \ + || TREE_ADDRESSABLE (DECL_ARG_TYPE (parm))) \ + arg_size = 1; \ + if (arg_size == 2 && i <= 2) \ + { \ + if (i == 1) i++; \ + fprintf (FILE, ",ARGW%d=GR", i++); \ + fprintf (FILE, ",ARGW%d=GR", i++); \ + } \ + else if (arg_size == 1) \ + fprintf (FILE, ",ARGW%d=GR", i++); \ + else \ + i += arg_size; \ + } \ + } \ + /* anonymous args */ \ + if ((TYPE_ARG_TYPES (tree_type) != 0 \ + && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (tree_type)))\ + != void_type_node)) \ + || current_function_varargs) \ + { \ + for (; i < 4; i++) \ + fprintf (FILE, ",ARGW%d=GR", i); \ + } \ + if (TYPE_MODE (fntype) == DFmode && ! TARGET_SOFT_FLOAT) \ + fputs (DFMODE_RETURN_STRING, FILE); \ + else if (TYPE_MODE (fntype) == SFmode && ! TARGET_SOFT_FLOAT) \ + fputs (SFMODE_RETURN_STRING, FILE); \ + else if (fntype != void_type_node) \ + fputs (",RTNVAL=GR", FILE); \ + fputs ("\n", FILE); \ + }} while (0) + +/* This macro generates the assembly code for function entry. + FILE is a stdio stream to output the code to. + SIZE is an int: how many units of temporary storage to allocate. + Refer to the array `regs_ever_live' to determine which registers + to save; `regs_ever_live[I]' is nonzero if register number I + is ever used in the function. This macro is responsible for + knowing which registers should not be saved even if used. */ + +/* On HP-PA, move-double insns between fpu and cpu need an 8-byte block + of memory. If any fpu reg is used in the function, we allocate + such a block here, at the bottom of the frame, just in case it's needed. + + If this function is a leaf procedure, then we may choose not + to do a "save" insn. The decision about whether or not + to do this is made in regclass.c. */ + +#define FUNCTION_PROLOGUE(FILE, SIZE) \ + output_function_prologue (FILE, SIZE) + +/* Output assembler code to FILE to increment profiler label # LABELNO + for profiling a function entry. + + Because HPUX _mcount is so different, we actually emit the + profiling code in function_prologue. This just stores LABELNO for + that. */ + +#define PROFILE_BEFORE_PROLOGUE +#define FUNCTION_PROFILER(FILE, LABELNO) \ +{ extern int hp_profile_labelno; hp_profile_labelno = (LABELNO);} + +/* EXIT_IGNORE_STACK should be nonzero if, when returning from a function, + the stack pointer does not matter. The value is tested only in + functions that have frame pointers. + No definition is equivalent to always zero. */ + +extern int may_call_alloca; +extern int current_function_pretend_args_size; + +#define EXIT_IGNORE_STACK \ + (get_frame_size () != 0 \ + || current_function_calls_alloca || current_function_outgoing_args_size) + + +/* This macro generates the assembly code for function exit, + on machines that need it. If FUNCTION_EPILOGUE is not defined + then individual return instructions are generated for each + return statement. Args are same as for FUNCTION_PROLOGUE. + + The function epilogue should not depend on the current stack pointer! + It should use the frame pointer only. This is mandatory because + of alloca; we also take advantage of it to omit stack adjustments + before returning. */ + +/* This declaration is needed due to traditional/ANSI + incompatibilities which cannot be #ifdefed away + because they occur inside of macros. Sigh. */ +extern union tree_node *current_function_decl; + +#define FUNCTION_EPILOGUE(FILE, SIZE) \ + output_function_epilogue (FILE, SIZE) + +/* Output assembler code for a block containing the constant parts + of a trampoline, leaving space for the variable parts.\ + + The trampoline sets the static chain pointer to STATIC_CHAIN_REGNUM + and then branches to the specified routine. + + This code template is copied from text segment to stack location + and then patched with INITIALIZE_TRAMPOLINE to contain + valid values, and then entered as a subroutine. + + It is best to keep this as small as possible to avoid having to + flush multiple lines in the cache. */ + +#define TRAMPOLINE_TEMPLATE(FILE) \ + { \ + fputs ("\tldw 36(0,%r22),%r21\n", FILE); \ + fputs ("\tbb,>=,n %r21,30,.+16\n", FILE); \ + fputs ("\tdepi 0,31,2,%r21\n", FILE); \ + fputs ("\tldw 4(0,%r21),%r19\n", FILE); \ + fputs ("\tldw 0(0,%r21),%r21\n", FILE); \ + fputs ("\tldsid (0,%r21),%r1\n", FILE); \ + fputs ("\tmtsp %r1,%sr0\n", FILE); \ + fputs ("\tbe 0(%sr0,%r21)\n", FILE); \ + fputs ("\tldw 40(0,%r22),%r29\n", FILE); \ + fputs ("\t.word 0\n", FILE); \ + fputs ("\t.word 0\n", FILE); \ + } + +/* Length in units of the trampoline for entering a nested function. + + Flush the cache entries corresponding to the first and last addresses + of the trampoline. This is necessary as the trampoline may cross two + cache lines. + + If the code part of the trampoline ever grows to > 32 bytes, then it + will become necessary to hack on the cacheflush pattern in pa.md. */ + +#define TRAMPOLINE_SIZE (11 * 4) + +/* Emit RTL insns to initialize the variable parts of a trampoline. + FNADDR is an RTX for the address of the function's pure code. + CXT is an RTX for the static chain value for the function. + + Move the function address to the trampoline template at offset 12. + Move the static chain value to trampoline template at offset 16. */ + +#define INITIALIZE_TRAMPOLINE(TRAMP, FNADDR, CXT) \ +{ \ + rtx start_addr, end_addr; \ + \ + start_addr = memory_address (Pmode, plus_constant ((TRAMP), 36)); \ + emit_move_insn (gen_rtx_MEM (Pmode, start_addr), (FNADDR)); \ + start_addr = memory_address (Pmode, plus_constant ((TRAMP), 40)); \ + emit_move_insn (gen_rtx_MEM (Pmode, start_addr), (CXT)); \ + /* fdc and fic only use registers for the address to flush, \ + they do not accept integer displacements. */ \ + start_addr = force_reg (SImode, (TRAMP)); \ + end_addr = force_reg (SImode, plus_constant ((TRAMP), 32)); \ + emit_insn (gen_dcacheflush (start_addr, end_addr)); \ + end_addr = force_reg (SImode, plus_constant (start_addr, 32)); \ + emit_insn (gen_icacheflush (start_addr, end_addr, start_addr, \ + gen_reg_rtx (SImode), gen_reg_rtx (SImode)));\ +} + +/* Emit code for a call to builtin_saveregs. We must emit USE insns which + reference the 4 integer arg registers and 4 fp arg registers. + Ordinarily they are not call used registers, but they are for + _builtin_saveregs, so we must make this explicit. */ + +extern struct rtx_def *hppa_builtin_saveregs (); +#define EXPAND_BUILTIN_SAVEREGS(ARGLIST) hppa_builtin_saveregs (ARGLIST) + + +/* Addressing modes, and classification of registers for them. */ +/* CYGNUS LOCAL PA8000/law */ +/* It is advisable to avoid autoincrement addressing modes on the PA8000 + series processors. This definition disables the most critical autoinc + usages (in loops). When the prologue/epilogue code is converted we + can probably disable autoinc entirely for the PA8000. */ +#define HAVE_POST_INCREMENT (pa_cpu == PROCESSOR_8000 ? flow2_completed : 1) +#define HAVE_POST_DECREMENT (pa_cpu == PROCESSOR_8000 ? flow2_completed : 1) + +#define HAVE_PRE_DECREMENT (pa_cpu == PROCESSOR_8000 ? flow2_completed : 1) +#define HAVE_PRE_INCREMENT (pa_cpu == PROCESSOR_8000 ? flow2_completed : 1) +/* END CYGNUS LOCAL */ + +/* Macros to check register numbers against specific register classes. */ + +/* These assume that REGNO is a hard or pseudo reg number. + They give nonzero only if REGNO is a hard reg of the suitable class + or a pseudo reg currently allocated to a suitable hard reg. + Since they use reg_renumber, they are safe only once reg_renumber + has been allocated, which happens in local-alloc.c. */ + +#define REGNO_OK_FOR_INDEX_P(REGNO) \ + ((REGNO) && ((REGNO) < 32 || (unsigned) reg_renumber[REGNO] < 32)) +#define REGNO_OK_FOR_BASE_P(REGNO) \ + ((REGNO) && ((REGNO) < 32 || (unsigned) reg_renumber[REGNO] < 32)) +#define REGNO_OK_FOR_FP_P(REGNO) \ + (FP_REGNO_P (REGNO) || FP_REGNO_P (reg_renumber[REGNO])) + +/* Now macros that check whether X is a register and also, + strictly, whether it is in a specified class. + + These macros are specific to the HP-PA, and may be used only + in code for printing assembler insns and in conditions for + define_optimization. */ + +/* 1 if X is an fp register. */ + +#define FP_REG_P(X) (REG_P (X) && REGNO_OK_FOR_FP_P (REGNO (X))) + +/* Maximum number of registers that can appear in a valid memory address. */ + +#define MAX_REGS_PER_ADDRESS 2 + +/* Recognize any constant value that is a valid address except + for symbolic addresses. We get better CSE by rejecting them + here and allowing hppa_legitimize_address to break them up. We + use most of the constants accepted by CONSTANT_P, except CONST_DOUBLE. */ + +#define CONSTANT_ADDRESS_P(X) \ + ((GET_CODE (X) == LABEL_REF || GET_CODE (X) == SYMBOL_REF \ + || GET_CODE (X) == CONST_INT || GET_CODE (X) == CONST \ + || GET_CODE (X) == HIGH) \ + && (reload_in_progress || reload_completed || ! symbolic_expression_p (X))) + +/* Include all constant integers and constant doubles, but not + floating-point, except for floating-point zero. + + Reject LABEL_REFs if we're not using gas or the new HP assembler. */ +#ifdef NEW_HP_ASSEMBLER +#define LEGITIMATE_CONSTANT_P(X) \ + ((GET_MODE_CLASS (GET_MODE (X)) != MODE_FLOAT \ + || (X) == CONST0_RTX (GET_MODE (X))) \ + && !function_label_operand (X, VOIDmode)) +#else +#define LEGITIMATE_CONSTANT_P(X) \ + ((GET_MODE_CLASS (GET_MODE (X)) != MODE_FLOAT \ + || (X) == CONST0_RTX (GET_MODE (X))) \ + && (GET_CODE (X) != LABEL_REF || TARGET_GAS)\ + && !function_label_operand (X, VOIDmode)) +#endif + +/* Subroutine for EXTRA_CONSTRAINT. + + Return 1 iff OP is a pseudo which did not get a hard register and + we are running the reload pass. */ + +#define IS_RELOADING_PSEUDO_P(OP) \ + ((reload_in_progress \ + && GET_CODE (OP) == REG \ + && REGNO (OP) >= FIRST_PSEUDO_REGISTER \ + && reg_renumber [REGNO (OP)] < 0)) + +/* CYGNUS LOCAL PA8000/law */ +/* Optional extra constraints for this machine. Borrowed from sparc.h. + + For the HPPA, `Q' means that this is a memory operand but not a + symbolic memory operand. Note that an unassigned pseudo register + is such a memory operand. Needed because reload will generate + these things in insns and then not re-recognize the insns, causing + constrain_operands to fail. + + `R' is for certain scaled indexed addresses + + `S' is the constant 31. + + `T' is for fp loads and stores. */ +#define EXTRA_CONSTRAINT(OP, C) \ + ((C) == 'Q' ? \ + (IS_RELOADING_PSEUDO_P (OP) \ + || (GET_CODE (OP) == MEM \ + && (memory_address_p (GET_MODE (OP), XEXP (OP, 0))\ + || reload_in_progress) \ + && ! symbolic_memory_operand (OP, VOIDmode) \ + && !(GET_CODE (XEXP (OP, 0)) == PLUS \ + && (GET_CODE (XEXP (XEXP (OP, 0), 0)) == MULT\ + || GET_CODE (XEXP (XEXP (OP, 0), 1)) == MULT))))\ + : ((C) == 'R' ? \ + (GET_CODE (OP) == MEM \ + && GET_CODE (XEXP (OP, 0)) == PLUS \ + && (GET_CODE (XEXP (XEXP (OP, 0), 0)) == MULT \ + || GET_CODE (XEXP (XEXP (OP, 0), 1)) == MULT) \ + && (move_operand (OP, GET_MODE (OP)) \ + || memory_address_p (GET_MODE (OP), XEXP (OP, 0))\ + || reload_in_progress)) \ + : ((C) == 'T' ? \ + (GET_CODE (OP) == MEM \ + /* Using DFmode forces only short displacements \ + to be recognized as valid in reg+d addresses. */\ + && memory_address_p (DFmode, XEXP (OP, 0)) \ + && !(GET_CODE (XEXP (OP, 0)) == PLUS \ + && (GET_CODE (XEXP (XEXP (OP, 0), 0)) == MULT\ + || GET_CODE (XEXP (XEXP (OP, 0), 1)) == MULT))) \ + : ((C) == 'S' ? \ + (GET_CODE (OP) == CONST_INT && INTVAL (OP) == 31) : 0)))) +/* END CYGNUS LOCAL */ + + +/* The macros REG_OK_FOR..._P assume that the arg is a REG rtx + and check its validity for a certain class. + We have two alternate definitions for each of them. + The usual definition accepts all pseudo regs; the other rejects + them unless they have been allocated suitable hard regs. + The symbol REG_OK_STRICT causes the latter definition to be used. + + Most source files want to accept pseudo regs in the hope that + they will get allocated to the class that the insn wants them to be in. + Source files for reload pass need to be strict. + After reload, it makes no difference, since pseudo regs have + been eliminated by then. */ + +#ifndef REG_OK_STRICT + +/* Nonzero if X is a hard reg that can be used as an index + or if it is a pseudo reg. */ +#define REG_OK_FOR_INDEX_P(X) \ +(REGNO (X) && (REGNO (X) < 32 || REGNO (X) >= FIRST_PSEUDO_REGISTER)) +/* Nonzero if X is a hard reg that can be used as a base reg + or if it is a pseudo reg. */ +#define REG_OK_FOR_BASE_P(X) \ +(REGNO (X) && (REGNO (X) < 32 || REGNO (X) >= FIRST_PSEUDO_REGISTER)) + +#else + +/* Nonzero if X is a hard reg that can be used as an index. */ +#define REG_OK_FOR_INDEX_P(X) REGNO_OK_FOR_INDEX_P (REGNO (X)) +/* Nonzero if X is a hard reg that can be used as a base reg. */ +#define REG_OK_FOR_BASE_P(X) REGNO_OK_FOR_BASE_P (REGNO (X)) + +#endif + +/* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression + that is a valid memory address for an instruction. + The MODE argument is the machine mode for the MEM expression + that wants to use this address. + + On the HP-PA, the actual legitimate addresses must be + REG+REG, REG+(REG*SCALE) or REG+SMALLINT. + But we can treat a SYMBOL_REF as legitimate if it is part of this + function's constant-pool, because such addresses can actually + be output as REG+SMALLINT. + + Note we only allow 5 bit immediates for access to a constant address; + doing so avoids losing for loading/storing a FP register at an address + which will not fit in 5 bits. */ + +#define VAL_5_BITS_P(X) ((unsigned)(X) + 0x10 < 0x20) +#define INT_5_BITS(X) VAL_5_BITS_P (INTVAL (X)) + +#define VAL_U5_BITS_P(X) ((unsigned)(X) < 0x20) +#define INT_U5_BITS(X) VAL_U5_BITS_P (INTVAL (X)) + +#define VAL_11_BITS_P(X) ((unsigned)(X) + 0x400 < 0x800) +#define INT_11_BITS(X) VAL_11_BITS_P (INTVAL (X)) + +#define VAL_14_BITS_P(X) ((unsigned)(X) + 0x2000 < 0x4000) +#define INT_14_BITS(X) VAL_14_BITS_P (INTVAL (X)) + +#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \ +{ \ + if ((REG_P (X) && REG_OK_FOR_BASE_P (X)) \ + || ((GET_CODE (X) == PRE_DEC || GET_CODE (X) == POST_DEC \ + || GET_CODE (X) == PRE_INC || GET_CODE (X) == POST_INC) \ + && REG_P (XEXP (X, 0)) \ + && REG_OK_FOR_BASE_P (XEXP (X, 0)))) \ + goto ADDR; \ + else if (GET_CODE (X) == PLUS) \ + { \ + rtx base = 0, index = 0; \ + if (flag_pic && XEXP (X, 0) == pic_offset_table_rtx)\ + { \ + if (GET_CODE (XEXP (X, 1)) == REG \ + && REG_OK_FOR_BASE_P (XEXP (X, 1))) \ + goto ADDR; \ + else if (flag_pic == 1 \ + && GET_CODE (XEXP (X, 1)) == SYMBOL_REF)\ + goto ADDR; \ + } \ + else if (REG_P (XEXP (X, 0)) \ + && REG_OK_FOR_BASE_P (XEXP (X, 0))) \ + base = XEXP (X, 0), index = XEXP (X, 1); \ + else if (REG_P (XEXP (X, 1)) \ + && REG_OK_FOR_BASE_P (XEXP (X, 1))) \ + base = XEXP (X, 1), index = XEXP (X, 0); \ + if (base != 0) \ + if (GET_CODE (index) == CONST_INT \ + && ((INT_14_BITS (index) \ + && (TARGET_SOFT_FLOAT \ + /* CYGNUS LOCAL pa8000/law */ \ + || (TARGET_PARISC_2_0 \ + && ((MODE == SFmode \ + && (INTVAL (index) % 4) == 0)\ + || (MODE == DFmode \ + && (INTVAL (index) % 8) == 0)))\ + /* END CYGNUS LOCAL pa8000/law */ \ + || ((MODE) != SFmode && (MODE) != DFmode))) \ + || INT_5_BITS (index))) \ + goto ADDR; \ + if (! TARGET_SOFT_FLOAT \ + && ! TARGET_DISABLE_INDEXING \ + && base \ + && (mode == SFmode || mode == DFmode) \ + && GET_CODE (index) == MULT \ + && GET_CODE (XEXP (index, 0)) == REG \ + && REG_OK_FOR_BASE_P (XEXP (index, 0)) \ + && GET_CODE (XEXP (index, 1)) == CONST_INT \ + && INTVAL (XEXP (index, 1)) == (mode == SFmode ? 4 : 8))\ + goto ADDR; \ + } \ + else if (GET_CODE (X) == LO_SUM \ + && GET_CODE (XEXP (X, 0)) == REG \ + && REG_OK_FOR_BASE_P (XEXP (X, 0)) \ + && CONSTANT_P (XEXP (X, 1)) \ + && (TARGET_SOFT_FLOAT \ + || ((MODE) != SFmode \ + && (MODE) != DFmode))) \ + goto ADDR; \ + else if (GET_CODE (X) == LO_SUM \ + && GET_CODE (XEXP (X, 0)) == SUBREG \ + && GET_CODE (SUBREG_REG (XEXP (X, 0))) == REG\ + && REG_OK_FOR_BASE_P (SUBREG_REG (XEXP (X, 0)))\ + && CONSTANT_P (XEXP (X, 1)) \ + && (TARGET_SOFT_FLOAT \ + || ((MODE) != SFmode \ + && (MODE) != DFmode))) \ + goto ADDR; \ + else if (GET_CODE (X) == LABEL_REF \ + || (GET_CODE (X) == CONST_INT \ + && INT_5_BITS (X))) \ + goto ADDR; \ + /* Needed for -fPIC */ \ + else if (GET_CODE (X) == LO_SUM \ + && GET_CODE (XEXP (X, 0)) == REG \ + && REG_OK_FOR_BASE_P (XEXP (X, 0)) \ + && GET_CODE (XEXP (X, 1)) == UNSPEC) \ + goto ADDR; \ +} + +/* Look for machine dependent ways to make the invalid address AD a + valid address. + + For the PA, transform: + + memory(X + <large int>) + + into: + + if (<large int> & mask) >= 16 + Y = (<large int> & ~mask) + mask + 1 Round up. + else + Y = (<large int> & ~mask) Round down. + Z = X + Y + memory (Z + (<large int> - Y)); + + This makes reload inheritance and reload_cse work better since Z + can be reused. + + There may be more opportunities to improve code with this hook. */ +#define LEGITIMIZE_RELOAD_ADDRESS(AD, MODE, OPNUM, TYPE, IND, WIN) \ +do { \ + int offset, newoffset, mask; \ + rtx new, temp = NULL_RTX; \ + mask = GET_MODE_CLASS (MODE) == MODE_FLOAT ? 0x1f : 0x3fff; \ + \ + /* CYGNUS LOCAL pa8000/law */ \ + mask = (GET_MODE_CLASS (MODE) == MODE_FLOAT \ + ? (TARGET_PARISC_2_0 ? 0x3fff : 0x1f) : 0x3fff); \ + /* END CYGNUS LOCAL */ \ + \ + if (optimize \ + && GET_CODE (AD) == PLUS) \ + temp = simplify_binary_operation (PLUS, Pmode, \ + XEXP (AD, 0), XEXP (AD, 1)); \ + \ + new = temp ? temp : AD; \ + \ + if (optimize \ + && GET_CODE (new) == PLUS \ + && GET_CODE (XEXP (new, 0)) == REG \ + && GET_CODE (XEXP (new, 1)) == CONST_INT) \ + { \ + offset = INTVAL (XEXP ((new), 1)); \ + \ + /* Choose rounding direction. Round up if we are >= halfway. */ \ + if ((offset & mask) >= ((mask + 1) / 2)) \ + newoffset = (offset & ~mask) + mask + 1; \ + else \ + newoffset = offset & ~mask; \ + \ + if (newoffset != 0 \ + && VAL_14_BITS_P (newoffset)) \ + { \ + \ + temp = gen_rtx_PLUS (Pmode, XEXP (new, 0), \ + GEN_INT (newoffset)); \ + AD = gen_rtx_PLUS (Pmode, temp, GEN_INT (offset - newoffset));\ + push_reload (XEXP (AD, 0), 0, &XEXP (AD, 0), 0, \ + BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, \ + (OPNUM), (TYPE)); \ + goto WIN; \ + } \ + } \ +} while (0) + + + + +/* Try machine-dependent ways of modifying an illegitimate address + to be legitimate. If we find one, return the new, valid address. + This macro is used in only one place: `memory_address' in explow.c. + + OLDX is the address as it was before break_out_memory_refs was called. + In some cases it is useful to look at this to decide what needs to be done. + + MODE and WIN are passed so that this macro can use + GO_IF_LEGITIMATE_ADDRESS. + + It is always safe for this macro to do nothing. It exists to recognize + opportunities to optimize the output. */ + +extern struct rtx_def *hppa_legitimize_address (); +#define LEGITIMIZE_ADDRESS(X, OLDX, MODE, WIN) \ +{ rtx orig_x = (X); \ + (X) = hppa_legitimize_address (X, OLDX, MODE); \ + if ((X) != orig_x && memory_address_p (MODE, X)) \ + goto WIN; } + +/* Go to LABEL if ADDR (a legitimate address expression) + has an effect that depends on the machine mode it is used for. */ + +#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR,LABEL) \ + if (GET_CODE (ADDR) == PRE_DEC \ + || GET_CODE (ADDR) == POST_DEC \ + || GET_CODE (ADDR) == PRE_INC \ + || GET_CODE (ADDR) == POST_INC) \ + goto LABEL + +/* Define this macro if references to a symbol must be treated + differently depending on something about the variable or + function named by the symbol (such as what section it is in). + + The macro definition, if any, is executed immediately after the + rtl for DECL or other node is created. + The value of the rtl will be a `mem' whose address is a + `symbol_ref'. + + The usual thing for this macro to do is to a flag in the + `symbol_ref' (such as `SYMBOL_REF_FLAG') or to store a modified + name string in the `symbol_ref' (if one bit is not enough + information). + + On the HP-PA we use this to indicate if a symbol is in text or + data space. Also, function labels need special treatment. */ + +#define TEXT_SPACE_P(DECL)\ + (TREE_CODE (DECL) == FUNCTION_DECL \ + || (TREE_CODE (DECL) == VAR_DECL \ + && TREE_READONLY (DECL) && ! TREE_SIDE_EFFECTS (DECL) \ + && (! DECL_INITIAL (DECL) || ! reloc_needed (DECL_INITIAL (DECL))) \ + && !flag_pic) \ + || (TREE_CODE_CLASS (TREE_CODE (DECL)) == 'c' \ + && !(TREE_CODE (DECL) == STRING_CST && flag_writable_strings))) + +#define FUNCTION_NAME_P(NAME) \ +(*(NAME) == '@' || (*(NAME) == '*' && *((NAME) + 1) == '@')) + +#define ENCODE_SECTION_INFO(DECL)\ +do \ + { if (TEXT_SPACE_P (DECL)) \ + { rtx _rtl; \ + if (TREE_CODE (DECL) == FUNCTION_DECL \ + || TREE_CODE (DECL) == VAR_DECL) \ + _rtl = DECL_RTL (DECL); \ + else \ + _rtl = TREE_CST_RTL (DECL); \ + SYMBOL_REF_FLAG (XEXP (_rtl, 0)) = 1; \ + if (TREE_CODE (DECL) == FUNCTION_DECL) \ + hppa_encode_label (XEXP (DECL_RTL (DECL), 0), 0);\ + } \ + } \ +while (0) + +/* Store the user-specified part of SYMBOL_NAME in VAR. + This is sort of inverse to ENCODE_SECTION_INFO. */ + +#define STRIP_NAME_ENCODING(VAR,SYMBOL_NAME) \ + (VAR) = ((SYMBOL_NAME) + ((SYMBOL_NAME)[0] == '*' ? \ + 1 + (SYMBOL_NAME)[1] == '@'\ + : (SYMBOL_NAME)[0] == '@')) + +/* On hpux10, the linker will give an error if we have a reference + in the read-only data section to a symbol defined in a shared + library. Therefore, expressions that might require a reloc can + not be placed in the read-only data section. */ +#define SELECT_SECTION(EXP,RELOC) \ + if (TREE_CODE (EXP) == VAR_DECL \ + && TREE_READONLY (EXP) \ + && !TREE_THIS_VOLATILE (EXP) \ + && DECL_INITIAL (EXP) \ + && (DECL_INITIAL (EXP) == error_mark_node \ + || TREE_CONSTANT (DECL_INITIAL (EXP))) \ + && !RELOC) \ + readonly_data_section (); \ + else if (TREE_CODE_CLASS (TREE_CODE (EXP)) == 'c' \ + && !(TREE_CODE (EXP) == STRING_CST && flag_writable_strings) \ + && !RELOC) \ + readonly_data_section (); \ + else \ + data_section (); + +/* Arghh. The hpux10 linker chokes if we have a reference to symbols + in a readonly data section when the symbol is defined in a shared + library. Since we can't know at compile time if a symbol will be + satisfied by a shared library or main program we put any symbolic + constant into the normal data section. */ +#define SELECT_RTX_SECTION(MODE,RTX) \ + if (symbolic_operand (RTX, MODE)) \ + data_section (); \ + else \ + readonly_data_section (); + +/* Specify the machine mode that this machine uses + for the index in the tablejump instruction. */ +#define CASE_VECTOR_MODE (TARGET_BIG_SWITCH ? TImode : DImode) + +/* Specify the tree operation to be used to convert reals to integers. */ +#define IMPLICIT_FIX_EXPR FIX_ROUND_EXPR + +/* This is the kind of divide that is easiest to do in the general case. */ +#define EASY_DIV_EXPR TRUNC_DIV_EXPR + +/* Define this as 1 if `char' should by default be signed; else as 0. */ +#define DEFAULT_SIGNED_CHAR 1 + +/* Max number of bytes we can move from memory to memory + in one reasonably fast instruction. */ +#define MOVE_MAX 8 + +/* Higher than the default as we prefer to use simple move insns + (better scheduling and delay slot filling) and because our + built-in block move is really a 2X unrolled loop. */ +#define MOVE_RATIO 4 + +/* Define if operations between registers always perform the operation + on the full register even if a narrower mode is specified. */ +#define WORD_REGISTER_OPERATIONS + +/* Define if loading in MODE, an integral mode narrower than BITS_PER_WORD + will either zero-extend or sign-extend. The value of this macro should + be the code that says which one of the two operations is implicitly + done, NIL if none. */ +#define LOAD_EXTEND_OP(MODE) ZERO_EXTEND + +/* Nonzero if access to memory by bytes is slow and undesirable. */ +#define SLOW_BYTE_ACCESS 1 + +/* Do not break .stabs pseudos into continuations. + + This used to be zero (no max length), but big enums and such can + cause huge strings which killed gas. + + We also have to avoid lossage in dbxout.c -- it does not compute the + string size accurately, so we are real conservative here. */ +#define DBX_CONTIN_LENGTH 3000 + +/* Value is 1 if truncating an integer of INPREC bits to OUTPREC bits + is done just by pretending it is already truncated. */ +#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1 + +/* We assume that the store-condition-codes instructions store 0 for false + and some other value for true. This is the value stored for true. */ + +#define STORE_FLAG_VALUE 1 + +/* When a prototype says `char' or `short', really pass an `int'. */ +#define PROMOTE_PROTOTYPES + +/* Specify the machine mode that pointers have. + After generation of rtl, the compiler makes no further distinction + between pointers and any other objects of this machine mode. */ +#define Pmode SImode + +/* Add any extra modes needed to represent the condition code. + + HPPA floating comparisons produce condition codes. */ +#define EXTRA_CC_MODES CCFPmode + +/* Define the names for the modes specified above. */ +#define EXTRA_CC_NAMES "CCFP" + +/* Given a comparison code (EQ, NE, etc.) and the first operand of a COMPARE, + return the mode to be used for the comparison. For floating-point, CCFPmode + should be used. CC_NOOVmode should be used when the first operand is a + PLUS, MINUS, or NEG. CCmode should be used when no special processing is + needed. */ +#define SELECT_CC_MODE(OP,X,Y) \ + (GET_MODE_CLASS (GET_MODE (X)) == MODE_FLOAT ? CCFPmode : CCmode) \ + +/* A function address in a call instruction + is a byte address (for indexing purposes) + so give the MEM rtx a byte's mode. */ +#define FUNCTION_MODE SImode + +/* Define this if addresses of constant functions + shouldn't be put through pseudo regs where they can be cse'd. + Desirable on machines where ordinary constants are expensive + but a CALL with constant address is cheap. */ +#define NO_FUNCTION_CSE + +/* Define this to be nonzero if shift instructions ignore all but the low-order + few bits. */ +#define SHIFT_COUNT_TRUNCATED 1 + +/* Use atexit for static constructors/destructors, instead of defining + our own exit function. */ +#define HAVE_ATEXIT + +/* Compute the cost of computing a constant rtl expression RTX + whose rtx-code is CODE. The body of this macro is a portion + of a switch statement. If the code is computed here, + return it with a return statement. Otherwise, break from the switch. */ + +#define CONST_COSTS(RTX,CODE,OUTER_CODE) \ + case CONST_INT: \ + if (INTVAL (RTX) == 0) return 0; \ + if (INT_14_BITS (RTX)) return 1; \ + case HIGH: \ + return 2; \ + case CONST: \ + case LABEL_REF: \ + case SYMBOL_REF: \ + return 4; \ + case CONST_DOUBLE: \ + if ((RTX == CONST0_RTX (DFmode) || RTX == CONST0_RTX (SFmode)) \ + && OUTER_CODE != SET) \ + return 0; \ + else \ + return 8; + +#define ADDRESS_COST(RTX) \ + (GET_CODE (RTX) == REG ? 1 : hppa_address_cost (RTX)) + +/* Compute extra cost of moving data between one register class + and another. + + Make moves from SAR so expensive they should never happen. We used to + have 0xffff here, but that generates overflow in rare cases. + + Copies involving a FP register and a non-FP register are relatively + expensive because they must go through memory. + + Other copies are reasonably cheap. */ +#define REGISTER_MOVE_COST(CLASS1, CLASS2) \ + (CLASS1 == SHIFT_REGS ? 0x100 \ + : FP_REG_CLASS_P (CLASS1) && ! FP_REG_CLASS_P (CLASS2) ? 16 \ + : FP_REG_CLASS_P (CLASS2) && ! FP_REG_CLASS_P (CLASS1) ? 16 \ + : 2) + + +/* Provide the costs of a rtl expression. This is in the body of a + switch on CODE. The purpose for the cost of MULT is to encourage + `synth_mult' to find a synthetic multiply when reasonable. */ + +#define RTX_COSTS(X,CODE,OUTER_CODE) \ + case MULT: \ + if (GET_MODE_CLASS (GET_MODE (X)) == MODE_FLOAT) \ + return COSTS_N_INSNS (3); \ + return (TARGET_SNAKE && ! TARGET_DISABLE_FPREGS && ! TARGET_SOFT_FLOAT) \ + ? COSTS_N_INSNS (8) : COSTS_N_INSNS (20); \ + case DIV: \ + if (GET_MODE_CLASS (GET_MODE (X)) == MODE_FLOAT) \ + return COSTS_N_INSNS (14); \ + case UDIV: \ + case MOD: \ + case UMOD: \ + return COSTS_N_INSNS (60); \ + case PLUS: /* this includes shNadd insns */ \ + case MINUS: \ + if (GET_MODE_CLASS (GET_MODE (X)) == MODE_FLOAT) \ + return COSTS_N_INSNS (3); \ + return COSTS_N_INSNS (1); \ + case ASHIFT: \ + case ASHIFTRT: \ + case LSHIFTRT: \ + return COSTS_N_INSNS (1); + +/* Adjust the cost of dependencies. */ + +#define ADJUST_COST(INSN,LINK,DEP,COST) \ + (COST) = pa_adjust_cost (INSN, LINK, DEP, COST) + +/* Adjust scheduling priorities. We use this to try and keep addil + and the next use of %r1 close together. */ +#define ADJUST_PRIORITY(PREV) \ + { \ + rtx set = single_set (PREV); \ + rtx src, dest; \ + if (set) \ + { \ + src = SET_SRC (set); \ + dest = SET_DEST (set); \ + if (GET_CODE (src) == LO_SUM \ + && symbolic_operand (XEXP (src, 1), VOIDmode) \ + && ! read_only_operand (XEXP (src, 1), VOIDmode)) \ + INSN_PRIORITY (PREV) >>= 3; \ + else if (GET_CODE (src) == MEM \ + && GET_CODE (XEXP (src, 0)) == LO_SUM \ + && symbolic_operand (XEXP (XEXP (src, 0), 1), VOIDmode)\ + && ! read_only_operand (XEXP (XEXP (src, 0), 1), VOIDmode))\ + INSN_PRIORITY (PREV) >>= 1; \ + else if (GET_CODE (dest) == MEM \ + && GET_CODE (XEXP (dest, 0)) == LO_SUM \ + && symbolic_operand (XEXP (XEXP (dest, 0), 1), VOIDmode)\ + && ! read_only_operand (XEXP (XEXP (dest, 0), 1), VOIDmode))\ + INSN_PRIORITY (PREV) >>= 3; \ + } \ + } + +/* Handling the special cases is going to get too complicated for a macro, + just call `pa_adjust_insn_length' to do the real work. */ +#define ADJUST_INSN_LENGTH(INSN, LENGTH) \ + LENGTH += pa_adjust_insn_length (INSN, LENGTH); + +/* Millicode insns are actually function calls with some special + constraints on arguments and register usage. + + Millicode calls always expect their arguments in the integer argument + registers, and always return their result in %r29 (ret1). They + are expected to clobber their arguments, %r1, %r29, and %r31 and + nothing else. + + These macros tell reorg that the references to arguments and + register clobbers for millicode calls do not appear to happen + until after the millicode call. This allows reorg to put insns + which set the argument registers into the delay slot of the millicode + call -- thus they act more like traditional CALL_INSNs. + + get_attr_type will try to recognize the given insn, so make sure to + filter out things it will not accept -- SEQUENCE, USE and CLOBBER insns + in particular. */ +#define INSN_SETS_ARE_DELAYED(X) (insn_sets_and_refs_are_delayed (X)) +#define INSN_REFERENCES_ARE_DELAYED(X) (insn_sets_and_refs_are_delayed (X)) + + +/* Control the assembler format that we output. */ + +/* Output at beginning of assembler file. */ + +#define ASM_FILE_START(FILE) \ +do { fputs ("\t.SPACE $PRIVATE$\n\ +\t.SUBSPA $DATA$,QUAD=1,ALIGN=8,ACCESS=31\n\ +\t.SUBSPA $BSS$,QUAD=1,ALIGN=8,ACCESS=31,ZERO,SORT=82\n\ +\t.SPACE $TEXT$\n\ +\t.SUBSPA $LIT$,QUAD=0,ALIGN=8,ACCESS=44\n\ +\t.SUBSPA $CODE$,QUAD=0,ALIGN=8,ACCESS=44,CODE_ONLY\n\ +\t.IMPORT $global$,DATA\n\ +\t.IMPORT $$dyncall,MILLICODE\n", FILE);\ + if (profile_flag)\ + fprintf (FILE, "\t.IMPORT _mcount, CODE\n");\ + if (write_symbols != NO_DEBUG) \ + output_file_directive ((FILE), main_input_filename); \ + } while (0) + +#define ASM_FILE_END(FILE) output_deferred_plabels (FILE) + +/* Output to assembler file text saying following lines + may contain character constants, extra white space, comments, etc. */ + +#define ASM_APP_ON "" + +/* Output to assembler file text saying following lines + no longer contain unusual constructs. */ + +#define ASM_APP_OFF "" + +/* We don't yet know how to identify GCC to HP-PA machines. */ +#define ASM_IDENTIFY_GCC(FILE) fputs ("; gcc_compiled.:\n", FILE) + +/* Output before code. */ + +/* Supposedly the assembler rejects the command if there is no tab! */ +#define TEXT_SECTION_ASM_OP "\t.SPACE $TEXT$\n\t.SUBSPA $CODE$\n" + +/* Output before read-only data. */ + +/* Supposedly the assembler rejects the command if there is no tab! */ +#define READONLY_DATA_ASM_OP "\t.SPACE $TEXT$\n\t.SUBSPA $LIT$\n" + +#define READONLY_DATA_SECTION readonly_data + +/* Output before writable data. */ + +/* Supposedly the assembler rejects the command if there is no tab! */ +#define DATA_SECTION_ASM_OP "\t.SPACE $PRIVATE$\n\t.SUBSPA $DATA$\n" + +/* Output before uninitialized data. */ + +#define BSS_SECTION_ASM_OP "\t.SPACE $PRIVATE$\n\t.SUBSPA $BSS$\n" + +/* Define the .bss section for ASM_OUTPUT_LOCAL to use. */ + +#ifndef CTORS_SECTION_FUNCTION +#define EXTRA_SECTIONS in_readonly_data +#define CTORS_SECTION_FUNCTION +#define DTORS_SECTION_FUNCTION +#else +#define EXTRA_SECTIONS in_readonly_data, in_ctors, in_dtors +#endif + +/* Switch into a generic section. + This is currently only used to support section attributes. + + We make the section read-only and executable for a function decl, + read-only for a const data decl, and writable for a non-const data decl. */ +#define ASM_OUTPUT_SECTION_NAME(FILE, DECL, NAME, RELOC) \ + if (DECL && TREE_CODE (DECL) == FUNCTION_DECL) \ + { \ + fputs ("\t.SPACE $TEXT$\n", FILE); \ + fprintf (FILE, \ + "\t.SUBSPA %s%s%s,QUAD=0,ALIGN=8,ACCESS=44,CODE_ONLY,SORT=24\n",\ + TARGET_GAS ? "" : "$", NAME, TARGET_GAS ? "" : "$"); \ + } \ + else if (DECL && DECL_READONLY_SECTION (DECL, RELOC)) \ + { \ + fputs ("\t.SPACE $TEXT$\n", FILE); \ + fprintf (FILE, \ + "\t.SUBSPA %s%s%s,QUAD=0,ALIGN=8,ACCESS=44,SORT=16\n", \ + TARGET_GAS ? "" : "$", NAME, TARGET_GAS ? "" : "$"); \ + } \ + else \ + { \ + fputs ("\t.SPACE $PRIVATE$\n", FILE); \ + fprintf (FILE, \ + "\t.SUBSPA %s%s%s,QUAD=1,ALIGN=8,ACCESS=31,SORT=16\n", \ + TARGET_GAS ? "" : "$", NAME, TARGET_GAS ? "" : "$"); \ + } + +/* FIXME: HPUX ld generates incorrect GOT entries for "T" fixups + which reference data within the $TEXT$ space (for example constant + strings in the $LIT$ subspace). + + The assemblers (GAS and HP as) both have problems with handling + the difference of two symbols which is the other correct way to + reference constant data during PIC code generation. + + So, there's no way to reference constant data which is in the + $TEXT$ space during PIC generation. Instead place all constant + data into the $PRIVATE$ subspace (this reduces sharing, but it + works correctly). */ + +#define EXTRA_SECTION_FUNCTIONS \ +void \ +readonly_data () \ +{ \ + if (in_section != in_readonly_data) \ + { \ + if (flag_pic) \ + fprintf (asm_out_file, "%s\n", DATA_SECTION_ASM_OP); \ + else \ + fprintf (asm_out_file, "%s\n", READONLY_DATA_ASM_OP); \ + in_section = in_readonly_data; \ + } \ +} \ +CTORS_SECTION_FUNCTION \ +DTORS_SECTION_FUNCTION + + +/* How to refer to registers in assembler output. + This sequence is indexed by compiler's hard-register-number (see above). */ + +#define REGISTER_NAMES \ +{"%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7", \ + "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", \ + "%r16", "%r17", "%r18", "%r19", "%r20", "%r21", "%r22", "%r23", \ + "%r24", "%r25", "%r26", "%r27", "%r28", "%r29", "%r30", "%r31", \ + "%fr4", "%fr4R", "%fr5", "%fr5R", "%fr6", "%fr6R", "%fr7", "%fr7R", \ + "%fr8", "%fr8R", "%fr9", "%fr9R", "%fr10", "%fr10R", "%fr11", "%fr11R", \ + "%fr12", "%fr12R", "%fr13", "%fr13R", "%fr14", "%fr14R", "%fr15", "%fr15R", \ + "%fr16", "%fr16R", "%fr17", "%fr17R", "%fr18", "%fr18R", "%fr19", "%fr19R", \ + "%fr20", "%fr20R", "%fr21", "%fr21R", "%fr22", "%fr22R", "%fr23", "%fr23R", \ + "%fr24", "%fr24R", "%fr25", "%fr25R", "%fr26", "%fr26R", "%fr27", "%fr27R", \ + "%fr28", "%fr28R", "%fr29", "%fr29R", "%fr30", "%fr30R", "%fr31", "%fr31R", \ + "SAR"} + +#define ADDITIONAL_REGISTER_NAMES \ +{{"%fr4L",32}, {"%fr5L",34}, {"%fr6L",36}, {"%fr7L",38}, \ + {"%fr8L",40}, {"%fr9L",42}, {"%fr10L",44}, {"%fr11L",46}, \ + {"%fr12L",48}, {"%fr13L",50}, {"%fr14L",52}, {"%fr15L",54}, \ + {"%fr16L",56}, {"%fr17L",58}, {"%fr18L",60}, {"%fr19L",62}, \ + {"%fr20L",64}, {"%fr21L",66}, {"%fr22L",68}, {"%fr23L",70}, \ + {"%fr24L",72}, {"%fr25L",74}, {"%fr26L",76}, {"%fr27L",78}, \ + {"%fr28L",80}, {"%fr29L",82}, {"%fr30L",84}, {"%fr31R",86}, \ + {"%cr11",88}} + +/* How to renumber registers for dbx and gdb. + + Registers 0 - 31 remain unchanged. + + Registers 32 - 87 are mapped to 72 - 127 + + Register 88 is mapped to 32. */ + +#define DBX_REGISTER_NUMBER(REGNO) \ + ((REGNO) <= 31 ? (REGNO) : \ + ((REGNO) > 31 && (REGNO) <= 87 ? (REGNO) + 40 : 32)) + +/* This is how to output the definition of a user-level label named NAME, + such as the label on a static function or variable NAME. */ + +#define ASM_OUTPUT_LABEL(FILE, NAME) \ + do { assemble_name (FILE, NAME); \ + fputc ('\n', FILE); } while (0) + +/* This is how to output a command to make the user-level label named NAME + defined for reference from other files. + + We call assemble_name, which in turn sets TREE_SYMBOL_REFERENCED. This + macro will restore the original value of TREE_SYMBOL_REFERENCED to avoid + placing useless function definitions in the output file. */ + +#define ASM_OUTPUT_EXTERNAL(FILE, DECL, NAME) \ + do { int save_referenced; \ + save_referenced = TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (DECL)); \ + fputs ("\t.IMPORT ", FILE); \ + assemble_name (FILE, NAME); \ + if (FUNCTION_NAME_P (NAME)) \ + fputs (",CODE\n", FILE); \ + else \ + fputs (",DATA\n", FILE); \ + TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (DECL)) = save_referenced; \ + } while (0) + +/* The bogus HP assembler requires ALL external references to be + "imported", even library calls. They look a bit different, so + here's this macro. + + Also note not all libcall names are passed to ENCODE_SECTION_INFO + (__main for example). To make sure all libcall names have section + info recorded in them, we do it here. */ + +#define ASM_OUTPUT_EXTERNAL_LIBCALL(FILE, RTL) \ + do { fputs ("\t.IMPORT ", FILE); \ + if (!function_label_operand (RTL, VOIDmode)) \ + hppa_encode_label (RTL, 1); \ + assemble_name (FILE, XSTR ((RTL), 0)); \ + fputs (",CODE\n", FILE); \ + } while (0) + +#define ASM_GLOBALIZE_LABEL(FILE, NAME) \ + do { \ + /* We only handle DATA objects here, functions are globalized in \ + ASM_DECLARE_FUNCTION_NAME. */ \ + if (! FUNCTION_NAME_P (NAME)) \ + { \ + fputs ("\t.EXPORT ", FILE); \ + assemble_name (FILE, NAME); \ + fputs (",DATA\n", FILE); \ + } \ + } while (0) + +/* This is how to output a reference to a user-level label named NAME. + `assemble_name' uses this. */ + +#define ASM_OUTPUT_LABELREF(FILE,NAME) \ + fprintf ((FILE), "%s", (NAME) + (FUNCTION_NAME_P (NAME) ? 1 : 0)) + +/* This is how to output an internal numbered label where + PREFIX is the class of label and NUM is the number within the class. */ + +#define ASM_OUTPUT_INTERNAL_LABEL(FILE,PREFIX,NUM) \ + {fprintf (FILE, "%c$%s%04d\n", (PREFIX)[0], (PREFIX) + 1, NUM);} + +/* This is how to store into the string LABEL + the symbol_ref name of an internal numbered label where + PREFIX is the class of label and NUM is the number within the class. + This is suitable for output with `assemble_name'. */ + +#define ASM_GENERATE_INTERNAL_LABEL(LABEL,PREFIX,NUM) \ + sprintf (LABEL, "*%c$%s%04d", (PREFIX)[0], (PREFIX) + 1, NUM) + +/* This is how to output an assembler line defining a `double' constant. */ + +#define ASM_OUTPUT_DOUBLE(FILE,VALUE) \ + do { long l[2]; \ + REAL_VALUE_TO_TARGET_DOUBLE (VALUE, l); \ + fprintf (FILE, "\t.word 0x%lx\n\t.word 0x%lx\n", l[0], l[1]); \ + } while (0) + +/* This is how to output an assembler line defining a `float' constant. */ + +#define ASM_OUTPUT_FLOAT(FILE,VALUE) \ + do { long l; \ + REAL_VALUE_TO_TARGET_SINGLE (VALUE, l); \ + fprintf (FILE, "\t.word 0x%lx\n", l); \ + } while (0) + +/* This is how to output an assembler line defining an `int' constant. + + This is made more complicated by the fact that functions must be + prefixed by a P% as well as code label references for the exception + table -- otherwise the linker chokes. */ + +#define ASM_OUTPUT_INT(FILE,VALUE) \ +{ fputs ("\t.word ", FILE); \ + if (function_label_operand (VALUE, VOIDmode) \ + && !TARGET_PORTABLE_RUNTIME) \ + fputs ("P%", FILE); \ + output_addr_const (FILE, (VALUE)); \ + fputs ("\n", FILE);} + +/* Likewise for `short' and `char' constants. */ + +#define ASM_OUTPUT_SHORT(FILE,VALUE) \ +( fputs ("\t.half ", FILE), \ + output_addr_const (FILE, (VALUE)), \ + fputs ("\n", FILE)) + +#define ASM_OUTPUT_CHAR(FILE,VALUE) \ +( fputs ("\t.byte ", FILE), \ + output_addr_const (FILE, (VALUE)), \ + fputs ("\n", FILE)) + +/* This is how to output an assembler line for a numeric constant byte. */ + +#define ASM_OUTPUT_BYTE(FILE,VALUE) \ + fprintf (FILE, "\t.byte 0x%x\n", (VALUE)) + +#define ASM_OUTPUT_ASCII(FILE, P, SIZE) \ + output_ascii ((FILE), (P), (SIZE)) + +#define ASM_OUTPUT_REG_PUSH(FILE,REGNO) +#define ASM_OUTPUT_REG_POP(FILE,REGNO) +/* This is how to output an element of a case-vector that is absolute. + Note that this method makes filling these branch delay slots + impossible. */ + +#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \ + if (TARGET_BIG_SWITCH) \ + fprintf (FILE, "\tstw %%r1,-16(%%r30)\n\tldil LR'L$%04d,%%r1\n\tbe RR'L$%04d(%%sr4,%%r1)\n\tldw -16(%%r30),%%r1\n", VALUE, VALUE); \ + else \ + fprintf (FILE, "\tb L$%04d\n\tnop\n", VALUE) + +/* Jump tables are executable code and live in the TEXT section on the PA. */ +#define JUMP_TABLES_IN_TEXT_SECTION 1 + +/* This is how to output an element of a case-vector that is relative. + This must be defined correctly as it is used when generating PIC code. + + I believe it safe to use the same definition as ASM_OUTPUT_ADDR_VEC_ELT + on the PA since ASM_OUTPUT_ADDR_VEC_ELT uses pc-relative jump instructions + rather than a table of absolute addresses. */ + +#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \ + if (TARGET_BIG_SWITCH) \ + fprintf (FILE, "\tstw %%r1,-16(%%r30)\n\tldw T'L$%04d(%%r19),%%r1\n\tbv 0(%%r1)\n\tldw -16(%%r30),%%r1\n", VALUE); \ + else \ + fprintf (FILE, "\tb L$%04d\n\tnop\n", VALUE) + +/* This is how to output an assembler line + that says to advance the location counter + to a multiple of 2**LOG bytes. */ + +#define ASM_OUTPUT_ALIGN(FILE,LOG) \ + fprintf (FILE, "\t.align %d\n", (1<<(LOG))) + +#define ASM_OUTPUT_SKIP(FILE,SIZE) \ + fprintf (FILE, "\t.blockz %d\n", (SIZE)) + +/* This says how to output an assembler line to define a global common symbol + with size SIZE (in bytes) and alignment ALIGN (in bits). */ + +#define ASM_OUTPUT_ALIGNED_COMMON(FILE, NAME, SIZE, ALIGNED) \ +{ bss_section (); \ + assemble_name ((FILE), (NAME)); \ + fputs ("\t.comm ", (FILE)); \ + fprintf ((FILE), "%d\n", MAX ((SIZE), ((ALIGNED) / BITS_PER_UNIT)));} + +/* This says how to output an assembler line to define a local common symbol + with size SIZE (in bytes) and alignment ALIGN (in bits). */ + +#define ASM_OUTPUT_ALIGNED_LOCAL(FILE, NAME, SIZE, ALIGNED) \ +{ bss_section (); \ + fprintf ((FILE), "\t.align %d\n", ((ALIGNED) / BITS_PER_UNIT)); \ + assemble_name ((FILE), (NAME)); \ + fprintf ((FILE), "\n\t.block %d\n", (SIZE));} + +/* Store in OUTPUT a string (made with alloca) containing + an assembler-name for a local static variable named NAME. + LABELNO is an integer which is different for each call. */ + +#define ASM_FORMAT_PRIVATE_NAME(OUTPUT, NAME, LABELNO) \ +( (OUTPUT) = (char *) alloca (strlen ((NAME)) + 12), \ + sprintf ((OUTPUT), "%s___%d", (NAME), (LABELNO))) + +/* Define the parentheses used to group arithmetic operations + in assembler code. */ + +#define ASM_OPEN_PAREN "(" +#define ASM_CLOSE_PAREN ")" + +/* All HP assemblers use "!" to separate logical lines. */ +#define IS_ASM_LOGICAL_LINE_SEPARATOR(C) ((C) == '!') + +/* Define results of standard character escape sequences. */ +#define TARGET_BELL 007 +#define TARGET_BS 010 +#define TARGET_TAB 011 +#define TARGET_NEWLINE 012 +#define TARGET_VT 013 +#define TARGET_FF 014 +#define TARGET_CR 015 + +#define PRINT_OPERAND_PUNCT_VALID_P(CHAR) \ + ((CHAR) == '@' || (CHAR) == '#' || (CHAR) == '*' || (CHAR) == '^') + +/* Print operand X (an rtx) in assembler syntax to file FILE. + CODE is a letter or dot (`z' in `%z0') or 0 if no letter was specified. + For `%' followed by punctuation, CODE is the punctuation and X is null. + + On the HP-PA, the CODE can be `r', meaning this is a register-only operand + and an immediate zero should be represented as `r0'. + + Several % codes are defined: + O an operation + C compare conditions + N extract conditions + M modifier to handle preincrement addressing for memory refs. + F modifier to handle preincrement addressing for fp memory refs */ + +#define PRINT_OPERAND(FILE, X, CODE) print_operand (FILE, X, CODE) + + +/* Print a memory address as an operand to reference that memory location. */ + +#define PRINT_OPERAND_ADDRESS(FILE, ADDR) \ +{ register rtx addr = ADDR; \ + register rtx base; \ + int offset; \ + switch (GET_CODE (addr)) \ + { \ + case REG: \ + fprintf (FILE, "0(0,%s)", reg_names [REGNO (addr)]); \ + break; \ + case PLUS: \ + if (GET_CODE (XEXP (addr, 0)) == CONST_INT) \ + offset = INTVAL (XEXP (addr, 0)), base = XEXP (addr, 1); \ + else if (GET_CODE (XEXP (addr, 1)) == CONST_INT) \ + offset = INTVAL (XEXP (addr, 1)), base = XEXP (addr, 0); \ + else \ + abort (); \ + fprintf (FILE, "%d(0,%s)", offset, reg_names [REGNO (base)]); \ + break; \ + case LO_SUM: \ + if (!symbolic_operand (XEXP (addr, 1))) \ + fputs ("R'", FILE); \ + else if (flag_pic == 0) \ + fputs ("RR'", FILE); \ + else if (flag_pic == 1) \ + abort (); \ + else if (flag_pic == 2) \ + fputs ("RT'", FILE); \ + output_global_address (FILE, XEXP (addr, 1), 0); \ + fputs ("(", FILE); \ + output_operand (XEXP (addr, 0), 0); \ + fputs (")", FILE); \ + break; \ + case CONST_INT: \ + fprintf (FILE, "%d(0,0)", INTVAL (addr)); \ + break; \ + default: \ + output_addr_const (FILE, addr); \ + }} + + +/* Define functions in pa.c and used in insn-output.c. */ + +extern char *output_and (); +extern char *output_ior (); +extern char *output_move_double (); +extern char *output_fp_move_double (); +extern char *output_block_move (); +extern char *output_cbranch (); +extern char *output_bb (); +extern char *output_bvb (); +extern char *output_dbra (); +extern char *output_movb (); +extern char *output_parallel_movb (); +extern char *output_parallel_addb (); +extern char *output_return (); +extern char *output_call (); +extern char *output_millicode_call (); +extern char *output_mul_insn (); +extern char *output_div_insn (); +extern char *output_mod_insn (); +extern char *singlemove_string (); +extern void output_arg_descriptor (); +extern void output_deferred_plabels (); +extern void override_options (); +extern void output_ascii (); +extern void output_function_prologue (); +extern void output_function_epilogue (); +extern void output_global_address (); +extern void print_operand (); +extern struct rtx_def *legitimize_pic_address (); +extern struct rtx_def *gen_cmp_fp (); +extern void hppa_encode_label (); +extern int arith11_operand (); +extern int symbolic_expression_p (); +extern int reloc_needed (); +extern int compute_frame_size (); +extern int hppa_address_cost (); +extern int and_mask_p (); +extern int symbolic_memory_operand (); +extern int pa_adjust_cost (); +extern int pa_adjust_insn_length (); +extern int int11_operand (); +extern int reg_or_cint_move_operand (); +extern int arith5_operand (); +extern int uint5_operand (); +extern int pic_label_operand (); +extern int plus_xor_ior_operator (); +extern int basereg_operand (); +extern int shadd_operand (); +extern int arith_operand (); +extern int read_only_operand (); +extern int move_operand (); +extern int and_operand (); +extern int ior_operand (); +extern int arith32_operand (); +extern int uint32_operand (); +extern int reg_or_nonsymb_mem_operand (); +extern int reg_or_0_operand (); +extern int reg_or_0_or_nonsymb_mem_operand (); +extern int pre_cint_operand (); +extern int post_cint_operand (); +extern int div_operand (); +extern int int5_operand (); +extern int movb_comparison_operator (); +extern int ireg_or_int5_operand (); +extern int fmpyaddoperands (); +extern int fmpysuboperands (); +extern int call_operand_address (); +extern int cint_ok_for_move (); +extern int ior_operand (); +extern void emit_bcond_fp (); +extern int emit_move_sequence (); +extern int emit_hpdiv_const (); +extern void hppa_expand_prologue (); +extern void hppa_expand_epilogue (); +extern int hppa_can_use_return_insn_p (); +extern int is_function_label_plus_const (); +extern int jump_in_call_delay (); +extern enum reg_class secondary_reload_class (); +extern int insn_sets_and_refs_are_delayed (); + +/* Declare functions defined in pa.c and used in templates. */ + +extern struct rtx_def *return_addr_rtx (); + +/* We want __gcc_plt_call to appear in every program built by + gcc, so we make a reference to it out of __main. + We use the asm statement to fool the optimizer into not + removing the dead (but important) initialization of + REFERENCE. */ + +#define DO_GLOBAL_DTORS_BODY \ +do { \ + extern void __gcc_plt_call (); \ + void (*reference)() = &__gcc_plt_call; \ + func_ptr *p; \ + __asm__ ("" : : "r" (reference)); \ + for (p = __DTOR_LIST__ + 1; *p; ) \ + (*p++) (); \ +} while (0) + +/* Find the return address associated with the frame given by + FRAMEADDR. */ +#define RETURN_ADDR_RTX(COUNT, FRAMEADDR) \ + (return_addr_rtx (COUNT, FRAMEADDR)) + +/* Used to mask out junk bits from the return address, such as + processor state, interrupt status, condition codes and the like. */ +#define MASK_RETURN_ADDR \ + /* The privilege level is in the two low order bits, mask em out \ + of the return address. */ \ + (GEN_INT (0xfffffffc)) + +/* The number of Pmode words for the setjmp buffer. */ +#define JMP_BUF_SIZE 50 diff --git a/gcc/config/pa/pa.md b/gcc/config/pa/pa.md new file mode 100755 index 0000000..174bbd7 --- /dev/null +++ b/gcc/config/pa/pa.md @@ -0,0 +1,5729 @@ +;;- Machine description for HP PA-RISC architecture for GNU C compiler +;; Copyright (C) 1992, 93-98, 1999 Free Software Foundation, Inc. +;; Contributed by the Center for Software Science at the University +;; of Utah. + +;; This file is part of GNU CC. + +;; GNU CC 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. + +;; GNU CC 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 GNU CC; see the file COPYING. If not, write to +;; the Free Software Foundation, 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;; This gcc Version 2 machine description is inspired by sparc.md and +;; mips.md. + +;;- See file "rtl.def" for documentation on define_insn, match_*, et. al. + +;; Insn type. Used to default other attribute values. + +;; type "unary" insns have one input operand (1) and one output operand (0) +;; type "binary" insns have two input operands (1,2) and one output (0) + +(define_attr "type" + "move,unary,binary,shift,nullshift,compare,load,store,uncond_branch,branch,cbranch,fbranch,call,dyncall,fpload,fpstore,fpalu,fpcc,fpmulsgl,fpmuldbl,fpdivsgl,fpdivdbl,fpsqrtsgl,fpsqrtdbl,multi,milli,parallel_branch" + (const_string "binary")) + +(define_attr "pa_combine_type" + "fmpy,faddsub,uncond_branch,addmove,none" + (const_string "none")) + +;; Processor type (for scheduling, not code generation) -- this attribute +;; must exactly match the processor_type enumeration in pa.h. +;; +;; FIXME: Add 800 scheduling for completeness? + +;; CYGNUS LOCAL PA8000/law +(define_attr "cpu" "700,7100,7100LC,7200,8000" (const (symbol_ref "pa_cpu_attr"))) +;; END CYGNUS LOCAL + +;; Length (in # of insns). +(define_attr "length" "" + (cond [(eq_attr "type" "load,fpload") + (if_then_else (match_operand 1 "symbolic_memory_operand" "") + (const_int 8) (const_int 4)) + + (eq_attr "type" "store,fpstore") + (if_then_else (match_operand 0 "symbolic_memory_operand" "") + (const_int 8) (const_int 4)) + + (eq_attr "type" "binary,shift,nullshift") + (if_then_else (match_operand 2 "arith_operand" "") + (const_int 4) (const_int 12)) + + (eq_attr "type" "move,unary,shift,nullshift") + (if_then_else (match_operand 1 "arith_operand" "") + (const_int 4) (const_int 8))] + + (const_int 4))) + +(define_asm_attributes + [(set_attr "length" "4") + (set_attr "type" "multi")]) + +;; Attributes for instruction and branch scheduling + +;; For conditional branches. +(define_attr "in_branch_delay" "false,true" + (if_then_else (and (eq_attr "type" "!uncond_branch,branch,cbranch,fbranch,call,dyncall,multi,milli,parallel_branch") + (eq_attr "length" "4")) + (const_string "true") + (const_string "false"))) + +;; Disallow instructions which use the FPU since they will tie up the FPU +;; even if the instruction is nullified. +(define_attr "in_nullified_branch_delay" "false,true" + (if_then_else (and (eq_attr "type" "!uncond_branch,branch,cbranch,fbranch,call,dyncall,multi,milli,fpcc,fpalu,fpmulsgl,fpmuldbl,fpdivsgl,fpdivdbl,fpsqrtsgl,fpsqrtdbl,parallel_branch") + (eq_attr "length" "4")) + (const_string "true") + (const_string "false"))) + +;; For calls and millicode calls. Allow unconditional branches in the +;; delay slot. +(define_attr "in_call_delay" "false,true" + (cond [(and (eq_attr "type" "!uncond_branch,branch,cbranch,fbranch,call,dyncall,multi,milli,parallel_branch") + (eq_attr "length" "4")) + (const_string "true") + (eq_attr "type" "uncond_branch") + (if_then_else (ne (symbol_ref "TARGET_JUMP_IN_DELAY") + (const_int 0)) + (const_string "true") + (const_string "false"))] + (const_string "false"))) + + +;; Call delay slot description. +(define_delay (eq_attr "type" "call") + [(eq_attr "in_call_delay" "true") (nil) (nil)]) + +;; millicode call delay slot description. Note it disallows delay slot +;; when TARGET_PORTABLE_RUNTIME is true. +(define_delay (eq_attr "type" "milli") + [(and (eq_attr "in_call_delay" "true") + (eq (symbol_ref "TARGET_PORTABLE_RUNTIME") (const_int 0))) + (nil) (nil)]) + +;; Return and other similar instructions. +(define_delay (eq_attr "type" "branch,parallel_branch") + [(eq_attr "in_branch_delay" "true") (nil) (nil)]) + +;; Floating point conditional branch delay slot description and +(define_delay (eq_attr "type" "fbranch") + [(eq_attr "in_branch_delay" "true") + (eq_attr "in_nullified_branch_delay" "true") + (nil)]) + +;; Integer conditional branch delay slot description. +;; Nullification of conditional branches on the PA is dependent on the +;; direction of the branch. Forward branches nullify true and +;; backward branches nullify false. If the direction is unknown +;; then nullification is not allowed. +(define_delay (eq_attr "type" "cbranch") + [(eq_attr "in_branch_delay" "true") + (and (eq_attr "in_nullified_branch_delay" "true") + (attr_flag "forward")) + (and (eq_attr "in_nullified_branch_delay" "true") + (attr_flag "backward"))]) + +(define_delay (and (eq_attr "type" "uncond_branch") + (eq (symbol_ref "following_call (insn)") + (const_int 0))) + [(eq_attr "in_branch_delay" "true") (nil) (nil)]) + +;; Function units of the HPPA. The following data is for the 700 CPUs +;; (Mustang CPU + Timex FPU aka PA-89) because that's what I have the docs for. +;; Scheduling instructions for PA-83 machines according to the Snake +;; constraints shouldn't hurt. + +;; (define_function_unit {name} {num-units} {n-users} {test} +;; {ready-delay} {issue-delay} [{conflict-list}]) + +;; The integer ALU. +;; (Noted only for documentation; units that take one cycle do not need to +;; be specified.) + +;; (define_function_unit "alu" 1 0 +;; (and (eq_attr "type" "unary,shift,nullshift,binary,move,address") +;; (eq_attr "cpu" "700")) +;; 1 0) + + +;; Memory. Disregarding Cache misses, the Mustang memory times are: +;; load: 2, fpload: 3 +;; store, fpstore: 3, no D-cache operations should be scheduled. + +(define_function_unit "pa700memory" 1 0 + (and (eq_attr "type" "load,fpload") + (eq_attr "cpu" "700")) 2 0) +(define_function_unit "pa700memory" 1 0 + (and (eq_attr "type" "store,fpstore") + (eq_attr "cpu" "700")) 3 3) + +;; The Timex (aka 700) has two floating-point units: ALU, and MUL/DIV/SQRT. +;; Timings: +;; Instruction Time Unit Minimum Distance (unit contention) +;; fcpy 3 ALU 2 +;; fabs 3 ALU 2 +;; fadd 3 ALU 2 +;; fsub 3 ALU 2 +;; fcmp 3 ALU 2 +;; fcnv 3 ALU 2 +;; fmpyadd 3 ALU,MPY 2 +;; fmpysub 3 ALU,MPY 2 +;; fmpycfxt 3 ALU,MPY 2 +;; fmpy 3 MPY 2 +;; fmpyi 3 MPY 2 +;; fdiv,sgl 10 MPY 10 +;; fdiv,dbl 12 MPY 12 +;; fsqrt,sgl 14 MPY 14 +;; fsqrt,dbl 18 MPY 18 + +(define_function_unit "pa700fp_alu" 1 0 + (and (eq_attr "type" "fpcc") + (eq_attr "cpu" "700")) 4 2) +(define_function_unit "pa700fp_alu" 1 0 + (and (eq_attr "type" "fpalu") + (eq_attr "cpu" "700")) 3 2) +(define_function_unit "pa700fp_mpy" 1 0 + (and (eq_attr "type" "fpmulsgl,fpmuldbl") + (eq_attr "cpu" "700")) 3 2) +(define_function_unit "pa700fp_mpy" 1 0 + (and (eq_attr "type" "fpdivsgl") + (eq_attr "cpu" "700")) 10 10) +(define_function_unit "pa700fp_mpy" 1 0 + (and (eq_attr "type" "fpdivdbl") + (eq_attr "cpu" "700")) 12 12) +(define_function_unit "pa700fp_mpy" 1 0 + (and (eq_attr "type" "fpsqrtsgl") + (eq_attr "cpu" "700")) 14 14) +(define_function_unit "pa700fp_mpy" 1 0 + (and (eq_attr "type" "fpsqrtdbl") + (eq_attr "cpu" "700")) 18 18) + +;; Function units for the 7100 and 7150. The 7100/7150 can dual-issue +;; floating point computations with non-floating point computations (fp loads +;; and stores are not fp computations). +;; + +;; Memory. Disregarding Cache misses, memory loads take two cycles; stores also +;; take two cycles, during which no Dcache operations should be scheduled. +;; Any special cases are handled in pa_adjust_cost. The 7100, 7150 and 7100LC +;; all have the same memory characteristics if one disregards cache misses. +(define_function_unit "pa7100memory" 1 0 + (and (eq_attr "type" "load,fpload") + (eq_attr "cpu" "7100,7100LC")) 2 0) +(define_function_unit "pa7100memory" 1 0 + (and (eq_attr "type" "store,fpstore") + (eq_attr "cpu" "7100,7100LC")) 2 2) + +;; The 7100/7150 has three floating-point units: ALU, MUL, and DIV. +;; Timings: +;; Instruction Time Unit Minimum Distance (unit contention) +;; fcpy 2 ALU 1 +;; fabs 2 ALU 1 +;; fadd 2 ALU 1 +;; fsub 2 ALU 1 +;; fcmp 2 ALU 1 +;; fcnv 2 ALU 1 +;; fmpyadd 2 ALU,MPY 1 +;; fmpysub 2 ALU,MPY 1 +;; fmpycfxt 2 ALU,MPY 1 +;; fmpy 2 MPY 1 +;; fmpyi 2 MPY 1 +;; fdiv,sgl 8 DIV 8 +;; fdiv,dbl 15 DIV 15 +;; fsqrt,sgl 8 DIV 8 +;; fsqrt,dbl 15 DIV 15 + +(define_function_unit "pa7100fp_alu" 1 0 + (and (eq_attr "type" "fpcc,fpalu") + (eq_attr "cpu" "7100")) 2 1) +(define_function_unit "pa7100fp_mpy" 1 0 + (and (eq_attr "type" "fpmulsgl,fpmuldbl") + (eq_attr "cpu" "7100")) 2 1) +(define_function_unit "pa7100fp_div" 1 0 + (and (eq_attr "type" "fpdivsgl,fpsqrtsgl") + (eq_attr "cpu" "7100")) 8 8) +(define_function_unit "pa7100fp_div" 1 0 + (and (eq_attr "type" "fpdivdbl,fpsqrtdbl") + (eq_attr "cpu" "7100")) 15 15) + +;; To encourage dual issue we define function units corresponding to +;; the instructions which can be dual issued. This is a rather crude +;; approximation, the "pa7100nonflop" test in particular could be refined. +(define_function_unit "pa7100flop" 1 1 + (and + (eq_attr "type" "fpcc,fpalu,fpmulsgl,fpmuldbl,fpdivsgl,fpsqrtsgl,fpdivdbl,fpsqrtdbl") + (eq_attr "cpu" "7100")) 1 1) + +(define_function_unit "pa7100nonflop" 1 1 + (and + (eq_attr "type" "!fpcc,fpalu,fpmulsgl,fpmuldbl,fpdivsgl,fpsqrtsgl,fpdivdbl,fpsqrtdbl") + (eq_attr "cpu" "7100")) 1 1) + + +;; Memory subsystem works just like 7100/7150 (except for cache miss times which +;; we don't model here). + +;; The 7100LC has three floating-point units: ALU, MUL, and DIV. +;; Note divides and sqrt flops lock the cpu until the flop is +;; finished. fmpy and xmpyu (fmpyi) lock the cpu for one cycle. +;; There's no way to avoid the penalty. +;; Timings: +;; Instruction Time Unit Minimum Distance (unit contention) +;; fcpy 2 ALU 1 +;; fabs 2 ALU 1 +;; fadd 2 ALU 1 +;; fsub 2 ALU 1 +;; fcmp 2 ALU 1 +;; fcnv 2 ALU 1 +;; fmpyadd,sgl 2 ALU,MPY 1 +;; fmpyadd,dbl 3 ALU,MPY 2 +;; fmpysub,sgl 2 ALU,MPY 1 +;; fmpysub,dbl 3 ALU,MPY 2 +;; fmpycfxt,sgl 2 ALU,MPY 1 +;; fmpycfxt,dbl 3 ALU,MPY 2 +;; fmpy,sgl 2 MPY 1 +;; fmpy,dbl 3 MPY 2 +;; fmpyi 3 MPY 2 +;; fdiv,sgl 8 DIV 8 +;; fdiv,dbl 15 DIV 15 +;; fsqrt,sgl 8 DIV 8 +;; fsqrt,dbl 15 DIV 15 + +(define_function_unit "pa7100LCfp_alu" 1 0 + (and (eq_attr "type" "fpcc,fpalu") + (eq_attr "cpu" "7100LC,7200")) 2 1) +(define_function_unit "pa7100LCfp_mpy" 1 0 + (and (eq_attr "type" "fpmulsgl") + (eq_attr "cpu" "7100LC,7200")) 2 1) +(define_function_unit "pa7100LCfp_mpy" 1 0 + (and (eq_attr "type" "fpmuldbl") + (eq_attr "cpu" "7100LC,7200")) 3 2) +(define_function_unit "pa7100LCfp_div" 1 0 + (and (eq_attr "type" "fpdivsgl,fpsqrtsgl") + (eq_attr "cpu" "7100LC,7200")) 8 8) +(define_function_unit "pa7100LCfp_div" 1 0 + (and (eq_attr "type" "fpdivdbl,fpsqrtdbl") + (eq_attr "cpu" "7100LC,7200")) 15 15) + +;; Define the various functional units for dual-issue. + +;; There's only one floating point unit. +(define_function_unit "pa7100LCflop" 1 1 + (and + (eq_attr "type" "fpcc,fpalu,fpmulsgl,fpmuldbl,fpdivsgl,fpsqrtsgl,fpdivdbl,fpsqrtdbl") + (eq_attr "cpu" "7100LC,7200")) 1 1) + +;; Shifts and memory ops actually execute in one of the integer +;; ALUs, but we can't really model that. +(define_function_unit "pa7100LCshiftmem" 1 1 + (and + (eq_attr "type" "shift,nullshift,load,fpload,store,fpstore") + (eq_attr "cpu" "7100LC,7200")) 1 1) + +;; We have two basic ALUs. +(define_function_unit "pa7100LCalu" 2 2 + (and + (eq_attr "type" "!fpcc,fpalu,fpmulsgl,fpmuldbl,fpdivsgl,fpsqrtsgl,fpdivdbl,fpsqrtdbl,load,fpload,store,fpstore,shift,nullshift") + (eq_attr "cpu" "7100LC,7200")) 1 1) + +;; I don't have complete information on the PA7200; however, most of +;; what I've heard makes it look like a 7100LC without the store-store +;; penalty. So that's how we'll model it. + +;; Memory. Disregarding Cache misses, memory loads and stores take +;; two cycles. Any special cases are handled in pa_adjust_cost. +(define_function_unit "pa7200memory" 1 0 + (and (eq_attr "type" "load,fpload,store,fpstore") + (eq_attr "cpu" "7200")) 2 0) + +;; I don't have detailed information on the PA7200 FP pipeline, so I +;; treat it just like the 7100LC pipeline. +;; Similarly for the multi-issue fake units. + +;; CYGNUS LOCAL PA8000/law +;; +;; Scheduling for the PA8000 is somewhat different than scheduling for a +;; traditional architecture. +;; +;; The PA8000 has a large (56) entry reorder buffer that is split between +;; memory and non-memory operations. +;; +;; The PA800 can issue two memory and two non-memory operations per cycle to +;; the function units. Similarly, the PA8000 can retire two memory and two +;; non-memory operations per cycle. +;; +;; Given the large reorder buffer, the processor can hide most latencies. +;; According to HP, they've got the best results by scheduling for retirement +;; bandwidth with limited latency scheduling for floating point operations. +;; Latency for integer operations and memory references is ignored. +;; +;; We claim floating point operations have a 2 cycle latency and are +;; fully pipelined, except for div and sqrt which are not pipelined. +;; +;; It is not necessary to define the shifter and integer alu units. +;; +;; These first two define_unit_unit descriptions model retirement from +;; the reorder buffer. +(define_function_unit "pa8000lsu" 2 2 + (and + (eq_attr "type" "load,fpload,store,fpstore") + (eq_attr "cpu" "8000")) 1 0) + +(define_function_unit "pa8000alu" 2 2 + (and + (eq_attr "type" "!load,fpload,store,fpstore") + (eq_attr "cpu" "8000")) 1 0) + +;; Claim floating point ops have a 2 cycle latency, including div and +;; sqrt, but div and sqrt are not pipelined and issue to different units. +(define_function_unit "pa8000fmac" 2 0 + (and + (eq_attr "type" "fpcc,fpalu,fpmulsgl,fpmuldbl") + (eq_attr "cpu" "8000")) 2 0) + +(define_function_unit "pa8000fdiv" 2 2 + (and + (eq_attr "type" "fpdivsgl,fpsqrtsgl") + (eq_attr "cpu" "8000")) 17 17) + +(define_function_unit "pa8000fdiv" 2 2 + (and + (eq_attr "type" "fpdivdbl,fpsqrtdbl") + (eq_attr "cpu" "8000")) 31 31) +;; END CYGNUS LOCAL + +;; Compare instructions. +;; This controls RTL generation and register allocation. + +;; We generate RTL for comparisons and branches by having the cmpxx +;; patterns store away the operands. Then, the scc and bcc patterns +;; emit RTL for both the compare and the branch. +;; + +(define_expand "cmpsi" + [(set (reg:CC 0) + (compare:CC (match_operand:SI 0 "reg_or_0_operand" "") + (match_operand:SI 1 "arith5_operand" "")))] + "" + " +{ + hppa_compare_op0 = operands[0]; + hppa_compare_op1 = operands[1]; + hppa_branch_type = CMP_SI; + DONE; +}") + +(define_expand "cmpsf" + [(set (reg:CCFP 0) + (compare:CCFP (match_operand:SF 0 "reg_or_0_operand" "") + (match_operand:SF 1 "reg_or_0_operand" "")))] + "! TARGET_SOFT_FLOAT" + " +{ + hppa_compare_op0 = operands[0]; + hppa_compare_op1 = operands[1]; + hppa_branch_type = CMP_SF; + DONE; +}") + +(define_expand "cmpdf" + [(set (reg:CCFP 0) + (compare:CCFP (match_operand:DF 0 "reg_or_0_operand" "") + (match_operand:DF 1 "reg_or_0_operand" "")))] + "! TARGET_SOFT_FLOAT" + " +{ + hppa_compare_op0 = operands[0]; + hppa_compare_op1 = operands[1]; + hppa_branch_type = CMP_DF; + DONE; +}") + +(define_insn "" + [(set (reg:CCFP 0) + (match_operator:CCFP 2 "comparison_operator" + [(match_operand:SF 0 "reg_or_0_operand" "fG") + (match_operand:SF 1 "reg_or_0_operand" "fG")]))] + "! TARGET_SOFT_FLOAT" + "fcmp,sgl,%Y2 %r0,%r1" + [(set_attr "length" "4") + (set_attr "type" "fpcc")]) + +(define_insn "" + [(set (reg:CCFP 0) + (match_operator:CCFP 2 "comparison_operator" + [(match_operand:DF 0 "reg_or_0_operand" "fG") + (match_operand:DF 1 "reg_or_0_operand" "fG")]))] + "! TARGET_SOFT_FLOAT" + "fcmp,dbl,%Y2 %r0,%r1" + [(set_attr "length" "4") + (set_attr "type" "fpcc")]) + +;; scc insns. + +(define_expand "seq" + [(set (match_operand:SI 0 "register_operand" "") + (eq:SI (match_dup 1) + (match_dup 2)))] + "" + " +{ + /* fp scc patterns rarely match, and are not a win on the PA. */ + if (hppa_branch_type != CMP_SI) + FAIL; + /* set up operands from compare. */ + operands[1] = hppa_compare_op0; + operands[2] = hppa_compare_op1; + /* fall through and generate default code */ +}") + +(define_expand "sne" + [(set (match_operand:SI 0 "register_operand" "") + (ne:SI (match_dup 1) + (match_dup 2)))] + "" + " +{ + /* fp scc patterns rarely match, and are not a win on the PA. */ + if (hppa_branch_type != CMP_SI) + FAIL; + operands[1] = hppa_compare_op0; + operands[2] = hppa_compare_op1; +}") + +(define_expand "slt" + [(set (match_operand:SI 0 "register_operand" "") + (lt:SI (match_dup 1) + (match_dup 2)))] + "" + " +{ + /* fp scc patterns rarely match, and are not a win on the PA. */ + if (hppa_branch_type != CMP_SI) + FAIL; + operands[1] = hppa_compare_op0; + operands[2] = hppa_compare_op1; +}") + +(define_expand "sgt" + [(set (match_operand:SI 0 "register_operand" "") + (gt:SI (match_dup 1) + (match_dup 2)))] + "" + " +{ + /* fp scc patterns rarely match, and are not a win on the PA. */ + if (hppa_branch_type != CMP_SI) + FAIL; + operands[1] = hppa_compare_op0; + operands[2] = hppa_compare_op1; +}") + +(define_expand "sle" + [(set (match_operand:SI 0 "register_operand" "") + (le:SI (match_dup 1) + (match_dup 2)))] + "" + " +{ + /* fp scc patterns rarely match, and are not a win on the PA. */ + if (hppa_branch_type != CMP_SI) + FAIL; + operands[1] = hppa_compare_op0; + operands[2] = hppa_compare_op1; +}") + +(define_expand "sge" + [(set (match_operand:SI 0 "register_operand" "") + (ge:SI (match_dup 1) + (match_dup 2)))] + "" + " +{ + /* fp scc patterns rarely match, and are not a win on the PA. */ + if (hppa_branch_type != CMP_SI) + FAIL; + operands[1] = hppa_compare_op0; + operands[2] = hppa_compare_op1; +}") + +(define_expand "sltu" + [(set (match_operand:SI 0 "register_operand" "") + (ltu:SI (match_dup 1) + (match_dup 2)))] + "" + " +{ + if (hppa_branch_type != CMP_SI) + FAIL; + operands[1] = hppa_compare_op0; + operands[2] = hppa_compare_op1; +}") + +(define_expand "sgtu" + [(set (match_operand:SI 0 "register_operand" "") + (gtu:SI (match_dup 1) + (match_dup 2)))] + "" + " +{ + if (hppa_branch_type != CMP_SI) + FAIL; + operands[1] = hppa_compare_op0; + operands[2] = hppa_compare_op1; +}") + +(define_expand "sleu" + [(set (match_operand:SI 0 "register_operand" "") + (leu:SI (match_dup 1) + (match_dup 2)))] + "" + " +{ + if (hppa_branch_type != CMP_SI) + FAIL; + operands[1] = hppa_compare_op0; + operands[2] = hppa_compare_op1; +}") + +(define_expand "sgeu" + [(set (match_operand:SI 0 "register_operand" "") + (geu:SI (match_dup 1) + (match_dup 2)))] + "" + " +{ + if (hppa_branch_type != CMP_SI) + FAIL; + operands[1] = hppa_compare_op0; + operands[2] = hppa_compare_op1; +}") + +;; Instruction canonicalization puts immediate operands second, which +;; is the reverse of what we want. + +(define_insn "scc" + [(set (match_operand:SI 0 "register_operand" "=r") + (match_operator:SI 3 "comparison_operator" + [(match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "arith11_operand" "rI")]))] + "" + "com%I2clr,%B3 %2,%1,%0\;ldi 1,%0" + [(set_attr "type" "binary") + (set_attr "length" "8")]) + +(define_insn "iorscc" + [(set (match_operand:SI 0 "register_operand" "=r") + (ior:SI (match_operator:SI 3 "comparison_operator" + [(match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "arith11_operand" "rI")]) + (match_operator:SI 6 "comparison_operator" + [(match_operand:SI 4 "register_operand" "r") + (match_operand:SI 5 "arith11_operand" "rI")])))] + "" + "com%I2clr,%S3 %2,%1,0\;com%I5clr,%B6 %5,%4,%0\;ldi 1,%0" + [(set_attr "type" "binary") + (set_attr "length" "12")]) + +;; Combiner patterns for common operations performed with the output +;; from an scc insn (negscc and incscc). +(define_insn "negscc" + [(set (match_operand:SI 0 "register_operand" "=r") + (neg:SI (match_operator:SI 3 "comparison_operator" + [(match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "arith11_operand" "rI")])))] + "" + "com%I2clr,%B3 %2,%1,%0\;ldi -1,%0" + [(set_attr "type" "binary") + (set_attr "length" "8")]) + +;; Patterns for adding/subtracting the result of a boolean expression from +;; a register. First we have special patterns that make use of the carry +;; bit, and output only two instructions. For the cases we can't in +;; general do in two instructions, the incscc pattern at the end outputs +;; two or three instructions. + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (leu:SI (match_operand:SI 2 "register_operand" "r") + (match_operand:SI 3 "arith11_operand" "rI")) + (match_operand:SI 1 "register_operand" "r")))] + "" + "sub%I3 %3,%2,0\;addc 0,%1,%0" + [(set_attr "type" "binary") + (set_attr "length" "8")]) + +; This need only accept registers for op3, since canonicalization +; replaces geu with gtu when op3 is an integer. +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (geu:SI (match_operand:SI 2 "register_operand" "r") + (match_operand:SI 3 "register_operand" "r")) + (match_operand:SI 1 "register_operand" "r")))] + "" + "sub %2,%3,0\;addc 0,%1,%0" + [(set_attr "type" "binary") + (set_attr "length" "8")]) + +; Match only integers for op3 here. This is used as canonical form of the +; geu pattern when op3 is an integer. Don't match registers since we can't +; make better code than the general incscc pattern. +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (gtu:SI (match_operand:SI 2 "register_operand" "r") + (match_operand:SI 3 "int11_operand" "I")) + (match_operand:SI 1 "register_operand" "r")))] + "" + "addi %k3,%2,0\;addc 0,%1,%0" + [(set_attr "type" "binary") + (set_attr "length" "8")]) + +(define_insn "incscc" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (plus:SI (match_operator:SI 4 "comparison_operator" + [(match_operand:SI 2 "register_operand" "r,r") + (match_operand:SI 3 "arith11_operand" "rI,rI")]) + (match_operand:SI 1 "register_operand" "0,?r")))] + "" + "@ + com%I3clr,%B4 %3,%2,0\;addi 1,%0,%0 + com%I3clr,%B4 %3,%2,0\;addi,tr 1,%1,%0\;copy %1,%0" + [(set_attr "type" "binary,binary") + (set_attr "length" "8,12")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (minus:SI (match_operand:SI 1 "register_operand" "r") + (gtu:SI (match_operand:SI 2 "register_operand" "r") + (match_operand:SI 3 "arith11_operand" "rI"))))] + "" + "sub%I3 %3,%2,0\;subb %1,0,%0" + [(set_attr "type" "binary") + (set_attr "length" "8")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (minus:SI (minus:SI (match_operand:SI 1 "register_operand" "r") + (gtu:SI (match_operand:SI 2 "register_operand" "r") + (match_operand:SI 3 "arith11_operand" "rI"))) + (match_operand:SI 4 "register_operand" "r")))] + "" + "sub%I3 %3,%2,0\;subb %1,%4,%0" + [(set_attr "type" "binary") + (set_attr "length" "8")]) + +; This need only accept registers for op3, since canonicalization +; replaces ltu with leu when op3 is an integer. +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (minus:SI (match_operand:SI 1 "register_operand" "r") + (ltu:SI (match_operand:SI 2 "register_operand" "r") + (match_operand:SI 3 "register_operand" "r"))))] + "" + "sub %2,%3,0\;subb %1,0,%0" + [(set_attr "type" "binary") + (set_attr "length" "8")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (minus:SI (minus:SI (match_operand:SI 1 "register_operand" "r") + (ltu:SI (match_operand:SI 2 "register_operand" "r") + (match_operand:SI 3 "register_operand" "r"))) + (match_operand:SI 4 "register_operand" "r")))] + "" + "sub %2,%3,0\;subb %1,%4,%0" + [(set_attr "type" "binary") + (set_attr "length" "8")]) + +; Match only integers for op3 here. This is used as canonical form of the +; ltu pattern when op3 is an integer. Don't match registers since we can't +; make better code than the general incscc pattern. +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (minus:SI (match_operand:SI 1 "register_operand" "r") + (leu:SI (match_operand:SI 2 "register_operand" "r") + (match_operand:SI 3 "int11_operand" "I"))))] + "" + "addi %k3,%2,0\;subb %1,0,%0" + [(set_attr "type" "binary") + (set_attr "length" "8")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (minus:SI (minus:SI (match_operand:SI 1 "register_operand" "r") + (leu:SI (match_operand:SI 2 "register_operand" "r") + (match_operand:SI 3 "int11_operand" "I"))) + (match_operand:SI 4 "register_operand" "r")))] + "" + "addi %k3,%2,0\;subb %1,%4,%0" + [(set_attr "type" "binary") + (set_attr "length" "8")]) + +(define_insn "decscc" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (minus:SI (match_operand:SI 1 "register_operand" "0,?r") + (match_operator:SI 4 "comparison_operator" + [(match_operand:SI 2 "register_operand" "r,r") + (match_operand:SI 3 "arith11_operand" "rI,rI")])))] + "" + "@ + com%I3clr,%B4 %3,%2,0\;addi -1,%0,%0 + com%I3clr,%B4 %3,%2,0\;addi,tr -1,%1,%0\;copy %1,%0" + [(set_attr "type" "binary,binary") + (set_attr "length" "8,12")]) + +; Patterns for max and min. (There is no need for an earlyclobber in the +; last alternative since the middle alternative will match if op0 == op1.) + +(define_insn "sminsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r,r") + (smin:SI (match_operand:SI 1 "register_operand" "%0,0,r") + (match_operand:SI 2 "arith11_operand" "r,I,M")))] + "" + "@ + comclr,> %2,%0,0\;copy %2,%0 + comiclr,> %2,%0,0\;ldi %2,%0 + comclr,> %1,%2,%0\;copy %1,%0" +[(set_attr "type" "multi,multi,multi") + (set_attr "length" "8,8,8")]) + +(define_insn "uminsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (umin:SI (match_operand:SI 1 "register_operand" "%0,0") + (match_operand:SI 2 "arith11_operand" "r,I")))] + "" + "@ + comclr,>> %2,%0,0\;copy %2,%0 + comiclr,>> %2,%0,0\;ldi %2,%0" +[(set_attr "type" "multi,multi") + (set_attr "length" "8,8")]) + +(define_insn "smaxsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r,r") + (smax:SI (match_operand:SI 1 "register_operand" "%0,0,r") + (match_operand:SI 2 "arith11_operand" "r,I,M")))] + "" + "@ + comclr,< %2,%0,0\;copy %2,%0 + comiclr,< %2,%0,0\;ldi %2,%0 + comclr,< %1,%2,%0\;copy %1,%0" +[(set_attr "type" "multi,multi,multi") + (set_attr "length" "8,8,8")]) + +(define_insn "umaxsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (umax:SI (match_operand:SI 1 "register_operand" "%0,0") + (match_operand:SI 2 "arith11_operand" "r,I")))] + "" + "@ + comclr,<< %2,%0,0\;copy %2,%0 + comiclr,<< %2,%0,0\;ldi %2,%0" +[(set_attr "type" "multi,multi") + (set_attr "length" "8,8")]) + +(define_insn "abssi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (abs:SI (match_operand:SI 1 "register_operand" "r")))] + "" + "or,>= %%r0,%1,%0\;subi 0,%0,%0" + [(set_attr "type" "multi") + (set_attr "length" "8")]) + +;;; Experimental conditional move patterns + +(define_expand "movsicc" + [(set (match_operand:SI 0 "register_operand" "") + (if_then_else:SI + (match_operator 1 "comparison_operator" + [(match_dup 4) + (match_dup 5)]) + (match_operand:SI 2 "reg_or_cint_move_operand" "") + (match_operand:SI 3 "reg_or_cint_move_operand" "")))] + "" + " +{ + enum rtx_code code = GET_CODE (operands[1]); + + if (hppa_branch_type != CMP_SI) + FAIL; + + /* operands[1] is currently the result of compare_from_rtx. We want to + emit a compare of the original operands. */ + operands[1] = gen_rtx_fmt_ee (code, SImode, hppa_compare_op0, hppa_compare_op1); + operands[4] = hppa_compare_op0; + operands[5] = hppa_compare_op1; +}") + +; We need the first constraint alternative in order to avoid +; earlyclobbers on all other alternatives. +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r") + (if_then_else:SI + (match_operator 5 "comparison_operator" + [(match_operand:SI 3 "register_operand" "r,r,r,r,r") + (match_operand:SI 4 "arith11_operand" "rI,rI,rI,rI,rI")]) + (match_operand:SI 1 "reg_or_cint_move_operand" "0,r,J,N,K") + (const_int 0)))] + "" + "@ + com%I4clr,%S5 %4,%3,0\;ldi 0,%0 + com%I4clr,%B5 %4,%3,%0\;copy %1,%0 + com%I4clr,%B5 %4,%3,%0\;ldi %1,%0 + com%I4clr,%B5 %4,%3,%0\;ldil L'%1,%0 + com%I4clr,%B5 %4,%3,%0\;zdepi %Z1,%0" + [(set_attr "type" "multi,multi,multi,multi,nullshift") + (set_attr "length" "8,8,8,8,8")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r,r,r,r") + (if_then_else:SI + (match_operator 5 "comparison_operator" + [(match_operand:SI 3 "register_operand" "r,r,r,r,r,r,r,r") + (match_operand:SI 4 "arith11_operand" "rI,rI,rI,rI,rI,rI,rI,rI")]) + (match_operand:SI 1 "reg_or_cint_move_operand" "0,0,0,0,r,J,N,K") + (match_operand:SI 2 "reg_or_cint_move_operand" "r,J,N,K,0,0,0,0")))] + "" + "@ + com%I4clr,%S5 %4,%3,0\;copy %2,%0 + com%I4clr,%S5 %4,%3,0\;ldi %2,%0 + com%I4clr,%S5 %4,%3,0\;ldil L'%2,%0 + com%I4clr,%S5 %4,%3,0\;zdepi %Z2,%0 + com%I4clr,%B5 %4,%3,0\;copy %1,%0 + com%I4clr,%B5 %4,%3,0\;ldi %1,%0 + com%I4clr,%B5 %4,%3,0\;ldil L'%1,%0 + com%I4clr,%B5 %4,%3,0\;zdepi %Z1,%0" + [(set_attr "type" "multi,multi,multi,nullshift,multi,multi,multi,nullshift") + (set_attr "length" "8,8,8,8,8,8,8,8")]) + +;; Conditional Branches + +(define_expand "beq" + [(set (pc) + (if_then_else (eq (match_dup 1) (match_dup 2)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (hppa_branch_type != CMP_SI) + { + emit_insn (gen_cmp_fp (EQ, hppa_compare_op0, hppa_compare_op1)); + emit_bcond_fp (NE, operands[0]); + DONE; + } + /* set up operands from compare. */ + operands[1] = hppa_compare_op0; + operands[2] = hppa_compare_op1; + /* fall through and generate default code */ +}") + +(define_expand "bne" + [(set (pc) + (if_then_else (ne (match_dup 1) (match_dup 2)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (hppa_branch_type != CMP_SI) + { + emit_insn (gen_cmp_fp (NE, hppa_compare_op0, hppa_compare_op1)); + emit_bcond_fp (NE, operands[0]); + DONE; + } + operands[1] = hppa_compare_op0; + operands[2] = hppa_compare_op1; +}") + +(define_expand "bgt" + [(set (pc) + (if_then_else (gt (match_dup 1) (match_dup 2)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (hppa_branch_type != CMP_SI) + { + emit_insn (gen_cmp_fp (GT, hppa_compare_op0, hppa_compare_op1)); + emit_bcond_fp (NE, operands[0]); + DONE; + } + operands[1] = hppa_compare_op0; + operands[2] = hppa_compare_op1; +}") + +(define_expand "blt" + [(set (pc) + (if_then_else (lt (match_dup 1) (match_dup 2)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (hppa_branch_type != CMP_SI) + { + emit_insn (gen_cmp_fp (LT, hppa_compare_op0, hppa_compare_op1)); + emit_bcond_fp (NE, operands[0]); + DONE; + } + operands[1] = hppa_compare_op0; + operands[2] = hppa_compare_op1; +}") + +(define_expand "bge" + [(set (pc) + (if_then_else (ge (match_dup 1) (match_dup 2)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (hppa_branch_type != CMP_SI) + { + emit_insn (gen_cmp_fp (GE, hppa_compare_op0, hppa_compare_op1)); + emit_bcond_fp (NE, operands[0]); + DONE; + } + operands[1] = hppa_compare_op0; + operands[2] = hppa_compare_op1; +}") + +(define_expand "ble" + [(set (pc) + (if_then_else (le (match_dup 1) (match_dup 2)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (hppa_branch_type != CMP_SI) + { + emit_insn (gen_cmp_fp (LE, hppa_compare_op0, hppa_compare_op1)); + emit_bcond_fp (NE, operands[0]); + DONE; + } + operands[1] = hppa_compare_op0; + operands[2] = hppa_compare_op1; +}") + +(define_expand "bgtu" + [(set (pc) + (if_then_else (gtu (match_dup 1) (match_dup 2)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (hppa_branch_type != CMP_SI) + FAIL; + operands[1] = hppa_compare_op0; + operands[2] = hppa_compare_op1; +}") + +(define_expand "bltu" + [(set (pc) + (if_then_else (ltu (match_dup 1) (match_dup 2)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (hppa_branch_type != CMP_SI) + FAIL; + operands[1] = hppa_compare_op0; + operands[2] = hppa_compare_op1; +}") + +(define_expand "bgeu" + [(set (pc) + (if_then_else (geu (match_dup 1) (match_dup 2)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (hppa_branch_type != CMP_SI) + FAIL; + operands[1] = hppa_compare_op0; + operands[2] = hppa_compare_op1; +}") + +(define_expand "bleu" + [(set (pc) + (if_then_else (leu (match_dup 1) (match_dup 2)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (hppa_branch_type != CMP_SI) + FAIL; + operands[1] = hppa_compare_op0; + operands[2] = hppa_compare_op1; +}") + +;; Match the branch patterns. + + +;; Note a long backward conditional branch with an annulled delay slot +;; has a length of 12. +(define_insn "" + [(set (pc) + (if_then_else + (match_operator 3 "comparison_operator" + [(match_operand:SI 1 "reg_or_0_operand" "rM") + (match_operand:SI 2 "arith5_operand" "rL")]) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "* +{ + return output_cbranch (operands, INSN_ANNULLED_BRANCH_P (insn), + get_attr_length (insn), 0, insn); +}" +[(set_attr "type" "cbranch") + (set (attr "length") + (cond [(lt (abs (minus (match_dup 0) (plus (pc) (const_int 8)))) + (const_int 8184)) + (const_int 4) + (lt (abs (minus (match_dup 0) (plus (pc) (const_int 8)))) + (const_int 262100)) + (const_int 8) + (eq (symbol_ref "flag_pic") (const_int 0)) + (const_int 20)] + (const_int 28)))]) + +;; Match the negated branch. + +(define_insn "" + [(set (pc) + (if_then_else + (match_operator 3 "comparison_operator" + [(match_operand:SI 1 "reg_or_0_operand" "rM") + (match_operand:SI 2 "arith5_operand" "rL")]) + (pc) + (label_ref (match_operand 0 "" ""))))] + "" + "* +{ + return output_cbranch (operands, INSN_ANNULLED_BRANCH_P (insn), + get_attr_length (insn), 1, insn); +}" +[(set_attr "type" "cbranch") + (set (attr "length") + (cond [(lt (abs (minus (match_dup 0) (plus (pc) (const_int 8)))) + (const_int 8184)) + (const_int 4) + (lt (abs (minus (match_dup 0) (plus (pc) (const_int 8)))) + (const_int 262100)) + (const_int 8) + (eq (symbol_ref "flag_pic") (const_int 0)) + (const_int 20)] + (const_int 28)))]) + +;; Branch on Bit patterns. +(define_insn "" + [(set (pc) + (if_then_else + (ne (zero_extract:SI (match_operand:SI 0 "register_operand" "r") + (const_int 1) + (match_operand:SI 1 "uint5_operand" "")) + (const_int 0)) + (label_ref (match_operand 2 "" "")) + (pc)))] + "" + "* +{ + return output_bb (operands, INSN_ANNULLED_BRANCH_P (insn), + get_attr_length (insn), 0, insn, 0); +}" +[(set_attr "type" "cbranch") + (set (attr "length") + (if_then_else (lt (abs (minus (match_dup 2) (plus (pc) (const_int 8)))) + (const_int 8184)) + (const_int 4) + (const_int 8)))]) + +(define_insn "" + [(set (pc) + (if_then_else + (ne (zero_extract:SI (match_operand:SI 0 "register_operand" "r") + (const_int 1) + (match_operand:SI 1 "uint5_operand" "")) + (const_int 0)) + (pc) + (label_ref (match_operand 2 "" ""))))] + "" + "* +{ + return output_bb (operands, INSN_ANNULLED_BRANCH_P (insn), + get_attr_length (insn), 1, insn, 0); +}" +[(set_attr "type" "cbranch") + (set (attr "length") + (if_then_else (lt (abs (minus (match_dup 2) (plus (pc) (const_int 8)))) + (const_int 8184)) + (const_int 4) + (const_int 8)))]) + +(define_insn "" + [(set (pc) + (if_then_else + (eq (zero_extract:SI (match_operand:SI 0 "register_operand" "r") + (const_int 1) + (match_operand:SI 1 "uint5_operand" "")) + (const_int 0)) + (label_ref (match_operand 2 "" "")) + (pc)))] + "" + "* +{ + return output_bb (operands, INSN_ANNULLED_BRANCH_P (insn), + get_attr_length (insn), 0, insn, 1); +}" +[(set_attr "type" "cbranch") + (set (attr "length") + (if_then_else (lt (abs (minus (match_dup 2) (plus (pc) (const_int 8)))) + (const_int 8184)) + (const_int 4) + (const_int 8)))]) + +(define_insn "" + [(set (pc) + (if_then_else + (eq (zero_extract:SI (match_operand:SI 0 "register_operand" "r") + (const_int 1) + (match_operand:SI 1 "uint5_operand" "")) + (const_int 0)) + (pc) + (label_ref (match_operand 2 "" ""))))] + "" + "* +{ + return output_bb (operands, INSN_ANNULLED_BRANCH_P (insn), + get_attr_length (insn), 1, insn, 1); +}" +[(set_attr "type" "cbranch") + (set (attr "length") + (if_then_else (lt (abs (minus (match_dup 2) (plus (pc) (const_int 8)))) + (const_int 8184)) + (const_int 4) + (const_int 8)))]) + +;; Branch on Variable Bit patterns. +(define_insn "" + [(set (pc) + (if_then_else + (ne (zero_extract:SI (match_operand:SI 0 "register_operand" "r") + (const_int 1) + (match_operand:SI 1 "register_operand" "q")) + (const_int 0)) + (label_ref (match_operand 2 "" "")) + (pc)))] + "" + "* +{ + return output_bvb (operands, INSN_ANNULLED_BRANCH_P (insn), + get_attr_length (insn), 0, insn, 0); +}" +[(set_attr "type" "cbranch") + (set (attr "length") + (if_then_else (lt (abs (minus (match_dup 2) (plus (pc) (const_int 8)))) + (const_int 8184)) + (const_int 4) + (const_int 8)))]) + +(define_insn "" + [(set (pc) + (if_then_else + (ne (zero_extract:SI (match_operand:SI 0 "register_operand" "r") + (const_int 1) + (match_operand:SI 1 "register_operand" "q")) + (const_int 0)) + (pc) + (label_ref (match_operand 2 "" ""))))] + "" + "* +{ + return output_bvb (operands, INSN_ANNULLED_BRANCH_P (insn), + get_attr_length (insn), 1, insn, 0); +}" +[(set_attr "type" "cbranch") + (set (attr "length") + (if_then_else (lt (abs (minus (match_dup 2) (plus (pc) (const_int 8)))) + (const_int 8184)) + (const_int 4) + (const_int 8)))]) + +(define_insn "" + [(set (pc) + (if_then_else + (eq (zero_extract:SI (match_operand:SI 0 "register_operand" "r") + (const_int 1) + (match_operand:SI 1 "register_operand" "q")) + (const_int 0)) + (label_ref (match_operand 2 "" "")) + (pc)))] + "" + "* +{ + return output_bvb (operands, INSN_ANNULLED_BRANCH_P (insn), + get_attr_length (insn), 0, insn, 1); +}" +[(set_attr "type" "cbranch") + (set (attr "length") + (if_then_else (lt (abs (minus (match_dup 2) (plus (pc) (const_int 8)))) + (const_int 8184)) + (const_int 4) + (const_int 8)))]) + +(define_insn "" + [(set (pc) + (if_then_else + (eq (zero_extract:SI (match_operand:SI 0 "register_operand" "r") + (const_int 1) + (match_operand:SI 1 "register_operand" "q")) + (const_int 0)) + (pc) + (label_ref (match_operand 2 "" ""))))] + "" + "* +{ + return output_bvb (operands, INSN_ANNULLED_BRANCH_P (insn), + get_attr_length (insn), 1, insn, 1); +}" +[(set_attr "type" "cbranch") + (set (attr "length") + (if_then_else (lt (abs (minus (match_dup 2) (plus (pc) (const_int 8)))) + (const_int 8184)) + (const_int 4) + (const_int 8)))]) + +;; Floating point branches +(define_insn "" + [(set (pc) (if_then_else (ne (reg:CCFP 0) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "! TARGET_SOFT_FLOAT" + "* +{ + if (INSN_ANNULLED_BRANCH_P (insn)) + return \"ftest\;bl,n %0,0\"; + else + return \"ftest\;bl%* %0,0\"; +}" + [(set_attr "type" "fbranch") + (set_attr "length" "8")]) + +(define_insn "" + [(set (pc) (if_then_else (ne (reg:CCFP 0) (const_int 0)) + (pc) + (label_ref (match_operand 0 "" ""))))] + "! TARGET_SOFT_FLOAT" + "* +{ + if (INSN_ANNULLED_BRANCH_P (insn)) + return \"ftest\;add,tr 0,0,0\;bl,n %0,0\"; + else + return \"ftest\;add,tr 0,0,0\;bl%* %0,0\"; +}" + [(set_attr "type" "fbranch") + (set_attr "length" "12")]) + +;; Move instructions + +(define_expand "movsi" + [(set (match_operand:SI 0 "general_operand" "") + (match_operand:SI 1 "general_operand" ""))] + "" + " +{ + if (emit_move_sequence (operands, SImode, 0)) + DONE; +}") + +;; Reloading an SImode or DImode value requires a scratch register if +;; going in to or out of float point registers. + +(define_expand "reload_insi" + [(set (match_operand:SI 0 "register_operand" "=Z") + (match_operand:SI 1 "non_hard_reg_operand" "")) + (clobber (match_operand:SI 2 "register_operand" "=&r"))] + "" + " +{ + if (emit_move_sequence (operands, SImode, operands[2])) + DONE; + + /* We don't want the clobber emitted, so handle this ourselves. */ + emit_insn (gen_rtx_SET (VOIDmode, operands[0], operands[1])); + DONE; +}") + +(define_expand "reload_outsi" + [(set (match_operand:SI 0 "non_hard_reg_operand" "") + (match_operand:SI 1 "register_operand" "Z")) + (clobber (match_operand:SI 2 "register_operand" "=&r"))] + "" + " +{ + if (emit_move_sequence (operands, SImode, operands[2])) + DONE; + + /* We don't want the clobber emitted, so handle this ourselves. */ + emit_insn (gen_rtx_SET (VOIDmode, operands[0], operands[1])); + DONE; +}") + +;;; pic symbol references + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (mem:SI (plus:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "symbolic_operand" ""))))] + "flag_pic && operands[1] == pic_offset_table_rtx" + "ldw T'%2(%1),%0" + [(set_attr "type" "load") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:SI 0 "reg_or_nonsymb_mem_operand" + "=r,r,r,r,r,Q,*q,!f,f,*TR") + (match_operand:SI 1 "move_operand" + "r,J,N,K,RQ,rM,rM,!fM,*RT,f"))] + "(register_operand (operands[0], SImode) + || reg_or_0_operand (operands[1], SImode)) + && ! TARGET_SOFT_FLOAT" + "@ + copy %1,%0 + ldi %1,%0 + ldil L'%1,%0 + zdepi %Z1,%0 + ldw%M1 %1,%0 + stw%M0 %r1,%0 + mtsar %r1 + fcpy,sgl %r1,%0 + fldw%F1 %1,%0 + fstw%F0 %1,%0" + [(set_attr "type" "move,move,move,shift,load,store,move,fpalu,fpload,fpstore") + (set_attr "pa_combine_type" "addmove") + (set_attr "length" "4,4,4,4,4,4,4,4,4,4")]) + +(define_insn "" + [(set (match_operand:SI 0 "reg_or_nonsymb_mem_operand" + "=r,r,r,r,r,Q,*q") + (match_operand:SI 1 "move_operand" + "r,J,N,K,RQ,rM,rM"))] + "(register_operand (operands[0], SImode) + || reg_or_0_operand (operands[1], SImode)) + && TARGET_SOFT_FLOAT" + "@ + copy %1,%0 + ldi %1,%0 + ldil L'%1,%0 + zdepi %Z1,%0 + ldw%M1 %1,%0 + stw%M0 %r1,%0 + mtsar %r1" + [(set_attr "type" "move,move,move,move,load,store,move") + (set_attr "pa_combine_type" "addmove") + (set_attr "length" "4,4,4,4,4,4,4")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (mem:SI (plus:SI (match_operand:SI 1 "basereg_operand" "r") + (match_operand:SI 2 "register_operand" "r"))))] + "! TARGET_DISABLE_INDEXING" + "* +{ + /* Reload can create backwards (relative to cse) unscaled index + address modes when eliminating registers and possibly for + pseudos that don't get hard registers. Deal with it. */ + if (operands[2] == hard_frame_pointer_rtx + || operands[2] == stack_pointer_rtx) + return \"ldwx %1(0,%2),%0\"; + else + return \"ldwx %2(0,%1),%0\"; +}" + [(set_attr "type" "load") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (mem:SI (plus:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "basereg_operand" "r"))))] + "! TARGET_DISABLE_INDEXING" + "* +{ + /* Reload can create backwards (relative to cse) unscaled index + address modes when eliminating registers and possibly for + pseudos that don't get hard registers. Deal with it. */ + if (operands[1] == hard_frame_pointer_rtx + || operands[1] == stack_pointer_rtx) + return \"ldwx %2(0,%1),%0\"; + else + return \"ldwx %1(0,%2),%0\"; +}" + [(set_attr "type" "load") + (set_attr "length" "4")]) + +;; Load or store with base-register modification. + +(define_insn "pre_ldwm" + [(set (match_operand:SI 0 "register_operand" "=r") + (mem:SI (plus:SI (match_operand:SI 1 "register_operand" "+r") + (match_operand:SI 2 "pre_cint_operand" "")))) + (set (match_dup 1) + (plus:SI (match_dup 1) (match_dup 2)))] + "" + "* +{ + if (INTVAL (operands[2]) < 0) + return \"ldwm %2(0,%1),%0\"; + return \"ldws,mb %2(0,%1),%0\"; +}" + [(set_attr "type" "load") + (set_attr "length" "4")]) + +(define_insn "pre_stwm" + [(set (mem:SI (plus:SI (match_operand:SI 0 "register_operand" "+r") + (match_operand:SI 1 "pre_cint_operand" ""))) + (match_operand:SI 2 "reg_or_0_operand" "rM")) + (set (match_dup 0) + (plus:SI (match_dup 0) (match_dup 1)))] + "" + "* +{ + if (INTVAL (operands[1]) < 0) + return \"stwm %r2,%1(0,%0)\"; + return \"stws,mb %r2,%1(0,%0)\"; +}" + [(set_attr "type" "store") + (set_attr "length" "4")]) + +(define_insn "post_ldwm" + [(set (match_operand:SI 0 "register_operand" "=r") + (mem:SI (match_operand:SI 1 "register_operand" "+r"))) + (set (match_dup 1) + (plus:SI (match_dup 1) + (match_operand:SI 2 "post_cint_operand" "")))] + "" + "* +{ + if (INTVAL (operands[2]) > 0) + return \"ldwm %2(0,%1),%0\"; + return \"ldws,ma %2(0,%1),%0\"; +}" + [(set_attr "type" "load") + (set_attr "length" "4")]) + +(define_insn "post_stwm" + [(set (mem:SI (match_operand:SI 0 "register_operand" "+r")) + (match_operand:SI 1 "reg_or_0_operand" "rM")) + (set (match_dup 0) + (plus:SI (match_dup 0) + (match_operand:SI 2 "post_cint_operand" "")))] + "" + "* +{ + if (INTVAL (operands[2]) > 0) + return \"stwm %r1,%2(0,%0)\"; + return \"stws,ma %r1,%2(0,%0)\"; +}" + [(set_attr "type" "store") + (set_attr "length" "4")]) + +;; For pic +;; Note since this pattern can be created at reload time (via movsi), all +;; the same rules for movsi apply here. (no new pseudos, no temporaries). +(define_insn "pic_load_label" + [(set (match_operand:SI 0 "register_operand" "=a") + (match_operand:SI 1 "pic_label_operand" ""))] + "" + "* +{ + rtx label_rtx = gen_label_rtx (); + rtx xoperands[3]; + extern FILE *asm_out_file; + + xoperands[0] = operands[0]; + xoperands[1] = operands[1]; + xoperands[2] = label_rtx; + output_asm_insn (\"bl .+8,%0\", xoperands); + output_asm_insn (\"depi 0,31,2,%0\", xoperands); + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\", + CODE_LABEL_NUMBER (label_rtx)); + + /* If we're trying to load the address of a label that happens to be + close, then we can use a shorter sequence. */ + if (GET_CODE (operands[1]) == LABEL_REF + && insn_addresses + && abs (insn_addresses[INSN_UID (XEXP (operands[1], 0))] + - insn_addresses[INSN_UID (insn)]) < 8100) + { + /* Prefixing with R% here is wrong, it extracts just 11 bits and is + always non-negative. */ + output_asm_insn (\"ldo %1-%2(%0),%0\", xoperands); + } + else + { + output_asm_insn (\"addil L%%%1-%2,%0\", xoperands); + output_asm_insn (\"ldo R%%%1-%2(%0),%0\", xoperands); + } + return \"\"; +}" + [(set_attr "type" "multi") + (set_attr "length" "16")]) ; 12 or 16 + +(define_insn "pic2_highpart" + [(set (match_operand:SI 0 "register_operand" "=a") + (plus:SI (match_operand:SI 1 "register_operand" "r") + (high:SI (match_operand 2 "" ""))))] + "symbolic_operand (operands[2], Pmode) + && ! function_label_operand (operands[2]) + && flag_pic == 2" + "addil LT'%G2,%1" + [(set_attr "type" "binary") + (set_attr "length" "4")]) + +; We need this to make sure CSE doesn't simplify a memory load with a +; symbolic address, whose content it think it knows. For PIC, what CSE +; think is the real value will be the address of that value. +(define_insn "pic2_lo_sum" + [(set (match_operand:SI 0 "register_operand" "=r") + (mem:SI (lo_sum:SI (match_operand:SI 1 "register_operand" "r") + (unspec:SI [(match_operand:SI 2 "symbolic_operand" "")] 0))))] + "" + "* +{ + if (flag_pic != 2) + abort (); + return \"ldw RT'%G2(%1),%0\"; +}" + [(set_attr "type" "load") + (set_attr "length" "4")]) + + +;; Always use addil rather than ldil;add sequences. This allows the +;; HP linker to eliminate the dp relocation if the symbolic operand +;; lives in the TEXT space. +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=a") + (high:SI (match_operand 1 "" "")))] + "symbolic_operand (operands[1], Pmode) + && ! function_label_operand (operands[1]) + && ! read_only_operand (operands[1]) + && ! flag_pic" + "* +{ + if (TARGET_LONG_LOAD_STORE) + return \"addil NLR'%H1,%%r27\;ldo N'%H1(%%r1),%%r1\"; + else + return \"addil LR'%H1,%%r27\"; +}" + [(set_attr "type" "binary") + (set (attr "length") + (if_then_else (eq (symbol_ref "TARGET_LONG_LOAD_STORE") (const_int 0)) + (const_int 4) + (const_int 8)))]) + + +;; This is for use in the prologue/epilogue code. We need it +;; to add large constants to a stack pointer or frame pointer. +;; Because of the additional %r1 pressure, we probably do not +;; want to use this in general code, so make it available +;; only after reload. +(define_insn "add_high_const" + [(set (match_operand:SI 0 "register_operand" "=!a,*r") + (plus:SI (match_operand:SI 1 "register_operand" "r,r") + (high:SI (match_operand 2 "const_int_operand" ""))))] + "reload_completed" + "@ + addil L'%G2,%1 + ldil L'%G2,%0\;addl %0,%1,%0" + [(set_attr "type" "binary,binary") + (set_attr "length" "4,8")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (high:SI (match_operand 1 "" "")))] + "(!flag_pic || !symbolic_operand (operands[1]), Pmode) + && !is_function_label_plus_const (operands[1])" + "* +{ + if (symbolic_operand (operands[1], Pmode)) + return \"ldil LR'%H1,%0\"; + else + return \"ldil L'%G1,%0\"; +}" + [(set_attr "type" "move") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (lo_sum:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "immediate_operand" "i")))] + "!is_function_label_plus_const (operands[2])" + "* +{ + if (flag_pic && symbolic_operand (operands[2], Pmode)) + abort (); + else if (symbolic_operand (operands[2], Pmode)) + return \"ldo RR'%G2(%1),%0\"; + else + return \"ldo R'%G2(%1),%0\"; +}" + [(set_attr "type" "move") + (set_attr "length" "4")]) + +;; Now that a symbolic_address plus a constant is broken up early +;; in the compilation phase (for better CSE) we need a special +;; combiner pattern to load the symbolic address plus the constant +;; in only 2 instructions. (For cases where the symbolic address +;; was not a common subexpression.) +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "symbolic_operand" "")) + (clobber (match_operand:SI 2 "register_operand" ""))] + "! (flag_pic && pic_label_operand (operands[1], SImode))" + [(set (match_dup 2) (high:SI (match_dup 1))) + (set (match_dup 0) (lo_sum:SI (match_dup 2) (match_dup 1)))] + "") + +;; hppa_legitimize_address goes to a great deal of trouble to +;; create addresses which use indexing. In some cases, this +;; is a lose because there isn't any store instructions which +;; allow indexed addresses (with integer register source). +;; +;; These define_splits try to turn a 3 insn store into +;; a 2 insn store with some creative RTL rewriting. +(define_split + [(set (mem:SI (plus:SI (mult:SI (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "shadd_operand" "")) + (plus:SI (match_operand:SI 2 "register_operand" "") + (match_operand:SI 3 "const_int_operand" "")))) + (match_operand:SI 4 "register_operand" "")) + (clobber (match_operand:SI 5 "register_operand" ""))] + "" + [(set (match_dup 5) (plus:SI (mult:SI (match_dup 0) (match_dup 1)) + (match_dup 2))) + (set (mem:SI (plus:SI (match_dup 5) (match_dup 3))) (match_dup 4))] + "") + +(define_split + [(set (mem:HI (plus:SI (mult:SI (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "shadd_operand" "")) + (plus:SI (match_operand:SI 2 "register_operand" "") + (match_operand:SI 3 "const_int_operand" "")))) + (match_operand:HI 4 "register_operand" "")) + (clobber (match_operand:SI 5 "register_operand" ""))] + "" + [(set (match_dup 5) (plus:SI (mult:SI (match_dup 0) (match_dup 1)) + (match_dup 2))) + (set (mem:HI (plus:SI (match_dup 5) (match_dup 3))) (match_dup 4))] + "") + +(define_split + [(set (mem:QI (plus:SI (mult:SI (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "shadd_operand" "")) + (plus:SI (match_operand:SI 2 "register_operand" "") + (match_operand:SI 3 "const_int_operand" "")))) + (match_operand:QI 4 "register_operand" "")) + (clobber (match_operand:SI 5 "register_operand" ""))] + "" + [(set (match_dup 5) (plus:SI (mult:SI (match_dup 0) (match_dup 1)) + (match_dup 2))) + (set (mem:QI (plus:SI (match_dup 5) (match_dup 3))) (match_dup 4))] + "") + +(define_expand "movhi" + [(set (match_operand:HI 0 "general_operand" "") + (match_operand:HI 1 "general_operand" ""))] + "" + " +{ + if (emit_move_sequence (operands, HImode, 0)) + DONE; +}") + +(define_insn "" + [(set (match_operand:HI 0 "reg_or_nonsymb_mem_operand" "=r,r,r,r,r,Q,*q,!f") + (match_operand:HI 1 "move_operand" "r,J,N,K,RQ,rM,rM,!fM"))] + "register_operand (operands[0], HImode) + || reg_or_0_operand (operands[1], HImode)" + "@ + copy %1,%0 + ldi %1,%0 + ldil L'%1,%0 + zdepi %Z1,%0 + ldh%M1 %1,%0 + sth%M0 %r1,%0 + mtsar %r1 + fcpy,sgl %r1,%0" + [(set_attr "type" "move,move,move,shift,load,store,move,fpalu") + (set_attr "pa_combine_type" "addmove") + (set_attr "length" "4,4,4,4,4,4,4,4")]) + +(define_insn "" + [(set (match_operand:HI 0 "register_operand" "=r") + (mem:HI (plus:SI (match_operand:SI 1 "basereg_operand" "r") + (match_operand:SI 2 "register_operand" "r"))))] + "! TARGET_DISABLE_INDEXING" + "* +{ + /* Reload can create backwards (relative to cse) unscaled index + address modes when eliminating registers and possibly for + pseudos that don't get hard registers. Deal with it. */ + if (operands[2] == hard_frame_pointer_rtx + || operands[2] == stack_pointer_rtx) + return \"ldhx %1(0,%2),%0\"; + else + return \"ldhx %2(0,%1),%0\"; +}" + [(set_attr "type" "load") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:HI 0 "register_operand" "=r") + (mem:HI (plus:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "basereg_operand" "r"))))] + "! TARGET_DISABLE_INDEXING" + "* +{ + /* Reload can create backwards (relative to cse) unscaled index + address modes when eliminating registers and possibly for + pseudos that don't get hard registers. Deal with it. */ + if (operands[1] == hard_frame_pointer_rtx + || operands[1] == stack_pointer_rtx) + return \"ldhx %2(0,%1),%0\"; + else + return \"ldhx %1(0,%2),%0\"; +}" + [(set_attr "type" "load") + (set_attr "length" "4")]) + +; Now zero extended variants. +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (zero_extend:SI (mem:HI + (plus:SI + (match_operand:SI 1 "basereg_operand" "r") + (match_operand:SI 2 "register_operand" "r")))))] + "! TARGET_DISABLE_INDEXING" + "* +{ + /* Reload can create backwards (relative to cse) unscaled index + address modes when eliminating registers and possibly for + pseudos that don't get hard registers. Deal with it. */ + if (operands[2] == hard_frame_pointer_rtx + || operands[2] == stack_pointer_rtx) + return \"ldhx %1(0,%2),%0\"; + else + return \"ldhx %2(0,%1),%0\"; +}" + [(set_attr "type" "load") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (zero_extend:SI (mem:HI + (plus:SI + (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "basereg_operand" "r")))))] + "! TARGET_DISABLE_INDEXING" + "* +{ + /* Reload can create backwards (relative to cse) unscaled index + address modes when eliminating registers and possibly for + pseudos that don't get hard registers. Deal with it. */ + if (operands[1] == hard_frame_pointer_rtx + || operands[1] == stack_pointer_rtx) + return \"ldhx %2(0,%1),%0\"; + else + return \"ldhx %1(0,%2),%0\"; +}" + [(set_attr "type" "load") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:HI 0 "register_operand" "=r") + (mem:HI (plus:SI (match_operand:SI 1 "register_operand" "+r") + (match_operand:SI 2 "int5_operand" "L")))) + (set (match_dup 1) + (plus:SI (match_dup 1) (match_dup 2)))] + "" + "ldhs,mb %2(0,%1),%0" + [(set_attr "type" "load") + (set_attr "length" "4")]) + +; And a zero extended variant. +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (zero_extend:SI (mem:HI + (plus:SI + (match_operand:SI 1 "register_operand" "+r") + (match_operand:SI 2 "int5_operand" "L"))))) + (set (match_dup 1) + (plus:SI (match_dup 1) (match_dup 2)))] + "" + "ldhs,mb %2(0,%1),%0" + [(set_attr "type" "load") + (set_attr "length" "4")]) + +(define_insn "" + [(set (mem:HI (plus:SI (match_operand:SI 0 "register_operand" "+r") + (match_operand:SI 1 "int5_operand" "L"))) + (match_operand:HI 2 "reg_or_0_operand" "rM")) + (set (match_dup 0) + (plus:SI (match_dup 0) (match_dup 1)))] + "" + "sths,mb %r2,%1(0,%0)" + [(set_attr "type" "store") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:HI 0 "register_operand" "=r") + (high:HI (match_operand 1 "const_int_operand" "")))] + "" + "ldil L'%G1,%0" + [(set_attr "type" "move") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:HI 0 "register_operand" "=r") + (lo_sum:HI (match_operand:HI 1 "register_operand" "r") + (match_operand 2 "const_int_operand" "")))] + "" + "ldo R'%G2(%1),%0" + [(set_attr "type" "move") + (set_attr "length" "4")]) + +(define_expand "movqi" + [(set (match_operand:QI 0 "general_operand" "") + (match_operand:QI 1 "general_operand" ""))] + "" + " +{ + if (emit_move_sequence (operands, QImode, 0)) + DONE; +}") + +(define_insn "" + [(set (match_operand:QI 0 "reg_or_nonsymb_mem_operand" "=r,r,r,r,r,Q,*q,!f") + (match_operand:QI 1 "move_operand" "r,J,N,K,RQ,rM,rM,!fM"))] + "register_operand (operands[0], QImode) + || reg_or_0_operand (operands[1], QImode)" + "@ + copy %1,%0 + ldi %1,%0 + ldil L'%1,%0 + zdepi %Z1,%0 + ldb%M1 %1,%0 + stb%M0 %r1,%0 + mtsar %r1 + fcpy,sgl %r1,%0" + [(set_attr "type" "move,move,move,shift,load,store,move,fpalu") + (set_attr "pa_combine_type" "addmove") + (set_attr "length" "4,4,4,4,4,4,4,4")]) + +(define_insn "" + [(set (match_operand:QI 0 "register_operand" "=r") + (mem:QI (plus:SI (match_operand:SI 1 "basereg_operand" "r") + (match_operand:SI 2 "register_operand" "r"))))] + "! TARGET_DISABLE_INDEXING" + "* +{ + /* Reload can create backwards (relative to cse) unscaled index + address modes when eliminating registers and possibly for + pseudos that don't get hard registers. Deal with it. */ + if (operands[2] == hard_frame_pointer_rtx + || operands[2] == stack_pointer_rtx) + return \"ldbx %1(0,%2),%0\"; + else + return \"ldbx %2(0,%1),%0\"; +}" + [(set_attr "type" "load") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:QI 0 "register_operand" "=r") + (mem:QI (plus:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "basereg_operand" "r"))))] + "! TARGET_DISABLE_INDEXING" + "* +{ + /* Reload can create backwards (relative to cse) unscaled index + address modes when eliminating registers and possibly for + pseudos that don't get hard registers. Deal with it. */ + if (operands[1] == hard_frame_pointer_rtx + || operands[1] == stack_pointer_rtx) + return \"ldbx %2(0,%1),%0\"; + else + return \"ldbx %1(0,%2),%0\"; +}" + [(set_attr "type" "load") + (set_attr "length" "4")]) + +; Indexed byte load with zero extension to SImode or HImode. +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (zero_extend:SI (mem:QI + (plus:SI + (match_operand:SI 1 "basereg_operand" "r") + (match_operand:SI 2 "register_operand" "r")))))] + "! TARGET_DISABLE_INDEXING" + "* +{ + /* Reload can create backwards (relative to cse) unscaled index + address modes when eliminating registers and possibly for + pseudos that don't get hard registers. Deal with it. */ + if (operands[2] == hard_frame_pointer_rtx + || operands[2] == stack_pointer_rtx) + return \"ldbx %1(0,%2),%0\"; + else + return \"ldbx %2(0,%1),%0\"; +}" + [(set_attr "type" "load") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (zero_extend:SI (mem:QI + (plus:SI + (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "basereg_operand" "r")))))] + "! TARGET_DISABLE_INDEXING" + "* +{ + /* Reload can create backwards (relative to cse) unscaled index + address modes when eliminating registers and possibly for + pseudos that don't get hard registers. Deal with it. */ + if (operands[1] == hard_frame_pointer_rtx + || operands[1] == stack_pointer_rtx) + return \"ldbx %2(0,%1),%0\"; + else + return \"ldbx %1(0,%2),%0\"; +}" + [(set_attr "type" "load") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:HI 0 "register_operand" "=r") + (zero_extend:HI (mem:QI + (plus:SI + (match_operand:SI 1 "basereg_operand" "r") + (match_operand:SI 2 "register_operand" "r")))))] + "! TARGET_DISABLE_INDEXING" + "* +{ + /* Reload can create backwards (relative to cse) unscaled index + address modes when eliminating registers and possibly for + pseudos that don't get hard registers. Deal with it. */ + if (operands[2] == hard_frame_pointer_rtx + || operands[2] == stack_pointer_rtx) + return \"ldbx %1(0,%2),%0\"; + else + return \"ldbx %2(0,%1),%0\"; +}" + [(set_attr "type" "load") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:HI 0 "register_operand" "=r") + (zero_extend:HI (mem:QI + (plus:SI + (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "basereg_operand" "r")))))] + "! TARGET_DISABLE_INDEXING" + "* +{ + /* Reload can create backwards (relative to cse) unscaled index + address modes when eliminating registers and possibly for + pseudos that don't get hard registers. Deal with it. */ + if (operands[1] == hard_frame_pointer_rtx + || operands[1] == stack_pointer_rtx) + return \"ldbx %2(0,%1),%0\"; + else + return \"ldbx %1(0,%2),%0\"; +}" + [(set_attr "type" "load") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:QI 0 "register_operand" "=r") + (mem:QI (plus:SI (match_operand:SI 1 "register_operand" "+r") + (match_operand:SI 2 "int5_operand" "L")))) + (set (match_dup 1) (plus:SI (match_dup 1) (match_dup 2)))] + "" + "ldbs,mb %2(0,%1),%0" + [(set_attr "type" "load") + (set_attr "length" "4")]) + +; Now the same thing with zero extensions. +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (zero_extend:SI (mem:QI (plus:SI + (match_operand:SI 1 "register_operand" "+r") + (match_operand:SI 2 "int5_operand" "L"))))) + (set (match_dup 1) (plus:SI (match_dup 1) (match_dup 2)))] + "" + "ldbs,mb %2(0,%1),%0" + [(set_attr "type" "load") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:HI 0 "register_operand" "=r") + (zero_extend:HI (mem:QI (plus:SI + (match_operand:SI 1 "register_operand" "+r") + (match_operand:SI 2 "int5_operand" "L"))))) + (set (match_dup 1) (plus:SI (match_dup 1) (match_dup 2)))] + "" + "ldbs,mb %2(0,%1),%0" + [(set_attr "type" "load") + (set_attr "length" "4")]) + +(define_insn "" + [(set (mem:QI (plus:SI (match_operand:SI 0 "register_operand" "+r") + (match_operand:SI 1 "int5_operand" "L"))) + (match_operand:QI 2 "reg_or_0_operand" "rM")) + (set (match_dup 0) + (plus:SI (match_dup 0) (match_dup 1)))] + "" + "stbs,mb %r2,%1(0,%0)" + [(set_attr "type" "store") + (set_attr "length" "4")]) + +;; The definition of this insn does not really explain what it does, +;; but it should suffice +;; that anything generated as this insn will be recognized as one +;; and that it will not successfully combine with anything. +(define_expand "movstrsi" + [(parallel [(set (match_operand:BLK 0 "" "") + (match_operand:BLK 1 "" "")) + (clobber (match_dup 7)) + (clobber (match_dup 8)) + (clobber (match_dup 4)) + (clobber (match_dup 5)) + (clobber (match_dup 6)) + (use (match_operand:SI 2 "arith_operand" "")) + (use (match_operand:SI 3 "const_int_operand" ""))])] + "" + " +{ + int size, align; + + /* HP provides very fast block move library routine for the PA; + this routine includes: + + 4x4 byte at a time block moves, + 1x4 byte at a time with alignment checked at runtime with + attempts to align the source and destination as needed + 1x1 byte loop + + With that in mind, here's the heuristics to try and guess when + the inlined block move will be better than the library block + move: + + If the size isn't constant, then always use the library routines. + + If the size is large in respect to the known alignment, then use + the library routines. + + If the size is small in repsect to the known alignment, then open + code the copy (since that will lead to better scheduling). + + Else use the block move pattern. */ + + /* Undetermined size, use the library routine. */ + if (GET_CODE (operands[2]) != CONST_INT) + FAIL; + + size = INTVAL (operands[2]); + align = INTVAL (operands[3]); + align = align > 4 ? 4 : align; + + /* If size/alignment > 8 (eg size is large in respect to alignment), + then use the library routines. */ + if (size / align > 16) + FAIL; + + /* This does happen, but not often enough to worry much about. */ + if (size / align < MOVE_RATIO) + FAIL; + + /* Fall through means we're going to use our block move pattern. */ + operands[0] + = change_address (operands[0], VOIDmode, + copy_to_mode_reg (SImode, XEXP (operands[0], 0))); + operands[1] + = change_address (operands[1], VOIDmode, + copy_to_mode_reg (SImode, XEXP (operands[1], 0))); + operands[4] = gen_reg_rtx (SImode); + operands[5] = gen_reg_rtx (SImode); + operands[6] = gen_reg_rtx (SImode); + operands[7] = XEXP (operands[0], 0); + operands[8] = XEXP (operands[1], 0); +}") + +;; The operand constraints are written like this to support both compile-time +;; and run-time determined byte count. If the count is run-time determined, +;; the register with the byte count is clobbered by the copying code, and +;; therefore it is forced to operand 2. If the count is compile-time +;; determined, we need two scratch registers for the unrolled code. +(define_insn "movstrsi_internal" + [(set (mem:BLK (match_operand:SI 0 "register_operand" "+r,r")) + (mem:BLK (match_operand:SI 1 "register_operand" "+r,r"))) + (clobber (match_dup 0)) + (clobber (match_dup 1)) + (clobber (match_operand:SI 2 "register_operand" "=r,r")) ;loop cnt/tmp + (clobber (match_operand:SI 3 "register_operand" "=&r,&r")) ;item tmp + (clobber (match_operand:SI 6 "register_operand" "=&r,&r")) ;item tmp2 + (use (match_operand:SI 4 "arith_operand" "J,2")) ;byte count + (use (match_operand:SI 5 "const_int_operand" "n,n"))] ;alignment + "" + "* return output_block_move (operands, !which_alternative);" + [(set_attr "type" "multi,multi")]) + +;; Floating point move insns + +;; This pattern forces (set (reg:DF ...) (const_double ...)) +;; to be reloaded by putting the constant into memory when +;; reg is a floating point register. +;; +;; For integer registers we use ldil;ldo to set the appropriate +;; value. +;; +;; This must come before the movdf pattern, and it must be present +;; to handle obscure reloading cases. +(define_insn "" + [(set (match_operand:DF 0 "register_operand" "=?r,f") + (match_operand:DF 1 "" "?F,m"))] + "GET_CODE (operands[1]) == CONST_DOUBLE + && operands[1] != CONST0_RTX (DFmode) + && ! TARGET_SOFT_FLOAT" + "* return (which_alternative == 0 ? output_move_double (operands) + : \"fldd%F1 %1,%0\");" + [(set_attr "type" "move,fpload") + (set_attr "length" "16,4")]) + +(define_expand "movdf" + [(set (match_operand:DF 0 "general_operand" "") + (match_operand:DF 1 "general_operand" ""))] + "" + " +{ + if (emit_move_sequence (operands, DFmode, 0)) + DONE; +}") + +;; Reloading an SImode or DImode value requires a scratch register if +;; going in to or out of float point registers. + +(define_expand "reload_indf" + [(set (match_operand:DF 0 "register_operand" "=Z") + (match_operand:DF 1 "non_hard_reg_operand" "")) + (clobber (match_operand:DF 2 "register_operand" "=&r"))] + "" + " +{ + if (emit_move_sequence (operands, DFmode, operands[2])) + DONE; + + /* We don't want the clobber emitted, so handle this ourselves. */ + emit_insn (gen_rtx_SET (VOIDmode, operands[0], operands[1])); + DONE; +}") + +(define_expand "reload_outdf" + [(set (match_operand:DF 0 "non_hard_reg_operand" "") + (match_operand:DF 1 "register_operand" "Z")) + (clobber (match_operand:DF 2 "register_operand" "=&r"))] + "" + " +{ + if (emit_move_sequence (operands, DFmode, operands[2])) + DONE; + + /* We don't want the clobber emitted, so handle this ourselves. */ + emit_insn (gen_rtx_SET (VOIDmode, operands[0], operands[1])); + DONE; +}") + +(define_insn "" + [(set (match_operand:DF 0 "reg_or_nonsymb_mem_operand" + "=f,*r,RQ,?o,?Q,f,*r,*r") + (match_operand:DF 1 "reg_or_0_or_nonsymb_mem_operand" + "fG,*rG,f,*r,*r,RQ,o,RQ"))] + "(register_operand (operands[0], DFmode) + || reg_or_0_operand (operands[1], DFmode)) + && ! (GET_CODE (operands[1]) == CONST_DOUBLE + && GET_CODE (operands[0]) == MEM) + && ! TARGET_SOFT_FLOAT" + "* +{ + if (FP_REG_P (operands[0]) || FP_REG_P (operands[1]) + || operands[1] == CONST0_RTX (DFmode)) + return output_fp_move_double (operands); + return output_move_double (operands); +}" + [(set_attr "type" "fpalu,move,fpstore,store,store,fpload,load,load") + (set_attr "length" "4,8,4,8,16,4,8,16")]) + +(define_insn "" + [(set (match_operand:DF 0 "reg_or_nonsymb_mem_operand" + "=r,?o,?Q,r,r") + (match_operand:DF 1 "reg_or_0_or_nonsymb_mem_operand" + "rG,r,r,o,Q"))] + "(register_operand (operands[0], DFmode) + || reg_or_0_operand (operands[1], DFmode)) + && TARGET_SOFT_FLOAT" + "* +{ + return output_move_double (operands); +}" + [(set_attr "type" "move,store,store,load,load") + (set_attr "length" "8,8,16,8,16")]) + +(define_insn "" + [(set (match_operand:DF 0 "register_operand" "=fx") + (mem:DF (plus:SI (match_operand:SI 1 "basereg_operand" "r") + (match_operand:SI 2 "register_operand" "r"))))] + "! TARGET_DISABLE_INDEXING && ! TARGET_SOFT_FLOAT" + "* +{ + /* Reload can create backwards (relative to cse) unscaled index + address modes when eliminating registers and possibly for + pseudos that don't get hard registers. Deal with it. */ + if (operands[2] == hard_frame_pointer_rtx + || operands[2] == stack_pointer_rtx) + return \"flddx %1(0,%2),%0\"; + else + return \"flddx %2(0,%1),%0\"; +}" + [(set_attr "type" "fpload") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:DF 0 "register_operand" "=fx") + (mem:DF (plus:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "basereg_operand" "r"))))] + "! TARGET_DISABLE_INDEXING && ! TARGET_SOFT_FLOAT" + "* +{ + /* Reload can create backwards (relative to cse) unscaled index + address modes when eliminating registers and possibly for + pseudos that don't get hard registers. Deal with it. */ + if (operands[1] == hard_frame_pointer_rtx + || operands[1] == stack_pointer_rtx) + return \"flddx %2(0,%1),%0\"; + else + return \"flddx %1(0,%2),%0\"; +}" + [(set_attr "type" "fpload") + (set_attr "length" "4")]) + +(define_insn "" + [(set (mem:DF (plus:SI (match_operand:SI 1 "basereg_operand" "r") + (match_operand:SI 2 "register_operand" "r"))) + (match_operand:DF 0 "register_operand" "fx"))] + "! TARGET_DISABLE_INDEXING && ! TARGET_SOFT_FLOAT" + "* +{ + /* Reload can create backwards (relative to cse) unscaled index + address modes when eliminating registers and possibly for + pseudos that don't get hard registers. Deal with it. */ + if (operands[2] == hard_frame_pointer_rtx + || operands[2] == stack_pointer_rtx) + return \"fstdx %0,%1(0,%2)\"; + else + return \"fstdx %0,%2(0,%1)\"; +}" + [(set_attr "type" "fpstore") + (set_attr "length" "4")]) + +(define_insn "" + [(set (mem:DF (plus:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "basereg_operand" "r"))) + (match_operand:DF 0 "register_operand" "fx"))] + "! TARGET_DISABLE_INDEXING && ! TARGET_SOFT_FLOAT" + "* +{ + /* Reload can create backwards (relative to cse) unscaled index + address modes when eliminating registers and possibly for + pseudos that don't get hard registers. Deal with it. */ + if (operands[1] == hard_frame_pointer_rtx + || operands[1] == stack_pointer_rtx) + return \"fstdx %0,%2(0,%1)\"; + else + return \"fstdx %0,%1(0,%2)\"; +}" + [(set_attr "type" "fpstore") + (set_attr "length" "4")]) + +(define_expand "movdi" + [(set (match_operand:DI 0 "reg_or_nonsymb_mem_operand" "") + (match_operand:DI 1 "general_operand" ""))] + "" + " +{ + if (emit_move_sequence (operands, DImode, 0)) + DONE; +}") + +(define_expand "reload_indi" + [(set (match_operand:DI 0 "register_operand" "=f") + (match_operand:DI 1 "non_hard_reg_operand" "")) + (clobber (match_operand:SI 2 "register_operand" "=&r"))] + "" + " +{ + if (emit_move_sequence (operands, DImode, operands[2])) + DONE; + + /* We don't want the clobber emitted, so handle this ourselves. */ + emit_insn (gen_rtx_SET (VOIDmode, operands[0], operands[1])); + DONE; +}") + +(define_expand "reload_outdi" + [(set (match_operand:DI 0 "general_operand" "") + (match_operand:DI 1 "register_operand" "f")) + (clobber (match_operand:SI 2 "register_operand" "=&r"))] + "" + " +{ + if (emit_move_sequence (operands, DImode, operands[2])) + DONE; + + /* We don't want the clobber emitted, so handle this ourselves. */ + emit_insn (gen_rtx_SET (VOIDmode, operands[0], operands[1])); + DONE; +}") + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=r") + (high:DI (match_operand 1 "" "")))] + "" + "* +{ + rtx op0 = operands[0]; + rtx op1 = operands[1]; + + if (GET_CODE (op1) == CONST_INT) + { + operands[0] = operand_subword (op0, 1, 0, DImode); + output_asm_insn (\"ldil L'%1,%0\", operands); + + operands[0] = operand_subword (op0, 0, 0, DImode); + if (INTVAL (op1) < 0) + output_asm_insn (\"ldi -1,%0\", operands); + else + output_asm_insn (\"ldi 0,%0\", operands); + return \"\"; + } + else if (GET_CODE (op1) == CONST_DOUBLE) + { + operands[0] = operand_subword (op0, 1, 0, DImode); + operands[1] = GEN_INT (CONST_DOUBLE_LOW (op1)); + output_asm_insn (\"ldil L'%1,%0\", operands); + + operands[0] = operand_subword (op0, 0, 0, DImode); + operands[1] = GEN_INT (CONST_DOUBLE_HIGH (op1)); + output_asm_insn (singlemove_string (operands), operands); + return \"\"; + } + else + abort (); +}" + [(set_attr "type" "move") + (set_attr "length" "8")]) + +;;; Experimental + +(define_insn "" + [(set (match_operand:DI 0 "reg_or_nonsymb_mem_operand" + "=r,o,Q,r,r,r,f,f,*TR") + (match_operand:DI 1 "general_operand" + "rM,r,r,o*R,Q,i,fM,*TR,f"))] + "(register_operand (operands[0], DImode) + || reg_or_0_operand (operands[1], DImode)) + && ! TARGET_SOFT_FLOAT" + "* +{ + if (FP_REG_P (operands[0]) || FP_REG_P (operands[1]) + || (operands[1] == CONST0_RTX (DImode))) + return output_fp_move_double (operands); + return output_move_double (operands); +}" + [(set_attr "type" "move,store,store,load,load,multi,fpalu,fpload,fpstore") + (set_attr "length" "8,8,16,8,16,16,4,4,4")]) + +(define_insn "" + [(set (match_operand:DI 0 "reg_or_nonsymb_mem_operand" + "=r,o,Q,r,r,r") + (match_operand:DI 1 "general_operand" + "rM,r,r,o,Q,i"))] + "(register_operand (operands[0], DImode) + || reg_or_0_operand (operands[1], DImode)) + && TARGET_SOFT_FLOAT" + "* +{ + return output_move_double (operands); +}" + [(set_attr "type" "move,store,store,load,load,multi") + (set_attr "length" "8,8,16,8,16,16")]) + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=r,&r") + (lo_sum:DI (match_operand:DI 1 "register_operand" "0,r") + (match_operand:DI 2 "immediate_operand" "i,i")))] + "" + "* +{ + /* Don't output a 64 bit constant, since we can't trust the assembler to + handle it correctly. */ + if (GET_CODE (operands[2]) == CONST_DOUBLE) + operands[2] = GEN_INT (CONST_DOUBLE_LOW (operands[2])); + if (which_alternative == 1) + output_asm_insn (\"copy %1,%0\", operands); + return \"ldo R'%G2(%R1),%R0\"; +}" + [(set_attr "type" "move,move") + (set_attr "length" "4,8")]) + +;; This pattern forces (set (reg:SF ...) (const_double ...)) +;; to be reloaded by putting the constant into memory when +;; reg is a floating point register. +;; +;; For integer registers we use ldil;ldo to set the appropriate +;; value. +;; +;; This must come before the movsf pattern, and it must be present +;; to handle obscure reloading cases. +(define_insn "" + [(set (match_operand:SF 0 "register_operand" "=?r,f") + (match_operand:SF 1 "" "?F,m"))] + "GET_CODE (operands[1]) == CONST_DOUBLE + && operands[1] != CONST0_RTX (SFmode) + && ! TARGET_SOFT_FLOAT" + "* return (which_alternative == 0 ? singlemove_string (operands) + : \" fldw%F1 %1,%0\");" + [(set_attr "type" "move,fpload") + (set_attr "length" "8,4")]) + +(define_expand "movsf" + [(set (match_operand:SF 0 "general_operand" "") + (match_operand:SF 1 "general_operand" ""))] + "" + " +{ + if (emit_move_sequence (operands, SFmode, 0)) + DONE; +}") + +;; Reloading an SImode or DImode value requires a scratch register if +;; going in to or out of float point registers. + +(define_expand "reload_insf" + [(set (match_operand:SF 0 "register_operand" "=Z") + (match_operand:SF 1 "non_hard_reg_operand" "")) + (clobber (match_operand:SF 2 "register_operand" "=&r"))] + "" + " +{ + if (emit_move_sequence (operands, SFmode, operands[2])) + DONE; + + /* We don't want the clobber emitted, so handle this ourselves. */ + emit_insn (gen_rtx_SET (VOIDmode, operands[0], operands[1])); + DONE; +}") + +(define_expand "reload_outsf" + [(set (match_operand:SF 0 "non_hard_reg_operand" "") + (match_operand:SF 1 "register_operand" "Z")) + (clobber (match_operand:SF 2 "register_operand" "=&r"))] + "" + " +{ + if (emit_move_sequence (operands, SFmode, operands[2])) + DONE; + + /* We don't want the clobber emitted, so handle this ourselves. */ + emit_insn (gen_rtx_SET (VOIDmode, operands[0], operands[1])); + DONE; +}") + +(define_insn "" + [(set (match_operand:SF 0 "reg_or_nonsymb_mem_operand" + "=f,r,f,r,RQ,Q") + (match_operand:SF 1 "reg_or_0_or_nonsymb_mem_operand" + "fG,rG,RQ,RQ,f,rG"))] + "(register_operand (operands[0], SFmode) + || reg_or_0_operand (operands[1], SFmode)) + && ! TARGET_SOFT_FLOAT" + "@ + fcpy,sgl %r1,%0 + copy %r1,%0 + fldw%F1 %1,%0 + ldw%M1 %1,%0 + fstw%F0 %r1,%0 + stw%M0 %r1,%0" + [(set_attr "type" "fpalu,move,fpload,load,fpstore,store") + (set_attr "pa_combine_type" "addmove") + (set_attr "length" "4,4,4,4,4,4")]) + +(define_insn "" + [(set (match_operand:SF 0 "reg_or_nonsymb_mem_operand" + "=r,r,Q") + (match_operand:SF 1 "reg_or_0_or_nonsymb_mem_operand" + "rG,RQ,rG"))] + "(register_operand (operands[0], SFmode) + || reg_or_0_operand (operands[1], SFmode)) + && TARGET_SOFT_FLOAT" + "@ + copy %r1,%0 + ldw%M1 %1,%0 + stw%M0 %r1,%0" + [(set_attr "type" "move,load,store") + (set_attr "pa_combine_type" "addmove") + (set_attr "length" "4,4,4")]) + +(define_insn "" + [(set (match_operand:SF 0 "register_operand" "=fx") + (mem:SF (plus:SI (match_operand:SI 1 "basereg_operand" "r") + (match_operand:SI 2 "register_operand" "r"))))] + "! TARGET_DISABLE_INDEXING && ! TARGET_SOFT_FLOAT" + "* +{ + /* Reload can create backwards (relative to cse) unscaled index + address modes when eliminating registers and possibly for + pseudos that don't get hard registers. Deal with it. */ + if (operands[2] == hard_frame_pointer_rtx + || operands[2] == stack_pointer_rtx) + return \"fldwx %1(0,%2),%0\"; + else + return \"fldwx %2(0,%1),%0\"; +}" + [(set_attr "type" "fpload") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:SF 0 "register_operand" "=fx") + (mem:SF (plus:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "basereg_operand" "r"))))] + "! TARGET_DISABLE_INDEXING && ! TARGET_SOFT_FLOAT" + "* +{ + /* Reload can create backwards (relative to cse) unscaled index + address modes when eliminating registers and possibly for + pseudos that don't get hard registers. Deal with it. */ + if (operands[1] == hard_frame_pointer_rtx + || operands[1] == stack_pointer_rtx) + return \"fldwx %2(0,%1),%0\"; + else + return \"fldwx %1(0,%2),%0\"; +}" + [(set_attr "type" "fpload") + (set_attr "length" "4")]) + +(define_insn "" + [(set (mem:SF (plus:SI (match_operand:SI 1 "basereg_operand" "r") + (match_operand:SI 2 "register_operand" "r"))) + (match_operand:SF 0 "register_operand" "fx"))] + "! TARGET_DISABLE_INDEXING && ! TARGET_SOFT_FLOAT" + "* +{ + /* Reload can create backwards (relative to cse) unscaled index + address modes when eliminating registers and possibly for + pseudos that don't get hard registers. Deal with it. */ + if (operands[2] == hard_frame_pointer_rtx + || operands[2] == stack_pointer_rtx) + return \"fstwx %0,%1(0,%2)\"; + else + return \"fstwx %0,%2(0,%1)\"; +}" + [(set_attr "type" "fpstore") + (set_attr "length" "4")]) + +(define_insn "" + [(set (mem:SF (plus:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "basereg_operand" "r"))) + (match_operand:SF 0 "register_operand" "fx"))] + "! TARGET_DISABLE_INDEXING && ! TARGET_SOFT_FLOAT" + "* +{ + /* Reload can create backwards (relative to cse) unscaled index + address modes when eliminating registers and possibly for + pseudos that don't get hard registers. Deal with it. */ + if (operands[1] == hard_frame_pointer_rtx + || operands[1] == stack_pointer_rtx) + return \"fstwx %0,%2(0,%1)\"; + else + return \"fstwx %0,%1(0,%2)\"; +}" + [(set_attr "type" "fpstore") + (set_attr "length" "4")]) + + +;;- zero extension instructions +;; We have define_expand for zero extension patterns to make sure the +;; operands get loaded into registers. The define_insns accept +;; memory operands. This gives us better overall code than just +;; having a pattern that does or does not accept memory operands. + +(define_expand "zero_extendhisi2" + [(set (match_operand:SI 0 "register_operand" "") + (zero_extend:SI + (match_operand:HI 1 "register_operand" "")))] + "" + "") + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (zero_extend:SI + (match_operand:HI 1 "move_operand" "r,RQ")))] + "GET_CODE (operands[1]) != CONST_INT" + "@ + extru %1,31,16,%0 + ldh%M1 %1,%0" + [(set_attr "type" "shift,load") + (set_attr "length" "4,4")]) + +(define_expand "zero_extendqihi2" + [(set (match_operand:HI 0 "register_operand" "") + (zero_extend:HI + (match_operand:QI 1 "register_operand" "")))] + "" + "") + +(define_insn "" + [(set (match_operand:HI 0 "register_operand" "=r,r") + (zero_extend:HI + (match_operand:QI 1 "move_operand" "r,RQ")))] + "GET_CODE (operands[1]) != CONST_INT" + "@ + extru %1,31,8,%0 + ldb%M1 %1,%0" + [(set_attr "type" "shift,load") + (set_attr "length" "4,4")]) + +(define_expand "zero_extendqisi2" + [(set (match_operand:SI 0 "register_operand" "") + (zero_extend:SI + (match_operand:QI 1 "register_operand" "")))] + "" + "") + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (zero_extend:SI + (match_operand:QI 1 "move_operand" "r,RQ")))] + "GET_CODE (operands[1]) != CONST_INT" + "@ + extru %1,31,8,%0 + ldb%M1 %1,%0" + [(set_attr "type" "shift,load") + (set_attr "length" "4,4")]) + +;;- sign extension instructions + +(define_insn "extendhisi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (sign_extend:SI (match_operand:HI 1 "register_operand" "r")))] + "" + "extrs %1,31,16,%0" + [(set_attr "type" "shift") + (set_attr "length" "4")]) + +(define_insn "extendqihi2" + [(set (match_operand:HI 0 "register_operand" "=r") + (sign_extend:HI (match_operand:QI 1 "register_operand" "r")))] + "" + "extrs %1,31,8,%0" + [(set_attr "type" "shift") + (set_attr "length" "4")]) + +(define_insn "extendqisi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (sign_extend:SI (match_operand:QI 1 "register_operand" "r")))] + "" + "extrs %1,31,8,%0" + [(set_attr "type" "shift") + (set_attr "length" "4")]) + +;; Conversions between float and double. + +(define_insn "extendsfdf2" + [(set (match_operand:DF 0 "register_operand" "=f") + (float_extend:DF + (match_operand:SF 1 "register_operand" "f")))] + "! TARGET_SOFT_FLOAT" + "fcnvff,sgl,dbl %1,%0" + [(set_attr "type" "fpalu") + (set_attr "length" "4")]) + +(define_insn "truncdfsf2" + [(set (match_operand:SF 0 "register_operand" "=f") + (float_truncate:SF + (match_operand:DF 1 "register_operand" "f")))] + "! TARGET_SOFT_FLOAT" + "fcnvff,dbl,sgl %1,%0" + [(set_attr "type" "fpalu") + (set_attr "length" "4")]) + +;; Conversion between fixed point and floating point. +;; Note that among the fix-to-float insns +;; the ones that start with SImode come first. +;; That is so that an operand that is a CONST_INT +;; (and therefore lacks a specific machine mode). +;; will be recognized as SImode (which is always valid) +;; rather than as QImode or HImode. + +;; This pattern forces (set (reg:SF ...) (float:SF (const_int ...))) +;; to be reloaded by putting the constant into memory. +;; It must come before the more general floatsisf2 pattern. +(define_insn "" + [(set (match_operand:SF 0 "register_operand" "=f") + (float:SF (match_operand:SI 1 "const_int_operand" "m")))] + "! TARGET_SOFT_FLOAT" + "fldw%F1 %1,%0\;fcnvxf,sgl,sgl %0,%0" + [(set_attr "type" "fpalu") + (set_attr "length" "8")]) + +(define_insn "floatsisf2" + [(set (match_operand:SF 0 "register_operand" "=f") + (float:SF (match_operand:SI 1 "register_operand" "f")))] + "! TARGET_SOFT_FLOAT" + "fcnvxf,sgl,sgl %1,%0" + [(set_attr "type" "fpalu") + (set_attr "length" "4")]) + + +;; This pattern forces (set (reg:DF ...) (float:DF (const_int ...))) +;; to be reloaded by putting the constant into memory. +;; It must come before the more general floatsidf2 pattern. +(define_insn "" + [(set (match_operand:DF 0 "register_operand" "=f") + (float:DF (match_operand:SI 1 "const_int_operand" "m")))] + "! TARGET_SOFT_FLOAT" + "fldw%F1 %1,%0\;fcnvxf,sgl,dbl %0,%0" + [(set_attr "type" "fpalu") + (set_attr "length" "8")]) + +(define_insn "floatsidf2" + [(set (match_operand:DF 0 "register_operand" "=f") + (float:DF (match_operand:SI 1 "register_operand" "f")))] + "! TARGET_SOFT_FLOAT" + "fcnvxf,sgl,dbl %1,%0" + [(set_attr "type" "fpalu") + (set_attr "length" "4")]) + +;; CYGNUS LOCAL pa8000/law */ +(define_expand "floatunssisf2" + [(set (subreg:SI (match_dup 2) 1) + (match_operand:SI 1 "register_operand" "")) + (set (subreg:SI (match_dup 2) 0) + (const_int 0)) + (set (match_operand:SF 0 "register_operand" "") + (float:SF (match_dup 2)))] + "TARGET_SNAKE && ! TARGET_SOFT_FLOAT" + " +{ + if (TARGET_PARISC_2_0) + { + emit_insn (gen_floatunssisf2_pa20 (operands[0], operands[1])); + DONE; + } + operands[2] = gen_reg_rtx (DImode); +}") + +(define_expand "floatunssidf2" + [(set (subreg:SI (match_dup 2) 1) + (match_operand:SI 1 "register_operand" "")) + (set (subreg:SI (match_dup 2) 0) + (const_int 0)) + (set (match_operand:DF 0 "register_operand" "") + (float:DF (match_dup 2)))] + "TARGET_SNAKE && ! TARGET_SOFT_FLOAT" + " +{ + if (TARGET_PARISC_2_0) + { + emit_insn (gen_floatunssidf2_pa20 (operands[0], operands[1])); + DONE; + } + operands[2] = gen_reg_rtx (DImode); +}") +;; END CYGNUS LOCAL + +(define_insn "floatdisf2" + [(set (match_operand:SF 0 "register_operand" "=f") + (float:SF (match_operand:DI 1 "register_operand" "f")))] + "TARGET_SNAKE && ! TARGET_SOFT_FLOAT" + "fcnvxf,dbl,sgl %1,%0" + [(set_attr "type" "fpalu") + (set_attr "length" "4")]) + +(define_insn "floatdidf2" + [(set (match_operand:DF 0 "register_operand" "=f") + (float:DF (match_operand:DI 1 "register_operand" "f")))] + "TARGET_SNAKE && ! TARGET_SOFT_FLOAT" + "fcnvxf,dbl,dbl %1,%0" + [(set_attr "type" "fpalu") + (set_attr "length" "4")]) + +;; Convert a float to an actual integer. +;; Truncation is performed as part of the conversion. + +(define_insn "fix_truncsfsi2" + [(set (match_operand:SI 0 "register_operand" "=f") + (fix:SI (fix:SF (match_operand:SF 1 "register_operand" "f"))))] + "! TARGET_SOFT_FLOAT" + "fcnvfxt,sgl,sgl %1,%0" + [(set_attr "type" "fpalu") + (set_attr "length" "4")]) + +(define_insn "fix_truncdfsi2" + [(set (match_operand:SI 0 "register_operand" "=f") + (fix:SI (fix:DF (match_operand:DF 1 "register_operand" "f"))))] + "! TARGET_SOFT_FLOAT" + "fcnvfxt,dbl,sgl %1,%0" + [(set_attr "type" "fpalu") + (set_attr "length" "4")]) + +(define_insn "fix_truncsfdi2" + [(set (match_operand:DI 0 "register_operand" "=f") + (fix:DI (fix:SF (match_operand:SF 1 "register_operand" "f"))))] + "TARGET_SNAKE && ! TARGET_SOFT_FLOAT" + "fcnvfxt,sgl,dbl %1,%0" + [(set_attr "type" "fpalu") + (set_attr "length" "4")]) + +(define_insn "fix_truncdfdi2" + [(set (match_operand:DI 0 "register_operand" "=f") + (fix:DI (fix:DF (match_operand:DF 1 "register_operand" "f"))))] + "TARGET_SNAKE && ! TARGET_SOFT_FLOAT" + "fcnvfxt,dbl,dbl %1,%0" + [(set_attr "type" "fpalu") + (set_attr "length" "4")]) + +;; CYGNUS LOCAL pa8000/law +(define_insn "floatunssidf2_pa20" + [(set (match_operand:DF 0 "register_operand" "=f") + (unsigned_float:DF (match_operand:SI 1 "register_operand" "f")))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + "fcnvuf,sgl,dbl %1,%0" + [(set_attr "type" "fpalu") + (set_attr "length" "4")]) + +(define_insn "floatunssisf2_pa20" + [(set (match_operand:SF 0 "register_operand" "=f") + (unsigned_float:SF (match_operand:SI 1 "register_operand" "f")))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + "fcnvuf,sgl,sgl %1,%0" + [(set_attr "type" "fpalu") + (set_attr "length" "4")]) + +(define_insn "floatunsdisf2" + [(set (match_operand:SF 0 "register_operand" "=f") + (unsigned_float:SF (match_operand:DI 1 "register_operand" "f")))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + "fcnvuf,dbl,sgl %1,%0" + [(set_attr "type" "fpalu") + (set_attr "length" "4")]) + +(define_insn "floatunsdidf2" + [(set (match_operand:DF 0 "register_operand" "=f") + (unsigned_float:DF (match_operand:DI 1 "register_operand" "f")))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + "fcnvuf,dbl,dbl %1,%0" + [(set_attr "type" "fpalu") + (set_attr "length" "4")]) + +(define_insn "fixuns_truncsfsi2" + [(set (match_operand:SI 0 "register_operand" "=f") + (unsigned_fix:SI (fix:SF (match_operand:SF 1 "register_operand" "f"))))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + "fcnvfut,sgl,sgl %1,%0" + [(set_attr "type" "fpalu") + (set_attr "length" "4")]) + +(define_insn "fixuns_truncdfsi2" + [(set (match_operand:SI 0 "register_operand" "=f") + (unsigned_fix:SI (fix:DF (match_operand:DF 1 "register_operand" "f"))))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + "fcnvfut,dbl,sgl %1,%0" + [(set_attr "type" "fpalu") + (set_attr "length" "4")]) + +(define_insn "fixuns_truncsfdi2" + [(set (match_operand:DI 0 "register_operand" "=f") + (unsigned_fix:DI (fix:SF (match_operand:SF 1 "register_operand" "f"))))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + "fcnvfut,sgl,dbl %1,%0" + [(set_attr "type" "fpalu") + (set_attr "length" "4")]) + +(define_insn "fixuns_truncdfdi2" + [(set (match_operand:DI 0 "register_operand" "=f") + (unsigned_fix:DI (fix:DF (match_operand:DF 1 "register_operand" "f"))))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + "fcnvfut,dbl,dbl %1,%0" + [(set_attr "type" "fpalu") + (set_attr "length" "4")]) +;; END CYGNUS LOCAL + +;;- arithmetic instructions + +(define_insn "adddi3" + [(set (match_operand:DI 0 "register_operand" "=r") + (plus:DI (match_operand:DI 1 "register_operand" "%r") + (match_operand:DI 2 "arith11_operand" "rI")))] + "" + "* +{ + if (GET_CODE (operands[2]) == CONST_INT) + { + if (INTVAL (operands[2]) >= 0) + return \"addi %2,%R1,%R0\;addc %1,0,%0\"; + else + return \"addi %2,%R1,%R0\;subb %1,0,%0\"; + } + else + return \"add %R2,%R1,%R0\;addc %2,%1,%0\"; +}" + [(set_attr "type" "binary") + (set_attr "length" "8")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (not:SI (match_operand:SI 1 "register_operand" "r")) + (match_operand:SI 2 "register_operand" "r")))] + "" + "uaddcm %2,%1,%0" + [(set_attr "type" "binary") + (set_attr "length" "4")]) + +;; define_splits to optimize cases of adding a constant integer +;; to a register when the constant does not fit in 14 bits. */ +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (plus:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "const_int_operand" ""))) + (clobber (match_operand:SI 4 "register_operand" ""))] + "! cint_ok_for_move (INTVAL (operands[2])) + && VAL_14_BITS_P (INTVAL (operands[2]) >> 1)" + [(set (match_dup 4) (plus:SI (match_dup 1) (match_dup 2))) + (set (match_dup 0) (plus:SI (match_dup 4) (match_dup 3)))] + " +{ + int val = INTVAL (operands[2]); + int low = (val < 0) ? -0x2000 : 0x1fff; + int rest = val - low; + + operands[2] = GEN_INT (rest); + operands[3] = GEN_INT (low); +}") + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (plus:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "const_int_operand" ""))) + (clobber (match_operand:SI 4 "register_operand" ""))] + "! cint_ok_for_move (INTVAL (operands[2]))" + [(set (match_dup 4) (match_dup 2)) + (set (match_dup 0) (plus:SI (mult:SI (match_dup 4) (match_dup 3)) + (match_dup 1)))] + " +{ + HOST_WIDE_INT intval = INTVAL (operands[2]); + + /* Try dividing the constant by 2, then 4, and finally 8 to see + if we can get a constant which can be loaded into a register + in a single instruction (cint_ok_for_move). + + If that fails, try to negate the constant and subtract it + from our input operand. */ + if (intval % 2 == 0 && cint_ok_for_move (intval / 2)) + { + operands[2] = GEN_INT (intval / 2); + operands[3] = GEN_INT (2); + } + else if (intval % 4 == 0 && cint_ok_for_move (intval / 4)) + { + operands[2] = GEN_INT (intval / 4); + operands[3] = GEN_INT (4); + } + else if (intval % 8 == 0 && cint_ok_for_move (intval / 8)) + { + operands[2] = GEN_INT (intval / 8); + operands[3] = GEN_INT (8); + } + else if (cint_ok_for_move (-intval)) + { + emit_insn (gen_rtx_SET (VOIDmode, operands[4], GEN_INT (-intval))); + emit_insn (gen_subsi3 (operands[0], operands[1], operands[4])); + DONE; + } + else + FAIL; +}") + +(define_insn "addsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (plus:SI (match_operand:SI 1 "register_operand" "%r,r") + (match_operand:SI 2 "arith_operand" "r,J")))] + "" + "@ + addl %1,%2,%0 + ldo %2(%1),%0" + [(set_attr "type" "binary,binary") + (set_attr "pa_combine_type" "addmove") + (set_attr "length" "4,4")]) + +;; Disgusting kludge to work around reload bugs with frame pointer +;; elimination. Similar to other magic reload patterns in the +;; indexed memory operations. +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=&r") + (plus:SI (plus:SI (match_operand:SI 1 "register_operand" "%r") + (match_operand:SI 2 "register_operand" "r")) + (match_operand:SI 3 "const_int_operand" "rL")))] + "reload_in_progress" + "* +{ + if (GET_CODE (operands[3]) == CONST_INT) + return \"ldo %3(%2),%0\;addl %1,%0,%0\"; + else + return \"addl %3,%2,%0\;addl %1,%0,%0\"; +}" + [(set_attr "type" "binary") + (set_attr "length" "8")]) + +(define_insn "subdi3" + [(set (match_operand:DI 0 "register_operand" "=r") + (minus:DI (match_operand:DI 1 "register_operand" "r") + (match_operand:DI 2 "register_operand" "r")))] + "" + "sub %R1,%R2,%R0\;subb %1,%2,%0" + [(set_attr "type" "binary") + (set_attr "length" "8")]) + +;; CYGNUS LOCAL PA8000/law +(define_expand "subsi3" + [(set (match_operand:SI 0 "register_operand" "") + (minus:SI (match_operand:SI 1 "arith11_operand" "") + (match_operand:SI 2 "register_operand" "")))] + "" + "") + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (minus:SI (match_operand:SI 1 "arith11_operand" "r,I") + (match_operand:SI 2 "register_operand" "r,r")))] + "!TARGET_PARISC_2_0" + "@ + sub %1,%2,%0 + subi %1,%2,%0" + [(set_attr "type" "binary,binary") + (set_attr "length" "4,4")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r,r,q") + (minus:SI (match_operand:SI 1 "arith11_operand" "r,I,S") + (match_operand:SI 2 "register_operand" "r,r,r")))] + "TARGET_PARISC_2_0" + "@ + sub %1,%2,%0 + subi %1,%2,%0 + mtsarcm %2" + [(set_attr "type" "binary,binary,move") + (set_attr "length" "4,4,4")]) +;; END CYGNUS LOCAL + +;; Clobbering a "register_operand" instead of a match_scratch +;; in operand3 of millicode calls avoids spilling %r1 and +;; produces better code. + +;; The mulsi3 insns set up registers for the millicode call. +(define_expand "mulsi3" + [(set (reg:SI 26) (match_operand:SI 1 "move_operand" "")) + (set (reg:SI 25) (match_operand:SI 2 "move_operand" "")) + (parallel [(set (reg:SI 29) (mult:SI (reg:SI 26) (reg:SI 25))) + (clobber (match_dup 3)) + (clobber (reg:SI 26)) + (clobber (reg:SI 25)) + (clobber (reg:SI 31))]) + (set (match_operand:SI 0 "general_operand" "") (reg:SI 29))] + "" + " +{ + if (TARGET_SNAKE && ! TARGET_DISABLE_FPREGS && ! TARGET_SOFT_FLOAT) + { + rtx scratch = gen_reg_rtx (DImode); + operands[1] = force_reg (SImode, operands[1]); + operands[2] = force_reg (SImode, operands[2]); + emit_insn (gen_umulsidi3 (scratch, operands[1], operands[2])); + emit_insn (gen_rtx_SET (VOIDmode, + operands[0], + gen_rtx_SUBREG (SImode, scratch, 1))); + DONE; + } + operands[3] = gen_reg_rtx (SImode); +}") + +(define_insn "umulsidi3" + [(set (match_operand:DI 0 "nonimmediate_operand" "=f") + (mult:DI (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "f")) + (zero_extend:DI (match_operand:SI 2 "nonimmediate_operand" "f"))))] + "TARGET_SNAKE && ! TARGET_DISABLE_FPREGS && ! TARGET_SOFT_FLOAT" + "xmpyu %1,%2,%0" + [(set_attr "type" "fpmuldbl") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:DI 0 "nonimmediate_operand" "=f") + (mult:DI (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "f")) + (match_operand:DI 2 "uint32_operand" "f")))] + "TARGET_SNAKE && ! TARGET_DISABLE_FPREGS && ! TARGET_SOFT_FLOAT" + "xmpyu %1,%R2,%0" + [(set_attr "type" "fpmuldbl") + (set_attr "length" "4")]) + +(define_insn "" + [(set (reg:SI 29) (mult:SI (reg:SI 26) (reg:SI 25))) + (clobber (match_operand:SI 0 "register_operand" "=a")) + (clobber (reg:SI 26)) + (clobber (reg:SI 25)) + (clobber (reg:SI 31))] + "" + "* return output_mul_insn (0, insn);" + [(set_attr "type" "milli") + (set (attr "length") + (cond [ +;; Target (or stub) within reach + (and (lt (plus (symbol_ref "total_code_bytes") (pc)) + (const_int 240000)) + (eq (symbol_ref "TARGET_PORTABLE_RUNTIME") + (const_int 0))) + (const_int 4) + +;; NO_SPACE_REGS + (ne (symbol_ref "TARGET_NO_SPACE_REGS || TARGET_FAST_INDIRECT_CALLS") + (const_int 0)) + (const_int 8) + +;; Out of reach, but not PIC or PORTABLE_RUNTIME +;; same as NO_SPACE_REGS code + (and (eq (symbol_ref "TARGET_PORTABLE_RUNTIME") + (const_int 0)) + (eq (symbol_ref "flag_pic") + (const_int 0))) + (const_int 8)] + +;; Out of range and either PIC or PORTABLE_RUNTIME + (const_int 24)))]) + +;;; Division and mod. +(define_expand "divsi3" + [(set (reg:SI 26) (match_operand:SI 1 "move_operand" "")) + (set (reg:SI 25) (match_operand:SI 2 "move_operand" "")) + (parallel [(set (reg:SI 29) (div:SI (reg:SI 26) (reg:SI 25))) + (clobber (match_dup 3)) + (clobber (reg:SI 26)) + (clobber (reg:SI 25)) + (clobber (reg:SI 31))]) + (set (match_operand:SI 0 "general_operand" "") (reg:SI 29))] + "" + " +{ + operands[3] = gen_reg_rtx (SImode); + if (GET_CODE (operands[2]) == CONST_INT && emit_hpdiv_const (operands, 0)) + DONE; +}") + +(define_insn "" + [(set (reg:SI 29) + (div:SI (reg:SI 26) (match_operand:SI 0 "div_operand" ""))) + (clobber (match_operand:SI 1 "register_operand" "=a")) + (clobber (reg:SI 26)) + (clobber (reg:SI 25)) + (clobber (reg:SI 31))] + "" + "* + return output_div_insn (operands, 0, insn);" + [(set_attr "type" "milli") + (set (attr "length") + (cond [ +;; Target (or stub) within reach + (and (lt (plus (symbol_ref "total_code_bytes") (pc)) + (const_int 240000)) + (eq (symbol_ref "TARGET_PORTABLE_RUNTIME") + (const_int 0))) + (const_int 4) + +;; NO_SPACE_REGS + (ne (symbol_ref "TARGET_NO_SPACE_REGS || TARGET_FAST_INDIRECT_CALLS") + (const_int 0)) + (const_int 8) + +;; Out of reach, but not PIC or PORTABLE_RUNTIME +;; same as NO_SPACE_REGS code + (and (eq (symbol_ref "TARGET_PORTABLE_RUNTIME") + (const_int 0)) + (eq (symbol_ref "flag_pic") + (const_int 0))) + (const_int 8)] + +;; Out of range and either PIC or PORTABLE_RUNTIME + (const_int 24)))]) + +(define_expand "udivsi3" + [(set (reg:SI 26) (match_operand:SI 1 "move_operand" "")) + (set (reg:SI 25) (match_operand:SI 2 "move_operand" "")) + (parallel [(set (reg:SI 29) (udiv:SI (reg:SI 26) (reg:SI 25))) + (clobber (match_dup 3)) + (clobber (reg:SI 26)) + (clobber (reg:SI 25)) + (clobber (reg:SI 31))]) + (set (match_operand:SI 0 "general_operand" "") (reg:SI 29))] + "" + " +{ + operands[3] = gen_reg_rtx (SImode); + if (GET_CODE (operands[2]) == CONST_INT && emit_hpdiv_const (operands, 1)) + DONE; +}") + +(define_insn "" + [(set (reg:SI 29) + (udiv:SI (reg:SI 26) (match_operand:SI 0 "div_operand" ""))) + (clobber (match_operand:SI 1 "register_operand" "=a")) + (clobber (reg:SI 26)) + (clobber (reg:SI 25)) + (clobber (reg:SI 31))] + "" + "* + return output_div_insn (operands, 1, insn);" + [(set_attr "type" "milli") + (set (attr "length") + (cond [ +;; Target (or stub) within reach + (and (lt (plus (symbol_ref "total_code_bytes") (pc)) + (const_int 240000)) + (eq (symbol_ref "TARGET_PORTABLE_RUNTIME") + (const_int 0))) + (const_int 4) + +;; NO_SPACE_REGS + (ne (symbol_ref "TARGET_NO_SPACE_REGS || TARGET_FAST_INDIRECT_CALLS") + (const_int 0)) + (const_int 8) + +;; Out of reach, but not PIC or PORTABLE_RUNTIME +;; same as NO_SPACE_REGS code + (and (eq (symbol_ref "TARGET_PORTABLE_RUNTIME") + (const_int 0)) + (eq (symbol_ref "flag_pic") + (const_int 0))) + (const_int 8)] + +;; Out of range and either PIC or PORTABLE_RUNTIME + (const_int 24)))]) + +(define_expand "modsi3" + [(set (reg:SI 26) (match_operand:SI 1 "move_operand" "")) + (set (reg:SI 25) (match_operand:SI 2 "move_operand" "")) + (parallel [(set (reg:SI 29) (mod:SI (reg:SI 26) (reg:SI 25))) + (clobber (match_dup 3)) + (clobber (reg:SI 26)) + (clobber (reg:SI 25)) + (clobber (reg:SI 31))]) + (set (match_operand:SI 0 "general_operand" "") (reg:SI 29))] + "" + " +{ + operands[3] = gen_reg_rtx (SImode); +}") + +(define_insn "" + [(set (reg:SI 29) (mod:SI (reg:SI 26) (reg:SI 25))) + (clobber (match_operand:SI 0 "register_operand" "=a")) + (clobber (reg:SI 26)) + (clobber (reg:SI 25)) + (clobber (reg:SI 31))] + "" + "* + return output_mod_insn (0, insn);" + [(set_attr "type" "milli") + (set (attr "length") + (cond [ +;; Target (or stub) within reach + (and (lt (plus (symbol_ref "total_code_bytes") (pc)) + (const_int 240000)) + (eq (symbol_ref "TARGET_PORTABLE_RUNTIME") + (const_int 0))) + (const_int 4) + +;; NO_SPACE_REGS + (ne (symbol_ref "TARGET_NO_SPACE_REGS || TARGET_FAST_INDIRECT_CALLS") + (const_int 0)) + (const_int 8) + +;; Out of reach, but not PIC or PORTABLE_RUNTIME +;; same as NO_SPACE_REGS code + (and (eq (symbol_ref "TARGET_PORTABLE_RUNTIME") + (const_int 0)) + (eq (symbol_ref "flag_pic") + (const_int 0))) + (const_int 8)] + +;; Out of range and either PIC or PORTABLE_RUNTIME + (const_int 24)))]) + +(define_expand "umodsi3" + [(set (reg:SI 26) (match_operand:SI 1 "move_operand" "")) + (set (reg:SI 25) (match_operand:SI 2 "move_operand" "")) + (parallel [(set (reg:SI 29) (umod:SI (reg:SI 26) (reg:SI 25))) + (clobber (match_dup 3)) + (clobber (reg:SI 26)) + (clobber (reg:SI 25)) + (clobber (reg:SI 31))]) + (set (match_operand:SI 0 "general_operand" "") (reg:SI 29))] + "" + " +{ + operands[3] = gen_reg_rtx (SImode); +}") + +(define_insn "" + [(set (reg:SI 29) (umod:SI (reg:SI 26) (reg:SI 25))) + (clobber (match_operand:SI 0 "register_operand" "=a")) + (clobber (reg:SI 26)) + (clobber (reg:SI 25)) + (clobber (reg:SI 31))] + "" + "* + return output_mod_insn (1, insn);" + [(set_attr "type" "milli") + (set (attr "length") + (cond [ +;; Target (or stub) within reach + (and (lt (plus (symbol_ref "total_code_bytes") (pc)) + (const_int 240000)) + (eq (symbol_ref "TARGET_PORTABLE_RUNTIME") + (const_int 0))) + (const_int 4) + +;; NO_SPACE_REGS + (ne (symbol_ref "TARGET_NO_SPACE_REGS || TARGET_FAST_INDIRECT_CALLS") + (const_int 0)) + (const_int 8) + +;; Out of reach, but not PIC or PORTABLE_RUNTIME +;; same as NO_SPACE_REGS code + (and (eq (symbol_ref "TARGET_PORTABLE_RUNTIME") + (const_int 0)) + (eq (symbol_ref "flag_pic") + (const_int 0))) + (const_int 8)] + +;; Out of range and either PIC or PORTABLE_RUNTIME + (const_int 24)))]) + +;;- and instructions +;; We define DImode `and` so with DImode `not` we can get +;; DImode `andn`. Other combinations are possible. + +(define_expand "anddi3" + [(set (match_operand:DI 0 "register_operand" "") + (and:DI (match_operand:DI 1 "arith_double_operand" "") + (match_operand:DI 2 "arith_double_operand" "")))] + "" + " +{ + if (! register_operand (operands[1], DImode) + || ! register_operand (operands[2], DImode)) + /* Let GCC break this into word-at-a-time operations. */ + FAIL; +}") + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=r") + (and:DI (match_operand:DI 1 "register_operand" "%r") + (match_operand:DI 2 "register_operand" "r")))] + "" + "and %1,%2,%0\;and %R1,%R2,%R0" + [(set_attr "type" "binary") + (set_attr "length" "8")]) + +; The ? for op1 makes reload prefer zdepi instead of loading a huge +; constant with ldil;ldo. +(define_insn "andsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (and:SI (match_operand:SI 1 "register_operand" "%?r,0") + (match_operand:SI 2 "and_operand" "rO,P")))] + "" + "* return output_and (operands); " + [(set_attr "type" "binary,shift") + (set_attr "length" "4,4")]) + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=r") + (and:DI (not:DI (match_operand:DI 1 "register_operand" "r")) + (match_operand:DI 2 "register_operand" "r")))] + "" + "andcm %2,%1,%0\;andcm %R2,%R1,%R0" + [(set_attr "type" "binary") + (set_attr "length" "8")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (and:SI (not:SI (match_operand:SI 1 "register_operand" "r")) + (match_operand:SI 2 "register_operand" "r")))] + "" + "andcm %2,%1,%0" + [(set_attr "type" "binary") + (set_attr "length" "4")]) + +(define_expand "iordi3" + [(set (match_operand:DI 0 "register_operand" "") + (ior:DI (match_operand:DI 1 "arith_double_operand" "") + (match_operand:DI 2 "arith_double_operand" "")))] + "" + " +{ + if (! register_operand (operands[1], DImode) + || ! register_operand (operands[2], DImode)) + /* Let GCC break this into word-at-a-time operations. */ + FAIL; +}") + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=r") + (ior:DI (match_operand:DI 1 "register_operand" "%r") + (match_operand:DI 2 "register_operand" "r")))] + "" + "or %1,%2,%0\;or %R1,%R2,%R0" + [(set_attr "type" "binary") + (set_attr "length" "8")]) + +;; Need a define_expand because we've run out of CONST_OK... characters. +(define_expand "iorsi3" + [(set (match_operand:SI 0 "register_operand" "") + (ior:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "arith32_operand" "")))] + "" + " +{ + if (! (ior_operand (operands[2], SImode) + || register_operand (operands[2], SImode))) + operands[2] = force_reg (SImode, operands[2]); +}") + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (ior:SI (match_operand:SI 1 "register_operand" "0,0") + (match_operand:SI 2 "ior_operand" "M,i")))] + "" + "* return output_ior (operands); " + [(set_attr "type" "binary,shift") + (set_attr "length" "4,4")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (ior:SI (match_operand:SI 1 "register_operand" "%r") + (match_operand:SI 2 "register_operand" "r")))] + "" + "or %1,%2,%0" + [(set_attr "type" "binary") + (set_attr "length" "4")]) + +(define_expand "xordi3" + [(set (match_operand:DI 0 "register_operand" "") + (xor:DI (match_operand:DI 1 "arith_double_operand" "") + (match_operand:DI 2 "arith_double_operand" "")))] + "" + " +{ + if (! register_operand (operands[1], DImode) + || ! register_operand (operands[2], DImode)) + /* Let GCC break this into word-at-a-time operations. */ + FAIL; +}") + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=r") + (xor:DI (match_operand:DI 1 "register_operand" "%r") + (match_operand:DI 2 "register_operand" "r")))] + "" + "xor %1,%2,%0\;xor %R1,%R2,%R0" + [(set_attr "type" "binary") + (set_attr "length" "8")]) + +(define_insn "xorsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (xor:SI (match_operand:SI 1 "register_operand" "%r") + (match_operand:SI 2 "register_operand" "r")))] + "" + "xor %1,%2,%0" + [(set_attr "type" "binary") + (set_attr "length" "4")]) + +(define_insn "negdi2" + [(set (match_operand:DI 0 "register_operand" "=r") + (neg:DI (match_operand:DI 1 "register_operand" "r")))] + "" + "sub 0,%R1,%R0\;subb 0,%1,%0" + [(set_attr "type" "unary") + (set_attr "length" "8")]) + +(define_insn "negsi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (neg:SI (match_operand:SI 1 "register_operand" "r")))] + "" + "sub 0,%1,%0" + [(set_attr "type" "unary") + (set_attr "length" "4")]) + +(define_expand "one_cmpldi2" + [(set (match_operand:DI 0 "register_operand" "") + (not:DI (match_operand:DI 1 "arith_double_operand" "")))] + "" + " +{ + if (! register_operand (operands[1], DImode)) + FAIL; +}") + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=r") + (not:DI (match_operand:DI 1 "register_operand" "r")))] + "" + "uaddcm 0,%1,%0\;uaddcm 0,%R1,%R0" + [(set_attr "type" "unary") + (set_attr "length" "8")]) + +(define_insn "one_cmplsi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (not:SI (match_operand:SI 1 "register_operand" "r")))] + "" + "uaddcm 0,%1,%0" + [(set_attr "type" "unary") + (set_attr "length" "4")]) + +;; Floating point arithmetic instructions. + +(define_insn "adddf3" + [(set (match_operand:DF 0 "register_operand" "=f") + (plus:DF (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f")))] + "! TARGET_SOFT_FLOAT" + "fadd,dbl %1,%2,%0" + [(set_attr "type" "fpalu") + (set_attr "pa_combine_type" "faddsub") + (set_attr "length" "4")]) + +(define_insn "addsf3" + [(set (match_operand:SF 0 "register_operand" "=f") + (plus:SF (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f")))] + "! TARGET_SOFT_FLOAT" + "fadd,sgl %1,%2,%0" + [(set_attr "type" "fpalu") + (set_attr "pa_combine_type" "faddsub") + (set_attr "length" "4")]) + +(define_insn "subdf3" + [(set (match_operand:DF 0 "register_operand" "=f") + (minus:DF (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f")))] + "! TARGET_SOFT_FLOAT" + "fsub,dbl %1,%2,%0" + [(set_attr "type" "fpalu") + (set_attr "pa_combine_type" "faddsub") + (set_attr "length" "4")]) + +(define_insn "subsf3" + [(set (match_operand:SF 0 "register_operand" "=f") + (minus:SF (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f")))] + "! TARGET_SOFT_FLOAT" + "fsub,sgl %1,%2,%0" + [(set_attr "type" "fpalu") + (set_attr "pa_combine_type" "faddsub") + (set_attr "length" "4")]) + +(define_insn "muldf3" + [(set (match_operand:DF 0 "register_operand" "=f") + (mult:DF (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f")))] + "! TARGET_SOFT_FLOAT" + "fmpy,dbl %1,%2,%0" + [(set_attr "type" "fpmuldbl") + (set_attr "pa_combine_type" "fmpy") + (set_attr "length" "4")]) + +(define_insn "mulsf3" + [(set (match_operand:SF 0 "register_operand" "=f") + (mult:SF (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f")))] + "! TARGET_SOFT_FLOAT" + "fmpy,sgl %1,%2,%0" + [(set_attr "type" "fpmulsgl") + (set_attr "pa_combine_type" "fmpy") + (set_attr "length" "4")]) + +(define_insn "divdf3" + [(set (match_operand:DF 0 "register_operand" "=f") + (div:DF (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f")))] + "! TARGET_SOFT_FLOAT" + "fdiv,dbl %1,%2,%0" + [(set_attr "type" "fpdivdbl") + (set_attr "length" "4")]) + +(define_insn "divsf3" + [(set (match_operand:SF 0 "register_operand" "=f") + (div:SF (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f")))] + "! TARGET_SOFT_FLOAT" + "fdiv,sgl %1,%2,%0" + [(set_attr "type" "fpdivsgl") + (set_attr "length" "4")]) + +;; CYGNUS LOCAL pa8000/law +(define_insn "negdf2" + [(set (match_operand:DF 0 "register_operand" "=f") + (neg:DF (match_operand:DF 1 "register_operand" "f")))] + "! TARGET_SOFT_FLOAT" + "* +{ + if (TARGET_PARISC_2_0) + return \"fneg,dbl %1,%0\"; + return \"fsub,dbl 0,%1,%0\"; +}" + [(set_attr "type" "fpalu") + (set_attr "length" "4")]) + +(define_insn "negsf2" + [(set (match_operand:SF 0 "register_operand" "=f") + (neg:SF (match_operand:SF 1 "register_operand" "f")))] + "! TARGET_SOFT_FLOAT" + "* +{ + if (TARGET_PARISC_2_0) + return \"fneg,sgl %1,%0\"; + return \"fsub,sgl 0,%1,%0\"; +}" + [(set_attr "type" "fpalu") + (set_attr "length" "4")]) +;; END CYGNUS LOCAL + +(define_insn "absdf2" + [(set (match_operand:DF 0 "register_operand" "=f") + (abs:DF (match_operand:DF 1 "register_operand" "f")))] + "! TARGET_SOFT_FLOAT" + "fabs,dbl %1,%0" + [(set_attr "type" "fpalu") + (set_attr "length" "4")]) + +(define_insn "abssf2" + [(set (match_operand:SF 0 "register_operand" "=f") + (abs:SF (match_operand:SF 1 "register_operand" "f")))] + "! TARGET_SOFT_FLOAT" + "fabs,sgl %1,%0" + [(set_attr "type" "fpalu") + (set_attr "length" "4")]) + +;; CYGNUS LOCAL pa8000/law +;; Simple fused multiply-add sequences. +(define_insn "" + [(set (match_operand:DF 0 "register_operand" "=f") + (plus:DF (mult:DF (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f")) + (match_operand:DF 3 "register_operand" "f")))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + "fmpyfadd,dbl %1,%2,%3,%0" + [(set_attr "type" "fpmuldbl") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:SF 0 "register_operand" "=f") + (plus:SF (mult:SF (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f")) + (match_operand:SF 3 "register_operand" "f")))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + "fmpyfadd,sgl %1,%2,%3,%0" + [(set_attr "type" "fpmuldbl") + (set_attr "length" "4")]) + +;; Generating a fused multiply sequence is a win for this case as it will +;; reduce the latency for the fused case without impacting the plain +;; multiply case. +;; +;; Similar possibilities exist for fnegabs, shadd and other insns which +;; perform two operations with the result of the first feeding the second. +(define_insn "" + [(set (match_operand:DF 0 "register_operand" "=f") + (plus:DF (mult:DF (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f")) + (match_operand:DF 3 "register_operand" "f"))) + (set (match_operand:DF 4 "register_operand" "=&f") + (mult:DF (match_dup 1) (match_dup 2)))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + "#" + [(set_attr "type" "fpmuldbl") + (set_attr "length" "8")]) + +;; We want to split this up during scheduling since we want both insns +;; to schedule independently. +(define_split + [(set (match_operand:DF 0 "register_operand" "=f") + (plus:DF (mult:DF (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f")) + (match_operand:DF 3 "register_operand" "f"))) + (set (match_operand:DF 4 "register_operand" "=&f") + (mult:DF (match_dup 1) (match_dup 2)))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + [(set (match_dup 4) (mult:DF (match_dup 1) (match_dup 2))) + (set (match_dup 0) (plus:DF (mult:DF (match_dup 1) (match_dup 2)) + (match_dup 3)))] + "") + +(define_insn "" + [(set (match_operand:SF 0 "register_operand" "=f") + (plus:SF (mult:SF (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f")) + (match_operand:SF 3 "register_operand" "f"))) + (set (match_operand:SF 4 "register_operand" "=&f") + (mult:SF (match_dup 1) (match_dup 2)))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + "#" + [(set_attr "type" "fpmuldbl") + (set_attr "length" "8")]) + +;; We want to split this up during scheduling since we want both insns +;; to schedule independently. +(define_split + [(set (match_operand:SF 0 "register_operand" "=f") + (plus:SF (mult:SF (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f")) + (match_operand:SF 3 "register_operand" "f"))) + (set (match_operand:SF 4 "register_operand" "=&f") + (mult:SF (match_dup 1) (match_dup 2)))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + [(set (match_dup 4) (mult:SF (match_dup 1) (match_dup 2))) + (set (match_dup 0) (plus:SF (mult:SF (match_dup 1) (match_dup 2)) + (match_dup 3)))] + "") + +;; Negating a multiply can be faked by adding zero in a fused multiply-add +;; instruction. +(define_insn "" + [(set (match_operand:DF 0 "register_operand" "=f") + (neg:DF (mult:DF (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f"))))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + "fmpynfadd,dbl %1,%2,0,%0" + [(set_attr "type" "fpmuldbl") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:SF 0 "register_operand" "=f") + (neg:SF (mult:SF (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f"))))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + "fmpynfadd,sgl %1,%2,0,%0" + [(set_attr "type" "fpmuldbl") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:DF 0 "register_operand" "=f") + (neg:DF (mult:DF (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f")))) + (set (match_operand:DF 3 "register_operand" "=&f") + (mult:DF (match_dup 1) (match_dup 2)))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + "#" + [(set_attr "type" "fpmuldbl") + (set_attr "length" "8")]) + +(define_split + [(set (match_operand:DF 0 "register_operand" "=f") + (neg:DF (mult:DF (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f")))) + (set (match_operand:DF 3 "register_operand" "=&f") + (mult:DF (match_dup 1) (match_dup 2)))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + [(set (match_dup 3) (mult:DF (match_dup 1) (match_dup 2))) + (set (match_dup 0) (neg:DF (mult:DF (match_dup 1) (match_dup 2))))] + "") + +(define_insn "" + [(set (match_operand:SF 0 "register_operand" "=f") + (neg:SF (mult:SF (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f")))) + (set (match_operand:SF 3 "register_operand" "=&f") + (mult:SF (match_dup 1) (match_dup 2)))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + "#" + [(set_attr "type" "fpmuldbl") + (set_attr "length" "8")]) + +(define_split + [(set (match_operand:SF 0 "register_operand" "=f") + (neg:SF (mult:SF (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f")))) + (set (match_operand:SF 3 "register_operand" "=&f") + (mult:SF (match_dup 1) (match_dup 2)))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + [(set (match_dup 3) (mult:SF (match_dup 1) (match_dup 2))) + (set (match_dup 0) (neg:SF (mult:SF (match_dup 1) (match_dup 2))))] + "") + +;; Now fused multiplies with the result of the multiply negated. +(define_insn "" + [(set (match_operand:DF 0 "register_operand" "=f") + (plus:DF (neg:DF (mult:DF (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f"))) + (match_operand:DF 3 "register_operand" "f")))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + "fmpynfadd,dbl %1,%2,%3,%0" + [(set_attr "type" "fpmuldbl") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:SF 0 "register_operand" "=f") + (plus:SF (neg:SF (mult:SF (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f"))) + (match_operand:SF 3 "register_operand" "f")))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + "fmpynfadd,sgl %1,%2,%3,%0" + [(set_attr "type" "fpmuldbl") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:DF 0 "register_operand" "=f") + (plus:DF (neg:DF (mult:DF (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f"))) + (match_operand:DF 3 "register_operand" "f"))) + (set (match_operand:DF 4 "register_operand" "=&f") + (mult:DF (match_dup 1) (match_dup 2)))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + "#" + [(set_attr "type" "fpmuldbl") + (set_attr "length" "8")]) + +(define_split + [(set (match_operand:DF 0 "register_operand" "=f") + (plus:DF (neg:DF (mult:DF (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f"))) + (match_operand:DF 3 "register_operand" "f"))) + (set (match_operand:DF 4 "register_operand" "=&f") + (mult:DF (match_dup 1) (match_dup 2)))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + [(set (match_dup 4) (mult:DF (match_dup 1) (match_dup 2))) + (set (match_dup 0) (plus:DF (neg:DF (mult:DF (match_dup 1) (match_dup 2))) + (match_dup 3)))] + "") + +(define_insn "" + [(set (match_operand:SF 0 "register_operand" "=f") + (plus:SF (neg:SF (mult:SF (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f"))) + (match_operand:SF 3 "register_operand" "f"))) + (set (match_operand:SF 4 "register_operand" "=&f") + (mult:SF (match_dup 1) (match_dup 2)))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + "#" + [(set_attr "type" "fpmuldbl") + (set_attr "length" "8")]) + +(define_split + [(set (match_operand:SF 0 "register_operand" "=f") + (plus:SF (neg:SF (mult:SF (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f"))) + (match_operand:SF 3 "register_operand" "f"))) + (set (match_operand:SF 4 "register_operand" "=&f") + (mult:SF (match_dup 1) (match_dup 2)))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + [(set (match_dup 4) (mult:SF (match_dup 1) (match_dup 2))) + (set (match_dup 0) (plus:SF (neg:SF (mult:SF (match_dup 1) (match_dup 2))) + (match_dup 3)))] + "") + +;; Same thing, but using minus instead of negation +(define_insn "" + [(set (match_operand:DF 0 "register_operand" "=f") + (minus:DF (match_operand:DF 3 "register_operand" "f") + (mult:DF (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f"))))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + "fmpynfadd,dbl %1,%2,%3,%0" + [(set_attr "type" "fpmuldbl") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:SF 0 "register_operand" "=f") + (minus:SF (match_operand:SF 3 "register_operand" "f") + (mult:SF (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f"))))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + "fmpynfadd,sgl %1,%2,%3,%0" + [(set_attr "type" "fpmuldbl") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:DF 0 "register_operand" "=f") + (minus:DF (match_operand:DF 3 "register_operand" "f") + (mult:DF (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f")))) + (set (match_operand:DF 4 "register_operand" "=&f") + (mult:DF (match_dup 1) (match_dup 2)))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + "#" + [(set_attr "type" "fpmuldbl") + (set_attr "length" "8")]) + +(define_split + [(set (match_operand:DF 0 "register_operand" "=f") + (minus:DF (match_operand:DF 3 "register_operand" "f") + (mult:DF (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f")))) + (set (match_operand:DF 4 "register_operand" "=&f") + (mult:DF (match_dup 1) (match_dup 2)))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + [(set (match_dup 4) (mult:DF (match_dup 1) (match_dup 2))) + (set (match_dup 0) (minus:DF (match_dup 3) + (mult:DF (match_dup 1) (match_dup 2))))] + "") + +(define_insn "" + [(set (match_operand:SF 0 "register_operand" "=f") + (minus:SF (match_operand:SF 3 "register_operand" "f") + (mult:SF (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f")))) + (set (match_operand:SF 4 "register_operand" "=&f") + (mult:SF (match_dup 1) (match_dup 2)))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + "#" + [(set_attr "type" "fpmuldbl") + (set_attr "length" "8")]) + +(define_split + [(set (match_operand:SF 0 "register_operand" "=f") + (minus:SF (match_operand:SF 3 "register_operand" "f") + (mult:SF (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f")))) + (set (match_operand:SF 4 "register_operand" "=&f") + (mult:SF (match_dup 1) (match_dup 2)))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + [(set (match_dup 4) (mult:SF (match_dup 1) (match_dup 2))) + (set (match_dup 0) (minus:SF (match_dup 3) + (mult:SF (match_dup 1) (match_dup 2))))] + "") + +;; And similarly for negation of an absolute value. +(define_insn "" + [(set (match_operand:DF 0 "register_operand" "=f") + (neg:DF (abs:DF (match_operand:DF 1 "register_operand" "f"))))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + "fnegabs,dbl %1,%0" + [(set_attr "type" "fpalu") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:SF 0 "register_operand" "=f") + (neg:SF (abs:SF (match_operand:SF 1 "register_operand" "f"))))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + "fnegabs,sgl %1,%0" + [(set_attr "type" "fpalu") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:DF 0 "register_operand" "=f") + (neg:DF (abs:DF (match_operand:DF 1 "register_operand" "f")))) + (set (match_operand:DF 2 "register_operand" "=&f") (abs:DF (match_dup 1)))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + "#" + [(set_attr "type" "fpalu") + (set_attr "length" "8")]) + +(define_split + [(set (match_operand:DF 0 "register_operand" "=f") + (neg:DF (abs:DF (match_operand:DF 1 "register_operand" "f")))) + (set (match_operand:DF 2 "register_operand" "=&f") (abs:DF (match_dup 1)))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + [(set (match_dup 2) (abs:DF (match_dup 1))) + (set (match_dup 0) (neg:DF (abs:DF (match_dup 1))))] + "") + +(define_insn "" + [(set (match_operand:SF 0 "register_operand" "=f") + (neg:SF (abs:SF (match_operand:SF 1 "register_operand" "f")))) + (set (match_operand:SF 2 "register_operand" "=&f") (abs:SF (match_dup 1)))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + "#" + [(set_attr "type" "fpalu") + (set_attr "length" "8")]) + +(define_split + [(set (match_operand:SF 0 "register_operand" "=f") + (neg:SF (abs:SF (match_operand:SF 1 "register_operand" "f")))) + (set (match_operand:SF 2 "register_operand" "=&f") (abs:SF (match_dup 1)))] + "! TARGET_SOFT_FLOAT && TARGET_PARISC_2_0" + [(set (match_dup 2) (abs:SF (match_dup 1))) + (set (match_dup 0) (neg:SF (abs:SF (match_dup 1))))] + "") + +;; END CYGNUS LOCAL + +(define_insn "sqrtdf2" + [(set (match_operand:DF 0 "register_operand" "=f") + (sqrt:DF (match_operand:DF 1 "register_operand" "f")))] + "! TARGET_SOFT_FLOAT" + "fsqrt,dbl %1,%0" + [(set_attr "type" "fpsqrtdbl") + (set_attr "length" "4")]) + +(define_insn "sqrtsf2" + [(set (match_operand:SF 0 "register_operand" "=f") + (sqrt:SF (match_operand:SF 1 "register_operand" "f")))] + "! TARGET_SOFT_FLOAT" + "fsqrt,sgl %1,%0" + [(set_attr "type" "fpsqrtsgl") + (set_attr "length" "4")]) + +;;- Shift instructions + +;; Optimized special case of shifting. + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (lshiftrt:SI (match_operand:SI 1 "memory_operand" "m") + (const_int 24)))] + "" + "ldb%M1 %1,%0" + [(set_attr "type" "load") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (lshiftrt:SI (match_operand:SI 1 "memory_operand" "m") + (const_int 16)))] + "" + "ldh%M1 %1,%0" + [(set_attr "type" "load") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (mult:SI (match_operand:SI 2 "register_operand" "r") + (match_operand:SI 3 "shadd_operand" "")) + (match_operand:SI 1 "register_operand" "r")))] + "" + "sh%O3addl %2,%1,%0" + [(set_attr "type" "binary") + (set_attr "length" "4")]) + +;; This variant of the above insn can occur if the first operand +;; is the frame pointer. This is a kludge, but there doesn't +;; seem to be a way around it. Only recognize it while reloading. +;; Note how operand 3 uses a predicate of "const_int_operand", but +;; has constraints allowing a register. I don't know how this works, +;; but it somehow makes sure that out-of-range constants are placed +;; in a register which somehow magically is a "const_int_operand". +;; (this was stolen from alpha.md, I'm not going to try and change it. + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=&r,r") + (plus:SI (plus:SI (mult:SI (match_operand:SI 2 "register_operand" "r,r") + (match_operand:SI 4 "shadd_operand" "")) + (match_operand:SI 1 "register_operand" "r,r")) + (match_operand:SI 3 "const_int_operand" "r,J")))] + "reload_in_progress" + "@ + sh%O4addl %2,%1,%0\;addl %3,%0,%0 + sh%O4addl %2,%1,%0\;ldo %3(%0),%0" + [(set_attr "type" "multi") + (set_attr "length" "8")]) + +;; This anonymous pattern and splitter wins because it reduces the latency +;; of the shadd sequence without increasing the latency of the shift. +;; +;; We want to make sure and split up the operations for the scheduler since +;; these instructions can (and should) schedule independently. +;; +;; It would be clearer if combine used the same operator for both expressions, +;; it's somewhat confusing to have a mult in ine operation and an ashift +;; in the other. +;; +;; If this pattern is not split before register allocation, then we must expose +;; the fact that operand 4 is set before operands 1, 2 and 3 have been read. +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (mult:SI (match_operand:SI 2 "register_operand" "r") + (match_operand:SI 3 "shadd_operand" "")) + (match_operand:SI 1 "register_operand" "r"))) + (set (match_operand:SI 4 "register_operand" "=&r") + (ashift:SI (match_dup 2) + (match_operand:SI 5 "const_int_operand" "i")))] + "INTVAL (operands[5]) == exact_log2 (INTVAL (operands[3]))" + "#" + [(set_attr "type" "binary") + (set_attr "length" "8")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (mult:SI (match_operand:SI 2 "register_operand" "r") + (match_operand:SI 3 "shadd_operand" "")) + (match_operand:SI 1 "register_operand" "r"))) + (set (match_operand:SI 4 "register_operand" "=&r") + (ashift:SI (match_dup 2) + (match_operand:SI 5 "const_int_operand" "i")))] + "INTVAL (operands[5]) == exact_log2 (INTVAL (operands[3]))" + [(set (match_dup 4) (ashift:SI (match_dup 2) (match_dup 5))) + (set (match_dup 0) (plus:SI (mult:SI (match_dup 2) (match_dup 3)) + (match_dup 1)))] + "") + +(define_expand "ashlsi3" + [(set (match_operand:SI 0 "register_operand" "") + (ashift:SI (match_operand:SI 1 "lhs_lshift_operand" "") + (match_operand:SI 2 "arith32_operand" "")))] + "" + " +{ + if (GET_CODE (operands[2]) != CONST_INT) + { + rtx temp = gen_reg_rtx (SImode); + emit_insn (gen_subsi3 (temp, GEN_INT (31), operands[2])); + if (GET_CODE (operands[1]) == CONST_INT) + emit_insn (gen_zvdep_imm (operands[0], operands[1], temp)); + else + emit_insn (gen_zvdep32 (operands[0], operands[1], temp)); + DONE; + } + /* Make sure both inputs are not constants, + there are no patterns for that. */ + operands[1] = force_reg (SImode, operands[1]); +}") + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (ashift:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "const_int_operand" "n")))] + "" + "zdep %1,%P2,%L2,%0" + [(set_attr "type" "shift") + (set_attr "length" "4")]) + +; Match cases of op1 a CONST_INT here that zvdep_imm doesn't handle. +; Doing it like this makes slightly better code since reload can +; replace a register with a known value in range -16..15 with a +; constant. Ideally, we would like to merge zvdep32 and zvdep_imm, +; but since we have no more CONST_OK... characters, that is not +; possible. +(define_insn "zvdep32" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (ashift:SI (match_operand:SI 1 "arith5_operand" "r,L") + (minus:SI (const_int 31) + (match_operand:SI 2 "register_operand" "q,q"))))] + "" + "@ + zvdep %1,32,%0 + zvdepi %1,32,%0" + [(set_attr "type" "shift,shift") + (set_attr "length" "4,4")]) + +(define_insn "zvdep_imm" + [(set (match_operand:SI 0 "register_operand" "=r") + (ashift:SI (match_operand:SI 1 "lhs_lshift_cint_operand" "") + (minus:SI (const_int 31) + (match_operand:SI 2 "register_operand" "q"))))] + "" + "* +{ + int x = INTVAL (operands[1]); + operands[2] = GEN_INT (4 + exact_log2 ((x >> 4) + 1)); + operands[1] = GEN_INT ((x & 0xf) - 0x10); + return \"zvdepi %1,%2,%0\"; +}" + [(set_attr "type" "shift") + (set_attr "length" "4")]) + +(define_insn "vdepi_ior" + [(set (match_operand:SI 0 "register_operand" "=r") + (ior:SI (ashift:SI (match_operand:SI 1 "const_int_operand" "") + (minus:SI (const_int 31) + (match_operand:SI 2 "register_operand" "q"))) + (match_operand:SI 3 "register_operand" "0")))] + ; accept ...0001...1, can this be generalized? + "exact_log2 (INTVAL (operands[1]) + 1) >= 0" + "* +{ + int x = INTVAL (operands[1]); + operands[2] = GEN_INT (exact_log2 (x + 1)); + return \"vdepi -1,%2,%0\"; +}" + [(set_attr "type" "shift") + (set_attr "length" "4")]) + +(define_insn "vdepi_and" + [(set (match_operand:SI 0 "register_operand" "=r") + (and:SI (rotate:SI (match_operand:SI 1 "const_int_operand" "") + (minus:SI (const_int 31) + (match_operand:SI 2 "register_operand" "q"))) + (match_operand:SI 3 "register_operand" "0")))] + ; this can be generalized...! + "INTVAL (operands[1]) == -2" + "* +{ + int x = INTVAL (operands[1]); + operands[2] = GEN_INT (exact_log2 ((~x) + 1)); + return \"vdepi 0,%2,%0\"; +}" + [(set_attr "type" "shift") + (set_attr "length" "4")]) + +(define_expand "ashrsi3" + [(set (match_operand:SI 0 "register_operand" "") + (ashiftrt:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "arith32_operand" "")))] + "" + " +{ + if (GET_CODE (operands[2]) != CONST_INT) + { + rtx temp = gen_reg_rtx (SImode); + emit_insn (gen_subsi3 (temp, GEN_INT (31), operands[2])); + emit_insn (gen_vextrs32 (operands[0], operands[1], temp)); + DONE; + } +}") + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (ashiftrt:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "const_int_operand" "n")))] + "" + "extrs %1,%P2,%L2,%0" + [(set_attr "type" "shift") + (set_attr "length" "4")]) + +(define_insn "vextrs32" + [(set (match_operand:SI 0 "register_operand" "=r") + (ashiftrt:SI (match_operand:SI 1 "register_operand" "r") + (minus:SI (const_int 31) + (match_operand:SI 2 "register_operand" "q"))))] + "" + "vextrs %1,32,%0" + [(set_attr "type" "shift") + (set_attr "length" "4")]) + +(define_insn "lshrsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (lshiftrt:SI (match_operand:SI 1 "register_operand" "r,r") + (match_operand:SI 2 "arith32_operand" "q,n")))] + "" + "@ + vshd 0,%1,%0 + extru %1,%P2,%L2,%0" + [(set_attr "type" "shift") + (set_attr "length" "4")]) + +(define_insn "rotrsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (rotatert:SI (match_operand:SI 1 "register_operand" "r,r") + (match_operand:SI 2 "arith32_operand" "q,n")))] + "" + "* +{ + if (GET_CODE (operands[2]) == CONST_INT) + { + operands[2] = GEN_INT (INTVAL (operands[2]) & 31); + return \"shd %1,%1,%2,%0\"; + } + else + return \"vshd %1,%1,%0\"; +}" + [(set_attr "type" "shift") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (match_operator:SI 5 "plus_xor_ior_operator" + [(ashift:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 3 "const_int_operand" "n")) + (lshiftrt:SI (match_operand:SI 2 "register_operand" "r") + (match_operand:SI 4 "const_int_operand" "n"))]))] + "INTVAL (operands[3]) + INTVAL (operands[4]) == 32" + "shd %1,%2,%4,%0" + [(set_attr "type" "shift") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (match_operator:SI 5 "plus_xor_ior_operator" + [(lshiftrt:SI (match_operand:SI 2 "register_operand" "r") + (match_operand:SI 4 "const_int_operand" "n")) + (ashift:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 3 "const_int_operand" "n"))]))] + "INTVAL (operands[3]) + INTVAL (operands[4]) == 32" + "shd %1,%2,%4,%0" + [(set_attr "type" "shift") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (and:SI (ashift:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "const_int_operand" "")) + (match_operand:SI 3 "const_int_operand" "")))] + "exact_log2 (1 + (INTVAL (operands[3]) >> (INTVAL (operands[2]) & 31))) >= 0" + "* +{ + int cnt = INTVAL (operands[2]) & 31; + operands[3] = GEN_INT (exact_log2 (1 + (INTVAL (operands[3]) >> cnt))); + operands[2] = GEN_INT (31 - cnt); + return \"zdep %1,%2,%3,%0\"; +}" + [(set_attr "type" "shift") + (set_attr "length" "4")]) + +;; Unconditional and other jump instructions. + +;; CYGNUS LOCAL PA8000/law +(define_insn "return" + [(return)] + "hppa_can_use_return_insn_p ()" + "* +{ + if (TARGET_PARISC_2_0) + return \"bve%* 0(%%r2)\"; + return \"bv%* 0(%%r2)\"; +}" + [(set_attr "type" "branch") + (set_attr "length" "4")]) + +;; Use a different pattern for functions which have non-trivial +;; epilogues so as not to confuse jump and reorg. +(define_insn "return_internal" + [(use (reg:SI 2)) + (return)] + "" + "* +{ + if (TARGET_PARISC_2_0) + return \"bve%* 0(%%r2)\"; + return \"bv%* 0(%%r2)\"; +}" + [(set_attr "type" "branch") + (set_attr "length" "4")]) +;; END CYGNUS LOCAL + +(define_expand "prologue" + [(const_int 0)] + "" + "hppa_expand_prologue ();DONE;") + +(define_expand "epilogue" + [(return)] + "" + " +{ + /* Try to use the trivial return first. Else use the full + epilogue. */ + if (hppa_can_use_return_insn_p ()) + emit_jump_insn (gen_return ()); + else + { + hppa_expand_epilogue (); + emit_jump_insn (gen_return_internal ()); + } + DONE; +}") + +;; Special because we use the value placed in %r2 by the bl instruction +;; from within its delay slot to set the value for the 2nd parameter to +;; the call. +(define_insn "call_profiler" + [(unspec_volatile [(const_int 0)] 0) + (use (match_operand:SI 0 "const_int_operand" ""))] + "" + "bl _mcount,%%r2\;ldo %0(%%r2),%%r25" + [(set_attr "type" "multi") + (set_attr "length" "8")]) + +(define_insn "blockage" + [(unspec_volatile [(const_int 2)] 0)] + "" + "" + [(set_attr "length" "0")]) + +(define_insn "jump" + [(set (pc) (label_ref (match_operand 0 "" "")))] + "" + "* +{ + extern int optimize; + + if (GET_MODE (insn) == SImode) + return \"bl %l0,0%#\"; + + /* An unconditional branch which can reach its target. */ + if (get_attr_length (insn) != 24 + && get_attr_length (insn) != 16) + return \"bl%* %l0,0\"; + + /* An unconditional branch which can not reach its target. + + We need to be able to use %r1 as a scratch register; however, + we can never be sure whether or not it's got a live value in + it. Therefore, we must restore its original value after the + jump. + + To make matters worse, we don't have a stack slot which we + can always clobber. sp-12/sp-16 shouldn't ever have a live + value during a non-optimizing compilation, so we use those + slots for now. We don't support very long branches when + optimizing -- they should be quite rare when optimizing. + + Really the way to go long term is a register scavenger; goto + the target of the jump and find a register which we can use + as a scratch to hold the value in %r1. */ + + /* We don't know how to register scavenge yet. */ + if (optimize) + abort (); + + /* First store %r1 into the stack. */ + output_asm_insn (\"stw %%r1,-16(%%r30)\", operands); + + /* Now load the target address into %r1 and do an indirect jump + to the value specified in %r1. Be careful to generate PIC + code as needed. */ + if (flag_pic) + { + rtx xoperands[2]; + xoperands[0] = operands[0]; + xoperands[1] = gen_label_rtx (); + + output_asm_insn (\"bl .+8,%%r1\\n\\taddil L'%l0-%l1,%%r1\", xoperands); + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\", + CODE_LABEL_NUMBER (xoperands[1])); + output_asm_insn (\"ldo R'%l0-%l1(%%r1),%%r1\\n\\tbv 0(%%r1)\", + xoperands); + } + else + output_asm_insn (\"ldil L'%l0,%%r1\\n\\tbe R'%l0(%%sr4,%%r1)\", operands);; + + /* And restore the value of %r1 in the delay slot. We're not optimizing, + so we know nothing else can be in the delay slot. */ + return \"ldw -16(%%r30),%%r1\"; +}" + [(set_attr "type" "uncond_branch") + (set_attr "pa_combine_type" "uncond_branch") + (set (attr "length") + (cond [(eq (symbol_ref "jump_in_call_delay (insn)") (const_int 1)) + (if_then_else (lt (abs (minus (match_dup 0) + (plus (pc) (const_int 8)))) + (const_int 8184)) + (const_int 4) + (const_int 8)) + (ge (abs (minus (match_dup 0) (plus (pc) (const_int 8)))) + (const_int 262100)) + (if_then_else (eq (symbol_ref "flag_pic") (const_int 0)) + (const_int 16) + (const_int 24))] + (const_int 4)))]) + +;; Subroutines of "casesi". +;; operand 0 is index +;; operand 1 is the minimum bound +;; operand 2 is the maximum bound - minimum bound + 1 +;; operand 3 is CODE_LABEL for the table; +;; operand 4 is the CODE_LABEL to go to if index out of range. + +(define_expand "casesi" + [(match_operand:SI 0 "general_operand" "") + (match_operand:SI 1 "const_int_operand" "") + (match_operand:SI 2 "const_int_operand" "") + (match_operand 3 "" "") + (match_operand 4 "" "")] + "" + " +{ + if (GET_CODE (operands[0]) != REG) + operands[0] = force_reg (SImode, operands[0]); + + if (operands[1] != const0_rtx) + { + rtx reg = gen_reg_rtx (SImode); + + operands[1] = GEN_INT (-INTVAL (operands[1])); + if (!INT_14_BITS (operands[1])) + operands[1] = force_reg (SImode, operands[1]); + emit_insn (gen_addsi3 (reg, operands[0], operands[1])); + + operands[0] = reg; + } + + if (!INT_5_BITS (operands[2])) + operands[2] = force_reg (SImode, operands[2]); + + emit_insn (gen_cmpsi (operands[0], operands[2])); + emit_jump_insn (gen_bgtu (operands[4])); + if (TARGET_BIG_SWITCH) + { + rtx temp = gen_reg_rtx (SImode); + emit_move_insn (temp, gen_rtx_PLUS (SImode, operands[0], operands[0])); + operands[0] = temp; + } + emit_jump_insn (gen_casesi0 (operands[0], operands[3])); + DONE; +}") + +(define_insn "casesi0" + [(set (pc) (plus:SI + (mem:SI (plus:SI (pc) + (match_operand:SI 0 "register_operand" "r"))) + (label_ref (match_operand 1 "" ""))))] + "" + "blr %0,0\;nop" + [(set_attr "type" "multi") + (set_attr "length" "8")]) + +;; Need nops for the calls because execution is supposed to continue +;; past; we don't want to nullify an instruction that we need. +;;- jump to subroutine + +(define_expand "call" + [(parallel [(call (match_operand:SI 0 "" "") + (match_operand 1 "" "")) + (clobber (reg:SI 2))])] + "" + " +{ + rtx op; + rtx call_insn; + + if (TARGET_PORTABLE_RUNTIME) + op = force_reg (SImode, XEXP (operands[0], 0)); + else + op = XEXP (operands[0], 0); + + /* Use two different patterns for calls to explicitly named functions + and calls through function pointers. This is necessary as these two + types of calls use different calling conventions, and CSE might try + to change the named call into an indirect call in some cases (using + two patterns keeps CSE from performing this optimization). */ + if (GET_CODE (op) == SYMBOL_REF) + call_insn = emit_call_insn (gen_call_internal_symref (op, operands[1])); + else + { + rtx tmpreg = gen_rtx_REG (SImode, 22); + emit_move_insn (tmpreg, force_reg (SImode, op)); + call_insn = emit_call_insn (gen_call_internal_reg (operands[1])); + } + + if (flag_pic) + { + use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), pic_offset_table_rtx); + + /* After each call we must restore the PIC register, even if it + doesn't appear to be used. + + This will set regs_ever_live for the callee saved register we + stored the PIC register in. */ + emit_move_insn (pic_offset_table_rtx, + gen_rtx_REG (SImode, PIC_OFFSET_TABLE_REGNUM_SAVED)); + emit_insn (gen_rtx_USE (VOIDmode, pic_offset_table_rtx)); + + /* Gross. We have to keep the scheduler from moving the restore + of the PIC register away from the call. SCHED_GROUP_P is + supposed to do this, but for some reason the compiler will + go into an infinite loop when we use that. + + This method (blockage insn) may make worse code (then again + it may not since calls are nearly blockages anyway), but at + least it should work. */ + emit_insn (gen_blockage ()); + } + DONE; +}") + +(define_insn "call_internal_symref" + [(call (mem:SI (match_operand:SI 0 "call_operand_address" "")) + (match_operand 1 "" "i")) + (clobber (reg:SI 2)) + (use (const_int 0))] + "! TARGET_PORTABLE_RUNTIME" + "* +{ + output_arg_descriptor (insn); + return output_call (insn, operands[0], gen_rtx_REG (SImode, 2)); +}" + [(set_attr "type" "call") + (set (attr "length") +;; If we're sure that we can either reach the target or that the +;; linker can use a long-branch stub, then the length is 4 bytes. +;; +;; For long-calls the length will be either 52 bytes (non-pic) +;; or 68 bytes (pic). */ +;; Else we have to use a long-call; + (if_then_else (lt (plus (symbol_ref "total_code_bytes") (pc)) + (const_int 240000)) + (const_int 4) + (if_then_else (eq (symbol_ref "flag_pic") + (const_int 0)) + (const_int 52) + (const_int 68))))]) + +(define_insn "call_internal_reg" + [(call (mem:SI (reg:SI 22)) + (match_operand 0 "" "i")) + (clobber (reg:SI 2)) + (use (const_int 1))] + "" + "* +{ + rtx xoperands[2]; + + /* First the special case for kernels, level 0 systems, etc. */ + if (TARGET_NO_SPACE_REGS || TARGET_FAST_INDIRECT_CALLS) + return \"ble 0(%%sr4,%%r22)\;copy %%r31,%%r2\"; + + /* Now the normal case -- we can reach $$dyncall directly or + we're sure that we can get there via a long-branch stub. + + No need to check target flags as the length uniquely identifies + the remaining cases. */ + if (get_attr_length (insn) == 8) + return \".CALL\\tARGW0=GR\;bl $$dyncall,%%r31\;copy %%r31,%%r2\"; + + /* Long millicode call, but we are not generating PIC or portable runtime + code. */ + if (get_attr_length (insn) == 12) + return \".CALL\\tARGW0=GR\;ldil L%%$$dyncall,%%r2\;ble R%%$$dyncall(%%sr4,%%r2)\;copy %%r31,%%r2\"; + + /* Long millicode call for portable runtime. */ + if (get_attr_length (insn) == 20) + return \"ldil L%%$$dyncall,%%r31\;ldo R%%$$dyncall(%%r31),%%r31\;blr 0,%%r2\;bv,n 0(%%r31)\;nop\"; + + /* If we're generating PIC code. */ + xoperands[0] = operands[0]; + xoperands[1] = gen_label_rtx (); + output_asm_insn (\"bl .+8,%%r1\", xoperands); + output_asm_insn (\"addil L%%$$dyncall-%1,%%r1\", xoperands); + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\", + CODE_LABEL_NUMBER (xoperands[1])); + output_asm_insn (\"ldo R%%$$dyncall-%1(%%r1),%%r1\", xoperands); + output_asm_insn (\"blr 0,%%r2\", xoperands); + output_asm_insn (\"bv,n 0(%%r1)\\n\\tnop\", xoperands); + return \"\"; +}" + [(set_attr "type" "dyncall") + (set (attr "length") + (cond [ +;; First NO_SPACE_REGS + (ne (symbol_ref "TARGET_NO_SPACE_REGS || TARGET_FAST_INDIRECT_CALLS") + (const_int 0)) + (const_int 8) + +;; Target (or stub) within reach + (and (lt (plus (symbol_ref "total_code_bytes") (pc)) + (const_int 240000)) + (eq (symbol_ref "TARGET_PORTABLE_RUNTIME") + (const_int 0))) + (const_int 8) + +;; Out of reach, but not PIC or PORTABLE_RUNTIME + (and (eq (symbol_ref "TARGET_PORTABLE_RUNTIME") + (const_int 0)) + (eq (symbol_ref "flag_pic") + (const_int 0))) + (const_int 12) + + (ne (symbol_ref "TARGET_PORTABLE_RUNTIME") + (const_int 0)) + (const_int 20)] + +;; Out of range PIC case + (const_int 24)))]) + +(define_expand "call_value" + [(parallel [(set (match_operand 0 "" "") + (call (match_operand:SI 1 "" "") + (match_operand 2 "" ""))) + (clobber (reg:SI 2))])] + "" + " +{ + rtx op; + rtx call_insn; + + if (TARGET_PORTABLE_RUNTIME) + op = force_reg (SImode, XEXP (operands[1], 0)); + else + op = XEXP (operands[1], 0); + + /* Use two different patterns for calls to explicitly named functions + and calls through function pointers. This is necessary as these two + types of calls use different calling conventions, and CSE might try + to change the named call into an indirect call in some cases (using + two patterns keeps CSE from performing this optimization). */ + if (GET_CODE (op) == SYMBOL_REF) + call_insn = emit_call_insn (gen_call_value_internal_symref (operands[0], + op, + operands[2])); + else + { + rtx tmpreg = gen_rtx_REG (SImode, 22); + emit_move_insn (tmpreg, force_reg (SImode, op)); + call_insn = emit_call_insn (gen_call_value_internal_reg (operands[0], + operands[2])); + } + if (flag_pic) + { + use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), pic_offset_table_rtx); + + /* After each call we must restore the PIC register, even if it + doesn't appear to be used. + + This will set regs_ever_live for the callee saved register we + stored the PIC register in. */ + emit_move_insn (pic_offset_table_rtx, + gen_rtx_REG (SImode, PIC_OFFSET_TABLE_REGNUM_SAVED)); + emit_insn (gen_rtx_USE (VOIDmode, pic_offset_table_rtx)); + + /* Gross. We have to keep the scheduler from moving the restore + of the PIC register away from the call. SCHED_GROUP_P is + supposed to do this, but for some reason the compiler will + go into an infinite loop when we use that. + + This method (blockage insn) may make worse code (then again + it may not since calls are nearly blockages anyway), but at + least it should work. */ + emit_insn (gen_blockage ()); + } + DONE; +}") + +(define_insn "call_value_internal_symref" + [(set (match_operand 0 "" "=rf") + (call (mem:SI (match_operand:SI 1 "call_operand_address" "")) + (match_operand 2 "" "i"))) + (clobber (reg:SI 2)) + (use (const_int 0))] + ;;- Don't use operand 1 for most machines. + "! TARGET_PORTABLE_RUNTIME" + "* +{ + output_arg_descriptor (insn); + return output_call (insn, operands[1], gen_rtx_REG (SImode, 2)); +}" + [(set_attr "type" "call") + (set (attr "length") +;; If we're sure that we can either reach the target or that the +;; linker can use a long-branch stub, then the length is 4 bytes. +;; +;; For long-calls the length will be either 52 bytes (non-pic) +;; or 68 bytes (pic). */ +;; Else we have to use a long-call; + (if_then_else (lt (plus (symbol_ref "total_code_bytes") (pc)) + (const_int 240000)) + (const_int 4) + (if_then_else (eq (symbol_ref "flag_pic") + (const_int 0)) + (const_int 52) + (const_int 68))))]) + +(define_insn "call_value_internal_reg" + [(set (match_operand 0 "" "=rf") + (call (mem:SI (reg:SI 22)) + (match_operand 1 "" "i"))) + (clobber (reg:SI 2)) + (use (const_int 1))] + "" + "* +{ + rtx xoperands[2]; + + /* First the special case for kernels, level 0 systems, etc. */ + if (TARGET_NO_SPACE_REGS || TARGET_FAST_INDIRECT_CALLS) + return \"ble 0(%%sr4,%%r22)\;copy %%r31,%%r2\"; + + /* Now the normal case -- we can reach $$dyncall directly or + we're sure that we can get there via a long-branch stub. + + No need to check target flags as the length uniquely identifies + the remaining cases. */ + if (get_attr_length (insn) == 8) + return \".CALL\\tARGW0=GR\;bl $$dyncall,%%r31\;copy %%r31,%%r2\"; + + /* Long millicode call, but we are not generating PIC or portable runtime + code. */ + if (get_attr_length (insn) == 12) + return \".CALL\\tARGW0=GR\;ldil L%%$$dyncall,%%r2\;ble R%%$$dyncall(%%sr4,%%r2)\;copy %%r31,%%r2\"; + + /* Long millicode call for portable runtime. */ + if (get_attr_length (insn) == 20) + return \"ldil L%%$$dyncall,%%r31\;ldo R%%$$dyncall(%%r31),%%r31\;blr 0,%%r2\;bv,n 0(%%r31)\;nop\"; + + /* If we're generating PIC code. */ + xoperands[0] = operands[1]; + xoperands[1] = gen_label_rtx (); + output_asm_insn (\"bl .+8,%%r1\", xoperands); + output_asm_insn (\"addil L%%$$dyncall-%1,%%r1\", xoperands); + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\", + CODE_LABEL_NUMBER (xoperands[1])); + output_asm_insn (\"ldo R%%$$dyncall-%1(%%r1),%%r1\", xoperands); + output_asm_insn (\"blr 0,%%r2\", xoperands); + output_asm_insn (\"bv,n 0(%%r1)\\n\\tnop\", xoperands); + return \"\"; +}" + [(set_attr "type" "dyncall") + (set (attr "length") + (cond [ +;; First NO_SPACE_REGS + (ne (symbol_ref "TARGET_NO_SPACE_REGS || TARGET_FAST_INDIRECT_CALLS") + (const_int 0)) + (const_int 8) + +;; Target (or stub) within reach + (and (lt (plus (symbol_ref "total_code_bytes") (pc)) + (const_int 240000)) + (eq (symbol_ref "TARGET_PORTABLE_RUNTIME") + (const_int 0))) + (const_int 8) + +;; Out of reach, but not PIC or PORTABLE_RUNTIME + (and (eq (symbol_ref "TARGET_PORTABLE_RUNTIME") + (const_int 0)) + (eq (symbol_ref "flag_pic") + (const_int 0))) + (const_int 12) + + (ne (symbol_ref "TARGET_PORTABLE_RUNTIME") + (const_int 0)) + (const_int 20)] + +;; Out of range PIC case + (const_int 24)))]) + +;; Call subroutine returning any type. + +(define_expand "untyped_call" + [(parallel [(call (match_operand 0 "" "") + (const_int 0)) + (match_operand 1 "" "") + (match_operand 2 "" "")])] + "" + " +{ + int i; + + emit_call_insn (gen_call (operands[0], const0_rtx)); + + for (i = 0; i < XVECLEN (operands[2], 0); i++) + { + rtx set = XVECEXP (operands[2], 0, i); + emit_move_insn (SET_DEST (set), SET_SRC (set)); + } + + /* The optimizer does not know that the call sets the function value + registers we stored in the result block. We avoid problems by + claiming that all hard registers are used and clobbered at this + point. */ + emit_insn (gen_blockage ()); + + DONE; +}") +(define_insn "nop" + [(const_int 0)] + "" + "nop" + [(set_attr "type" "move") + (set_attr "length" "4")]) + +;; These are just placeholders so we know where branch tables +;; begin and end. +(define_insn "begin_brtab" + [(const_int 1)] + "" + "* +{ + /* Only GAS actually supports this pseudo-op. */ + if (TARGET_GAS) + return \".begin_brtab\"; + else + return \"\"; +}" + [(set_attr "type" "move") + (set_attr "length" "0")]) + +(define_insn "end_brtab" + [(const_int 2)] + "" + "* +{ + /* Only GAS actually supports this pseudo-op. */ + if (TARGET_GAS) + return \".end_brtab\"; + else + return \"\"; +}" + [(set_attr "type" "move") + (set_attr "length" "0")]) + +;;; Hope this is only within a function... +(define_insn "indirect_jump" + [(set (pc) (match_operand:SI 0 "register_operand" "r"))] + "" + "bv%* 0(%0)" + [(set_attr "type" "branch") + (set_attr "length" "4")]) + +(define_insn "extzv" + [(set (match_operand:SI 0 "register_operand" "=r") + (zero_extract:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "uint5_operand" "") + (match_operand:SI 3 "uint5_operand" "")))] + "" + "extru %1,%3+%2-1,%2,%0" + [(set_attr "type" "shift") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (zero_extract:SI (match_operand:SI 1 "register_operand" "r") + (const_int 1) + (match_operand:SI 3 "register_operand" "q")))] + "" + "vextru %1,1,%0" + [(set_attr "type" "shift") + (set_attr "length" "4")]) + +(define_insn "extv" + [(set (match_operand:SI 0 "register_operand" "=r") + (sign_extract:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "uint5_operand" "") + (match_operand:SI 3 "uint5_operand" "")))] + "" + "extrs %1,%3+%2-1,%2,%0" + [(set_attr "type" "shift") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (sign_extract:SI (match_operand:SI 1 "register_operand" "r") + (const_int 1) + (match_operand:SI 3 "register_operand" "q")))] + "" + "vextrs %1,1,%0" + [(set_attr "type" "shift") + (set_attr "length" "4")]) + +(define_insn "insv" + [(set (zero_extract:SI (match_operand:SI 0 "register_operand" "+r,r") + (match_operand:SI 1 "uint5_operand" "") + (match_operand:SI 2 "uint5_operand" "")) + (match_operand:SI 3 "arith5_operand" "r,L"))] + "" + "@ + dep %3,%2+%1-1,%1,%0 + depi %3,%2+%1-1,%1,%0" + [(set_attr "type" "shift,shift") + (set_attr "length" "4,4")]) + +;; Optimize insertion of const_int values of type 1...1xxxx. +(define_insn "" + [(set (zero_extract:SI (match_operand:SI 0 "register_operand" "+r") + (match_operand:SI 1 "uint5_operand" "") + (match_operand:SI 2 "uint5_operand" "")) + (match_operand:SI 3 "const_int_operand" ""))] + "(INTVAL (operands[3]) & 0x10) != 0 && + (~INTVAL (operands[3]) & ((1L << INTVAL (operands[1])) - 1) & ~0xf) == 0" + "* +{ + operands[3] = GEN_INT ((INTVAL (operands[3]) & 0xf) - 0x10); + return \"depi %3,%2+%1-1,%1,%0\"; +}" + [(set_attr "type" "shift") + (set_attr "length" "4")]) + +;; This insn is used for some loop tests, typically loops reversed when +;; strength reduction is used. It is actually created when the instruction +;; combination phase combines the special loop test. Since this insn +;; is both a jump insn and has an output, it must deal with its own +;; reloads, hence the `m' constraints. The `!' constraints direct reload +;; to not choose the register alternatives in the event a reload is needed. +(define_insn "decrement_and_branch_until_zero" + [(set (pc) + (if_then_else + (match_operator 2 "comparison_operator" + [(plus:SI (match_operand:SI 0 "register_operand" "+!r,!*f,!*m") + (match_operand:SI 1 "int5_operand" "L,L,L")) + (const_int 0)]) + (label_ref (match_operand 3 "" "")) + (pc))) + (set (match_dup 0) + (plus:SI (match_dup 0) (match_dup 1))) + (clobber (match_scratch:SI 4 "=X,r,r"))] + "" + "* return output_dbra (operands, insn, which_alternative); " +;; Do not expect to understand this the first time through. +[(set_attr "type" "cbranch,multi,multi") + (set (attr "length") + (if_then_else (eq_attr "alternative" "0") +;; Loop counter in register case +;; Short branch has length of 4 +;; Long branch has length of 8 + (if_then_else (lt (abs (minus (match_dup 3) (plus (pc) (const_int 8)))) + (const_int 8184)) + (const_int 4) + (const_int 8)) + +;; Loop counter in FP reg case. +;; Extra goo to deal with additional reload insns. + (if_then_else (eq_attr "alternative" "1") + (if_then_else (lt (match_dup 3) (pc)) + (if_then_else + (lt (abs (minus (match_dup 3) (plus (pc) (const_int 24)))) + (const_int 8184)) + (const_int 24) + (const_int 28)) + (if_then_else + (lt (abs (minus (match_dup 3) (plus (pc) (const_int 8)))) + (const_int 8184)) + (const_int 24) + (const_int 28))) +;; Loop counter in memory case. +;; Extra goo to deal with additional reload insns. + (if_then_else (lt (match_dup 3) (pc)) + (if_then_else + (lt (abs (minus (match_dup 3) (plus (pc) (const_int 12)))) + (const_int 8184)) + (const_int 12) + (const_int 16)) + (if_then_else + (lt (abs (minus (match_dup 3) (plus (pc) (const_int 8)))) + (const_int 8184)) + (const_int 12) + (const_int 16))))))]) + +(define_insn "" + [(set (pc) + (if_then_else + (match_operator 2 "movb_comparison_operator" + [(match_operand:SI 1 "register_operand" "r,r,r,r") (const_int 0)]) + (label_ref (match_operand 3 "" "")) + (pc))) + (set (match_operand:SI 0 "register_operand" "=!r,!*f,!*m,!*q") + (match_dup 1))] + "" +"* return output_movb (operands, insn, which_alternative, 0); " +;; Do not expect to understand this the first time through. +[(set_attr "type" "cbranch,multi,multi,multi") + (set (attr "length") + (if_then_else (eq_attr "alternative" "0") +;; Loop counter in register case +;; Short branch has length of 4 +;; Long branch has length of 8 + (if_then_else (lt (abs (minus (match_dup 3) (plus (pc) (const_int 8)))) + (const_int 8184)) + (const_int 4) + (const_int 8)) + +;; Loop counter in FP reg case. +;; Extra goo to deal with additional reload insns. + (if_then_else (eq_attr "alternative" "1") + (if_then_else (lt (match_dup 3) (pc)) + (if_then_else + (lt (abs (minus (match_dup 3) (plus (pc) (const_int 12)))) + (const_int 8184)) + (const_int 12) + (const_int 16)) + (if_then_else + (lt (abs (minus (match_dup 3) (plus (pc) (const_int 8)))) + (const_int 8184)) + (const_int 12) + (const_int 16))) +;; Loop counter in memory or sar case. +;; Extra goo to deal with additional reload insns. + (if_then_else + (lt (abs (minus (match_dup 3) (plus (pc) (const_int 8)))) + (const_int 8184)) + (const_int 8) + (const_int 12)))))]) + +;; Handle negated branch. +(define_insn "" + [(set (pc) + (if_then_else + (match_operator 2 "movb_comparison_operator" + [(match_operand:SI 1 "register_operand" "r,r,r,r") (const_int 0)]) + (pc) + (label_ref (match_operand 3 "" "")))) + (set (match_operand:SI 0 "register_operand" "=!r,!*f,!*m,!*q") + (match_dup 1))] + "" +"* return output_movb (operands, insn, which_alternative, 1); " +;; Do not expect to understand this the first time through. +[(set_attr "type" "cbranch,multi,multi,multi") + (set (attr "length") + (if_then_else (eq_attr "alternative" "0") +;; Loop counter in register case +;; Short branch has length of 4 +;; Long branch has length of 8 + (if_then_else (lt (abs (minus (match_dup 3) (plus (pc) (const_int 8)))) + (const_int 8184)) + (const_int 4) + (const_int 8)) + +;; Loop counter in FP reg case. +;; Extra goo to deal with additional reload insns. + (if_then_else (eq_attr "alternative" "1") + (if_then_else (lt (match_dup 3) (pc)) + (if_then_else + (lt (abs (minus (match_dup 3) (plus (pc) (const_int 12)))) + (const_int 8184)) + (const_int 12) + (const_int 16)) + (if_then_else + (lt (abs (minus (match_dup 3) (plus (pc) (const_int 8)))) + (const_int 8184)) + (const_int 12) + (const_int 16))) +;; Loop counter in memory or SAR case. +;; Extra goo to deal with additional reload insns. + (if_then_else + (lt (abs (minus (match_dup 3) (plus (pc) (const_int 8)))) + (const_int 8184)) + (const_int 8) + (const_int 12)))))]) + +;; The next several patterns (parallel_addb, parallel_movb, fmpyadd and +;; fmpysub aren't currently used by the FSF sources, but will be soon. +;; +;; They're in the FSF tree for documentation and to make Cygnus<->FSF +;; merging easier. +(define_insn "" + [(set (pc) (label_ref (match_operand 3 "" "" ))) + (set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "ireg_or_int5_operand" "rL")))] + "(reload_completed && operands[0] == operands[1]) || operands[0] == operands[2]" + "* +{ + return output_parallel_addb (operands, get_attr_length (insn)); +}" + [(set_attr "type" "parallel_branch") + (set (attr "length") + (if_then_else (lt (abs (minus (match_dup 3) (plus (pc) (const_int 8)))) + (const_int 8184)) + (const_int 4) + (const_int 8)))]) + +(define_insn "" + [(set (pc) (label_ref (match_operand 2 "" "" ))) + (set (match_operand:SF 0 "register_operand" "=r") + (match_operand:SF 1 "ireg_or_int5_operand" "rL"))] + "reload_completed" + "* +{ + return output_parallel_movb (operands, get_attr_length (insn)); +}" + [(set_attr "type" "parallel_branch") + (set (attr "length") + (if_then_else (lt (abs (minus (match_dup 2) (plus (pc) (const_int 8)))) + (const_int 8184)) + (const_int 4) + (const_int 8)))]) + +(define_insn "" + [(set (pc) (label_ref (match_operand 2 "" "" ))) + (set (match_operand:SI 0 "register_operand" "=r") + (match_operand:SI 1 "ireg_or_int5_operand" "rL"))] + "reload_completed" + "* +{ + return output_parallel_movb (operands, get_attr_length (insn)); +}" + [(set_attr "type" "parallel_branch") + (set (attr "length") + (if_then_else (lt (abs (minus (match_dup 2) (plus (pc) (const_int 8)))) + (const_int 8184)) + (const_int 4) + (const_int 8)))]) + +(define_insn "" + [(set (pc) (label_ref (match_operand 2 "" "" ))) + (set (match_operand:HI 0 "register_operand" "=r") + (match_operand:HI 1 "ireg_or_int5_operand" "rL"))] + "reload_completed" + "* +{ + return output_parallel_movb (operands, get_attr_length (insn)); +}" + [(set_attr "type" "parallel_branch") + (set (attr "length") + (if_then_else (lt (abs (minus (match_dup 2) (plus (pc) (const_int 8)))) + (const_int 8184)) + (const_int 4) + (const_int 8)))]) + +(define_insn "" + [(set (pc) (label_ref (match_operand 2 "" "" ))) + (set (match_operand:QI 0 "register_operand" "=r") + (match_operand:QI 1 "ireg_or_int5_operand" "rL"))] + "reload_completed" + "* +{ + return output_parallel_movb (operands, get_attr_length (insn)); +}" + [(set_attr "type" "parallel_branch") + (set (attr "length") + (if_then_else (lt (abs (minus (match_dup 2) (plus (pc) (const_int 8)))) + (const_int 8184)) + (const_int 4) + (const_int 8)))]) + +(define_insn "" + [(set (match_operand 0 "register_operand" "=f") + (mult (match_operand 1 "register_operand" "f") + (match_operand 2 "register_operand" "f"))) + (set (match_operand 3 "register_operand" "+f") + (plus (match_operand 4 "register_operand" "f") + (match_operand 5 "register_operand" "f")))] + "TARGET_SNAKE && ! TARGET_SOFT_FLOAT + && reload_completed && fmpyaddoperands (operands)" + "* +{ + if (GET_MODE (operands[0]) == DFmode) + { + if (rtx_equal_p (operands[3], operands[5])) + return \"fmpyadd,dbl %1,%2,%0,%4,%3\"; + else + return \"fmpyadd,dbl %1,%2,%0,%5,%3\"; + } + else + { + if (rtx_equal_p (operands[3], operands[5])) + return \"fmpyadd,sgl %1,%2,%0,%4,%3\"; + else + return \"fmpyadd,sgl %1,%2,%0,%5,%3\"; + } +}" + [(set_attr "type" "fpalu") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand 3 "register_operand" "+f") + (plus (match_operand 4 "register_operand" "f") + (match_operand 5 "register_operand" "f"))) + (set (match_operand 0 "register_operand" "=f") + (mult (match_operand 1 "register_operand" "f") + (match_operand 2 "register_operand" "f")))] + "TARGET_SNAKE && ! TARGET_SOFT_FLOAT + && reload_completed && fmpyaddoperands (operands)" + "* +{ + if (GET_MODE (operands[0]) == DFmode) + { + if (rtx_equal_p (operands[3], operands[5])) + return \"fmpyadd,dbl %1,%2,%0,%4,%3\"; + else + return \"fmpyadd,dbl %1,%2,%0,%5,%3\"; + } + else + { + if (rtx_equal_p (operands[3], operands[5])) + return \"fmpyadd,sgl %1,%2,%0,%4,%3\"; + else + return \"fmpyadd,sgl %1,%2,%0,%5,%3\"; + } +}" + [(set_attr "type" "fpalu") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand 0 "register_operand" "=f") + (mult (match_operand 1 "register_operand" "f") + (match_operand 2 "register_operand" "f"))) + (set (match_operand 3 "register_operand" "+f") + (minus (match_operand 4 "register_operand" "f") + (match_operand 5 "register_operand" "f")))] + "TARGET_SNAKE && ! TARGET_SOFT_FLOAT + && reload_completed && fmpysuboperands (operands)" + "* +{ + if (GET_MODE (operands[0]) == DFmode) + return \"fmpysub,dbl %1,%2,%0,%5,%3\"; + else + return \"fmpysub,sgl %1,%2,%0,%5,%3\"; +}" + [(set_attr "type" "fpalu") + (set_attr "length" "4")]) + +(define_insn "" + [(set (match_operand 3 "register_operand" "+f") + (minus (match_operand 4 "register_operand" "f") + (match_operand 5 "register_operand" "f"))) + (set (match_operand 0 "register_operand" "=f") + (mult (match_operand 1 "register_operand" "f") + (match_operand 2 "register_operand" "f")))] + "TARGET_SNAKE && ! TARGET_SOFT_FLOAT + && reload_completed && fmpysuboperands (operands)" + "* +{ + if (GET_MODE (operands[0]) == DFmode) + return \"fmpysub,dbl %1,%2,%0,%5,%3\"; + else + return \"fmpysub,sgl %1,%2,%0,%5,%3\"; +}" + [(set_attr "type" "fpalu") + (set_attr "length" "4")]) + +;; Clean up turds left by reload. +(define_peephole + [(set (match_operand 0 "reg_or_nonsymb_mem_operand" "") + (match_operand 1 "register_operand" "fr")) + (set (match_operand 2 "register_operand" "fr") + (match_dup 0))] + "! TARGET_SOFT_FLOAT + && GET_CODE (operands[0]) == MEM + && ! MEM_VOLATILE_P (operands[0]) + && GET_MODE (operands[0]) == GET_MODE (operands[1]) + && GET_MODE (operands[0]) == GET_MODE (operands[2]) + && GET_MODE (operands[0]) == DFmode + && GET_CODE (operands[1]) == REG + && GET_CODE (operands[2]) == REG + && ! side_effects_p (XEXP (operands[0], 0)) + && REGNO_REG_CLASS (REGNO (operands[1])) + == REGNO_REG_CLASS (REGNO (operands[2]))" + "* +{ + rtx xoperands[2]; + + if (FP_REG_P (operands[1])) + output_asm_insn (output_fp_move_double (operands), operands); + else + output_asm_insn (output_move_double (operands), operands); + + if (rtx_equal_p (operands[1], operands[2])) + return \"\"; + + xoperands[0] = operands[2]; + xoperands[1] = operands[1]; + + if (FP_REG_P (xoperands[1])) + output_asm_insn (output_fp_move_double (xoperands), xoperands); + else + output_asm_insn (output_move_double (xoperands), xoperands); + + return \"\"; +}") + +(define_peephole + [(set (match_operand 0 "register_operand" "fr") + (match_operand 1 "reg_or_nonsymb_mem_operand" "")) + (set (match_operand 2 "register_operand" "fr") + (match_dup 1))] + "! TARGET_SOFT_FLOAT + && GET_CODE (operands[1]) == MEM + && ! MEM_VOLATILE_P (operands[1]) + && GET_MODE (operands[0]) == GET_MODE (operands[1]) + && GET_MODE (operands[0]) == GET_MODE (operands[2]) + && GET_MODE (operands[0]) == DFmode + && GET_CODE (operands[0]) == REG + && GET_CODE (operands[2]) == REG + && ! side_effects_p (XEXP (operands[1], 0)) + && REGNO_REG_CLASS (REGNO (operands[0])) + == REGNO_REG_CLASS (REGNO (operands[2]))" + "* +{ + rtx xoperands[2]; + + if (FP_REG_P (operands[0])) + output_asm_insn (output_fp_move_double (operands), operands); + else + output_asm_insn (output_move_double (operands), operands); + + xoperands[0] = operands[2]; + xoperands[1] = operands[0]; + + if (FP_REG_P (xoperands[1])) + output_asm_insn (output_fp_move_double (xoperands), xoperands); + else + output_asm_insn (output_move_double (xoperands), xoperands); + + return \"\"; +}") + +;; Flush the I and D cache line found at the address in operand 0. +;; This is used by the trampoline code for nested functions. +;; So long as the trampoline itself is less than 32 bytes this +;; is sufficient. + +(define_insn "dcacheflush" + [(unspec_volatile [(const_int 1)] 0) + (use (mem:SI (match_operand:SI 0 "register_operand" "r"))) + (use (mem:SI (match_operand:SI 1 "register_operand" "r")))] + "" + "fdc 0(0,%0)\;fdc 0(0,%1)\;sync" + [(set_attr "type" "multi") + (set_attr "length" "12")]) + +(define_insn "icacheflush" + [(unspec_volatile [(const_int 2)] 0) + (use (mem:SI (match_operand:SI 0 "register_operand" "r"))) + (use (mem:SI (match_operand:SI 1 "register_operand" "r"))) + (use (match_operand:SI 2 "register_operand" "r")) + (clobber (match_operand:SI 3 "register_operand" "=&r")) + (clobber (match_operand:SI 4 "register_operand" "=&r"))] + "" + "mfsp %%sr0,%4\;ldsid (0,%2),%3\;mtsp %3,%%sr0\;fic 0(%%sr0,%0)\;fic 0(%%sr0,%1)\;sync\;mtsp %4,%%sr0\;nop\;nop\;nop\;nop\;nop\;nop" + [(set_attr "type" "multi") + (set_attr "length" "52")]) + +;; An out-of-line prologue. +(define_insn "outline_prologue_call" + [(unspec_volatile [(const_int 0)] 0) + (clobber (reg:SI 31)) + (clobber (reg:SI 22)) + (clobber (reg:SI 21)) + (clobber (reg:SI 20)) + (clobber (reg:SI 19)) + (clobber (reg:SI 1))] + "" + "* +{ + extern int frame_pointer_needed; + + /* We need two different versions depending on whether or not we + need a frame pointer. Also note that we return to the instruction + immediately after the branch rather than two instructions after the + break as normally is the case. */ + if (frame_pointer_needed) + { + /* Must import the magic millicode routine(s). */ + output_asm_insn (\".IMPORT __outline_prologue_fp,MILLICODE\", NULL); + + if (TARGET_PORTABLE_RUNTIME) + { + output_asm_insn (\"ldil L'__outline_prologue_fp,%%r31\", NULL); + output_asm_insn (\"ble,n R'__outline_prologue_fp(%%sr0,%%r31)\", + NULL); + } + else + output_asm_insn (\"bl,n __outline_prologue_fp,%%r31\", NULL); + } + else + { + /* Must import the magic millicode routine(s). */ + output_asm_insn (\".IMPORT __outline_prologue,MILLICODE\", NULL); + + if (TARGET_PORTABLE_RUNTIME) + { + output_asm_insn (\"ldil L'__outline_prologue,%%r31\", NULL); + output_asm_insn (\"ble,n R'__outline_prologue(%%sr0,%%r31)\", NULL); + } + else + output_asm_insn (\"bl,n __outline_prologue,%%r31\", NULL); + } + return \"\"; +}" + [(set_attr "type" "multi") + (set_attr "length" "8")]) + +;; An out-of-line epilogue. +(define_insn "outline_epilogue_call" + [(unspec_volatile [(const_int 1)] 0) + (use (reg:SI 29)) + (use (reg:SI 28)) + (clobber (reg:SI 31)) + (clobber (reg:SI 22)) + (clobber (reg:SI 21)) + (clobber (reg:SI 20)) + (clobber (reg:SI 19)) + (clobber (reg:SI 2)) + (clobber (reg:SI 1))] + "" + "* +{ + extern int frame_pointer_needed; + + /* We need two different versions depending on whether or not we + need a frame pointer. Also note that we return to the instruction + immediately after the branch rather than two instructions after the + break as normally is the case. */ + if (frame_pointer_needed) + { + /* Must import the magic millicode routine. */ + output_asm_insn (\".IMPORT __outline_epilogue_fp,MILLICODE\", NULL); + + /* The out-of-line prologue will make sure we return to the right + instruction. */ + if (TARGET_PORTABLE_RUNTIME) + { + output_asm_insn (\"ldil L'__outline_epilogue_fp,%%r31\", NULL); + output_asm_insn (\"ble,n R'__outline_epilogue_fp(%%sr0,%%r31)\", + NULL); + } + else + output_asm_insn (\"bl,n __outline_epilogue_fp,%%r31\", NULL); + } + else + { + /* Must import the magic millicode routine. */ + output_asm_insn (\".IMPORT __outline_epilogue,MILLICODE\", NULL); + + /* The out-of-line prologue will make sure we return to the right + instruction. */ + if (TARGET_PORTABLE_RUNTIME) + { + output_asm_insn (\"ldil L'__outline_epilogue,%%r31\", NULL); + output_asm_insn (\"ble,n R'__outline_epilogue(%%sr0,%%r31)\", NULL); + } + else + output_asm_insn (\"bl,n __outline_epilogue,%%r31\", NULL); + } + return \"\"; +}" + [(set_attr "type" "multi") + (set_attr "length" "8")]) + +;; Given a function pointer, canonicalize it so it can be +;; reliably compared to another function pointer. */ +(define_expand "canonicalize_funcptr_for_compare" + [(set (reg:SI 26) (match_operand:SI 1 "register_operand" "")) + (parallel [(set (reg:SI 29) (unspec:SI [(reg:SI 26)] 0)) + (clobber (match_dup 2)) + (clobber (reg:SI 26)) + (clobber (reg:SI 22)) + (clobber (reg:SI 31))]) + (set (match_operand:SI 0 "register_operand" "") + (reg:SI 29))] + "! TARGET_PORTABLE_RUNTIME" + " +{ + operands[2] = gen_reg_rtx (SImode); + if (GET_CODE (operands[1]) != REG) + { + rtx tmp = gen_reg_rtx (Pmode); + emit_move_insn (tmp, operands[1]); + operands[1] = tmp; + } +}") + +(define_insn "" + [(set (reg:SI 29) (unspec:SI [(reg:SI 26)] 0)) + (clobber (match_operand:SI 0 "register_operand" "=a")) + (clobber (reg:SI 26)) + (clobber (reg:SI 22)) + (clobber (reg:SI 31))] + "" + "* +{ + /* Must import the magic millicode routine. */ + output_asm_insn (\".IMPORT $$sh_func_adrs,MILLICODE\", NULL); + + /* This is absolutely amazing. + + First, copy our input parameter into %r29 just in case we don't + need to call $$sh_func_adrs. */ + output_asm_insn (\"copy %%r26,%%r29\", NULL); + + /* Next, examine the low two bits in %r26, if they aren't 0x2, then + we use %r26 unchanged. */ + if (get_attr_length (insn) == 32) + output_asm_insn (\"extru %%r26,31,2,%%r31\;comib,<>,n 2,%%r31,.+24\", NULL); + else if (get_attr_length (insn) == 40) + output_asm_insn (\"extru %%r26,31,2,%%r31\;comib,<>,n 2,%%r31,.+32\", NULL); + else if (get_attr_length (insn) == 44) + output_asm_insn (\"extru %%r26,31,2,%%r31\;comib,<>,n 2,%%r31,.+36\", NULL); + else + output_asm_insn (\"extru %%r26,31,2,%%r31\;comib,<>,n 2,%%r31,.+20\", NULL); + + /* Next, compare %r26 with 4096, if %r26 is less than or equal to + 4096, then we use %r26 unchanged. */ + if (get_attr_length (insn) == 32) + output_asm_insn (\"ldi 4096,%%r31\;comb,<<,n %%r26,%%r31,.+16\", NULL); + else if (get_attr_length (insn) == 40) + output_asm_insn (\"ldi 4096,%%r31\;comb,<<,n %%r26,%%r31,.+24\", NULL); + else if (get_attr_length (insn) == 44) + output_asm_insn (\"ldi 4096,%%r31\;comb,<<,n %%r26,%%r31,.+28\", NULL); + else + output_asm_insn (\"ldi 4096,%%r31\;comb,<<,n %%r26,%%r31,.+12\", NULL); + + /* Else call $$sh_func_adrs to extract the function's real add24. */ + return output_millicode_call (insn, + gen_rtx_SYMBOL_REF (SImode, \"$$sh_func_adrs\")); +}" + [(set_attr "type" "multi") + (set (attr "length") + (cond [ +;; Target (or stub) within reach + (and (lt (plus (symbol_ref "total_code_bytes") (pc)) + (const_int 240000)) + (eq (symbol_ref "TARGET_PORTABLE_RUNTIME") + (const_int 0))) + (const_int 28) + +;; NO_SPACE_REGS + (ne (symbol_ref "TARGET_NO_SPACE_REGS || TARGET_FAST_INDIRECT_CALLS") + (const_int 0)) + (const_int 32) + +;; Out of reach, but not PIC or PORTABLE_RUNTIME +;; same as NO_SPACE_REGS code + (and (eq (symbol_ref "TARGET_PORTABLE_RUNTIME") + (const_int 0)) + (eq (symbol_ref "flag_pic") + (const_int 0))) + (const_int 32) + +;; PORTABLE_RUNTIME + (ne (symbol_ref "TARGET_PORTABLE_RUNTIME") + (const_int 0)) + (const_int 40)] + +;; Out of range and PIC + (const_int 44)))]) + +;; On the PA, the PIC register is call clobbered, so it must +;; be saved & restored around calls by the caller. If the call +;; doesn't return normally (nonlocal goto, or an exception is +;; thrown), then the code at the exception handler label must +;; restore the PIC register. +(define_expand "exception_receiver" + [(const_int 4)] + "!TARGET_PORTABLE_RUNTIME && flag_pic" + " +{ + /* Load the PIC register from the stack slot (in our caller's + frame). */ + emit_move_insn (pic_offset_table_rtx, + gen_rtx_MEM (SImode, plus_constant (stack_pointer_rtx, -32))); + emit_insn (gen_rtx_USE (VOIDmode, pic_offset_table_rtx)); + emit_insn (gen_blockage ()); + DONE; +}") + + diff --git a/gcc/config/pa/pa1.h b/gcc/config/pa/pa1.h new file mode 100755 index 0000000..418de75 --- /dev/null +++ b/gcc/config/pa/pa1.h @@ -0,0 +1,28 @@ +/* Definitions of target machine for GNU compiler, for HP PA-RISC 1.1 + Copyright (C) 1991 Free Software Foundation, Inc. + Contributed by Tim Moore (moore@defmacro.cs.utah.edu) + +This file is part of GNU CC. + +GNU CC 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. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#define TARGET_DEFAULT 0x89 /* TARGET_SNAKE + TARGET_GAS + + TARGET_JUMP_IN_DELAY */ + +/* This is the same as pa.h, except that we generate snake code by + default. */ + +#include "pa/pa.h" diff --git a/gcc/config/pa/rtems.h b/gcc/config/pa/rtems.h new file mode 100755 index 0000000..a0d5b7a --- /dev/null +++ b/gcc/config/pa/rtems.h @@ -0,0 +1,31 @@ +/* Definitions of target machine for GNU compiler, for PRO. + Copyright (C) 1997 Free Software Foundation, Inc. + Contributed by Joel Sherrill (joel@OARcorp.com). + +This file is part of GNU CC. + +GNU CC 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. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Specify predefined symbols in preprocessor. */ + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dhppa -DPWB -Acpu(hppa) -Amachine(hppa) \ + -Drtems -D__rtems__ -Asystem(rtems)" + +/* Generate calls to memcpy, memcmp and memset. */ +#ifndef TARGET_MEM_FUNCTIONS +#define TARGET_MEM_FUNCTIONS +#endif diff --git a/gcc/config/pa/t-dce-thr b/gcc/config/pa/t-dce-thr new file mode 100755 index 0000000..8d86a41 --- /dev/null +++ b/gcc/config/pa/t-dce-thr @@ -0,0 +1,5 @@ +MULTILIB_OPTIONS = threads +MULTILIB_DIRNAMES = threads + +LIBGCC = stmp-multilib +INSTALL_LIBGCC = install-multilib diff --git a/gcc/config/pa/t-pa b/gcc/config/pa/t-pa new file mode 100755 index 0000000..a359918 --- /dev/null +++ b/gcc/config/pa/t-pa @@ -0,0 +1,18 @@ +LIBGCC1=libgcc1.null +CROSS_LIBGCC1=libgcc1.null +ADA_CFLAGS=-mdisable-indexing +LIB2FUNCS_EXTRA=lib2funcs.asm ee.asm ee_fp.asm + +lib2funcs.asm: $(srcdir)/config/pa/lib2funcs.asm + rm -f lib2funcs.asm + cp $(srcdir)/config/pa/lib2funcs.asm . + +ee.asm: $(srcdir)/config/pa/ee.asm + rm -f ee.asm + cp $(srcdir)/config/pa/ee.asm . + +ee_fp.asm: $(srcdir)/config/pa/ee_fp.asm + rm -f ee_fp.asm + cp $(srcdir)/config/pa/ee_fp.asm . + +TARGET_LIBGCC2_CFLAGS = -fPIC diff --git a/gcc/config/pa/t-pro b/gcc/config/pa/t-pro new file mode 100755 index 0000000..f40b2e4 --- /dev/null +++ b/gcc/config/pa/t-pro @@ -0,0 +1,38 @@ +LIBGCC1=libgcc1.null +CROSS_LIBGCC1 = libgcc1.null +LIB1ASMSRC = +LIB1ASMFUNCS = + +LIBGCC1_TEST = + +ADA_CFLAGS=-mdisable-indexing + +LIB2FUNCS_EXTRA=fp-bit.c dp-bit.c lib2funcs.asm ee.asm ee_fp.asm + +dp-bit.c: $(srcdir)/config/fp-bit.c + cat $(srcdir)/config/fp-bit.c > dp-bit.c + +fp-bit.c: $(srcdir)/config/fp-bit.c + echo '#define FLOAT' > fp-bit.c + cat $(srcdir)/config/fp-bit.c >> fp-bit.c + +lib2funcs.asm: $(srcdir)/config/pa/lib2funcs.asm + rm -f lib2funcs.asm + cp $(srcdir)/config/pa/lib2funcs.asm . + +ee.asm: $(srcdir)/config/pa/ee.asm + rm -f ee.asm + cp $(srcdir)/config/pa/ee.asm . + +ee_fp.asm: $(srcdir)/config/pa/ee_fp.asm + rm -f ee_fp.asm + cp $(srcdir)/config/pa/ee_fp.asm . + +# Build the libraries for both speed and space optimizations + +MULTILIB_OPTIONS=mspace +MULTILIB_DIRNAMES=space +MULTILIB_MATCHES= + +LIBGCC = stmp-multilib +INSTALL_LIBGCC = install-multilib diff --git a/gcc/config/pa/x-pa b/gcc/config/pa/x-pa new file mode 100755 index 0000000..4c25047 --- /dev/null +++ b/gcc/config/pa/x-pa @@ -0,0 +1,3 @@ +# BSD on the PA already has ANSI include files which are c++ compatible. +USER_H = $(EXTRA_HEADERS) $(LANG_EXTRA_HEADERS) +STMP_FIXPROTO= diff --git a/gcc/config/pa/x-pa-hpux b/gcc/config/pa/x-pa-hpux new file mode 100755 index 0000000..1b8bb9f --- /dev/null +++ b/gcc/config/pa/x-pa-hpux @@ -0,0 +1,4 @@ +ALLOCA=alloca.o + +# So putenv and other functions get seen by fixproto. +FIXPROTO_DEFINES = -D_HPUX_SOURCE diff --git a/gcc/config/pa/xm-pa.h b/gcc/config/pa/xm-pa.h new file mode 100755 index 0000000..0249055 --- /dev/null +++ b/gcc/config/pa/xm-pa.h @@ -0,0 +1,62 @@ +/* Configuration for GNU C-compiler for PA-RISC. + Copyright (C) 1988, 1995 Free Software Foundation, Inc. + Contributed by Michael Tiemann (tiemann@cygnus.com). + +This file is part of GNU CC. + +GNU CC 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. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +extern int errno; + +/* #defines that need visibility everywhere. */ +#define FALSE 0 +#define TRUE 1 + +/* This describes the machine the compiler is hosted on. */ +#define HOST_BITS_PER_CHAR 8 +#define HOST_BITS_PER_SHORT 16 +#define HOST_BITS_PER_INT 32 +#define HOST_BITS_PER_LONG 32 +#define HOST_BITS_PER_LONGLONG 64 + +/* Doubles are stored in memory with the high order word first. This + matters when cross-compiling. */ +#define HOST_WORDS_BIG_ENDIAN 1 + +/* Place any machine-dependent include files here, in case we + are bootstrapping. */ + +/* target machine dependencies. + tm.h is a symbolic link to the actual target specific file. */ +#include "tm.h" + +/* Arguments to use with `exit'. */ +#define SUCCESS_EXIT_CODE 0 +#define FATAL_EXIT_CODE 33 + +/* Don't try to use sys_siglist. */ +#define NO_SYS_SIGLIST + +/* 4.3BSD, OSF1 and Lites on the PA are all derived from NET2 or + later code from Berkeley. */ +#define __BSD_NET2__ + +/* HP's compiler has problems with enum bitfields. */ +#define ONLY_INT_FIELDS + +/* Always claim to use C alloca; this prevents losing if building with + gcc -fno-builtin ... */ +#define USE_C_ALLOCA diff --git a/gcc/config/pa/xm-pahpux.h b/gcc/config/pa/xm-pahpux.h new file mode 100755 index 0000000..09c949b --- /dev/null +++ b/gcc/config/pa/xm-pahpux.h @@ -0,0 +1,61 @@ +/* Configuration for GNU C-compiler for PA-RISC. + Copyright (C) 1988, 1995, 1997 Free Software Foundation, Inc. + Contributed by Michael Tiemann (tiemann@cygnus.com). + +This file is part of GNU CC. + +GNU CC 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. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + + +/* HP-UX is a flavor of System V */ +#define USG + +/* Use System V memory functions. */ +/* #defines that need visibility everywhere. */ +#define FALSE 0 +#define TRUE 1 + +/* This describes the machine the compiler is hosted on. */ +#define HOST_BITS_PER_CHAR 8 +#define HOST_BITS_PER_SHORT 16 +#define HOST_BITS_PER_INT 32 +#define HOST_BITS_PER_LONG 32 +#define HOST_BITS_PER_LONGLONG 64 + +/* Doubles are stored in memory with the high order word first. This + matters when cross-compiling. */ +#define HOST_WORDS_BIG_ENDIAN 1 + +/* Place any machine-dependent include files here, in case we + are bootstrapping. */ + +/* target machine dependencies. + tm.h is a symbolic link to the actual target specific file. */ +#include "tm.h" + +/* Arguments to use with `exit'. */ +#define SUCCESS_EXIT_CODE 0 +#define FATAL_EXIT_CODE 33 + +/* Don't try to use sys_siglist. */ +#define NO_SYS_SIGLIST + +/* HP's compiler has problems with enum bitfields. */ +#define ONLY_INT_FIELDS + +/* Always claim to use C alloca; this prevents losing if building with + gcc -fno-builtin ... " */ +#define USE_C_ALLOCA diff --git a/gcc/config/pa/xm-papro.h b/gcc/config/pa/xm-papro.h new file mode 100755 index 0000000..d36e201 --- /dev/null +++ b/gcc/config/pa/xm-papro.h @@ -0,0 +1,58 @@ +/* Configuration for GNU C-compiler for PA-RISC. + Copyright (C) 1994, 1995 Free Software Foundation, Inc. + Contributed by Michael Tiemann (tiemann@cygnus.com). + +This file is part of GNU CC. + +GNU CC 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. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +extern int errno; + +/* #defines that need visibility everywhere. */ +#define FALSE 0 +#define TRUE 1 + +/* This describes the machine the compiler is hosted on. */ +#define HOST_BITS_PER_CHAR 8 +#define HOST_BITS_PER_SHORT 16 +#define HOST_BITS_PER_INT 32 +#define HOST_BITS_PER_LONG 32 +#define HOST_BITS_PER_LONGLONG 64 + +/* Doubles are stored in memory with the high order word first. This + matters when cross-compiling. */ +#define HOST_WORDS_BIG_ENDIAN 1 + +/* Place any machine-dependent include files here, in case we + are bootstrapping. */ + +/* target machine dependencies. + tm.h is a symbolic link to the actual target specific file. */ +#include "tm.h" + +/* Arguments to use with `exit'. */ +#define SUCCESS_EXIT_CODE 0 +#define FATAL_EXIT_CODE 33 + +/* Don't try to use sys_siglist. */ +#define NO_SYS_SIGLIST + +/* HP's compiler has problems with enum bitfields. */ +#define ONLY_INT_FIELDS + +/* Always claim to use C alloca; this prevents losing if building with + gcc -fno-builtin ... */ +#define USE_C_ALLOCA |