diff options
author | YamaArashi <shadow962@live.com> | 2016-01-06 01:47:28 -0800 |
---|---|---|
committer | YamaArashi <shadow962@live.com> | 2016-01-06 01:47:28 -0800 |
commit | be8b04496302184c6e8f04d6179f9c3afc50aeb6 (patch) | |
tree | 726e2468c0c07add773c0dbd86ab6386844259ae /gcc/config/sparc |
initial commit
Diffstat (limited to 'gcc/config/sparc')
64 files changed, 24676 insertions, 0 deletions
diff --git a/gcc/config/sparc/aout.h b/gcc/config/sparc/aout.h new file mode 100755 index 0000000..478d710 --- /dev/null +++ b/gcc/config/sparc/aout.h @@ -0,0 +1,26 @@ +/* Definitions of target machine for GNU compiler, for SPARC using a.out. + Copyright (C) 1994, 1996 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. */ + +#include "sparc/sparc.h" /* SPARC definitions */ +#include "aoutos.h" /* A.out definitions */ + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dsparc -Acpu(sparc) -Amachine(sparc)" diff --git a/gcc/config/sparc/bsd.h b/gcc/config/sparc/bsd.h new file mode 100755 index 0000000..761abe2 --- /dev/null +++ b/gcc/config/sparc/bsd.h @@ -0,0 +1,7 @@ +#include "sparc/sparc.h" + +#undef LIB_SPEC +#define LIB_SPEC "%{!p:%{!pg:-lc}}%{p:-lc_p}%{pg:-lc_p}" + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC "%{pg:gcrt0.o%s}%{!pg:%{p:gcrt0.o%s}%{!p:crt0.o%s}}" diff --git a/gcc/config/sparc/elf.h b/gcc/config/sparc/elf.h new file mode 100755 index 0000000..635238f --- /dev/null +++ b/gcc/config/sparc/elf.h @@ -0,0 +1,58 @@ +/* Definitions of target machine for GNU compiler, + for SPARC running in an embedded environment using the ELF file format. + Copyright (C) 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. */ + +#include "sol2.h" + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dsparc -D__elf__ -Acpu(sparc) -Amachine(sparc)" + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC "crt0.o%s crti.o%s crtbegin.o%s" + +#undef ENDFILE_SPEC +#define ENDFILE_SPEC "crtend.o%s crtn.o%s" + +/* Use the default. */ +#undef LINK_SPEC + +/* Don't set the target flags, this is done by the linker script */ +#undef LIB_SPEC +#define LIB_SPEC "" + +/* FIXME: until fixed */ +#undef LONG_DOUBLE_TYPE_SIZE +#define LONG_DOUBLE_TYPE_SIZE 64 + +/* This solaris2 define does not apply. */ +#undef STDC_0_IN_SYSTEM_HEADERS + +/* We don't want to use the Solaris2 specific long long int conversion + routines. */ +#undef INIT_SUBTARGET_OPTABS +#define INIT_SUBTARGET_OPTABS + +/* ??? We haven't added Solaris2 equivalent 64 bit library routines to + lb1sp*.asm, so we need to avoid using them. */ +#undef MULDI3_LIBCALL +#undef DIVDI3_LIBCALL +#undef UDIVDI3_LIBCALL +#undef MODDI3_LIBCALL +#undef UMODDI3_LIBCALL diff --git a/gcc/config/sparc/gmon-sol2.c b/gcc/config/sparc/gmon-sol2.c new file mode 100755 index 0000000..2a5b898 --- /dev/null +++ b/gcc/config/sparc/gmon-sol2.c @@ -0,0 +1,429 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* Mangled into a form that works on Sparc Solaris 2 by Mark Eichin + * for Cygnus Support, July 1992. + */ + +#ifndef lint +static char sccsid[] = "@(#)gmon.c 5.3 (Berkeley) 5/22/91"; +#endif /* not lint */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <unistd.h> +#include <fcntl.h> + +#if 0 +#include "sparc/gmon.h" +#else +struct phdr { + char *lpc; + char *hpc; + int ncnt; +}; +#define HISTFRACTION 2 +#define HISTCOUNTER unsigned short +#define HASHFRACTION 1 +#define ARCDENSITY 2 +#define MINARCS 50 +struct tostruct { + char *selfpc; + long count; + unsigned short link; +}; +struct rawarc { + unsigned long raw_frompc; + unsigned long raw_selfpc; + long raw_count; +}; +#define ROUNDDOWN(x,y) (((x)/(y))*(y)) +#define ROUNDUP(x,y) ((((x)+(y)-1)/(y))*(y)) + +#endif + +/* extern mcount() asm ("mcount"); */ +/*extern*/ char *minbrk /* asm ("minbrk") */; + + /* + * froms is actually a bunch of unsigned shorts indexing tos + */ +static int profiling = 3; +static unsigned short *froms; +static struct tostruct *tos = 0; +static long tolimit = 0; +static char *s_lowpc = 0; +static char *s_highpc = 0; +static unsigned long s_textsize = 0; + +static int ssiz; +static char *sbuf; +static int s_scale; + /* see profil(2) where this is describe (incorrectly) */ +#define SCALE_1_TO_1 0x10000L + +#define MSG "No space for profiling buffer(s)\n" + +static void moncontrol(); + +void monstartup(lowpc, highpc) + char *lowpc; + char *highpc; +{ + int monsize; + char *buffer; + register int o; + + /* + * round lowpc and highpc to multiples of the density we're using + * so the rest of the scaling (here and in gprof) stays in ints. + */ + lowpc = (char *) + ROUNDDOWN((unsigned)lowpc, HISTFRACTION*sizeof(HISTCOUNTER)); + s_lowpc = lowpc; + highpc = (char *) + ROUNDUP((unsigned)highpc, HISTFRACTION*sizeof(HISTCOUNTER)); + s_highpc = highpc; + s_textsize = highpc - lowpc; + monsize = (s_textsize / HISTFRACTION) + sizeof(struct phdr); + buffer = sbrk( monsize ); + if ( buffer == (char *) -1 ) { + write( 2 , MSG , sizeof(MSG) ); + return; + } + froms = (unsigned short *) sbrk( s_textsize / HASHFRACTION ); + if ( froms == (unsigned short *) -1 ) { + write( 2 , MSG , sizeof(MSG) ); + froms = 0; + return; + } + tolimit = s_textsize * ARCDENSITY / 100; + if ( tolimit < MINARCS ) { + tolimit = MINARCS; + } else if ( tolimit > 65534 ) { + tolimit = 65534; + } + tos = (struct tostruct *) sbrk( tolimit * sizeof( struct tostruct ) ); + if ( tos == (struct tostruct *) -1 ) { + write( 2 , MSG , sizeof(MSG) ); + froms = 0; + tos = 0; + return; + } + minbrk = sbrk(0); + tos[0].link = 0; + sbuf = buffer; + ssiz = monsize; + ( (struct phdr *) buffer ) -> lpc = lowpc; + ( (struct phdr *) buffer ) -> hpc = highpc; + ( (struct phdr *) buffer ) -> ncnt = ssiz; + monsize -= sizeof(struct phdr); + if ( monsize <= 0 ) + return; + o = highpc - lowpc; + if( monsize < o ) +#ifndef hp300 + s_scale = ( (float) monsize / o ) * SCALE_1_TO_1; +#else /* avoid floating point */ + { + int quot = o / monsize; + + if (quot >= 0x10000) + s_scale = 1; + else if (quot >= 0x100) + s_scale = 0x10000 / quot; + else if (o >= 0x800000) + s_scale = 0x1000000 / (o / (monsize >> 8)); + else + s_scale = 0x1000000 / ((o << 8) / monsize); + } +#endif + else + s_scale = SCALE_1_TO_1; + moncontrol(1); +} + +void +_mcleanup() +{ + int fd; + int fromindex; + int endfrom; + char *frompc; + int toindex; + struct rawarc rawarc; + char *profdir; + char *proffile; + char *progname; + char buf[PATH_MAX]; + extern char **___Argv; + + moncontrol(0); + + if ((profdir = getenv("PROFDIR")) != NULL) { + /* If PROFDIR contains a null value, no profiling output is produced */ + if (*profdir == '\0') { + return; + } + + progname=strrchr(___Argv[0], '/'); + if (progname == NULL) + progname=___Argv[0]; + else + progname++; + + sprintf(buf, "%s/%ld.%s", profdir, getpid(), progname); + proffile = buf; + } else { + proffile = "gmon.out"; + } + + fd = creat( proffile, 0666 ); + if ( fd < 0 ) { + perror( proffile ); + return; + } +# ifdef DEBUG + fprintf( stderr , "[mcleanup] sbuf 0x%x ssiz %d\n" , sbuf , ssiz ); +# endif DEBUG + write( fd , sbuf , ssiz ); + endfrom = s_textsize / (HASHFRACTION * sizeof(*froms)); + for ( fromindex = 0 ; fromindex < endfrom ; fromindex++ ) { + if ( froms[fromindex] == 0 ) { + continue; + } + frompc = s_lowpc + (fromindex * HASHFRACTION * sizeof(*froms)); + for (toindex=froms[fromindex]; toindex!=0; toindex=tos[toindex].link) { +# ifdef DEBUG + fprintf( stderr , + "[mcleanup] frompc 0x%x selfpc 0x%x count %d\n" , + frompc , tos[toindex].selfpc , tos[toindex].count ); +# endif DEBUG + rawarc.raw_frompc = (unsigned long) frompc; + rawarc.raw_selfpc = (unsigned long) tos[toindex].selfpc; + rawarc.raw_count = tos[toindex].count; + write( fd , &rawarc , sizeof rawarc ); + } + } + close( fd ); +} + +/* + * The Sparc stack frame is only held together by the frame pointers + * in the register windows. According to the SVR4 SPARC ABI + * Supplement, Low Level System Information/Operating System + * Interface/Software Trap Types, a type 3 trap will flush all of the + * register windows to the stack, which will make it possible to walk + * the frames and find the return addresses. + * However, it seems awfully expensive to incur a trap (system + * call) for every function call. It turns out that "call" simply puts + * the return address in %o7 expecting the "save" in the procedure to + * shift it into %i7; this means that before the "save" occurs, %o7 + * contains the address of the call to mcount, and %i7 still contains + * the caller above that. The asm mcount here simply saves those + * registers in argument registers and branches to internal_mcount, + * simulating a call with arguments. + * Kludges: + * 1) the branch to internal_mcount is hard coded; it should be + * possible to tell asm to use the assembler-name of a symbol. + * 2) in theory, the function calling mcount could have saved %i7 + * somewhere and reused the register; in practice, I *think* this will + * break longjmp (and maybe the debugger) but I'm not certain. (I take + * some comfort in the knowledge that it will break the native mcount + * as well.) + * 3) if builtin_return_address worked, this could be portable. + * However, it would really have to be optimized for arguments of 0 + * and 1 and do something like what we have here in order to avoid the + * trap per function call performance hit. + * 4) the atexit and monsetup calls prevent this from simply + * being a leaf routine that doesn't do a "save" (and would thus have + * access to %o7 and %i7 directly) but the call to write() at the end + * would have also prevented this. + * + * -- [eichin:19920702.1107EST] + */ + +/* i7 == last ret, -> frompcindex */ +/* o7 == current ret, -> selfpc */ +/* Solaris 2 libraries use _mcount. */ +asm(".global _mcount; _mcount: mov %i7,%o1; mov %o7,%o0;b,a internal_mcount"); +/* This is for compatibility with old versions of gcc which used mcount. */ +asm(".global mcount; mcount: mov %i7,%o1; mov %o7,%o0;b,a internal_mcount"); + +static void internal_mcount(selfpc, frompcindex) + register char *selfpc; + register unsigned short *frompcindex; +{ + register struct tostruct *top; + register struct tostruct *prevtop; + register long toindex; + static char already_setup; + + /* + * find the return address for mcount, + * and the return address for mcount's caller. + */ + + if(!already_setup) { + extern etext(); + already_setup = 1; + monstartup(0, etext); +#ifdef USE_ONEXIT + on_exit(_mcleanup, 0); +#else + atexit(_mcleanup); +#endif + } + /* + * check that we are profiling + * and that we aren't recursively invoked. + */ + if (profiling) { + goto out; + } + profiling++; + /* + * check that frompcindex is a reasonable pc value. + * for example: signal catchers get called from the stack, + * not from text space. too bad. + */ + frompcindex = (unsigned short *)((long)frompcindex - (long)s_lowpc); + if ((unsigned long)frompcindex > s_textsize) { + goto done; + } + frompcindex = + &froms[((long)frompcindex) / (HASHFRACTION * sizeof(*froms))]; + toindex = *frompcindex; + if (toindex == 0) { + /* + * first time traversing this arc + */ + toindex = ++tos[0].link; + if (toindex >= tolimit) { + goto overflow; + } + *frompcindex = toindex; + top = &tos[toindex]; + top->selfpc = selfpc; + top->count = 1; + top->link = 0; + goto done; + } + top = &tos[toindex]; + if (top->selfpc == selfpc) { + /* + * arc at front of chain; usual case. + */ + top->count++; + goto done; + } + /* + * have to go looking down chain for it. + * top points to what we are looking at, + * prevtop points to previous top. + * we know it is not at the head of the chain. + */ + for (; /* goto done */; ) { + if (top->link == 0) { + /* + * top is end of the chain and none of the chain + * had top->selfpc == selfpc. + * so we allocate a new tostruct + * and link it to the head of the chain. + */ + toindex = ++tos[0].link; + if (toindex >= tolimit) { + goto overflow; + } + top = &tos[toindex]; + top->selfpc = selfpc; + top->count = 1; + top->link = *frompcindex; + *frompcindex = toindex; + goto done; + } + /* + * otherwise, check the next arc on the chain. + */ + prevtop = top; + top = &tos[top->link]; + if (top->selfpc == selfpc) { + /* + * there it is. + * increment its count + * move it to the head of the chain. + */ + top->count++; + toindex = prevtop->link; + prevtop->link = top->link; + top->link = *frompcindex; + *frompcindex = toindex; + goto done; + } + + } +done: + profiling--; + /* and fall through */ +out: + return; /* normal return restores saved registers */ + +overflow: + profiling++; /* halt further profiling */ +# define TOLIMIT "mcount: tos overflow\n" + write(2, TOLIMIT, sizeof(TOLIMIT)); + goto out; +} + +/* + * Control profiling + * profiling is what mcount checks to see if + * all the data structures are ready. + */ +static void moncontrol(mode) + int mode; +{ + if (mode) { + /* start */ + profil((unsigned short *)(sbuf + sizeof(struct phdr)), + ssiz - sizeof(struct phdr), + (int)s_lowpc, s_scale); + profiling = 0; + } else { + /* stop */ + profil((unsigned short *)0, 0, 0, 0); + profiling = 3; + } +} diff --git a/gcc/config/sparc/hal.h b/gcc/config/sparc/hal.h new file mode 100755 index 0000000..0222b81 --- /dev/null +++ b/gcc/config/sparc/hal.h @@ -0,0 +1,33 @@ +/* Definitions of target machine for GNU compiler, for HAL + SPARC running Solaris 2 HALOS + Copyright 1998 Free Software Foundation, Inc. + Contributed by Carol LePage (carolo@hal.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. */ + +/* Need different command line for assembler */ + +#undef ASM_SPEC +#define ASM_SPEC \ + "%{V} %{v:%{!V:-V}} %{Qy:} %{!Qn:-Qy} %{n} %{T} %{Ym,*} %{Wa,*:%*} -e1 \ + %{fpic:-K PIC} %{fPIC:-K PIC}" + +/* Need DWARF for debuggers. */ + +#undef PREFERRED_DEBUGGING_TYPE +#define PREFERRED_DEBUGGING_TYPE DWARF_DEBUG diff --git a/gcc/config/sparc/lb1spc.asm b/gcc/config/sparc/lb1spc.asm new file mode 100755 index 0000000..831f33a --- /dev/null +++ b/gcc/config/sparc/lb1spc.asm @@ -0,0 +1,784 @@ +/* This is an assembly language implementation of libgcc1.c for the sparc + processor. + + These routines are derived from the Sparc Architecture Manual, version 8, + slightly edited to match the desired calling convention, and also to + optimize them for our purposes. */ + +#ifdef L_mulsi3 +.text + .align 4 + .global .umul + .proc 4 +.umul: + or %o0, %o1, %o4 ! logical or of multiplier and multiplicand + mov %o0, %y ! multiplier to Y register + andncc %o4, 0xfff, %o5 ! mask out lower 12 bits + be mul_shortway ! can do it the short way + andcc %g0, %g0, %o4 ! zero the partial product and clear NV cc + ! + ! long multiply + ! + mulscc %o4, %o1, %o4 ! first iteration of 33 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 ! 32nd iteration + mulscc %o4, %g0, %o4 ! last iteration only shifts + ! the upper 32 bits of product are wrong, but we do not care + retl + rd %y, %o0 + ! + ! short multiply + ! +mul_shortway: + mulscc %o4, %o1, %o4 ! first iteration of 13 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 + mulscc %o4, %o1, %o4 ! 12th iteration + mulscc %o4, %g0, %o4 ! last iteration only shifts + rd %y, %o5 + sll %o4, 12, %o4 ! left shift partial product by 12 bits + srl %o5, 20, %o5 ! right shift partial product by 20 bits + retl + or %o5, %o4, %o0 ! merge for true product +#endif + +#ifdef L_divsi3 +/* + * Division and remainder, from Appendix E of the Sparc Version 8 + * Architecture Manual, with fixes from Gordon Irlam. + */ + +/* + * Input: dividend and divisor in %o0 and %o1 respectively. + * + * m4 parameters: + * .div name of function to generate + * div div=div => %o0 / %o1; div=rem => %o0 % %o1 + * true true=true => signed; true=false => unsigned + * + * Algorithm parameters: + * N how many bits per iteration we try to get (4) + * WORDSIZE total number of bits (32) + * + * Derived constants: + * TOPBITS number of bits in the top decade of a number + * + * Important variables: + * Q the partial quotient under development (initially 0) + * R the remainder so far, initially the dividend + * ITER number of main division loop iterations required; + * equal to ceil(log2(quotient) / N). Note that this + * is the log base (2^N) of the quotient. + * V the current comparand, initially divisor*2^(ITER*N-1) + * + * Cost: + * Current estimate for non-large dividend is + * ceil(log2(quotient) / N) * (10 + 7N/2) + C + * A large dividend is one greater than 2^(31-TOPBITS) and takes a + * different path, as the upper bits of the quotient must be developed + * one bit at a time. + */ + .global .udiv + .align 4 + .proc 4 + .text +.udiv: + b ready_to_divide + mov 0, %g3 ! result is always positive + + .global .div + .align 4 + .proc 4 + .text +.div: + ! compute sign of result; if neither is negative, no problem + orcc %o1, %o0, %g0 ! either negative? + bge ready_to_divide ! no, go do the divide + xor %o1, %o0, %g3 ! compute sign in any case + tst %o1 + bge 1f + tst %o0 + ! %o1 is definitely negative; %o0 might also be negative + bge ready_to_divide ! if %o0 not negative... + sub %g0, %o1, %o1 ! in any case, make %o1 nonneg +1: ! %o0 is negative, %o1 is nonnegative + sub %g0, %o0, %o0 ! make %o0 nonnegative + + +ready_to_divide: + + ! Ready to divide. Compute size of quotient; scale comparand. + orcc %o1, %g0, %o5 + bne 1f + mov %o0, %o3 + + ! Divide by zero trap. If it returns, return 0 (about as + ! wrong as possible, but that is what SunOS does...). + ta 0x2 ! ST_DIV0 + retl + clr %o0 + +1: + cmp %o3, %o5 ! if %o1 exceeds %o0, done + blu got_result ! (and algorithm fails otherwise) + clr %o2 + sethi %hi(1 << (32 - 4 - 1)), %g1 + cmp %o3, %g1 + blu not_really_big + clr %o4 + + ! Here the dividend is >= 2**(31-N) or so. We must be careful here, + ! as our usual N-at-a-shot divide step will cause overflow and havoc. + ! The number of bits in the result here is N*ITER+SC, where SC <= N. + ! Compute ITER in an unorthodox manner: know we need to shift V into + ! the top decade: so do not even bother to compare to R. + 1: + cmp %o5, %g1 + bgeu 3f + mov 1, %g2 + sll %o5, 4, %o5 + b 1b + add %o4, 1, %o4 + + ! Now compute %g2. + 2: addcc %o5, %o5, %o5 + bcc not_too_big + add %g2, 1, %g2 + + ! We get here if the %o1 overflowed while shifting. + ! This means that %o3 has the high-order bit set. + ! Restore %o5 and subtract from %o3. + sll %g1, 4, %g1 ! high order bit + srl %o5, 1, %o5 ! rest of %o5 + add %o5, %g1, %o5 + b do_single_div + sub %g2, 1, %g2 + + not_too_big: + 3: cmp %o5, %o3 + blu 2b + nop + be do_single_div + nop + /* NB: these are commented out in the V8-Sparc manual as well */ + /* (I do not understand this) */ + ! %o5 > %o3: went too far: back up 1 step + ! srl %o5, 1, %o5 + ! dec %g2 + ! do single-bit divide steps + ! + ! We have to be careful here. We know that %o3 >= %o5, so we can do the + ! first divide step without thinking. BUT, the others are conditional, + ! and are only done if %o3 >= 0. Because both %o3 and %o5 may have the high- + ! order bit set in the first step, just falling into the regular + ! division loop will mess up the first time around. + ! So we unroll slightly... + do_single_div: + subcc %g2, 1, %g2 + bl end_regular_divide + nop + sub %o3, %o5, %o3 + mov 1, %o2 + b end_single_divloop + nop + single_divloop: + sll %o2, 1, %o2 + bl 1f + srl %o5, 1, %o5 + ! %o3 >= 0 + sub %o3, %o5, %o3 + b 2f + add %o2, 1, %o2 + 1: ! %o3 < 0 + add %o3, %o5, %o3 + sub %o2, 1, %o2 + 2: + end_single_divloop: + subcc %g2, 1, %g2 + bge single_divloop + tst %o3 + b,a end_regular_divide + +not_really_big: +1: + sll %o5, 4, %o5 + cmp %o5, %o3 + bleu 1b + addcc %o4, 1, %o4 + be got_result + sub %o4, 1, %o4 + + tst %o3 ! set up for initial iteration +divloop: + sll %o2, 4, %o2 + ! depth 1, accumulated bits 0 + bl L1.16 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 2, accumulated bits 1 + bl L2.17 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 3, accumulated bits 3 + bl L3.19 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits 7 + bl L4.23 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (7*2+1), %o2 + +L4.23: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (7*2-1), %o2 + + +L3.19: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits 5 + bl L4.21 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (5*2+1), %o2 + +L4.21: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (5*2-1), %o2 + +L2.17: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 3, accumulated bits 1 + bl L3.17 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits 3 + bl L4.19 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (3*2+1), %o2 + +L4.19: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (3*2-1), %o2 + +L3.17: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits 1 + bl L4.17 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (1*2+1), %o2 + +L4.17: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (1*2-1), %o2 + +L1.16: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 2, accumulated bits -1 + bl L2.15 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 3, accumulated bits -1 + bl L3.15 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits -1 + bl L4.15 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-1*2+1), %o2 + +L4.15: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-1*2-1), %o2 + +L3.15: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits -3 + bl L4.13 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-3*2+1), %o2 + +L4.13: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-3*2-1), %o2 + +L2.15: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 3, accumulated bits -3 + bl L3.13 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits -5 + bl L4.11 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-5*2+1), %o2 + +L4.11: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-5*2-1), %o2 + +L3.13: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits -7 + bl L4.9 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-7*2+1), %o2 + +L4.9: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-7*2-1), %o2 + + 9: +end_regular_divide: + subcc %o4, 1, %o4 + bge divloop + tst %o3 + bl,a got_result + ! non-restoring fixup here (one instruction only!) + sub %o2, 1, %o2 + + +got_result: + ! check to see if answer should be < 0 + tst %g3 + bl,a 1f + sub %g0, %o2, %o2 +1: + retl + mov %o2, %o0 +#endif + +#ifdef L_modsi3 +/* This implementation was taken from glibc: + * + * Input: dividend and divisor in %o0 and %o1 respectively. + * + * Algorithm parameters: + * N how many bits per iteration we try to get (4) + * WORDSIZE total number of bits (32) + * + * Derived constants: + * TOPBITS number of bits in the top decade of a number + * + * Important variables: + * Q the partial quotient under development (initially 0) + * R the remainder so far, initially the dividend + * ITER number of main division loop iterations required; + * equal to ceil(log2(quotient) / N). Note that this + * is the log base (2^N) of the quotient. + * V the current comparand, initially divisor*2^(ITER*N-1) + * + * Cost: + * Current estimate for non-large dividend is + * ceil(log2(quotient) / N) * (10 + 7N/2) + C + * A large dividend is one greater than 2^(31-TOPBITS) and takes a + * different path, as the upper bits of the quotient must be developed + * one bit at a time. + */ +.text + .align 4 + .global .urem + .proc 4 +.urem: + b divide + mov 0, %g3 ! result always positive + + .align 4 + .global .rem + .proc 4 +.rem: + ! compute sign of result; if neither is negative, no problem + orcc %o1, %o0, %g0 ! either negative? + bge 2f ! no, go do the divide + mov %o0, %g3 ! sign of remainder matches %o0 + tst %o1 + bge 1f + tst %o0 + ! %o1 is definitely negative; %o0 might also be negative + bge 2f ! if %o0 not negative... + sub %g0, %o1, %o1 ! in any case, make %o1 nonneg +1: ! %o0 is negative, %o1 is nonnegative + sub %g0, %o0, %o0 ! make %o0 nonnegative +2: + + ! Ready to divide. Compute size of quotient; scale comparand. +divide: + orcc %o1, %g0, %o5 + bne 1f + mov %o0, %o3 + + ! Divide by zero trap. If it returns, return 0 (about as + ! wrong as possible, but that is what SunOS does...). + ta 0x2 !ST_DIV0 + retl + clr %o0 + +1: + cmp %o3, %o5 ! if %o1 exceeds %o0, done + blu got_result ! (and algorithm fails otherwise) + clr %o2 + sethi %hi(1 << (32 - 4 - 1)), %g1 + cmp %o3, %g1 + blu not_really_big + clr %o4 + + ! Here the dividend is >= 2**(31-N) or so. We must be careful here, + ! as our usual N-at-a-shot divide step will cause overflow and havoc. + ! The number of bits in the result here is N*ITER+SC, where SC <= N. + ! Compute ITER in an unorthodox manner: know we need to shift V into + ! the top decade: so do not even bother to compare to R. + 1: + cmp %o5, %g1 + bgeu 3f + mov 1, %g2 + sll %o5, 4, %o5 + b 1b + add %o4, 1, %o4 + + ! Now compute %g2. + 2: addcc %o5, %o5, %o5 + bcc not_too_big + add %g2, 1, %g2 + + ! We get here if the %o1 overflowed while shifting. + ! This means that %o3 has the high-order bit set. + ! Restore %o5 and subtract from %o3. + sll %g1, 4, %g1 ! high order bit + srl %o5, 1, %o5 ! rest of %o5 + add %o5, %g1, %o5 + b do_single_div + sub %g2, 1, %g2 + + not_too_big: + 3: cmp %o5, %o3 + blu 2b + nop + be do_single_div + nop + /* NB: these are commented out in the V8-Sparc manual as well */ + /* (I do not understand this) */ + ! %o5 > %o3: went too far: back up 1 step + ! srl %o5, 1, %o5 + ! dec %g2 + ! do single-bit divide steps + ! + ! We have to be careful here. We know that %o3 >= %o5, so we can do the + ! first divide step without thinking. BUT, the others are conditional, + ! and are only done if %o3 >= 0. Because both %o3 and %o5 may have the high- + ! order bit set in the first step, just falling into the regular + ! division loop will mess up the first time around. + ! So we unroll slightly... + do_single_div: + subcc %g2, 1, %g2 + bl end_regular_divide + nop + sub %o3, %o5, %o3 + mov 1, %o2 + b end_single_divloop + nop + single_divloop: + sll %o2, 1, %o2 + bl 1f + srl %o5, 1, %o5 + ! %o3 >= 0 + sub %o3, %o5, %o3 + b 2f + add %o2, 1, %o2 + 1: ! %o3 < 0 + add %o3, %o5, %o3 + sub %o2, 1, %o2 + 2: + end_single_divloop: + subcc %g2, 1, %g2 + bge single_divloop + tst %o3 + b,a end_regular_divide + +not_really_big: +1: + sll %o5, 4, %o5 + cmp %o5, %o3 + bleu 1b + addcc %o4, 1, %o4 + be got_result + sub %o4, 1, %o4 + + tst %o3 ! set up for initial iteration +divloop: + sll %o2, 4, %o2 + ! depth 1, accumulated bits 0 + bl L1.16 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 2, accumulated bits 1 + bl L2.17 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 3, accumulated bits 3 + bl L3.19 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits 7 + bl L4.23 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (7*2+1), %o2 +L4.23: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (7*2-1), %o2 + +L3.19: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits 5 + bl L4.21 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (5*2+1), %o2 + +L4.21: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (5*2-1), %o2 + +L2.17: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 3, accumulated bits 1 + bl L3.17 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits 3 + bl L4.19 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (3*2+1), %o2 + +L4.19: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (3*2-1), %o2 + +L3.17: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits 1 + bl L4.17 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (1*2+1), %o2 + +L4.17: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (1*2-1), %o2 + +L1.16: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 2, accumulated bits -1 + bl L2.15 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 3, accumulated bits -1 + bl L3.15 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits -1 + bl L4.15 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-1*2+1), %o2 + +L4.15: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-1*2-1), %o2 + +L3.15: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits -3 + bl L4.13 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-3*2+1), %o2 + +L4.13: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-3*2-1), %o2 + +L2.15: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 3, accumulated bits -3 + bl L3.13 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits -5 + bl L4.11 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-5*2+1), %o2 + +L4.11: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-5*2-1), %o2 + +L3.13: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits -7 + bl L4.9 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-7*2+1), %o2 + +L4.9: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-7*2-1), %o2 + + 9: +end_regular_divide: + subcc %o4, 1, %o4 + bge divloop + tst %o3 + bl,a got_result + ! non-restoring fixup here (one instruction only!) + add %o3, %o1, %o3 + +got_result: + ! check to see if answer should be < 0 + tst %g3 + bl,a 1f + sub %g0, %o3, %o3 +1: + retl + mov %o3, %o0 + +#endif + diff --git a/gcc/config/sparc/lb1spl.asm b/gcc/config/sparc/lb1spl.asm new file mode 100755 index 0000000..4c8bc30 --- /dev/null +++ b/gcc/config/sparc/lb1spl.asm @@ -0,0 +1,246 @@ +/* This is an assembly language implementation of libgcc1.c for the sparclite + processor. + + These routines are all from the Sparclite User's Guide, slightly edited + to match the desired calling convention, and also to optimize them. */ + +#ifdef L_udivsi3 +.text + .align 4 + .global .udiv + .proc 04 +.udiv: + wr %g0,%g0,%y ! Not a delayed write for sparclite + tst %g0 + divscc %o0,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + retl + divscc %g1,%o1,%o0 +#endif + +#ifdef L_umodsi3 +.text + .align 4 + .global .urem + .proc 04 +.urem: + wr %g0,%g0,%y ! Not a delayed write for sparclite + tst %g0 + divscc %o0,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + divscc %g1,%o1,%g1 + bl 1f + rd %y,%o0 + retl + nop +1: retl + add %o0,%o1,%o0 +#endif + +#ifdef L_divsi3 +.text + .align 4 + .global .div + .proc 04 +! ??? This routine could be made faster if was optimized, and if it was +! rewritten to only calculate the quotient. +.div: + wr %g0,%g0,%y ! Not a delayed write for sparclite + mov %o1,%o4 + tst %o1 + bl,a 1f + sub %g0,%o4,%o4 +1: tst %o0 + bl,a 2f + mov -1,%y +2: divscc %o0,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + be 6f + mov %y,%o3 + bg 4f + addcc %o3,%o4,%g0 + be,a 6f + mov %g0,%o3 + tst %o0 + bl 5f + tst %g1 + ba 5f + add %o3,%o4,%o3 +4: subcc %o3,%o4,%g0 + be,a 6f + mov %g0,%o3 + tst %o0 + bge 5f + tst %g1 + sub %o3,%o4,%o3 +5: bl,a 6f + add %g1,1,%g1 +6: tst %o1 + bl,a 7f + sub %g0,%g1,%g1 +7: retl + mov %g1,%o0 ! Quotient is in %g1. +#endif + +#ifdef L_modsi3 +.text + .align 4 + .global .rem + .proc 04 +! ??? This routine could be made faster if was optimized, and if it was +! rewritten to only calculate the remainder. +.rem: + wr %g0,%g0,%y ! Not a delayed write for sparclite + mov %o1,%o4 + tst %o1 + bl,a 1f + sub %g0,%o4,%o4 +1: tst %o0 + bl,a 2f + mov -1,%y +2: divscc %o0,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + divscc %g1,%o4,%g1 + be 6f + mov %y,%o3 + bg 4f + addcc %o3,%o4,%g0 + be,a 6f + mov %g0,%o3 + tst %o0 + bl 5f + tst %g1 + ba 5f + add %o3,%o4,%o3 +4: subcc %o3,%o4,%g0 + be,a 6f + mov %g0,%o3 + tst %o0 + bge 5f + tst %g1 + sub %o3,%o4,%o3 +5: bl,a 6f + add %g1,1,%g1 +6: tst %o1 + bl,a 7f + sub %g0,%g1,%g1 +7: retl + mov %o3,%o0 ! Remainder is in %o3. +#endif diff --git a/gcc/config/sparc/linux-aout.h b/gcc/config/sparc/linux-aout.h new file mode 100755 index 0000000..76d7653 --- /dev/null +++ b/gcc/config/sparc/linux-aout.h @@ -0,0 +1,130 @@ +/* Definitions for SPARC running Linux-based GNU systems with a.out. + Copyright (C) 1996, 1997 Free Software Foundation, Inc. + Contributed by Eddie C. Dost (ecd@skynet.be) + +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 <aoutos.h> +#include <sparc/sparc.h> + +/* Don't assume anything about the header files. */ +#define NO_IMPLICIT_EXTERN_C + +#undef HAVE_ATEXIT +#define HAVE_ATEXIT + +/* GNU/Linux uses ctype from glibc.a. I am not sure how complete it is. + For now, we play safe. It may change later. */ + +#if 0 +#undef MULTIBYTE_CHARS +#define MULTIBYTE_CHARS 1 +#endif + +/* We need that too. */ +#define HANDLE_SYSV_PRAGMA + +#undef MD_EXEC_PREFIX +#undef MD_STARTFILE_PREFIX + +/* Output at beginning of assembler file. */ +/* The .file command should always begin the output. */ +#undef ASM_FILE_START +#define ASM_FILE_START(FILE) \ + do { \ + output_file_directive (FILE, main_input_filename); \ + fprintf (FILE, "\t.version\t\"01.01\"\n"); \ + } while (0) + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC "%{pg:gcrt0.o%s} %{!pg:%{p:gcrt0.o%s} %{!p:crt0.o%s}} %{static:-static}" + +#undef TARGET_VERSION +#define TARGET_VERSION fprintf (stderr, " (sparc GNU/Linux with a.out)"); + +#undef SIZE_TYPE +#define SIZE_TYPE "unsigned int" + +#undef PTRDIFF_TYPE +#define PTRDIFF_TYPE "int" + +#undef WCHAR_TYPE +#define WCHAR_TYPE "long int" + +#undef WCHAR_TYPE_SIZE +#define WCHAR_TYPE_SIZE BITS_PER_WORD + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dunix -Dsparc -Dlinux -Asystem(unix) -Asystem(posix)" + +#undef CPP_SUBTARGET_SPEC +#define CPP_SUBTARGET_SPEC \ +"%{fPIC:-D__PIC__ -D__pic__} %{fpic:-D__PIC__ -D__pic__} %{posix:-D_POSIX_SOURCE}" + +/* 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 + +#undef LIB_SPEC + +#if 1 +/* We no longer link with libc_p.a or libg.a by default. If you + want to profile or debug the GNU/Linux C library, please add + -lc_p or -ggdb to LDFLAGS at the link time, respectively. */ +#define LIB_SPEC \ +"%{mieee-fp:-lieee} %{p:-lgmon} %{pg:-lgmon} %{!ggdb:-lc} %{ggdb:-lg}" +#else +#define LIB_SPEC \ +"%{mieee-fp:-lieee} %{p:-lgmon -lc_p} %{pg:-lgmon -lc_p} \ + %{!p:%{!pg:%{!g*:-lc} %{g*:-lg -static}}}" +#endif + +#undef LINK_SPEC +#define LINK_SPEC "-m sparclinux" + +/* The sun bundled assembler doesn't accept -Yd, (and neither does gas). + It's safe to pass -s always, even if -g is not used. */ +#undef ASM_SPEC +#define ASM_SPEC \ + "%{V} %{v:%{!V:-V}} %{n} %{T} %{Ym,*} %{Wa,*:%*} -s %{fpic:-K PIC} %{fPIC:-K PIC}" + +#if 0 +/* Define for support of TFmode long double and REAL_ARITHMETIC. + Sparc ABI says that long double is 4 words. GNU/Linux does not support + long double yet. */ +#define LONG_DOUBLE_TYPE_SIZE 128 +#endif + +/* Override MACHINE_STATE_{SAVE,RESTORE} because we have special + traps available which can get and set the condition codes + reliably. */ +#undef MACHINE_STATE_SAVE +#define MACHINE_STATE_SAVE(ID) \ + unsigned long int ms_flags, ms_saveret; \ + asm volatile("ta 0x20\n\t" \ + "mov %%g1, %0\n\t" \ + "mov %%g2, %1\n\t" \ + : "=r" (ms_flags), "=r" (ms_saveret)); + +#undef MACHINE_STATE_RESTORE +#define MACHINE_STATE_RESTORE(ID) \ + asm volatile("mov %0, %%g1\n\t" \ + "mov %1, %%g2\n\t" \ + "ta 0x21\n\t" \ + : /* no outputs */ \ + : "r" (ms_flags), "r" (ms_saveret)); diff --git a/gcc/config/sparc/linux.h b/gcc/config/sparc/linux.h new file mode 100755 index 0000000..d967b01 --- /dev/null +++ b/gcc/config/sparc/linux.h @@ -0,0 +1,259 @@ +/* Definitions for SPARC running Linux-based GNU systems with ELF. + Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc. + Contributed by Eddie C. Dost (ecd@skynet.be) + +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 LINUX_DEFAULT_ELF + +/* Don't assume anything about the header files. */ +#define NO_IMPLICIT_EXTERN_C + +#undef HAVE_ATEXIT +#define HAVE_ATEXIT + +/* GNU/Linux uses ctype from glibc.a. I am not sure how complete it is. + For now, we play safe. It may change later. */ + +#if 0 +#undef MULTIBYTE_CHARS +#define MULTIBYTE_CHARS 1 +#endif + +#ifndef USE_GNULIBC_1 +#undef DEFAULT_VTABLE_THUNKS +#define DEFAULT_VTABLE_THUNKS 1 +#endif + +/* Use stabs instead of DWARF debug format. */ +#define PREFERRED_DEBUGGING_TYPE DBX_DEBUG + +#include <sparc/sysv4.h> + +#undef MD_EXEC_PREFIX +#undef MD_STARTFILE_PREFIX + +/* Output at beginning of assembler file. */ +/* The .file command should always begin the output. */ +#undef ASM_FILE_START +#define ASM_FILE_START(FILE) \ + do { \ + output_file_directive (FILE, main_input_filename); \ + fprintf (FILE, "\t.version\t\"01.01\"\n"); \ + } while (0) + +/* Provide a STARTFILE_SPEC appropriate for GNU/Linux. Here we add + the GNU/Linux magical crtbegin.o file (see crtstuff.c) which + provides part of the support for getting C++ file-scope static + object constructed before entering `main'. */ + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC \ + "%{!shared: \ + %{pg:gcrt1.o%s} %{!pg:%{p:gcrt1.o%s} %{!p:crt1.o%s}}}\ + crti.o%s %{!shared:crtbegin.o%s} %{shared:crtbeginS.o%s}" + +/* Provide a ENDFILE_SPEC appropriate for GNU/Linux. Here we tack on + the GNU/Linux magical crtend.o file (see crtstuff.c) which + provides part of the support for getting C++ file-scope static + object constructed before entering `main', followed by a normal + GNU/Linux "finalizer" file, `crtn.o'. */ + +#undef ENDFILE_SPEC +#define ENDFILE_SPEC \ + "%{!shared:crtend.o%s} %{shared:crtendS.o%s} crtn.o%s" + +/* This is for -profile to use -lc_p instead of -lc. */ +#undef CC1_SPEC +#define CC1_SPEC "%{profile:-p} \ +%{sun4:} %{target:} \ +%{mcypress:-mcpu=cypress} \ +%{msparclite:-mcpu=sparclite} %{mf930:-mcpu=f930} %{mf934:-mcpu=f934} \ +%{mv8:-mcpu=v8} %{msupersparc:-mcpu=supersparc} \ +" + +#undef TARGET_VERSION +#define TARGET_VERSION fprintf (stderr, " (sparc GNU/Linux with ELF)"); + +#undef SIZE_TYPE +#define SIZE_TYPE "unsigned int" + +#undef PTRDIFF_TYPE +#define PTRDIFF_TYPE "int" + +#undef WCHAR_TYPE +#define WCHAR_TYPE "long int" + +#undef WCHAR_TYPE_SIZE +#define WCHAR_TYPE_SIZE BITS_PER_WORD + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-D__ELF__ -Dunix -D__sparc__ -Dlinux -Asystem(unix) -Asystem(posix)" + +#undef CPP_SUBTARGET_SPEC +#ifdef USE_GNULIBC_1 +#define CPP_SUBTARGET_SPEC \ +"%{fPIC:-D__PIC__ -D__pic__} %{fpic:-D__PIC__ -D__pic__} %{posix:-D_POSIX_SOURCE}" +#else +#define CPP_SUBTARGET_SPEC \ +"%{fPIC:-D__PIC__ -D__pic__} %{fpic:-D__PIC__ -D__pic__} %{posix:-D_POSIX_SOURCE} %{pthread:-D_REENTRANT}" +#endif + +#undef LIB_SPEC +/* We no longer link with libc_p.a or libg.a by default. If you + want to profile or debug the GNU/Linux C library, please add + -lc_p or -ggdb to LDFLAGS at the link time, respectively. */ +#if 1 +#ifdef USE_GNULIBC_1 +#define LIB_SPEC \ + "%{!shared: %{p:-lgmon} %{pg:-lgmon} %{profile:-lgmon -lc_p} \ + %{!profile:%{!ggdb:-lc} %{ggdb:-lg}}}" +#else +#define LIB_SPEC \ + "%{shared: -lc} \ + %{!shared: %{mieee-fp:-lieee} %{pthread:-lpthread} \ + %{profile:-lc_p} %{!profile: -lc}}" +#endif +#else +#define LIB_SPEC \ + "%{!shared: \ + %{mieee-fp:-lieee} %{p:-lgmon -lc_p} %{pg:-lgmon -lc_p} \ + %{!p:%{!pg:%{!g*:-lc} %{g*:-lg}}}}" +#endif + +/* Provide a LINK_SPEC appropriate for GNU/Linux. Here we provide support + for the special GCC options -static and -shared, which allow us to + link things in one of these three modes by applying the appropriate + combinations of options at link-time. We like to support here for + as many of the other GNU linker options as possible. But I don't + have the time to search for those flags. I am sure how to add + support for -soname shared_object_name. H.J. + + I took out %{v:%{!V:-V}}. It is too much :-(. They can use + -Wl,-V. + + When the -shared link option is used a final link is not being + done. */ + +/* If ELF is the default format, we should not use /lib/elf. */ + +#undef LINK_SPEC +#ifdef USE_GNULIBC_1 +#ifndef LINUX_DEFAULT_ELF +#define LINK_SPEC "-m elf32_sparc -Y P,/usr/lib %{shared:-shared} \ + %{!shared: \ + %{!ibcs: \ + %{!static: \ + %{rdynamic:-export-dynamic} \ + %{!dynamic-linker:-dynamic-linker /lib/elf/ld-linux.so.1} \ + %{!rpath:-rpath /lib/elf/}} %{static:-static}}}" +#else +#define LINK_SPEC "-m elf32_sparc -Y P,/usr/lib %{shared:-shared} \ + %{!shared: \ + %{!ibcs: \ + %{!static: \ + %{rdynamic:-export-dynamic} \ + %{!dynamic-linker:-dynamic-linker /lib/ld-linux.so.1}} \ + %{static:-static}}}" +#endif +#else +#define LINK_SPEC "-m elf32_sparc -Y P,/usr/lib %{shared:-shared} \ + %{!shared: \ + %{!ibcs: \ + %{!static: \ + %{rdynamic:-export-dynamic} \ + %{!dynamic-linker:-dynamic-linker /lib/ld-linux.so.2}} \ + %{static:-static}}}" +#endif + +/* The sun bundled assembler doesn't accept -Yd, (and neither does gas). + It's safe to pass -s always, even if -g is not used. */ +#undef ASM_SPEC +#define ASM_SPEC \ + "%{V} %{v:%{!V:-V}} %{!Qn:-Qy} %{n} %{T} %{Ym,*} %{Wa,*:%*} -s %{fpic:-K PIC} %{fPIC:-K PIC}" + +/* Same as sparc.h */ +#undef DBX_REGISTER_NUMBER +#define DBX_REGISTER_NUMBER(REGNO) (REGNO) + +/* We use stabs-in-elf for debugging, because that is what the native + toolchain uses. XXX */ +#undef PREFERRED_DEBUGGING_TYPE +#define PREFERRED_DEBUGGING_TYPE DBX_DEBUG + +#undef ASM_OUTPUT_ALIGNED_LOCAL +#define ASM_OUTPUT_ALIGNED_LOCAL(FILE, NAME, SIZE, ALIGN) \ +do { \ + fputs ("\t.local\t", (FILE)); \ + assemble_name ((FILE), (NAME)); \ + putc ('\n', (FILE)); \ + ASM_OUTPUT_ALIGNED_COMMON (FILE, NAME, SIZE, ALIGN); \ +} while (0) + +#undef COMMON_ASM_OP +#define COMMON_ASM_OP "\t.common" + +/* This is how to output a definition of an internal numbered label where + PREFIX is the class of label and NUM is the number within the class. */ + +#undef ASM_OUTPUT_INTERNAL_LABEL +#define ASM_OUTPUT_INTERNAL_LABEL(FILE,PREFIX,NUM) \ + fprintf (FILE, ".L%s%d:\n", PREFIX, NUM) + +/* This is how to output a reference to an internal numbered label where + PREFIX is the class of label and NUM is the number within the class. */ + +#undef ASM_OUTPUT_INTERNAL_LABELREF +#define ASM_OUTPUT_INTERNAL_LABELREF(FILE,PREFIX,NUM) \ + fprintf (FILE, ".L%s%d", PREFIX, 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'. */ + +#undef ASM_GENERATE_INTERNAL_LABEL +#define ASM_GENERATE_INTERNAL_LABEL(LABEL,PREFIX,NUM) \ + sprintf (LABEL, "*.L%s%d", PREFIX, NUM) + + +#if 0 +/* Define for support of TFmode long double and REAL_ARITHMETIC. + Sparc ABI says that long double is 4 words. GNU/Linux does not support + long double yet. */ +#define LONG_DOUBLE_TYPE_SIZE 128 +#endif + +/* Override MACHINE_STATE_{SAVE,RESTORE} because we have special + traps available which can get and set the condition codes + reliably. */ +#undef MACHINE_STATE_SAVE +#define MACHINE_STATE_SAVE(ID) \ + unsigned long int ms_flags, ms_saveret; \ + asm volatile("ta 0x20\n\t" \ + "mov %%g1, %0\n\t" \ + "mov %%g2, %1\n\t" \ + : "=r" (ms_flags), "=r" (ms_saveret)); + +#undef MACHINE_STATE_RESTORE +#define MACHINE_STATE_RESTORE(ID) \ + asm volatile("mov %0, %%g1\n\t" \ + "mov %1, %%g2\n\t" \ + "ta 0x21\n\t" \ + : /* no outputs */ \ + : "r" (ms_flags), "r" (ms_saveret)); diff --git a/gcc/config/sparc/linux64.h b/gcc/config/sparc/linux64.h new file mode 100755 index 0000000..705b5ca --- /dev/null +++ b/gcc/config/sparc/linux64.h @@ -0,0 +1,366 @@ +/* Definitions for 64-bit SPARC running Linux-based GNU systems with ELF. + Copyright 1996, 1997, 1998 Free Software Foundation, Inc. + Contributed by David S. Miller (davem@caip.rutgers.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 SPARC_BI_ARCH + +#define LINUX_DEFAULT_ELF + +/* Don't assume anything about the header files. */ +#define NO_IMPLICIT_EXTERN_C + +#undef HAVE_ATEXIT +#define HAVE_ATEXIT + +#include <sparc/sysv4.h> + +#undef MD_EXEC_PREFIX +#undef MD_STARTFILE_PREFIX + +#if TARGET_CPU_DEFAULT == TARGET_CPU_v9 || TARGET_CPU_DEFAULT == TARGET_CPU_ultrasparc +/* A 64 bit v9 compiler with stack-bias, + in a Medium/Low code model environment. */ + +#undef TARGET_DEFAULT +#define TARGET_DEFAULT \ + (MASK_V9 + MASK_PTR64 + MASK_64BIT /* + MASK_HARD_QUAD */ \ + + MASK_STACK_BIAS + MASK_APP_REGS + MASK_EPILOGUE + MASK_FPU) +#endif + +/* Output at beginning of assembler file. */ +/* The .file command should always begin the output. */ +#undef ASM_FILE_START +#define ASM_FILE_START(FILE) \ + do { \ + output_file_directive (FILE, main_input_filename); \ + fprintf (FILE, "\t.version\t\"01.01\"\n"); \ + } while (0) + +#undef ASM_CPU_DEFAULT_SPEC +#define ASM_CPU_DEFAULT_SPEC "-Av9a" + +/* Provide a STARTFILE_SPEC appropriate for GNU/Linux. Here we add + the GNU/Linux magical crtbegin.o file (see crtstuff.c) which + provides part of the support for getting C++ file-scope static + object constructed before entering `main'. */ + +#undef STARTFILE_SPEC + +#define STARTFILE_SPEC32 \ + "%{!shared: \ + %{pg:gcrt1.o%s} %{!pg:%{p:gcrt1.o%s} %{!p:crt1.o%s}}}\ + crti.o%s %{!shared:crtbegin.o%s} %{shared:crtbeginS.o%s}" + +#define STARTFILE_SPEC64 \ + "%{!shared: \ + %{pg:/usr/lib64/gcrt1.o%s} %{!pg:%{p:/usr/lib64/gcrt1.o%s} %{!p:/usr/lib64/crt1.o%s}}}\ + /usr/lib64/crti.o%s %{!shared:crtbegin.o%s} %{shared:crtbeginS.o%s}" + +#ifdef SPARC_BI_ARCH + +#if DEFAULT_ARCH32_P +#define STARTFILE_SPEC "\ +%{m32:" STARTFILE_SPEC32 "} \ +%{m64:" STARTFILE_SPEC64 "} \ +%{!m32:%{!m64:" STARTFILE_SPEC32 "}}" +#else +#define STARTFILE_SPEC "\ +%{m32:" STARTFILE_SPEC32 "} \ +%{m64:" STARTFILE_SPEC64 "} \ +%{!m32:%{!m64:" STARTFILE_SPEC64 "}}" +#endif + +#else + +#define STARTFILE_SPEC STARTFILE_SPEC64 + +#endif + +/* Provide a ENDFILE_SPEC appropriate for GNU/Linux. Here we tack on + the GNU/Linux magical crtend.o file (see crtstuff.c) which + provides part of the support for getting C++ file-scope static + object constructed before entering `main', followed by a normal + GNU/Linux "finalizer" file, `crtn.o'. */ + +#undef ENDFILE_SPEC + +#define ENDFILE_SPEC32 \ + "%{!shared:crtend.o%s} %{shared:crtendS.o%s} crtn.o%s" + +#define ENDFILE_SPEC64 \ + "%{!shared:crtend.o%s} %{shared:crtendS.o%s} /usr/lib64/crtn.o%s" + +#ifdef SPARC_BI_ARCH + +#if DEFAULT_ARCH32_P +#define ENDFILE_SPEC "\ +%{m32:" ENDFILE_SPEC32 "} \ +%{m64:" ENDFILE_SPEC64 "} \ +%{!m32:%{!m64:" ENDFILE_SPEC32 "}}" +#else +#define ENDFILE_SPEC "\ +%{m32:" ENDFILE_SPEC32 "} \ +%{m64:" ENDFILE_SPEC64 "} \ +%{!m32:%{!m64:" ENDFILE_SPEC64 "}}" +#endif + +#else + +#define ENDFILE_SPEC ENDFILE_SPEC64 + +#endif + +#undef TARGET_VERSION +#define TARGET_VERSION fprintf (stderr, " (sparc64 GNU/Linux with ELF)"); + +/* The default code model. */ +#undef SPARC_DEFAULT_CMODEL +#define SPARC_DEFAULT_CMODEL CM_MEDLOW + +#undef WCHAR_TYPE +#define WCHAR_TYPE "long int" + +#undef WCHAR_TYPE_SIZE +#define WCHAR_TYPE_SIZE BITS_PER_WORD + +#undef LONG_DOUBLE_TYPE_SIZE +#define LONG_DOUBLE_TYPE_SIZE 128 + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-D__ELF__ -Dunix -D_LONGLONG -D__sparc__ -Dlinux -Asystem(unix) -Asystem(posix)" + +#undef CPP_SUBTARGET_SPEC +#define CPP_SUBTARGET_SPEC "\ +%{fPIC:-D__PIC__ -D__pic__} \ +%{fpic:-D__PIC__ -D__pic__} \ +%{posix:-D_POSIX_SOURCE} \ +%{pthread:-D_REENTRANT} \ +" + +#undef LIB_SPEC +#define LIB_SPEC \ + "%{shared: -lc} \ + %{!shared: %{mieee-fp:-lieee} %{pthread:-lpthread} \ + %{profile:-lc_p} %{!profile: -lc}}" + +/* Provide a LINK_SPEC appropriate for GNU/Linux. Here we provide support + for the special GCC options -static and -shared, which allow us to + link things in one of these three modes by applying the appropriate + combinations of options at link-time. We like to support here for + as many of the other GNU linker options as possible. But I don't + have the time to search for those flags. I am sure how to add + support for -soname shared_object_name. H.J. + + I took out %{v:%{!V:-V}}. It is too much :-(. They can use + -Wl,-V. + + When the -shared link option is used a final link is not being + done. */ + +/* If ELF is the default format, we should not use /lib/elf. */ + +#ifdef SPARC_BI_ARCH + +#undef SUBTARGET_EXTRA_SPECS +#define SUBTARGET_EXTRA_SPECS \ + { "link_arch32", LINK_ARCH32_SPEC }, \ + { "link_arch64", LINK_ARCH64_SPEC }, \ + { "link_arch_default", LINK_ARCH_DEFAULT_SPEC }, \ + { "link_arch", LINK_ARCH_SPEC }, + +#define LINK_ARCH32_SPEC "-m elf32_sparc -Y P,/usr/lib %{shared:-shared} \ + %{!shared: \ + %{!ibcs: \ + %{!static: \ + %{rdynamic:-export-dynamic} \ + %{!dynamic-linker:-dynamic-linker /lib/ld-linux.so.2}} \ + %{static:-static}}} \ +" + +#define LINK_ARCH64_SPEC "-m elf64_sparc -Y P,/usr/lib64 %{shared:-shared} \ + %{!shared: \ + %{!ibcs: \ + %{!static: \ + %{rdynamic:-export-dynamic} \ + %{!dynamic-linker:-dynamic-linker /lib64/ld-linux.so.2}} \ + %{static:-static}}} \ +" + +#define LINK_ARCH_SPEC "\ +%{m32:%(link_arch32)} \ +%{m64:%(link_arch64)} \ +%{!m32:%{!m64:%(link_arch_default)}} \ +" + +#define LINK_ARCH_DEFAULT_SPEC \ +(DEFAULT_ARCH32_P ? LINK_ARCH32_SPEC : LINK_ARCH64_SPEC) + +#undef LINK_SPEC +#define LINK_SPEC "\ +%(link_arch) \ +%{mlittle-endian:-EL} \ +" + +#undef CC1_SPEC +#if DEFAULT_ARCH32_P +#define CC1_SPEC "\ +%{sun4:} %{target:} \ +%{mcypress:-mcpu=cypress} \ +%{msparclite:-mcpu=sparclite} %{mf930:-mcpu=f930} %{mf934:-mcpu=f934} \ +%{mv8:-mcpu=v8} %{msupersparc:-mcpu=supersparc} \ +%{m64:-mptr64 -mcpu=ultrasparc -mstack-bias} \ +" +#else +#define CC1_SPEC "\ +%{sun4:} %{target:} \ +%{mcypress:-mcpu=cypress} \ +%{msparclite:-mcpu=sparclite} %{mf930:-mcpu=f930} %{mf934:-mcpu=f934} \ +%{mv8:-mcpu=v8} %{msupersparc:-mcpu=supersparc} \ +%{m32:-mptr32 -mcpu=cypress -mno-stack-bias} \ +" +#endif + +#if DEFAULT_ARCH32_P +#define MULTILIB_DEFAULTS { "m32" } +#else +#define MULTILIB_DEFAULTS { "m64" } +#endif + +#else /* !SPARC_BI_ARCH */ + +#undef LINK_SPEC +#define LINK_ARCH_SPEC "-m elf64_sparc -Y P,/usr/lib64 %{shared:-shared} \ + %{!shared: \ + %{!ibcs: \ + %{!static: \ + %{rdynamic:-export-dynamic} \ + %{!dynamic-linker:-dynamic-linker /lib64/ld-linux.so.2}} \ + %{static:-static}}} \ +%{mlittle-endian:-EL} \ +" + +#endif /* !SPARC_BI_ARCH */ + +/* The sun bundled assembler doesn't accept -Yd, (and neither does gas). + It's safe to pass -s always, even if -g is not used. */ +#undef ASM_SPEC +#define ASM_SPEC "\ +%{V} \ +%{v:%{!V:-V}} \ +%{!Qn:-Qy} \ +%{n} \ +%{T} \ +%{Ym,*} \ +%{Wa,*:%*} \ +-s %{fpic:-K PIC} %{fPIC:-K PIC} \ +%{mlittle-endian:-EL} \ +%(asm_cpu) %(asm_arch) \ +" + +/* Same as sparc.h */ +#undef DBX_REGISTER_NUMBER +#define DBX_REGISTER_NUMBER(REGNO) (REGNO) + +/* System V Release 4 uses DWARF debugging info. Buf DWARF1 doesn't do + 64-bit anything, so we use DWARF2. */ + +#undef DWARF2_DEBUGGING_INFO +#undef DWARF_DEBUGGING_INFO +#undef DBX_DEBUGGING_INFO +#define DWARF2_DEBUGGING_INFO +#define DBX_DEBUGGING_INFO + +#undef PREFERRED_DEBUGGING_TYPE +#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG + +#undef ASM_OUTPUT_ALIGNED_LOCAL +#define ASM_OUTPUT_ALIGNED_LOCAL(FILE, NAME, SIZE, ALIGN) \ +do { \ + fputs ("\t.local\t", (FILE)); \ + assemble_name ((FILE), (NAME)); \ + putc ('\n', (FILE)); \ + ASM_OUTPUT_ALIGNED_COMMON (FILE, NAME, SIZE, ALIGN); \ +} while (0) + +#undef COMMON_ASM_OP +#define COMMON_ASM_OP "\t.common" + +/* This is how to output a definition of an internal numbered label where + PREFIX is the class of label and NUM is the number within the class. */ + +#undef ASM_OUTPUT_INTERNAL_LABEL +#define ASM_OUTPUT_INTERNAL_LABEL(FILE,PREFIX,NUM) \ + fprintf (FILE, ".L%s%d:\n", PREFIX, NUM) + +/* This is how to output a reference to an internal numbered label where + PREFIX is the class of label and NUM is the number within the class. */ + +#undef ASM_OUTPUT_INTERNAL_LABELREF +#define ASM_OUTPUT_INTERNAL_LABELREF(FILE,PREFIX,NUM) \ + fprintf (FILE, ".L%s%d", PREFIX, 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'. */ + +#undef ASM_GENERATE_INTERNAL_LABEL +#define ASM_GENERATE_INTERNAL_LABEL(LABEL,PREFIX,NUM) \ + sprintf (LABEL, "*.L%s%d", PREFIX, NUM) + +/* Stabs doesn't use this, and it confuses a simulator. */ +/* ??? Need to see what DWARF needs, if anything. */ +#undef ASM_IDENTIFY_GCC +#define ASM_IDENTIFY_GCC(FILE) + +/* Define the names of various pseudo-ops used by the Sparc/svr4 assembler. + ??? If ints are 64 bits then UNALIGNED_INT_ASM_OP (defined elsewhere) is + misnamed. These should all refer to explicit sizes (half/word/xword?), + anything other than short/int/long/etc. */ + +#define UNALIGNED_DOUBLE_INT_ASM_OP ".uaxword" + +/* DWARF bits. */ + +/* Follow Irix 6 and not the Dwarf2 draft in using 64-bit offsets. + Obviously the Dwarf2 folks havn't tried to actually build systems + with their spec. On a 64-bit system, only 64-bit relocs become + RELATIVE relocations. */ + +/* #define DWARF_OFFSET_SIZE PTR_SIZE */ + +/* Override MACHINE_STATE_{SAVE,RESTORE} because we have special + traps available which can get and set the condition codes + reliably. */ +#undef MACHINE_STATE_SAVE +#define MACHINE_STATE_SAVE(ID) \ + unsigned long int ms_flags, ms_saveret; \ + asm volatile("ta 0x20\n\t" \ + "mov %%g1, %0\n\t" \ + "mov %%g2, %1\n\t" \ + : "=r" (ms_flags), "=r" (ms_saveret)); + +#undef MACHINE_STATE_RESTORE +#define MACHINE_STATE_RESTORE(ID) \ + asm volatile("mov %0, %%g1\n\t" \ + "mov %1, %%g2\n\t" \ + "ta 0x21\n\t" \ + : /* no outputs */ \ + : "r" (ms_flags), "r" (ms_saveret)); diff --git a/gcc/config/sparc/lite.h b/gcc/config/sparc/lite.h new file mode 100755 index 0000000..8d75647 --- /dev/null +++ b/gcc/config/sparc/lite.h @@ -0,0 +1,45 @@ +/* Definitions of target machine for GNU compiler, for SPARClite w/o FPU. + Copyright (C) 1993, 1996, 1999 Free Software Foundation, Inc. + Contributed by Jim Wilson (wilson@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. */ + +#include "sparc/sparc.h" + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dsparc -Dsparclite -Acpu(sparc) -Amachine(sparc)" + +#undef TARGET_VERSION +#define TARGET_VERSION fprintf (stderr, " (sparclite)"); + +/* Enable sparclite, app-regs and epilogue options. Do not enable the fpu. */ + +#undef TARGET_DEFAULT +#define TARGET_DEFAULT (MASK_SPARCLITE + MASK_APP_REGS + MASK_EPILOGUE) + +/* US Software GOFAST library support. */ +#include "gofast.h" +#undef INIT_SUBTARGET_OPTABS +#define INIT_SUBTARGET_OPTABS INIT_GOFAST_OPTABS + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC "" + +/* Don't set the target flags, this is done by the linker script */ +#undef LIB_SPEC +#define LIB_SPEC "" diff --git a/gcc/config/sparc/litecoff.h b/gcc/config/sparc/litecoff.h new file mode 100755 index 0000000..bd89e1b --- /dev/null +++ b/gcc/config/sparc/litecoff.h @@ -0,0 +1,113 @@ +/* Definitions of target machine for GNU compiler, for SPARClite w/o FPU, COFF. + Copyright (C) 1994, 1996 Free Software Foundation, Inc. + Written by Ken Raeburn (raeburn@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. */ + +#include "sparc/lite.h" + +#undef ASM_OUTPUT_IDENT + +#undef SELECT_SECTION +#undef SELECT_RTX_SECTION +#define BSS_SECTION_ASM_OP ".section\t\".bss\"" + +#include "svr3.h" + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dsparc -Dsparclite -Acpu(sparc) -Amachine(sparc)" + +/* Default to stabs in COFF. */ + +#define PREFERRED_DEBUGGING_TYPE DBX_DEBUG + +#include "dbxcoff.h" + +/* Support the ctors and dtors sections for g++. */ + +#undef INIT_SECTION_ASM_OP + +/* Support the ctors and dtors sections for g++. */ + +#undef CTORS_SECTION_ASM_OP +#define CTORS_SECTION_ASM_OP ".section\t.ctors,\"x\"" +#undef DTORS_SECTION_ASM_OP +#define DTORS_SECTION_ASM_OP ".section\t.dtors,\"x\"" + +/* A list of other sections which the compiler might be "in" at any + given time. */ + +#undef EXTRA_SECTIONS +#define EXTRA_SECTIONS in_const, in_ctors, in_dtors + +/* A list of extra section function definitions. */ + +#undef EXTRA_SECTION_FUNCTIONS +#define EXTRA_SECTION_FUNCTIONS \ + CONST_SECTION_FUNCTION \ + CTORS_SECTION_FUNCTION \ + DTORS_SECTION_FUNCTION + +#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; \ + } \ +} + +#define INT_ASM_OP ".long" + +/* A C statement (sans semicolon) to output an element in the table of + global constructors. */ +#undef ASM_OUTPUT_CONSTRUCTOR +#define ASM_OUTPUT_CONSTRUCTOR(FILE,NAME) \ + do { \ + ctors_section (); \ + fprintf (FILE, "\t%s\t ", INT_ASM_OP); \ + assemble_name (FILE, NAME); \ + fprintf (FILE, "\n"); \ + } while (0) + +/* A C statement (sans semicolon) to output an element in the table of + global destructors. */ +#undef ASM_OUTPUT_DESTRUCTOR +#define ASM_OUTPUT_DESTRUCTOR(FILE,NAME) \ + do { \ + dtors_section (); \ + fprintf (FILE, "\t%s\t ", INT_ASM_OP); \ + assemble_name (FILE, NAME); \ + fprintf (FILE, "\n"); \ + } while (0) + +#undef DO_GLOBAL_CTORS_BODY +#undef DO_GLOBAL_DTORS_BODY diff --git a/gcc/config/sparc/liteelf.h b/gcc/config/sparc/liteelf.h new file mode 100755 index 0000000..7861256 --- /dev/null +++ b/gcc/config/sparc/liteelf.h @@ -0,0 +1,53 @@ +/* Definitions of target machine for GNU compiler, for SPARClite w/o FPU. + Copyright (C) 1998, 1999 Free Software Foundation, Inc. + Contributed by Stan Cox (scox@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. */ + +#include "sparc/elf.h" + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dsparc -Dsparclite -Acpu(sparc) -Amachine(sparc)" + +/* Default to dwarf2 in ELF. */ + +#define DWARF_DEBUGGING_INFO +#define DWARF2_DEBUGGING_INFO + +#undef PREFERRED_DEBUGGING_TYPE +#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG + +#undef TARGET_VERSION +#define TARGET_VERSION fprintf (stderr, " (sparclite)"); + +/* Enable sparclite, app-regs and epilogue options. Do not enable the fpu. */ + +#undef TARGET_DEFAULT +#define TARGET_DEFAULT (MASK_SPARCLITE + MASK_APP_REGS + MASK_EPILOGUE) + +/* US Software GOFAST library support. */ +#include "gofast.h" +#undef INIT_SUBTARGET_OPTABS +#define INIT_SUBTARGET_OPTABS INIT_GOFAST_OPTABS + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC "crti.o%s crtbegin.o%s" + +/* Use __main method of constructor invocation */ +#undef INIT_SECTION_ASM_OP +#undef FINI_SECTION_ASM_OP diff --git a/gcc/config/sparc/lynx-ng.h b/gcc/config/sparc/lynx-ng.h new file mode 100755 index 0000000..9e9f82c --- /dev/null +++ b/gcc/config/sparc/lynx-ng.h @@ -0,0 +1,41 @@ +/* Definitions for SPARC running LynxOS, using Lynx's old as and ld. + Copyright (C) 1993, 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. + +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 <sparc/sparc.h> +#include <lynx-ng.h> + +/* ??? Must redefine to get sparclite and v8 defines. Can this be done + differently? */ + +#undef CPP_SPEC +#define CPP_SPEC "%{mthreads:-D_MULTITHREADED} \ + %{mposix:-D_POSIX_SOURCE} \ + %{msystem-v:-I/usr/include_v} \ + %(cpp_cpu)" + +/* Names to predefine in the preprocessor for this target machine. */ + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dunix -Dsparc -DLynx -DIBITS32 -Asystem(unix) -Asystem(lynx) -Acpu(sparc) -Amachine(sparc)" + +/* Provide required defaults for linker switches. */ + +#undef LINK_SPEC +#define LINK_SPEC "-e __main -T 0 %{msystem-v:-V} %{mcoff:-k}" diff --git a/gcc/config/sparc/lynx.h b/gcc/config/sparc/lynx.h new file mode 100755 index 0000000..99b319a --- /dev/null +++ b/gcc/config/sparc/lynx.h @@ -0,0 +1,53 @@ +/* Definitions for SPARC running LynxOS. + 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. */ + +#include <sparc/sparc.h> + +#undef ASM_OUTPUT_IDENT +#undef SELECT_SECTION +#undef SELECT_RTX_SECTION + +#define BSS_SECTION_ASM_OP ".section\t\".bss\"" + +#include <lynx.h> + +/* ??? Must redefine to get sparclite and v8 defines. Can this be done + differently? */ + +#undef CPP_SPEC +#define CPP_SPEC "%{mthreads:-D_MULTITHREADED} \ + %{mposix:-D_POSIX_SOURCE} \ + %{msystem-v:-I/usr/include_v} \ + %(cpp_cpu)" + +/* Names to predefine in the preprocessor for this target machine. */ + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dunix -Dsparc -DSPARC -DLynx -DLYNX -DIBITS32 -Asystem(unix) -Asystem(lynx) -Acpu(sparc) -Amachine(sparc)" + +#undef LINK_SPEC + +/* Sparc version of libc.a has references to libm.a (printf calls pow for + instance), so we must always link both. */ + +#undef LIB_SPEC +#define LIB_SPEC "%{mthreads:-L/lib/thread/} \ + %{msystem-v:-lc_v -lm_v -lc_v} \ + %{!msystem-v:%{mposix:-lc_p} -lc -lm -lc}" diff --git a/gcc/config/sparc/netbsd.h b/gcc/config/sparc/netbsd.h new file mode 100755 index 0000000..a512f41 --- /dev/null +++ b/gcc/config/sparc/netbsd.h @@ -0,0 +1,46 @@ +#include <sparc/sparc.h> + +/* Get generic NetBSD definitions. */ + +#include <netbsd.h> + +/* Names to predefine in the preprocessor for this target machine. */ + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dunix -Dsparc -D__NetBSD__ -Asystem(unix) -Asystem(NetBSD) -Acpu(sparc) -Amachine(sparc)" + +/* Make gcc agree with <machine/ansi.h> */ + +#undef SIZE_TYPE +#define SIZE_TYPE "unsigned int" + +#undef PTRDIFF_TYPE +#define PTRDIFF_TYPE "int" + +#undef WCHAR_TYPE +#define WCHAR_TYPE "int" + +#undef WCHAR_UNSIGNED +#define WCHAR_UNSIGNED 0 + +#undef WCHAR_TYPE_SIZE +#define WCHAR_TYPE_SIZE 32 + +/* This is BSD, so it wants DBX format. */ + +#define DBX_DEBUGGING_INFO + +/* This is the char to use for continuation (in case we need to turn + continuation back on). */ + +#define DBX_CONTIN_CHAR '?' + +/* Don't default to pcc-struct-return, because gcc is the only compiler, and + we want to retain compatibility with older gcc versions. */ +#undef DEFAULT_PCC_STRUCT_RETURN +#define DEFAULT_PCC_STRUCT_RETURN 0 + +/* Until they use ELF or something that handles dwarf2 unwinds + and initialization stuff better. */ +#define DWARF2_UNWIND_INFO 0 + diff --git a/gcc/config/sparc/pbd.h b/gcc/config/sparc/pbd.h new file mode 100755 index 0000000..459bffd --- /dev/null +++ b/gcc/config/sparc/pbd.h @@ -0,0 +1,184 @@ +/* Definitions of target machine for GNU compiler, Citicorp/TTI Unicom PBD + version (using GAS and COFF (encapsulated is unacceptable) ) + Copyright (C) 1990, 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. */ + +#include "sparc/sparc.h" + +/* Names to predefine in the preprocessor for this target machine. */ + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dsparc -DUnicomPBD -Dunix -D__GCC_NEW_VARARGS__ -Asystem(unix) -Acpu(sparc) -Amachine(sparc)" + +/* We want DBX format for use with gdb under COFF. */ + +#define DBX_DEBUGGING_INFO + +/* Generate calls to memcpy, memcmp and memset. */ + +#define TARGET_MEM_FUNCTIONS + +/* we use /lib/libp/lib* when profiling */ + +#undef LIB_SPEC +#define LIB_SPEC "%{p:-L/usr/lib/libp} %{pg:-L/usr/lib/libp} -lc" + + +/* Use crt1.o as a startup file and crtn.o as a closing file. */ +/* + * The loader directive file gcc.ifile defines how to merge the constructor + * sections into the data section. Also, since gas only puts out those + * sections in response to N_SETT stabs, and does not (yet) have a + * ".sections" directive, gcc.ifile also defines the list symbols + * __DTOR_LIST__ and __CTOR_LIST__. + * + * Finally, we must explicitly specify the file from libgcc.a that defines + * exit(), otherwise if the user specifies (for example) "-lc_s" on the + * command line, the wrong exit() will be used and global destructors will + * not get called . + */ + +#define STARTFILE_SPEC \ +"%{!r: gcc.ifile%s} %{pg:gcrt1.o%s}%{!pg:%{p:mcrt1.o%s}%{!p:crt1.o%s}} \ +%{!r:_exit.o%s}" + +#define ENDFILE_SPEC "crtn.o%s" + +/* cpp has to support a #sccs directive for the /usr/include files */ + +#define SCCS_DIRECTIVE + +/* LINK_SPEC is needed only for SunOS 4. */ + +#undef LINK_SPEC + +/* Although the gas we use can create .ctor and .dtor sections from N_SETT + stabs, it does not support section directives, so we need to have the loader + define the lists. + */ +#define CTOR_LISTS_DEFINED_EXTERNALLY + +/* similar to default, but allows for the table defined by ld with gcc.ifile. + nptrs is always 0. So we need to instead check that __DTOR_LIST__[1] != 0. + The old check is left in so that the same macro can be used if and when + a future version of gas does support section directives. */ + +#define DO_GLOBAL_DTORS_BODY {int nptrs = *(int *)__DTOR_LIST__; int i; \ + if (nptrs == -1 || (__DTOR_LIST__[0] == 0 && __DTOR_LIST__[1] != 0)) \ + for (nptrs = 0; __DTOR_LIST__[nptrs + 1] != 0; nptrs++); \ + for (i = nptrs; i >= 1; i--) \ + __DTOR_LIST__[i] (); } + +/* + * Here is an example gcc.ifile. I've tested it on PBD sparc + * systems. The NEXT(0x200000) works on just about all 386 and m68k systems, + * but can be reduced to any power of 2 that is >= NBPS (0x40000 on a pbd). + + SECTIONS { + .text BIND(0x41000200) BLOCK (0x200) : + { *(.init) *(.text) vfork = fork; *(.fini) } + + GROUP BIND( NEXT(0x200000) + ADDR(.text) + SIZEOF(.text)): + { .data : { __CTOR_LIST__ = . ; . += 4; *(.ctor) . += 4 ; + __DTOR_LIST__ = . ; . += 4; *(.dtor) . += 4 ; } + .bss : { } + } + } + */ + +/* The prefix to add to user-visible assembler symbols. */ + +#undef USER_LABEL_PREFIX +#define USER_LABEL_PREFIX "" + +/* fixes: */ +/* + * Internal labels are prefixed with a period. + */ + +/* 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'. */ + +#undef ASM_GENERATE_INTERNAL_LABEL + +#define ASM_GENERATE_INTERNAL_LABEL(LABEL,PREFIX,NUM) \ + sprintf (LABEL, "*.%s%d", PREFIX, NUM) + + +/* This is how to output an internal numbered label where + PREFIX is the class of label and NUM is the number within the class. */ + +#undef ASM_OUTPUT_INTERNAL_LABEL +#define ASM_OUTPUT_INTERNAL_LABEL(FILE,PREFIX,NUM) \ + fprintf (FILE, ".%s%d:\n", PREFIX, NUM) + +/* This is how to output an element of a case-vector that is relative. */ + +#undef ASM_OUTPUT_ADDR_DIFF_ELT +#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \ + fprintf (FILE, "\t.word .L%d-.L%d\n", VALUE, REL) + +/* This is how to output an element of a case-vector that is absolute. + (The 68000 does not use such vectors, + but we must define this macro anyway.) */ + +#undef ASM_OUTPUT_ADDR_VEC_ELT +#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \ + fprintf (FILE, "\t.word .L%d\n", VALUE) + +/* Output assembler code to FILE to increment profiler label # LABELNO + for profiling a function entry. */ + +#undef FUNCTION_PROFILER +#define FUNCTION_PROFILER(FILE, LABELNO) \ + fprintf (FILE, "\tsethi %%hi(.LP%d),%%o0\n\tcall mcount\n\tor %%lo(.LP%d),%%o0,%%o0\n", \ + (LABELNO), (LABELNO)) + +/* Output assembler code to FILE to initialize this source file's + basic block profiling info, if that has not already been done. */ + +#undef FUNCTION_BLOCK_PROFILER +#define FUNCTION_BLOCK_PROFILER(FILE, LABELNO) \ + fprintf (FILE, "\tsethi %%hi(.LPBX0),%%o0\n\tld [%%lo(.LPBX0)+%%o0],%%o1\n\ttst %%o1\n\tbne .LPY%d\n\tnop\n\tcall ___bb_init_func\n\tnop\n.LPY%d:\n", \ + (LABELNO), (LABELNO)) + +/* Output assembler code to FILE to increment the entry-count for + the BLOCKNO'th basic block in this source file. */ + +#undef BLOCK_PROFILER +#define BLOCK_PROFILER(FILE, BLOCKNO) \ +{ \ + int blockn = (BLOCKNO); \ + fprintf (FILE, "\tsethi %%hi(.LPBX2+%d),%%g1\n\tld [%%lo(.LPBX2+%d)+%%g1],%%g2\n\ +\tadd %%g2,1,%%g2\n\tst %%g2,[%%lo(.LPBX2+%d)+%%g1]\n", \ + 4 * blockn, 4 * blockn, 4 * blockn); \ + CC_STATUS_INIT; /* We have clobbered %g1. Also %g2. */ \ +} +/* This is needed for SunOS 4.0, and should not hurt for 3.2 + versions either. */ +#undef ASM_OUTPUT_SOURCE_LINE(file, line) +#define ASM_OUTPUT_SOURCE_LINE(file, line) \ + { static int sym_lineno = 1; \ + fprintf (file, ".stabn 68,0,%d,.LM%d\n.LM%d:\n", \ + line, sym_lineno, sym_lineno); \ + sym_lineno += 1; } + +#define ASM_INT_OP ".long " diff --git a/gcc/config/sparc/rtems.h b/gcc/config/sparc/rtems.h new file mode 100755 index 0000000..1ab0a42 --- /dev/null +++ b/gcc/config/sparc/rtems.h @@ -0,0 +1,35 @@ +/* Definitions for rtems targeting a SPARC using a.out. + Copyright (C) 1996, 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. */ + +#include "sparc/aout.h" + +/* Specify predefined symbols in preprocessor. */ + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dsparc -D__GCC_NEW_VARARGS__ -Drtems -D__rtems__ \ + -Asystem(rtems) -Acpu(sparc) -Amachine(sparc)" + +/* Generate calls to memcpy, memcmp and memset. */ +#ifndef TARGET_MEM_FUNCTIONS +#define TARGET_MEM_FUNCTIONS +#endif + +/* end of sparc/rtems.h */ diff --git a/gcc/config/sparc/sol2-c1.asm b/gcc/config/sparc/sol2-c1.asm new file mode 100755 index 0000000..894a8c3 --- /dev/null +++ b/gcc/config/sparc/sol2-c1.asm @@ -0,0 +1,110 @@ +! crt1.s for sparc & sparcv9 (SunOS 5) + +! Copyright (C) 1992 Free Software Foundation, Inc. +! Written By David Vinayak Henkel-Wallace, June 1992 +! +! This file is free software; you can redistribute it and/or modify it +! under the terms of the GNU General Public License as published by the +! Free Software Foundation; either version 2, or (at your option) any +! later version. +! +! In addition to the permissions in the GNU General Public License, the +! Free Software Foundation gives you unlimited permission to link the +! compiled version of this file with other programs, and to distribute +! those programs without any restriction coming from the use of this +! file. (The General Public License restrictions do apply in other +! respects; for example, they cover modification of the file, and +! distribution when not linked into another program.) +! +! This file is distributed in the hope that it will be useful, but +! WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +! General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with this program; see the file COPYING. If not, write to +! the Free Software Foundation, 59 Temple Place - Suite 330, +! Boston, MA 02111-1307, USA. +! +! As a special exception, if you link this library with files +! compiled with GCC to produce an executable, this does not cause +! the resulting executable to be covered by the GNU General Public License. +! This exception does not however invalidate any other reasons why +! the executable file might be covered by the GNU General Public License. +! + +! This file takes control of the process from the kernel, as specified +! in section 3 of the SVr4 ABI. +! This file is the first thing linked into any executable. + +#ifdef __sparcv9 +#define CPTRSIZE 8 +#define CPTRSHIFT 3 +#define STACK_BIAS 2047 +#define ldn ldx +#define stn stx +#define setn(s, scratch, dst) setx s, scratch, dst +#else +#define CPTRSIZE 4 +#define CPTRSHIFT 2 +#define STACK_BIAS 0 +#define ldn ld +#define stn st +#define setn(s, scratch, dst) set s, dst +#endif + + .section ".text" + .proc 022 + .global _start + +_start: + mov 0, %fp ! Mark bottom frame pointer + ldn [%sp + (16 * CPTRSIZE) + STACK_BIAS], %l0 ! argc + add %sp, (17 * CPTRSIZE) + STACK_BIAS, %l1 ! argv + + ! Leave some room for a call. Sun leaves 32 octets (to sit on + ! a cache line?) so we do too. +#ifdef __sparcv9 + sub %sp, 48, %sp +#else + sub %sp, 32, %sp +#endif + + ! %g1 may contain a function to be registered w/atexit + orcc %g0, %g1, %g0 +#ifdef __sparcv9 + be %xcc, .nope +#else + be .nope +#endif + mov %g1, %o0 + call atexit + nop +.nope: + ! Now make sure constructors and destructors are handled. + setn(_fini, %o1, %o0) + call atexit, 1 + nop + call _init, 0 + nop + + ! We ignore the auxiliary vector; there is no defined way to + ! access those data anyway. Instead, go straight to main: + mov %l0, %o0 ! argc + mov %l1, %o1 ! argv + ! Skip argc words past argv, to env: + sll %l0, CPTRSHIFT, %o2 + add %o2, CPTRSIZE, %o2 + add %l1, %o2, %o2 ! env + setn(_environ, %o4, %o3) + stn %o2, [%o3] ! *_environ + call main, 4 + nop + call exit, 0 + nop + call _exit, 0 + nop + ! We should never get here. + + .type _start,#function + .size _start,.-_start diff --git a/gcc/config/sparc/sol2-ci.asm b/gcc/config/sparc/sol2-ci.asm new file mode 100755 index 0000000..3dc793c --- /dev/null +++ b/gcc/config/sparc/sol2-ci.asm @@ -0,0 +1,68 @@ +! crti.s for solaris 2.0. + +! Copyright (C) 1992 Free Software Foundation, Inc. +! Written By David Vinayak Henkel-Wallace, June 1992 +! +! This file is free software; you can redistribute it and/or modify it +! under the terms of the GNU General Public License as published by the +! Free Software Foundation; either version 2, or (at your option) any +! later version. +! +! In addition to the permissions in the GNU General Public License, the +! Free Software Foundation gives you unlimited permission to link the +! compiled version of this file with other programs, and to distribute +! those programs without any restriction coming from the use of this +! file. (The General Public License restrictions do apply in other +! respects; for example, they cover modification of the file, and +! distribution when not linked into another program.) +! +! This file is distributed in the hope that it will be useful, but +! WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +! General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with this program; see the file COPYING. If not, write to +! the Free Software Foundation, 59 Temple Place - Suite 330, +! Boston, MA 02111-1307, USA. +! +! As a special exception, if you link this library with files +! compiled with GCC to produce an executable, this does not cause +! the resulting executable to be covered by the GNU General Public License. +! This exception does not however invalidate any other reasons why +! the executable file might be covered by the GNU General Public License. +! + +! This file just make a stack frame for the contents of the .fini and +! .init sections. Users may put any desired instructions in those +! sections. + +! This file is linked in before the Values-Xx.o files and also before +! crtbegin, with which perhaps it should be merged. + + .file "crti.s" + + .section ".init" + .proc 022 + .global _init + .type _init,#function + .align 4 +_init: +#ifdef __sparcv9 + save %sp, -176, %sp +#else + save %sp, -96, %sp +#endif + + + .section ".fini" + .proc 022 + .global _fini + .type _fini,#function + .align 4 +_fini: +#ifdef __sparcv9 + save %sp, -176, %sp +#else + save %sp, -96, %sp +#endif diff --git a/gcc/config/sparc/sol2-cn.asm b/gcc/config/sparc/sol2-cn.asm new file mode 100755 index 0000000..49e070f --- /dev/null +++ b/gcc/config/sparc/sol2-cn.asm @@ -0,0 +1,54 @@ +! crtn.s for solaris 2.0. + +! Copyright (C) 1992 Free Software Foundation, Inc. +! Written By David Vinayak Henkel-Wallace, June 1992 +! +! This file is free software; you can redistribute it and/or modify it +! under the terms of the GNU General Public License as published by the +! Free Software Foundation; either version 2, or (at your option) any +! later version. +! +! In addition to the permissions in the GNU General Public License, the +! Free Software Foundation gives you unlimited permission to link the +! compiled version of this file with other programs, and to distribute +! those programs without any restriction coming from the use of this +! file. (The General Public License restrictions do apply in other +! respects; for example, they cover modification of the file, and +! distribution when not linked into another program.) +! +! This file is distributed in the hope that it will be useful, but +! WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +! General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with this program; see the file COPYING. If not, write to +! the Free Software Foundation, 59 Temple Place - Suite 330, +! Boston, MA 02111-1307, USA. +! +! As a special exception, if you link this library with files +! compiled with GCC to produce an executable, this does not cause +! the resulting executable to be covered by the GNU General Public License. +! This exception does not however invalidate any other reasons why +! the executable file might be covered by the GNU General Public License. +! + +! This file just makes sure that the .fini and .init sections do in +! fact return. Users may put any desired instructions in those sections. +! This file is the last thing linked into any executable. + + .file "crtn.s" + + .section ".init" + .align 4 + + ret + restore + + .section ".fini" + .align 4 + + ret + restore + +! Th-th-th-that is all folks! diff --git a/gcc/config/sparc/sol2-g1.asm b/gcc/config/sparc/sol2-g1.asm new file mode 100755 index 0000000..b9d8788 --- /dev/null +++ b/gcc/config/sparc/sol2-g1.asm @@ -0,0 +1,88 @@ +! gcrt1.s for solaris 2.0. + +! Copyright (C) 1992 Free Software Foundation, Inc. +! Written By David Vinayak Henkel-Wallace, June 1992 +! +! This file is free software; you can redistribute it and/or modify it +! under the terms of the GNU General Public License as published by the +! Free Software Foundation; either version 2, or (at your option) any +! later version. +! +! In addition to the permissions in the GNU General Public License, the +! Free Software Foundation gives you unlimited permission to link the +! compiled version of this file with other programs, and to distribute +! those programs without any restriction coming from the use of this +! file. (The General Public License restrictions do apply in other +! respects; for example, they cover modification of the file, and +! distribution when not linked into another program.) +! +! This file is distributed in the hope that it will be useful, but +! WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +! General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with this program; see the file COPYING. If not, write to +! the Free Software Foundation, 59 Temple Place - Suite 330, +! Boston, MA 02111-1307, USA. +! +! As a special exception, if you link this library with files +! compiled with GCC to produce an executable, this does not cause +! the resulting executable to be covered by the GNU General Public License. +! This exception does not however invalidate any other reasons why +! the executable file might be covered by the GNU General Public License. +! + +! This file takes control of the process from the kernel, as specified +! in section 3 of the SVr4 ABI. +! This file is the first thing linked into any executable. + + .section ".text" + .proc 022 + .global _start + +_start: + mov 0, %fp ! Mark bottom frame pointer + ld [%sp + 64], %l0 ! argc + add %sp, 68, %l1 ! argv + + ! Leave some room for a call. Sun leaves 32 octets (to sit on + ! a cache line?) so we do too. + sub %sp, 32, %sp + + ! %g1 may contain a function to be registered w/atexit + orcc %g0, %g1, %g0 + be .nope + mov %g1, %o0 + call atexit + nop +.nope: + ! Now make sure constructors and destructors are handled. + set _fini, %o0 + call atexit, 1 + nop + call _init, 0 + nop + + ! We ignore the auxiliary vector; there's no defined way to + ! access those data anyway. Instead, go straight to main: + mov %l0, %o0 ! argc + mov %l1, %o1 ! argv + set ___Argv, %o3 + st %o1, [%o3] ! *___Argv + ! Skip argc words past argv, to env: + sll %l0, 2, %o2 + add %o2, 4, %o2 + add %l1, %o2, %o2 ! env + set _environ, %o3 + st %o2, [%o3] ! *_environ + call main, 4 + nop + call exit, 0 + nop + call _exit, 0 + nop + ! We should never get here. + + .type _start,#function + .size _start,.-_start diff --git a/gcc/config/sparc/sol2-sld-64.h b/gcc/config/sparc/sol2-sld-64.h new file mode 100755 index 0000000..c2518d8 --- /dev/null +++ b/gcc/config/sparc/sol2-sld-64.h @@ -0,0 +1,363 @@ +/* Definitions of target machine for GNU compiler, for 64-bit SPARC + running Solaris 2 using the system linker. */ + +#define SPARC_BI_ARCH + +#include "sparc/sol2.h" + +/* At least up through Solaris 2.6, + the system linker does not work with DWARF or DWARF2, + since it does not have working support for relocations + to unaligned data. */ + +#define LINKER_DOES_NOT_WORK_WITH_DWARF2 + +/* A 64 bit v9 compiler with stack-bias */ + +#if TARGET_CPU_DEFAULT == TARGET_CPU_v9 || TARGET_CPU_DEFAULT == TARGET_CPU_ultrasparc +#undef TARGET_DEFAULT +#define TARGET_DEFAULT \ + (MASK_V9 + MASK_PTR64 + MASK_64BIT /* + MASK_HARD_QUAD */ + \ + MASK_STACK_BIAS + MASK_APP_REGS + MASK_EPILOGUE + MASK_FPU) +#endif + +/* The default code model. */ +#undef SPARC_DEFAULT_CMODEL +#define SPARC_DEFAULT_CMODEL CM_MEDANY + +#undef LONG_DOUBLE_TYPE_SIZE +#define LONG_DOUBLE_TYPE_SIZE 128 + +#undef ASM_CPU32_DEFAULT_SPEC +#define ASM_CPU32_DEFAULT_SPEC "" +#undef ASM_CPU64_DEFAULT_SPEC +#define ASM_CPU64_DEFAULT_SPEC "-xarch=v9" + +#if TARGET_CPU_DEFAULT == TARGET_CPU_v9 +#undef CPP_CPU64_DEFAULT_SPEC +#define CPP_CPU64_DEFAULT_SPEC "" +#undef ASM_CPU32_DEFAULT_SPEC +#define ASM_CPU32_DEFAULT_SPEC "-xarch=v8plus" +#endif +#if TARGET_CPU_DEFAULT == TARGET_CPU_ultrasparc +#undef CPP_CPU64_DEFAULT_SPEC +#define CPP_CPU64_DEFAULT_SPEC "" +#undef ASM_CPU32_DEFAULT_SPEC +#define ASM_CPU32_DEFAULT_SPEC "-xarch=v8plusa" +#undef ASM_CPU64_DEFAULT_SPEC +#define ASM_CPU64_DEFAULT_SPEC "-xarch=v9a" +#endif + +/* The sun bundled assembler doesn't accept -Yd, (and neither does gas). + It's safe to pass -s always, even if -g is not used. */ +#undef ASM_SPEC +#define ASM_SPEC "\ +%{v:-V} %{Qy:} %{!Qn:-Qy} %{n} %{T} %{Ym,*} %{Wa,*:%*} -s \ +%{fpic:-K PIC} %{fPIC:-K PIC} \ +%(asm_cpu)\ +" + +#if DEFAULT_ARCH32_P +#define DEF_ARCH32_SPEC(__str) "%{!m64:" __str "}" +#define DEF_ARCH64_SPEC(__str) "%{m64:" __str "}" +#else +#define DEF_ARCH32_SPEC(__str) "%{m32:" __str "}" +#define DEF_ARCH64_SPEC(__str) "%{!m32:" __str "}" +#endif + +#undef CPP_CPU_SPEC +#define CPP_CPU_SPEC "\ +%{mcypress:} \ +%{msparclite:-D__sparclite__} \ +%{mf930:-D__sparclite__} %{mf934:-D__sparclite__} \ +%{mv8:" DEF_ARCH32_SPEC("-D__sparcv8") "} \ +%{msupersparc:-D__supersparc__ " DEF_ARCH32_SPEC("-D__sparcv8") "} \ +%{mcpu=sparclet:-D__sparclet__} %{mcpu=tsc701:-D__sparclet__} \ +%{mcpu=sparclite:-D__sparclite__} \ +%{mcpu=f930:-D__sparclite__} %{mcpu=f934:-D__sparclite__} \ +%{mcpu=v8:" DEF_ARCH32_SPEC("-D__sparcv8") "} \ +%{mcpu=supersparc:-D__supersparc__ " DEF_ARCH32_SPEC("-D__sparcv8") "} \ +%{mcpu=v9:" DEF_ARCH32_SPEC("-D__sparcv8") "} \ +%{mcpu=ultrasparc:" DEF_ARCH32_SPEC("-D__sparcv8") "} \ +%{!mcpu*:%{!mcypress:%{!msparclite:%{!mf930:%{!mf934:%{!mv8:%{!msupersparc:%(cpp_cpu_default)}}}}}}} \ +" + +#undef ASM_CPU_SPEC +#define ASM_CPU_SPEC "\ +%{mcpu=ultrasparc:" DEF_ARCH32_SPEC("-xarch=v8plusa") DEF_ARCH64_SPEC("-xarch=v9a") "} \ +%{mcpu=v9:" DEF_ARCH32_SPEC("-xarch=v8plus") DEF_ARCH64_SPEC("-xarch=v9") "} \ +%{!mcpu=ultrasparc:%{!mcpu=v9:%{mcpu*:" DEF_ARCH32_SPEC("-xarch=v8") DEF_ARCH64_SPEC("-xarch=v9") "}}} \ +%{!mcpu*:%(asm_cpu_default)} \ +" + +#define STARTFILE_SPEC32 "\ +%{ansi:values-Xc.o%s} \ +%{!ansi: \ + %{traditional:values-Xt.o%s} \ + %{!traditional:values-Xa.o%s}}" + +#define STARTFILE_SPEC64 "\ +%{ansi:/usr/lib/sparcv9/values-Xc.o%s} \ +%{!ansi: \ + %{traditional:/usr/lib/sparcv9/values-Xt.o%s} \ + %{!traditional:/usr/lib/sparcv9/values-Xa.o%s}}" + +#ifdef SPARC_BI_ARCH + +#if DEFAULT_ARCH32_P +#define STARTFILE_ARCH_SPEC "\ +%{m32:" STARTFILE_SPEC32 "} \ +%{m64:" STARTFILE_SPEC64 "} \ +%{!m32:%{!m64:" STARTFILE_SPEC32 "}}" +#else +#define STARTFILE_ARCH_SPEC "\ +%{m32:" STARTFILE_SPEC32 "} \ +%{m64:" STARTFILE_SPEC64 "} \ +%{!m32:%{!m64:" STARTFILE_SPEC64 "}}" +#endif + +#else /* !SPARC_BI_ARCH */ + +/* In this case we define MD_STARTFILE_PREFIX to /usr/lib/sparcv9/ */ +#define STARTFILE_ARCH_SPEC STARTFILE_SPEC32 + +#endif /* !SPARC_BI_ARCH */ + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC "%{!shared: \ + %{!symbolic: \ + %{p:mcrt1.o%s} \ + %{!p: \ + %{pg:gcrt1.o%s gmon.o%s} \ + %{!pg:crt1.o%s}}}} \ + crti.o%s" STARTFILE_ARCH_SPEC " \ + crtbegin.o%s" + +#ifdef SPARC_BI_ARCH + +#undef CPP_CPU_DEFAULT_SPEC +#define CPP_CPU_DEFAULT_SPEC \ +(DEFAULT_ARCH32_P ? "\ +%{m64:" CPP_CPU64_DEFAULT_SPEC "} \ +%{!m64:" CPP_CPU32_DEFAULT_SPEC "} \ +" : "\ +%{m32:" CPP_CPU32_DEFAULT_SPEC "} \ +%{!m32:" CPP_CPU64_DEFAULT_SPEC "} \ +") + +#undef ASM_CPU_DEFAULT_SPEC +#define ASM_CPU_DEFAULT_SPEC \ +(DEFAULT_ARCH32_P ? "\ +%{m64:" ASM_CPU64_DEFAULT_SPEC "} \ +%{!m64:" ASM_CPU32_DEFAULT_SPEC "} \ +" : "\ +%{m32:" ASM_CPU32_DEFAULT_SPEC "} \ +%{!m32:" ASM_CPU64_DEFAULT_SPEC "} \ +") + +#undef CPP_ARCH32_SPEC +#define CPP_ARCH32_SPEC "-D__SIZE_TYPE__=unsigned\\ int -D__PTRDIFF_TYPE__=int \ +-D__GCC_NEW_VARARGS__ -Acpu(sparc) -Amachine(sparc)" +#undef CPP_ARCH64_SPEC +#define CPP_ARCH64_SPEC "-D__SIZE_TYPE__=long\\ unsigned\\ int -D__PTRDIFF_TYPE__=long\\ int \ +-D__arch64__ -Acpu(sparc64) -Amachine(sparcv9) -D__sparcv9" + +#undef CPP_ARCH_SPEC +#define CPP_ARCH_SPEC "\ +%{m32:%(cpp_arch32)} \ +%{m64:%(cpp_arch64)} \ +%{!m32:%{!m64:%(cpp_arch_default)}} \ +" + +#undef ASM_ARCH_SPEC +#define ASM_ARCH_SPEC "" + +#undef ASM_ARCH32_SPEC +#define ASM_ARCH32_SPEC "" + +#undef ASM_ARCH64_SPEC +#define ASM_ARCH64_SPEC "" + +#undef ASM_ARCH_DEFAULT_SPEC +#define ASM_ARCH_DEFAULT_SPEC "" + +#undef SUBTARGET_EXTRA_SPECS +#define SUBTARGET_EXTRA_SPECS \ + { "link_arch32", LINK_ARCH32_SPEC }, \ + { "link_arch64", LINK_ARCH64_SPEC }, \ + { "link_arch_default", LINK_ARCH_DEFAULT_SPEC }, \ + { "link_arch", LINK_ARCH_SPEC }, + +/* This should be the same as in svr4.h, except with -R added. */ +#define LINK_ARCH32_SPEC \ + "%{G:-G} \ + %{YP,*} \ + %{R*} \ + %{compat-bsd: \ + %{!YP,*:%{p:-Y P,/usr/ucblib:/usr/ccs/lib/libp:/usr/lib/libp:/usr/ccs/lib:/usr/lib} \ + %{pg:-Y P,/usr/ucblib:/usr/ccs/lib/libp:/usr/lib/libp:/usr/ccs/lib:/usr/lib} \ + %{!p:%{!pg:-Y P,/usr/ucblib:/usr/ccs/lib:/usr/lib}}} \ + -R /usr/ucblib} \ + %{!compat-bsd: \ + %{!YP,*:%{p:-Y P,/usr/ccs/lib/libp:/usr/lib/libp:/usr/ccs/lib:/usr/lib} \ + %{pg:-Y P,/usr/ccs/lib/libp:/usr/lib/libp:/usr/ccs/lib:/usr/lib} \ + %{!p:%{!pg:-Y P,/usr/ccs/lib:/usr/lib}}}}" + +#define LINK_ARCH64_SPEC \ + "%{mcmodel=medlow:-M /usr/lib/ld/sparcv9/map.below4G} \ + %{G:-G} \ + %{YP,*} \ + %{R*} \ + %{compat-bsd: \ + %{!YP,*:%{p:-Y P,/usr/ucblib/sparcv9:/usr/lib/libp/sparcv9:/usr/lib/sparcv9} \ + %{pg:-Y P,/usr/ucblib/sparcv9:/usr/lib/libp/sparcv9:/usr/lib/sparcv9} \ + %{!p:%{!pg:-Y P,/usr/ucblib/sparcv9:/usr/lib/sparcv9}}} \ + -R /usr/ucblib} \ + %{!compat-bsd: \ + %{!YP,*:%{p:-Y P,/usr/lib/libp/sparcv9:/usr/lib/sparcv9} \ + %{pg:-Y P,/usr/lib/libp/sparcv9:/usr/lib/sparcv9} \ + %{!p:%{!pg:-Y P,/usr/lib/sparcv9}}}}" + +#define LINK_ARCH_SPEC "\ +%{m32:%(link_arch32)} \ +%{m64:%(link_arch64)} \ +%{!m32:%{!m64:%(link_arch_default)}} \ +" + +#define LINK_ARCH_DEFAULT_SPEC \ +(DEFAULT_ARCH32_P ? LINK_ARCH32_SPEC : LINK_ARCH64_SPEC) + +#undef LINK_SPEC +#define LINK_SPEC \ + "%{h*} %{v:-V} \ + %{b} %{Wl,*:%*} \ + %{static:-dn -Bstatic} \ + %{shared:-G -dy %{!mimpure-text:-z text}} \ + %{symbolic:-Bsymbolic -G -dy -z text} \ + %(link_arch) \ + %{Qy:} %{!Qn:-Qy}" + +#undef CC1_SPEC +#if DEFAULT_ARCH32_P +#define CC1_SPEC "\ +%{sun4:} %{target:} \ +%{mcypress:-mcpu=cypress} \ +%{msparclite:-mcpu=sparclite} %{mf930:-mcpu=f930} %{mf934:-mcpu=f934} \ +%{mv8:-mcpu=v8} %{msupersparc:-mcpu=supersparc} \ +%{m64:-mptr64 -mcpu=v9 -mstack-bias -mno-v8plus} \ +" +#else +#define CC1_SPEC "\ +%{sun4:} %{target:} \ +%{mcypress:-mcpu=cypress} \ +%{msparclite:-mcpu=sparclite} %{mf930:-mcpu=f930} %{mf934:-mcpu=f934} \ +%{mv8:-mcpu=v8} %{msupersparc:-mcpu=supersparc} \ +%{m32:-mptr32 -mcpu=cypress -mno-stack-bias} \ +%{mv8plus:-m32 -mptr32 -mcpu=cypress -mno-stack-bias} \ +" +#endif + +#if DEFAULT_ARCH32_P +#define MULTILIB_DEFAULTS { "m32" } +#else +#define MULTILIB_DEFAULTS { "m64" } +#endif + +#else /* !SPARC_BI_ARCH */ + +/* + * This should be the same as in sol2-sld.h, except with "/sparcv9" + * appended to the paths and /usr/ccs/lib is no longer necessary + */ +#undef LINK_SPEC +#define LINK_SPEC \ + "%{h*} %{v:-V} \ + %{b} %{Wl,*:%*} \ + %{static:-dn -Bstatic} \ + %{shared:-G -dy %{!mimpure-text:-z text}} \ + %{symbolic:-Bsymbolic -G -dy -z text} \ + %{mcmodel=medlow:-M /usr/lib/ld/sparcv9/map.below4G} \ + %{G:-G} \ + %{YP,*} \ + %{R*} \ + %{compat-bsd: \ + %{!YP,*:%{p:-Y P,/usr/ucblib/sparcv9:/usr/lib/libp/sparcv9:/usr/lib/sparcv9} \ + %{pg:-Y P,/usr/ucblib/sparcv9:/usr/lib/libp/sparcv9:/usr/lib/sparcv9} \ + %{!p:%{!pg:-Y P,/usr/ucblib/sparcv9:/usr/lib/sparcv9}}} \ + -R /usr/ucblib} \ + %{!compat-bsd: \ + %{!YP,*:%{p:-Y P,/usr/lib/libp/sparcv9:/usr/lib/sparcv9} \ + %{pg:-Y P,/usr/lib/libp/sparcv9:/usr/lib/sparcv9} \ + %{!p:%{!pg:-Y P,/usr/lib/sparcv9}}}} \ + %{Qy:} %{!Qn:-Qy}" + +#undef MD_STARTFILE_PREFIX +#define MD_STARTFILE_PREFIX "/usr/lib/sparcv9/" + +#endif /* ! SPARC_BI_ARCH */ + +/* + * Attempt to turn on access permissions for the stack. + * + * This code must be defined when compiling gcc but not when compiling + * libgcc2.a, unless we're generating code for 64 bits SPARC + * + * _SC_STACK_PROT is only defined for post 2.6, but we want this code + * to run always. 2.6 can change the stack protection but has no way to + * query it. + * + */ + +#define TRANSFER_FROM_TRAMPOLINE \ +static int need_enable_exec_stack; \ + \ +static void check_enabling(void) __attribute__ ((constructor)); \ +static void check_enabling(void) \ +{ \ + extern long sysconf(int); \ + \ + int prot = (int) sysconf(515 /*_SC_STACK_PROT */); \ + if (prot != 7) \ + need_enable_exec_stack = 1; \ +} \ + \ +void \ +__enable_execute_stack (addr) \ + void *addr; \ +{ \ + if (!need_enable_exec_stack) \ + return; \ + else { \ + long size = getpagesize (); \ + long mask = ~(size-1); \ + char *page = (char *) (((long) addr) & mask); \ + char *end = (char *) ((((long) (addr + TRAMPOLINE_SIZE)) & mask) + size); \ + \ + /* 7 is PROT_READ | PROT_WRITE | PROT_EXEC */ \ + if (mprotect (page, end - page, 7) < 0) \ + perror ("mprotect of trampoline code"); \ + } \ +} + +/* A C statement (sans semicolon) to output an element in the table of + global constructors. */ +#undef ASM_OUTPUT_CONSTRUCTOR +#define ASM_OUTPUT_CONSTRUCTOR(FILE,NAME) \ + do { \ + ctors_section (); \ + fprintf (FILE, "\t%s\t ", TARGET_ARCH64 ? ASM_LONGLONG : INT_ASM_OP); \ + assemble_name (FILE, NAME); \ + fprintf (FILE, "\n"); \ + } while (0) + +/* A C statement (sans semicolon) to output an element in the table of + global destructors. */ +#undef ASM_OUTPUT_DESTRUCTOR +#define ASM_OUTPUT_DESTRUCTOR(FILE,NAME) \ + do { \ + dtors_section (); \ + fprintf (FILE, "\t%s\t ", TARGET_ARCH64 ? ASM_LONGLONG : INT_ASM_OP); \ + assemble_name (FILE, NAME); \ + fprintf (FILE, "\n"); \ + } while (0) + diff --git a/gcc/config/sparc/sol2-sld.h b/gcc/config/sparc/sol2-sld.h new file mode 100755 index 0000000..a824987 --- /dev/null +++ b/gcc/config/sparc/sol2-sld.h @@ -0,0 +1,11 @@ +/* Definitions of target machine for GNU compiler, for SPARC running Solaris 2 + using the system linker. */ + +#include "sparc/sol2.h" + +/* At least up through Solaris 2.6, + the system linker does not work with DWARF or DWARF2, + since it does not have working support for relocations + to unaligned data. */ + +#define LINKER_DOES_NOT_WORK_WITH_DWARF2 diff --git a/gcc/config/sparc/sol2.h b/gcc/config/sparc/sol2.h new file mode 100755 index 0000000..9274f9d --- /dev/null +++ b/gcc/config/sparc/sol2.h @@ -0,0 +1,236 @@ +/* Definitions of target machine for GNU compiler, for SPARC running Solaris 2 + Copyright 1992, 1995, 1996, 1997, 1998 Free Software Foundation, Inc. + Contributed by Ron Guilmette (rfg@netcom.com). + Additional changes by David V. Henkel-Wallace (gumby@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. */ + +/* Supposedly the same as vanilla sparc svr4, except for the stuff below: */ +#include "sparc/sysv4.h" + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES \ +"-Dsparc -Dsun -Dunix -D__svr4__ -D__SVR4 \ +-Asystem(unix) -Asystem(svr4)" + +#undef CPP_SUBTARGET_SPEC +#define CPP_SUBTARGET_SPEC "\ +%{pthreads:-D_REENTRANT -D_PTHREADS} \ +%{!pthreads:%{threads:-D_REENTRANT -D_SOLARIS_THREADS}} \ +%{compat-bsd:-iwithprefixbefore ucbinclude -I/usr/ucbinclude} \ +" + +/* The sun bundled assembler doesn't accept -Yd, (and neither does gas). + It's safe to pass -s always, even if -g is not used. */ +#undef ASM_SPEC +#define ASM_SPEC "\ +%{v:-V} %{Qy:} %{!Qn:-Qy} %{n} %{T} %{Ym,*} %{Wa,*:%*} -s \ +%{fpic:-K PIC} %{fPIC:-K PIC} \ +%(asm_cpu) \ +" + +/* This is here rather than in sparc.h because it's not known what + other assemblers will accept. */ +#if TARGET_CPU_DEFAULT == TARGET_CPU_v9 +#undef ASM_CPU_DEFAULT_SPEC +#define ASM_CPU_DEFAULT_SPEC "-xarch=v8plus" +#endif +#if TARGET_CPU_DEFAULT == TARGET_CPU_ultrasparc +#undef ASM_CPU_DEFAULT_SPEC +#define ASM_CPU_DEFAULT_SPEC "-xarch=v8plusa" +#endif +#undef ASM_CPU_SPEC +#define ASM_CPU_SPEC "\ +%{mcpu=v8plus:-xarch=v8plus} \ +%{mcpu=ultrasparc:-xarch=v8plusa} \ +%{!mcpu*:%(asm_cpu_default)} \ +" + +/* However it appears that Solaris 2.0 uses the same reg numbering as + the old BSD-style system did. */ + +#undef DBX_REGISTER_NUMBER +/* Same as sparc.h */ +#define DBX_REGISTER_NUMBER(REGNO) \ + (TARGET_FLAT && REGNO == FRAME_POINTER_REGNUM ? 31 : REGNO) + +/* We use stabs-in-elf for debugging, because that is what the native + toolchain uses. */ +#undef PREFERRED_DEBUGGING_TYPE +#define PREFERRED_DEBUGGING_TYPE DBX_DEBUG + +/* The Solaris 2 assembler uses .skip, not .zero, so put this back. */ +#undef ASM_OUTPUT_SKIP +#define ASM_OUTPUT_SKIP(FILE,SIZE) \ + fprintf (FILE, "\t.skip %u\n", (SIZE)) + +/* Use .uahalf/.uaword so packed structure members don't generate + assembler errors when using the native assembler. */ +#undef ASM_SHORT +#define ASM_SHORT ".uahalf" +#undef ASM_LONG +#define ASM_LONG ".uaword" + +/* This is how to output a definition of an internal numbered label where + PREFIX is the class of label and NUM is the number within the class. */ + +#undef ASM_OUTPUT_INTERNAL_LABEL +#define ASM_OUTPUT_INTERNAL_LABEL(FILE,PREFIX,NUM) \ + fprintf (FILE, ".L%s%d:\n", PREFIX, NUM) + +/* This is how to output a reference to an internal numbered label where + PREFIX is the class of label and NUM is the number within the class. */ + +#undef ASM_OUTPUT_INTERNAL_LABELREF +#define ASM_OUTPUT_INTERNAL_LABELREF(FILE,PREFIX,NUM) \ + fprintf (FILE, ".L%s%d", PREFIX, 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'. */ + +#undef ASM_GENERATE_INTERNAL_LABEL +#define ASM_GENERATE_INTERNAL_LABEL(LABEL,PREFIX,NUM) \ + sprintf ((LABEL), "*.L%s%ld", (PREFIX), (long)(NUM)) + + +/* We don't use the standard svr4 STARTFILE_SPEC because it's wrong for us. + We don't use the standard LIB_SPEC only because we don't yet support c++ */ + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC "%{!shared: \ + %{!symbolic: \ + %{p:mcrt1.o%s} \ + %{!p: \ + %{pg:gcrt1.o%s gmon.o%s} \ + %{!pg:crt1.o%s}}}} \ + crti.o%s \ + %{ansi:values-Xc.o%s} \ + %{!ansi: \ + %{traditional:values-Xt.o%s} \ + %{!traditional:values-Xa.o%s}} \ + crtbegin.o%s" + +/* ??? Note: in order for -compat-bsd to work fully, + we must somehow arrange to fixincludes /usr/ucbinclude + and put the result in $(libsubdir)/ucbinclude. */ + +#undef LIB_SPEC +#define LIB_SPEC \ + "%{compat-bsd:-lucb -lsocket -lnsl -lelf -laio} \ + %{!shared:\ + %{!symbolic:\ + %{pthreads:-lpthread} \ + %{!pthreads:%{threads:-lthread}} \ + -lc}}" + +#undef ENDFILE_SPEC +#define ENDFILE_SPEC "crtend.o%s crtn.o%s" + +/* This should be the same as in svr4.h, except with -R added. */ +#undef LINK_SPEC +#define LINK_SPEC \ + "%{h*} %{v:-V} \ + %{b} %{Wl,*:%*} \ + %{static:-dn -Bstatic} \ + %{shared:-G -dy %{!mimpure-text:-z text}} \ + %{symbolic:-Bsymbolic -G -dy -z text} \ + %{G:-G} \ + %{YP,*} \ + %{R*} \ + %{compat-bsd: \ + %{!YP,*:%{p:-Y P,/usr/ucblib:/usr/ccs/lib/libp:/usr/lib/libp:/usr/ccs/lib:/usr/lib} \ + %{pg:-Y P,/usr/ucblib:/usr/ccs/lib/libp:/usr/lib/libp:/usr/ccs/lib:/usr/lib} \ + %{!p:%{!pg:-Y P,/usr/ucblib:/usr/ccs/lib:/usr/lib}}} \ + -R /usr/ucblib} \ + %{!compat-bsd: \ + %{!YP,*:%{p:-Y P,/usr/ccs/lib/libp:/usr/lib/libp:/usr/ccs/lib:/usr/lib} \ + %{pg:-Y P,/usr/ccs/lib/libp:/usr/lib/libp:/usr/ccs/lib:/usr/lib} \ + %{!p:%{!pg:-Y P,/usr/ccs/lib:/usr/lib}}}} \ + %{Qy:} %{!Qn:-Qy}" + +/* This defines which switch letters take arguments. + It is as in svr4.h but with -R added. */ + +#undef SWITCH_TAKES_ARG +#define SWITCH_TAKES_ARG(CHAR) \ + (DEFAULT_SWITCH_TAKES_ARG(CHAR) \ + || (CHAR) == 'R' \ + || (CHAR) == 'h' \ + || (CHAR) == 'x' \ + || (CHAR) == 'z') + +/* ??? This does not work in SunOS 4.x, so it is not enabled in sparc.h. + Instead, it is enabled here, because it does work under Solaris. */ +/* Define for support of TFmode long double and REAL_ARITHMETIC. + Sparc ABI says that long double is 4 words. */ +#define LONG_DOUBLE_TYPE_SIZE 128 + +/* But indicate that it isn't supported by the hardware. */ +#define WIDEST_HARDWARE_FP_SIZE 64 + +#define STDC_0_IN_SYSTEM_HEADERS + +#define MULDI3_LIBCALL "__mul64" +#define DIVDI3_LIBCALL "__div64" +#define UDIVDI3_LIBCALL "__udiv64" +#define MODDI3_LIBCALL "__rem64" +#define UMODDI3_LIBCALL "__urem64" + +#undef INIT_SUBTARGET_OPTABS +#define INIT_SUBTARGET_OPTABS \ + fixsfdi_libfunc = gen_rtx_SYMBOL_REF (Pmode, \ + TARGET_ARCH64 ? "__ftol" : "__ftoll"); \ + fixunssfdi_libfunc = gen_rtx_SYMBOL_REF (Pmode, \ + TARGET_ARCH64 ? "__ftoul" : "__ftoull"); \ + fixdfdi_libfunc = gen_rtx_SYMBOL_REF (Pmode, \ + TARGET_ARCH64 ? "__dtol" : "__dtoll"); \ + fixunsdfdi_libfunc = gen_rtx_SYMBOL_REF (Pmode, \ + TARGET_ARCH64 ? "__dtoul" : "__dtoull") + +/* No weird SPARC variants on Solaris */ +#undef TARGET_LIVE_G0 +#define TARGET_LIVE_G0 0 +#undef TARGET_BROKEN_SAVERESTORE +#define TARGET_BROKEN_SAVERESTORE 0 + +/* Solaris allows 64 bit out and global registers in 32 bit mode. + sparc_override_options will disable V8+ if not generating V9 code. */ +#undef TARGET_DEFAULT +#define TARGET_DEFAULT (MASK_APP_REGS + MASK_EPILOGUE + MASK_FPU + MASK_V8PLUS) + +/* Override MACHINE_STATE_{SAVE,RESTORE} because we have special + traps available which can get and set the condition codes + reliably. */ +#undef MACHINE_STATE_SAVE +#define MACHINE_STATE_SAVE(ID) \ + unsigned long int ms_flags, ms_saveret; \ + asm volatile("ta 0x20\n\t" \ + "mov %%g1, %0\n\t" \ + "mov %%g2, %1\n\t" \ + : "=r" (ms_flags), "=r" (ms_saveret)); + +#undef MACHINE_STATE_RESTORE +#define MACHINE_STATE_RESTORE(ID) \ + asm volatile("mov %0, %%g1\n\t" \ + "mov %1, %%g2\n\t" \ + "ta 0x21\n\t" \ + : /* no outputs */ \ + : "r" (ms_flags), "r" (ms_saveret)); + diff --git a/gcc/config/sparc/sp64-aout.h b/gcc/config/sparc/sp64-aout.h new file mode 100755 index 0000000..e3056df --- /dev/null +++ b/gcc/config/sparc/sp64-aout.h @@ -0,0 +1,38 @@ +/* Definitions of target machine for GNU compiler, for SPARC64, a.out. + Copyright (C) 1994, 1996, 1997, 1998 Free Software Foundation, Inc. + Contributed by Doug Evans, dje@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. */ + +#include "sparc/sparc.h" +#include "aoutos.h" + +#undef TARGET_VERSION +#define TARGET_VERSION fprintf (stderr, " (sparc64-aout)") + +#undef TARGET_DEFAULT +#define TARGET_DEFAULT \ + (MASK_V9 + MASK_PTR64 + MASK_64BIT + MASK_HARD_QUAD \ + + MASK_APP_REGS + MASK_EPILOGUE + MASK_FPU + MASK_STACK_BIAS) + +/* The only code model supported is Medium/Low. */ +#undef SPARC_DEFAULT_CMODEL +#define SPARC_DEFAULT_CMODEL CM_MEDLOW + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dsparc -Acpu(sparc) -Amachine(sparc)" diff --git a/gcc/config/sparc/sp64-elf.h b/gcc/config/sparc/sp64-elf.h new file mode 100755 index 0000000..a1e4ef0 --- /dev/null +++ b/gcc/config/sparc/sp64-elf.h @@ -0,0 +1,158 @@ +/* Definitions of target machine for GNU compiler, for SPARC64, ELF. + Copyright (C) 1994, 1995, 1996, 1997, 1998 Free Software Foundation, Inc. + Contributed by Doug Evans, dje@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. */ + +/* ??? We're taking the scheme of including another file and then overriding + the values we don't like a bit too far here. The alternative is to more or + less duplicate all of svr4.h, sparc/sysv4.h, and sparc/sol2.h here + (suitably cleaned up). */ + +#include "sparc/sol2.h" + +#undef TARGET_VERSION +#define TARGET_VERSION fprintf (stderr, " (sparc64-elf)") + +/* A 64 bit v9 compiler in a Medium/Anywhere code model environment. */ + +#undef TARGET_DEFAULT +#define TARGET_DEFAULT \ +(MASK_V9 + MASK_PTR64 + MASK_64BIT + MASK_HARD_QUAD \ + + MASK_APP_REGS + MASK_EPILOGUE + MASK_FPU + MASK_STACK_BIAS) + +#undef SPARC_DEFAULT_CMODEL +#define SPARC_DEFAULT_CMODEL CM_EMBMEDANY + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dsparc -D__ELF__ -Acpu(sparc) -Amachine(sparc)" + +/* __svr4__ is used by the C library (FIXME) */ +#undef CPP_SUBTARGET_SPEC +#define CPP_SUBTARGET_SPEC "-D__svr4__" + +#undef MD_EXEC_PREFIX +#undef MD_STARTFILE_PREFIX + +#undef ASM_SPEC +#define ASM_SPEC "\ +%{v:-V} -s %{fpic:-K PIC} %{fPIC:-K PIC} \ +%{mlittle-endian:-EL} \ +%(asm_cpu) %(asm_arch) \ +" + +/* This is taken from sol2.h. */ +#undef LINK_SPEC +#define LINK_SPEC "\ +%{v:-V} \ +%{mlittle-endian:-EL} \ +" + +/* We need something a little simpler for the embedded environment. + Profiling doesn't really work yet so we just copy the default. */ +#undef STARTFILE_SPEC +#define STARTFILE_SPEC "\ +%{!shared:%{pg:gcrt0.o%s}%{!pg:%{p:mcrt0.o%s}%{!p:crt0.o%s}}} \ +crtbegin.o%s \ +" + +#undef ENDFILE_SPEC +#define ENDFILE_SPEC "crtend.o%s" + +/* Use the default (for now). */ +#undef LIB_SPEC + +/* V9 chips can handle either endianness. */ +#undef SUBTARGET_SWITCHES +#define SUBTARGET_SWITCHES \ +{"big-endian", -MASK_LITTLE_ENDIAN}, \ +{"little-endian", MASK_LITTLE_ENDIAN}, + +#undef BYTES_BIG_ENDIAN +#define BYTES_BIG_ENDIAN (! TARGET_LITTLE_ENDIAN) + +#undef WORDS_BIG_ENDIAN +#define WORDS_BIG_ENDIAN (! TARGET_LITTLE_ENDIAN) + +/* ??? This should be 32 bits for v9 but what can we do? */ +#undef WCHAR_TYPE +#define WCHAR_TYPE "short unsigned int" + +#undef WCHAR_TYPE_SIZE +#define WCHAR_TYPE_SIZE 16 + +#undef LONG_DOUBLE_TYPE_SIZE +#define LONG_DOUBLE_TYPE_SIZE 128 + +/* The medium/anywhere code model practically requires us to put jump tables + in the text section as gcc is unable to distinguish LABEL_REF's of jump + tables from other label refs (when we need to). */ +/* But we now defer the tables to the end of the function, so we make + this 0 to not confuse the branch shortening code. */ +#undef JUMP_TABLES_IN_TEXT_SECTION +#define JUMP_TABLES_IN_TEXT_SECTION 0 + +/* System V Release 4 uses DWARF debugging info. + GDB doesn't support 64 bit stabs yet and the desired debug format is DWARF + anyway so it is the default. */ + +#define DWARF_DEBUGGING_INFO +#define DWARF2_DEBUGGING_INFO +#define DBX_DEBUGGING_INFO + +#undef PREFERRED_DEBUGGING_TYPE +#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG + +/* Stabs doesn't use this, and it confuses a simulator. */ +/* ??? Need to see what DWARF needs, if anything. */ +#undef ASM_IDENTIFY_GCC +#define ASM_IDENTIFY_GCC(FILE) + +/* Define the names of various pseudo-ops used by the Sparc/svr4 assembler. + ??? If ints are 64 bits then UNALIGNED_INT_ASM_OP (defined elsewhere) is + misnamed. These should all refer to explicit sizes (half/word/xword?), + anything other than short/int/long/etc. */ + +#define UNALIGNED_LONGLONG_ASM_OP ".uaxword" + +/* DWARF stuff. */ + +#define ASM_OUTPUT_DWARF_ADDR(FILE, LABEL) \ +do { \ + fprintf ((FILE), "\t%s\t", UNALIGNED_LONGLONG_ASM_OP); \ + assemble_name ((FILE), (LABEL)); \ + fprintf ((FILE), "\n"); \ +} while (0) + +#define ASM_OUTPUT_DWARF_ADDR_CONST(FILE, RTX) \ +do { \ + fprintf ((FILE), "\t%s\t", UNALIGNED_LONGLONG_ASM_OP); \ + output_addr_const ((FILE), (RTX)); \ + fputc ('\n', (FILE)); \ +} while (0) + +#define ASM_OUTPUT_DWARF2_ADDR_CONST(FILE, ADDR) \ + fprintf ((FILE), "\t%s\t%s", UNALIGNED_LONGLONG_ASM_OP, (ADDR)) + +/* ??? Not sure if this should be 4 or 8 bytes. 4 works for now. */ +#define ASM_OUTPUT_DWARF_REF(FILE, LABEL) \ +do { \ + fprintf ((FILE), "\t%s\t", UNALIGNED_INT_ASM_OP); \ + assemble_name ((FILE), (LABEL)); \ + fprintf ((FILE), "\n"); \ +} while (0) diff --git a/gcc/config/sparc/sp64-elf_020422.h b/gcc/config/sparc/sp64-elf_020422.h new file mode 100755 index 0000000..4e993dd --- /dev/null +++ b/gcc/config/sparc/sp64-elf_020422.h @@ -0,0 +1,155 @@ +/* Definitions of target machine for GNU compiler, for SPARC64, ELF. + Copyright (C) 1994, 1995, 1996, 1997, 1998, 2001 Free Software Foundation, Inc. + Contributed by Doug Evans, dje@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. */ + +/* ??? We're taking the scheme of including another file and then overriding + the values we don't like a bit too far here. The alternative is to more or + less duplicate all of svr4.h, sparc/sysv4.h, and sparc/sol2.h here + (suitably cleaned up). */ + +#include "sparc/sol2.h" + +#undef TARGET_VERSION +#define TARGET_VERSION fprintf (stderr, " (sparc64-elf)") + +/* A 64 bit v9 compiler in a Medium/Anywhere code model environment. */ + +#undef TARGET_DEFAULT +#define TARGET_DEFAULT \ +(MASK_V9 + MASK_PTR64 + MASK_64BIT + MASK_HARD_QUAD \ + + MASK_APP_REGS + MASK_EPILOGUE + MASK_FPU + MASK_STACK_BIAS) + +#undef SPARC_DEFAULT_CMODEL +#define SPARC_DEFAULT_CMODEL CM_EMBMEDANY + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dsparc -D__ELF__ -Acpu(sparc) -Amachine(sparc)" + +/* __svr4__ is used by the C library (FIXME) */ +#undef CPP_SUBTARGET_SPEC +#define CPP_SUBTARGET_SPEC "-D__svr4__" + +#undef MD_EXEC_PREFIX +#undef MD_STARTFILE_PREFIX + +#undef ASM_SPEC +#define ASM_SPEC "\ +%{v:-V} -s %{fpic:-K PIC} %{fPIC:-K PIC} \ +%{mlittle-endian:-EL} \ +%(asm_cpu) %(asm_arch) \ +" + +/* This is taken from sol2.h. */ +#undef LINK_SPEC +#define LINK_SPEC "\ +%{v:-V} \ +%{mlittle-endian:-EL} \ +" + +/* We need something a little simpler for the embedded environment. + Profiling doesn't really work yet so we just copy the default. */ +#undef STARTFILE_SPEC +#define STARTFILE_SPEC "\ +%{!shared:%{pg:gcrt0.o%s}%{!pg:%{p:mcrt0.o%s}%{!p:crt0.o%s}}} \ +crtbegin.o%s \ +" + +#undef ENDFILE_SPEC +#define ENDFILE_SPEC "crtend.o%s" + +/* Use the default (for now). */ +#undef LIB_SPEC + +/* V9 chips can handle either endianness. */ +#undef SUBTARGET_SWITCHES +#define SUBTARGET_SWITCHES \ +{"big-endian", -MASK_LITTLE_ENDIAN}, \ +{"little-endian", MASK_LITTLE_ENDIAN}, + +#undef BYTES_BIG_ENDIAN +#define BYTES_BIG_ENDIAN (! TARGET_LITTLE_ENDIAN) + +#undef WORDS_BIG_ENDIAN +#define WORDS_BIG_ENDIAN (! TARGET_LITTLE_ENDIAN) + +/* ??? This should be 32 bits for v9 but what can we do? */ +#undef WCHAR_TYPE +#define WCHAR_TYPE "short unsigned int" + +#undef WCHAR_TYPE_SIZE +#define WCHAR_TYPE_SIZE 16 + +#undef LONG_DOUBLE_TYPE_SIZE +#define LONG_DOUBLE_TYPE_SIZE 128 + +/* The medium/anywhere code model practically requires us to put jump tables + in the text section as gcc is unable to distinguish LABEL_REF's of jump + tables from other label refs (when we need to). */ +/* But we now defer the tables to the end of the function, so we make + this 0 to not confuse the branch shortening code. */ +#undef JUMP_TABLES_IN_TEXT_SECTION +#define JUMP_TABLES_IN_TEXT_SECTION 0 + +/* System V Release 4 uses DWARF debugging info. + GDB doesn't support 64 bit stabs yet and the desired debug format is DWARF + anyway so it is the default. */ + +#define DWARF_DEBUGGING_INFO +#define DWARF2_DEBUGGING_INFO +#define DBX_DEBUGGING_INFO + +#undef PREFERRED_DEBUGGING_TYPE +#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG + +/* Stabs doesn't use this, and it confuses a simulator. */ +/* ??? Need to see what DWARF needs, if anything. */ +#undef ASM_IDENTIFY_GCC +#define ASM_IDENTIFY_GCC(FILE) + +/* Define the names of various pseudo-ops used by the Sparc/svr4 assembler. + ??? If ints are 64 bits then UNALIGNED_INT_ASM_OP (defined elsewhere) is + misnamed. These should all refer to explicit sizes (half/word/xword?), + anything other than short/int/long/etc. */ + +#define UNALIGNED_LONGLONG_ASM_OP ".uaxword" + +/* DWARF stuff. */ + +#define ASM_OUTPUT_DWARF_ADDR(FILE, LABEL) \ +do { \ + fprintf ((FILE), "\t%s\t", UNALIGNED_LONGLONG_ASM_OP); \ + assemble_name ((FILE), (LABEL)); \ + fprintf ((FILE), "\n"); \ +} while (0) + +#define ASM_OUTPUT_DWARF_ADDR_CONST(FILE, RTX) \ +do { \ + fprintf ((FILE), "\t%s\t", UNALIGNED_LONGLONG_ASM_OP); \ + output_addr_const ((FILE), (RTX)); \ + fputc ('\n', (FILE)); \ +} while (0) + +/* ??? Not sure if this should be 4 or 8 bytes. 4 works for now. */ +#define ASM_OUTPUT_DWARF_REF(FILE, LABEL) \ +do { \ + fprintf ((FILE), "\t%s\t", UNALIGNED_INT_ASM_OP); \ + assemble_name ((FILE), (LABEL)); \ + fprintf ((FILE), "\n"); \ +} while (0) diff --git a/gcc/config/sparc/sp86x-aout.h b/gcc/config/sparc/sp86x-aout.h new file mode 100755 index 0000000..0860f8c --- /dev/null +++ b/gcc/config/sparc/sp86x-aout.h @@ -0,0 +1,60 @@ +/* Definitions of target machine for GNU compiler, for sparclite 86x w/o FPU. + 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. */ + +#include "sparc/sparc.h" + +#define HAVE_ATEXIT + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dsparc -Dsparclite86x -Acpu(sparc) -Amachine(sparc)" + +#undef TARGET_VERSION +#define TARGET_VERSION fprintf (stderr, " (sparclite 86x)"); + +/* Enable sparclite, app-regs and epilogue options. Do not enable the fpu. */ + +#undef TARGET_DEFAULT +#define TARGET_DEFAULT (MASK_SPARCLITE + MASK_APP_REGS + MASK_EPILOGUE) + +#undef SUBTARGET_SWITCHES +#define SUBTARGET_SWITCHES \ +{"big-endian", -MASK_LITTLE_ENDIAN}, \ +{"little-endian", MASK_LITTLE_ENDIAN}, + +#undef ASM_SPEC +#define ASM_SPEC "%{v:-v} %{mlittle-endian-data:--little-endian-data} %(asm_cpu)" + +/* US Software GOFAST library support. */ +#include "gofast.h" +#undef INIT_SUBTARGET_OPTABS +#define INIT_SUBTARGET_OPTABS INIT_GOFAST_OPTABS + +#undef LINK_SPEC +#define LINK_SPEC "%{v:-V}" + +#undef BYTES_BIG_ENDIAN +#define BYTES_BIG_ENDIAN (! TARGET_LITTLE_ENDIAN_DATA) +#undef WORDS_BIG_ENDIAN +#define WORDS_BIG_ENDIAN (! TARGET_LITTLE_ENDIAN_DATA) + +#define TARGET_LITTLE_ENDIAN_DATA (target_flags & MASK_LITTLE_ENDIAN) +#undef SUBTARGET_SWITCHES +#define SUBTARGET_SWITCHES \ + { "little-endian-data", MASK_LITTLE_ENDIAN }, diff --git a/gcc/config/sparc/sp86x-elf.h b/gcc/config/sparc/sp86x-elf.h new file mode 100755 index 0000000..d734b85 --- /dev/null +++ b/gcc/config/sparc/sp86x-elf.h @@ -0,0 +1,73 @@ +/* Definitions of target machine for GNU compiler, for sparclite 86x w/o FPU. + Copyright (C) 1998 Free Software Foundation, Inc. + Contributed by Stan Cox (scox@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. */ + +#include "sparc/elf.h" + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dsparc -Dsparclite86x -Acpu(sparc) -Amachine(sparc)" + +/* Default to dwarf2 in ELF. */ + +#define DWARF_DEBUGGING_INFO +#define DWARF2_DEBUGGING_INFO + +#undef PREFERRED_DEBUGGING_TYPE +#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG + +#undef TARGET_VERSION +#define TARGET_VERSION fprintf (stderr, " (sparclite 86x)"); + +/* Enable sparclite, app-regs and epilogue options. Do not enable the fpu. */ + +#undef TARGET_DEFAULT +#define TARGET_DEFAULT (MASK_SPARCLITE + MASK_APP_REGS + MASK_EPILOGUE) + +#undef SUBTARGET_SWITCHES +#define SUBTARGET_SWITCHES \ +{"big-endian", -MASK_LITTLE_ENDIAN}, \ +{"little-endian", MASK_LITTLE_ENDIAN}, + +#undef ASM_SPEC +#define ASM_SPEC "%{v:-V} %{mlittle-endian-data:--little-endian-data} %(asm_cpu)" + +/* US Software GOFAST library support. */ +#include "gofast.h" +#undef INIT_SUBTARGET_OPTABS +#define INIT_SUBTARGET_OPTABS INIT_GOFAST_OPTABS + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC "crti.o%s crtbegin.o%s" + +#undef LINK_SPEC +#define LINK_SPEC "%{v:-V} %{mlittle-endian-data:-EL}" + +#undef BYTES_BIG_ENDIAN +#define BYTES_BIG_ENDIAN (! TARGET_LITTLE_ENDIAN_DATA) +#undef WORDS_BIG_ENDIAN +#define WORDS_BIG_ENDIAN (! TARGET_LITTLE_ENDIAN_DATA) + +/* Use __main method of constructor invocation */ +#undef INIT_SECTION_ASM_OP + +#define TARGET_LITTLE_ENDIAN_DATA (target_flags & MASK_LITTLE_ENDIAN) +#undef SUBTARGET_SWITCHES +#define SUBTARGET_SWITCHES \ + { "little-endian-data", MASK_LITTLE_ENDIAN }, diff --git a/gcc/config/sparc/sparc.c b/gcc/config/sparc/sparc.c new file mode 100755 index 0000000..5ad6620 --- /dev/null +++ b/gcc/config/sparc/sparc.c @@ -0,0 +1,7532 @@ +/* Subroutines for insn-output.c for Sun SPARC. + Copyright (C) 1987, 88, 89, 92-98, 1999 Free Software Foundation, Inc. + Contributed by Michael Tiemann (tiemann@cygnus.com) + 64 bit SPARC V9 support by Michael Tiemann, Jim Wilson, and Doug Evans, + at Cygnus Support. + +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 "tree.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 "expr.h" +#include "recog.h" +#include "toplev.h" + +/* 1 if the caller has placed an "unimp" insn immediately after the call. + This is used in v8 code when calling a function that returns a structure. + v9 doesn't have this. Be careful to have this test be the same as that + used on the call. */ + +#define SKIP_CALLERS_UNIMP_P \ +(!TARGET_ARCH64 && current_function_returns_struct \ + && ! integer_zerop (DECL_SIZE (DECL_RESULT (current_function_decl))) \ + && (TREE_CODE (DECL_SIZE (DECL_RESULT (current_function_decl))) \ + == INTEGER_CST)) + +/* Global variables for machine-dependent things. */ + +/* Size of frame. Need to know this to emit return insns from leaf procedures. + ACTUAL_FSIZE is set by compute_frame_size() which is called during the + reload pass. This is important as the value is later used in insn + scheduling (to see what can go in a delay slot). + APPARENT_FSIZE is the size of the stack less the register save area and less + the outgoing argument area. It is used when saving call preserved regs. */ +static int apparent_fsize; +static int actual_fsize; + +/* Save the operands last given to a compare for use when we + generate a scc or bcc insn. */ + +rtx sparc_compare_op0, sparc_compare_op1; + +/* We may need an epilogue if we spill too many registers. + If this is non-zero, then we branch here for the epilogue. */ +static rtx leaf_label; + +#ifdef LEAF_REGISTERS + +/* Vector to say how input registers are mapped to output + registers. FRAME_POINTER_REGNUM cannot be remapped by + this function to eliminate it. You must use -fomit-frame-pointer + to get that. */ +char leaf_reg_remap[] = +{ 0, 1, 2, 3, 4, 5, 6, 7, + -1, -1, -1, -1, -1, -1, 14, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 8, 9, 10, 11, 12, 13, -1, 15, + + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100}; + +#endif + +/* Name of where we pretend to think the frame pointer points. + Normally, this is "%fp", but if we are in a leaf procedure, + this is "%sp+something". We record "something" separately as it may be + too big for reg+constant addressing. */ + +static char *frame_base_name; +static int frame_base_offset; + +static rtx pic_setup_code PROTO((void)); +static void sparc_init_modes PROTO((void)); +static int save_regs PROTO((FILE *, int, int, char *, + int, int, int)); +static int restore_regs PROTO((FILE *, int, int, char *, int, int)); +static void build_big_number PROTO((FILE *, int, char *)); +static int function_arg_slotno PROTO((const CUMULATIVE_ARGS *, + enum machine_mode, tree, int, int, + int *, int *)); + +static int supersparc_adjust_cost PROTO((rtx, rtx, rtx, int)); +static int hypersparc_adjust_cost PROTO((rtx, rtx, rtx, int)); +static int ultrasparc_adjust_cost PROTO((rtx, rtx, rtx, int)); + +static void sparc_output_addr_vec PROTO((rtx)); +static void sparc_output_addr_diff_vec PROTO((rtx)); +static void sparc_output_deferred_case_vectors PROTO((void)); + + +#ifdef DWARF2_DEBUGGING_INFO +extern char *dwarf2out_cfi_label (); +#endif + +/* Option handling. */ + +/* Code model option as passed by user. */ +char *sparc_cmodel_string; +/* Parsed value. */ +enum cmodel sparc_cmodel; + +/* Record alignment options as passed by user. */ +char *sparc_align_loops_string; +char *sparc_align_jumps_string; +char *sparc_align_funcs_string; + +/* Parsed values, as a power of two. */ +int sparc_align_loops; +int sparc_align_jumps; +int sparc_align_funcs; + +struct sparc_cpu_select sparc_select[] = +{ + /* switch name, tune arch */ + { (char *)0, "default", 1, 1 }, + { (char *)0, "-mcpu=", 1, 1 }, + { (char *)0, "-mtune=", 1, 0 }, + { 0, 0, 0, 0 } +}; + +/* CPU type. This is set from TARGET_CPU_DEFAULT and -m{cpu,tune}=xxx. */ +enum processor_type sparc_cpu; + +/* Validate and override various options, and do some machine dependent + initialization. */ + +void +sparc_override_options () +{ + static struct code_model { + char *name; + int value; + } cmodels[] = { + { "32", CM_32 }, + { "medlow", CM_MEDLOW }, + { "medmid", CM_MEDMID }, + { "medany", CM_MEDANY }, + { "embmedany", CM_EMBMEDANY }, + { 0, 0 } + }; + struct code_model *cmodel; + /* Map TARGET_CPU_DEFAULT to value for -m{arch,tune}=. */ + static struct cpu_default { + int cpu; + char *name; + } cpu_default[] = { + /* There must be one entry here for each TARGET_CPU value. */ + { TARGET_CPU_sparc, "cypress" }, + { TARGET_CPU_sparclet, "tsc701" }, + { TARGET_CPU_sparclite, "f930" }, + { TARGET_CPU_v8, "v8" }, + { TARGET_CPU_hypersparc, "hypersparc" }, + { TARGET_CPU_sparclite86x, "sparclite86x" }, + { TARGET_CPU_supersparc, "supersparc" }, + { TARGET_CPU_v9, "v9" }, + { TARGET_CPU_ultrasparc, "ultrasparc" }, + { 0, 0 } + }; + struct cpu_default *def; + /* Table of values for -m{cpu,tune}=. */ + static struct cpu_table { + char *name; + enum processor_type processor; + int disable; + int enable; + } cpu_table[] = { + { "v7", PROCESSOR_V7, MASK_ISA, 0 }, + { "cypress", PROCESSOR_CYPRESS, MASK_ISA, 0 }, + { "v8", PROCESSOR_V8, MASK_ISA, MASK_V8 }, + /* TI TMS390Z55 supersparc */ + { "supersparc", PROCESSOR_SUPERSPARC, MASK_ISA, MASK_V8 }, + { "sparclite", PROCESSOR_SPARCLITE, MASK_ISA, MASK_SPARCLITE }, + /* The Fujitsu MB86930 is the original sparclite chip, with no fpu. + The Fujitsu MB86934 is the recent sparclite chip, with an fpu. */ + { "f930", PROCESSOR_F930, MASK_ISA|MASK_FPU, MASK_SPARCLITE }, + { "f934", PROCESSOR_F934, MASK_ISA, MASK_SPARCLITE|MASK_FPU }, + { "hypersparc", PROCESSOR_HYPERSPARC, MASK_ISA, MASK_V8|MASK_FPU }, + { "sparclite86x", PROCESSOR_SPARCLITE86X, MASK_ISA|MASK_FPU, + MASK_SPARCLITE }, + { "sparclet", PROCESSOR_SPARCLET, MASK_ISA, MASK_SPARCLET }, + /* TEMIC sparclet */ + { "tsc701", PROCESSOR_TSC701, MASK_ISA, MASK_SPARCLET }, + { "v9", PROCESSOR_V9, MASK_ISA, MASK_V9 }, + /* TI ultrasparc */ + { "ultrasparc", PROCESSOR_ULTRASPARC, MASK_ISA, MASK_V9 }, + { 0, 0, 0, 0 } + }; + struct cpu_table *cpu; + struct sparc_cpu_select *sel; + int fpu; + +#ifndef SPARC_BI_ARCH + /* Check for unsupported architecture size. */ + if (! TARGET_64BIT != DEFAULT_ARCH32_P) + { + error ("%s is not supported by this configuration", + DEFAULT_ARCH32_P ? "-m64" : "-m32"); + } +#endif + + /* At the moment we don't allow different pointer size and architecture */ + if (! TARGET_64BIT != ! TARGET_PTR64) + { + error ("-mptr%d not allowed on -m%d", + TARGET_PTR64 ? 64 : 32, TARGET_64BIT ? 64 : 32); + if (TARGET_64BIT) + target_flags |= MASK_PTR64; + else + target_flags &= ~MASK_PTR64; + } + + /* Code model selection. */ + sparc_cmodel = SPARC_DEFAULT_CMODEL; + +#ifdef SPARC_BI_ARCH + if (TARGET_ARCH32) + sparc_cmodel = CM_32; +#endif + + if (sparc_cmodel_string != NULL) + { + if (TARGET_ARCH64) + { + for (cmodel = &cmodels[0]; cmodel->name; cmodel++) + if (strcmp (sparc_cmodel_string, cmodel->name) == 0) + break; + if (cmodel->name == NULL) + error ("bad value (%s) for -mcmodel= switch", sparc_cmodel_string); + else + sparc_cmodel = cmodel->value; + } + else + error ("-mcmodel= is not supported on 32 bit systems"); + } + + fpu = TARGET_FPU; /* save current -mfpu status */ + + /* Set the default CPU. */ + for (def = &cpu_default[0]; def->name; ++def) + if (def->cpu == TARGET_CPU_DEFAULT) + break; + if (! def->name) + abort (); + sparc_select[0].string = def->name; + + for (sel = &sparc_select[0]; sel->name; ++sel) + { + if (sel->string) + { + for (cpu = &cpu_table[0]; cpu->name; ++cpu) + if (! strcmp (sel->string, cpu->name)) + { + if (sel->set_tune_p) + sparc_cpu = cpu->processor; + + if (sel->set_arch_p) + { + target_flags &= ~cpu->disable; + target_flags |= cpu->enable; + } + break; + } + + if (! cpu->name) + error ("bad value (%s) for %s switch", sel->string, sel->name); + } + } + + /* If -mfpu or -mno-fpu was explicitly used, don't override with + the processor default. */ + if (TARGET_FPU_SET) + target_flags = (target_flags & ~MASK_FPU) | fpu; + + /* Use the deprecated v8 insns for sparc64 in 32 bit mode. */ + if (TARGET_V9 && TARGET_ARCH32) + target_flags |= MASK_DEPRECATED_V8_INSNS; + + /* V8PLUS requires V9, makes no sense in 64 bit mode. */ + if (! TARGET_V9 || TARGET_ARCH64) + target_flags &= ~MASK_V8PLUS; + + /* Don't use stack biasing in 32 bit mode. */ + if (TARGET_ARCH32) + target_flags &= ~MASK_STACK_BIAS; + + /* Don't allow -mvis if FPU is disabled. */ + if (! TARGET_FPU) + target_flags &= ~MASK_VIS; + + /* Validate -malign-loops= value, or provide default. */ + if (sparc_align_loops_string) + { + sparc_align_loops = exact_log2 (atoi (sparc_align_loops_string)); + if (sparc_align_loops < 2 || sparc_align_loops > 7) + fatal ("-malign-loops=%s is not between 4 and 128 or is not a power of two", + sparc_align_loops_string); + } + else + { + /* ??? This relies on ASM_OUTPUT_ALIGN to not emit the alignment if + its 0. This sounds a bit kludgey. */ + sparc_align_loops = 0; + } + + /* Validate -malign-jumps= value, or provide default. */ + if (sparc_align_jumps_string) + { + sparc_align_jumps = exact_log2 (atoi (sparc_align_jumps_string)); + if (sparc_align_jumps < 2 || sparc_align_loops > 7) + fatal ("-malign-jumps=%s is not between 4 and 128 or is not a power of two", + sparc_align_jumps_string); + } + else + { + /* ??? This relies on ASM_OUTPUT_ALIGN to not emit the alignment if + its 0. This sounds a bit kludgey. */ + sparc_align_jumps = 0; + } + + /* Validate -malign-functions= value, or provide default. */ + if (sparc_align_funcs_string) + { + sparc_align_funcs = exact_log2 (atoi (sparc_align_funcs_string)); + if (sparc_align_funcs < 2 || sparc_align_loops > 7) + fatal ("-malign-functions=%s is not between 4 and 128 or is not a power of two", + sparc_align_funcs_string); + } + else + sparc_align_funcs = DEFAULT_SPARC_ALIGN_FUNCS; + + /* Validate PCC_STRUCT_RETURN. */ + if (flag_pcc_struct_return == DEFAULT_PCC_STRUCT_RETURN) + flag_pcc_struct_return = (TARGET_ARCH64 ? 0 : 1); + + /* Do various machine dependent initializations. */ + sparc_init_modes (); +} + +/* Miscellaneous utilities. */ + +/* Nonzero if CODE, a comparison, is suitable for use in v9 conditional move + or branch on register contents instructions. */ + +int +v9_regcmp_p (code) + enum rtx_code code; +{ + return (code == EQ || code == NE || code == GE || code == LT + || code == LE || code == GT); +} + + +/* Operand constraints. */ + +/* Return non-zero only if OP is a register of mode MODE, + or const0_rtx. Don't allow const0_rtx if TARGET_LIVE_G0 because + %g0 may contain anything. */ + +int +reg_or_0_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (register_operand (op, mode)) + return 1; + if (TARGET_LIVE_G0) + return 0; + if (op == const0_rtx) + return 1; + if (GET_MODE (op) == VOIDmode && GET_CODE (op) == CONST_DOUBLE + && CONST_DOUBLE_HIGH (op) == 0 + && CONST_DOUBLE_LOW (op) == 0) + return 1; + if (GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT + && GET_CODE (op) == CONST_DOUBLE + && fp_zero_operand (op)) + return 1; + return 0; +} + +/* Nonzero if OP is a floating point value with value 0.0. */ + +int +fp_zero_operand (op) + rtx op; +{ + REAL_VALUE_TYPE r; + + REAL_VALUE_FROM_CONST_DOUBLE (r, op); + return (REAL_VALUES_EQUAL (r, dconst0) && ! REAL_VALUE_MINUS_ZERO (r)); +} + +/* Nonzero if OP is an integer register. */ + +int +intreg_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return (register_operand (op, SImode) + || (TARGET_ARCH64 && register_operand (op, DImode))); +} + +/* Nonzero if OP is a floating point condition code register. */ + +int +fcc_reg_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + /* This can happen when recog is called from combine. Op may be a MEM. + Fail instead of calling abort in this case. */ + if (GET_CODE (op) != REG) + return 0; + + if (mode != VOIDmode && mode != GET_MODE (op)) + return 0; + if (mode == VOIDmode + && (GET_MODE (op) != CCFPmode && GET_MODE (op) != CCFPEmode)) + return 0; + +#if 0 /* ??? ==> 1 when %fcc0-3 are pseudos first. See gen_compare_reg(). */ + if (reg_renumber == 0) + return REGNO (op) >= FIRST_PSEUDO_REGISTER; + return REGNO_OK_FOR_CCFP_P (REGNO (op)); +#else + return (unsigned) REGNO (op) - SPARC_FIRST_V9_FCC_REG < 4; +#endif +} + +/* Nonzero if OP is an integer or floating point condition code register. */ + +int +icc_or_fcc_reg_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (GET_CODE (op) == REG && REGNO (op) == SPARC_ICC_REG) + { + if (mode != VOIDmode && mode != GET_MODE (op)) + return 0; + if (mode == VOIDmode + && GET_MODE (op) != CCmode && GET_MODE (op) != CCXmode) + return 0; + return 1; + } + + return fcc_reg_operand (op, mode); +} + +/* Nonzero if OP can appear as the dest of a RESTORE insn. */ +int +restore_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (GET_CODE (op) == REG && GET_MODE (op) == mode + && (REGNO (op) < 8 || (REGNO (op) >= 24 && REGNO (op) < 32))); +} + +/* Call insn on SPARC can take a PC-relative constant address, or any regular + memory address. */ + +int +call_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (GET_CODE (op) != MEM) + abort (); + op = XEXP (op, 0); + return (symbolic_operand (op, mode) || memory_address_p (Pmode, op)); +} + +int +call_operand_address (op, mode) + rtx op; + enum machine_mode mode; +{ + return (symbolic_operand (op, mode) || memory_address_p (Pmode, op)); +} + +/* Returns 1 if OP is either a symbol reference or a sum of a symbol + reference and a constant. */ + +int +symbolic_operand (op, mode) + register rtx op; + enum machine_mode mode; +{ + 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); + + /* ??? This clause seems to be irrelevant. */ + case CONST_DOUBLE: + return GET_MODE (op) == mode; + + 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 truth value of statement that OP is a LABEL_REF of mode MODE. */ + +int +label_ref_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (GET_CODE (op) != LABEL_REF) + return 0; + if (GET_MODE (op) != mode) + return 0; + return 1; +} + +/* Return 1 if the operand is an argument used in generating pic references + in either the medium/low or medium/anywhere code models of sparc64. */ + +int +sp64_medium_pic_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + /* Check for (const (minus (symbol_ref:GOT) + (const (minus (label) (pc))))). */ + if (GET_CODE (op) != CONST) + return 0; + op = XEXP (op, 0); + if (GET_CODE (op) != MINUS) + return 0; + if (GET_CODE (XEXP (op, 0)) != SYMBOL_REF) + return 0; + /* ??? Ensure symbol is GOT. */ + if (GET_CODE (XEXP (op, 1)) != CONST) + return 0; + if (GET_CODE (XEXP (XEXP (op, 1), 0)) != MINUS) + return 0; + return 1; +} + +/* Return 1 if the operand is a data segment reference. This includes + the readonly data segment, or in other words anything but the text segment. + This is needed in the medium/anywhere code model on v9. These values + are accessed with EMBMEDANY_BASE_REG. */ + +int +data_segment_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + switch (GET_CODE (op)) + { + case SYMBOL_REF : + return ! SYMBOL_REF_FLAG (op); + case PLUS : + /* Assume canonical format of symbol + constant. + Fall through. */ + case CONST : + return data_segment_operand (XEXP (op, 0)); + default : + return 0; + } +} + +/* Return 1 if the operand is a text segment reference. + This is needed in the medium/anywhere code model on v9. */ + +int +text_segment_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + switch (GET_CODE (op)) + { + case LABEL_REF : + return 1; + case SYMBOL_REF : + return SYMBOL_REF_FLAG (op); + case PLUS : + /* Assume canonical format of symbol + constant. + Fall through. */ + case CONST : + return text_segment_operand (XEXP (op, 0)); + default : + return 0; + } +} + +/* 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; +} + +int +splittable_symbolic_memory_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + if (GET_CODE (op) != MEM) + return 0; + if (! symbolic_operand (XEXP (op, 0), Pmode)) + return 0; + return 1; +} + +int +splittable_immediate_memory_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + if (GET_CODE (op) != MEM) + return 0; + if (! immediate_operand (XEXP (op, 0), Pmode)) + return 0; + return 1; +} + +/* Return truth value of whether OP is EQ or NE. */ + +int +eq_or_neq (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return (GET_CODE (op) == EQ || GET_CODE (op) == NE); +} + +/* Return 1 if this is a comparison operator, but not an EQ, NE, GEU, + or LTU for non-floating-point. We handle those specially. */ + +int +normal_comp_operator (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + enum rtx_code code = GET_CODE (op); + + if (GET_RTX_CLASS (code) != '<') + return 0; + + if (GET_MODE (XEXP (op, 0)) == CCFPmode + || GET_MODE (XEXP (op, 0)) == CCFPEmode) + return 1; + + return (code != NE && code != EQ && code != GEU && code != LTU); +} + +/* Return 1 if this is a comparison operator. This allows the use of + MATCH_OPERATOR to recognize all the branch insns. */ + +int +noov_compare_op (op, mode) + register rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + enum rtx_code code = GET_CODE (op); + + if (GET_RTX_CLASS (code) != '<') + return 0; + + if (GET_MODE (XEXP (op, 0)) == CC_NOOVmode) + /* These are the only branches which work with CC_NOOVmode. */ + return (code == EQ || code == NE || code == GE || code == LT); + return 1; +} + +/* Nonzero if OP is a comparison operator suitable for use in v9 + conditional move or branch on register contents instructions. */ + +int +v9_regcmp_op (op, mode) + register rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + enum rtx_code code = GET_CODE (op); + + if (GET_RTX_CLASS (code) != '<') + return 0; + + return v9_regcmp_p (code); +} + +/* Return 1 if this is a SIGN_EXTEND or ZERO_EXTEND operation. */ + +int +extend_op (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return GET_CODE (op) == SIGN_EXTEND || GET_CODE (op) == ZERO_EXTEND; +} + +/* Return nonzero if OP is an operator of mode MODE which can set + the condition codes explicitly. We do not include PLUS and MINUS + because these require CC_NOOVmode, which we handle explicitly. */ + +int +cc_arithop (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + if (GET_CODE (op) == AND + || GET_CODE (op) == IOR + || GET_CODE (op) == XOR) + return 1; + + return 0; +} + +/* Return nonzero if OP is an operator of mode MODE which can bitwise + complement its second operand and set the condition codes explicitly. */ + +int +cc_arithopn (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + /* XOR is not here because combine canonicalizes (xor (not ...) ...) + and (xor ... (not ...)) to (not (xor ...)). */ + return (GET_CODE (op) == AND + || GET_CODE (op) == IOR); +} + +/* Return true if OP is a register, or is a CONST_INT that can fit in a + signed 13 bit immediate field. This is an acceptable SImode operand for + most 3 address instructions. */ + +int +arith_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + int val; + if (register_operand (op, mode)) + return 1; + if (GET_CODE (op) != CONST_INT) + return 0; + val = INTVAL (op) & 0xffffffff; + return SPARC_SIMM13_P (val); +} + +/* Return true if OP is a constant 4096 */ + +int +arith_4096_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + int val; + if (GET_CODE (op) != CONST_INT) + return 0; + val = INTVAL (op) & 0xffffffff; + return val == 4096; +} + +/* Return true if OP is suitable as second operand for add/sub */ + +int +arith_add_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return arith_operand (op, mode) || arith_4096_operand (op, mode); +} + +/* Return true if OP is a CONST_INT or a CONST_DOUBLE which can fit in the + immediate field of OR and XOR instructions. Used for 64-bit + constant formation patterns. */ +int +const64_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return ((GET_CODE (op) == CONST_INT + && SPARC_SIMM13_P (INTVAL (op))) +#if HOST_BITS_PER_WIDE_INT != 64 + || (GET_CODE (op) == CONST_DOUBLE + && SPARC_SIMM13_P (CONST_DOUBLE_LOW (op)) + && (CONST_DOUBLE_HIGH (op) == + ((CONST_DOUBLE_LOW (op) & 0x80000000) != 0 ? + (HOST_WIDE_INT)0xffffffff : 0))) +#endif + ); +} + +/* The same, but only for sethi instructions. */ +int +const64_high_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return ((GET_CODE (op) == CONST_INT + && (INTVAL (op) & 0xfffffc00) != 0 + && SPARC_SETHI_P (INTVAL (op)) +#if HOST_BITS_PER_WIDE_INT != 64 + /* Must be positive on non-64bit host else the + optimizer is fooled into thinking that sethi + sign extends, even though it does not. */ + && INTVAL (op) >= 0 +#endif + ) + || (GET_CODE (op) == CONST_DOUBLE + && CONST_DOUBLE_HIGH (op) == 0 + && (CONST_DOUBLE_LOW (op) & 0xfffffc00) != 0 + && SPARC_SETHI_P (CONST_DOUBLE_LOW (op)))); +} + +/* Return true if OP is a register, or is a CONST_INT that can fit in a + signed 11 bit immediate field. This is an acceptable SImode operand for + the movcc instructions. */ + +int +arith11_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (register_operand (op, mode) + || (GET_CODE (op) == CONST_INT && SPARC_SIMM11_P (INTVAL (op)))); +} + +/* Return true if OP is a register, or is a CONST_INT that can fit in a + signed 10 bit immediate field. This is an acceptable SImode operand for + the movrcc instructions. */ + +int +arith10_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (register_operand (op, mode) + || (GET_CODE (op) == CONST_INT && SPARC_SIMM10_P (INTVAL (op)))); +} + +/* Return true if OP is a register, is a CONST_INT that fits in a 13 bit + immediate field, or is a CONST_DOUBLE whose both parts fit in a 13 bit + immediate field. + v9: Return true if OP is a register, or is a CONST_INT or CONST_DOUBLE that + can fit in a 13 bit immediate field. This is an acceptable DImode operand + for most 3 address instructions. */ + +int +arith_double_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (register_operand (op, mode) + || (GET_CODE (op) == CONST_INT && SMALL_INT (op)) + || (! TARGET_ARCH64 + && GET_CODE (op) == CONST_DOUBLE + && (unsigned HOST_WIDE_INT) (CONST_DOUBLE_LOW (op) + 0x1000) < 0x2000 + && (unsigned HOST_WIDE_INT) (CONST_DOUBLE_HIGH (op) + 0x1000) < 0x2000) + || (TARGET_ARCH64 + && GET_CODE (op) == CONST_DOUBLE + && (unsigned HOST_WIDE_INT) (CONST_DOUBLE_LOW (op) + 0x1000) < 0x2000 + && ((CONST_DOUBLE_HIGH (op) == -1 + && (CONST_DOUBLE_LOW (op) & 0x1000) == 0x1000) + || (CONST_DOUBLE_HIGH (op) == 0 + && (CONST_DOUBLE_LOW (op) & 0x1000) == 0)))); +} + +/* Return true if OP is a constant 4096 for DImode on ARCH64 */ + +int +arith_double_4096_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return (TARGET_ARCH64 && + ((GET_CODE (op) == CONST_INT && INTVAL (op) == 4096) || + (GET_CODE (op) == CONST_DOUBLE && + CONST_DOUBLE_LOW (op) == 4096 && + CONST_DOUBLE_HIGH (op) == 0))); +} + +/* Return true if OP is suitable as second operand for add/sub in DImode */ + +int +arith_double_add_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return arith_double_operand (op, mode) || arith_double_4096_operand (op, mode); +} + +/* Return true if OP is a register, or is a CONST_INT or CONST_DOUBLE that + can fit in an 11 bit immediate field. This is an acceptable DImode + operand for the movcc instructions. */ +/* ??? Replace with arith11_operand? */ + +int +arith11_double_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (register_operand (op, mode) + || (GET_CODE (op) == CONST_DOUBLE + && (GET_MODE (op) == mode || GET_MODE (op) == VOIDmode) + && (unsigned HOST_WIDE_INT) (CONST_DOUBLE_LOW (op) + 0x400) < 0x800 + && ((CONST_DOUBLE_HIGH (op) == -1 + && (CONST_DOUBLE_LOW (op) & 0x400) == 0x400) + || (CONST_DOUBLE_HIGH (op) == 0 + && (CONST_DOUBLE_LOW (op) & 0x400) == 0))) + || (GET_CODE (op) == CONST_INT + && (GET_MODE (op) == mode || GET_MODE (op) == VOIDmode) + && (unsigned HOST_WIDE_INT) (INTVAL (op) + 0x400) < 0x800)); +} + +/* Return true if OP is a register, or is a CONST_INT or CONST_DOUBLE that + can fit in an 10 bit immediate field. This is an acceptable DImode + operand for the movrcc instructions. */ +/* ??? Replace with arith10_operand? */ + +int +arith10_double_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (register_operand (op, mode) + || (GET_CODE (op) == CONST_DOUBLE + && (GET_MODE (op) == mode || GET_MODE (op) == VOIDmode) + && (unsigned) (CONST_DOUBLE_LOW (op) + 0x200) < 0x400 + && ((CONST_DOUBLE_HIGH (op) == -1 + && (CONST_DOUBLE_LOW (op) & 0x200) == 0x200) + || (CONST_DOUBLE_HIGH (op) == 0 + && (CONST_DOUBLE_LOW (op) & 0x200) == 0))) + || (GET_CODE (op) == CONST_INT + && (GET_MODE (op) == mode || GET_MODE (op) == VOIDmode) + && (unsigned HOST_WIDE_INT) (INTVAL (op) + 0x200) < 0x400)); +} + +/* Return truth value of whether OP is a integer which fits the + range constraining immediate operands in most three-address insns, + which have a 13 bit immediate field. */ + +int +small_int (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return (GET_CODE (op) == CONST_INT && SMALL_INT (op)); +} + +int +small_int_or_double (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return ((GET_CODE (op) == CONST_INT && SMALL_INT (op)) + || (GET_CODE (op) == CONST_DOUBLE + && CONST_DOUBLE_HIGH (op) == 0 + && SPARC_SIMM13_P (CONST_DOUBLE_LOW (op)))); +} + +/* Recognize operand values for the umul instruction. That instruction sign + extends immediate values just like all other sparc instructions, but + interprets the extended result as an unsigned number. */ + +int +uns_small_int (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) < 0x1000) + || (INTVAL (op) >= 0xFFFFF000 + && INTVAL (op) < 0x100000000))); +#else + return ((GET_CODE (op) == CONST_INT && (unsigned) INTVAL (op) < 0x1000) + || (GET_CODE (op) == CONST_DOUBLE + && CONST_DOUBLE_HIGH (op) == 0 + && (unsigned) CONST_DOUBLE_LOW (op) - 0xFFFFF000 < 0x1000)); +#endif +} + +int +uns_arith_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return register_operand (op, mode) || uns_small_int (op, mode); +} + +/* Return truth value of statement that OP is a call-clobbered register. */ +int +clobbered_register (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return (GET_CODE (op) == REG && call_used_regs[REGNO (op)]); +} + +/* Return 1 if OP is const0_rtx, used for TARGET_LIVE_G0 insns. */ + +int +zero_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return op == const0_rtx; +} + +/* Return 1 if OP is a valid operand for the source of a move insn. */ + +int +input_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + /* If both modes are non-void they must be the same. */ + if (mode != VOIDmode && GET_MODE (op) != VOIDmode && mode != GET_MODE (op)) + return 0; + + /* Only a tiny bit of handling for CONSTANT_P_RTX is necessary. */ + if (GET_CODE (op) == CONST && GET_CODE (XEXP (op, 0)) == CONSTANT_P_RTX) + return 1; + + /* Allow any one instruction integer constant, and all CONST_INT + variants when we are working in DImode and !arch64. */ + if (GET_MODE_CLASS (mode) == MODE_INT + && ((GET_CODE (op) == CONST_INT + && ((SPARC_SETHI_P (INTVAL (op)) + && (! TARGET_ARCH64 + || (INTVAL (op) >= 0) + || mode == SImode)) + || SPARC_SIMM13_P (INTVAL (op)) + || (mode == DImode + && ! TARGET_ARCH64))) + || (TARGET_ARCH64 + && GET_CODE (op) == CONST_DOUBLE + && ((CONST_DOUBLE_HIGH (op) == 0 + && SPARC_SETHI_P (CONST_DOUBLE_LOW (op))) + || +#if HOST_BITS_PER_WIDE_INT == 64 + (CONST_DOUBLE_HIGH (op) == 0 + && SPARC_SIMM13_P (CONST_DOUBLE_LOW (op))) +#else + (SPARC_SIMM13_P (CONST_DOUBLE_LOW (op)) + && (((CONST_DOUBLE_LOW (op) & 0x80000000) == 0 + && CONST_DOUBLE_HIGH (op) == 0) + || (CONST_DOUBLE_HIGH (op) == -1))) +#endif + )))) + return 1; + + /* If !arch64 and this is a DImode const, allow it so that + the splits can be generated. */ + if (! TARGET_ARCH64 + && mode == DImode + && GET_CODE (op) == CONST_DOUBLE) + return 1; + + if (register_operand (op, mode)) + return 1; + + /* If this is a SUBREG, look inside so that we handle + paradoxical ones. */ + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + /* Check for valid MEM forms. */ + if (GET_CODE (op) == MEM) + { + rtx inside = XEXP (op, 0); + + if (GET_CODE (inside) == LO_SUM) + { + /* We can't allow these because all of the splits + (eventually as they trickle down into DFmode + splits) require offsettable memory references. */ + if (! TARGET_V9 + && GET_MODE (op) == TFmode) + return 0; + + return (register_operand (XEXP (inside, 0), Pmode) + && CONSTANT_P (XEXP (inside, 1))); + } + return memory_address_p (mode, inside); + } + + return 0; +} + + +/* We know it can't be done in one insn when we get here, + the movsi expander guarentees this. */ +void +sparc_emit_set_const32 (op0, op1) + rtx op0; + rtx op1; +{ + enum machine_mode mode = GET_MODE (op0); + rtx temp; + + if (GET_CODE (op1) == CONST_INT) + { + HOST_WIDE_INT value = INTVAL (op1); + + if (SPARC_SETHI_P (value) + || SPARC_SIMM13_P (value)) + abort (); + } + + /* Full 2-insn decomposition is needed. */ + if (reload_in_progress || reload_completed) + temp = op0; + else + temp = gen_reg_rtx (mode); + + if (GET_CODE (op1) == CONST_INT) + { + /* Emit them as real moves instead of a HIGH/LO_SUM, + this way CSE can see everything and reuse intermediate + values if it wants. */ + if (TARGET_ARCH64 + && HOST_BITS_PER_WIDE_INT != 64 + && (INTVAL (op1) & 0x80000000) != 0) + { + emit_insn (gen_rtx_SET (VOIDmode, + temp, + gen_rtx_CONST_DOUBLE (VOIDmode, const0_rtx, + INTVAL (op1) & 0xfffffc00, 0))); + } + else + { + emit_insn (gen_rtx_SET (VOIDmode, + temp, + GEN_INT (INTVAL (op1) & 0xfffffc00))); + } + emit_insn (gen_rtx_SET (VOIDmode, + op0, + gen_rtx_IOR (mode, + temp, + GEN_INT (INTVAL (op1) & 0x3ff)))); + } + else + { + /* A symbol, emit in the traditional way. */ + emit_insn (gen_rtx_SET (VOIDmode, + temp, + gen_rtx_HIGH (mode, + op1))); + emit_insn (gen_rtx_SET (VOIDmode, + op0, + gen_rtx_LO_SUM (mode, + temp, + op1))); + + } +} + + +/* Sparc-v9 code-model support. */ +void +sparc_emit_set_symbolic_const64 (op0, op1, temp1) + rtx op0; + rtx op1; + rtx temp1; +{ + switch (sparc_cmodel) + { + case CM_MEDLOW: + /* The range spanned by all instructions in the object is less + than 2^31 bytes (2GB) and the distance from any instruction + to the location of the label _GLOBAL_OFFSET_TABLE_ is less + than 2^31 bytes (2GB). + + The executable must be in the low 4TB of the virtual address + space. + + sethi %hi(symbol), %temp + or %temp, %lo(symbol), %reg */ + emit_insn (gen_rtx_SET (VOIDmode, temp1, gen_rtx_HIGH (DImode, op1))); + emit_insn (gen_rtx_SET (VOIDmode, op0, gen_rtx_LO_SUM (DImode, temp1, op1))); + break; + + case CM_MEDMID: + /* The range spanned by all instructions in the object is less + than 2^31 bytes (2GB) and the distance from any instruction + to the location of the label _GLOBAL_OFFSET_TABLE_ is less + than 2^31 bytes (2GB). + + The executable must be in the low 16TB of the virtual address + space. + + sethi %h44(symbol), %temp1 + or %temp1, %m44(symbol), %temp2 + sllx %temp2, 12, %temp3 + or %temp3, %l44(symbol), %reg */ + emit_insn (gen_seth44 (op0, op1)); + emit_insn (gen_setm44 (op0, op0, op1)); + emit_insn (gen_rtx_SET (VOIDmode, temp1, + gen_rtx_ASHIFT (DImode, op0, GEN_INT (12)))); + emit_insn (gen_setl44 (op0, temp1, op1)); + break; + + case CM_MEDANY: + /* The range spanned by all instructions in the object is less + than 2^31 bytes (2GB) and the distance from any instruction + to the location of the label _GLOBAL_OFFSET_TABLE_ is less + than 2^31 bytes (2GB). + + The executable can be placed anywhere in the virtual address + space. + + sethi %hh(symbol), %temp1 + sethi %lm(symbol), %temp2 + or %temp1, %hm(symbol), %temp3 + or %temp2, %lo(symbol), %temp4 + sllx %temp3, 32, %temp5 + or %temp4, %temp5, %reg */ + + /* Getting this right wrt. reloading is really tricky. + We _MUST_ have a seperate temporary at this point, + if we don't barf immediately instead of generating + incorrect code. */ + if (temp1 == op0) + abort (); + + emit_insn (gen_sethh (op0, op1)); + emit_insn (gen_setlm (temp1, op1)); + emit_insn (gen_sethm (op0, op0, op1)); + emit_insn (gen_rtx_SET (VOIDmode, op0, + gen_rtx_ASHIFT (DImode, op0, GEN_INT (32)))); + emit_insn (gen_rtx_SET (VOIDmode, op0, + gen_rtx_PLUS (DImode, op0, temp1))); + emit_insn (gen_setlo (op0, op0, op1)); + break; + + case CM_EMBMEDANY: + /* Old old old backwards compatibility kruft here. + Essentially it is MEDLOW with a fixed 64-bit + virtual base added to all data segment addresses. + Text-segment stuff is computed like MEDANY, we can't + reuse the code above because the relocation knobs + look different. + + Data segment: sethi %hi(symbol), %temp1 + or %temp1, %lo(symbol), %temp2 + add %temp2, EMBMEDANY_BASE_REG, %reg + + Text segment: sethi %uhi(symbol), %temp1 + sethi %hi(symbol), %temp2 + or %temp1, %ulo(symbol), %temp3 + or %temp2, %lo(symbol), %temp4 + sllx %temp3, 32, %temp5 + or %temp4, %temp5, %reg */ + if (data_segment_operand (op1, GET_MODE (op1))) + { + emit_insn (gen_embmedany_sethi (temp1, op1)); + emit_insn (gen_embmedany_brsum (op0, temp1)); + emit_insn (gen_embmedany_losum (op0, op0, op1)); + } + else + { + /* Getting this right wrt. reloading is really tricky. + We _MUST_ have a seperate temporary at this point, + so we barf immediately instead of generating + incorrect code. */ + if (temp1 == op0) + abort (); + + emit_insn (gen_embmedany_textuhi (op0, op1)); + emit_insn (gen_embmedany_texthi (temp1, op1)); + emit_insn (gen_embmedany_textulo (op0, op0, op1)); + emit_insn (gen_rtx_SET (VOIDmode, op0, + gen_rtx_ASHIFT (DImode, op0, GEN_INT (32)))); + emit_insn (gen_rtx_SET (VOIDmode, op0, + gen_rtx_PLUS (DImode, op0, temp1))); + emit_insn (gen_embmedany_textlo (op0, op0, op1)); + } + break; + + default: + abort(); + } +} + +/* These avoid problems when cross compiling. If we do not + go through all this hair then the optimizer will see + invalid REG_EQUAL notes or in some cases none at all. */ +static void sparc_emit_set_safe_HIGH64 PROTO ((rtx, HOST_WIDE_INT)); +static rtx gen_safe_SET64 PROTO ((rtx, HOST_WIDE_INT)); +static rtx gen_safe_OR64 PROTO ((rtx, HOST_WIDE_INT)); +static rtx gen_safe_XOR64 PROTO ((rtx, HOST_WIDE_INT)); + +#if HOST_BITS_PER_WIDE_INT == 64 +#define GEN_HIGHINT64(__x) GEN_INT ((__x) & 0xfffffc00) +#define GEN_INT64(__x) GEN_INT (__x) +#else +#define GEN_HIGHINT64(__x) \ + gen_rtx_CONST_DOUBLE (VOIDmode, const0_rtx, \ + (__x) & 0xfffffc00, 0) +#define GEN_INT64(__x) \ + gen_rtx_CONST_DOUBLE (VOIDmode, const0_rtx, \ + (__x) & 0xffffffff, \ + ((__x) & 0x80000000 \ + ? 0xffffffff : 0)) +#endif + +/* The optimizer is not to assume anything about exactly + which bits are set for a HIGH, they are unspecified. + Unfortunately this leads to many missed optimizations + during CSE. We mask out the non-HIGH bits, and matches + a plain movdi, to alleviate this problem. */ +static void +sparc_emit_set_safe_HIGH64 (dest, val) + rtx dest; + HOST_WIDE_INT val; +{ + emit_insn (gen_rtx_SET (VOIDmode, dest, GEN_HIGHINT64 (val))); +} + +static rtx +gen_safe_SET64 (dest, val) + rtx dest; + HOST_WIDE_INT val; +{ + return gen_rtx_SET (VOIDmode, dest, GEN_INT64 (val)); +} + +static rtx +gen_safe_OR64 (src, val) + rtx src; + HOST_WIDE_INT val; +{ + return gen_rtx_IOR (DImode, src, GEN_INT64 (val)); +} + +static rtx +gen_safe_XOR64 (src, val) + rtx src; + HOST_WIDE_INT val; +{ + return gen_rtx_XOR (DImode, src, GEN_INT64 (val)); +} + +/* Worker routines for 64-bit constant formation on arch64. + One of the key things to be doing in these emissions is + to create as many temp REGs as possible. This makes it + possible for half-built constants to be used later when + such values are similar to something required later on. + Without doing this, the optimizer cannot see such + opportunities. */ + +static void sparc_emit_set_const64_quick1 + PROTO((rtx, rtx, unsigned HOST_WIDE_INT, int)); + +static void +sparc_emit_set_const64_quick1 (op0, temp, low_bits, is_neg) + rtx op0; + rtx temp; + unsigned HOST_WIDE_INT low_bits; + int is_neg; +{ + unsigned HOST_WIDE_INT high_bits; + + if (is_neg) + high_bits = (~low_bits) & 0xffffffff; + else + high_bits = low_bits; + + sparc_emit_set_safe_HIGH64 (temp, high_bits); + if (!is_neg) + { + emit_insn (gen_rtx_SET (VOIDmode, op0, + gen_safe_OR64 (temp, (high_bits & 0x3ff)))); + } + else + { + /* If we are XOR'ing with -1, then we should emit a one's complement + instead. This way the combiner will notice logical operations + such as ANDN later on and substitute. */ + if ((low_bits & 0x3ff) == 0x3ff) + { + emit_insn (gen_rtx_SET (VOIDmode, op0, + gen_rtx_NOT (DImode, temp))); + } + else + { + emit_insn (gen_rtx_SET (VOIDmode, op0, + gen_safe_XOR64 (temp, + (-0x400 | (low_bits & 0x3ff))))); + } + } +} + +static void sparc_emit_set_const64_quick2 + PROTO((rtx, rtx, unsigned HOST_WIDE_INT, + unsigned HOST_WIDE_INT, int)); + +static void +sparc_emit_set_const64_quick2 (op0, temp, high_bits, low_immediate, shift_count) + rtx op0; + rtx temp; + unsigned HOST_WIDE_INT high_bits; + unsigned HOST_WIDE_INT low_immediate; + int shift_count; +{ + rtx temp2 = op0; + + if ((high_bits & 0xfffffc00) != 0) + { + sparc_emit_set_safe_HIGH64 (temp, high_bits); + if ((high_bits & ~0xfffffc00) != 0) + emit_insn (gen_rtx_SET (VOIDmode, op0, + gen_safe_OR64 (temp, (high_bits & 0x3ff)))); + else + temp2 = temp; + } + else + { + emit_insn (gen_safe_SET64 (temp, high_bits)); + temp2 = temp; + } + + /* Now shift it up into place. */ + emit_insn (gen_rtx_SET (VOIDmode, op0, + gen_rtx_ASHIFT (DImode, temp2, + GEN_INT (shift_count)))); + + /* If there is a low immediate part piece, finish up by + putting that in as well. */ + if (low_immediate != 0) + emit_insn (gen_rtx_SET (VOIDmode, op0, + gen_safe_OR64 (op0, low_immediate))); +} + +static void sparc_emit_set_const64_longway + PROTO((rtx, rtx, unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT)); + +/* Full 64-bit constant decomposition. Even though this is the + 'worst' case, we still optimize a few things away. */ +static void +sparc_emit_set_const64_longway (op0, temp, high_bits, low_bits) + rtx op0; + rtx temp; + unsigned HOST_WIDE_INT high_bits; + unsigned HOST_WIDE_INT low_bits; +{ + rtx sub_temp; + + if (reload_in_progress || reload_completed) + sub_temp = op0; + else + sub_temp = gen_reg_rtx (DImode); + + if ((high_bits & 0xfffffc00) != 0) + { + sparc_emit_set_safe_HIGH64 (temp, high_bits); + if ((high_bits & ~0xfffffc00) != 0) + emit_insn (gen_rtx_SET (VOIDmode, + sub_temp, + gen_safe_OR64 (temp, (high_bits & 0x3ff)))); + else + sub_temp = temp; + } + else + { + emit_insn (gen_safe_SET64 (temp, high_bits)); + sub_temp = temp; + } + + if (!reload_in_progress && !reload_completed) + { + rtx temp2 = gen_reg_rtx (DImode); + rtx temp3 = gen_reg_rtx (DImode); + rtx temp4 = gen_reg_rtx (DImode); + + emit_insn (gen_rtx_SET (VOIDmode, temp4, + gen_rtx_ASHIFT (DImode, sub_temp, + GEN_INT (32)))); + + sparc_emit_set_safe_HIGH64 (temp2, low_bits); + if ((low_bits & ~0xfffffc00) != 0) + { + emit_insn (gen_rtx_SET (VOIDmode, temp3, + gen_safe_OR64 (temp2, (low_bits & 0x3ff)))); + emit_insn (gen_rtx_SET (VOIDmode, op0, + gen_rtx_PLUS (DImode, temp4, temp3))); + } + else + { + emit_insn (gen_rtx_SET (VOIDmode, op0, + gen_rtx_PLUS (DImode, temp4, temp2))); + } + } + else + { + rtx low1 = GEN_INT ((low_bits >> (32 - 12)) & 0xfff); + rtx low2 = GEN_INT ((low_bits >> (32 - 12 - 12)) & 0xfff); + rtx low3 = GEN_INT ((low_bits >> (32 - 12 - 12 - 8)) & 0x0ff); + int to_shift = 12; + + /* We are in the middle of reload, so this is really + painful. However we do still make an attempt to + avoid emitting truly stupid code. */ + if (low1 != const0_rtx) + { + emit_insn (gen_rtx_SET (VOIDmode, op0, + gen_rtx_ASHIFT (DImode, sub_temp, + GEN_INT (to_shift)))); + emit_insn (gen_rtx_SET (VOIDmode, op0, + gen_rtx_IOR (DImode, op0, low1))); + sub_temp = op0; + to_shift = 12; + } + else + { + to_shift += 12; + } + if (low2 != const0_rtx) + { + emit_insn (gen_rtx_SET (VOIDmode, op0, + gen_rtx_ASHIFT (DImode, sub_temp, + GEN_INT (to_shift)))); + emit_insn (gen_rtx_SET (VOIDmode, op0, + gen_rtx_IOR (DImode, op0, low2))); + sub_temp = op0; + to_shift = 8; + } + else + { + to_shift += 8; + } + emit_insn (gen_rtx_SET (VOIDmode, op0, + gen_rtx_ASHIFT (DImode, sub_temp, + GEN_INT (to_shift)))); + if (low3 != const0_rtx) + emit_insn (gen_rtx_SET (VOIDmode, op0, + gen_rtx_IOR (DImode, op0, low3))); + /* phew... */ + } +} + +/* Analyze a 64-bit constant for certain properties. */ +static void analyze_64bit_constant + PROTO((unsigned HOST_WIDE_INT, + unsigned HOST_WIDE_INT, + int *, int *, int *)); + +static void +analyze_64bit_constant (high_bits, low_bits, hbsp, lbsp, abbasp) + unsigned HOST_WIDE_INT high_bits, low_bits; + int *hbsp, *lbsp, *abbasp; +{ + int lowest_bit_set, highest_bit_set, all_bits_between_are_set; + int i; + + lowest_bit_set = highest_bit_set = -1; + i = 0; + do + { + if ((lowest_bit_set == -1) + && ((low_bits >> i) & 1)) + lowest_bit_set = i; + if ((highest_bit_set == -1) + && ((high_bits >> (32 - i - 1)) & 1)) + highest_bit_set = (64 - i - 1); + } + while (++i < 32 + && ((highest_bit_set == -1) + || (lowest_bit_set == -1))); + if (i == 32) + { + i = 0; + do + { + if ((lowest_bit_set == -1) + && ((high_bits >> i) & 1)) + lowest_bit_set = i + 32; + if ((highest_bit_set == -1) + && ((low_bits >> (32 - i - 1)) & 1)) + highest_bit_set = 32 - i - 1; + } + while (++i < 32 + && ((highest_bit_set == -1) + || (lowest_bit_set == -1))); + } + /* If there are no bits set this should have gone out + as one instruction! */ + if (lowest_bit_set == -1 + || highest_bit_set == -1) + abort (); + all_bits_between_are_set = 1; + for (i = lowest_bit_set; i <= highest_bit_set; i++) + { + if (i < 32) + { + if ((low_bits & (1 << i)) != 0) + continue; + } + else + { + if ((high_bits & (1 << (i - 32))) != 0) + continue; + } + all_bits_between_are_set = 0; + break; + } + *hbsp = highest_bit_set; + *lbsp = lowest_bit_set; + *abbasp = all_bits_between_are_set; +} + +static int const64_is_2insns + PROTO((unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT)); + +static int +const64_is_2insns (high_bits, low_bits) + unsigned HOST_WIDE_INT high_bits, low_bits; +{ + int highest_bit_set, lowest_bit_set, all_bits_between_are_set; + + if (high_bits == 0 + || high_bits == 0xffffffff) + return 1; + + analyze_64bit_constant (high_bits, low_bits, + &highest_bit_set, &lowest_bit_set, + &all_bits_between_are_set); + + if ((highest_bit_set == 63 + || lowest_bit_set == 0) + && all_bits_between_are_set != 0) + return 1; + + if ((highest_bit_set - lowest_bit_set) < 21) + return 1; + + return 0; +} + +static unsigned HOST_WIDE_INT create_simple_focus_bits + PROTO((unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT, + int, int)); + +static unsigned HOST_WIDE_INT +create_simple_focus_bits (high_bits, low_bits, lowest_bit_set, shift) + unsigned HOST_WIDE_INT high_bits, low_bits; + int lowest_bit_set, shift; +{ + HOST_WIDE_INT hi, lo; + + if (lowest_bit_set < 32) + { + lo = (low_bits >> lowest_bit_set) << shift; + hi = ((high_bits << (32 - lowest_bit_set)) << shift); + } + else + { + lo = 0; + hi = ((high_bits >> (lowest_bit_set - 32)) << shift); + } + if (hi & lo) + abort (); + return (hi | lo); +} + +/* Here we are sure to be arch64 and this is an integer constant + being loaded into a register. Emit the most efficient + insn sequence possible. Detection of all the 1-insn cases + has been done already. */ +void +sparc_emit_set_const64 (op0, op1) + rtx op0; + rtx op1; +{ + unsigned HOST_WIDE_INT high_bits, low_bits; + int lowest_bit_set, highest_bit_set; + int all_bits_between_are_set; + rtx temp; + + /* Sanity check that we know what we are working with. */ + if (! TARGET_ARCH64 + || GET_CODE (op0) != REG + || (REGNO (op0) >= SPARC_FIRST_FP_REG + && REGNO (op0) <= SPARC_LAST_V9_FP_REG)) + abort (); + + if (reload_in_progress || reload_completed) + temp = op0; + else + temp = gen_reg_rtx (DImode); + + if (GET_CODE (op1) != CONST_DOUBLE + && GET_CODE (op1) != CONST_INT) + { + sparc_emit_set_symbolic_const64 (op0, op1, temp); + return; + } + + if (GET_CODE (op1) == CONST_DOUBLE) + { +#if HOST_BITS_PER_WIDE_INT == 64 + high_bits = (CONST_DOUBLE_LOW (op1) >> 32) & 0xffffffff; + low_bits = CONST_DOUBLE_LOW (op1) & 0xffffffff; +#else + high_bits = CONST_DOUBLE_HIGH (op1); + low_bits = CONST_DOUBLE_LOW (op1); +#endif + } + else + { +#if HOST_BITS_PER_WIDE_INT == 64 + high_bits = ((INTVAL (op1) >> 32) & 0xffffffff); + low_bits = (INTVAL (op1) & 0xffffffff); +#else + high_bits = ((INTVAL (op1) < 0) ? + 0xffffffff : + 0x00000000); + low_bits = INTVAL (op1); +#endif + } + + /* low_bits bits 0 --> 31 + high_bits bits 32 --> 63 */ + + analyze_64bit_constant (high_bits, low_bits, + &highest_bit_set, &lowest_bit_set, + &all_bits_between_are_set); + + /* First try for a 2-insn sequence. */ + + /* These situations are preferred because the optimizer can + * do more things with them: + * 1) mov -1, %reg + * sllx %reg, shift, %reg + * 2) mov -1, %reg + * srlx %reg, shift, %reg + * 3) mov some_small_const, %reg + * sllx %reg, shift, %reg + */ + if (((highest_bit_set == 63 + || lowest_bit_set == 0) + && all_bits_between_are_set != 0) + || ((highest_bit_set - lowest_bit_set) < 12)) + { + HOST_WIDE_INT the_const = -1; + int shift = lowest_bit_set; + + if ((highest_bit_set != 63 + && lowest_bit_set != 0) + || all_bits_between_are_set == 0) + { + the_const = + create_simple_focus_bits (high_bits, low_bits, + lowest_bit_set, 0); + } + else if (lowest_bit_set == 0) + shift = -(63 - highest_bit_set); + + if (! SPARC_SIMM13_P (the_const)) + abort (); + + emit_insn (gen_safe_SET64 (temp, the_const)); + if (shift > 0) + emit_insn (gen_rtx_SET (VOIDmode, + op0, + gen_rtx_ASHIFT (DImode, + temp, + GEN_INT (shift)))); + else if (shift < 0) + emit_insn (gen_rtx_SET (VOIDmode, + op0, + gen_rtx_LSHIFTRT (DImode, + temp, + GEN_INT (-shift)))); + else + abort (); + return; + } + + /* Now a range of 22 or less bits set somewhere. + * 1) sethi %hi(focus_bits), %reg + * sllx %reg, shift, %reg + * 2) sethi %hi(focus_bits), %reg + * srlx %reg, shift, %reg + */ + if ((highest_bit_set - lowest_bit_set) < 21) + { + unsigned HOST_WIDE_INT focus_bits = + create_simple_focus_bits (high_bits, low_bits, + lowest_bit_set, 10); + + if (! SPARC_SETHI_P (focus_bits)) + abort (); + + sparc_emit_set_safe_HIGH64 (temp, focus_bits); + + /* If lowest_bit_set == 10 then a sethi alone could have done it. */ + if (lowest_bit_set < 10) + emit_insn (gen_rtx_SET (VOIDmode, + op0, + gen_rtx_LSHIFTRT (DImode, temp, + GEN_INT (10 - lowest_bit_set)))); + else if (lowest_bit_set > 10) + emit_insn (gen_rtx_SET (VOIDmode, + op0, + gen_rtx_ASHIFT (DImode, temp, + GEN_INT (lowest_bit_set - 10)))); + else + abort (); + return; + } + + /* 1) sethi %hi(low_bits), %reg + * or %reg, %lo(low_bits), %reg + * 2) sethi %hi(~low_bits), %reg + * xor %reg, %lo(-0x400 | (low_bits & 0x3ff)), %reg + */ + if (high_bits == 0 + || high_bits == 0xffffffff) + { + sparc_emit_set_const64_quick1 (op0, temp, low_bits, + (high_bits == 0xffffffff)); + return; + } + + /* Now, try 3-insn sequences. */ + + /* 1) sethi %hi(high_bits), %reg + * or %reg, %lo(high_bits), %reg + * sllx %reg, 32, %reg + */ + if (low_bits == 0) + { + sparc_emit_set_const64_quick2 (op0, temp, high_bits, 0, 32); + return; + } + + /* We may be able to do something quick + when the constant is negated, so try that. */ + if (const64_is_2insns ((~high_bits) & 0xffffffff, + (~low_bits) & 0xfffffc00)) + { + /* NOTE: The trailing bits get XOR'd so we need the + non-negated bits, not the negated ones. */ + unsigned HOST_WIDE_INT trailing_bits = low_bits & 0x3ff; + + if ((((~high_bits) & 0xffffffff) == 0 + && ((~low_bits) & 0x80000000) == 0) + || (((~high_bits) & 0xffffffff) == 0xffffffff + && ((~low_bits) & 0x80000000) != 0)) + { + int fast_int = (~low_bits & 0xffffffff); + + if ((SPARC_SETHI_P (fast_int) + && (~high_bits & 0xffffffff) == 0) + || SPARC_SIMM13_P (fast_int)) + emit_insn (gen_safe_SET64 (temp, fast_int)); + else + sparc_emit_set_const64 (temp, GEN_INT64 (fast_int)); + } + else + { + rtx negated_const; +#if HOST_BITS_PER_WIDE_INT == 64 + negated_const = GEN_INT (((~low_bits) & 0xfffffc00) | + (((HOST_WIDE_INT)((~high_bits) & 0xffffffff))<<32)); +#else + negated_const = gen_rtx_CONST_DOUBLE (DImode, const0_rtx, + (~low_bits) & 0xfffffc00, + (~high_bits) & 0xffffffff); +#endif + sparc_emit_set_const64 (temp, negated_const); + } + + /* If we are XOR'ing with -1, then we should emit a one's complement + instead. This way the combiner will notice logical operations + such as ANDN later on and substitute. */ + if (trailing_bits == 0x3ff) + { + emit_insn (gen_rtx_SET (VOIDmode, op0, + gen_rtx_NOT (DImode, temp))); + } + else + { + emit_insn (gen_rtx_SET (VOIDmode, + op0, + gen_safe_XOR64 (temp, + (-0x400 | trailing_bits)))); + } + return; + } + + /* 1) sethi %hi(xxx), %reg + * or %reg, %lo(xxx), %reg + * sllx %reg, yyy, %reg + * + * ??? This is just a generalized version of the low_bits==0 + * thing above, FIXME... + */ + if ((highest_bit_set - lowest_bit_set) < 32) + { + unsigned HOST_WIDE_INT focus_bits = + create_simple_focus_bits (high_bits, low_bits, + lowest_bit_set, 0); + + /* We can't get here in this state. */ + if (highest_bit_set < 32 + || lowest_bit_set >= 32) + abort (); + + /* So what we know is that the set bits straddle the + middle of the 64-bit word. */ + sparc_emit_set_const64_quick2 (op0, temp, + focus_bits, 0, + lowest_bit_set); + return; + } + + /* 1) sethi %hi(high_bits), %reg + * or %reg, %lo(high_bits), %reg + * sllx %reg, 32, %reg + * or %reg, low_bits, %reg + */ + if (SPARC_SIMM13_P(low_bits) + && ((int)low_bits > 0)) + { + sparc_emit_set_const64_quick2 (op0, temp, high_bits, low_bits, 32); + return; + } + + /* The easiest way when all else fails, is full decomposition. */ +#if 0 + printf ("sparc_emit_set_const64: Hard constant [%08lx%08lx] neg[%08lx%08lx]\n", + high_bits, low_bits, ~high_bits, ~low_bits); +#endif + sparc_emit_set_const64_longway (op0, temp, high_bits, low_bits); +} + +/* X and Y are two things to compare using CODE. Emit the compare insn and + return the rtx for the cc reg in the proper mode. */ + +rtx +gen_compare_reg (code, x, y) + enum rtx_code code; + rtx x, y; +{ + enum machine_mode mode = SELECT_CC_MODE (code, x, y); + rtx cc_reg; + + /* ??? We don't have movcc patterns so we cannot generate pseudo regs for the + fcc regs (cse can't tell they're really call clobbered regs and will + remove a duplicate comparison even if there is an intervening function + call - it will then try to reload the cc reg via an int reg which is why + we need the movcc patterns). It is possible to provide the movcc + patterns by using the ldxfsr/stxfsr v9 insns. I tried it: you need two + registers (say %g1,%g5) and it takes about 6 insns. A better fix would be + to tell cse that CCFPE mode registers (even pseudos) are call + clobbered. */ + + /* ??? This is an experiment. Rather than making changes to cse which may + or may not be easy/clean, we do our own cse. This is possible because + we will generate hard registers. Cse knows they're call clobbered (it + doesn't know the same thing about pseudos). If we guess wrong, no big + deal, but if we win, great! */ + + if (TARGET_V9 && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT) +#if 1 /* experiment */ + { + int reg; + /* We cycle through the registers to ensure they're all exercised. */ + static int next_fcc_reg = 0; + /* Previous x,y for each fcc reg. */ + static rtx prev_args[4][2]; + + /* Scan prev_args for x,y. */ + for (reg = 0; reg < 4; reg++) + if (prev_args[reg][0] == x && prev_args[reg][1] == y) + break; + if (reg == 4) + { + reg = next_fcc_reg; + prev_args[reg][0] = x; + prev_args[reg][1] = y; + next_fcc_reg = (next_fcc_reg + 1) & 3; + } + cc_reg = gen_rtx_REG (mode, reg + SPARC_FIRST_V9_FCC_REG); + } +#else + cc_reg = gen_reg_rtx (mode); +#endif /* ! experiment */ + else if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT) + cc_reg = gen_rtx_REG (mode, SPARC_FCC_REG); + else + cc_reg = gen_rtx_REG (mode, SPARC_ICC_REG); + + emit_insn (gen_rtx_SET (VOIDmode, cc_reg, + gen_rtx_COMPARE (mode, x, y))); + + return cc_reg; +} + +/* This function is used for v9 only. + CODE is the code for an Scc's comparison. + OPERANDS[0] is the target of the Scc insn. + OPERANDS[1] is the value we compare against const0_rtx (which hasn't + been generated yet). + + This function is needed to turn + + (set (reg:SI 110) + (gt (reg:CCX 100 %icc) + (const_int 0))) + into + (set (reg:SI 110) + (gt:DI (reg:CCX 100 %icc) + (const_int 0))) + + IE: The instruction recognizer needs to see the mode of the comparison to + find the right instruction. We could use "gt:DI" right in the + define_expand, but leaving it out allows us to handle DI, SI, etc. + + We refer to the global sparc compare operands sparc_compare_op0 and + sparc_compare_op1. */ + +int +gen_v9_scc (compare_code, operands) + enum rtx_code compare_code; + register rtx *operands; +{ + rtx temp, op0, op1; + + if (! TARGET_ARCH64 + && (GET_MODE (sparc_compare_op0) == DImode + || GET_MODE (operands[0]) == DImode)) + return 0; + + /* Handle the case where operands[0] == sparc_compare_op0. + We "early clobber" the result. */ + if (REGNO (operands[0]) == REGNO (sparc_compare_op0)) + { + op0 = gen_reg_rtx (GET_MODE (sparc_compare_op0)); + emit_move_insn (op0, sparc_compare_op0); + } + else + op0 = sparc_compare_op0; + /* For consistency in the following. */ + op1 = sparc_compare_op1; + + /* Try to use the movrCC insns. */ + if (TARGET_ARCH64 + && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT + && op1 == const0_rtx + && v9_regcmp_p (compare_code)) + { + /* Special case for op0 != 0. This can be done with one instruction if + operands[0] == sparc_compare_op0. We don't assume they are equal + now though. */ + + if (compare_code == NE + && GET_MODE (operands[0]) == DImode + && GET_MODE (op0) == DImode) + { + emit_insn (gen_rtx_SET (VOIDmode, operands[0], op0)); + emit_insn (gen_rtx_SET (VOIDmode, operands[0], + gen_rtx_IF_THEN_ELSE (DImode, + gen_rtx_fmt_ee (compare_code, DImode, + op0, const0_rtx), + const1_rtx, + operands[0]))); + return 1; + } + + emit_insn (gen_rtx_SET (VOIDmode, operands[0], const0_rtx)); + if (GET_MODE (op0) != DImode) + { + temp = gen_reg_rtx (DImode); + convert_move (temp, op0, 0); + } + else + temp = op0; + emit_insn (gen_rtx_SET (VOIDmode, operands[0], + gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]), + gen_rtx_fmt_ee (compare_code, DImode, + temp, const0_rtx), + const1_rtx, + operands[0]))); + return 1; + } + else + { + operands[1] = gen_compare_reg (compare_code, op0, op1); + + switch (GET_MODE (operands[1])) + { + case CCmode : + case CCXmode : + case CCFPEmode : + case CCFPmode : + break; + default : + abort (); + } + emit_insn (gen_rtx_SET (VOIDmode, operands[0], const0_rtx)); + emit_insn (gen_rtx_SET (VOIDmode, operands[0], + gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]), + gen_rtx_fmt_ee (compare_code, + GET_MODE (operands[1]), + operands[1], const0_rtx), + const1_rtx, operands[0]))); + return 1; + } +} + +/* Emit a conditional jump insn for the v9 architecture using comparison code + CODE and jump target LABEL. + This function exists to take advantage of the v9 brxx insns. */ + +void +emit_v9_brxx_insn (code, op0, label) + enum rtx_code code; + rtx op0, label; +{ + emit_jump_insn (gen_rtx_SET (VOIDmode, + pc_rtx, + gen_rtx_IF_THEN_ELSE (VOIDmode, + gen_rtx_fmt_ee (code, GET_MODE (op0), + op0, const0_rtx), + gen_rtx_LABEL_REF (VOIDmode, label), + pc_rtx))); +} + +/* Return nonzero if a return peephole merging return with + setting of output register is ok. */ +int +leaf_return_peephole_ok () +{ + return (actual_fsize == 0); +} + +/* Return nonzero if TRIAL can go into the function epilogue's + delay slot. SLOT is the slot we are trying to fill. */ + +int +eligible_for_epilogue_delay (trial, slot) + rtx trial; + int slot; +{ + rtx pat, src; + + if (slot >= 1) + return 0; + + if (GET_CODE (trial) != INSN || GET_CODE (PATTERN (trial)) != SET) + return 0; + + if (get_attr_length (trial) != 1) + return 0; + + /* If %g0 is live, there are lots of things we can't handle. + Rather than trying to find them all now, let's punt and only + optimize things as necessary. */ + if (TARGET_LIVE_G0) + return 0; + + /* In the case of a true leaf function, anything can go into the delay slot. + A delay slot only exists however if the frame size is zero, otherwise + we will put an insn to adjust the stack after the return. */ + if (leaf_function) + { + if (leaf_return_peephole_ok ()) + return ((get_attr_in_uncond_branch_delay (trial) + == IN_BRANCH_DELAY_TRUE)); + return 0; + } + + /* If only trivial `restore' insns work, nothing can go in the + delay slot. */ + else if (TARGET_BROKEN_SAVERESTORE) + return 0; + + pat = PATTERN (trial); + + /* Otherwise, only operations which can be done in tandem with + a `restore' insn can go into the delay slot. */ + if (GET_CODE (SET_DEST (pat)) != REG + || REGNO (SET_DEST (pat)) >= 32 + || REGNO (SET_DEST (pat)) < 24) + return 0; + + /* The set of insns matched here must agree precisely with the set of + patterns paired with a RETURN in sparc.md. */ + + src = SET_SRC (pat); + + /* This matches "*return_[qhs]i" or even "*return_di" on TARGET_ARCH64. */ + if (arith_operand (src, GET_MODE (src))) + { + if (TARGET_ARCH64) + return GET_MODE_SIZE (GET_MODE (src)) <= GET_MODE_SIZE (DImode); + else + return GET_MODE_SIZE (GET_MODE (src)) <= GET_MODE_SIZE (SImode); + } + + /* This matches "*return_di". */ + else if (arith_double_operand (src, GET_MODE (src))) + return GET_MODE_SIZE (GET_MODE (src)) <= GET_MODE_SIZE (DImode); + + /* This matches "*return_sf_no_fpu". */ + else if (! TARGET_FPU && restore_operand (SET_DEST (pat), SFmode) + && register_operand (src, SFmode)) + return 1; + + /* This matches "*return_addsi". */ + else if (GET_CODE (src) == PLUS + && arith_operand (XEXP (src, 0), SImode) + && arith_operand (XEXP (src, 1), SImode) + && (register_operand (XEXP (src, 0), SImode) + || register_operand (XEXP (src, 1), SImode))) + return 1; + + /* This matches "*return_adddi". */ + else if (GET_CODE (src) == PLUS + && arith_double_operand (XEXP (src, 0), DImode) + && arith_double_operand (XEXP (src, 1), DImode) + && (register_operand (XEXP (src, 0), DImode) + || register_operand (XEXP (src, 1), DImode))) + return 1; + + return 0; +} + +static int +check_return_regs (x) + rtx x; +{ + switch (GET_CODE (x)) + { + case REG: + return IN_OR_GLOBAL_P (x); + + case CONST_INT: + case CONST_DOUBLE: + case CONST: + case SYMBOL_REF: + case LABEL_REF: + return 1; + + case SET: + case IOR: + case AND: + case XOR: + case PLUS: + case MINUS: + if (check_return_regs (XEXP (x, 1)) == 0) + return 0; + case NOT: + case NEG: + case MEM: + return check_return_regs (XEXP (x, 0)); + + default: + return 0; + } + +} + +/* Return 1 if TRIAL references only in and global registers. */ +int +eligible_for_return_delay (trial) + rtx trial; +{ + if (GET_CODE (PATTERN (trial)) != SET) + return 0; + + return check_return_regs (PATTERN (trial)); +} + +int +short_branch (uid1, uid2) + int uid1, uid2; +{ + unsigned int delta = insn_addresses[uid1] - insn_addresses[uid2]; + if (delta + 1024 < 2048) + return 1; + /* warning ("long branch, distance %d", delta); */ + return 0; +} + +/* Return non-zero if REG is not used after INSN. + We assume REG is a reload reg, and therefore does + not live past labels or calls or jumps. */ +int +reg_unused_after (reg, insn) + rtx reg; + rtx insn; +{ + enum rtx_code code, prev_code = UNKNOWN; + + while ((insn = NEXT_INSN (insn))) + { + if (prev_code == CALL_INSN && call_used_regs[REGNO (reg)]) + return 1; + + code = GET_CODE (insn); + if (GET_CODE (insn) == CODE_LABEL) + return 1; + + if (GET_RTX_CLASS (code) == 'i') + { + rtx set = single_set (insn); + int in_src = set && reg_overlap_mentioned_p (reg, SET_SRC (set)); + if (set && in_src) + return 0; + if (set && reg_overlap_mentioned_p (reg, SET_DEST (set))) + return 1; + if (set == 0 && reg_overlap_mentioned_p (reg, PATTERN (insn))) + return 0; + } + prev_code = code; + } + return 1; +} + +/* The table we use to reference PIC data. */ +static rtx global_offset_table; + +/* The function we use to get at it. */ +static rtx get_pc_symbol; +static char get_pc_symbol_name[256]; + +/* Ensure that we are not using patterns that are not OK with PIC. */ + +int +check_pic (i) + int i; +{ + switch (flag_pic) + { + case 1: + if (GET_CODE (recog_operand[i]) == SYMBOL_REF + || (GET_CODE (recog_operand[i]) == CONST + && ! (GET_CODE (XEXP (recog_operand[i], 0)) == MINUS + && (XEXP (XEXP (recog_operand[i], 0), 0) + == global_offset_table) + && (GET_CODE (XEXP (XEXP (recog_operand[i], 0), 1)) + == CONST)))) + abort (); + case 2: + default: + return 1; + } +} + +/* Return true if X is an address which needs a temporary register when + reloaded while generating PIC code. */ + +int +pic_address_needs_scratch (x) + rtx x; +{ + /* An address which is a symbolic plus a non SMALL_INT needs a temp reg. */ + if (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS + && GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && ! SMALL_INT (XEXP (XEXP (x, 0), 1))) + return 1; + + return 0; +} + +/* Legitimize PIC addresses. If the address is already position-independent, + we return ORIG. Newly generated position-independent addresses go into a + reg. This is REG if non zero, otherwise we allocate register(s) as + necessary. */ + +rtx +legitimize_pic_address (orig, mode, reg) + rtx orig; + enum machine_mode mode ATTRIBUTE_UNUSED; + rtx reg; +{ + if (GET_CODE (orig) == SYMBOL_REF) + { + rtx pic_ref, address; + rtx insn; + + if (reg == 0) + { + if (reload_in_progress || reload_completed) + abort (); + else + reg = gen_reg_rtx (Pmode); + } + + if (flag_pic == 2) + { + /* If not during reload, allocate another temp reg here for loading + in the address, so that these instructions can be optimized + properly. */ + rtx temp_reg = ((reload_in_progress || reload_completed) + ? reg : gen_reg_rtx (Pmode)); + + /* Must put the SYMBOL_REF inside an UNSPEC here so that cse + won't get confused into thinking that these two instructions + are loading in the true address of the symbol. If in the + future a PIC rtx exists, that should be used instead. */ + if (Pmode == SImode) + { + emit_insn (gen_movsi_high_pic (temp_reg, orig)); + emit_insn (gen_movsi_lo_sum_pic (temp_reg, temp_reg, orig)); + } + else + { + emit_insn (gen_movdi_high_pic (temp_reg, orig)); + emit_insn (gen_movdi_lo_sum_pic (temp_reg, temp_reg, orig)); + } + address = temp_reg; + } + else + address = orig; + + pic_ref = gen_rtx_MEM (Pmode, + gen_rtx_PLUS (Pmode, + pic_offset_table_rtx, address)); + current_function_uses_pic_offset_table = 1; + RTX_UNCHANGING_P (pic_ref) = 1; + insn = emit_move_insn (reg, pic_ref); + /* Put a REG_EQUAL note on this insn, so that it can be optimized + by loop. */ + REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUAL, orig, + REG_NOTES (insn)); + return reg; + } + else if (GET_CODE (orig) == CONST) + { + rtx base, offset; + + if (GET_CODE (XEXP (orig, 0)) == PLUS + && XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx) + return orig; + + if (reg == 0) + { + if (reload_in_progress || reload_completed) + abort (); + else + reg = gen_reg_rtx (Pmode); + } + + if (GET_CODE (XEXP (orig, 0)) == PLUS) + { + base = legitimize_pic_address (XEXP (XEXP (orig, 0), 0), Pmode, reg); + offset = legitimize_pic_address (XEXP (XEXP (orig, 0), 1), Pmode, + base == reg ? 0 : reg); + } + else + abort (); + + if (GET_CODE (offset) == CONST_INT) + { + if (SMALL_INT (offset)) + return plus_constant_for_output (base, INTVAL (offset)); + else if (! reload_in_progress && ! reload_completed) + offset = force_reg (Pmode, offset); + else + /* If we reach here, then something is seriously wrong. */ + abort (); + } + return gen_rtx_PLUS (Pmode, base, offset); + } + else if (GET_CODE (orig) == LABEL_REF) + /* ??? Why do we do this? */ + /* Now movsi_pic_label_ref uses it, but we ought to be checking that + the register is live instead, in case it is eliminated. */ + current_function_uses_pic_offset_table = 1; + + return orig; +} + +/* Return the RTX for insns to set the PIC register. */ + +static rtx +pic_setup_code () +{ + rtx seq; + + start_sequence (); + emit_insn (gen_get_pc (pic_offset_table_rtx, global_offset_table, + get_pc_symbol)); + seq = gen_sequence (); + end_sequence (); + + return seq; +} + +/* Emit special PIC prologues and epilogues. */ + +void +finalize_pic () +{ + /* Labels to get the PC in the prologue of this function. */ + int orig_flag_pic = flag_pic; + rtx insn; + + if (current_function_uses_pic_offset_table == 0) + return; + + if (! flag_pic) + abort (); + + /* If we havn't emitted the special get_pc helper function, do so now. */ + if (get_pc_symbol_name[0] == 0) + { + int align; + + ASM_GENERATE_INTERNAL_LABEL (get_pc_symbol_name, "LGETPC", 0); + text_section (); + + align = floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT); + if (align > 0) + ASM_OUTPUT_ALIGN (asm_out_file, align); + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LGETPC", 0); + fputs ("\tretl\n\tadd %o7,%l7,%l7\n", asm_out_file); + } + + /* Initialize every time through, since we can't easily + know this to be permanent. */ + global_offset_table = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_"); + get_pc_symbol = gen_rtx_SYMBOL_REF (Pmode, get_pc_symbol_name); + flag_pic = 0; + + emit_insn_after (pic_setup_code (), get_insns ()); + + /* Insert the code in each nonlocal goto receiver. + If you make changes here or to the nonlocal_goto_receiver + pattern, make sure the unspec_volatile numbers still + match. */ + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + if (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == UNSPEC_VOLATILE + && XINT (PATTERN (insn), 1) == 5) + emit_insn_after (pic_setup_code (), insn); + + flag_pic = orig_flag_pic; + + /* Need to emit this whether or not we obey regdecls, + since setjmp/longjmp can cause life info to screw up. + ??? In the case where we don't obey regdecls, this is not sufficient + since we may not fall out the bottom. */ + emit_insn (gen_rtx_USE (VOIDmode, pic_offset_table_rtx)); +} + +/* Return 1 if RTX is a MEM which is known to be aligned to at + least an 8 byte boundary. */ + +int +mem_min_alignment (mem, desired) + rtx mem; + int desired; +{ + rtx addr, base, offset; + + /* If it's not a MEM we can't accept it. */ + if (GET_CODE (mem) != MEM) + return 0; + + addr = XEXP (mem, 0); + base = offset = NULL_RTX; + if (GET_CODE (addr) == PLUS) + { + if (GET_CODE (XEXP (addr, 0)) == REG) + { + base = XEXP (addr, 0); + + /* What we are saying here is that if the base + REG is aligned properly, the compiler will make + sure any REG based index upon it will be so + as well. */ + if (GET_CODE (XEXP (addr, 1)) == CONST_INT) + offset = XEXP (addr, 1); + else + offset = const0_rtx; + } + } + else if (GET_CODE (addr) == REG) + { + base = addr; + offset = const0_rtx; + } + + if (base != NULL_RTX) + { + int regno = REGNO (base); + + if (regno != FRAME_POINTER_REGNUM + && regno != STACK_POINTER_REGNUM) + { + /* Check if the compiler has recorded some information + about the alignment of the base REG. If reload has + completed, we already matched with proper alignments. */ + if (((regno_pointer_align != NULL + && REGNO_POINTER_ALIGN (regno) >= desired) + || reload_completed) + && ((INTVAL (offset) & (desired - 1)) == 0)) + return 1; + } + else + { + if (((INTVAL (offset) - SPARC_STACK_BIAS) & (desired - 1)) == 0) + return 1; + } + } + else if (! TARGET_UNALIGNED_DOUBLES + || CONSTANT_P (addr) + || GET_CODE (addr) == LO_SUM) + { + /* Anything else we know is properly aligned unless TARGET_UNALIGNED_DOUBLES + is true, in which case we can only assume that an access is aligned if + it is to a constant address, or the address involves a LO_SUM. */ + return 1; + } + + /* An obviously unaligned address. */ + return 0; +} + + +/* Vectors to keep interesting information about registers where it can easily + be got. We use to use the actual mode value as the bit number, but there + are more than 32 modes now. Instead we use two tables: one indexed by + hard register number, and one indexed by mode. */ + +/* The purpose of sparc_mode_class is to shrink the range of modes so that + they all fit (as bit numbers) in a 32 bit word (again). Each real mode is + mapped into one sparc_mode_class mode. */ + +enum sparc_mode_class { + S_MODE, D_MODE, T_MODE, O_MODE, + SF_MODE, DF_MODE, TF_MODE, OF_MODE, + CC_MODE, CCFP_MODE +}; + +/* Modes for single-word and smaller quantities. */ +#define S_MODES ((1 << (int) S_MODE) | (1 << (int) SF_MODE)) + +/* Modes for double-word and smaller quantities. */ +#define D_MODES (S_MODES | (1 << (int) D_MODE) | (1 << DF_MODE)) + +/* Modes for quad-word and smaller quantities. */ +#define T_MODES (D_MODES | (1 << (int) T_MODE) | (1 << (int) TF_MODE)) + +/* Modes for single-float quantities. We must allow any single word or + smaller quantity. This is because the fix/float conversion instructions + take integer inputs/outputs from the float registers. */ +#define SF_MODES (S_MODES) + +/* Modes for double-float and smaller quantities. */ +#define DF_MODES (S_MODES | D_MODES) + +#define DF_MODES64 DF_MODES + +/* Modes for double-float only quantities. */ +#define DF_ONLY_MODES ((1 << (int) DF_MODE) | (1 << (int) D_MODE)) + +/* Modes for double-float and larger quantities. */ +#define DF_UP_MODES (DF_ONLY_MODES | TF_ONLY_MODES) + +/* Modes for quad-float only quantities. */ +#define TF_ONLY_MODES (1 << (int) TF_MODE) + +/* Modes for quad-float and smaller quantities. */ +#define TF_MODES (DF_MODES | TF_ONLY_MODES) + +#define TF_MODES64 (DF_MODES64 | TF_ONLY_MODES) + +/* Modes for condition codes. */ +#define CC_MODES (1 << (int) CC_MODE) +#define CCFP_MODES (1 << (int) CCFP_MODE) + +/* Value is 1 if register/mode pair is acceptable on sparc. + The funny mixture of D and T modes is because integer operations + do not specially operate on tetra quantities, so non-quad-aligned + registers can hold quadword quantities (except %o4 and %i4 because + they cross fixed registers). */ + +/* This points to either the 32 bit or the 64 bit version. */ +int *hard_regno_mode_classes; + +static int hard_32bit_mode_classes[] = { + S_MODES, S_MODES, T_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES, + T_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES, D_MODES, S_MODES, + T_MODES, S_MODES, T_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES, + T_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES, D_MODES, S_MODES, + + TF_MODES, SF_MODES, DF_MODES, SF_MODES, TF_MODES, SF_MODES, DF_MODES, SF_MODES, + TF_MODES, SF_MODES, DF_MODES, SF_MODES, TF_MODES, SF_MODES, DF_MODES, SF_MODES, + TF_MODES, SF_MODES, DF_MODES, SF_MODES, TF_MODES, SF_MODES, DF_MODES, SF_MODES, + TF_MODES, SF_MODES, DF_MODES, SF_MODES, TF_MODES, SF_MODES, DF_MODES, SF_MODES, + + /* FP regs f32 to f63. Only the even numbered registers actually exist, + and none can hold SFmode/SImode values. */ + DF_UP_MODES, 0, DF_ONLY_MODES, 0, DF_UP_MODES, 0, DF_ONLY_MODES, 0, + DF_UP_MODES, 0, DF_ONLY_MODES, 0, DF_UP_MODES, 0, DF_ONLY_MODES, 0, + DF_UP_MODES, 0, DF_ONLY_MODES, 0, DF_UP_MODES, 0, DF_ONLY_MODES, 0, + DF_UP_MODES, 0, DF_ONLY_MODES, 0, DF_UP_MODES, 0, DF_ONLY_MODES, 0, + + /* %fcc[0123] */ + CCFP_MODES, CCFP_MODES, CCFP_MODES, CCFP_MODES, + + /* %icc */ + CC_MODES +}; + +static int hard_64bit_mode_classes[] = { + D_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, + T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, + T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, + T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, + + TF_MODES64, SF_MODES, DF_MODES64, SF_MODES, TF_MODES64, SF_MODES, DF_MODES64, SF_MODES, + TF_MODES64, SF_MODES, DF_MODES64, SF_MODES, TF_MODES64, SF_MODES, DF_MODES64, SF_MODES, + TF_MODES64, SF_MODES, DF_MODES64, SF_MODES, TF_MODES64, SF_MODES, DF_MODES64, SF_MODES, + TF_MODES64, SF_MODES, DF_MODES64, SF_MODES, TF_MODES64, SF_MODES, DF_MODES64, SF_MODES, + + /* FP regs f32 to f63. Only the even numbered registers actually exist, + and none can hold SFmode/SImode values. */ + DF_UP_MODES, 0, DF_ONLY_MODES, 0, DF_UP_MODES, 0, DF_ONLY_MODES, 0, + DF_UP_MODES, 0, DF_ONLY_MODES, 0, DF_UP_MODES, 0, DF_ONLY_MODES, 0, + DF_UP_MODES, 0, DF_ONLY_MODES, 0, DF_UP_MODES, 0, DF_ONLY_MODES, 0, + DF_UP_MODES, 0, DF_ONLY_MODES, 0, DF_UP_MODES, 0, DF_ONLY_MODES, 0, + + /* %fcc[0123] */ + CCFP_MODES, CCFP_MODES, CCFP_MODES, CCFP_MODES, + + /* %icc */ + CC_MODES +}; + +int sparc_mode_class [NUM_MACHINE_MODES]; + +enum reg_class sparc_regno_reg_class[FIRST_PSEUDO_REGISTER]; + +static void +sparc_init_modes () +{ + int i; + + for (i = 0; i < NUM_MACHINE_MODES; i++) + { + switch (GET_MODE_CLASS (i)) + { + case MODE_INT: + case MODE_PARTIAL_INT: + case MODE_COMPLEX_INT: + if (GET_MODE_SIZE (i) <= 4) + sparc_mode_class[i] = 1 << (int) S_MODE; + else if (GET_MODE_SIZE (i) == 8) + sparc_mode_class[i] = 1 << (int) D_MODE; + else if (GET_MODE_SIZE (i) == 16) + sparc_mode_class[i] = 1 << (int) T_MODE; + else if (GET_MODE_SIZE (i) == 32) + sparc_mode_class[i] = 1 << (int) O_MODE; + else + sparc_mode_class[i] = 0; + break; + case MODE_FLOAT: + case MODE_COMPLEX_FLOAT: + if (GET_MODE_SIZE (i) <= 4) + sparc_mode_class[i] = 1 << (int) SF_MODE; + else if (GET_MODE_SIZE (i) == 8) + sparc_mode_class[i] = 1 << (int) DF_MODE; + else if (GET_MODE_SIZE (i) == 16) + sparc_mode_class[i] = 1 << (int) TF_MODE; + else if (GET_MODE_SIZE (i) == 32) + sparc_mode_class[i] = 1 << (int) OF_MODE; + else + sparc_mode_class[i] = 0; + break; + case MODE_CC: + default: + /* mode_class hasn't been initialized yet for EXTRA_CC_MODES, so + we must explicitly check for them here. */ + if (i == (int) CCFPmode || i == (int) CCFPEmode) + sparc_mode_class[i] = 1 << (int) CCFP_MODE; + else if (i == (int) CCmode || i == (int) CC_NOOVmode + || i == (int) CCXmode || i == (int) CCX_NOOVmode) + sparc_mode_class[i] = 1 << (int) CC_MODE; + else + sparc_mode_class[i] = 0; + break; + } + } + + if (TARGET_ARCH64) + hard_regno_mode_classes = hard_64bit_mode_classes; + else + hard_regno_mode_classes = hard_32bit_mode_classes; + + /* Initialize the array used by REGNO_REG_CLASS. */ + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + { + if (i < 16 && TARGET_V8PLUS) + sparc_regno_reg_class[i] = I64_REGS; + else if (i < 32) + sparc_regno_reg_class[i] = GENERAL_REGS; + else if (i < 64) + sparc_regno_reg_class[i] = FP_REGS; + else if (i < 96) + sparc_regno_reg_class[i] = EXTRA_FP_REGS; + else if (i < 100) + sparc_regno_reg_class[i] = FPCC_REGS; + else + sparc_regno_reg_class[i] = NO_REGS; + } +} + +/* Save non call used registers from LOW to HIGH at BASE+OFFSET. + N_REGS is the number of 4-byte regs saved thus far. This applies even to + v9 int regs as it simplifies the code. */ + +static int +save_regs (file, low, high, base, offset, n_regs, real_offset) + FILE *file; + int low, high; + char *base; + int offset; + int n_regs; + int real_offset; +{ + int i; + + if (TARGET_ARCH64 && high <= 32) + { + for (i = low; i < high; i++) + { + if (regs_ever_live[i] && ! call_used_regs[i]) + { + fprintf (file, "\tstx\t%s, [%s+%d]\n", + reg_names[i], base, offset + 4 * n_regs); + if (dwarf2out_do_frame ()) + dwarf2out_reg_save ("", i, real_offset + 4 * n_regs); + n_regs += 2; + } + } + } + else + { + for (i = low; i < high; i += 2) + { + if (regs_ever_live[i] && ! call_used_regs[i]) + { + if (regs_ever_live[i+1] && ! call_used_regs[i+1]) + { + fprintf (file, "\tstd\t%s, [%s+%d]\n", + reg_names[i], base, offset + 4 * n_regs); + if (dwarf2out_do_frame ()) + { + char *l = dwarf2out_cfi_label (); + dwarf2out_reg_save (l, i, real_offset + 4 * n_regs); + dwarf2out_reg_save (l, i+1, real_offset + 4 * n_regs + 4); + } + n_regs += 2; + } + else + { + fprintf (file, "\tst\t%s, [%s+%d]\n", + reg_names[i], base, offset + 4 * n_regs); + if (dwarf2out_do_frame ()) + dwarf2out_reg_save ("", i, real_offset + 4 * n_regs); + n_regs += 2; + } + } + else + { + if (regs_ever_live[i+1] && ! call_used_regs[i+1]) + { + fprintf (file, "\tst\t%s, [%s+%d]\n", + reg_names[i+1], base, offset + 4 * n_regs + 4); + if (dwarf2out_do_frame ()) + dwarf2out_reg_save ("", i + 1, real_offset + 4 * n_regs + 4); + n_regs += 2; + } + } + } + } + return n_regs; +} + +/* Restore non call used registers from LOW to HIGH at BASE+OFFSET. + + N_REGS is the number of 4-byte regs saved thus far. This applies even to + v9 int regs as it simplifies the code. */ + +static int +restore_regs (file, low, high, base, offset, n_regs) + FILE *file; + int low, high; + char *base; + int offset; + int n_regs; +{ + int i; + + if (TARGET_ARCH64 && high <= 32) + { + for (i = low; i < high; i++) + { + if (regs_ever_live[i] && ! call_used_regs[i]) + fprintf (file, "\tldx\t[%s+%d], %s\n", + base, offset + 4 * n_regs, reg_names[i]), + n_regs += 2; + } + } + else + { + for (i = low; i < high; i += 2) + { + if (regs_ever_live[i] && ! call_used_regs[i]) + if (regs_ever_live[i+1] && ! call_used_regs[i+1]) + fprintf (file, "\tldd\t[%s+%d], %s\n", + base, offset + 4 * n_regs, reg_names[i]), + n_regs += 2; + else + fprintf (file, "\tld\t[%s+%d],%s\n", + base, offset + 4 * n_regs, reg_names[i]), + n_regs += 2; + else if (regs_ever_live[i+1] && ! call_used_regs[i+1]) + fprintf (file, "\tld\t[%s+%d],%s\n", + base, offset + 4 * n_regs + 4, reg_names[i+1]), + n_regs += 2; + } + } + return n_regs; +} + +/* Static variables we want to share between prologue and epilogue. */ + +/* Number of live general or floating point registers needed to be saved + (as 4-byte quantities). This is only done if TARGET_EPILOGUE. */ +static int num_gfregs; + +/* Compute the frame size required by the function. This function is called + during the reload pass and also by output_function_prologue(). */ + +int +compute_frame_size (size, leaf_function) + int size; + int leaf_function; +{ + int n_regs = 0, i; + int outgoing_args_size = (current_function_outgoing_args_size + + REG_PARM_STACK_SPACE (current_function_decl)); + + if (TARGET_EPILOGUE) + { + /* N_REGS is the number of 4-byte regs saved thus far. This applies + even to v9 int regs to be consistent with save_regs/restore_regs. */ + + if (TARGET_ARCH64) + { + for (i = 0; i < 8; i++) + if (regs_ever_live[i] && ! call_used_regs[i]) + n_regs += 2; + } + else + { + for (i = 0; i < 8; i += 2) + if ((regs_ever_live[i] && ! call_used_regs[i]) + || (regs_ever_live[i+1] && ! call_used_regs[i+1])) + n_regs += 2; + } + + for (i = 32; i < (TARGET_V9 ? 96 : 64); i += 2) + if ((regs_ever_live[i] && ! call_used_regs[i]) + || (regs_ever_live[i+1] && ! call_used_regs[i+1])) + n_regs += 2; + } + + /* Set up values for use in `function_epilogue'. */ + num_gfregs = n_regs; + + if (leaf_function && n_regs == 0 + && size == 0 && current_function_outgoing_args_size == 0) + { + actual_fsize = apparent_fsize = 0; + } + else + { + /* We subtract STARTING_FRAME_OFFSET, remember it's negative. + The stack bias (if any) is taken out to undo its effects. */ + apparent_fsize = (size - STARTING_FRAME_OFFSET + SPARC_STACK_BIAS + 7) & -8; + apparent_fsize += n_regs * 4; + actual_fsize = apparent_fsize + ((outgoing_args_size + 7) & -8); + } + + /* Make sure nothing can clobber our register windows. + If a SAVE must be done, or there is a stack-local variable, + the register window area must be allocated. + ??? For v8 we apparently need an additional 8 bytes of reserved space. */ + if (leaf_function == 0 || size > 0) + actual_fsize += (16 * UNITS_PER_WORD) + (TARGET_ARCH64 ? 0 : 8); + + return SPARC_STACK_ALIGN (actual_fsize); +} + +/* Build a (32 bit) big number in a register. */ +/* ??? We may be able to use the set macro here too. */ + +static void +build_big_number (file, num, reg) + FILE *file; + int num; + char *reg; +{ + if (num >= 0 || ! TARGET_ARCH64) + { + fprintf (file, "\tsethi\t%%hi(%d), %s\n", num, reg); + if ((num & 0x3ff) != 0) + fprintf (file, "\tor\t%s, %%lo(%d), %s\n", reg, num, reg); + } + else /* num < 0 && TARGET_ARCH64 */ + { + /* Sethi does not sign extend, so we must use a little trickery + to use it for negative numbers. Invert the constant before + loading it in, then use xor immediate to invert the loaded bits + (along with the upper 32 bits) to the desired constant. This + works because the sethi and immediate fields overlap. */ + int asize = num; + int inv = ~asize; + int low = -0x400 + (asize & 0x3FF); + + fprintf (file, "\tsethi\t%%hi(%d), %s\n\txor\t%s, %d, %s\n", + inv, reg, reg, low, reg); + } +} + +/* Output code for the function prologue. */ + +void +output_function_prologue (file, size, leaf_function) + FILE *file; + int size; + int leaf_function; +{ + /* Need to use actual_fsize, since we are also allocating + space for our callee (and our own register save area). */ + actual_fsize = compute_frame_size (size, leaf_function); + + if (leaf_function) + { + frame_base_name = "%sp"; + frame_base_offset = actual_fsize + SPARC_STACK_BIAS; + } + else + { + frame_base_name = "%fp"; + frame_base_offset = SPARC_STACK_BIAS; + } + + /* This is only for the human reader. */ + fprintf (file, "\t%s#PROLOGUE# 0\n", ASM_COMMENT_START); + + if (actual_fsize == 0) + /* do nothing. */ ; + else if (! leaf_function && ! TARGET_BROKEN_SAVERESTORE) + { + if (actual_fsize <= 4096) + fprintf (file, "\tsave\t%%sp, -%d, %%sp\n", actual_fsize); + else if (actual_fsize <= 8192) + { + fprintf (file, "\tsave\t%%sp, -4096, %%sp\n"); + fprintf (file, "\tadd\t%%sp, -%d, %%sp\n", actual_fsize - 4096); + } + else + { + build_big_number (file, -actual_fsize, "%g1"); + fprintf (file, "\tsave\t%%sp, %%g1, %%sp\n"); + } + } + else if (! leaf_function && TARGET_BROKEN_SAVERESTORE) + { + /* We assume the environment will properly handle or otherwise avoid + trouble associated with an interrupt occurring after the `save' or + trap occurring during it. */ + fprintf (file, "\tsave\n"); + + if (actual_fsize <= 4096) + fprintf (file, "\tadd\t%%fp, -%d, %%sp\n", actual_fsize); + else if (actual_fsize <= 8192) + { + fprintf (file, "\tadd\t%%fp, -4096, %%sp\n"); + fprintf (file, "\tadd\t%%fp, -%d, %%sp\n", actual_fsize - 4096); + } + else + { + build_big_number (file, -actual_fsize, "%g1"); + fprintf (file, "\tadd\t%%fp, %%g1, %%sp\n"); + } + } + else /* leaf function */ + { + if (actual_fsize <= 4096) + fprintf (file, "\tadd\t%%sp, -%d, %%sp\n", actual_fsize); + else if (actual_fsize <= 8192) + { + fprintf (file, "\tadd\t%%sp, -4096, %%sp\n"); + fprintf (file, "\tadd\t%%sp, -%d, %%sp\n", actual_fsize - 4096); + } + else + { + build_big_number (file, -actual_fsize, "%g1"); + fprintf (file, "\tadd\t%%sp, %%g1, %%sp\n"); + } + } + + if (dwarf2out_do_frame () && actual_fsize) + { + char *label = dwarf2out_cfi_label (); + + /* The canonical frame address refers to the top of the frame. */ + dwarf2out_def_cfa (label, (leaf_function ? STACK_POINTER_REGNUM + : FRAME_POINTER_REGNUM), + frame_base_offset); + + if (! leaf_function) + { + /* Note the register window save. This tells the unwinder that + it needs to restore the window registers from the previous + frame's window save area at 0(cfa). */ + dwarf2out_window_save (label); + + /* The return address (-8) is now in %i7. */ + dwarf2out_return_reg (label, 31); + } + } + + /* If doing anything with PIC, do it now. */ + if (! flag_pic) + fprintf (file, "\t%s#PROLOGUE# 1\n", ASM_COMMENT_START); + + /* Call saved registers are saved just above the outgoing argument area. */ + if (num_gfregs) + { + int offset, real_offset, n_regs; + char *base; + + real_offset = -apparent_fsize; + offset = -apparent_fsize + frame_base_offset; + if (offset < -4096 || offset + num_gfregs * 4 > 4096) + { + /* ??? This might be optimized a little as %g1 might already have a + value close enough that a single add insn will do. */ + /* ??? Although, all of this is probably only a temporary fix + because if %g1 can hold a function result, then + output_function_epilogue will lose (the result will get + clobbered). */ + build_big_number (file, offset, "%g1"); + fprintf (file, "\tadd\t%s, %%g1, %%g1\n", frame_base_name); + base = "%g1"; + offset = 0; + } + else + { + base = frame_base_name; + } + + n_regs = 0; + if (TARGET_EPILOGUE && ! leaf_function) + /* ??? Originally saved regs 0-15 here. */ + n_regs = save_regs (file, 0, 8, base, offset, 0, real_offset); + else if (leaf_function) + /* ??? Originally saved regs 0-31 here. */ + n_regs = save_regs (file, 0, 8, base, offset, 0, real_offset); + if (TARGET_EPILOGUE) + save_regs (file, 32, TARGET_V9 ? 96 : 64, base, offset, n_regs, + real_offset); + } + + leaf_label = 0; + if (leaf_function && actual_fsize != 0) + { + /* warning ("leaf procedure with frame size %d", actual_fsize); */ + if (! TARGET_EPILOGUE) + leaf_label = gen_label_rtx (); + } +} + +/* Output code for the function epilogue. */ + +void +output_function_epilogue (file, size, leaf_function) + FILE *file; + int size ATTRIBUTE_UNUSED; + int leaf_function; +{ + char *ret; + + if (leaf_label) + { + emit_label_after (leaf_label, get_last_insn ()); + final_scan_insn (get_last_insn (), file, 0, 0, 1); + } + +#ifdef FUNCTION_BLOCK_PROFILER_EXIT + else if (profile_block_flag == 2) + { + FUNCTION_BLOCK_PROFILER_EXIT(file); + } +#endif + + else if (current_function_epilogue_delay_list == 0) + { + /* If code does not drop into the epilogue, we need + do nothing except output pending case vectors. */ + rtx insn = get_last_insn (); + if (GET_CODE (insn) == NOTE) + insn = prev_nonnote_insn (insn); + if (insn && GET_CODE (insn) == BARRIER) + goto output_vectors; + } + + /* Restore any call saved registers. */ + if (num_gfregs) + { + int offset, n_regs; + char *base; + + offset = -apparent_fsize + frame_base_offset; + if (offset < -4096 || offset + num_gfregs * 4 > 4096 - 8 /*double*/) + { + build_big_number (file, offset, "%g1"); + fprintf (file, "\tadd\t%s, %%g1, %%g1\n", frame_base_name); + base = "%g1"; + offset = 0; + } + else + { + base = frame_base_name; + } + + n_regs = 0; + if (TARGET_EPILOGUE && ! leaf_function) + /* ??? Originally saved regs 0-15 here. */ + n_regs = restore_regs (file, 0, 8, base, offset, 0); + else if (leaf_function) + /* ??? Originally saved regs 0-31 here. */ + n_regs = restore_regs (file, 0, 8, base, offset, 0); + if (TARGET_EPILOGUE) + restore_regs (file, 32, TARGET_V9 ? 96 : 64, base, offset, n_regs); + } + + /* Work out how to skip the caller's unimp instruction if required. */ + if (leaf_function) + ret = (SKIP_CALLERS_UNIMP_P ? "jmp\t%o7+12" : "retl"); + else + ret = (SKIP_CALLERS_UNIMP_P ? "jmp\t%i7+12" : "ret"); + + if (TARGET_EPILOGUE || leaf_label) + { + int old_target_epilogue = TARGET_EPILOGUE; + target_flags &= ~old_target_epilogue; + + if (! leaf_function) + { + /* If we wound up with things in our delay slot, flush them here. */ + if (current_function_epilogue_delay_list) + { + rtx insn = emit_jump_insn_after (gen_rtx_RETURN (VOIDmode), + get_last_insn ()); + PATTERN (insn) = gen_rtx_PARALLEL (VOIDmode, + gen_rtvec (2, + PATTERN (XEXP (current_function_epilogue_delay_list, 0)), + PATTERN (insn))); + final_scan_insn (insn, file, 1, 0, 1); + } + else if (TARGET_V9 && ! SKIP_CALLERS_UNIMP_P) + fputs ("\treturn\t%i7+8\n\tnop\n", file); + else + fprintf (file, "\t%s\n\trestore\n", ret); + } + /* All of the following cases are for leaf functions. */ + else if (current_function_epilogue_delay_list) + { + /* eligible_for_epilogue_delay_slot ensures that if this is a + leaf function, then we will only have insn in the delay slot + if the frame size is zero, thus no adjust for the stack is + needed here. */ + if (actual_fsize != 0) + abort (); + fprintf (file, "\t%s\n", ret); + final_scan_insn (XEXP (current_function_epilogue_delay_list, 0), + file, 1, 0, 1); + } + /* Output 'nop' instead of 'sub %sp,-0,%sp' when no frame, so as to + avoid generating confusing assembly language output. */ + else if (actual_fsize == 0) + fprintf (file, "\t%s\n\tnop\n", ret); + else if (actual_fsize <= 4096) + fprintf (file, "\t%s\n\tsub\t%%sp, -%d, %%sp\n", ret, actual_fsize); + else if (actual_fsize <= 8192) + fprintf (file, "\tsub\t%%sp, -4096, %%sp\n\t%s\n\tsub\t%%sp, -%d, %%sp\n", + ret, actual_fsize - 4096); + else if ((actual_fsize & 0x3ff) == 0) + fprintf (file, "\tsethi\t%%hi(%d), %%g1\n\t%s\n\tadd\t%%sp, %%g1, %%sp\n", + actual_fsize, ret); + else + fprintf (file, "\tsethi\t%%hi(%d), %%g1\n\tor\t%%g1, %%lo(%d), %%g1\n\t%s\n\tadd\t%%sp, %%g1, %%sp\n", + actual_fsize, actual_fsize, ret); + target_flags |= old_target_epilogue; + } + + output_vectors: + sparc_output_deferred_case_vectors (); +} + +/* Functions for handling argument passing. + + For v8 the first six args are normally in registers and the rest are + pushed. Any arg that starts within the first 6 words is at least + partially passed in a register unless its data type forbids. + + For v9, the argument registers are laid out as an array of 16 elements + and arguments are added sequentially. The first 6 int args and up to the + first 16 fp args (depending on size) are passed in regs. + + Slot Stack Integral Float Float in structure Double Long Double + ---- ----- -------- ----- ------------------ ------ ----------- + 15 [SP+248] %f31 %f30,%f31 %d30 + 14 [SP+240] %f29 %f28,%f29 %d28 %q28 + 13 [SP+232] %f27 %f26,%f27 %d26 + 12 [SP+224] %f25 %f24,%f25 %d24 %q24 + 11 [SP+216] %f23 %f22,%f23 %d22 + 10 [SP+208] %f21 %f20,%f21 %d20 %q20 + 9 [SP+200] %f19 %f18,%f19 %d18 + 8 [SP+192] %f17 %f16,%f17 %d16 %q16 + 7 [SP+184] %f15 %f14,%f15 %d14 + 6 [SP+176] %f13 %f12,%f13 %d12 %q12 + 5 [SP+168] %o5 %f11 %f10,%f11 %d10 + 4 [SP+160] %o4 %f9 %f8,%f9 %d8 %q8 + 3 [SP+152] %o3 %f7 %f6,%f7 %d6 + 2 [SP+144] %o2 %f5 %f4,%f5 %d4 %q4 + 1 [SP+136] %o1 %f3 %f2,%f3 %d2 + 0 [SP+128] %o0 %f1 %f0,%f1 %d0 %q0 + + Here SP = %sp if -mno-stack-bias or %sp+stack_bias otherwise. + + Integral arguments are always passed as 64 bit quantities appropriately + extended. + + Passing of floating point values is handled as follows. + If a prototype is in scope: + If the value is in a named argument (i.e. not a stdarg function or a + value not part of the `...') then the value is passed in the appropriate + fp reg. + If the value is part of the `...' and is passed in one of the first 6 + slots then the value is passed in the appropriate int reg. + If the value is part of the `...' and is not passed in one of the first 6 + slots then the value is passed in memory. + If a prototype is not in scope: + If the value is one of the first 6 arguments the value is passed in the + appropriate integer reg and the appropriate fp reg. + If the value is not one of the first 6 arguments the value is passed in + the appropriate fp reg and in memory. + */ + +/* Maximum number of int regs for args. */ +#define SPARC_INT_ARG_MAX 6 +/* Maximum number of fp regs for args. */ +#define SPARC_FP_ARG_MAX 16 + +#define ROUND_ADVANCE(SIZE) (((SIZE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) + +/* Handle the INIT_CUMULATIVE_ARGS macro. + 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. */ + +void +init_cumulative_args (cum, fntype, libname, indirect) + CUMULATIVE_ARGS *cum; + tree fntype; + tree libname ATTRIBUTE_UNUSED; + int indirect ATTRIBUTE_UNUSED; +{ + cum->words = 0; + cum->prototype_p = fntype && TYPE_ARG_TYPES (fntype); + cum->libcall_p = fntype == 0; +} + +/* Compute the slot number to pass an argument in. + Returns the slot number or -1 if passing on the stack. + + CUM is a variable of type CUMULATIVE_ARGS which gives info about + the preceding args and about the function being called. + 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. + NAMED is nonzero if this argument is a named parameter + (otherwise it is an extra parameter matching an ellipsis). + INCOMING_P is zero for FUNCTION_ARG, nonzero for FUNCTION_INCOMING_ARG. + *PREGNO records the register number to use if scalar type. + *PPADDING records the amount of padding needed in words. */ + +static int +function_arg_slotno (cum, mode, type, named, incoming_p, pregno, ppadding) + const CUMULATIVE_ARGS *cum; + enum machine_mode mode; + tree type; + int named; + int incoming_p; + int *pregno; + int *ppadding; +{ + int regbase = (incoming_p + ? SPARC_INCOMING_INT_ARG_FIRST + : SPARC_OUTGOING_INT_ARG_FIRST); + int slotno = cum->words; + int regno; + + *ppadding = 0; + + if (type != 0 && TREE_ADDRESSABLE (type)) + return -1; + if (TARGET_ARCH32 + && type != 0 && mode == BLKmode + && TYPE_ALIGN (type) % PARM_BOUNDARY != 0) + return -1; + + switch (mode) + { + case VOIDmode : + /* MODE is VOIDmode when generating the actual call. + See emit_call_1. */ + return -1; + + case QImode : case CQImode : + case HImode : case CHImode : + case SImode : case CSImode : + case DImode : case CDImode : + if (slotno >= SPARC_INT_ARG_MAX) + return -1; + regno = regbase + slotno; + break; + + case SFmode : case SCmode : + case DFmode : case DCmode : + case TFmode : case TCmode : + if (TARGET_ARCH32) + { + if (slotno >= SPARC_INT_ARG_MAX) + return -1; + regno = regbase + slotno; + } + else + { + if ((mode == TFmode || mode == TCmode) + && (slotno & 1) != 0) + slotno++, *ppadding = 1; + if (TARGET_FPU && named) + { + if (slotno >= SPARC_FP_ARG_MAX) + return -1; + regno = SPARC_FP_ARG_FIRST + slotno * 2; + if (mode == SFmode) + regno++; + } + else + { + if (slotno >= SPARC_INT_ARG_MAX) + return -1; + regno = regbase + slotno; + } + } + break; + + case BLKmode : + /* For sparc64, objects requiring 16 byte alignment get it. */ + if (TARGET_ARCH64) + { + if (type && TYPE_ALIGN (type) == 128 && (slotno & 1) != 0) + slotno++, *ppadding = 1; + } + + if (TARGET_ARCH32 + || (type && TREE_CODE (type) == UNION_TYPE)) + { + if (slotno >= SPARC_INT_ARG_MAX) + return -1; + regno = regbase + slotno; + } + else + { + tree field; + int intregs_p = 0, fpregs_p = 0; + /* The ABI obviously doesn't specify how packed + structures are passed. These are defined to be passed + in int regs if possible, otherwise memory. */ + int packed_p = 0; + + /* First see what kinds of registers we need. */ + for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field)) + { + if (TREE_CODE (field) == FIELD_DECL) + { + if (TREE_CODE (TREE_TYPE (field)) == REAL_TYPE + && TARGET_FPU) + fpregs_p = 1; + else + intregs_p = 1; + if (DECL_PACKED (field)) + packed_p = 1; + } + } + if (packed_p || !named) + fpregs_p = 0, intregs_p = 1; + + /* If all arg slots are filled, then must pass on stack. */ + if (fpregs_p && slotno >= SPARC_FP_ARG_MAX) + return -1; + /* If there are only int args and all int arg slots are filled, + then must pass on stack. */ + if (!fpregs_p && intregs_p && slotno >= SPARC_INT_ARG_MAX) + return -1; + /* Note that even if all int arg slots are filled, fp members may + still be passed in regs if such regs are available. + *PREGNO isn't set because there may be more than one, it's up + to the caller to compute them. */ + return slotno; + } + break; + + default : + abort (); + } + + *pregno = regno; + return slotno; +} + +/* Handle recursive register counting for structure field layout. */ + +struct function_arg_record_value_parms +{ + rtx ret; + int slotno, named, regbase; + int nregs, intoffset; +}; + +static void function_arg_record_value_3 + PROTO((int, struct function_arg_record_value_parms *)); +static void function_arg_record_value_2 + PROTO((tree, int, struct function_arg_record_value_parms *)); +static rtx function_arg_record_value + PROTO((tree, enum machine_mode, int, int, int)); + +static void +function_arg_record_value_1 (type, startbitpos, parms) + tree type; + int startbitpos; + struct function_arg_record_value_parms *parms; +{ + tree field; + + /* The ABI obviously doesn't specify how packed structures are + passed. These are defined to be passed in int regs if possible, + otherwise memory. */ + int packed_p = 0; + + /* We need to compute how many registers are needed so we can + allocate the PARALLEL but before we can do that we need to know + whether there are any packed fields. If there are, int regs are + used regardless of whether there are fp values present. */ + for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field)) + { + if (TREE_CODE (field) == FIELD_DECL && DECL_PACKED (field)) + { + packed_p = 1; + break; + } + } + + /* Compute how many registers we need. */ + for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field)) + { + if (TREE_CODE (field) == FIELD_DECL) + { + int bitpos = startbitpos; + if (DECL_FIELD_BITPOS (field)) + bitpos += TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field)); + /* ??? FIXME: else assume zero offset. */ + + if (TREE_CODE (TREE_TYPE (field)) == RECORD_TYPE) + { + function_arg_record_value_1 (TREE_TYPE (field), bitpos, parms); + } + else if (TREE_CODE (TREE_TYPE (field)) == REAL_TYPE + && TARGET_FPU + && ! packed_p + && parms->named) + { + if (parms->intoffset != -1) + { + int intslots, this_slotno; + + intslots = (bitpos - parms->intoffset + BITS_PER_WORD - 1) + / BITS_PER_WORD; + this_slotno = parms->slotno + parms->intoffset + / BITS_PER_WORD; + + intslots = MIN (intslots, SPARC_INT_ARG_MAX - this_slotno); + intslots = MAX (intslots, 0); + parms->nregs += intslots; + parms->intoffset = -1; + } + + /* There's no need to check this_slotno < SPARC_FP_ARG MAX. + If it wasn't true we wouldn't be here. */ + parms->nregs += 1; + } + else + { + if (parms->intoffset == -1) + parms->intoffset = bitpos; + } + } + } +} + +/* Handle recursive structure field register assignment. */ + +static void +function_arg_record_value_3 (bitpos, parms) + int bitpos; + struct function_arg_record_value_parms *parms; +{ + enum machine_mode mode; + int regno, this_slotno, intslots, intoffset; + rtx reg; + + if (parms->intoffset == -1) + return; + intoffset = parms->intoffset; + parms->intoffset = -1; + + intslots = (bitpos - intoffset + BITS_PER_WORD - 1) / BITS_PER_WORD; + this_slotno = parms->slotno + intoffset / BITS_PER_WORD; + + intslots = MIN (intslots, SPARC_INT_ARG_MAX - this_slotno); + if (intslots <= 0) + return; + + /* If this is the trailing part of a word, only load that much into + the register. Otherwise load the whole register. Note that in + the latter case we may pick up unwanted bits. It's not a problem + at the moment but may wish to revisit. */ + + if (intoffset % BITS_PER_WORD != 0) + { + mode = mode_for_size (BITS_PER_WORD - intoffset%BITS_PER_WORD, + MODE_INT, 0); + } + else + mode = word_mode; + + intoffset /= BITS_PER_UNIT; + do + { + regno = parms->regbase + this_slotno; + reg = gen_rtx_REG (mode, regno); + XVECEXP (parms->ret, 0, parms->nregs) + = gen_rtx_EXPR_LIST (VOIDmode, reg, GEN_INT (intoffset)); + + this_slotno += 1; + intoffset = (intoffset | (UNITS_PER_WORD-1)) + 1; + parms->nregs += 1; + intslots -= 1; + } + while (intslots > 0); +} + +static void +function_arg_record_value_2 (type, startbitpos, parms) + tree type; + int startbitpos; + struct function_arg_record_value_parms *parms; +{ + tree field; + int packed_p = 0; + + for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field)) + { + if (TREE_CODE (field) == FIELD_DECL && DECL_PACKED (field)) + { + packed_p = 1; + break; + } + } + + for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field)) + { + if (TREE_CODE (field) == FIELD_DECL) + { + int bitpos = startbitpos; + if (DECL_FIELD_BITPOS (field)) + bitpos += TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field)); + /* ??? FIXME: else assume zero offset. */ + + if (TREE_CODE (TREE_TYPE (field)) == RECORD_TYPE) + { + function_arg_record_value_2 (TREE_TYPE (field), bitpos, parms); + } + else if (TREE_CODE (TREE_TYPE (field)) == REAL_TYPE + && TARGET_FPU + && ! packed_p + && parms->named) + { + int this_slotno = parms->slotno + bitpos / BITS_PER_WORD; + rtx reg; + + function_arg_record_value_3 (bitpos, parms); + + reg = gen_rtx_REG (DECL_MODE (field), + (SPARC_FP_ARG_FIRST + this_slotno * 2 + + (DECL_MODE (field) == SFmode + && (bitpos & 32) != 0))); + XVECEXP (parms->ret, 0, parms->nregs) + = gen_rtx_EXPR_LIST (VOIDmode, reg, + GEN_INT (bitpos / BITS_PER_UNIT)); + parms->nregs += 1; + } + else + { + if (parms->intoffset == -1) + parms->intoffset = bitpos; + } + } + } +} + +static rtx +function_arg_record_value (type, mode, slotno, named, regbase) + tree type; + enum machine_mode mode; + int slotno, named, regbase; +{ + HOST_WIDE_INT typesize = int_size_in_bytes (type); + struct function_arg_record_value_parms parms; + int nregs; + + parms.ret = NULL_RTX; + parms.slotno = slotno; + parms.named = named; + parms.regbase = regbase; + + /* Compute how many registers we need. */ + parms.nregs = 0; + parms.intoffset = 0; + function_arg_record_value_1 (type, 0, &parms); + + if (parms.intoffset != -1) + { + int intslots, this_slotno; + + intslots = (typesize*BITS_PER_UNIT - parms.intoffset + BITS_PER_WORD - 1) + / BITS_PER_WORD; + this_slotno = slotno + parms.intoffset / BITS_PER_WORD; + + intslots = MIN (intslots, SPARC_INT_ARG_MAX - this_slotno); + intslots = MAX (intslots, 0); + + parms.nregs += intslots; + } + nregs = parms.nregs; + + /* Allocate the vector and handle some annoying special cases. */ + if (nregs == 0) + { + /* ??? Empty structure has no value? Duh? */ + if (typesize <= 0) + { + /* Though there's nothing really to store, return a word register + anyway so the rest of gcc doesn't go nuts. Returning a PARALLEL + leads to breakage due to the fact that there are zero bytes to + load. */ + return gen_rtx_REG (mode, regbase); + } + else + { + /* ??? C++ has structures with no fields, and yet a size. Give up + for now and pass everything back in integer registers. */ + nregs = (typesize + UNITS_PER_WORD - 1) / UNITS_PER_WORD; + } + if (nregs + slotno > SPARC_INT_ARG_MAX) + nregs = SPARC_INT_ARG_MAX - slotno; + } + if (nregs == 0) + abort (); + + parms.ret = gen_rtx_PARALLEL (mode, rtvec_alloc (nregs)); + + /* Fill in the entries. */ + parms.nregs = 0; + parms.intoffset = 0; + function_arg_record_value_2 (type, 0, &parms); + function_arg_record_value_3 (typesize * BITS_PER_UNIT, &parms); + + if (parms.nregs != nregs) + abort (); + + return parms.ret; +} + +/* Handle the FUNCTION_ARG macro. + 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. + + CUM is a variable of type CUMULATIVE_ARGS which gives info about + the preceding args and about the function being called. + 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. + NAMED is nonzero if this argument is a named parameter + (otherwise it is an extra parameter matching an ellipsis). + INCOMING_P is zero for FUNCTION_ARG, nonzero for FUNCTION_INCOMING_ARG. */ + +rtx +function_arg (cum, mode, type, named, incoming_p) + const CUMULATIVE_ARGS *cum; + enum machine_mode mode; + tree type; + int named; + int incoming_p; +{ + int regbase = (incoming_p + ? SPARC_INCOMING_INT_ARG_FIRST + : SPARC_OUTGOING_INT_ARG_FIRST); + int slotno, regno, padding; + rtx reg; + + slotno = function_arg_slotno (cum, mode, type, named, incoming_p, + ®no, &padding); + + if (slotno == -1) + return 0; + + if (TARGET_ARCH32) + { + reg = gen_rtx_REG (mode, regno); + return reg; + } + + /* v9 fp args in reg slots beyond the int reg slots get passed in regs + but also have the slot allocated for them. + If no prototype is in scope fp values in register slots get passed + in two places, either fp regs and int regs or fp regs and memory. */ + if ((GET_MODE_CLASS (mode) == MODE_FLOAT + || GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT) + && SPARC_FP_REG_P (regno)) + { + reg = gen_rtx_REG (mode, regno); + if (cum->prototype_p || cum->libcall_p) + { + /* "* 2" because fp reg numbers are recorded in 4 byte + quantities. */ +#if 0 + /* ??? This will cause the value to be passed in the fp reg and + in the stack. When a prototype exists we want to pass the + value in the reg but reserve space on the stack. That's an + optimization, and is deferred [for a bit]. */ + if ((regno - SPARC_FP_ARG_FIRST) >= SPARC_INT_ARG_MAX * 2) + return gen_rtx_PARALLEL (mode, + gen_rtvec (2, + gen_rtx_EXPR_LIST (VOIDmode, + NULL_RTX, const0_rtx), + gen_rtx_EXPR_LIST (VOIDmode, + reg, const0_rtx))); + else +#else + /* ??? It seems that passing back a register even when past + the area declared by REG_PARM_STACK_SPACE will allocate + space appropriately, and will not copy the data onto the + stack, exactly as we desire. + + This is due to locate_and_pad_parm being called in + expand_call whenever reg_parm_stack_space > 0, which + while benefical to our example here, would seem to be + in error from what had been intended. Ho hum... -- r~ */ +#endif + return reg; + } + else + { + rtx v0, v1; + + if ((regno - SPARC_FP_ARG_FIRST) < SPARC_INT_ARG_MAX * 2) + { + int intreg; + + /* On incoming, we don't need to know that the value + is passed in %f0 and %i0, and it confuses other parts + causing needless spillage even on the simplest cases. */ + if (incoming_p) + return reg; + + intreg = (SPARC_OUTGOING_INT_ARG_FIRST + + (regno - SPARC_FP_ARG_FIRST) / 2); + + v0 = gen_rtx_EXPR_LIST (VOIDmode, reg, const0_rtx); + v1 = gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_REG (mode, intreg), + const0_rtx); + return gen_rtx_PARALLEL (mode, gen_rtvec (2, v0, v1)); + } + else + { + v0 = gen_rtx_EXPR_LIST (VOIDmode, NULL_RTX, const0_rtx); + v1 = gen_rtx_EXPR_LIST (VOIDmode, reg, const0_rtx); + return gen_rtx_PARALLEL (mode, gen_rtvec (2, v0, v1)); + } + } + } + else if (type && TREE_CODE (type) == RECORD_TYPE) + { + /* Structures up to 16 bytes in size are passed in arg slots on the + stack and are promoted to registers where possible. */ + + if (int_size_in_bytes (type) > 16) + abort (); /* shouldn't get here */ + + return function_arg_record_value (type, mode, slotno, named, regbase); + } + else if (type && TREE_CODE (type) == UNION_TYPE) + { + enum machine_mode mode; + int bytes = int_size_in_bytes (type); + + if (bytes > 16) + abort (); + + mode = mode_for_size (bytes * BITS_PER_UNIT, MODE_INT, 0); + reg = gen_rtx_REG (mode, regno); + } + else + { + /* Scalar or complex int. */ + reg = gen_rtx_REG (mode, regno); + } + + return reg; +} + +/* Handle the FUNCTION_ARG_PARTIAL_NREGS macro. + 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. + + Any arg that starts in the first 6 regs but won't entirely fit in them + needs partial registers on v8. On v9, structures with integer + values in arg slots 5,6 will be passed in %o5 and SP+176, and complex fp + values that begin in the last fp reg [where "last fp reg" varies with the + mode] will be split between that reg and memory. */ + +int +function_arg_partial_nregs (cum, mode, type, named) + const CUMULATIVE_ARGS *cum; + enum machine_mode mode; + tree type; + int named; +{ + int slotno, regno, padding; + + /* We pass 0 for incoming_p here, it doesn't matter. */ + slotno = function_arg_slotno (cum, mode, type, named, 0, ®no, &padding); + + if (slotno == -1) + return 0; + + if (TARGET_ARCH32) + { + if ((slotno + (mode == BLKmode + ? ROUND_ADVANCE (int_size_in_bytes (type)) + : ROUND_ADVANCE (GET_MODE_SIZE (mode)))) + > NPARM_REGS (SImode)) + return NPARM_REGS (SImode) - slotno; + return 0; + } + else + { + if (type && AGGREGATE_TYPE_P (type)) + { + int size = int_size_in_bytes (type); + int align = TYPE_ALIGN (type); + + if (align == 16) + slotno += slotno & 1; + if (size > 8 && size <= 16 + && slotno == SPARC_INT_ARG_MAX - 1) + return 1; + } + else if (GET_MODE_CLASS (mode) == MODE_COMPLEX_INT + || (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT + && ! TARGET_FPU)) + { + if (GET_MODE_ALIGNMENT (mode) == 128) + { + slotno += slotno & 1; + if (slotno == SPARC_INT_ARG_MAX - 2) + return 1; + } + else + { + if (slotno == SPARC_INT_ARG_MAX - 1) + return 1; + } + } + else if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT) + { + if (GET_MODE_ALIGNMENT (mode) == 128) + slotno += slotno & 1; + if ((slotno + GET_MODE_SIZE (mode) / UNITS_PER_WORD) + > SPARC_FP_ARG_MAX) + return 1; + } + return 0; + } +} + +/* Handle the FUNCTION_ARG_PASS_BY_REFERENCE macro. + !v9: The SPARC ABI stipulates passing struct arguments (of any size) and + quad-precision floats by invisible reference. + v9: Aggregates greater than 16 bytes are passed by reference. + For Pascal, also pass arrays by reference. */ + +int +function_arg_pass_by_reference (cum, mode, type, named) + const CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED; + enum machine_mode mode; + tree type; + int named ATTRIBUTE_UNUSED; +{ + if (TARGET_ARCH32) + { + return ((type && AGGREGATE_TYPE_P (type)) + || mode == TFmode || mode == TCmode); + } + else + { + return ((type && TREE_CODE (type) == ARRAY_TYPE) + /* Consider complex values as aggregates, so care for TCmode. */ + || GET_MODE_SIZE (mode) > 16 + || (type && AGGREGATE_TYPE_P (type) + && int_size_in_bytes (type) > 16)); + } +} + +/* Handle the FUNCTION_ARG_ADVANCE macro. + 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. */ + +void +function_arg_advance (cum, mode, type, named) + CUMULATIVE_ARGS *cum; + enum machine_mode mode; + tree type; + int named; +{ + int slotno, regno, padding; + + /* We pass 0 for incoming_p here, it doesn't matter. */ + slotno = function_arg_slotno (cum, mode, type, named, 0, ®no, &padding); + + /* If register required leading padding, add it. */ + if (slotno != -1) + cum->words += padding; + + if (TARGET_ARCH32) + { + cum->words += (mode != BLKmode + ? ROUND_ADVANCE (GET_MODE_SIZE (mode)) + : ROUND_ADVANCE (int_size_in_bytes (type))); + } + else + { + if (type && AGGREGATE_TYPE_P (type)) + { + int size = int_size_in_bytes (type); + + if (size <= 8) + ++cum->words; + else if (size <= 16) + cum->words += 2; + else /* passed by reference */ + ++cum->words; + } + else if (GET_MODE_CLASS (mode) == MODE_COMPLEX_INT) + { + cum->words += 2; + } + else if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT) + { + cum->words += GET_MODE_SIZE (mode) / UNITS_PER_WORD; + } + else + { + cum->words += (mode != BLKmode + ? ROUND_ADVANCE (GET_MODE_SIZE (mode)) + : ROUND_ADVANCE (int_size_in_bytes (type))); + } + } +} + +/* Handle the FUNCTION_ARG_PADDING macro. + For the 64 bit ABI structs are always stored left shifted in their + argument slot. */ + +enum direction +function_arg_padding (mode, type) + enum machine_mode mode; + tree type; +{ + if (TARGET_ARCH64 && type != 0 && AGGREGATE_TYPE_P (type)) + return upward; + + /* This is the default definition. */ + return (! BYTES_BIG_ENDIAN + ? upward + : ((mode == BLKmode + ? (type && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST + && int_size_in_bytes (type) < (PARM_BOUNDARY / BITS_PER_UNIT)) + : GET_MODE_BITSIZE (mode) < PARM_BOUNDARY) + ? downward : upward)); +} + +/* Handle FUNCTION_VALUE, FUNCTION_OUTGOING_VALUE, and LIBCALL_VALUE macros. + For v9, function return values are subject to the same rules as arguments, + except that up to 32-bytes may be returned in registers. */ + +rtx +function_value (type, mode, incoming_p) + tree type; + enum machine_mode mode; + int incoming_p; +{ + int regno; + int regbase = (incoming_p + ? SPARC_OUTGOING_INT_ARG_FIRST + : SPARC_INCOMING_INT_ARG_FIRST); + + if (TARGET_ARCH64 && type) + { + if (TREE_CODE (type) == RECORD_TYPE) + { + /* Structures up to 32 bytes in size are passed in registers, + promoted to fp registers where possible. */ + + if (int_size_in_bytes (type) > 32) + abort (); /* shouldn't get here */ + + return function_arg_record_value (type, mode, 0, 1, regbase); + } + else if (TREE_CODE (type) == UNION_TYPE) + { + int bytes = int_size_in_bytes (type); + + if (bytes > 32) + abort (); + + mode = mode_for_size (bytes * BITS_PER_UNIT, MODE_INT, 0); + } + } + + if (TARGET_ARCH64 + && GET_MODE_CLASS (mode) == MODE_INT + && GET_MODE_SIZE (mode) < UNITS_PER_WORD + && type && TREE_CODE (type) != UNION_TYPE) + mode = DImode; + + if (incoming_p) + regno = BASE_RETURN_VALUE_REG (mode); + else + regno = BASE_OUTGOING_VALUE_REG (mode); + + return gen_rtx_REG (mode, regno); +} + +/* Do what is necessary for `va_start'. The argument is ignored. + + We look at the current function to determine if stdarg or varargs + is used and return the address of the first unnamed parameter. */ + +rtx +sparc_builtin_saveregs (arglist) + tree arglist ATTRIBUTE_UNUSED; +{ + int first_reg = current_function_args_info.words; + rtx address; + int regno; + + for (regno = first_reg; regno < NPARM_REGS (word_mode); regno++) + emit_move_insn (gen_rtx_MEM (word_mode, + gen_rtx_PLUS (Pmode, + frame_pointer_rtx, + GEN_INT (STACK_POINTER_OFFSET + + UNITS_PER_WORD * regno))), + gen_rtx_REG (word_mode, + BASE_INCOMING_ARG_REG (word_mode) + regno)); + + address = gen_rtx_PLUS (Pmode, + frame_pointer_rtx, + GEN_INT (STACK_POINTER_OFFSET + + UNITS_PER_WORD * first_reg)); + + if (current_function_check_memory_usage + && first_reg < NPARM_REGS (word_mode)) + emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3, + address, ptr_mode, + GEN_INT (UNITS_PER_WORD + * (NPARM_REGS (word_mode) - first_reg)), + TYPE_MODE (sizetype), GEN_INT (MEMORY_USE_RW), + TYPE_MODE (integer_type_node)); + + return address; +} + +/* Return the string to output a conditional branch to LABEL, which is + the operand number of the label. OP is the conditional expression. + XEXP (OP, 0) is assumed to be a condition code register (integer or + floating point) and its mode specifies what kind of comparison we made. + + REVERSED is non-zero if we should reverse the sense of the comparison. + + ANNUL is non-zero if we should generate an annulling branch. + + NOOP is non-zero if we have to follow this branch by a noop. + + INSN, if set, is the insn. */ + +char * +output_cbranch (op, label, reversed, annul, noop, insn) + rtx op; + int label; + int reversed, annul, noop; + rtx insn; +{ + static char string[32]; + enum rtx_code code = GET_CODE (op); + rtx cc_reg = XEXP (op, 0); + enum machine_mode mode = GET_MODE (cc_reg); + static char v8_labelno[] = "%lX"; + static char v9_icc_labelno[] = "%%icc, %lX"; + static char v9_xcc_labelno[] = "%%xcc, %lX"; + static char v9_fcc_labelno[] = "%%fccX, %lY"; + char *labelno; + int labeloff, spaces = 8; + + /* ??? !v9: FP branches cannot be preceded by another floating point insn. + Because there is currently no concept of pre-delay slots, we can fix + this only by always emitting a nop before a floating point branch. */ + + if ((mode == CCFPmode || mode == CCFPEmode) && ! TARGET_V9) + strcpy (string, "nop\n\t"); + else + string[0] = '\0'; + + /* If not floating-point or if EQ or NE, we can just reverse the code. */ + if (reversed + && ((mode != CCFPmode && mode != CCFPEmode) || code == EQ || code == NE)) + code = reverse_condition (code), reversed = 0; + + /* Start by writing the branch condition. */ + switch (code) + { + case NE: + if (mode == CCFPmode || mode == CCFPEmode) + { + strcat (string, "fbne"); + spaces -= 4; + } + else + { + strcpy (string, "bne"); + spaces -= 3; + } + break; + + case EQ: + if (mode == CCFPmode || mode == CCFPEmode) + { + strcat (string, "fbe"); + spaces -= 3; + } + else + { + strcpy (string, "be"); + spaces -= 2; + } + break; + + case GE: + if (mode == CCFPmode || mode == CCFPEmode) + { + if (reversed) + strcat (string, "fbul"); + else + strcat (string, "fbge"); + spaces -= 4; + } + else if (mode == CC_NOOVmode) + { + strcpy (string, "bpos"); + spaces -= 4; + } + else + { + strcpy (string, "bge"); + spaces -= 3; + } + break; + + case GT: + if (mode == CCFPmode || mode == CCFPEmode) + { + if (reversed) + { + strcat (string, "fbule"); + spaces -= 5; + } + else + { + strcat (string, "fbg"); + spaces -= 3; + } + } + else + { + strcpy (string, "bg"); + spaces -= 2; + } + break; + + case LE: + if (mode == CCFPmode || mode == CCFPEmode) + { + if (reversed) + strcat (string, "fbug"); + else + strcat (string, "fble"); + spaces -= 4; + } + else + { + strcpy (string, "ble"); + spaces -= 3; + } + break; + + case LT: + if (mode == CCFPmode || mode == CCFPEmode) + { + if (reversed) + { + strcat (string, "fbuge"); + spaces -= 5; + } + else + { + strcat (string, "fbl"); + spaces -= 3; + } + } + else if (mode == CC_NOOVmode) + { + strcpy (string, "bneg"); + spaces -= 4; + } + else + { + strcpy (string, "bl"); + spaces -= 2; + } + break; + + case GEU: + strcpy (string, "bgeu"); + spaces -= 4; + break; + + case GTU: + strcpy (string, "bgu"); + spaces -= 3; + break; + + case LEU: + strcpy (string, "bleu"); + spaces -= 4; + break; + + case LTU: + strcpy (string, "blu"); + spaces -= 3; + break; + + default: + abort (); + } + + /* Now add the annulling, the label, and a possible noop. */ + if (annul) + { + strcat (string, ",a"); + spaces -= 2; + } + + if (! TARGET_V9) + { + labeloff = 2; + labelno = v8_labelno; + } + else + { + rtx note; + + if (insn && (note = find_reg_note (insn, REG_BR_PRED, NULL_RTX))) + { + strcat (string, + INTVAL (XEXP (note, 0)) & ATTR_FLAG_likely ? ",pt" : ",pn"); + spaces -= 3; + } + + labeloff = 9; + if (mode == CCFPmode || mode == CCFPEmode) + { + labeloff = 10; + labelno = v9_fcc_labelno; + /* Set the char indicating the number of the fcc reg to use. */ + labelno[5] = REGNO (cc_reg) - SPARC_FIRST_V9_FCC_REG + '0'; + } + else if (mode == CCXmode || mode == CCX_NOOVmode) + labelno = v9_xcc_labelno; + else + labelno = v9_icc_labelno; + } + /* Set the char indicating the number of the operand containing the + label_ref. */ + labelno[labeloff] = label + '0'; + if (spaces > 0) + strcat (string, "\t"); + else + strcat (string, " "); + strcat (string, labelno); + + if (noop) + strcat (string, "\n\tnop"); + + return string; +} + +/* Return the string to output a conditional branch to LABEL, testing + register REG. LABEL is the operand number of the label; REG is the + operand number of the reg. OP is the conditional expression. The mode + of REG says what kind of comparison we made. + + REVERSED is non-zero if we should reverse the sense of the comparison. + + ANNUL is non-zero if we should generate an annulling branch. + + NOOP is non-zero if we have to follow this branch by a noop. */ + +char * +output_v9branch (op, reg, label, reversed, annul, noop, insn) + rtx op; + int reg, label; + int reversed, annul, noop; + rtx insn; +{ + static char string[20]; + enum rtx_code code = GET_CODE (op); + enum machine_mode mode = GET_MODE (XEXP (op, 0)); + static char labelno[] = "%X, %lX"; + rtx note; + int spaces = 8; + + /* If not floating-point or if EQ or NE, we can just reverse the code. */ + if (reversed) + code = reverse_condition (code), reversed = 0; + + /* Only 64 bit versions of these instructions exist. */ + if (mode != DImode) + abort (); + + /* Start by writing the branch condition. */ + + switch (code) + { + case NE: + strcpy (string, "brnz"); + spaces -= 4; + break; + + case EQ: + strcpy (string, "brz"); + spaces -= 3; + break; + + case GE: + strcpy (string, "brgez"); + spaces -= 5; + break; + + case LT: + strcpy (string, "brlz"); + spaces -= 4; + break; + + case LE: + strcpy (string, "brlez"); + spaces -= 5; + break; + + case GT: + strcpy (string, "brgz"); + spaces -= 4; + break; + + default: + abort (); + } + + /* Now add the annulling, reg, label, and nop. */ + if (annul) + { + strcat (string, ",a"); + spaces -= 2; + } + + if (insn && (note = find_reg_note (insn, REG_BR_PRED, NULL_RTX))) + { + strcat (string, + INTVAL (XEXP (note, 0)) & ATTR_FLAG_likely ? ",pt" : ",pn"); + spaces -= 3; + } + + labelno[1] = reg + '0'; + labelno[6] = label + '0'; + if (spaces > 0) + strcat (string, "\t"); + else + strcat (string, " "); + strcat (string, labelno); + + if (noop) + strcat (string, "\n\tnop"); + + return string; +} + +/* Renumber registers in delay slot. Replace registers instead of + renumbering because they may be shared. + + This does not handle instructions other than move. */ + +static void +epilogue_renumber (where) + rtx *where; +{ + rtx x = *where; + enum rtx_code code = GET_CODE (x); + + switch (code) + { + case MEM: + *where = x = copy_rtx (x); + epilogue_renumber (&XEXP (x, 0)); + return; + + case REG: + { + int regno = REGNO (x); + if (regno > 8 && regno < 24) + abort (); + if (regno >= 24 && regno < 32) + *where = gen_rtx_REG (GET_MODE (x), regno - 16); + return; + } + case CONST_INT: + case CONST_DOUBLE: + case CONST: + case SYMBOL_REF: + case LABEL_REF: + return; + + case IOR: + case AND: + case XOR: + case PLUS: + case MINUS: + epilogue_renumber (&XEXP (x, 1)); + case NEG: + case NOT: + epilogue_renumber (&XEXP (x, 0)); + return; + + default: + debug_rtx (*where); + abort (); + } +} + +/* Output assembler code to return from a function. */ + +char * +output_return (operands) + rtx *operands; +{ + rtx delay = final_sequence ? XVECEXP (final_sequence, 0, 1) : 0; + + if (leaf_label) + { + operands[0] = leaf_label; + return "b%* %l0%("; + } + else if (leaf_function) + { + /* No delay slot in a leaf function. */ + if (delay) + abort (); + + /* If we didn't allocate a frame pointer for the current function, + the stack pointer might have been adjusted. Output code to + restore it now. */ + + operands[0] = GEN_INT (actual_fsize); + + /* Use sub of negated value in first two cases instead of add to + allow actual_fsize == 4096. */ + + if (actual_fsize <= 4096) + { + if (SKIP_CALLERS_UNIMP_P) + return "jmp\t%%o7+12\n\tsub\t%%sp, -%0, %%sp"; + else + return "retl\n\tsub\t%%sp, -%0, %%sp"; + } + else if (actual_fsize <= 8192) + { + operands[0] = GEN_INT (actual_fsize - 4096); + if (SKIP_CALLERS_UNIMP_P) + return "sub\t%%sp, -4096, %%sp\n\tjmp\t%%o7+12\n\tsub\t%%sp, -%0, %%sp"; + else + return "sub\t%%sp, -4096, %%sp\n\tretl\n\tsub\t%%sp, -%0, %%sp"; + } + else if (SKIP_CALLERS_UNIMP_P) + { + if ((actual_fsize & 0x3ff) != 0) + return "sethi\t%%hi(%a0), %%g1\n\tor\t%%g1, %%lo(%a0), %%g1\n\tjmp\t%%o7+12\n\tadd\t%%sp, %%g1, %%sp"; + else + return "sethi\t%%hi(%a0), %%g1\n\tjmp\t%%o7+12\n\tadd\t%%sp, %%g1, %%sp"; + } + else + { + if ((actual_fsize & 0x3ff) != 0) + return "sethi %%hi(%a0),%%g1\n\tor %%g1,%%lo(%a0),%%g1\n\tretl\n\tadd %%sp,%%g1,%%sp"; + else + return "sethi %%hi(%a0),%%g1\n\tretl\n\tadd %%sp,%%g1,%%sp"; + } + } + else if (TARGET_V9) + { + if (delay) + { + epilogue_renumber (&SET_DEST (PATTERN (delay))); + epilogue_renumber (&SET_SRC (PATTERN (delay))); + } + if (SKIP_CALLERS_UNIMP_P) + return "return\t%%i7+12%#"; + else + return "return\t%%i7+8%#"; + } + else + { + if (delay) + abort (); + if (SKIP_CALLERS_UNIMP_P) + return "jmp\t%%i7+12\n\trestore"; + else + return "ret\n\trestore"; + } +} + +/* Leaf functions and non-leaf functions have different needs. */ + +static int +reg_leaf_alloc_order[] = REG_LEAF_ALLOC_ORDER; + +static int +reg_nonleaf_alloc_order[] = REG_ALLOC_ORDER; + +static int *reg_alloc_orders[] = { + reg_leaf_alloc_order, + reg_nonleaf_alloc_order}; + +void +order_regs_for_local_alloc () +{ + static int last_order_nonleaf = 1; + + if (regs_ever_live[15] != last_order_nonleaf) + { + last_order_nonleaf = !last_order_nonleaf; + bcopy ((char *) reg_alloc_orders[last_order_nonleaf], + (char *) reg_alloc_order, FIRST_PSEUDO_REGISTER * sizeof (int)); + } +} + +/* Return 1 if REG and MEM are legitimate enough to allow the various + mem<-->reg splits to be run. */ + +int +sparc_splitdi_legitimate (reg, mem) + rtx reg; + rtx mem; +{ + /* Punt if we are here by mistake. */ + if (! reload_completed) + abort (); + + /* We must have an offsettable memory reference. */ + if (! offsettable_memref_p (mem)) + return 0; + + /* If we have legitimate args for ldd/std, we do not want + the split to happen. */ + if ((REGNO (reg) % 2) == 0 + && mem_min_alignment (mem, 8)) + return 0; + + /* Success. */ + return 1; +} + +/* Return 1 if x and y are some kind of REG and they refer to + different hard registers. This test is guarenteed to be + run after reload. */ + +int +sparc_absnegfloat_split_legitimate (x, y) + rtx x, y; +{ + if (GET_CODE (x) == SUBREG) + x = alter_subreg (x); + if (GET_CODE (x) != REG) + return 0; + if (GET_CODE (y) == SUBREG) + y = alter_subreg (y); + if (GET_CODE (y) != REG) + return 0; + if (REGNO (x) == REGNO (y)) + return 0; + return 1; +} + +/* Return 1 if REGNO (reg1) is even and REGNO (reg1) == REGNO (reg2) - 1. + This makes them candidates for using ldd and std insns. + + Note reg1 and reg2 *must* be hard registers. */ + +int +registers_ok_for_ldd_peep (reg1, reg2) + rtx reg1, reg2; +{ + /* We might have been passed a SUBREG. */ + if (GET_CODE (reg1) != REG || GET_CODE (reg2) != REG) + return 0; + + if (REGNO (reg1) % 2 != 0) + return 0; + + /* Integer ldd is deprecated in SPARC V9 */ + if (TARGET_V9 && REGNO (reg1) < 32) + return 0; + + return (REGNO (reg1) == REGNO (reg2) - 1); +} + +/* Return 1 if addr1 and addr2 are suitable for use in an ldd or + std insn. + + This can only happen when addr1 and addr2 are consecutive memory + locations (addr1 + 4 == addr2). addr1 must also be aligned on a + 64 bit boundary (addr1 % 8 == 0). + + We know %sp and %fp are kept aligned on a 64 bit boundary. Other + registers are assumed to *never* be properly aligned and are + rejected. + + Knowing %sp and %fp are kept aligned on a 64 bit boundary, we + need only check that the offset for addr1 % 8 == 0. */ + +int +addrs_ok_for_ldd_peep (addr1, addr2) + rtx addr1, addr2; +{ + int reg1, offset1; + + /* Extract a register number and offset (if used) from the first addr. */ + if (GET_CODE (addr1) == PLUS) + { + /* If not a REG, return zero. */ + if (GET_CODE (XEXP (addr1, 0)) != REG) + return 0; + else + { + reg1 = REGNO (XEXP (addr1, 0)); + /* The offset must be constant! */ + if (GET_CODE (XEXP (addr1, 1)) != CONST_INT) + return 0; + offset1 = INTVAL (XEXP (addr1, 1)); + } + } + else if (GET_CODE (addr1) != REG) + return 0; + else + { + reg1 = REGNO (addr1); + /* This was a simple (mem (reg)) expression. Offset is 0. */ + offset1 = 0; + } + + /* Make sure the second address is a (mem (plus (reg) (const_int). */ + if (GET_CODE (addr2) != PLUS) + return 0; + + if (GET_CODE (XEXP (addr2, 0)) != REG + || GET_CODE (XEXP (addr2, 1)) != CONST_INT) + return 0; + + /* Only %fp and %sp are allowed. Additionally both addresses must + use the same register. */ + if (reg1 != FRAME_POINTER_REGNUM && reg1 != STACK_POINTER_REGNUM) + return 0; + + if (reg1 != REGNO (XEXP (addr2, 0))) + return 0; + + /* The first offset must be evenly divisible by 8 to ensure the + address is 64 bit aligned. */ + if (offset1 % 8 != 0) + return 0; + + /* The offset for the second addr must be 4 more than the first addr. */ + if (INTVAL (XEXP (addr2, 1)) != offset1 + 4) + return 0; + + /* All the tests passed. addr1 and addr2 are valid for ldd and std + instructions. */ + return 1; +} + +/* Return 1 if reg is a pseudo, or is the first register in + a hard register pair. This makes it a candidate for use in + ldd and std insns. */ + +int +register_ok_for_ldd (reg) + rtx reg; +{ + /* We might have been passed a SUBREG. */ + if (GET_CODE (reg) != REG) + return 0; + + if (REGNO (reg) < FIRST_PSEUDO_REGISTER) + return (REGNO (reg) % 2 == 0); + else + return 1; +} + +/* 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\t nop", file); + return; + case '*': + /* Output an annul flag if there's nothing for the delay slot and we + are optimizing. This is always used with '(' below. */ + /* Sun OS 4.1.1 dbx can't handle an annulled unconditional branch; + this is a dbx bug. So, we only do this when optimizing. */ + /* On UltraSPARC, a branch in a delay slot causes a pipeline flush. + Always emit a nop in case the next instruction is a branch. */ + if (dbr_sequence_length () == 0 + && (optimize && (int)sparc_cpu < PROCESSOR_V9)) + fputs (",a", file); + return; + case '(': + /* Output a 'nop' if there's nothing for the delay slot and we are + not optimizing. This is always used with '*' above. */ + if (dbr_sequence_length () == 0 + && ! (optimize && (int)sparc_cpu < PROCESSOR_V9)) + fputs ("\n\t nop", file); + return; + case '_': + /* Output the Embedded Medium/Anywhere code model base register. */ + fputs (EMBMEDANY_BASE_REG, file); + return; + case '@': + /* Print out what we are using as the frame pointer. This might + be %fp, or might be %sp+offset. */ + /* ??? What if offset is too big? Perhaps the caller knows it isn't? */ + fprintf (file, "%s+%d", frame_base_name, frame_base_offset); + return; + case 'Y': + /* Adjust the operand to take into account a RESTORE operation. */ + if (GET_CODE (x) == CONST_INT) + break; + else if (GET_CODE (x) != REG) + output_operand_lossage ("Invalid %%Y operand"); + else if (REGNO (x) < 8) + fputs (reg_names[REGNO (x)], file); + else if (REGNO (x) >= 24 && REGNO (x) < 32) + fputs (reg_names[REGNO (x)-16], file); + else + output_operand_lossage ("Invalid %%Y operand"); + return; + case 'L': + /* Print out the low order register name of a register pair. */ + if (WORDS_BIG_ENDIAN) + fputs (reg_names[REGNO (x)+1], file); + else + fputs (reg_names[REGNO (x)], file); + return; + case 'H': + /* Print out the high order register name of a register pair. */ + if (WORDS_BIG_ENDIAN) + fputs (reg_names[REGNO (x)], file); + else + fputs (reg_names[REGNO (x)+1], file); + return; + case 'R': + /* Print out the second register name of a register pair or quad. + I.e., R (%o0) => %o1. */ + fputs (reg_names[REGNO (x)+1], file); + return; + case 'S': + /* Print out the third register name of a register quad. + I.e., S (%o0) => %o2. */ + fputs (reg_names[REGNO (x)+2], file); + return; + case 'T': + /* Print out the fourth register name of a register quad. + I.e., T (%o0) => %o3. */ + fputs (reg_names[REGNO (x)+3], file); + return; + case 'x': + /* Print a condition code register. */ + if (REGNO (x) == SPARC_ICC_REG) + { + /* We don't handle CC[X]_NOOVmode because they're not supposed + to occur here. */ + if (GET_MODE (x) == CCmode) + fputs ("%icc", file); + else if (GET_MODE (x) == CCXmode) + fputs ("%xcc", file); + else + abort (); + } + else + /* %fccN register */ + fputs (reg_names[REGNO (x)], file); + return; + case 'm': + /* Print the operand's address only. */ + output_address (XEXP (x, 0)); + return; + case 'r': + /* In this case we need a register. Use %g0 if the + operand is const0_rtx. */ + if (x == const0_rtx + || (GET_MODE (x) != VOIDmode && x == CONST0_RTX (GET_MODE (x)))) + { + fputs ("%g0", file); + return; + } + else + break; + + case 'A': + switch (GET_CODE (x)) + { + case IOR: fputs ("or", file); break; + case AND: fputs ("and", file); break; + case XOR: fputs ("xor", file); break; + default: output_operand_lossage ("Invalid %%A operand"); + } + return; + + case 'B': + switch (GET_CODE (x)) + { + case IOR: fputs ("orn", file); break; + case AND: fputs ("andn", file); break; + case XOR: fputs ("xnor", file); break; + default: output_operand_lossage ("Invalid %%B operand"); + } + return; + + /* These are used by the conditional move instructions. */ + case 'c' : + case 'C': + { + enum rtx_code rc = (code == 'c' + ? reverse_condition (GET_CODE (x)) + : GET_CODE (x)); + switch (rc) + { + case NE: fputs ("ne", file); break; + case EQ: fputs ("e", file); break; + case GE: fputs ("ge", file); break; + case GT: fputs ("g", file); break; + case LE: fputs ("le", file); break; + case LT: fputs ("l", file); break; + case GEU: fputs ("geu", file); break; + case GTU: fputs ("gu", file); break; + case LEU: fputs ("leu", file); break; + case LTU: fputs ("lu", file); break; + default: output_operand_lossage (code == 'c' + ? "Invalid %%c operand" + : "Invalid %%C operand"); + } + return; + } + + /* These are used by the movr instruction pattern. */ + case 'd': + case 'D': + { + enum rtx_code rc = (code == 'd' + ? reverse_condition (GET_CODE (x)) + : GET_CODE (x)); + switch (rc) + { + case NE: fputs ("ne", file); break; + case EQ: fputs ("e", file); break; + case GE: fputs ("gez", file); break; + case LT: fputs ("lz", file); break; + case LE: fputs ("lez", file); break; + case GT: fputs ("gz", file); break; + default: output_operand_lossage (code == 'd' + ? "Invalid %%d operand" + : "Invalid %%D operand"); + } + return; + } + + case 'b': + { + /* Print a sign-extended character. */ + int i = INTVAL (x) & 0xff; + if (i & 0x80) + i |= 0xffffff00; + fprintf (file, "%d", i); + return; + } + + case 'f': + /* Operand must be a MEM; write its address. */ + if (GET_CODE (x) != MEM) + output_operand_lossage ("Invalid %%f operand"); + output_address (XEXP (x, 0)); + return; + + case 0: + /* Do nothing special. */ + break; + + default: + /* Undocumented flag. */ + output_operand_lossage ("invalid operand output code"); + } + + if (GET_CODE (x) == REG) + fputs (reg_names[REGNO (x)], file); + else if (GET_CODE (x) == MEM) + { + fputc ('[', file); + /* Poor Sun assembler doesn't understand absolute addressing. */ + if (CONSTANT_P (XEXP (x, 0)) + && ! TARGET_LIVE_G0) + fputs ("%g0+", file); + output_address (XEXP (x, 0)); + fputc (']', file); + } + else if (GET_CODE (x) == HIGH) + { + fputs ("%hi(", file); + output_addr_const (file, XEXP (x, 0)); + fputc (')', file); + } + else if (GET_CODE (x) == LO_SUM) + { + print_operand (file, XEXP (x, 0), 0); + if (TARGET_CM_MEDMID) + fputs ("+%l44(", file); + else + fputs ("+%lo(", file); + output_addr_const (file, XEXP (x, 1)); + fputc (')', file); + } + else if (GET_CODE (x) == CONST_DOUBLE + && (GET_MODE (x) == VOIDmode + || GET_MODE_CLASS (GET_MODE (x)) == MODE_INT)) + { + if (CONST_DOUBLE_HIGH (x) == 0) + fprintf (file, "%u", CONST_DOUBLE_LOW (x)); + else if (CONST_DOUBLE_HIGH (x) == -1 + && CONST_DOUBLE_LOW (x) < 0) + fprintf (file, "%d", CONST_DOUBLE_LOW (x)); + else + output_operand_lossage ("long long constant not a valid immediate operand"); + } + else if (GET_CODE (x) == CONST_DOUBLE) + output_operand_lossage ("floating point constant not a valid immediate operand"); + else { output_addr_const (file, x); } +} + +/* This function outputs assembler code for VALUE to FILE, where VALUE is + a 64 bit (DImode) value. */ + +/* ??? If there is a 64 bit counterpart to .word that the assembler + understands, then using that would simply this code greatly. */ +/* ??? We only output .xword's for symbols and only then in environments + where the assembler can handle them. */ + +void +output_double_int (file, value) + FILE *file; + rtx value; +{ + if (GET_CODE (value) == CONST_INT) + { + /* ??? This has endianness issues. */ +#if HOST_BITS_PER_WIDE_INT == 64 + HOST_WIDE_INT xword = INTVAL (value); + HOST_WIDE_INT high, low; + + high = (xword >> 32) & 0xffffffff; + low = xword & 0xffffffff; + ASM_OUTPUT_INT (file, GEN_INT (high)); + ASM_OUTPUT_INT (file, GEN_INT (low)); +#else + if (INTVAL (value) < 0) + ASM_OUTPUT_INT (file, constm1_rtx); + else + ASM_OUTPUT_INT (file, const0_rtx); + ASM_OUTPUT_INT (file, value); +#endif + } + else if (GET_CODE (value) == CONST_DOUBLE) + { + ASM_OUTPUT_INT (file, GEN_INT (CONST_DOUBLE_HIGH (value))); + ASM_OUTPUT_INT (file, GEN_INT (CONST_DOUBLE_LOW (value))); + } + else if (GET_CODE (value) == SYMBOL_REF + || GET_CODE (value) == CONST + || GET_CODE (value) == PLUS + || (TARGET_ARCH64 && + (GET_CODE (value) == LABEL_REF + || GET_CODE (value) == CODE_LABEL + || GET_CODE (value) == MINUS))) + { + if (! TARGET_V9) + { + ASM_OUTPUT_INT (file, const0_rtx); + ASM_OUTPUT_INT (file, value); + } + else + { + fprintf (file, "\t%s\t", ASM_LONGLONG); + output_addr_const (file, value); + fprintf (file, "\n"); + } + } + else + abort (); +} + +/* Return the value of a code used in the .proc pseudo-op that says + what kind of result this function returns. For non-C types, we pick + the closest C type. */ + +#ifndef CHAR_TYPE_SIZE +#define CHAR_TYPE_SIZE BITS_PER_UNIT +#endif + +#ifndef SHORT_TYPE_SIZE +#define SHORT_TYPE_SIZE (BITS_PER_UNIT * 2) +#endif + +#ifndef INT_TYPE_SIZE +#define INT_TYPE_SIZE BITS_PER_WORD +#endif + +#ifndef LONG_TYPE_SIZE +#define LONG_TYPE_SIZE BITS_PER_WORD +#endif + +#ifndef LONG_LONG_TYPE_SIZE +#define LONG_LONG_TYPE_SIZE (BITS_PER_WORD * 2) +#endif + +#ifndef FLOAT_TYPE_SIZE +#define FLOAT_TYPE_SIZE BITS_PER_WORD +#endif + +#ifndef DOUBLE_TYPE_SIZE +#define DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2) +#endif + +#ifndef LONG_DOUBLE_TYPE_SIZE +#define LONG_DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2) +#endif + +unsigned long +sparc_type_code (type) + register tree type; +{ + register unsigned long qualifiers = 0; + register unsigned shift; + + /* Only the first 30 bits of the qualifier are valid. We must refrain from + setting more, since some assemblers will give an error for this. Also, + we must be careful to avoid shifts of 32 bits or more to avoid getting + unpredictable results. */ + + for (shift = 6; shift < 30; shift += 2, type = TREE_TYPE (type)) + { + switch (TREE_CODE (type)) + { + case ERROR_MARK: + return qualifiers; + + case ARRAY_TYPE: + qualifiers |= (3 << shift); + break; + + case FUNCTION_TYPE: + case METHOD_TYPE: + qualifiers |= (2 << shift); + break; + + case POINTER_TYPE: + case REFERENCE_TYPE: + case OFFSET_TYPE: + qualifiers |= (1 << shift); + break; + + case RECORD_TYPE: + return (qualifiers | 8); + + case UNION_TYPE: + case QUAL_UNION_TYPE: + return (qualifiers | 9); + + case ENUMERAL_TYPE: + return (qualifiers | 10); + + case VOID_TYPE: + return (qualifiers | 16); + + case INTEGER_TYPE: + /* If this is a range type, consider it to be the underlying + type. */ + if (TREE_TYPE (type) != 0) + break; + + /* Carefully distinguish all the standard types of C, + without messing up if the language is not C. We do this by + testing TYPE_PRECISION and TREE_UNSIGNED. The old code used to + look at both the names and the above fields, but that's redundant. + Any type whose size is between two C types will be considered + to be the wider of the two types. Also, we do not have a + special code to use for "long long", so anything wider than + long is treated the same. Note that we can't distinguish + between "int" and "long" in this code if they are the same + size, but that's fine, since neither can the assembler. */ + + if (TYPE_PRECISION (type) <= CHAR_TYPE_SIZE) + return (qualifiers | (TREE_UNSIGNED (type) ? 12 : 2)); + + else if (TYPE_PRECISION (type) <= SHORT_TYPE_SIZE) + return (qualifiers | (TREE_UNSIGNED (type) ? 13 : 3)); + + else if (TYPE_PRECISION (type) <= INT_TYPE_SIZE) + return (qualifiers | (TREE_UNSIGNED (type) ? 14 : 4)); + + else + return (qualifiers | (TREE_UNSIGNED (type) ? 15 : 5)); + + case REAL_TYPE: + /* If this is a range type, consider it to be the underlying + type. */ + if (TREE_TYPE (type) != 0) + break; + + /* Carefully distinguish all the standard types of C, + without messing up if the language is not C. */ + + if (TYPE_PRECISION (type) == FLOAT_TYPE_SIZE) + return (qualifiers | 6); + + else + return (qualifiers | 7); + + case COMPLEX_TYPE: /* GNU Fortran COMPLEX type. */ + /* ??? We need to distinguish between double and float complex types, + but I don't know how yet because I can't reach this code from + existing front-ends. */ + return (qualifiers | 7); /* Who knows? */ + + case CHAR_TYPE: /* GNU Pascal CHAR type. Not used in C. */ + case BOOLEAN_TYPE: /* GNU Fortran BOOLEAN type. */ + case FILE_TYPE: /* GNU Pascal FILE type. */ + case SET_TYPE: /* GNU Pascal SET type. */ + case LANG_TYPE: /* ? */ + return qualifiers; + + default: + abort (); /* Not a type! */ + } + } + + return qualifiers; +} + +/* Nested function support. */ + +/* 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. + + This takes 16 insns: 2 shifts & 2 ands (to split up addresses), 4 sethi + (to load in opcodes), 4 iors (to merge address and opcodes), and 4 writes + (to store insns). This is a bit excessive. Perhaps a different + mechanism would be better here. + + Emit enough FLUSH insns to synchronize the data and instruction caches. */ + +void +sparc_initialize_trampoline (tramp, fnaddr, cxt) + rtx tramp, fnaddr, cxt; +{ + /* SPARC 32 bit trampoline: + + sethi %hi(fn), %g1 + sethi %hi(static), %g2 + jmp %g1+%lo(fn) + or %g2, %lo(static), %g2 + + SETHI i,r = 00rr rrr1 00ii iiii iiii iiii iiii iiii + JMPL r+i,d = 10dd ddd1 1100 0rrr rr1i iiii iiii iiii + */ +#ifdef TRANSFER_FROM_TRAMPOLINE + emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "__enable_execute_stack"), + 0, VOIDmode, 1, tramp, Pmode); +#endif + + emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 0)), + expand_binop (SImode, ior_optab, + expand_shift (RSHIFT_EXPR, SImode, fnaddr, + size_int (10), 0, 1), + GEN_INT (0x03000000), + NULL_RTX, 1, OPTAB_DIRECT)); + + emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 4)), + expand_binop (SImode, ior_optab, + expand_shift (RSHIFT_EXPR, SImode, cxt, + size_int (10), 0, 1), + GEN_INT (0x05000000), + NULL_RTX, 1, OPTAB_DIRECT)); + + emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 8)), + expand_binop (SImode, ior_optab, + expand_and (fnaddr, GEN_INT (0x3ff), NULL_RTX), + GEN_INT (0x81c06000), + NULL_RTX, 1, OPTAB_DIRECT)); + + emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 12)), + expand_binop (SImode, ior_optab, + expand_and (cxt, GEN_INT (0x3ff), NULL_RTX), + GEN_INT (0x8410a000), + NULL_RTX, 1, OPTAB_DIRECT)); + + emit_insn (gen_flush (validize_mem (gen_rtx_MEM (SImode, tramp)))); + /* On UltraSPARC a flush flushes an entire cache line. The trampoline is + aligned on a 16 byte boundary so one flush clears it all. */ + if (sparc_cpu != PROCESSOR_ULTRASPARC) + emit_insn (gen_flush (validize_mem (gen_rtx_MEM (SImode, + plus_constant (tramp, 8))))); +} + +/* The 64 bit version is simpler because it makes more sense to load the + values as "immediate" data out of the trampoline. It's also easier since + we can read the PC without clobbering a register. */ + +void +sparc64_initialize_trampoline (tramp, fnaddr, cxt) + rtx tramp, fnaddr, cxt; +{ +#ifdef TRANSFER_FROM_TRAMPOLINE + emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "__enable_execute_stack"), + 0, VOIDmode, 1, tramp, Pmode); +#endif + + /* + rd %pc, %g1 + ldx [%g1+24], %g5 + jmp %g5 + ldx [%g1+16], %g5 + +16 bytes data + */ + + emit_move_insn (gen_rtx_MEM (SImode, tramp), + GEN_INT (0x83414000)); + emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 4)), + GEN_INT (0xca586018)); + emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 8)), + GEN_INT (0x81c14000)); + emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 12)), + GEN_INT (0xca586010)); + emit_move_insn (gen_rtx_MEM (DImode, plus_constant (tramp, 16)), cxt); + emit_move_insn (gen_rtx_MEM (DImode, plus_constant (tramp, 24)), fnaddr); + emit_insn (gen_flush (validize_mem (gen_rtx_MEM (DImode, tramp)))); + + if (sparc_cpu != PROCESSOR_ULTRASPARC) + emit_insn (gen_flush (validize_mem (gen_rtx_MEM (DImode, plus_constant (tramp, 8))))); +} + +/* Subroutines to support a flat (single) register window calling + convention. */ + +/* Single-register window sparc stack frames look like: + + Before call After call + +-----------------------+ +-----------------------+ + high | | | | + mem | caller's temps. | | caller's temps. | + | | | | + +-----------------------+ +-----------------------+ + | | | | + | arguments on stack. | | arguments on stack. | + | | | | + +-----------------------+FP+92->+-----------------------+ + | 6 words to save | | 6 words to save | + | arguments passed | | arguments passed | + | in registers, even | | in registers, even | + | if not passed. | | if not passed. | + SP+68->+-----------------------+FP+68->+-----------------------+ + | 1 word struct addr | | 1 word struct addr | + +-----------------------+FP+64->+-----------------------+ + | | | | + | 16 word reg save area | | 16 word reg save area | + | | | | + SP->+-----------------------+ FP->+-----------------------+ + | 4 word area for | + | fp/alu reg moves | + FP-16->+-----------------------+ + | | + | local variables | + | | + +-----------------------+ + | | + | fp register save | + | | + +-----------------------+ + | | + | gp register save | + | | + +-----------------------+ + | | + | alloca allocations | + | | + +-----------------------+ + | | + | arguments on stack | + | | + SP+92->+-----------------------+ + | 6 words to save | + | arguments passed | + | in registers, even | + low | if not passed. | + memory SP+68->+-----------------------+ + | 1 word struct addr | + SP+64->+-----------------------+ + | | + I 16 word reg save area | + | | + SP->+-----------------------+ */ + +/* Structure to be filled in by sparc_flat_compute_frame_size with register + save masks, and offsets for the current function. */ + +struct sparc_frame_info +{ + unsigned long total_size; /* # bytes that the entire frame takes up. */ + unsigned long var_size; /* # bytes that variables take up. */ + unsigned long args_size; /* # bytes that outgoing arguments take up. */ + unsigned long extra_size; /* # bytes of extra gunk. */ + unsigned int gp_reg_size; /* # bytes needed to store gp regs. */ + unsigned int fp_reg_size; /* # bytes needed to store fp regs. */ + unsigned long gmask; /* Mask of saved gp registers. */ + unsigned long fmask; /* Mask of saved fp registers. */ + unsigned long reg_offset; /* Offset from new sp to store regs. */ + int initialized; /* Nonzero if frame size already calculated. */ +}; + +/* Current frame information calculated by sparc_flat_compute_frame_size. */ +struct sparc_frame_info current_frame_info; + +/* Zero structure to initialize current_frame_info. */ +struct sparc_frame_info zero_frame_info; + +/* Tell prologue and epilogue if register REGNO should be saved / restored. */ + +#define RETURN_ADDR_REGNUM 15 +#define FRAME_POINTER_MASK (1 << (FRAME_POINTER_REGNUM)) +#define RETURN_ADDR_MASK (1 << (RETURN_ADDR_REGNUM)) + +#define MUST_SAVE_REGISTER(regno) \ + ((regs_ever_live[regno] && !call_used_regs[regno]) \ + || (regno == FRAME_POINTER_REGNUM && frame_pointer_needed) \ + || (regno == RETURN_ADDR_REGNUM && regs_ever_live[RETURN_ADDR_REGNUM])) + +/* Return the bytes needed to compute the frame pointer from the current + stack pointer. */ + +unsigned long +sparc_flat_compute_frame_size (size) + int size; /* # of var. bytes allocated. */ +{ + int regno; + unsigned long total_size; /* # bytes that the entire frame takes up. */ + unsigned long var_size; /* # bytes that variables take up. */ + unsigned long args_size; /* # bytes that outgoing arguments take up. */ + unsigned long extra_size; /* # extra bytes. */ + unsigned int gp_reg_size; /* # bytes needed to store gp regs. */ + unsigned int fp_reg_size; /* # bytes needed to store fp regs. */ + unsigned long gmask; /* Mask of saved gp registers. */ + unsigned long fmask; /* Mask of saved fp registers. */ + unsigned long reg_offset; /* Offset to register save area. */ + int need_aligned_p; /* 1 if need the save area 8 byte aligned. */ + + /* This is the size of the 16 word reg save area, 1 word struct addr + area, and 4 word fp/alu register copy area. */ + extra_size = -STARTING_FRAME_OFFSET + FIRST_PARM_OFFSET(0); + var_size = size; + gp_reg_size = 0; + fp_reg_size = 0; + gmask = 0; + fmask = 0; + reg_offset = 0; + need_aligned_p = 0; + + args_size = 0; + if (!leaf_function_p ()) + { + /* Also include the size needed for the 6 parameter registers. */ + args_size = current_function_outgoing_args_size + 24; + } + total_size = var_size + args_size; + + /* Calculate space needed for gp registers. */ + for (regno = 1; regno <= 31; regno++) + { + if (MUST_SAVE_REGISTER (regno)) + { + /* If we need to save two regs in a row, ensure there's room to bump + up the address to align it to a doubleword boundary. */ + if ((regno & 0x1) == 0 && MUST_SAVE_REGISTER (regno+1)) + { + if (gp_reg_size % 8 != 0) + gp_reg_size += 4; + gp_reg_size += 2 * UNITS_PER_WORD; + gmask |= 3 << regno; + regno++; + need_aligned_p = 1; + } + else + { + gp_reg_size += UNITS_PER_WORD; + gmask |= 1 << regno; + } + } + } + + /* Calculate space needed for fp registers. */ + for (regno = 32; regno <= 63; regno++) + { + if (regs_ever_live[regno] && !call_used_regs[regno]) + { + fp_reg_size += UNITS_PER_WORD; + fmask |= 1 << (regno - 32); + } + } + + if (gmask || fmask) + { + int n; + reg_offset = FIRST_PARM_OFFSET(0) + args_size; + /* Ensure save area is 8 byte aligned if we need it. */ + n = reg_offset % 8; + if (need_aligned_p && n != 0) + { + total_size += 8 - n; + reg_offset += 8 - n; + } + total_size += gp_reg_size + fp_reg_size; + } + + /* If we must allocate a stack frame at all, we must also allocate + room for register window spillage, so as to be binary compatible + with libraries and operating systems that do not use -mflat. */ + if (total_size > 0) + total_size += extra_size; + else + extra_size = 0; + + total_size = SPARC_STACK_ALIGN (total_size); + + /* Save other computed information. */ + current_frame_info.total_size = total_size; + current_frame_info.var_size = var_size; + current_frame_info.args_size = args_size; + current_frame_info.extra_size = extra_size; + current_frame_info.gp_reg_size = gp_reg_size; + current_frame_info.fp_reg_size = fp_reg_size; + current_frame_info.gmask = gmask; + current_frame_info.fmask = fmask; + current_frame_info.reg_offset = reg_offset; + current_frame_info.initialized = reload_completed; + + /* Ok, we're done. */ + return total_size; +} + +/* Save/restore registers in GMASK and FMASK at register BASE_REG plus offset + OFFSET. + + BASE_REG must be 8 byte aligned. This allows us to test OFFSET for + appropriate alignment and use DOUBLEWORD_OP when we can. We assume + [BASE_REG+OFFSET] will always be a valid address. + + WORD_OP is either "st" for save, "ld" for restore. + DOUBLEWORD_OP is either "std" for save, "ldd" for restore. */ + +void +sparc_flat_save_restore (file, base_reg, offset, gmask, fmask, word_op, + doubleword_op, base_offset) + FILE *file; + char *base_reg; + unsigned int offset; + unsigned long gmask; + unsigned long fmask; + char *word_op; + char *doubleword_op; + unsigned long base_offset; +{ + int regno; + + if (gmask == 0 && fmask == 0) + return; + + /* Save registers starting from high to low. We've already saved the + previous frame pointer and previous return address for the debugger's + sake. The debugger allows us to not need a nop in the epilog if at least + one register is reloaded in addition to return address. */ + + if (gmask) + { + for (regno = 1; regno <= 31; regno++) + { + if ((gmask & (1L << regno)) != 0) + { + if ((regno & 0x1) == 0 && ((gmask & (1L << (regno+1))) != 0)) + { + /* We can save two registers in a row. If we're not at a + double word boundary, move to one. + sparc_flat_compute_frame_size ensures there's room to do + this. */ + if (offset % 8 != 0) + offset += UNITS_PER_WORD; + + if (word_op[0] == 's') + { + fprintf (file, "\t%s\t%s, [%s+%d]\n", + doubleword_op, reg_names[regno], + base_reg, offset); + if (dwarf2out_do_frame ()) + { + char *l = dwarf2out_cfi_label (); + dwarf2out_reg_save (l, regno, offset + base_offset); + dwarf2out_reg_save + (l, regno+1, offset+base_offset + UNITS_PER_WORD); + } + } + else + fprintf (file, "\t%s\t[%s+%d], %s\n", + doubleword_op, base_reg, offset, + reg_names[regno]); + + offset += 2 * UNITS_PER_WORD; + regno++; + } + else + { + if (word_op[0] == 's') + { + fprintf (file, "\t%s\t%s, [%s+%d]\n", + word_op, reg_names[regno], + base_reg, offset); + if (dwarf2out_do_frame ()) + dwarf2out_reg_save ("", regno, offset + base_offset); + } + else + fprintf (file, "\t%s\t[%s+%d], %s\n", + word_op, base_reg, offset, reg_names[regno]); + + offset += UNITS_PER_WORD; + } + } + } + } + + if (fmask) + { + for (regno = 32; regno <= 63; regno++) + { + if ((fmask & (1L << (regno - 32))) != 0) + { + if (word_op[0] == 's') + { + fprintf (file, "\t%s\t%s, [%s+%d]\n", + word_op, reg_names[regno], + base_reg, offset); + if (dwarf2out_do_frame ()) + dwarf2out_reg_save ("", regno, offset + base_offset); + } + else + fprintf (file, "\t%s\t[%s+%d], %s\n", + word_op, base_reg, offset, reg_names[regno]); + + offset += UNITS_PER_WORD; + } + } + } +} + +/* Set up the stack and frame (if desired) for the function. */ + +void +sparc_flat_output_function_prologue (file, size) + FILE *file; + int size; +{ + char *sp_str = reg_names[STACK_POINTER_REGNUM]; + unsigned long gmask = current_frame_info.gmask; + + /* This is only for the human reader. */ + fprintf (file, "\t%s#PROLOGUE# 0\n", ASM_COMMENT_START); + fprintf (file, "\t%s# vars= %ld, regs= %d/%d, args= %d, extra= %ld\n", + ASM_COMMENT_START, + current_frame_info.var_size, + current_frame_info.gp_reg_size / 4, + current_frame_info.fp_reg_size / 4, + current_function_outgoing_args_size, + current_frame_info.extra_size); + + size = SPARC_STACK_ALIGN (size); + size = (! current_frame_info.initialized + ? sparc_flat_compute_frame_size (size) + : current_frame_info.total_size); + + /* These cases shouldn't happen. Catch them now. */ + if (size == 0 && (gmask || current_frame_info.fmask)) + abort (); + + /* Allocate our stack frame by decrementing %sp. + At present, the only algorithm gdb can use to determine if this is a + flat frame is if we always set %i7 if we set %sp. This can be optimized + in the future by putting in some sort of debugging information that says + this is a `flat' function. However, there is still the case of debugging + code without such debugging information (including cases where most fns + have such info, but there is one that doesn't). So, always do this now + so we don't get a lot of code out there that gdb can't handle. + If the frame pointer isn't needn't then that's ok - gdb won't be able to + distinguish us from a non-flat function but there won't (and shouldn't) + be any differences anyway. The return pc is saved (if necessary) right + after %i7 so gdb won't have to look too far to find it. */ + if (size > 0) + { + unsigned int reg_offset = current_frame_info.reg_offset; + char *fp_str = reg_names[FRAME_POINTER_REGNUM]; + char *t1_str = "%g1"; + + /* Things get a little tricky if local variables take up more than ~4096 + bytes and outgoing arguments take up more than ~4096 bytes. When that + happens, the register save area can't be accessed from either end of + the frame. Handle this by decrementing %sp to the start of the gp + register save area, save the regs, update %i7, and then set %sp to its + final value. Given that we only have one scratch register to play + with it is the cheapest solution, and it helps gdb out as it won't + slow down recognition of flat functions. + Don't change the order of insns emitted here without checking with + the gdb folk first. */ + + /* Is the entire register save area offsettable from %sp? */ + if (reg_offset < 4096 - 64 * UNITS_PER_WORD) + { + if (size <= 4096) + { + fprintf (file, "\tadd\t%s, %d, %s\n", + sp_str, -size, sp_str); + if (gmask & FRAME_POINTER_MASK) + { + fprintf (file, "\tst\t%s, [%s+%d]\n", + fp_str, sp_str, reg_offset); + fprintf (file, "\tsub\t%s, %d, %s\t%s# set up frame pointer\n", + sp_str, -size, fp_str, ASM_COMMENT_START); + reg_offset += 4; + } + } + else + { + fprintf (file, "\tset\t%d, %s\n\tsub\t%s, %s, %s\n", + size, t1_str, sp_str, t1_str, sp_str); + if (gmask & FRAME_POINTER_MASK) + { + fprintf (file, "\tst\t%s, [%s+%d]\n", + fp_str, sp_str, reg_offset); + fprintf (file, "\tadd\t%s, %s, %s\t%s# set up frame pointer\n", + sp_str, t1_str, fp_str, ASM_COMMENT_START); + reg_offset += 4; + } + } + if (dwarf2out_do_frame ()) + { + char *l = dwarf2out_cfi_label (); + if (gmask & FRAME_POINTER_MASK) + { + dwarf2out_reg_save (l, FRAME_POINTER_REGNUM, + reg_offset - 4 - size); + dwarf2out_def_cfa (l, FRAME_POINTER_REGNUM, 0); + } + else + dwarf2out_def_cfa (l, STACK_POINTER_REGNUM, size); + } + if (gmask & RETURN_ADDR_MASK) + { + fprintf (file, "\tst\t%s, [%s+%d]\n", + reg_names[RETURN_ADDR_REGNUM], sp_str, reg_offset); + if (dwarf2out_do_frame ()) + dwarf2out_return_save ("", reg_offset - size); + reg_offset += 4; + } + sparc_flat_save_restore (file, sp_str, reg_offset, + gmask & ~(FRAME_POINTER_MASK | RETURN_ADDR_MASK), + current_frame_info.fmask, + "st", "std", -size); + } + else + { + /* Subtract %sp in two steps, but make sure there is always a + 64 byte register save area, and %sp is properly aligned. */ + /* Amount to decrement %sp by, the first time. */ + unsigned int size1 = ((size - reg_offset + 64) + 15) & -16; + /* Offset to register save area from %sp. */ + unsigned int offset = size1 - (size - reg_offset); + + if (size1 <= 4096) + { + fprintf (file, "\tadd\t%s, %d, %s\n", + sp_str, -size1, sp_str); + if (gmask & FRAME_POINTER_MASK) + { + fprintf (file, "\tst\t%s, [%s+%d]\n\tsub\t%s, %d, %s\t%s# set up frame pointer\n", + fp_str, sp_str, offset, sp_str, -size1, fp_str, + ASM_COMMENT_START); + offset += 4; + } + } + else + { + fprintf (file, "\tset\t%d, %s\n\tsub\t%s, %s, %s\n", + size1, t1_str, sp_str, t1_str, sp_str); + if (gmask & FRAME_POINTER_MASK) + { + fprintf (file, "\tst\t%s, [%s+%d]\n\tadd\t%s, %s, %s\t%s# set up frame pointer\n", + fp_str, sp_str, offset, sp_str, t1_str, fp_str, + ASM_COMMENT_START); + offset += 4; + } + } + if (dwarf2out_do_frame ()) + { + char *l = dwarf2out_cfi_label (); + if (gmask & FRAME_POINTER_MASK) + { + dwarf2out_reg_save (l, FRAME_POINTER_REGNUM, + offset - 4 - size1); + dwarf2out_def_cfa (l, FRAME_POINTER_REGNUM, 0); + } + else + dwarf2out_def_cfa (l, STACK_POINTER_REGNUM, size1); + } + if (gmask & RETURN_ADDR_MASK) + { + fprintf (file, "\tst\t%s, [%s+%d]\n", + reg_names[RETURN_ADDR_REGNUM], sp_str, offset); + if (dwarf2out_do_frame ()) + /* offset - size1 == reg_offset - size + if reg_offset were updated above like offset. */ + dwarf2out_return_save ("", offset - size1); + offset += 4; + } + sparc_flat_save_restore (file, sp_str, offset, + gmask & ~(FRAME_POINTER_MASK | RETURN_ADDR_MASK), + current_frame_info.fmask, + "st", "std", -size1); + fprintf (file, "\tset\t%d, %s\n\tsub\t%s, %s, %s\n", + size - size1, t1_str, sp_str, t1_str, sp_str); + if (dwarf2out_do_frame ()) + if (! (gmask & FRAME_POINTER_MASK)) + dwarf2out_def_cfa ("", STACK_POINTER_REGNUM, size); + } + } + + fprintf (file, "\t%s#PROLOGUE# 1\n", ASM_COMMENT_START); +} + +/* Do any necessary cleanup after a function to restore stack, frame, + and regs. */ + +void +sparc_flat_output_function_epilogue (file, size) + FILE *file; + int size; +{ + rtx epilogue_delay = current_function_epilogue_delay_list; + int noepilogue = FALSE; + + /* This is only for the human reader. */ + fprintf (file, "\t%s#EPILOGUE#\n", ASM_COMMENT_START); + + /* The epilogue does not depend on any registers, but the stack + registers, so we assume that if we have 1 pending nop, it can be + ignored, and 2 it must be filled (2 nops occur for integer + multiply and divide). */ + + size = SPARC_STACK_ALIGN (size); + size = (!current_frame_info.initialized + ? sparc_flat_compute_frame_size (size) + : current_frame_info.total_size); + + if (size == 0 && epilogue_delay == 0) + { + rtx insn = get_last_insn (); + + /* If the last insn was a BARRIER, we don't have to write any code + because a jump (aka return) was put there. */ + if (GET_CODE (insn) == NOTE) + insn = prev_nonnote_insn (insn); + if (insn && GET_CODE (insn) == BARRIER) + noepilogue = TRUE; + } + + if (!noepilogue) + { + unsigned int reg_offset = current_frame_info.reg_offset; + unsigned int size1; + char *sp_str = reg_names[STACK_POINTER_REGNUM]; + char *fp_str = reg_names[FRAME_POINTER_REGNUM]; + char *t1_str = "%g1"; + + /* In the reload sequence, we don't need to fill the load delay + slots for most of the loads, also see if we can fill the final + delay slot if not otherwise filled by the reload sequence. */ + + if (size > 4095) + fprintf (file, "\tset\t%d, %s\n", size, t1_str); + + if (frame_pointer_needed) + { + if (size > 4095) + fprintf (file,"\tsub\t%s, %s, %s\t\t%s# sp not trusted here\n", + fp_str, t1_str, sp_str, ASM_COMMENT_START); + else + fprintf (file,"\tsub\t%s, %d, %s\t\t%s# sp not trusted here\n", + fp_str, size, sp_str, ASM_COMMENT_START); + } + + /* Is the entire register save area offsettable from %sp? */ + if (reg_offset < 4096 - 64 * UNITS_PER_WORD) + { + size1 = 0; + } + else + { + /* Restore %sp in two steps, but make sure there is always a + 64 byte register save area, and %sp is properly aligned. */ + /* Amount to increment %sp by, the first time. */ + size1 = ((reg_offset - 64 - 16) + 15) & -16; + /* Offset to register save area from %sp. */ + reg_offset = size1 - reg_offset; + + fprintf (file, "\tset\t%d, %s\n\tadd\t%s, %s, %s\n", + size1, t1_str, sp_str, t1_str, sp_str); + } + + /* We must restore the frame pointer and return address reg first + because they are treated specially by the prologue output code. */ + if (current_frame_info.gmask & FRAME_POINTER_MASK) + { + fprintf (file, "\tld\t[%s+%d], %s\n", + sp_str, reg_offset, fp_str); + reg_offset += 4; + } + if (current_frame_info.gmask & RETURN_ADDR_MASK) + { + fprintf (file, "\tld\t[%s+%d], %s\n", + sp_str, reg_offset, reg_names[RETURN_ADDR_REGNUM]); + reg_offset += 4; + } + + /* Restore any remaining saved registers. */ + sparc_flat_save_restore (file, sp_str, reg_offset, + current_frame_info.gmask & ~(FRAME_POINTER_MASK | RETURN_ADDR_MASK), + current_frame_info.fmask, + "ld", "ldd", 0); + + /* If we had to increment %sp in two steps, record it so the second + restoration in the epilogue finishes up. */ + if (size1 > 0) + { + size -= size1; + if (size > 4095) + fprintf (file, "\tset\t%d, %s\n", + size, t1_str); + } + + if (current_function_returns_struct) + fprintf (file, "\tjmp\t%%o7+12\n"); + else + fprintf (file, "\tretl\n"); + + /* If the only register saved is the return address, we need a + nop, unless we have an instruction to put into it. Otherwise + we don't since reloading multiple registers doesn't reference + the register being loaded. */ + + if (epilogue_delay) + { + if (size) + abort (); + final_scan_insn (XEXP (epilogue_delay, 0), file, 1, -2, 1); + } + + else if (size > 4095) + fprintf (file, "\tadd\t%s, %s, %s\n", sp_str, t1_str, sp_str); + + else if (size > 0) + fprintf (file, "\tadd\t%s, %d, %s\n", sp_str, size, sp_str); + + else + fprintf (file, "\tnop\n"); + } + + /* Reset state info for each function. */ + current_frame_info = zero_frame_info; + + sparc_output_deferred_case_vectors (); +} + +/* Define the number of delay slots needed for the function epilogue. + + On the sparc, we need a slot if either no stack has been allocated, + or the only register saved is the return register. */ + +int +sparc_flat_epilogue_delay_slots () +{ + if (!current_frame_info.initialized) + (void) sparc_flat_compute_frame_size (get_frame_size ()); + + if (current_frame_info.total_size == 0) + return 1; + + return 0; +} + +/* Return true is TRIAL is a valid insn for the epilogue delay slot. + Any single length instruction which doesn't reference the stack or frame + pointer is OK. */ + +int +sparc_flat_eligible_for_epilogue_delay (trial, slot) + rtx trial; + int slot ATTRIBUTE_UNUSED; +{ + rtx pat = PATTERN (trial); + + if (get_attr_length (trial) != 1) + return 0; + + /* If %g0 is live, there are lots of things we can't handle. + Rather than trying to find them all now, let's punt and only + optimize things as necessary. */ + if (TARGET_LIVE_G0) + return 0; + + if (! reg_mentioned_p (stack_pointer_rtx, pat) + && ! reg_mentioned_p (frame_pointer_rtx, pat)) + return 1; + + return 0; +} + +/* 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. */ + +static int +supersparc_adjust_cost (insn, link, dep_insn, cost) + rtx insn; + rtx link; + rtx dep_insn; + int cost; +{ + enum attr_type insn_type; + + if (! recog_memoized (insn)) + return 0; + + insn_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 a load, then the dependence must be on the memory address; + add an extra "cycle". Note that the cost could be two cycles + if the reg was written late in an instruction group; we ca not tell + here. */ + if (insn_type == TYPE_LOAD || insn_type == TYPE_FPLOAD) + return cost + 3; + + /* Get the delay only if the address of the store is the dependence. */ + if (insn_type == TYPE_STORE || insn_type == TYPE_FPSTORE) + { + rtx pat = PATTERN(insn); + rtx dep_pat = PATTERN (dep_insn); + + if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET) + return cost; /* This should not happen! */ + + /* The dependency between the two instructions was on the data that + is being stored. Assume that this implies that the address of the + store is not dependent. */ + if (rtx_equal_p (SET_DEST (dep_pat), SET_SRC (pat))) + return cost; + + return cost + 3; /* An approximation. */ + } + + /* A shift instruction cannot receive its data from an instruction + in the same cycle; add a one cycle penalty. */ + if (insn_type == TYPE_SHIFT) + return cost + 3; /* Split before cascade into shift. */ + } + else + { + /* Anti- or output- dependency; DEP_INSN reads/writes a register that + INSN writes some cycles later. */ + + /* These are only significant for the fpu unit; writing a fp reg before + the fpu has finished with it stalls the processor. */ + + /* Reusing an integer register causes no problems. */ + if (insn_type == TYPE_IALU || insn_type == TYPE_SHIFT) + return 0; + } + + return cost; +} + +static int +hypersparc_adjust_cost (insn, link, dep_insn, cost) + rtx insn; + rtx link; + rtx dep_insn; + int cost; +{ + enum attr_type insn_type, dep_type; + rtx pat = PATTERN(insn); + rtx dep_pat = PATTERN (dep_insn); + + if (recog_memoized (insn) < 0 || recog_memoized (dep_insn) < 0) + return cost; + + insn_type = get_attr_type (insn); + dep_type = get_attr_type (dep_insn); + + switch (REG_NOTE_KIND (link)) + { + case 0: + /* Data dependency; DEP_INSN writes a register that INSN reads some + cycles later. */ + + switch (insn_type) + { + case TYPE_STORE: + case TYPE_FPSTORE: + /* Get the delay iff the address of the store is the dependence. */ + if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET) + return cost; + + if (rtx_equal_p (SET_DEST (dep_pat), SET_SRC (pat))) + return cost; + return cost + 3; + + case TYPE_LOAD: + case TYPE_SLOAD: + case TYPE_FPLOAD: + /* If a load, then the dependence must be on the memory address. If + the addresses aren't equal, then it might be a false dependency */ + if (dep_type == TYPE_STORE || dep_type == TYPE_FPSTORE) + { + if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET + || GET_CODE (SET_DEST (dep_pat)) != MEM + || GET_CODE (SET_SRC (pat)) != MEM + || ! rtx_equal_p (XEXP (SET_DEST (dep_pat), 0), + XEXP (SET_SRC (pat), 0))) + return cost + 2; + + return cost + 8; + } + break; + + case TYPE_BRANCH: + /* Compare to branch latency is 0. There is no benefit from + separating compare and branch. */ + if (dep_type == TYPE_COMPARE) + return 0; + /* Floating point compare to branch latency is less than + compare to conditional move. */ + if (dep_type == TYPE_FPCMP) + return cost - 1; + break; + } + break; + + case REG_DEP_ANTI: + /* Anti-dependencies only penalize the fpu unit. */ + if (insn_type == TYPE_IALU || insn_type == TYPE_SHIFT) + return 0; + break; + + default: + break; + } + + return cost; +} + +static int +ultrasparc_adjust_cost (insn, link, dep_insn, cost) + rtx insn; + rtx link; + rtx dep_insn; + int cost; +{ + enum attr_type insn_type, dep_type; + rtx pat = PATTERN(insn); + rtx dep_pat = PATTERN (dep_insn); + + if (recog_memoized (insn) < 0 || recog_memoized (dep_insn) < 0) + return cost; + + insn_type = get_attr_type (insn); + dep_type = get_attr_type (dep_insn); + + /* Nothing issues in parallel with integer multiplies, so + mark as zero cost since the scheduler can not do anything + about it. */ + if (insn_type == TYPE_IMUL) + return 0; + +#define SLOW_FP(dep_type) \ +(dep_type == TYPE_FPSQRT || dep_type == TYPE_FPDIVS || dep_type == TYPE_FPDIVD) + + switch (REG_NOTE_KIND (link)) + { + case 0: + /* Data dependency; DEP_INSN writes a register that INSN reads some + cycles later. */ + + if (dep_type == TYPE_CMOVE) + { + /* Instructions that read the result of conditional moves cannot + be in the same group or the following group. */ + return cost + 1; + } + + switch (insn_type) + { + /* UltraSPARC can dual issue a store and an instruction setting + the value stored, except for divide and square root. */ + case TYPE_FPSTORE: + if (! SLOW_FP (dep_type)) + return 0; + return cost; + + case TYPE_STORE: + if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET) + return cost; + + if (rtx_equal_p (SET_DEST (dep_pat), SET_SRC (pat))) + /* The dependency between the two instructions is on the data + that is being stored. Assume that the address of the store + is not also dependent. */ + return 0; + return cost; + + case TYPE_LOAD: + case TYPE_SLOAD: + case TYPE_FPLOAD: + /* A load does not return data until at least 11 cycles after + a store to the same location. 3 cycles are accounted for + in the load latency; add the other 8 here. */ + if (dep_type == TYPE_STORE || dep_type == TYPE_FPSTORE) + { + /* If the addresses are not equal this may be a false + dependency because pointer aliasing could not be + determined. Add only 2 cycles in that case. 2 is + an arbitrary compromise between 8, which would cause + the scheduler to generate worse code elsewhere to + compensate for a dependency which might not really + exist, and 0. */ + if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET + || GET_CODE (SET_SRC (pat)) != MEM + || GET_CODE (SET_DEST (dep_pat)) != MEM + || ! rtx_equal_p (XEXP (SET_SRC (pat), 0), + XEXP (SET_DEST (dep_pat), 0))) + return cost + 2; + + return cost + 8; + } + return cost; + + case TYPE_BRANCH: + /* Compare to branch latency is 0. There is no benefit from + separating compare and branch. */ + if (dep_type == TYPE_COMPARE) + return 0; + /* Floating point compare to branch latency is less than + compare to conditional move. */ + if (dep_type == TYPE_FPCMP) + return cost - 1; + return cost; + + case TYPE_FPCMOVE: + /* FMOVR class instructions can not issue in the same cycle + or the cycle after an instruction which writes any + integer register. Model this as cost 2 for dependent + instructions. */ + if ((dep_type == TYPE_IALU || dep_type == TYPE_UNARY + || dep_type == TYPE_BINARY) + && cost < 2) + return 2; + /* Otherwise check as for integer conditional moves. */ + + case TYPE_CMOVE: + /* Conditional moves involving integer registers wait until + 3 cycles after loads return data. The interlock applies + to all loads, not just dependent loads, but that is hard + to model. */ + if (dep_type == TYPE_LOAD || dep_type == TYPE_SLOAD) + return cost + 3; + return cost; + + default: + break; + } + break; + + case REG_DEP_ANTI: + /* Divide and square root lock destination registers for full latency. */ + if (! SLOW_FP (dep_type)) + return 0; + break; + + case REG_DEP_OUTPUT: + /* IEU and FPU instruction that have the same destination + register cannot be grouped together. */ + return cost + 1; + + default: + break; + } + + /* Other costs not accounted for: + - Single precision floating point loads lock the other half of + the even/odd register pair. + - Several hazards associated with ldd/std are ignored because these + instructions are rarely generated for V9. + - The floating point pipeline can not have both a single and double + precision operation active at the same time. Format conversions + and graphics instructions are given honorary double precision status. + - call and jmpl are always the first instruction in a group. */ + + return cost; + +#undef SLOW_FP +} + +int +sparc_adjust_cost(insn, link, dep, cost) + rtx insn; + rtx link; + rtx dep; + int cost; +{ + switch (sparc_cpu) + { + case PROCESSOR_SUPERSPARC: + cost = supersparc_adjust_cost (insn, link, dep, cost); + break; + case PROCESSOR_HYPERSPARC: + case PROCESSOR_SPARCLITE86X: + cost = hypersparc_adjust_cost (insn, link, dep, cost); + break; + case PROCESSOR_ULTRASPARC: + cost = ultrasparc_adjust_cost (insn, link, dep, cost); + break; + default: + break; + } + return cost; +} + +/* This describes the state of the UltraSPARC pipeline during + instruction scheduling. */ + +#define TMASK(__x) ((unsigned)1 << ((int)(__x))) +#define UMASK(__x) ((unsigned)1 << ((int)(__x))) + +enum ultra_code { NONE=0, /* no insn at all */ + IEU0, /* shifts and conditional moves */ + IEU1, /* condition code setting insns, calls+jumps */ + IEUN, /* all other single cycle ieu insns */ + LSU, /* loads and stores */ + CTI, /* branches */ + FPM, /* FPU pipeline 1, multiplies and divides */ + FPA, /* FPU pipeline 2, all other operations */ + SINGLE, /* single issue instructions */ + NUM_ULTRA_CODES }; + +static char *ultra_code_names[NUM_ULTRA_CODES] = { + "NONE", "IEU0", "IEU1", "IEUN", "LSU", "CTI", + "FPM", "FPA", "SINGLE" }; + +struct ultrasparc_pipeline_state { + /* The insns in this group. */ + rtx group[4]; + + /* The code for each insn. */ + enum ultra_code codes[4]; + + /* Which insns in this group have been committed by the + scheduler. This is how we determine how many more + can issue this cycle. */ + char commit[4]; + + /* How many insns in this group. */ + char group_size; + + /* Mask of free slots still in this group. */ + char free_slot_mask; + + /* The slotter uses the following to determine what other + insn types can still make their way into this group. */ + char contents [NUM_ULTRA_CODES]; + char num_ieu_insns; +}; + +#define ULTRA_NUM_HIST 8 +static struct ultrasparc_pipeline_state ultra_pipe_hist[ULTRA_NUM_HIST]; +static int ultra_cur_hist; +static int ultra_cycles_elapsed; + +#define ultra_pipe (ultra_pipe_hist[ultra_cur_hist]) + +/* Given TYPE_MASK compute the ultra_code it has. */ +static enum ultra_code +ultra_code_from_mask (type_mask) + int type_mask; +{ + if (type_mask & (TMASK (TYPE_SHIFT) | TMASK (TYPE_CMOVE))) + return IEU0; + else if (type_mask & (TMASK (TYPE_COMPARE) | + TMASK (TYPE_CALL) | + TMASK (TYPE_UNCOND_BRANCH))) + return IEU1; + else if (type_mask & (TMASK (TYPE_IALU) | TMASK (TYPE_BINARY) | + TMASK (TYPE_MOVE) | TMASK (TYPE_UNARY))) + return IEUN; + else if (type_mask & (TMASK (TYPE_LOAD) | TMASK (TYPE_SLOAD) | + TMASK (TYPE_STORE) | TMASK (TYPE_FPLOAD) | + TMASK (TYPE_FPSTORE))) + return LSU; + else if (type_mask & (TMASK (TYPE_FPMUL) | TMASK (TYPE_FPDIVS) | + TMASK (TYPE_FPDIVD) | TMASK (TYPE_FPSQRT))) + return FPM; + else if (type_mask & (TMASK (TYPE_FPMOVE) | TMASK (TYPE_FPCMOVE) | + TMASK (TYPE_FP) | TMASK (TYPE_FPCMP))) + return FPA; + else if (type_mask & TMASK (TYPE_BRANCH)) + return CTI; + + return SINGLE; +} + +/* Check INSN (a conditional move) and make sure that it's + results are available at this cycle. Return 1 if the + results are in fact ready. */ +static int +ultra_cmove_results_ready_p (insn) + rtx insn; +{ + struct ultrasparc_pipeline_state *up; + int entry, slot; + + /* If this got dispatched in the previous + group, the results are not ready. */ + entry = (ultra_cur_hist - 1) % (ULTRA_NUM_HIST - 1); + up = &ultra_pipe_hist[entry]; + slot = 4; + while (--slot >= 0) + if (up->group[slot] == insn) + return 0; + + return 1; +} + +/* Walk backwards in pipeline history looking for FPU + operations which use a mode different than FPMODE and + will create a stall if an insn using FPMODE were to be + dispatched this cycle. */ +static int +ultra_fpmode_conflict_exists (fpmode) + enum machine_mode fpmode; +{ + int hist_ent; + int hist_lim; + + hist_ent = (ultra_cur_hist - 1) % (ULTRA_NUM_HIST - 1); + if (ultra_cycles_elapsed < 4) + hist_lim = ultra_cycles_elapsed; + else + hist_lim = 4; + while (hist_lim > 0) + { + struct ultrasparc_pipeline_state *up = &ultra_pipe_hist[hist_ent]; + int slot = 4; + + while (--slot >= 0) + { + rtx insn = up->group[slot]; + enum machine_mode this_mode; + rtx pat; + + if (! insn + || GET_CODE (insn) != INSN + || (pat = PATTERN (insn)) == 0 + || GET_CODE (pat) != SET) + continue; + + this_mode = GET_MODE (SET_DEST (pat)); + if ((this_mode != SFmode + && this_mode != DFmode) + || this_mode == fpmode) + continue; + + /* If it is not FMOV, FABS, FNEG, FDIV, or FSQRT then + we will get a stall. Loads and stores are independant + of these rules. */ + if (GET_CODE (SET_SRC (pat)) != ABS + && GET_CODE (SET_SRC (pat)) != NEG + && ((TMASK (get_attr_type (insn)) & + (TMASK (TYPE_FPDIVS) | TMASK (TYPE_FPDIVD) | + TMASK (TYPE_FPMOVE) | TMASK (TYPE_FPSQRT) | + TMASK (TYPE_LOAD) | TMASK (TYPE_STORE))) == 0)) + return 1; + } + hist_lim--; + hist_ent = (hist_ent - 1) % (ULTRA_NUM_HIST - 1); + } + + /* No conflicts, safe to dispatch. */ + return 0; +} + +/* Find an instruction in LIST which has one of the + type attributes enumerated in TYPE_MASK. START + says where to begin the search. + + NOTE: This scheme depends upon the fact that we + have less than 32 distinct type attributes. */ + +static int ultra_types_avail; + +static rtx * +ultra_find_type (type_mask, list, start) + int type_mask; + rtx *list; + int start; +{ + int i; + + /* Short circuit if no such insn exists in the ready + at the moment. */ + if ((type_mask & ultra_types_avail) == 0) + return 0; + + for (i = start; i >= 0; i--) + { + rtx insn = list[i]; + + if (recog_memoized (insn) >= 0 + && (TMASK(get_attr_type (insn)) & type_mask)) + { + enum machine_mode fpmode = SFmode; + rtx pat = 0; + int slot; + int check_depend = 0; + int check_fpmode_conflict = 0; + + if (GET_CODE (insn) == INSN + && (pat = PATTERN(insn)) != 0 + && GET_CODE (pat) == SET + && !(type_mask & (TMASK (TYPE_STORE) | + TMASK (TYPE_FPSTORE)))) + { + check_depend = 1; + if (GET_MODE (SET_DEST (pat)) == SFmode + || GET_MODE (SET_DEST (pat)) == DFmode) + { + fpmode = GET_MODE (SET_DEST (pat)); + check_fpmode_conflict = 1; + } + } + + slot = 4; + while(--slot >= 0) + { + rtx slot_insn = ultra_pipe.group[slot]; + rtx slot_pat; + + /* Already issued, bad dependency, or FPU + mode conflict. */ + if (slot_insn != 0 + && (slot_pat = PATTERN (slot_insn)) != 0 + && ((insn == slot_insn) + || (check_depend == 1 + && GET_CODE (slot_insn) == INSN + && GET_CODE (slot_pat) == SET + && ((GET_CODE (SET_DEST (slot_pat)) == REG + && GET_CODE (SET_SRC (pat)) == REG + && REGNO (SET_DEST (slot_pat)) == + REGNO (SET_SRC (pat))) + || (GET_CODE (SET_DEST (slot_pat)) == SUBREG + && GET_CODE (SET_SRC (pat)) == SUBREG + && REGNO (SUBREG_REG (SET_DEST (slot_pat))) == + REGNO (SUBREG_REG (SET_SRC (pat))) + && SUBREG_WORD (SET_DEST (slot_pat)) == + SUBREG_WORD (SET_SRC (pat))))) + || (check_fpmode_conflict == 1 + && GET_CODE (slot_insn) == INSN + && GET_CODE (slot_pat) == SET + && (GET_MODE (SET_DEST (slot_pat)) == SFmode + || GET_MODE (SET_DEST (slot_pat)) == DFmode) + && GET_MODE (SET_DEST (slot_pat)) != fpmode))) + goto next; + } + + /* Check for peculiar result availability and dispatch + interference situations. */ + if (pat != 0 + && ultra_cycles_elapsed > 0) + { + rtx link; + + for (link = LOG_LINKS (insn); link; link = XEXP (link, 1)) + { + rtx link_insn = XEXP (link, 0); + if (GET_CODE (link_insn) == INSN + && recog_memoized (link_insn) >= 0 + && (TMASK (get_attr_type (link_insn)) & + (TMASK (TYPE_CMOVE) | TMASK (TYPE_FPCMOVE))) + && ! ultra_cmove_results_ready_p (link_insn)) + goto next; + } + + if (check_fpmode_conflict + && ultra_fpmode_conflict_exists (fpmode)) + goto next; + } + + return &list[i]; + } + next: + ; + } + return 0; +} + +static void +ultra_build_types_avail (ready, n_ready) + rtx *ready; + int n_ready; +{ + int i = n_ready - 1; + + ultra_types_avail = 0; + while(i >= 0) + { + rtx insn = ready[i]; + + if (recog_memoized (insn) >= 0) + ultra_types_avail |= TMASK (get_attr_type (insn)); + + i -= 1; + } +} + +/* Place insn pointed to my IP into the pipeline. + Make element THIS of READY be that insn if it + is not already. TYPE indicates the pipeline class + this insn falls into. */ +static void +ultra_schedule_insn (ip, ready, this, type) + rtx *ip; + rtx *ready; + int this; + enum ultra_code type; +{ + int pipe_slot; + char mask = ultra_pipe.free_slot_mask; + + /* Obtain free slot. */ + for (pipe_slot = 0; pipe_slot < 4; pipe_slot++) + if ((mask & (1 << pipe_slot)) != 0) + break; + if (pipe_slot == 4) + abort (); + + /* In it goes, and it hasn't been committed yet. */ + ultra_pipe.group[pipe_slot] = *ip; + ultra_pipe.codes[pipe_slot] = type; + ultra_pipe.contents[type] = 1; + if (UMASK (type) & + (UMASK (IEUN) | UMASK (IEU0) | UMASK (IEU1))) + ultra_pipe.num_ieu_insns += 1; + + ultra_pipe.free_slot_mask = (mask & ~(1 << pipe_slot)); + ultra_pipe.group_size += 1; + ultra_pipe.commit[pipe_slot] = 0; + + /* Update ready list. */ + if (ip != &ready[this]) + { + rtx temp = *ip; + + *ip = ready[this]; + ready[this] = temp; + } +} + +/* Advance to the next pipeline group. */ +static void +ultra_flush_pipeline () +{ + ultra_cur_hist = (ultra_cur_hist + 1) % (ULTRA_NUM_HIST - 1); + ultra_cycles_elapsed += 1; + bzero ((char *) &ultra_pipe, sizeof ultra_pipe); + ultra_pipe.free_slot_mask = 0xf; +} + +static int ultra_reorder_called_this_block; + +/* Init our data structures for this current block. */ +void +ultrasparc_sched_init (dump, sched_verbose) + FILE *dump ATTRIBUTE_UNUSED; + int sched_verbose ATTRIBUTE_UNUSED; +{ + bzero ((char *) ultra_pipe_hist, sizeof ultra_pipe_hist); + ultra_cur_hist = 0; + ultra_cycles_elapsed = 0; + ultra_reorder_called_this_block = 0; + ultra_pipe.free_slot_mask = 0xf; +} + +/* INSN has been scheduled, update pipeline commit state + and return how many instructions are still to be + scheduled in this group. */ +int +ultrasparc_variable_issue (insn) + rtx insn; +{ + struct ultrasparc_pipeline_state *up = &ultra_pipe; + int i, left_to_fire; + + left_to_fire = 0; + for (i = 0; i < 4; i++) + { + if (up->group[i] == 0) + continue; + + if (up->group[i] == insn) + { + up->commit[i] = 1; + } + else if (! up->commit[i]) + left_to_fire++; + } + + return left_to_fire; +} + +/* In actual_hazard_this_instance, we may have yanked some + instructions from the ready list due to conflict cost + adjustments. If so, and such an insn was in our pipeline + group, remove it and update state. */ +static void +ultra_rescan_pipeline_state (ready, n_ready) + rtx *ready; + int n_ready; +{ + struct ultrasparc_pipeline_state *up = &ultra_pipe; + int i; + + for (i = 0; i < 4; i++) + { + rtx insn = up->group[i]; + int j; + + if (! insn) + continue; + + /* If it has been committed, then it was removed from + the ready list because it was actually scheduled, + and that is not the case we are searching for here. */ + if (up->commit[i] != 0) + continue; + + for (j = n_ready - 1; j >= 0; j--) + if (ready[j] == insn) + break; + + /* If we didn't find it, toss it. */ + if (j < 0) + { + enum ultra_code ucode = up->codes[i]; + + up->group[i] = 0; + up->codes[i] = NONE; + up->contents[ucode] = 0; + if (UMASK (ucode) & + (UMASK (IEUN) | UMASK (IEU0) | UMASK (IEU1))) + up->num_ieu_insns -= 1; + + up->free_slot_mask |= (1 << i); + up->group_size -= 1; + up->commit[i] = 0; + } + } +} + +void +ultrasparc_sched_reorder (dump, sched_verbose, ready, n_ready) + FILE *dump; + int sched_verbose; + rtx *ready; + int n_ready; +{ + struct ultrasparc_pipeline_state *up = &ultra_pipe; + int i, this_insn; + + /* We get called once unnecessarily per block of insns + scheduled. */ + if (ultra_reorder_called_this_block == 0) + { + ultra_reorder_called_this_block = 1; + return; + } + + if (sched_verbose) + { + int n; + + fprintf (dump, "\n;;\tUltraSPARC Looking at ["); + for (n = n_ready - 1; n >= 0; n--) + { + rtx insn = ready[n]; + enum ultra_code ucode; + + if (recog_memoized (insn) < 0) + continue; + ucode = ultra_code_from_mask (TMASK (get_attr_type (insn))); + if (n != 0) + fprintf (dump, "%s(%d) ", + ultra_code_names[ucode], + INSN_UID (insn)); + else + fprintf (dump, "%s(%d)", + ultra_code_names[ucode], + INSN_UID (insn)); + } + fprintf (dump, "]\n"); + } + + this_insn = n_ready - 1; + + /* Skip over junk we don't understand. */ + while ((this_insn >= 0) + && recog_memoized (ready[this_insn]) < 0) + this_insn--; + + ultra_build_types_avail (ready, this_insn + 1); + + while (this_insn >= 0) { + int old_group_size = up->group_size; + + if (up->group_size != 0) + { + int num_committed; + + num_committed = (up->commit[0] + up->commit[1] + + up->commit[2] + up->commit[3]); + /* If nothing has been commited from our group, or all of + them have. Clear out the (current cycle's) pipeline + state and start afresh. */ + if (num_committed == 0 + || num_committed == up->group_size) + { + ultra_flush_pipeline (); + up = &ultra_pipe; + old_group_size = 0; + } + else + { + /* OK, some ready list insns got requeued and thus removed + from the ready list. Account for this fact. */ + ultra_rescan_pipeline_state (ready, n_ready); + + /* Something "changed", make this look like a newly + formed group so the code at the end of the loop + knows that progress was in fact made. */ + if (up->group_size != old_group_size) + old_group_size = 0; + } + } + + if (up->group_size == 0) + { + /* If the pipeline is (still) empty and we have any single + group insns, get them out now as this is a good time. */ + rtx *ip = ultra_find_type ((TMASK (TYPE_RETURN) | TMASK (TYPE_ADDRESS) | + TMASK (TYPE_IMUL) | TMASK (TYPE_CMOVE) | + TMASK (TYPE_MULTI) | TMASK (TYPE_MISC)), + ready, this_insn); + if (ip) + { + ultra_schedule_insn (ip, ready, this_insn, SINGLE); + break; + } + + /* If we are not in the process of emptying out the pipe, try to + obtain an instruction which must be the first in it's group. */ + ip = ultra_find_type ((TMASK (TYPE_CALL) | + TMASK (TYPE_CALL_NO_DELAY_SLOT) | + TMASK (TYPE_UNCOND_BRANCH)), + ready, this_insn); + if (ip) + { + ultra_schedule_insn (ip, ready, this_insn, IEU1); + this_insn--; + } + else if ((ip = ultra_find_type ((TMASK (TYPE_FPDIVS) | + TMASK (TYPE_FPDIVD) | + TMASK (TYPE_FPSQRT)), + ready, this_insn)) != 0) + { + ultra_schedule_insn (ip, ready, this_insn, FPM); + this_insn--; + } + } + + /* Try to fill the integer pipeline. First, look for an IEU0 specific + operation. We can't do more IEU operations if the first 3 slots are + all full or we have dispatched two IEU insns already. */ + if ((up->free_slot_mask & 0x7) != 0 + && up->num_ieu_insns < 2 + && up->contents[IEU0] == 0 + && up->contents[IEUN] == 0) + { + rtx *ip = ultra_find_type (TMASK(TYPE_SHIFT), ready, this_insn); + if (ip) + { + ultra_schedule_insn (ip, ready, this_insn, IEU0); + this_insn--; + } + } + + /* If we can, try to find an IEU1 specific or an unnamed + IEU instruction. */ + if ((up->free_slot_mask & 0x7) != 0 + && up->num_ieu_insns < 2) + { + rtx *ip = ultra_find_type ((TMASK (TYPE_IALU) | TMASK (TYPE_BINARY) | + TMASK (TYPE_MOVE) | TMASK (TYPE_UNARY) | + (up->contents[IEU1] == 0 ? TMASK (TYPE_COMPARE) : 0)), + ready, this_insn); + if (ip) + { + rtx insn = *ip; + + ultra_schedule_insn (ip, ready, this_insn, + (!up->contents[IEU1] + && get_attr_type (insn) == TYPE_COMPARE) + ? IEU1 : IEUN); + this_insn--; + } + } + + /* If only one IEU insn has been found, try to find another unnamed + IEU operation or an IEU1 specific one. */ + if ((up->free_slot_mask & 0x7) != 0 + && up->num_ieu_insns < 2) + { + rtx *ip; + int tmask = (TMASK (TYPE_IALU) | TMASK (TYPE_BINARY) | + TMASK (TYPE_MOVE) | TMASK (TYPE_UNARY)); + + if (!up->contents[IEU1]) + tmask |= TMASK (TYPE_COMPARE); + ip = ultra_find_type (tmask, ready, this_insn); + if (ip) + { + rtx insn = *ip; + + ultra_schedule_insn (ip, ready, this_insn, + (!up->contents[IEU1] + && get_attr_type (insn) == TYPE_COMPARE) + ? IEU1 : IEUN); + this_insn--; + } + } + + /* Try for a load or store, but such an insn can only be issued + if it is within' one of the first 3 slots. */ + if ((up->free_slot_mask & 0x7) != 0 + && up->contents[LSU] == 0) + { + rtx *ip = ultra_find_type ((TMASK (TYPE_LOAD) | TMASK (TYPE_SLOAD) | + TMASK (TYPE_STORE) | TMASK (TYPE_FPLOAD) | + TMASK (TYPE_FPSTORE)), ready, this_insn); + if (ip) + { + ultra_schedule_insn (ip, ready, this_insn, LSU); + this_insn--; + } + } + + /* Now find FPU operations, first FPM class. But not divisions or + square-roots because those will break the group up. Unlike all + the previous types, these can go in any slot. */ + if (up->free_slot_mask != 0 + && up->contents[FPM] == 0) + { + rtx *ip = ultra_find_type (TMASK (TYPE_FPMUL), ready, this_insn); + if (ip) + { + ultra_schedule_insn (ip, ready, this_insn, FPM); + this_insn--; + } + } + + /* Continue on with FPA class if we have not filled the group already. */ + if (up->free_slot_mask != 0 + && up->contents[FPA] == 0) + { + rtx *ip = ultra_find_type ((TMASK (TYPE_FPMOVE) | TMASK (TYPE_FPCMOVE) | + TMASK (TYPE_FP) | TMASK (TYPE_FPCMP)), + ready, this_insn); + if (ip) + { + ultra_schedule_insn (ip, ready, this_insn, FPA); + this_insn--; + } + } + + /* Finally, maybe stick a branch in here. */ + if (up->free_slot_mask != 0 + && up->contents[CTI] == 0) + { + rtx *ip = ultra_find_type (TMASK (TYPE_BRANCH), ready, this_insn); + + /* Try to slip in a branch only if it is one of the + next 2 in the ready list. */ + if (ip && ((&ready[this_insn] - ip) < 2)) + { + ultra_schedule_insn (ip, ready, this_insn, CTI); + this_insn--; + } + } + + up->group_size = 0; + for (i = 0; i < 4; i++) + if ((up->free_slot_mask & (1 << i)) == 0) + up->group_size++; + + /* See if we made any progress... */ + if (old_group_size != up->group_size) + break; + + /* Clean out the (current cycle's) pipeline state + and try once more. If we placed no instructions + into the pipeline at all, it means a real hard + conflict exists with some earlier issued instruction + so we must advance to the next cycle to clear it up. */ + if (up->group_size == 0) + { + ultra_flush_pipeline (); + up = &ultra_pipe; + } + else + { + bzero ((char *) &ultra_pipe, sizeof ultra_pipe); + ultra_pipe.free_slot_mask = 0xf; + } + } + + if (sched_verbose) + { + int n, gsize; + + fprintf (dump, ";;\tUltraSPARC Launched ["); + gsize = up->group_size; + for (n = 0; n < 4; n++) + { + rtx insn = up->group[n]; + + if (! insn) + continue; + + gsize -= 1; + if (gsize != 0) + fprintf (dump, "%s(%d) ", + ultra_code_names[up->codes[n]], + INSN_UID (insn)); + else + fprintf (dump, "%s(%d)", + ultra_code_names[up->codes[n]], + INSN_UID (insn)); + } + fprintf (dump, "]\n"); + } +} + +int +sparc_issue_rate () +{ + switch (sparc_cpu) + { + default: + return 1; + case PROCESSOR_V9: + /* Assume V9 processors are capable of at least dual-issue. */ + return 2; + case PROCESSOR_SUPERSPARC: + return 3; + case PROCESSOR_HYPERSPARC: + case PROCESSOR_SPARCLITE86X: + return 2; + case PROCESSOR_ULTRASPARC: + return 4; + } +} + +static int +set_extends(x, insn) + rtx x, insn; +{ + register rtx pat = PATTERN (insn); + + switch (GET_CODE (SET_SRC (pat))) + { + /* Load and some shift instructions zero extend. */ + case MEM: + case ZERO_EXTEND: + /* sethi clears the high bits */ + case HIGH: + /* LO_SUM is used with sethi. sethi cleared the high + bits and the values used with lo_sum are positive */ + case LO_SUM: + /* Store flag stores 0 or 1 */ + case LT: case LTU: + case GT: case GTU: + case LE: case LEU: + case GE: case GEU: + case EQ: + case NE: + return 1; + case AND: + { + rtx op1 = XEXP (SET_SRC (pat), 1); + if (GET_CODE (op1) == CONST_INT) + return INTVAL (op1) >= 0; + if (GET_CODE (XEXP (SET_SRC (pat), 0)) == REG + && sparc_check_64 (XEXP (SET_SRC (pat), 0), insn) == 1) + return 1; + if (GET_CODE (op1) == REG + && sparc_check_64 ((op1), insn) == 1) + return 1; + } + case ASHIFT: + case LSHIFTRT: + return GET_MODE (SET_SRC (pat)) == SImode; + /* Positive integers leave the high bits zero. */ + case CONST_DOUBLE: + return ! (CONST_DOUBLE_LOW (x) & 0x80000000); + case CONST_INT: + return ! (INTVAL (x) & 0x80000000); + case ASHIFTRT: + case SIGN_EXTEND: + return - (GET_MODE (SET_SRC (pat)) == SImode); + default: + return 0; + } +} + +/* We _ought_ to have only one kind per function, but... */ +static rtx sparc_addr_diff_list; +static rtx sparc_addr_list; + +void +sparc_defer_case_vector (lab, vec, diff) + rtx lab, vec; + int diff; +{ + vec = gen_rtx_EXPR_LIST (VOIDmode, lab, vec); + if (diff) + sparc_addr_diff_list + = gen_rtx_EXPR_LIST (VOIDmode, vec, sparc_addr_diff_list); + else + sparc_addr_list = gen_rtx_EXPR_LIST (VOIDmode, vec, sparc_addr_list); +} + +static void +sparc_output_addr_vec (vec) + rtx vec; +{ + rtx lab = XEXP (vec, 0), body = XEXP (vec, 1); + int idx, vlen = XVECLEN (body, 0); + +#ifdef ASM_OUTPUT_ADDR_VEC_START + ASM_OUTPUT_ADDR_VEC_START (asm_out_file); +#endif + +#ifdef ASM_OUTPUT_CASE_LABEL + ASM_OUTPUT_CASE_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (lab), + NEXT_INSN (lab)); +#else + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (lab)); +#endif + + for (idx = 0; idx < vlen; idx++) + { + ASM_OUTPUT_ADDR_VEC_ELT + (asm_out_file, CODE_LABEL_NUMBER (XEXP (XVECEXP (body, 0, idx), 0))); + } + +#ifdef ASM_OUTPUT_ADDR_VEC_END + ASM_OUTPUT_ADDR_VEC_END (asm_out_file); +#endif +} + +static void +sparc_output_addr_diff_vec (vec) + rtx vec; +{ + rtx lab = XEXP (vec, 0), body = XEXP (vec, 1); + rtx base = XEXP (XEXP (body, 0), 0); + int idx, vlen = XVECLEN (body, 1); + +#ifdef ASM_OUTPUT_ADDR_VEC_START + ASM_OUTPUT_ADDR_VEC_START (asm_out_file); +#endif + +#ifdef ASM_OUTPUT_CASE_LABEL + ASM_OUTPUT_CASE_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (lab), + NEXT_INSN (lab)); +#else + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (lab)); +#endif + + for (idx = 0; idx < vlen; idx++) + { + ASM_OUTPUT_ADDR_DIFF_ELT + (asm_out_file, + body, + CODE_LABEL_NUMBER (XEXP (XVECEXP (body, 1, idx), 0)), + CODE_LABEL_NUMBER (base)); + } + +#ifdef ASM_OUTPUT_ADDR_VEC_END + ASM_OUTPUT_ADDR_VEC_END (asm_out_file); +#endif +} + +static void +sparc_output_deferred_case_vectors () +{ + rtx t; + int align; + + if (sparc_addr_list == NULL_RTX + && sparc_addr_diff_list == NULL_RTX) + return; + + /* Align to cache line in the function's code section. */ + function_section (current_function_decl); + + align = floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT); + if (align > 0) + ASM_OUTPUT_ALIGN (asm_out_file, align); + + for (t = sparc_addr_list; t ; t = XEXP (t, 1)) + sparc_output_addr_vec (XEXP (t, 0)); + for (t = sparc_addr_diff_list; t ; t = XEXP (t, 1)) + sparc_output_addr_diff_vec (XEXP (t, 0)); + + sparc_addr_list = sparc_addr_diff_list = NULL_RTX; +} + +/* Return 0 if the high 32 bits of X (the low word of X, if DImode) are + unknown. Return 1 if the high bits are zero, -1 if the register is + sign extended. */ +int +sparc_check_64 (x, insn) + rtx x, insn; +{ + /* If a register is set only once it is safe to ignore insns this + code does not know how to handle. The loop will either recognize + the single set and return the correct value or fail to recognize + it and return 0. */ + int set_once = 0; + + if (GET_CODE (x) == REG + && flag_expensive_optimizations + && REG_N_SETS (REGNO (x)) == 1) + set_once = 1; + + if (insn == 0) + { + if (set_once) + insn = get_last_insn_anywhere (); + else + return 0; + } + + while ((insn = PREV_INSN (insn))) + { + switch (GET_CODE (insn)) + { + case JUMP_INSN: + case NOTE: + break; + case CODE_LABEL: + case CALL_INSN: + default: + if (! set_once) + return 0; + break; + case INSN: + { + rtx pat = PATTERN (insn); + if (GET_CODE (pat) != SET) + return 0; + if (rtx_equal_p (x, SET_DEST (pat))) + return set_extends (x, insn); + if (reg_overlap_mentioned_p (SET_DEST (pat), x)) + return 0; + } + } + } + return 0; +} + +char * +sparc_v8plus_shift (operands, insn, opcode) + rtx *operands; + rtx insn; + char *opcode; +{ + static char asm_code[60]; + + if (GET_CODE (operands[3]) == SCRATCH) + operands[3] = operands[0]; + if (GET_CODE (operands[1]) == CONST_INT) + { + output_asm_insn ("mov %1,%3", operands); + } + else + { + output_asm_insn ("sllx %H1,32,%3", operands); + if (sparc_check_64 (operands[1], insn) <= 0) + output_asm_insn ("srl %L1,0,%L1", operands); + output_asm_insn ("or %L1,%3,%3", operands); + } + + strcpy(asm_code, opcode); + if (which_alternative != 2) + return strcat (asm_code, " %0,%2,%L0\n\tsrlx %L0,32,%H0"); + else + return strcat (asm_code, " %3,%2,%3\n\tsrlx %3,32,%H0\n\tmov %3,%L0"); +} + + +/* Return 1 if DEST and SRC reference only global and in registers. */ + +int +sparc_return_peephole_ok (dest, src) + rtx dest, src; +{ + if (! TARGET_V9) + return 0; + if (leaf_function) + return 0; + if (GET_CODE (src) != CONST_INT + && (GET_CODE (src) != REG || ! IN_OR_GLOBAL_P (src))) + return 0; + return IN_OR_GLOBAL_P (dest); +} diff --git a/gcc/config/sparc/sparc.h b/gcc/config/sparc/sparc.h new file mode 100755 index 0000000..5080b3e --- /dev/null +++ b/gcc/config/sparc/sparc.h @@ -0,0 +1,3504 @@ +/* Definitions of target machine for GNU compiler, for Sun SPARC. + Copyright (C) 1987, 88, 89, 92, 94-98, 1999 Free Software Foundation, Inc. + Contributed by Michael Tiemann (tiemann@cygnus.com). + 64 bit SPARC V9 support by Michael Tiemann, Jim Wilson, and Doug Evans, + at Cygnus Support. + +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. */ + +/* Note that some other tm.h files include this one and then override + whatever definitions are necessary. */ + +/* Specify this in a cover file to provide bi-architecture (32/64) support. */ +/* #define SPARC_BI_ARCH */ + +/* Macro used later in this file to determine default architecture. */ +#define DEFAULT_ARCH32_P ((TARGET_DEFAULT & MASK_64BIT) == 0) + +/* TARGET_ARCH{32,64} are the main macros to decide which of the two + architectures to compile for. We allow targets to choose compile time or + runtime selection. */ +#ifdef SPARC_BI_ARCH +#ifdef IN_LIBGCC2 +#if defined(__sparcv9) || defined(__sparcv_v9) || defined(__arch64__) +#define TARGET_ARCH32 0 +#else +#define TARGET_ARCH32 1 +#endif /* V9 sparc */ +#else +#define TARGET_ARCH32 (! TARGET_64BIT) +#endif /* IN_LIBGCC2 */ +#else +#define TARGET_ARCH32 (DEFAULT_ARCH32_P) +#endif /* SPARC_BI_ARCH */ +#define TARGET_ARCH64 (! TARGET_ARCH32) + +/* Code model selection. + -mcmodel is used to select the v9 code model. + Different code models aren't supported for v8 code. + + TARGET_CM_32: 32 bit address space, top 32 bits = 0, + pointers are 32 bits. Note that this isn't intended + to imply a v8 abi. + + TARGET_CM_MEDLOW: 32 bit address space, top 32 bits = 0, + avoid generating %uhi and %ulo terms, + pointers are 64 bits. + + TARGET_CM_MEDMID: 64 bit address space. + The executable must be in the low 16 TB of memory. + This corresponds to the low 44 bits, and the %[hml]44 + relocs are used. The text segment has a maximum size + of 31 bits. + + TARGET_CM_MEDANY: 64 bit address space. + The text and data segments have a maximum size of 31 + bits and may be located anywhere. The maximum offset + from any instruction to the label _GLOBAL_OFFSET_TABLE_ + is 31 bits. + + TARGET_CM_EMBMEDANY: 64 bit address space. + The text and data segments have a maximum size of 31 bits + and may be located anywhere. Register %g4 contains + the start address of the data segment. +*/ + +enum cmodel { + CM_32, + CM_MEDLOW, + CM_MEDMID, + CM_MEDANY, + CM_EMBMEDANY +}; + +/* Value of -mcmodel specified by user. */ +extern char *sparc_cmodel_string; +/* One of CM_FOO. */ +extern enum cmodel sparc_cmodel; + +/* V9 code model selection. */ +#define TARGET_CM_MEDLOW (sparc_cmodel == CM_MEDLOW) +#define TARGET_CM_MEDMID (sparc_cmodel == CM_MEDMID) +#define TARGET_CM_MEDANY (sparc_cmodel == CM_MEDANY) +#define TARGET_CM_EMBMEDANY (sparc_cmodel == CM_EMBMEDANY) + +#define SPARC_DEFAULT_CMODEL CM_MEDLOW + +/* This is call-clobbered in the normal ABI, but is reserved in the + home grown (aka upward compatible) embedded ABI. */ +#define EMBMEDANY_BASE_REG "%g4" + +/* Values of TARGET_CPU_DEFAULT, set via -D in the Makefile, + and specified by the user via --with-cpu=foo. + This specifies the cpu implementation, not the architecture size. */ +/* Note that TARGET_CPU_v9 is assumed to start the list of 64-bit + capable cpu's. */ +#define TARGET_CPU_sparc 0 +#define TARGET_CPU_v7 0 /* alias for previous */ +#define TARGET_CPU_sparclet 1 +#define TARGET_CPU_sparclite 2 +#define TARGET_CPU_v8 3 /* generic v8 implementation */ +#define TARGET_CPU_supersparc 4 +#define TARGET_CPU_hypersparc 5 +#define TARGET_CPU_sparc86x 6 +#define TARGET_CPU_sparclite86x 6 +#define TARGET_CPU_v9 7 /* generic v9 implementation */ +#define TARGET_CPU_sparcv9 7 /* alias */ +#define TARGET_CPU_sparc64 7 /* alias */ +#define TARGET_CPU_ultrasparc 8 + +#if TARGET_CPU_DEFAULT == TARGET_CPU_v9 \ + || TARGET_CPU_DEFAULT == TARGET_CPU_ultrasparc + +#define CPP_CPU32_DEFAULT_SPEC "" +#define ASM_CPU32_DEFAULT_SPEC "" + +#if TARGET_CPU_DEFAULT == TARGET_CPU_v9 +/* ??? What does Sun's CC pass? */ +#define CPP_CPU64_DEFAULT_SPEC "-D__sparc_v9__" +/* ??? It's not clear how other assemblers will handle this, so by default + use GAS. Sun's Solaris assembler recognizes -xarch=v8plus, but this case + is handled in sol2.h. */ +#define ASM_CPU64_DEFAULT_SPEC "-Av9" +#endif +#if TARGET_CPU_DEFAULT == TARGET_CPU_ultrasparc +#define CPP_CPU64_DEFAULT_SPEC "-D__sparc_v9__" +#define ASM_CPU64_DEFAULT_SPEC "-Av9a" +#endif + +#else + +#define CPP_CPU64_DEFAULT_SPEC "" +#define ASM_CPU64_DEFAULT_SPEC "" + +#if TARGET_CPU_DEFAULT == TARGET_CPU_sparc \ + || TARGET_CPU_DEFAULT == TARGET_CPU_v8 +#define CPP_CPU32_DEFAULT_SPEC "" +#define ASM_CPU32_DEFAULT_SPEC "" +#endif + +#if TARGET_CPU_DEFAULT == TARGET_CPU_sparclet +#define CPP_CPU32_DEFAULT_SPEC "-D__sparclet__" +#define ASM_CPU32_DEFAULT_SPEC "-Asparclet" +#endif + +#if TARGET_CPU_DEFAULT == TARGET_CPU_sparclite +#define CPP_CPU32_DEFAULT_SPEC "-D__sparclite__" +#define ASM_CPU32_DEFAULT_SPEC "-Asparclite" +#endif + +#if TARGET_CPU_DEFAULT == TARGET_CPU_supersparc +#define CPP_CPU32_DEFAULT_SPEC "-D__supersparc__ -D__sparc_v8__" +#define ASM_CPU32_DEFAULT_SPEC "" +#endif + +#if TARGET_CPU_DEFAULT == TARGET_CPU_hypersparc +#define CPP_CPU32_DEFAULT_SPEC "-D__hypersparc__ -D__sparc_v8__" +#define ASM_CPU32_DEFAULT_SPEC "" +#endif + +#if TARGET_CPU_DEFAULT == TARGET_CPU_sparclite86x +#define CPP_CPU32_DEFAULT_SPEC "-D__sparclite86x__" +#define ASM_CPU32_DEFAULT_SPEC "-Asparclite" +#endif + +#endif + +#if !defined(CPP_CPU32_DEFAULT_SPEC) || !defined(CPP_CPU64_DEFAULT_SPEC) +Unrecognized value in TARGET_CPU_DEFAULT. +#endif + +#ifdef SPARC_BI_ARCH + +#define CPP_CPU_DEFAULT_SPEC \ +(DEFAULT_ARCH32_P ? "\ +%{m64:" CPP_CPU64_DEFAULT_SPEC "} \ +%{!m64:" CPP_CPU32_DEFAULT_SPEC "} \ +" : "\ +%{m32:" CPP_CPU32_DEFAULT_SPEC "} \ +%{!m32:" CPP_CPU64_DEFAULT_SPEC "} \ +") +#define ASM_CPU_DEFAULT_SPEC \ +(DEFAULT_ARCH32_P ? "\ +%{m64:" ASM_CPU64_DEFAULT_SPEC "} \ +%{!m64:" ASM_CPU32_DEFAULT_SPEC "} \ +" : "\ +%{m32:" ASM_CPU32_DEFAULT_SPEC "} \ +%{!m32:" ASM_CPU64_DEFAULT_SPEC "} \ +") + +#else /* !SPARC_BI_ARCH */ + +#define CPP_CPU_DEFAULT_SPEC (DEFAULT_ARCH32_P ? CPP_CPU32_DEFAULT_SPEC : CPP_CPU64_DEFAULT_SPEC) +#define ASM_CPU_DEFAULT_SPEC (DEFAULT_ARCH32_P ? ASM_CPU32_DEFAULT_SPEC : ASM_CPU64_DEFAULT_SPEC) + +#endif /* !SPARC_BI_ARCH */ + +/* Names to predefine in the preprocessor for this target machine. + ??? It would be nice to not include any subtarget specific values here, + however there's no way to portably provide subtarget values to + CPP_PREFINES. Also, -D values in CPP_SUBTARGET_SPEC don't get turned into + foo, __foo and __foo__. */ + +#define CPP_PREDEFINES "-Dsparc -Dsun -Dunix -Asystem(unix) -Asystem(bsd)" + +/* Define macros to distinguish architectures. */ + +/* Common CPP definitions used by CPP_SPEC amongst the various targets + for handling -mcpu=xxx switches. */ +#define CPP_CPU_SPEC "\ +%{mcypress:} \ +%{msparclite:-D__sparclite__} \ +%{mf930:-D__sparclite__} %{mf934:-D__sparclite__} \ +%{mv8:-D__sparc_v8__} \ +%{msupersparc:-D__supersparc__ -D__sparc_v8__} \ +%{mcpu=sparclet:-D__sparclet__} %{mcpu=tsc701:-D__sparclet__} \ +%{mcpu=sparclite:-D__sparclite__} \ +%{mcpu=f930:-D__sparclite__} %{mcpu=f934:-D__sparclite__} \ +%{mcpu=v8:-D__sparc_v8__} \ +%{mcpu=supersparc:-D__supersparc__ -D__sparc_v8__} \ +%{mcpu=hypersparc:-D__hypersparc__ -D__sparc_v8__} \ +%{mcpu=sparclite86x:-D__sparclite86x__} \ +%{mcpu=v9:-D__sparc_v9__} \ +%{mcpu=ultrasparc:-D__sparc_v9__} \ +%{!mcpu*:%{!mcypress:%{!msparclite:%{!mf930:%{!mf934:%{!mv8:%{!msupersparc:%(cpp_cpu_default)}}}}}}} \ +" + +/* ??? The GCC_NEW_VARARGS macro is now obsolete, because gcc always uses + the right varags.h file when bootstrapping. */ +/* ??? It's not clear what value we want to use for -Acpu/machine for + sparc64 in 32 bit environments, so for now we only use `sparc64' in + 64 bit environments. */ + +#ifdef SPARC_BI_ARCH + +#define CPP_ARCH32_SPEC "-D__SIZE_TYPE__=unsigned\\ int -D__PTRDIFF_TYPE__=int \ +-D__GCC_NEW_VARARGS__ -Acpu(sparc) -Amachine(sparc)" +#define CPP_ARCH64_SPEC "-D__SIZE_TYPE__=long\\ unsigned\\ int -D__PTRDIFF_TYPE__=long\\ int \ +-D__arch64__ -Acpu(sparc64) -Amachine(sparc64)" + +#else + +#define CPP_ARCH32_SPEC "-D__GCC_NEW_VARARGS__ -Acpu(sparc) -Amachine(sparc)" +#define CPP_ARCH64_SPEC "-D__arch64__ -Acpu(sparc64) -Amachine(sparc64)" + +#endif + +#define CPP_ARCH_DEFAULT_SPEC \ +(DEFAULT_ARCH32_P ? CPP_ARCH32_SPEC : CPP_ARCH64_SPEC) + +#define CPP_ARCH_SPEC "\ +%{m32:%(cpp_arch32)} \ +%{m64:%(cpp_arch64)} \ +%{!m32:%{!m64:%(cpp_arch_default)}} \ +" + +/* Macros to distinguish endianness. */ +#define CPP_ENDIAN_SPEC "\ +%{mlittle-endian:-D__LITTLE_ENDIAN__} \ +%{mlittle-endian-data:-D__LITTLE_ENDIAN_DATA__}" + +/* Macros to distinguish the particular subtarget. */ +#define CPP_SUBTARGET_SPEC "" + +#define CPP_SPEC "%(cpp_cpu) %(cpp_arch) %(cpp_endian) %(cpp_subtarget)" + +/* Prevent error on `-sun4' and `-target sun4' options. */ +/* This used to translate -dalign to -malign, but that is no good + because it can't turn off the usual meaning of making debugging dumps. */ +/* Translate old style -m<cpu> into new style -mcpu=<cpu>. + ??? Delete support for -m<cpu> for 2.9. */ + +#define CC1_SPEC "\ +%{sun4:} %{target:} \ +%{mcypress:-mcpu=cypress} \ +%{msparclite:-mcpu=sparclite} %{mf930:-mcpu=f930} %{mf934:-mcpu=f934} \ +%{mv8:-mcpu=v8} %{msupersparc:-mcpu=supersparc} \ +" + +/* Override in target specific files. */ +#define ASM_CPU_SPEC "\ +%{mcpu=sparclet:-Asparclet} %{mcpu=tsc701:-Asparclet} \ +%{msparclite:-Asparclite} \ +%{mf930:-Asparclite} %{mf934:-Asparclite} \ +%{mcpu=sparclite:-Asparclite} \ +%{mcpu=sparclite86x:-Asparclite} \ +%{mcpu=f930:-Asparclite} %{mcpu=f934:-Asparclite} \ +%{mv8plus:-Av8plus} \ +%{mcpu=v9:-Av9} \ +%{mcpu=ultrasparc:%{!mv8plus:-Av9a}} \ +%{!mcpu*:%{!mcypress:%{!msparclite:%{!mf930:%{!mf934:%{!mv8:%{!msupersparc:%(asm_cpu_default)}}}}}}} \ +" + +/* Word size selection, among other things. + This is what GAS uses. Add %(asm_arch) to ASM_SPEC to enable. */ + +#define ASM_ARCH32_SPEC "-32" +#define ASM_ARCH64_SPEC "-64" +#define ASM_ARCH_DEFAULT_SPEC \ +(DEFAULT_ARCH32_P ? ASM_ARCH32_SPEC : ASM_ARCH64_SPEC) + +#define ASM_ARCH_SPEC "\ +%{m32:%(asm_arch32)} \ +%{m64:%(asm_arch64)} \ +%{!m32:%{!m64:%(asm_arch_default)}} \ +" + +/* Special flags to the Sun-4 assembler when using pipe for input. */ + +#define ASM_SPEC "\ +%| %{R} %{!pg:%{!p:%{fpic:-k} %{fPIC:-k}}} %{keep-local-as-symbols:-L} \ +%(asm_cpu) \ +" + +#define LIB_SPEC "%{!shared:%{!p:%{!pg:-lc}}%{p:-lc_p}%{pg:-lc_p} %{g:-lg}}" + +/* Provide required defaults for linker -e and -d switches. */ + +#define LINK_SPEC \ + "%{!shared:%{!nostdlib:%{!r*:%{!e*:-e start}}} -dc -dp} %{static:-Bstatic} \ + %{assert*} %{shared:%{!mimpure-text:-assert pure-text}}" + +/* This macro defines names of additional specifications to put in the specs + that can be used in various specifications like CC1_SPEC. Its definition + is an initializer with a subgrouping for each command option. + + Each subgrouping contains a string constant, that defines the + specification name, and a string constant that used by the GNU CC driver + program. + + Do not define this macro if it does not need to do anything. */ + +#define EXTRA_SPECS \ + { "cpp_cpu", CPP_CPU_SPEC }, \ + { "cpp_cpu_default", CPP_CPU_DEFAULT_SPEC }, \ + { "cpp_arch32", CPP_ARCH32_SPEC }, \ + { "cpp_arch64", CPP_ARCH64_SPEC }, \ + { "cpp_arch_default", CPP_ARCH_DEFAULT_SPEC },\ + { "cpp_arch", CPP_ARCH_SPEC }, \ + { "cpp_endian", CPP_ENDIAN_SPEC }, \ + { "cpp_subtarget", CPP_SUBTARGET_SPEC }, \ + { "asm_cpu", ASM_CPU_SPEC }, \ + { "asm_cpu_default", ASM_CPU_DEFAULT_SPEC }, \ + { "asm_arch32", ASM_ARCH32_SPEC }, \ + { "asm_arch64", ASM_ARCH64_SPEC }, \ + { "asm_arch_default", ASM_ARCH_DEFAULT_SPEC },\ + { "asm_arch", ASM_ARCH_SPEC }, \ + SUBTARGET_EXTRA_SPECS + +#define SUBTARGET_EXTRA_SPECS + +#ifdef SPARC_BI_ARCH +#define NO_BUILTIN_PTRDIFF_TYPE +#define NO_BUILTIN_SIZE_TYPE +#endif +#define PTRDIFF_TYPE (TARGET_ARCH64 ? "long int" : "int") +#define SIZE_TYPE (TARGET_ARCH64 ? "long unsigned int" : "unsigned int") + +/* ??? This should be 32 bits for v9 but what can we do? */ +#define WCHAR_TYPE "short unsigned int" +#define WCHAR_TYPE_SIZE 16 +#define MAX_WCHAR_TYPE_SIZE 16 + +/* Show we can debug even without a frame pointer. */ +#define CAN_DEBUG_WITHOUT_FP + +/* To make profiling work with -f{pic,PIC}, we need to emit the profiling + code into the rtl. Also, if we are profiling, we cannot eliminate + the frame pointer (because the return address will get smashed). */ + +void sparc_override_options (); + +#define OVERRIDE_OPTIONS \ + do { \ + if (profile_flag || profile_block_flag || profile_arc_flag) \ + { \ + if (flag_pic) \ + { \ + char *pic_string = (flag_pic == 1) ? "-fpic" : "-fPIC"; \ + warning ("%s and profiling conflict: disabling %s", \ + pic_string, pic_string); \ + flag_pic = 0; \ + } \ + flag_omit_frame_pointer = 0; \ + } \ + sparc_override_options (); \ + SUBTARGET_OVERRIDE_OPTIONS; \ + } while (0) + +/* This is meant to be redefined in the host dependent files. */ +#define SUBTARGET_OVERRIDE_OPTIONS + +/* These compiler options take an argument. We ignore -target for now. */ + +#define WORD_SWITCH_TAKES_ARG(STR) \ + (DEFAULT_WORD_SWITCH_TAKES_ARG (STR) \ + || !strcmp (STR, "target") || !strcmp (STR, "assert")) + +/* Print subsidiary information on the compiler version in use. */ + +#define TARGET_VERSION fprintf (stderr, " (sparc)"); + +/* Generate DBX debugging information. */ + +#define DBX_DEBUGGING_INFO + +/* Run-time compilation parameters selecting different hardware subsets. */ + +extern int target_flags; + +/* Nonzero if we should generate code to use the fpu. */ +#define MASK_FPU 1 +#define TARGET_FPU (target_flags & MASK_FPU) + +/* Nonzero if we should use FUNCTION_EPILOGUE. Otherwise, we + use fast return insns, but lose some generality. */ +#define MASK_EPILOGUE 2 +#define TARGET_EPILOGUE (target_flags & MASK_EPILOGUE) + +/* Nonzero if we should assume that double pointers might be unaligned. + This can happen when linking gcc compiled code with other compilers, + because the ABI only guarantees 4 byte alignment. */ +#define MASK_UNALIGNED_DOUBLES 4 +#define TARGET_UNALIGNED_DOUBLES (target_flags & MASK_UNALIGNED_DOUBLES) + +/* Nonzero means that we should generate code for a v8 sparc. */ +#define MASK_V8 0x8 +#define TARGET_V8 (target_flags & MASK_V8) + +/* Nonzero means that we should generate code for a sparclite. + This enables the sparclite specific instructions, but does not affect + whether FPU instructions are emitted. */ +#define MASK_SPARCLITE 0x10 +#define TARGET_SPARCLITE (target_flags & MASK_SPARCLITE) + +/* Nonzero if we're compiling for the sparclet. */ +#define MASK_SPARCLET 0x20 +#define TARGET_SPARCLET (target_flags & MASK_SPARCLET) + +/* Nonzero if we're compiling for v9 sparc. + Note that v9's can run in 32 bit mode so this doesn't necessarily mean + the word size is 64. */ +#define MASK_V9 0x40 +#define TARGET_V9 (target_flags & MASK_V9) + +/* Non-zero to generate code that uses the instructions deprecated in + the v9 architecture. This option only applies to v9 systems. */ +/* ??? This isn't user selectable yet. It's used to enable such insns + on 32 bit v9 systems and for the moment they're permanently disabled + on 64 bit v9 systems. */ +#define MASK_DEPRECATED_V8_INSNS 0x80 +#define TARGET_DEPRECATED_V8_INSNS (target_flags & MASK_DEPRECATED_V8_INSNS) + +/* Mask of all CPU selection flags. */ +#define MASK_ISA \ +(MASK_V8 + MASK_SPARCLITE + MASK_SPARCLET + MASK_V9 + MASK_DEPRECATED_V8_INSNS) + +/* Non-zero means don't pass `-assert pure-text' to the linker. */ +#define MASK_IMPURE_TEXT 0x100 +#define TARGET_IMPURE_TEXT (target_flags & MASK_IMPURE_TEXT) + +/* Nonzero means that we should generate code using a flat register window + model, i.e. no save/restore instructions are generated, which is + compatible with normal sparc code. + The frame pointer is %i7 instead of %fp. */ +#define MASK_FLAT 0x200 +#define TARGET_FLAT (target_flags & MASK_FLAT) + +/* Nonzero means use the registers that the Sparc ABI reserves for + application software. This must be the default to coincide with the + setting in FIXED_REGISTERS. */ +#define MASK_APP_REGS 0x400 +#define TARGET_APP_REGS (target_flags & MASK_APP_REGS) + +/* Option to select how quad word floating point is implemented. + When TARGET_HARD_QUAD is true, we use the hardware quad instructions. + Otherwise, we use the SPARC ABI quad library functions. */ +#define MASK_HARD_QUAD 0x800 +#define TARGET_HARD_QUAD (target_flags & MASK_HARD_QUAD) + +/* Non-zero on little-endian machines. */ +/* ??? Little endian support currently only exists for sparclet-aout and + sparc64-elf configurations. May eventually want to expand the support + to all targets, but for now it's kept local to only those two. */ +#define MASK_LITTLE_ENDIAN 0x1000 +#define TARGET_LITTLE_ENDIAN (target_flags & MASK_LITTLE_ENDIAN) + +/* 0x2000, 0x4000 are unused */ + +/* Nonzero if pointers are 64 bits. + At the moment it must follow architecture size flag. */ +#define MASK_PTR64 0x8000 +#define TARGET_PTR64 (target_flags & MASK_PTR64) + +/* Nonzero if generating code to run in a 64 bit environment. + This is intended to only be used by TARGET_ARCH{32,64} as they are the + mechanism used to control compile time or run time selection. */ +#define MASK_64BIT 0x10000 +#define TARGET_64BIT (target_flags & MASK_64BIT) + +/* 0x20000,0x40000 unused */ + +/* Non-zero means use a stack bias of 2047. Stack offsets are obtained by + adding 2047 to %sp. This option is for v9 only and is the default. */ +#define MASK_STACK_BIAS 0x80000 +#define TARGET_STACK_BIAS (target_flags & MASK_STACK_BIAS) + +/* Non-zero means %g0 is a normal register. + We still clobber it as necessary, but we can't rely on it always having + a zero value. + We don't bother to support this in true 64 bit mode. */ +#define MASK_LIVE_G0 0x100000 +#define TARGET_LIVE_G0 (target_flags & MASK_LIVE_G0) + +/* Non-zero means the cpu has broken `save' and `restore' insns, only + the trivial versions work (save %g0,%g0,%g0; restore %g0,%g0,%g0). + We assume the environment will properly handle or otherwise avoid + trouble associated with an interrupt occurring after the `save' or trap + occurring during it. */ +#define MASK_BROKEN_SAVERESTORE 0x200000 +#define TARGET_BROKEN_SAVERESTORE (target_flags & MASK_BROKEN_SAVERESTORE) + +/* Non-zero means -m{,no-}fpu was passed on the command line. */ +#define MASK_FPU_SET 0x400000 +#define TARGET_FPU_SET (target_flags & MASK_FPU_SET) + +/* Use the UltraSPARC Visual Instruction Set extensions. */ +#define MASK_VIS 0x1000000 +#define TARGET_VIS (target_flags & MASK_VIS) + +/* Compile for Solaris V8+. 32 bit Solaris preserves the high bits of + the current out and global registers. Linux saves the high bits on + context switches but not signals. */ +#define MASK_V8PLUS 0x2000000 +#define TARGET_V8PLUS (target_flags & MASK_V8PLUS) + +/* TARGET_HARD_MUL: Use hardware multiply instructions but not %y. + TARGET_HARD_MUL32: Use hardware multiply instructions with rd %y + to get high 32 bits. False in V8+ or V9 because multiply stores + a 64 bit result in a register. */ + +#define TARGET_HARD_MUL32 \ + ((TARGET_V8 || TARGET_SPARCLITE \ + || TARGET_SPARCLET || TARGET_DEPRECATED_V8_INSNS) \ + && ! TARGET_V8PLUS) + +#define TARGET_HARD_MUL \ + (TARGET_V8 || TARGET_SPARCLITE || TARGET_SPARCLET \ + || TARGET_DEPRECATED_V8_INSNS || TARGET_V8PLUS) + + +/* 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 \ + { {"fpu", MASK_FPU | MASK_FPU_SET}, \ + {"no-fpu", -MASK_FPU}, \ + {"no-fpu", MASK_FPU_SET}, \ + {"hard-float", MASK_FPU | MASK_FPU_SET}, \ + {"soft-float", -MASK_FPU}, \ + {"soft-float", MASK_FPU_SET}, \ + {"epilogue", MASK_EPILOGUE}, \ + {"no-epilogue", -MASK_EPILOGUE}, \ + {"unaligned-doubles", MASK_UNALIGNED_DOUBLES}, \ + {"no-unaligned-doubles", -MASK_UNALIGNED_DOUBLES}, \ + {"impure-text", MASK_IMPURE_TEXT}, \ + {"no-impure-text", -MASK_IMPURE_TEXT}, \ + {"flat", MASK_FLAT}, \ + {"no-flat", -MASK_FLAT}, \ + {"app-regs", MASK_APP_REGS}, \ + {"no-app-regs", -MASK_APP_REGS}, \ + {"hard-quad-float", MASK_HARD_QUAD}, \ + {"soft-quad-float", -MASK_HARD_QUAD}, \ + {"v8plus", MASK_V8PLUS}, \ + {"no-v8plus", -MASK_V8PLUS}, \ + {"vis", MASK_VIS}, \ + {"no-vis", -MASK_VIS}, \ + /* ??? These are deprecated, coerced to -mcpu=. Delete in 2.9. */ \ + {"cypress", 0}, \ + {"sparclite", 0}, \ + {"f930", 0}, \ + {"f934", 0}, \ + {"v8", 0}, \ + {"supersparc", 0}, \ + /* End of deprecated options. */ \ + {"ptr64", MASK_PTR64}, \ + {"ptr32", -MASK_PTR64}, \ + {"32", -MASK_64BIT}, \ + {"64", MASK_64BIT}, \ + {"stack-bias", MASK_STACK_BIAS}, \ + {"no-stack-bias", -MASK_STACK_BIAS}, \ + SUBTARGET_SWITCHES \ + { "", TARGET_DEFAULT}} + +/* MASK_APP_REGS must always be the default because that's what + FIXED_REGISTERS is set to and -ffixed- is processed before + CONDITIONAL_REGISTER_USAGE is called (where we process -mno-app-regs). */ +#define TARGET_DEFAULT (MASK_APP_REGS + MASK_EPILOGUE + MASK_FPU) + +/* This is meant to be redefined in target specific files. */ +#define SUBTARGET_SWITCHES + +/* Processor type. + These must match the values for the cpu attribute in sparc.md. */ +enum processor_type { + PROCESSOR_V7, + PROCESSOR_CYPRESS, + PROCESSOR_V8, + PROCESSOR_SUPERSPARC, + PROCESSOR_SPARCLITE, + PROCESSOR_F930, + PROCESSOR_F934, + PROCESSOR_HYPERSPARC, + PROCESSOR_SPARCLITE86X, + PROCESSOR_SPARCLET, + PROCESSOR_TSC701, + PROCESSOR_V9, + PROCESSOR_ULTRASPARC +}; + +/* This is set from -m{cpu,tune}=xxx. */ +extern enum processor_type sparc_cpu; + +/* Recast the cpu class to be the cpu attribute. + Every file includes us, but not every file includes insn-attr.h. */ +#define sparc_cpu_attr ((enum attr_cpu) sparc_cpu) + +/* This macro is similar to `TARGET_SWITCHES' but defines names of + command options that have values. Its definition is an + initializer with a subgrouping for each command option. + + Each subgrouping contains a string constant, that defines the + fixed part of the option name, and the address of a variable. + The variable, type `char *', is set to the variable part of the + given option if the fixed part matches. The actual option name + is made by appending `-m' to the specified name. + + Here is an example which defines `-mshort-data-NUMBER'. If the + given option is `-mshort-data-512', the variable `m88k_short_data' + will be set to the string `"512"'. + + extern char *m88k_short_data; + #define TARGET_OPTIONS { { "short-data-", &m88k_short_data } } */ + +#define TARGET_OPTIONS \ +{ \ + { "cpu=", &sparc_select[1].string }, \ + { "tune=", &sparc_select[2].string }, \ + { "cmodel=", &sparc_cmodel_string }, \ + { "align-loops=", &sparc_align_loops_string }, \ + { "align-jumps=", &sparc_align_jumps_string }, \ + { "align-functions=", &sparc_align_funcs_string }, \ + SUBTARGET_OPTIONS \ +} + +/* This is meant to be redefined in target specific files. */ +#define SUBTARGET_OPTIONS + +/* sparc_select[0] is reserved for the default cpu. */ +struct sparc_cpu_select +{ + char *string; + char *name; + int set_tune_p; + int set_arch_p; +}; + +extern struct sparc_cpu_select sparc_select[]; + +/* Variables to record values the user passes. */ +extern char *sparc_align_loops_string; +extern char *sparc_align_jumps_string; +extern char *sparc_align_funcs_string; +/* Parsed values as a power of two. */ +extern int sparc_align_loops; +extern int sparc_align_jumps; +extern int sparc_align_funcs; + +#define DEFAULT_SPARC_ALIGN_FUNCS \ +(sparc_cpu == PROCESSOR_ULTRASPARC ? 5 : 2) + +/* target machine storage layout */ + +/* Define for cross-compilation to a sparc target with no TFmode from a host + with a different float format (e.g. VAX). */ +#define REAL_ARITHMETIC + +/* 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. */ +#define BYTES_BIG_ENDIAN 1 + +/* Define this if most significant word of a multiword number is the lowest + numbered. */ +#define WORDS_BIG_ENDIAN 1 + +/* Define this to set the endianness to use in libgcc2.c, which can + not depend on target_flags. */ +#if defined (__LITTLE_ENDIAN__) || defined(__LITTLE_ENDIAN_DATA__) +#define LIBGCC2_WORDS_BIG_ENDIAN 0 +#else +#define LIBGCC2_WORDS_BIG_ENDIAN 1 +#endif + +/* 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 (TARGET_ARCH64 ? 64 : 32) +#define MAX_BITS_PER_WORD 64 + +/* Width of a word, in units (bytes). */ +#define UNITS_PER_WORD (TARGET_ARCH64 ? 8 : 4) +#define MIN_UNITS_PER_WORD 4 + +/* Now define the sizes of the C data types. */ + +#define SHORT_TYPE_SIZE 16 +#define INT_TYPE_SIZE 32 +#define LONG_TYPE_SIZE (TARGET_ARCH64 ? 64 : 32) +#define LONG_LONG_TYPE_SIZE 64 +#define FLOAT_TYPE_SIZE 32 +#define DOUBLE_TYPE_SIZE 64 + +#if defined (SPARC_BI_ARCH) +#define MAX_LONG_TYPE_SIZE 64 +#endif + +#if 0 +/* ??? This does not work in SunOS 4.x, so it is not enabled here. + Instead, it is enabled in sol2.h, because it does work under Solaris. */ +/* Define for support of TFmode long double and REAL_ARITHMETIC. + Sparc ABI says that long double is 4 words. */ +#define LONG_DOUBLE_TYPE_SIZE 128 +#endif + +/* Width in bits of a pointer. + See also the macro `Pmode' defined below. */ +#define POINTER_SIZE (TARGET_PTR64 ? 64 : 32) + +/* A macro to update MODE and UNSIGNEDP when an object whose type + is TYPE and which has the specified mode and signedness is to be + stored in a register. This macro is only called when TYPE is a + scalar type. */ +#define PROMOTE_MODE(MODE, UNSIGNEDP, TYPE) \ +if (TARGET_ARCH64 \ + && GET_MODE_CLASS (MODE) == MODE_INT \ + && GET_MODE_SIZE (MODE) < UNITS_PER_WORD) \ +{ \ + (MODE) = DImode; \ +} + +/* Define this macro if the promotion described by PROMOTE_MODE + should also be done for outgoing function arguments. */ +/* This is only needed for TARGET_ARCH64, but since PROMOTE_MODE is a no-op + for TARGET_ARCH32 this is ok. Otherwise we'd need to add a runtime test + for this value. */ +#define PROMOTE_FUNCTION_ARGS + +/* Define this macro if the promotion described by PROMOTE_MODE + should also be done for the return value of functions. + If this macro is defined, FUNCTION_VALUE must perform the same + promotions done by PROMOTE_MODE. */ +/* This is only needed for TARGET_ARCH64, but since PROMOTE_MODE is a no-op + for TARGET_ARCH32 this is ok. Otherwise we'd need to add a runtime test + for this value. */ +#define PROMOTE_FUNCTION_RETURN + +/* Allocation boundary (in *bits*) for storing arguments in argument list. */ +#define PARM_BOUNDARY (TARGET_ARCH64 ? 64 : 32) + +/* Boundary (in *bits*) on which stack pointer should be aligned. */ +#define STACK_BOUNDARY (TARGET_ARCH64 ? 128 : 64) + +/* ALIGN FRAMES on double word boundaries */ + +#define SPARC_STACK_ALIGN(LOC) \ + (TARGET_ARCH64 ? (((LOC)+15) & ~15) : (((LOC)+7) & ~7)) + +/* Allocation boundary (in *bits*) for the code of a function. */ +#define FUNCTION_BOUNDARY (1 << (sparc_align_funcs + 3)) + +/* Alignment of field after `int : 0' in a structure. */ +#define EMPTY_FIELD_BOUNDARY (TARGET_ARCH64 ? 64 : 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 (TARGET_ARCH64 ? 128 : 64) + +/* The best alignment to use in cases where we have a choice. */ +#define FASTEST_ALIGNMENT 64 + +/* Make strings word-aligned so strcpy from constants will be faster. */ +#define CONSTANT_ALIGNMENT(EXP, ALIGN) \ + ((TREE_CODE (EXP) == STRING_CST \ + && (ALIGN) < FASTEST_ALIGNMENT) \ + ? FASTEST_ALIGNMENT : (ALIGN)) + +/* 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) < FASTEST_ALIGNMENT ? FASTEST_ALIGNMENT : (ALIGN)) + +/* Set this nonzero if move instructions will actually fail to work + when given unaligned data. */ +#define STRICT_ALIGNMENT 1 + +/* Things that must be doubleword aligned cannot go in the text section, + because the linker fails to align the text section enough! + Put them in the data section. This macro is only used in this file. */ +#define MAX_TEXT_ALIGN 32 + +/* This forces all variables and constants to the data section when PIC. + This is because the SunOS 4 shared library scheme thinks everything in + text is a function, and patches the address to point to a loader stub. */ +/* This is defined to zero for every system which doesn't use the a.out object + file format. */ +#ifndef SUNOS4_SHARED_LIBRARIES +#define SUNOS4_SHARED_LIBRARIES 0 +#endif + +/* This is defined differently for v9 in a cover file. */ +#define SELECT_SECTION(T,RELOC) \ +{ \ + if (TREE_CODE (T) == VAR_DECL) \ + { \ + if (TREE_READONLY (T) && ! TREE_SIDE_EFFECTS (T) \ + && DECL_INITIAL (T) \ + && (DECL_INITIAL (T) == error_mark_node \ + || TREE_CONSTANT (DECL_INITIAL (T))) \ + && DECL_ALIGN (T) <= MAX_TEXT_ALIGN \ + && ! (flag_pic && ((RELOC) || SUNOS4_SHARED_LIBRARIES))) \ + text_section (); \ + else \ + data_section (); \ + } \ + else if (TREE_CODE (T) == CONSTRUCTOR) \ + { \ + if (flag_pic && ((RELOC) || SUNOS4_SHARED_LIBRARIES)) \ + data_section (); \ + } \ + else if (TREE_CODE_CLASS (TREE_CODE (T)) == 'c') \ + { \ + if ((TREE_CODE (T) == STRING_CST && flag_writable_strings) \ + || TYPE_ALIGN (TREE_TYPE (T)) > MAX_TEXT_ALIGN \ + || (flag_pic && ((RELOC) || SUNOS4_SHARED_LIBRARIES))) \ + data_section (); \ + else \ + text_section (); \ + } \ +} + +/* Use text section for a constant + unless we need more alignment than that offers. */ +/* This is defined differently for v9 in a cover file. */ +#define SELECT_RTX_SECTION(MODE, X) \ +{ \ + if (GET_MODE_BITSIZE (MODE) <= MAX_TEXT_ALIGN \ + && ! (flag_pic && (symbolic_operand (X) || SUNOS4_SHARED_LIBRARIES))) \ + text_section (); \ + else \ + data_section (); \ +} + +/* 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. + + SPARC has 32 integer registers and 32 floating point registers. + 64 bit SPARC has 32 additional fp regs, but the odd numbered ones are not + accessible. We still account for them to simplify register computations + (eg: in CLASS_MAX_NREGS). There are also 4 fp condition code registers, so + 32+32+32+4 == 100. + Register 100 is used as the integer condition code register. */ + +#define FIRST_PSEUDO_REGISTER 101 + +#define SPARC_FIRST_FP_REG 32 +/* Additional V9 fp regs. */ +#define SPARC_FIRST_V9_FP_REG 64 +#define SPARC_LAST_V9_FP_REG 95 +/* V9 %fcc[0123]. V8 uses (figuratively) %fcc0. */ +#define SPARC_FIRST_V9_FCC_REG 96 +#define SPARC_LAST_V9_FCC_REG 99 +/* V8 fcc reg. */ +#define SPARC_FCC_REG 96 +/* Integer CC reg. We don't distinguish %icc from %xcc. */ +#define SPARC_ICC_REG 100 + +/* Nonzero if REGNO is an fp reg. */ +#define SPARC_FP_REG_P(REGNO) \ +((REGNO) >= SPARC_FIRST_FP_REG && (REGNO) <= SPARC_LAST_V9_FP_REG) + +/* Argument passing regs. */ +#define SPARC_OUTGOING_INT_ARG_FIRST 8 +#define SPARC_INCOMING_INT_ARG_FIRST (TARGET_FLAT ? 8 : 24) +#define SPARC_FP_ARG_FIRST 32 + +/* 1 for registers that have pervasive standard uses + and are not available for the register allocator. + + On non-v9 systems: + g1 is free to use as temporary. + g2-g4 are reserved for applications. Gcc normally uses them as + temporaries, but this can be disabled via the -mno-app-regs option. + g5 through g7 are reserved for the operating system. + + On v9 systems: + g1,g5 are free to use as temporaries, and are free to use between calls + if the call is to an external function via the PLT. + g4 is free to use as a temporary in the non-embedded case. + g4 is reserved in the embedded case. + g2-g3 are reserved for applications. Gcc normally uses them as + temporaries, but this can be disabled via the -mno-app-regs option. + g6-g7 are reserved for the operating system (or application in + embedded case). + ??? Register 1 is used as a temporary by the 64 bit sethi pattern, so must + currently be a fixed register until this pattern is rewritten. + Register 1 is also used when restoring call-preserved registers in large + stack frames. + + Registers fixed in arch32 and not arch64 (or vice-versa) are marked in + CONDITIONAL_REGISTER_USAGE in order to properly handle -ffixed-. +*/ + +#define FIXED_REGISTERS \ + {1, 0, 0, 0, 0, 0, 1, 1, \ + 0, 0, 0, 0, 0, 0, 1, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 1, 1, \ + \ + 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, 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, 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, 1, 1, \ + \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + \ + 1, 1, 1, 1, 1} + +/* If !TARGET_FPU, then make the fp registers and fp cc regs fixed so that + they won't be allocated. */ + +#define CONDITIONAL_REGISTER_USAGE \ +do \ + { \ + if (flag_pic) \ + { \ + fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1; \ + call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1; \ + } \ + if (TARGET_ARCH32) \ + { \ + fixed_regs[5] = 1; \ + } \ + if (TARGET_LIVE_G0) \ + fixed_regs[0] = 0; \ + if (! TARGET_V9) \ + { \ + int regno; \ + for (regno = SPARC_FIRST_V9_FP_REG; \ + regno <= SPARC_LAST_V9_FP_REG; \ + regno++) \ + fixed_regs[regno] = 1; \ + /* %fcc0 is used by v8 and v9. */ \ + for (regno = SPARC_FIRST_V9_FCC_REG + 1; \ + regno <= SPARC_LAST_V9_FCC_REG; \ + regno++) \ + fixed_regs[regno] = 1; \ + } \ + if (! TARGET_FPU) \ + { \ + int regno; \ + for (regno = 32; regno < SPARC_LAST_V9_FCC_REG; regno++) \ + fixed_regs[regno] = 1; \ + } \ + /* Don't unfix g2-g4 if they were fixed with -ffixed-. */ \ + fixed_regs[2] |= ! TARGET_APP_REGS; \ + fixed_regs[3] |= ! TARGET_APP_REGS; \ + fixed_regs[4] |= ! TARGET_APP_REGS || TARGET_CM_EMBMEDANY; \ + if (TARGET_FLAT) \ + { \ + /* Let the compiler believe the frame pointer is still \ + %fp, but output it as %i7. */ \ + fixed_regs[31] = 1; \ + reg_names[FRAME_POINTER_REGNUM] = "%i7"; \ + /* ??? This is a hack to disable leaf functions. */ \ + global_regs[7] = 1; \ + } \ + if (profile_block_flag) \ + { \ + /* %g1 and %g2 must be fixed, because BLOCK_PROFILER \ + uses them. */ \ + fixed_regs[1] = 1; \ + fixed_regs[2] = 1; \ + } \ + } \ +while (0) + +/* 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 SPARC, ordinary registers hold 32 bits worth; + this means both integer and floating point registers. + On v9, integer regs hold 64 bits worth; floating point regs hold + 32 bits worth (this includes the new fp regs as even the odd ones are + included in the hard register count). */ + +#define HARD_REGNO_NREGS(REGNO, MODE) \ + (TARGET_ARCH64 \ + ? ((REGNO) < 32 \ + ? (GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD \ + : (GET_MODE_SIZE (MODE) + 3) / 4) \ + : ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)) + +/* A subreg in 64 bit mode will have the wrong offset for a floating point + register. The least significant part is at offset 1, compared to 0 for + integer registers. This only applies when FMODE is a larger mode. + We also need to handle a special case of TF-->DF conversions. */ +#define ALTER_HARD_SUBREG(TMODE, WORD, FMODE, REGNO) \ + (TARGET_ARCH64 \ + && (REGNO) >= SPARC_FIRST_FP_REG \ + && (REGNO) <= SPARC_LAST_V9_FP_REG \ + && (TMODE) == SImode \ + && !((FMODE) == QImode || (FMODE) == HImode) \ + ? ((REGNO) + 1) \ + : ((TMODE) == DFmode && (FMODE) == TFmode) \ + ? ((REGNO) + ((WORD) * 2)) \ + : ((REGNO) + (WORD))) + +/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE. + See sparc.c for how we initialize this. */ +extern int *hard_regno_mode_classes; +extern int sparc_mode_class[]; +#define HARD_REGNO_MODE_OK(REGNO, MODE) \ + ((hard_regno_mode_classes[REGNO] & sparc_mode_class[MODE]) != 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. + + For V9: SFmode can't be combined with other float modes, because they can't + be allocated to the %d registers. Also, DFmode won't fit in odd %f + registers, but SFmode will. */ +#define MODES_TIEABLE_P(MODE1, MODE2) \ + ((MODE1) == (MODE2) \ + || (GET_MODE_CLASS (MODE1) == GET_MODE_CLASS (MODE2) \ + && (! TARGET_V9 \ + || (GET_MODE_CLASS (MODE1) != MODE_FLOAT \ + || (MODE1 != SFmode && MODE2 != SFmode))))) + +/* Specify the registers used for certain standard purposes. + The values of these macros are register numbers. */ + +/* SPARC 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 14 + +/* Actual top-of-stack address is 92/176 greater than the contents of the + stack pointer register for !v9/v9. That is: + - !v9: 64 bytes for the in and local registers, 4 bytes for structure return + address, and 6*4 bytes for the 6 register parameters. + - v9: 128 bytes for the in and local registers + 6*8 bytes for the integer + parameter regs. */ +#define STACK_POINTER_OFFSET FIRST_PARM_OFFSET(0) + +/* The stack bias (amount by which the hardware register is offset by). */ +#define SPARC_STACK_BIAS ((TARGET_ARCH64 && TARGET_STACK_BIAS) ? 2047 : 0) + +/* Is stack biased? */ +#define STACK_BIAS SPARC_STACK_BIAS + +/* Base register for access to local variables of the function. */ +#define FRAME_POINTER_REGNUM 30 + +#if 0 +/* Register that is used for the return address for the flat model. */ +#define RETURN_ADDR_REGNUM 15 +#endif + +/* Value should be nonzero if functions must have frame pointers. + Zero means the frame pointer need not be set up (and parms + may be accessed via the stack pointer) in functions that seem suitable. + This is computed in `reload', in reload1.c. + Used in flow.c, global.c, and reload1.c. + + Being a non-leaf function does not mean a frame pointer is needed in the + flat window model. However, the debugger won't be able to backtrace through + us with out it. */ +#define FRAME_POINTER_REQUIRED \ + (TARGET_FLAT ? (current_function_calls_alloca || current_function_varargs \ + || !leaf_function_p ()) \ + : ! (leaf_function_p () && only_leaf_regs_used ())) + +/* 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) \ + ((VAR) = (TARGET_FLAT ? sparc_flat_compute_frame_size (get_frame_size ()) \ + : compute_frame_size (get_frame_size (), 1))) + +/* Base register for access to arguments of the function. */ +#define ARG_POINTER_REGNUM FRAME_POINTER_REGNUM + +/* Register in which static-chain is passed to a function. This must + not be a register used by the prologue. */ +#define STATIC_CHAIN_REGNUM (TARGET_ARCH64 ? 5 : 2) + +/* Register which holds offset table for position-independent + data references. */ + +#define PIC_OFFSET_TABLE_REGNUM 23 + +#define FINALIZE_PIC finalize_pic () + +/* Pick a default value we can notice from override_options: + !v9: Default is on. + v9: Default is off. */ + +#define DEFAULT_PCC_STRUCT_RETURN -1 + +/* Sparc ABI says that quad-precision floats and all structures are returned + in memory. + For v9: unions <= 32 bytes in size are returned in int regs, + structures up to 32 bytes are returned in int and fp regs. */ + +#define RETURN_IN_MEMORY(TYPE) \ +(TARGET_ARCH32 \ + ? (TYPE_MODE (TYPE) == BLKmode \ + || TYPE_MODE (TYPE) == TFmode \ + || TYPE_MODE (TYPE) == TCmode) \ + : (TYPE_MODE (TYPE) == BLKmode \ + && int_size_in_bytes (TYPE) > 32)) + +/* Functions which return large structures get the address + to place the wanted value at offset 64 from the frame. + Must reserve 64 bytes for the in and local registers. + v9: Functions which return large structures get the address to place the + wanted value from an invisible first argument. */ +/* Used only in other #defines in this file. */ +#define STRUCT_VALUE_OFFSET 64 + +#define STRUCT_VALUE \ + (TARGET_ARCH64 \ + ? 0 \ + : gen_rtx_MEM (Pmode, \ + gen_rtx_PLUS (Pmode, stack_pointer_rtx, \ + GEN_INT (STRUCT_VALUE_OFFSET)))) +#define STRUCT_VALUE_INCOMING \ + (TARGET_ARCH64 \ + ? 0 \ + : gen_rtx_MEM (Pmode, \ + gen_rtx_PLUS (Pmode, frame_pointer_rtx, \ + GEN_INT (STRUCT_VALUE_OFFSET)))) + +/* 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 SPARC has various kinds of registers: general, floating point, + and condition codes [well, it has others as well, but none that we + care directly about]. + + For v9 we must distinguish between the upper and lower floating point + registers because the upper ones can't hold SFmode values. + HARD_REGNO_MODE_OK won't help here because reload assumes that register(s) + satisfying a group need for a class will also satisfy a single need for + that class. EXTRA_FP_REGS is a bit of a misnomer as it covers all 64 fp + regs. + + It is important that one class contains all the general and all the standard + fp regs. Otherwise find_reg() won't properly allocate int regs for moves, + because reg_class_record() will bias the selection in favor of fp regs, + because reg_class_subunion[GENERAL_REGS][FP_REGS] will yield FP_REGS, + because FP_REGS > GENERAL_REGS. + + It is also important that one class contain all the general and all the + fp regs. Otherwise when spilling a DFmode reg, it may be from EXTRA_FP_REGS + but find_reloads() may use class GENERAL_OR_FP_REGS. This will cause + allocate_reload_reg() to bypass it causing an abort because the compiler + thinks it doesn't have a spill reg when in fact it does. + + v9 also has 4 floating point condition code registers. Since we don't + have a class that is the union of FPCC_REGS with either of the others, + it is important that it appear first. Otherwise the compiler will die + trying to compile _fixunsdfsi because fix_truncdfsi2 won't match its + constraints. + + It is important that SPARC_ICC_REG have class NO_REGS. Otherwise combine + may try to use it to hold an SImode value. See register_operand. + ??? Should %fcc[0123] be handled similarly? +*/ + +enum reg_class { NO_REGS, FPCC_REGS, I64_REGS, GENERAL_REGS, FP_REGS, + EXTRA_FP_REGS, GENERAL_OR_FP_REGS, GENERAL_OR_EXTRA_FP_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", "FPCC_REGS", "I64_REGS", "GENERAL_REGS", "FP_REGS", \ + "EXTRA_FP_REGS", "GENERAL_OR_FP_REGS", "GENERAL_OR_EXTRA_FP_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. */ + +#define REG_CLASS_CONTENTS \ + {{0, 0, 0, 0}, {0, 0, 0, 0xf}, {0xffff, 0, 0, 0}, \ + {-1, 0, 0, 0}, {0, -1, 0, 0}, {0, -1, -1, 0}, \ + {-1, -1, 0, 0}, {-1, -1, -1, 0}, {-1, -1, -1, 0x1f}} + +/* 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. */ + +extern enum reg_class sparc_regno_reg_class[]; + +#define REGNO_REG_CLASS(REGNO) sparc_regno_reg_class[(REGNO)] + +/* This is the order in which to allocate registers normally. + + We put %f0/%f1 last among the float registers, so as to make it more + likely that a pseudo-register which dies in the float return register + will get allocated to the float return register, thus saving a move + instruction at the end of the function. */ + +#define REG_ALLOC_ORDER \ +{ 8, 9, 10, 11, 12, 13, 2, 3, \ + 15, 16, 17, 18, 19, 20, 21, 22, \ + 23, 24, 25, 26, 27, 28, 29, 31, \ + 34, 35, 36, 37, 38, 39, /* %f2-%f7 */ \ + 40, 41, 42, 43, 44, 45, 46, 47, /* %f8-%f15 */ \ + 48, 49, 50, 51, 52, 53, 54, 55, /* %f16-%f23 */ \ + 56, 57, 58, 59, 60, 61, 62, 63, /* %f24-%f31 */ \ + 64, 65, 66, 67, 68, 69, 70, 71, /* %f32-%f39 */ \ + 72, 73, 74, 75, 76, 77, 78, 79, /* %f40-%f47 */ \ + 80, 81, 82, 83, 84, 85, 86, 87, /* %f48-%f55 */ \ + 88, 89, 90, 91, 92, 93, 94, 95, /* %f56-%f63 */ \ + 32, 33, /* %f0,%f1 */ \ + 96, 97, 98, 99, 100, /* %fcc0-3, %icc */ \ + 1, 4, 5, 6, 7, 0, 14, 30} + +/* This is the order in which to allocate registers for + leaf functions. If all registers can fit in the "i" registers, + then we have the possibility of having a leaf function. */ + +#define REG_LEAF_ALLOC_ORDER \ +{ 2, 3, 24, 25, 26, 27, 28, 29, \ + 15, 8, 9, 10, 11, 12, 13, \ + 16, 17, 18, 19, 20, 21, 22, 23, \ + 34, 35, 36, 37, 38, 39, \ + 40, 41, 42, 43, 44, 45, 46, 47, \ + 48, 49, 50, 51, 52, 53, 54, 55, \ + 56, 57, 58, 59, 60, 61, 62, 63, \ + 64, 65, 66, 67, 68, 69, 70, 71, \ + 72, 73, 74, 75, 76, 77, 78, 79, \ + 80, 81, 82, 83, 84, 85, 86, 87, \ + 88, 89, 90, 91, 92, 93, 94, 95, \ + 32, 33, \ + 96, 97, 98, 99, 100, \ + 1, 4, 5, 6, 7, 0, 14, 30, 31} + +#define ORDER_REGS_FOR_LOCAL_ALLOC order_regs_for_local_alloc () + +/* ??? %g7 is not a leaf register to effectively #undef LEAF_REGISTERS when + -mflat is used. Function only_leaf_regs_used will return 0 if a global + register is used and is not permitted in a leaf function. We make %g7 + a global reg if -mflat and voila. Since %g7 is a system register and is + fixed it won't be used by gcc anyway. */ + +#define LEAF_REGISTERS \ +{ 1, 1, 1, 1, 1, 1, 1, 0, \ + 0, 0, 0, 0, 0, 0, 1, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 1, 1, 1, 1, 1, 1, 0, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1} + +extern char leaf_reg_remap[]; +#define LEAF_REG_REMAP(REGNO) (leaf_reg_remap[REGNO]) + +/* The class value for index registers, and the one for base regs. */ +#define INDEX_REG_CLASS GENERAL_REGS +#define BASE_REG_CLASS GENERAL_REGS + +/* Local macro to handle the two v9 classes of FP regs. */ +#define FP_REG_CLASS_P(CLASS) ((CLASS) == FP_REGS || (CLASS) == EXTRA_FP_REGS) + +/* Get reg_class from a letter such as appears in the machine description. + In the not-v9 case, coerce v9's 'e' class to 'f', so we can use 'e' in the + .md file for v8 and v9. + 'd' and 'b' are used for single and double precision VIS operations, + if TARGET_VIS. + 'h' is used for V8+ 64 bit global and out registers. */ + +#define REG_CLASS_FROM_LETTER(C) \ +(TARGET_V9 \ + ? ((C) == 'f' ? FP_REGS \ + : (C) == 'e' ? EXTRA_FP_REGS \ + : (C) == 'c' ? FPCC_REGS \ + : ((C) == 'd' && TARGET_VIS) ? FP_REGS\ + : ((C) == 'b' && TARGET_VIS) ? EXTRA_FP_REGS\ + : ((C) == 'h' && TARGET_V8PLUS) ? I64_REGS\ + : NO_REGS) \ + : ((C) == 'f' ? FP_REGS \ + : (C) == 'e' ? FP_REGS \ + : (C) == 'c' ? FPCC_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 range of constants an insn can actually contain. + `J' is used for the range which is just zero (since that is R0). + `K' is used for constants which can be loaded with a single sethi insn. + `L' is used for the range of constants supported by the movcc insns. + `M' is used for the range of constants supported by the movrcc insns. */ + +#define SPARC_SIMM10_P(X) ((unsigned HOST_WIDE_INT) (X) + 0x200 < 0x400) +#define SPARC_SIMM11_P(X) ((unsigned HOST_WIDE_INT) (X) + 0x400 < 0x800) +#define SPARC_SIMM13_P(X) ((unsigned HOST_WIDE_INT) (X) + 0x1000 < 0x2000) +/* 10 and 11 bit immediates are only used for a few specific insns. + SMALL_INT is used throughout the port so we continue to use it. */ +#define SMALL_INT(X) (SPARC_SIMM13_P (INTVAL (X))) +/* 13 bit immediate, considering only the low 32 bits */ +#define SMALL_INT32(X) (SPARC_SIMM13_P ((int)INTVAL (X) & 0xffffffff)) +#define SPARC_SETHI_P(X) \ +(((unsigned HOST_WIDE_INT) (X) & ~(unsigned HOST_WIDE_INT) 0xfffffc00) == 0) + +#define CONST_OK_FOR_LETTER_P(VALUE, C) \ + ((C) == 'I' ? SPARC_SIMM13_P (VALUE) \ + : (C) == 'J' ? (VALUE) == 0 \ + : (C) == 'K' ? SPARC_SETHI_P (VALUE) \ + : (C) == 'L' ? SPARC_SIMM11_P (VALUE) \ + : (C) == 'M' ? SPARC_SIMM10_P (VALUE) \ + : 0) + +/* Similar, but for floating constants, and defining letters G and H. + Here VALUE is the CONST_DOUBLE rtx itself. */ + +#define CONST_DOUBLE_OK_FOR_LETTER_P(VALUE, C) \ + ((C) == 'G' ? fp_zero_operand (VALUE) \ + : (C) == 'H' ? arith_double_operand (VALUE, DImode) \ + : 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. */ +/* - We can't load constants into FP registers. We can't load any FP + constant if an 'E' constraint fails to match it. + - Try and reload integer constants (symbolic or otherwise) back into + registers directly, rather than having them dumped to memory. */ + +#define PREFERRED_RELOAD_CLASS(X,CLASS) \ + (CONSTANT_P (X) \ + ? ((FP_REG_CLASS_P (CLASS) \ + || (GET_MODE_CLASS (GET_MODE (X)) == MODE_FLOAT \ + && (HOST_FLOAT_FORMAT != IEEE_FLOAT_FORMAT \ + || HOST_BITS_PER_INT != BITS_PER_WORD))) \ + ? NO_REGS \ + : (!FP_REG_CLASS_P (CLASS) \ + && GET_MODE_CLASS (GET_MODE (X)) == MODE_INT) \ + ? GENERAL_REGS \ + : (CLASS)) \ + : (CLASS)) + +/* Return the register class of a scratch register needed to load IN into + a register of class CLASS in MODE. + + We need a temporary when loading/storing a HImode/QImode value + between memory and the FPU registers. This can happen when combine puts + a paradoxical subreg in a float/fix conversion insn. */ + +#define SECONDARY_INPUT_RELOAD_CLASS(CLASS, MODE, IN) \ + ((FP_REG_CLASS_P (CLASS) \ + && ((MODE) == HImode || (MODE) == QImode) \ + && (GET_CODE (IN) == MEM \ + || ((GET_CODE (IN) == REG || GET_CODE (IN) == SUBREG) \ + && true_regnum (IN) == -1))) \ + ? GENERAL_REGS \ + : (((TARGET_CM_MEDANY \ + && symbolic_operand ((IN), (MODE))) \ + || (TARGET_CM_EMBMEDANY \ + && text_segment_operand ((IN), (MODE)))) \ + && !flag_pic) \ + ? GENERAL_REGS \ + : NO_REGS) + +#define SECONDARY_OUTPUT_RELOAD_CLASS(CLASS, MODE, IN) \ + ((FP_REG_CLASS_P (CLASS) \ + && ((MODE) == HImode || (MODE) == QImode) \ + && (GET_CODE (IN) == MEM \ + || ((GET_CODE (IN) == REG || GET_CODE (IN) == SUBREG) \ + && true_regnum (IN) == -1))) \ + ? GENERAL_REGS \ + : (((TARGET_CM_MEDANY \ + && symbolic_operand ((IN), (MODE))) \ + || (TARGET_CM_EMBMEDANY \ + && text_segment_operand ((IN), (MODE)))) \ + && !flag_pic) \ + ? GENERAL_REGS \ + : NO_REGS) + +/* On SPARC 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. + We want to use the reserved location just below the frame pointer. + However, we must ensure that there is a frame, so use assign_stack_local + if the frame size is zero. */ +#define SECONDARY_MEMORY_NEEDED_RTX(MODE) \ + (get_frame_size () == 0 \ + ? assign_stack_local (MODE, GET_MODE_SIZE (MODE), 0) \ + : gen_rtx_MEM (MODE, gen_rtx_PLUS (Pmode, frame_pointer_rtx, \ + GEN_INT (STARTING_FRAME_OFFSET)))) + +/* Get_secondary_mem widens its argument to BITS_PER_WORD which loses on v9 + because the movsi and movsf patterns don't handle r/f moves. + For v8 we copy the default definition. */ +#define SECONDARY_MEMORY_NEEDED_MODE(MODE) \ + (TARGET_ARCH64 \ + ? (GET_MODE_BITSIZE (MODE) < 32 \ + ? mode_for_size (32, GET_MODE_CLASS (MODE), 0) \ + : MODE) \ + : (GET_MODE_BITSIZE (MODE) < BITS_PER_WORD \ + ? mode_for_size (BITS_PER_WORD, GET_MODE_CLASS (MODE), 0) \ + : MODE)) + +/* Return the maximum number of consecutive registers + needed to represent mode MODE in a register of class CLASS. */ +/* On SPARC, this is the size of MODE in words. */ +#define CLASS_MAX_NREGS(CLASS, MODE) \ + (FP_REG_CLASS_P (CLASS) ? (GET_MODE_SIZE (MODE) + 3) / 4 \ + : (GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) + +/* Stack layout; function entry, exit and calling. */ + +/* Define the number of register that can hold parameters. + This macro is only used in other macro definitions below and in sparc.c. + MODE is the mode of the argument. + !v9: All args are passed in %o0-%o5. + v9: %o0-%o5 and %f0-%f31 are cumulatively used to pass values. + See the description in sparc.c. */ +#define NPARM_REGS(MODE) \ +(TARGET_ARCH64 \ + ? (GET_MODE_CLASS (MODE) == MODE_FLOAT ? 32 : 6) \ + : 6) + +/* Define this if pushing a word on the stack + makes the stack pointer a smaller address. */ +#define STACK_GROWS_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. */ +/* This allows space for one TFmode floating point value. */ +#define STARTING_FRAME_OFFSET \ + (TARGET_ARCH64 ? (SPARC_STACK_BIAS - 16) \ + : (-SPARC_STACK_ALIGN (LONG_DOUBLE_TYPE_SIZE / BITS_PER_UNIT))) + +/* If we generate an insn to push BYTES bytes, + this says how many the stack pointer really advances by. + On SPARC, don't define this because there are no push insns. */ +/* #define PUSH_ROUNDING(BYTES) */ + +/* Offset of first parameter from the argument pointer register value. + !v9: This is 64 for the ins and locals, plus 4 for the struct-return reg + even if this function isn't going to use it. + v9: This is 128 for the ins and locals. */ +#define FIRST_PARM_OFFSET(FNDECL) \ + (TARGET_ARCH64 ? (SPARC_STACK_BIAS + 16 * UNITS_PER_WORD) \ + : (STRUCT_VALUE_OFFSET + UNITS_PER_WORD)) + +/* Offset from the argument pointer register value to the CFA. */ + +#define ARG_POINTER_CFA_OFFSET SPARC_STACK_BIAS + +/* When a parameter is passed in a register, stack space is still + allocated for it. + !v9: All 6 possible integer registers have backing store allocated. + v9: Only space for the arguments passed is allocated. */ +/* ??? Ideally, we'd use zero here (as the minimum), but zero has special + meaning to the backend. Further, we need to be able to detect if a + varargs/unprototyped function is called, as they may want to spill more + registers than we've provided space. Ugly, ugly. So for now we retain + all 6 slots even for v9. */ +#define REG_PARM_STACK_SPACE(DECL) (6 * UNITS_PER_WORD) + +/* 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 + +/* Value is the number of bytes of arguments automatically + popped when returning from a subroutine 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. + SIZE is the number of bytes of arguments passed on the stack. */ + +#define RETURN_POPS_ARGS(FUNDECL,FUNTYPE,SIZE) 0 + +/* Some subroutine macros specific to this machine. + When !TARGET_FPU, put float return values in the general registers, + since we don't have any fp registers. */ +#define BASE_RETURN_VALUE_REG(MODE) \ + (TARGET_ARCH64 \ + ? (TARGET_FPU && FLOAT_MODE_P (MODE) ? 32 : 8) \ + : (((MODE) == SFmode || (MODE) == DFmode) && TARGET_FPU ? 32 : 8)) + +#define BASE_OUTGOING_VALUE_REG(MODE) \ + (TARGET_ARCH64 \ + ? (TARGET_FPU && FLOAT_MODE_P (MODE) ? 32 \ + : TARGET_FLAT ? 8 : 24) \ + : (((MODE) == SFmode || (MODE) == DFmode) && TARGET_FPU ? 32 \ + : (TARGET_FLAT ? 8 : 24))) + +#define BASE_PASSING_ARG_REG(MODE) \ + (TARGET_ARCH64 \ + ? (TARGET_FPU && FLOAT_MODE_P (MODE) ? 32 : 8) \ + : 8) + +/* ??? FIXME -- seems wrong for v9 structure passing... */ +#define BASE_INCOMING_ARG_REG(MODE) \ + (TARGET_ARCH64 \ + ? (TARGET_FPU && FLOAT_MODE_P (MODE) ? 32 \ + : TARGET_FLAT ? 8 : 24) \ + : (TARGET_FLAT ? 8 : 24)) + +/* Define this macro if the target machine has "register windows". This + C expression returns the register number as seen by the called function + corresponding to register number OUT as seen by the calling function. + Return OUT if register number OUT is not an outbound register. */ + +#define INCOMING_REGNO(OUT) \ + ((TARGET_FLAT || (OUT) < 8 || (OUT) > 15) ? (OUT) : (OUT) + 16) + +/* Define this macro if the target machine has "register windows". This + C expression returns the register number as seen by the calling function + corresponding to register number IN as seen by the called function. + Return IN if register number IN is not an inbound register. */ + +#define OUTGOING_REGNO(IN) \ + ((TARGET_FLAT || (IN) < 24 || (IN) > 31) ? (IN) : (IN) - 16) + +/* 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 SPARC the value is found in the first "output" register. */ + +extern struct rtx_def *function_value (); +#define FUNCTION_VALUE(VALTYPE, FUNC) \ + function_value ((VALTYPE), TYPE_MODE (VALTYPE), 1) + +/* But the called function leaves it in the first "input" register. */ + +#define FUNCTION_OUTGOING_VALUE(VALTYPE, FUNC) \ + function_value ((VALTYPE), TYPE_MODE (VALTYPE), 0) + +/* Define how to find the value returned by a library function + assuming the value has mode MODE. */ + +#define LIBCALL_VALUE(MODE) \ + function_value (NULL_TREE, (MODE), 1) + +/* 1 if N is a possible register number for a function value + as seen by the caller. + On SPARC, the first "output" reg is used for integer values, + and the first floating point register is used for floating point values. */ + +#define FUNCTION_VALUE_REGNO_P(N) ((N) == 8 || (N) == 32) + +/* Define the size of space to allocate for the return value of an + untyped_call. */ + +#define APPLY_RESULT_SIZE 16 + +/* 1 if N is a possible register number for function argument passing. + On SPARC, these are the "output" registers. v9 also uses %f0-%f31. */ + +#define FUNCTION_ARG_REGNO_P(N) \ +(TARGET_ARCH64 \ + ? (((N) >= 8 && (N) <= 13) || ((N) >= 32 && (N) <= 63)) \ + : ((N) >= 8 && (N) <= 13)) + +/* 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 SPARC (!v9), 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 7 or more means all following args should go on the stack. + + For v9, we also need to know whether a prototype is present. */ + +struct sparc_args { + int words; /* number of words passed so far */ + int prototype_p; /* non-zero if a prototype is present */ + int libcall_p; /* non-zero if a library call */ +}; +#define CUMULATIVE_ARGS struct sparc_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. */ + +extern void init_cumulative_args (); +#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT) \ +init_cumulative_args (& (CUM), (FNTYPE), (LIBNAME), (INDIRECT)); + +/* 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. */ + +extern void function_arg_advance (); +#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \ +function_arg_advance (& (CUM), (MODE), (TYPE), (NAMED)) + +/* 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). */ + +extern struct rtx_def *function_arg (); +#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \ +function_arg (& (CUM), (MODE), (TYPE), (NAMED), 0) + +/* Define where a function finds its arguments. + This is different from FUNCTION_ARG because of register windows. */ + +#define FUNCTION_INCOMING_ARG(CUM, MODE, TYPE, NAMED) \ +function_arg (& (CUM), (MODE), (TYPE), (NAMED), 1) + +/* 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. */ + +extern int function_arg_partial_nregs (); +#define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) \ +function_arg_partial_nregs (& (CUM), (MODE), (TYPE), (NAMED)) + +/* A C expression that indicates when an argument must be passed by reference. + If nonzero for an argument, a copy of that argument is made in memory and a + pointer to the argument is passed instead of the argument itself. + The pointer is passed in whatever way is appropriate for passing a pointer + to that type. */ + +extern int function_arg_pass_by_reference (); +#define FUNCTION_ARG_PASS_BY_REFERENCE(CUM, MODE, TYPE, NAMED) \ +function_arg_pass_by_reference (& (CUM), (MODE), (TYPE), (NAMED)) + +/* If defined, a C expression which determines whether, and in which direction, + to pad out an argument with extra space. The value should be of type + `enum direction': either `upward' to pad above the argument, + `downward' to pad below, or `none' to inhibit padding. */ + +#define FUNCTION_ARG_PADDING(MODE, TYPE) \ +function_arg_padding ((MODE), (TYPE)) + +/* 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. + For sparc64, objects requiring 16 byte alignment are passed that way. */ + +#define FUNCTION_ARG_BOUNDARY(MODE, TYPE) \ +((TARGET_ARCH64 \ + && (GET_MODE_ALIGNMENT (MODE) == 128 \ + || ((TYPE) && TYPE_ALIGN (TYPE) == 128))) \ + ? 128 : PARM_BOUNDARY) + +/* Define the information needed to generate branch and scc insns. This is + stored from the compare operation. Note that we can't use "rtx" here + since it hasn't been defined! */ + +extern struct rtx_def *sparc_compare_op0, *sparc_compare_op1; + +/* Define the function that build the compare insn for scc and bcc. */ + +extern struct rtx_def *gen_compare_reg (); + +/* This function handles all v9 scc insns */ + +extern int gen_v9_scc (); + +/* Generate the special assembly code needed to tell the assembler whatever + it might need to know about the return value of a function. + + For Sparc assemblers, we need to output a .proc pseudo-op which conveys + information to the assembler relating to peephole optimization (done in + the assembler). */ + +#define ASM_DECLARE_RESULT(FILE, RESULT) \ + fprintf ((FILE), "\t.proc\t0%lo\n", sparc_type_code (TREE_TYPE (RESULT))) + +/* Output the label for a function definition. */ + +#define ASM_DECLARE_FUNCTION_NAME(FILE, NAME, DECL) \ +do { \ + ASM_DECLARE_RESULT (FILE, DECL_RESULT (DECL)); \ + ASM_OUTPUT_LABEL (FILE, NAME); \ +} 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 SPARC, 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. */ + +extern int leaf_function; +#define FUNCTION_PROLOGUE(FILE, SIZE) \ + (TARGET_FLAT ? sparc_flat_output_function_prologue (FILE, (int)SIZE) \ + : output_function_prologue (FILE, (int)SIZE, leaf_function)) + +/* Output assembler code to FILE to increment profiler label # LABELNO + for profiling a function entry. + + 32 bit sparc uses %g2 as the STATIC_CHAIN_REGNUM which gets clobbered + during profiling so we need to save/restore it around the call to mcount. + We're guaranteed that a save has just been done, and we use the space + allocated for intreg/fpreg value passing. */ + +#define FUNCTION_PROFILER(FILE, LABELNO) \ + do { \ + char buf[20]; \ + ASM_GENERATE_INTERNAL_LABEL (buf, "LP", (LABELNO)); \ + if (! TARGET_ARCH64) \ + fputs ("\tst %g2,[%fp-4]\n", FILE); \ + fputs ("\tsethi %hi(", FILE); \ + assemble_name (FILE, buf); \ + fputs ("),%o0\n", FILE); \ + fputs ("\tcall mcount\n\tadd %o0,%lo(", FILE); \ + assemble_name (FILE, buf); \ + fputs ("),%o0\n", FILE); \ + if (! TARGET_ARCH64) \ + fputs ("\tld [%fp-4],%g2\n", FILE); \ + } while (0) + +/* There are three profiling modes for basic blocks available. + The modes are selected at compile time by using the options + -a or -ax of the gnu compiler. + The variable `profile_block_flag' will be set according to the + selected option. + + profile_block_flag == 0, no option used: + + No profiling done. + + profile_block_flag == 1, -a option used. + + Count frequency of execution of every basic block. + + profile_block_flag == 2, -ax option used. + + Generate code to allow several different profiling modes at run time. + Available modes are: + Produce a trace of all basic blocks. + Count frequency of jump instructions executed. + In every mode it is possible to start profiling upon entering + certain functions and to disable profiling of some other functions. + + The result of basic-block profiling will be written to a file `bb.out'. + If the -ax option is used parameters for the profiling will be read + from file `bb.in'. + +*/ + +/* The following macro shall output assembler code to FILE + to initialize basic-block profiling. + + If profile_block_flag == 2 + + Output code to call the subroutine `__bb_init_trace_func' + and pass two parameters to it. The first parameter is + the address of a block allocated in the object module. + The second parameter is the number of the first basic block + of the function. + + The name of the block is a local symbol made with this statement: + + ASM_GENERATE_INTERNAL_LABEL (BUFFER, "LPBX", 0); + + Of course, since you are writing the definition of + `ASM_GENERATE_INTERNAL_LABEL' as well as that of this macro, you + can take a short cut in the definition of this macro and use the + name that you know will result. + + The number of the first basic block of the function is + passed to the macro in BLOCK_OR_LABEL. + + If described in a virtual assembler language the code to be + output looks like: + + parameter1 <- LPBX0 + parameter2 <- BLOCK_OR_LABEL + call __bb_init_trace_func + + else if profile_block_flag != 0 + + Output code to call the subroutine `__bb_init_func' + and pass one single parameter to it, which is the same + as the first parameter to `__bb_init_trace_func'. + + The first word of this parameter is a flag which will be nonzero if + the object module has already been initialized. So test this word + first, and do not call `__bb_init_func' if the flag is nonzero. + Note: When profile_block_flag == 2 the test need not be done + but `__bb_init_trace_func' *must* be called. + + BLOCK_OR_LABEL may be used to generate a label number as a + branch destination in case `__bb_init_func' will not be called. + + If described in a virtual assembler language the code to be + output looks like: + + cmp (LPBX0),0 + jne local_label + parameter1 <- LPBX0 + call __bb_init_func +local_label: + +*/ + +#define FUNCTION_BLOCK_PROFILER(FILE, BLOCK_OR_LABEL) \ +do \ + { \ + int bol = (BLOCK_OR_LABEL); \ + switch (profile_block_flag) \ + { \ + case 2: \ + fprintf (FILE, "\tsethi %%hi(LPBX0),%%o0\n\tor %%o0,%%lo(LPBX0),%%o0\n\tsethi %%hi(%d),%%o1\n\tcall ___bb_init_trace_func\n\tor %%o1,%%lo(%d),%%o1\n",\ + bol, bol); \ + break; \ + default: \ + fprintf (FILE, "\tsethi %%hi(LPBX0),%%o0\n\tld [%%lo(LPBX0)+%%o0],%%o1\n\ttst %%o1\n\tbne LPY%d\n\tadd %%o0,%%lo(LPBX0),%%o0\n\tcall ___bb_init_func\n\tnop\nLPY%d:\n",\ + bol, bol); \ + break; \ + } \ + } \ +while (0) + +/* The following macro shall output assembler code to FILE + to increment a counter associated with basic block number BLOCKNO. + + If profile_block_flag == 2 + + Output code to initialize the global structure `__bb' and + call the function `__bb_trace_func' which will increment the + counter. + + `__bb' consists of two words. In the first word the number + of the basic block has to be stored. In the second word + the address of a block allocated in the object module + has to be stored. + + The basic block number is given by BLOCKNO. + + The address of the block is given by the label created with + + ASM_GENERATE_INTERNAL_LABEL (BUFFER, "LPBX", 0); + + by FUNCTION_BLOCK_PROFILER. + + Of course, since you are writing the definition of + `ASM_GENERATE_INTERNAL_LABEL' as well as that of this macro, you + can take a short cut in the definition of this macro and use the + name that you know will result. + + If described in a virtual assembler language the code to be + output looks like: + + move BLOCKNO -> (__bb) + move LPBX0 -> (__bb+4) + call __bb_trace_func + + Note that function `__bb_trace_func' must not change the + machine state, especially the flag register. To grant + this, you must output code to save and restore registers + either in this macro or in the macros MACHINE_STATE_SAVE + and MACHINE_STATE_RESTORE. The last two macros will be + used in the function `__bb_trace_func', so you must make + sure that the function prologue does not change any + register prior to saving it with MACHINE_STATE_SAVE. + + else if profile_block_flag != 0 + + Output code to increment the counter directly. + Basic blocks are numbered separately from zero within each + compiled object module. The count associated with block number + BLOCKNO is at index BLOCKNO in an array of words; the name of + this array is a local symbol made with this statement: + + ASM_GENERATE_INTERNAL_LABEL (BUFFER, "LPBX", 2); + + Of course, since you are writing the definition of + `ASM_GENERATE_INTERNAL_LABEL' as well as that of this macro, you + can take a short cut in the definition of this macro and use the + name that you know will result. + + If described in a virtual assembler language, the code to be + output looks like: + + inc (LPBX2+4*BLOCKNO) + +*/ + +#define BLOCK_PROFILER(FILE, BLOCKNO) \ +do \ + { \ + int blockn = (BLOCKNO); \ + switch (profile_block_flag) \ + { \ + case 2: \ + fprintf (FILE, "\tsethi %%hi(___bb),%%g1\n\tsethi %%hi(%d),%%g2\n\tor %%g2,%%lo(%d),%%g2\n\tst %%g2,[%%lo(___bb)+%%g1]\n\tsethi %%hi(LPBX0),%%g2\n\tor %%g2,%%lo(LPBX0),%%g2\n\tadd 4,%%g1,%%g1\n\tst %%g2,[%%lo(___bb)+%%g1]\n\tmov %%o7,%%g2\n\tcall ___bb_trace_func\n\tnop\n\tmov %%g2,%%o7\n",\ + blockn, blockn); \ + break; \ + default: \ + fprintf (FILE, "\tsethi %%hi(LPBX2+%d),%%g1\n\tld [%%lo(LPBX2+%d)+%%g1],%%g2\n\ +\tadd %%g2,1,%%g2\n\tst %%g2,[%%lo(LPBX2+%d)+%%g1]\n", \ + 4 * blockn, 4 * blockn, 4 * blockn); \ + break; \ + } \ + } \ +while(0) + +/* The following macro shall output assembler code to FILE + to indicate a return from function during basic-block profiling. + + If profiling_block_flag == 2: + + Output assembler code to call function `__bb_trace_ret'. + + Note that function `__bb_trace_ret' must not change the + machine state, especially the flag register. To grant + this, you must output code to save and restore registers + either in this macro or in the macros MACHINE_STATE_SAVE_RET + and MACHINE_STATE_RESTORE_RET. The last two macros will be + used in the function `__bb_trace_ret', so you must make + sure that the function prologue does not change any + register prior to saving it with MACHINE_STATE_SAVE_RET. + + else if profiling_block_flag != 0: + + The macro will not be used, so it need not distinguish + these cases. +*/ + +#define FUNCTION_BLOCK_PROFILER_EXIT(FILE) \ + fprintf (FILE, "\tcall ___bb_trace_ret\n\tnop\n" ); + +/* The function `__bb_trace_func' is called in every basic block + and is not allowed to change the machine state. Saving (restoring) + the state can either be done in the BLOCK_PROFILER macro, + before calling function (rsp. after returning from function) + `__bb_trace_func', or it can be done inside the function by + defining the macros: + + MACHINE_STATE_SAVE(ID) + MACHINE_STATE_RESTORE(ID) + + In the latter case care must be taken, that the prologue code + of function `__bb_trace_func' does not already change the + state prior to saving it with MACHINE_STATE_SAVE. + + The parameter `ID' is a string identifying a unique macro use. + + On sparc it is sufficient to save the psw register to memory. + Unfortunately the psw register can be read in supervisor mode only, + so we read only the condition codes by using branch instructions + and hope that this is enough. */ + +#define MACHINE_STATE_SAVE(ID) \ + int ms_flags, ms_saveret; \ + asm volatile( \ + "mov %%g0,%0\n\ + be,a LFLGNZ"ID"\n\ + or %0,4,%0\n\ +LFLGNZ"ID":\n\ + bcs,a LFLGNC"ID"\n\ + or %0,1,%0\n\ +LFLGNC"ID":\n\ + bvs,a LFLGNV"ID"\n\ + or %0,2,%0\n\ +LFLGNV"ID":\n\ + bneg,a LFLGNN"ID"\n\ + or %0,8,%0\n\ +LFLGNN"ID":\n\ + mov %%g2,%1" \ + : "=r"(ms_flags), "=r"(ms_saveret)); + +/* On sparc MACHINE_STATE_RESTORE restores the psw register from memory. + The psw register can be written in supervisor mode only, + which is true even for simple condition codes. + We use some combination of instructions to produce the + proper condition codes, but some flag combinations can not + be generated in this way. If this happens an unimplemented + instruction will be executed to abort the program. */ + +#define MACHINE_STATE_RESTORE(ID) \ +{ extern char flgtab[] __asm__("LFLGTAB"ID); \ + int scratch; \ + asm volatile ( \ + "jmpl %2+%1,%%g0\n\ + ! Do part of VC in the delay slot here, as it needs 3 insns.\n\ + addcc 2,%3,%%g0\n\ +LFLGTAB" ID ":\n\ + ! 0\n\ + ba LFLGRET"ID"\n\ + orcc 1,%%g0,%%g0\n\ + ! C\n\ + ba LFLGRET"ID"\n\ + addcc 2,%3,%%g0\n\ + ! V\n\ + unimp\n\ + nop\n\ + ! VC\n\ + ba LFLGRET"ID"\n\ + addxcc %4,%4,%0\n\ + ! Z\n\ + ba LFLGRET"ID"\n\ + subcc %%g0,%%g0,%%g0\n\ + ! ZC\n\ + ba LFLGRET"ID"\n\ + addcc 1,%3,%0\n\ + ! ZVC\n\ + ba LFLGRET"ID"\n\ + addcc %4,%4,%0\n\ + ! N\n\ + ba LFLGRET"ID"\n\ + orcc %%g0,-1,%%g0\n\ + ! NC\n\ + ba LFLGRET"ID"\n\ + addcc %%g0,%3,%%g0\n\ + ! NV\n\ + unimp\n\ + nop\n\ + ! NVC\n\ + unimp\n\ + nop\n\ + ! NZ\n\ + unimp\n\ + nop\n\ + ! NZC\n\ + unimp\n\ + nop\n\ + ! NZV\n\ + unimp\n\ + nop\n\ + ! NZVC\n\ + unimp\n\ + nop\n\ +LFLGRET"ID":\n\ + mov %5,%%g2" \ + : "=r"(scratch) \ + : "r"(ms_flags*8), "r"(flgtab), "r"(-1), \ + "r"(0x80000000), "r"(ms_saveret) \ + : "cc", "%g2"); } + +/* 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 current_function_calls_alloca; +extern int current_function_outgoing_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) \ + (TARGET_FLAT ? sparc_flat_output_function_epilogue (FILE, (int)SIZE) \ + : output_function_epilogue (FILE, (int)SIZE, leaf_function)) + +#define DELAY_SLOTS_FOR_EPILOGUE \ + (TARGET_FLAT ? sparc_flat_epilogue_delay_slots () : 1) +#define ELIGIBLE_FOR_EPILOGUE_DELAY(trial, slots_filled) \ + (TARGET_FLAT ? sparc_flat_eligible_for_epilogue_delay (trial, slots_filled) \ + : eligible_for_epilogue_delay (trial, slots_filled)) + +/* Define registers used by the epilogue and return instruction. */ +#define EPILOGUE_USES(REGNO) \ + (!TARGET_FLAT && REGNO == 31) + +/* Length in units of the trampoline for entering a nested function. */ + +#define TRAMPOLINE_SIZE (TARGET_ARCH64 ? 32 : 16) + +#define TRAMPOLINE_ALIGNMENT 128 /* 16 bytes */ + +/* 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. */ + +void sparc_initialize_trampoline (); +void sparc64_initialize_trampoline (); +#define INITIALIZE_TRAMPOLINE(TRAMP, FNADDR, CXT) \ + if (TARGET_ARCH64) \ + sparc64_initialize_trampoline (TRAMP, FNADDR, CXT); \ + else \ + sparc_initialize_trampoline (TRAMP, FNADDR, CXT) + +/* Generate necessary RTL for __builtin_saveregs(). + ARGLIST is the argument list; see expr.c. */ + +extern struct rtx_def *sparc_builtin_saveregs (); +#define EXPAND_BUILTIN_SAVEREGS(ARGLIST) sparc_builtin_saveregs (ARGLIST) + +/* Define this macro if the location where a function argument is passed + depends on whether or not it is a named argument. + + This macro controls how the NAMED argument to FUNCTION_ARG + is set for varargs and stdarg functions. With this macro defined, + the NAMED argument is always true for named arguments, and false for + unnamed arguments. If this is not defined, but SETUP_INCOMING_VARARGS + is defined, then all arguments are treated as named. Otherwise, all named + arguments except the last are treated as named. + For the v9 we want NAMED to mean what it says it means. */ + +#define STRICT_ARGUMENT_NAMING TARGET_V9 + +/* Generate RTL to flush the register windows so as to make arbitrary frames + available. */ +#define SETUP_FRAME_ADDRESSES() \ + emit_insn (gen_flush_register_windows ()) + +/* Given an rtx for the address of a frame, + return an rtx for the address of the word in the frame + that holds the dynamic chain--the previous frame's address. + ??? -mflat support? */ +#define DYNAMIC_CHAIN_ADDRESS(frame) \ + gen_rtx_PLUS (Pmode, frame, GEN_INT (14 * UNITS_PER_WORD)) + +/* The return address isn't on the stack, it is in a register, so we can't + access it from the current frame pointer. We can access it from the + previous frame pointer though by reading a value from the register window + save area. */ +#define RETURN_ADDR_IN_PREVIOUS_FRAME + +/* This is the offset of the return address to the true next instruction to be + executed for the current function. */ +#define RETURN_ADDR_OFFSET \ + (8 + 4 * (! TARGET_ARCH64 && current_function_returns_struct)) + +/* The current return address is in %i7. The return address of anything + farther back is in the register window save area at [%fp+60]. */ +/* ??? This ignores the fact that the actual return address is +8 for normal + returns, and +12 for structure returns. */ +#define RETURN_ADDR_RTX(count, frame) \ + ((count == -1) \ + ? gen_rtx_REG (Pmode, 31) \ + : gen_rtx_MEM (Pmode, \ + memory_address (Pmode, plus_constant (frame, 15 * UNITS_PER_WORD)))) + +/* Before the prologue, the return address is %o7 + 8. OK, sometimes it's + +12, but always using +8 is close enough for frame unwind purposes. + Actually, just using %o7 is close enough for unwinding, but %o7+8 + is something you can return to. */ +#define INCOMING_RETURN_ADDR_RTX \ + gen_rtx_PLUS (word_mode, gen_rtx_REG (word_mode, 15), GEN_INT (8)) + +/* The offset from the incoming value of %sp to the top of the stack frame + for the current function. On sparc64, we have to account for the stack + bias if present. */ +#define INCOMING_FRAME_SP_OFFSET SPARC_STACK_BIAS + +#define DOESNT_NEED_UNWINDER (! TARGET_FLAT) + +/* Addressing modes, and classification of registers for them. */ + +/* #define HAVE_POST_INCREMENT 0 */ +/* #define HAVE_POST_DECREMENT 0 */ + +/* #define HAVE_PRE_DECREMENT 0 */ +/* #define HAVE_PRE_INCREMENT 0 */ + +/* 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) < 32 || (unsigned) reg_renumber[REGNO] < (unsigned)32) +#define REGNO_OK_FOR_BASE_P(REGNO) \ +((REGNO) < 32 || (unsigned) reg_renumber[REGNO] < (unsigned)32) +#define REGNO_OK_FOR_FP_P(REGNO) \ + (((unsigned) (REGNO) - 32 < (TARGET_V9 ? (unsigned)64 : (unsigned)32)) \ + || ((unsigned) reg_renumber[REGNO] - 32 < (TARGET_V9 ? (unsigned)64 : (unsigned)32))) +#define REGNO_OK_FOR_CCFP_P(REGNO) \ + (TARGET_V9 \ + && (((unsigned) (REGNO) - 96 < (unsigned)4) \ + || ((unsigned) reg_renumber[REGNO] - 96 < (unsigned)4))) + +/* 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 SPARC, 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))) + +/* Is X, a REG, an in or global register? i.e. is regno 0..7 or 24..31 */ +#define IN_OR_GLOBAL_P(X) (REGNO (X) < 8 || (REGNO (X) >= 24 && REGNO (X) <= 31)) + +/* 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. + When PIC, we do not accept an address that would require a scratch reg + to load into a register. */ + +#define CONSTANT_ADDRESS_P(X) \ + (GET_CODE (X) == LABEL_REF || GET_CODE (X) == SYMBOL_REF \ + || GET_CODE (X) == CONST_INT || GET_CODE (X) == HIGH \ + || (GET_CODE (X) == CONST \ + && ! (flag_pic && pic_address_needs_scratch (X)))) + +/* Define this, so that when PIC, reload won't try to reload invalid + addresses which require two reload registers. */ + +#define LEGITIMATE_PIC_OPERAND_P(X) (! pic_address_needs_scratch (X)) + +/* Nonzero if the constant value X is a legitimate general operand. + Anything can be made to work except floating point constants. + If TARGET_VIS, 0.0 can be made to work as well. */ + +#define LEGITIMATE_CONSTANT_P(X) \ + (GET_CODE (X) != CONST_DOUBLE || GET_MODE (X) == VOIDmode || \ + (TARGET_VIS && (GET_MODE (X) == SFmode || GET_MODE (X) == DFmode) && \ + fp_zero_operand (X))) + +/* 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. */ + +/* Optional extra constraints for this machine. + + 'T' handles memory addresses where the alignment is known to + be at least 8 bytes. + + `U' handles all pseudo registers or a hard even numbered + integer register, needed for ldd/std instructions. */ + +#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) \ + (((unsigned) REGNO (X)) - 32 >= (FIRST_PSEUDO_REGISTER - 32)) +/* 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) \ + (((unsigned) REGNO (X)) - 32 >= (FIRST_PSEUDO_REGISTER - 32)) + +/* 'T', 'U' are for aligned memory loads which aren't needed for v9. */ + +#define EXTRA_CONSTRAINT(OP, C) \ + ((! TARGET_ARCH64 && (C) == 'T') \ + ? (mem_min_alignment (OP, 8)) \ + : ((! TARGET_ARCH64 && (C) == 'U') \ + ? (register_ok_for_ldd (OP)) \ + : 0)) + +#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)) + +#define EXTRA_CONSTRAINT(OP, C) \ + ((! TARGET_ARCH64 && (C) == 'T') \ + ? mem_min_alignment (OP, 8) && strict_memory_address_p (Pmode, XEXP (OP, 0)) \ + : ((! TARGET_ARCH64 && (C) == 'U') \ + ? (GET_CODE (OP) == REG \ + && (REGNO (OP) < FIRST_PSEUDO_REGISTER \ + || reg_renumber[REGNO (OP)] >= 0) \ + && register_ok_for_ldd (OP)) \ + : 0)) +#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 SPARC, the actual legitimate addresses must be REG+REG or REG+SMALLINT + ordinarily. This changes a bit when generating PIC. + + If you change this, execute "rm explow.o recog.o reload.o". */ + +#define RTX_OK_FOR_BASE_P(X) \ + ((GET_CODE (X) == REG && REG_OK_FOR_BASE_P (X)) \ + || (GET_CODE (X) == SUBREG \ + && GET_CODE (SUBREG_REG (X)) == REG \ + && REG_OK_FOR_BASE_P (SUBREG_REG (X)))) + +#define RTX_OK_FOR_INDEX_P(X) \ + ((GET_CODE (X) == REG && REG_OK_FOR_INDEX_P (X)) \ + || (GET_CODE (X) == SUBREG \ + && GET_CODE (SUBREG_REG (X)) == REG \ + && REG_OK_FOR_INDEX_P (SUBREG_REG (X)))) + +#define RTX_OK_FOR_OFFSET_P(X) \ + (GET_CODE (X) == CONST_INT && INTVAL (X) >= -0x1000 && INTVAL (X) < 0x1000) + +#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \ +{ if (RTX_OK_FOR_BASE_P (X)) \ + goto ADDR; \ + else if (GET_CODE (X) == PLUS) \ + { \ + register rtx op0 = XEXP (X, 0); \ + register rtx op1 = XEXP (X, 1); \ + if (flag_pic && op0 == pic_offset_table_rtx) \ + { \ + if (RTX_OK_FOR_BASE_P (op1)) \ + goto ADDR; \ + else if (flag_pic == 1 \ + && GET_CODE (op1) != REG \ + && GET_CODE (op1) != LO_SUM \ + && GET_CODE (op1) != MEM \ + && (GET_CODE (op1) != CONST_INT \ + || SMALL_INT (op1))) \ + goto ADDR; \ + } \ + else if (RTX_OK_FOR_BASE_P (op0)) \ + { \ + if (RTX_OK_FOR_INDEX_P (op1) \ + /* We prohibit REG + REG for TFmode when \ + there are no instructions which accept \ + REG+REG instructions. We make this \ + because REG+REG is not offsetable \ + address and we could get the situation \ + in reload when source and destination \ + of a movtf pattern are MEMs with \ + REG+REG address and only one of them \ + gets converted to an offsetable \ + address. */ \ + && (MODE != TFmode \ + || (TARGET_FPU && TARGET_ARCH64 \ + && TARGET_V9 && TARGET_HARD_QUAD))\ + || RTX_OK_FOR_OFFSET_P (op1)) \ + goto ADDR; \ + } \ + else if (RTX_OK_FOR_BASE_P (op1)) \ + { \ + if (RTX_OK_FOR_INDEX_P (op0) \ + /* See the previous comment. */ \ + && (MODE != TFmode \ + || (TARGET_FPU && TARGET_ARCH64 \ + && TARGET_V9 && TARGET_HARD_QUAD))\ + || RTX_OK_FOR_OFFSET_P (op0)) \ + goto ADDR; \ + } \ + } \ + else if (GET_CODE (X) == LO_SUM) \ + { \ + register rtx op0 = XEXP (X, 0); \ + register rtx op1 = XEXP (X, 1); \ + if (RTX_OK_FOR_BASE_P (op0) \ + && CONSTANT_P (op1) \ + /* We can't allow TFmode, because an offset \ + greater than or equal to the alignment (8) \ + may cause the LO_SUM to overflow if !v9. */\ + && (MODE != TFmode || TARGET_V9)) \ + goto ADDR; \ + } \ + else if (GET_CODE (X) == CONST_INT && SMALL_INT (X)) \ + goto ADDR; \ +} + +/* 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. */ + +/* On SPARC, change REG+N into REG+REG, and REG+(X*Y) into REG+REG. */ +extern struct rtx_def *legitimize_pic_address (); +#define LEGITIMIZE_ADDRESS(X,OLDX,MODE,WIN) \ +{ rtx sparc_x = (X); \ + if (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 0)) == MULT) \ + (X) = gen_rtx_PLUS (Pmode, XEXP (X, 1), \ + force_operand (XEXP (X, 0), NULL_RTX)); \ + if (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == MULT) \ + (X) = gen_rtx_PLUS (Pmode, XEXP (X, 0), \ + force_operand (XEXP (X, 1), NULL_RTX)); \ + if (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 0)) == PLUS) \ + (X) = gen_rtx_PLUS (Pmode, force_operand (XEXP (X, 0), NULL_RTX),\ + XEXP (X, 1)); \ + if (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == PLUS) \ + (X) = gen_rtx_PLUS (Pmode, XEXP (X, 0), \ + force_operand (XEXP (X, 1), NULL_RTX)); \ + if (sparc_x != (X) && memory_address_p (MODE, X)) \ + goto WIN; \ + if (flag_pic) (X) = legitimize_pic_address (X, MODE, 0); \ + else if (GET_CODE (X) == PLUS && CONSTANT_ADDRESS_P (XEXP (X, 1))) \ + (X) = gen_rtx_PLUS (Pmode, XEXP (X, 0), \ + copy_to_mode_reg (Pmode, XEXP (X, 1))); \ + else if (GET_CODE (X) == PLUS && CONSTANT_ADDRESS_P (XEXP (X, 0))) \ + (X) = gen_rtx_PLUS (Pmode, XEXP (X, 1), \ + copy_to_mode_reg (Pmode, XEXP (X, 0))); \ + else if (GET_CODE (X) == SYMBOL_REF || GET_CODE (X) == CONST \ + || GET_CODE (X) == LABEL_REF) \ + (X) = copy_to_suggested_reg (X, NULL_RTX, Pmode); \ + if (memory_address_p (MODE, X)) \ + goto WIN; } + +/* Try a machine-dependent way of reloading an illegitimate address + operand. If we find one, push the reload and jump to WIN. This + macro is used in only one place: `find_reloads_address' in reload.c. + + For Sparc 32, we wish to handle addresses by splitting them into + HIGH+LO_SUM pairs, retaining the LO_SUM in the memory reference. + This cuts the number of extra insns by one. */ + +#define LEGITIMIZE_RELOAD_ADDRESS(X,MODE,OPNUM,TYPE,IND_LEVELS,WIN) \ +do { \ + /* Decompose SImode constants into hi+lo_sum. We do have to \ + rerecognize what we produce, so be careful. */ \ + if (CONSTANT_P (X) \ + && (MODE != TFmode || TARGET_V9) \ + && GET_MODE (X) == SImode \ + && GET_CODE (X) != LO_SUM && GET_CODE (X) != HIGH) \ + { \ + X = gen_rtx_LO_SUM (GET_MODE (X), \ + gen_rtx_HIGH (GET_MODE (X), X), X); \ + push_reload (XEXP (X, 0), NULL_RTX, &XEXP (X, 0), NULL_PTR, \ + BASE_REG_CLASS, GET_MODE (X), VOIDmode, 0, 0, \ + OPNUM, TYPE); \ + goto WIN; \ + } \ + /* ??? 64-bit reloads. */ \ +} while (0) + +/* Go to LABEL if ADDR (a legitimate address expression) + has an effect that depends on the machine mode it is used for. + On the SPARC this is never true. */ + +#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR,LABEL) + +/* If we are referencing a function make the SYMBOL_REF special. + In the Embedded Medium/Anywhere code model, %g4 points to the data segment + so we must not add it to function addresses. */ + +#define ENCODE_SECTION_INFO(DECL) \ + do { \ + if (TARGET_CM_EMBMEDANY && TREE_CODE (DECL) == FUNCTION_DECL) \ + SYMBOL_REF_FLAG (XEXP (DECL_RTL (DECL), 0)) = 1; \ + } while (0) + +/* Specify the machine mode that this machine uses + for the index in the tablejump instruction. */ +/* If we ever implement any of the full models (such as CM_FULLANY), + this has to be DImode in that case */ +#ifdef HAVE_GAS_SUBSECTION_ORDERING +#define CASE_VECTOR_MODE \ +(! TARGET_PTR64 ? SImode : flag_pic ? SImode : TARGET_CM_MEDLOW ? SImode : DImode) +#else +/* If assembler does not have working .subsection -1, we use DImode for pic, as otherwise + we have to sign extend which slows things down. */ +#define CASE_VECTOR_MODE \ +(! TARGET_PTR64 ? SImode : flag_pic ? DImode : TARGET_CM_MEDLOW ? SImode : DImode) +#endif + +/* Define as C expression which evaluates to nonzero if the tablejump + instruction expects the table to contain offsets from the address of the + table. + Do not define this if the table should contain absolute addresses. */ +/* #define CASE_VECTOR_PC_RELATIVE 1 */ + +/* 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 + +#if 0 /* Sun 4 has matherr, so this is no good. */ +/* This is the value of the error code EDOM for this machine, + used by the sqrt instruction. */ +#define TARGET_EDOM 33 + +/* This is how to refer to the variable errno. */ +#define GEN_ERRNO_RTX \ + gen_rtx_MEM (SImode, gen_rtx_SYMBOL_REF (Pmode, "errno")) +#endif /* 0 */ + +/* 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. + For RISC chips, it means that access to memory by bytes is no + better than access by words when possible, so grab a whole word + and maybe make use of that. */ +#define SLOW_BYTE_ACCESS 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 + +/* Define this to be nonzero if shift instructions ignore all but the low-order + few bits. */ +#define SHIFT_COUNT_TRUNCATED 1 + +/* 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 + +/* 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 (TARGET_PTR64 ? DImode : SImode) + +/* Generate calls to memcpy, memcmp and memset. */ +#define TARGET_MEM_FUNCTIONS + +/* Add any extra modes needed to represent the condition code. + + On the Sparc, we have a "no-overflow" mode which is used when an add or + subtract insn is used to set the condition code. Different branches are + used in this case for some operations. + + We also have two modes to indicate that the relevant condition code is + in the floating-point condition code register. One for comparisons which + will generate an exception if the result is unordered (CCFPEmode) and + one for comparisons which will never trap (CCFPmode). + + CCXmode and CCX_NOOVmode are only used by v9. */ + +#define EXTRA_CC_MODES CCXmode, CC_NOOVmode, CCX_NOOVmode, CCFPmode, CCFPEmode + +/* Define the names for the modes specified above. */ + +#define EXTRA_CC_NAMES "CCX", "CC_NOOV", "CCX_NOOV", "CCFP", "CCFPE" + +/* 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, + CCFP[E]mode is used. CC_NOOVmode should be used when the first operand is a + PLUS, MINUS, NEG, or ASHIFT. 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 \ + ? ((OP == EQ || OP == NE) ? CCFPmode : CCFPEmode) \ + : ((GET_CODE (X) == PLUS || GET_CODE (X) == MINUS \ + || GET_CODE (X) == NEG || GET_CODE (X) == ASHIFT) \ + ? (TARGET_ARCH64 && GET_MODE (X) == DImode ? CCX_NOOVmode : CC_NOOVmode) \ + : ((TARGET_ARCH64 || TARGET_V8PLUS) && GET_MODE (X) == DImode ? CCXmode : CCmode))) + +/* Return non-zero if SELECT_CC_MODE will never return MODE for a + floating point inequality comparison. */ + +#define REVERSIBLE_CC_MODE(MODE) ((MODE) != CCFPEmode) + +/* 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 + +/* alloca should avoid clobbering the old register save area. */ +#define SETJMP_VIA_SAVE_AREA + +/* Define subroutines to call to handle multiply and divide. + Use the subroutines that Sun's library provides. + The `*' prevents an underscore from being prepended by the compiler. */ + +#define DIVSI3_LIBCALL "*.div" +#define UDIVSI3_LIBCALL "*.udiv" +#define MODSI3_LIBCALL "*.rem" +#define UMODSI3_LIBCALL "*.urem" +/* .umul is a little faster than .mul. */ +#define MULSI3_LIBCALL "*.umul" + +/* Define library calls for quad FP operations. These are all part of the + SPARC ABI. + ??? ARCH64 still does not work as the _Qp_* routines take pointers. */ +#define ADDTF3_LIBCALL (TARGET_ARCH64 ? "_Qp_add" : "_Q_add") +#define SUBTF3_LIBCALL (TARGET_ARCH64 ? "_Qp_sub" : "_Q_sub") +#define NEGTF2_LIBCALL (TARGET_ARCH64 ? "_Qp_neg" : "_Q_neg") +#define MULTF3_LIBCALL (TARGET_ARCH64 ? "_Qp_mul" : "_Q_mul") +#define DIVTF3_LIBCALL (TARGET_ARCH64 ? "_Qp_div" : "_Q_div") +#define FLOATSITF2_LIBCALL (TARGET_ARCH64 ? "_Qp_itoq" : "_Q_itoq") +#define FIX_TRUNCTFSI2_LIBCALL (TARGET_ARCH64 ? "_Qp_qtoi" : "_Q_qtoi") +#define FIXUNS_TRUNCTFSI2_LIBCALL (TARGET_ARCH64 ? "_Qp_qtoui" : "_Q_qtou") +#define EXTENDSFTF2_LIBCALL (TARGET_ARCH64 ? "_Qp_stoq" : "_Q_stoq") +#define TRUNCTFSF2_LIBCALL (TARGET_ARCH64 ? "_Qp_qtos" : "_Q_qtos") +#define EXTENDDFTF2_LIBCALL (TARGET_ARCH64 ? "_Qp_dtoq" : "_Q_dtoq") +#define TRUNCTFDF2_LIBCALL (TARGET_ARCH64 ? "_Qp_qtod" : "_Q_qtod") +#define EQTF2_LIBCALL (TARGET_ARCH64 ? "_Qp_feq" : "_Q_feq") +#define NETF2_LIBCALL (TARGET_ARCH64 ? "_Qp_fne" : "_Q_fne") +#define GTTF2_LIBCALL (TARGET_ARCH64 ? "_Qp_fgt" : "_Q_fgt") +#define GETF2_LIBCALL (TARGET_ARCH64 ? "_Qp_fge" : "_Q_fge") +#define LTTF2_LIBCALL (TARGET_ARCH64 ? "_Qp_flt" : "_Q_flt") +#define LETF2_LIBCALL (TARGET_ARCH64 ? "_Qp_fle" : "_Q_fle") + +/* We can define the TFmode sqrt optab only if TARGET_FPU. This is because + with soft-float, the SFmode and DFmode sqrt instructions will be absent, + and the compiler will notice and try to use the TFmode sqrt instruction + for calls to the builtin function sqrt, but this fails. */ +#define INIT_TARGET_OPTABS \ + do { \ + add_optab->handlers[(int) TFmode].libfunc \ + = gen_rtx_SYMBOL_REF (Pmode, ADDTF3_LIBCALL); \ + sub_optab->handlers[(int) TFmode].libfunc \ + = gen_rtx_SYMBOL_REF (Pmode, SUBTF3_LIBCALL); \ + neg_optab->handlers[(int) TFmode].libfunc \ + = gen_rtx_SYMBOL_REF (Pmode, NEGTF2_LIBCALL); \ + smul_optab->handlers[(int) TFmode].libfunc \ + = gen_rtx_SYMBOL_REF (Pmode, MULTF3_LIBCALL); \ + flodiv_optab->handlers[(int) TFmode].libfunc \ + = gen_rtx_SYMBOL_REF (Pmode, DIVTF3_LIBCALL); \ + eqtf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, EQTF2_LIBCALL); \ + netf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, NETF2_LIBCALL); \ + gttf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, GTTF2_LIBCALL); \ + getf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, GETF2_LIBCALL); \ + lttf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, LTTF2_LIBCALL); \ + letf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, LETF2_LIBCALL); \ + trunctfsf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, TRUNCTFSF2_LIBCALL); \ + trunctfdf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, TRUNCTFDF2_LIBCALL); \ + extendsftf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, EXTENDSFTF2_LIBCALL); \ + extenddftf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, EXTENDDFTF2_LIBCALL); \ + floatsitf_libfunc = gen_rtx_SYMBOL_REF (Pmode, FLOATSITF2_LIBCALL); \ + fixtfsi_libfunc = gen_rtx_SYMBOL_REF (Pmode, FIX_TRUNCTFSI2_LIBCALL); \ + fixunstfsi_libfunc \ + = gen_rtx_SYMBOL_REF (Pmode, FIXUNS_TRUNCTFSI2_LIBCALL); \ + if (TARGET_FPU) \ + sqrt_optab->handlers[(int) TFmode].libfunc \ + = gen_rtx_SYMBOL_REF (Pmode, "_Q_sqrt"); \ + INIT_SUBTARGET_OPTABS; \ + } while (0) + +/* This is meant to be redefined in the host dependent files */ +#define INIT_SUBTARGET_OPTABS + +/* 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) < 0x1000 && INTVAL (RTX) >= -0x1000) \ + return 0; \ + case HIGH: \ + return 2; \ + case CONST: \ + case LABEL_REF: \ + case SYMBOL_REF: \ + return 4; \ + case CONST_DOUBLE: \ + if (GET_MODE (RTX) == DImode) \ + if ((XINT (RTX, 3) == 0 \ + && (unsigned) XINT (RTX, 2) < 0x1000) \ + || (XINT (RTX, 3) == -1 \ + && XINT (RTX, 2) < 0 \ + && XINT (RTX, 2) >= -0x1000)) \ + return 0; \ + return 8; + +#define ADDRESS_COST(RTX) 1 + +/* Compute extra cost of moving data between one register class + and another. */ +#define GENERAL_OR_I64(C) ((C) == GENERAL_REGS || (C) == I64_REGS) +#define REGISTER_MOVE_COST(CLASS1, CLASS2) \ + (((FP_REG_CLASS_P (CLASS1) && GENERAL_OR_I64 (CLASS2)) \ + || (GENERAL_OR_I64 (CLASS1) && FP_REG_CLASS_P (CLASS2)) \ + || (CLASS1) == FPCC_REGS || (CLASS2) == FPCC_REGS) \ + ? (sparc_cpu == PROCESSOR_ULTRASPARC ? 12 : 6) : 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. + + If we need more than 12 insns to do a multiply, then go out-of-line, + since the call overhead will be < 10% of the cost of the multiply. */ + +#define RTX_COSTS(X,CODE,OUTER_CODE) \ + case MULT: \ + if (sparc_cpu == PROCESSOR_ULTRASPARC) \ + return (GET_MODE (X) == DImode ? \ + COSTS_N_INSNS (34) : COSTS_N_INSNS (19)); \ + return TARGET_HARD_MUL ? COSTS_N_INSNS (5) : COSTS_N_INSNS (25); \ + case DIV: \ + case UDIV: \ + case MOD: \ + case UMOD: \ + if (sparc_cpu == PROCESSOR_ULTRASPARC) \ + return (GET_MODE (X) == DImode ? \ + COSTS_N_INSNS (68) : COSTS_N_INSNS (37)); \ + return COSTS_N_INSNS (25); \ + /* Make FLOAT and FIX more expensive than CONST_DOUBLE,\ + so that cse will favor the latter. */ \ + case FLOAT: \ + case FIX: \ + return 19; + +#define ISSUE_RATE sparc_issue_rate() + +/* Adjust the cost of dependencies. */ +#define ADJUST_COST(INSN,LINK,DEP,COST) \ + sparc_adjust_cost(INSN, LINK, DEP, COST) + +extern void ultrasparc_sched_reorder (); +extern void ultrasparc_sched_init (); +extern int ultrasparc_variable_issue (); + +#define MD_SCHED_INIT(DUMP, SCHED_VERBOSE) \ + if (sparc_cpu == PROCESSOR_ULTRASPARC) \ + ultrasparc_sched_init (DUMP, SCHED_VERBOSE) + +#define MD_SCHED_REORDER(DUMP, SCHED_VERBOSE, READY, N_READY) \ + if (sparc_cpu == PROCESSOR_ULTRASPARC) \ + ultrasparc_sched_reorder (DUMP, SCHED_VERBOSE, READY, N_READY) + +#define MD_SCHED_VARIABLE_ISSUE(DUMP, SCHED_VERBOSE, INSN, CAN_ISSUE_MORE) \ + if (sparc_cpu == PROCESSOR_ULTRASPARC) \ + (CAN_ISSUE_MORE) = ultrasparc_variable_issue (INSN); \ + else \ + (CAN_ISSUE_MORE)-- + +/* Conditional branches with empty delay slots have a length of two. */ +#define ADJUST_INSN_LENGTH(INSN, LENGTH) \ + if (GET_CODE (INSN) == CALL_INSN \ + || (GET_CODE (INSN) == JUMP_INSN && ! simplejump_p (insn))) \ + LENGTH += 1; else + +/* Control the assembler format that we output. */ + +/* Output at beginning of assembler file. */ + +#define ASM_FILE_START(file) + +/* A C string constant describing how to begin a comment in the target + assembler language. The compiler assumes that the comment will end at + the end of the line. */ + +#define ASM_COMMENT_START "!" + +/* 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 "" + +/* ??? Try to make the style consistent here (_OP?). */ + +#define ASM_LONGLONG ".xword" +#define ASM_LONG ".word" +#define ASM_SHORT ".half" +#define ASM_BYTE_OP ".byte" +#define ASM_FLOAT ".single" +#define ASM_DOUBLE ".double" +#define ASM_LONGDOUBLE ".xxx" /* ??? Not known (or used yet). */ + +/* Output before read-only data. */ + +#define TEXT_SECTION_ASM_OP ".text" + +/* Output before writable data. */ + +#define DATA_SECTION_ASM_OP ".data" + +/* How to refer to registers in assembler output. + This sequence is indexed by compiler's hard-register-number (see above). */ + +#define REGISTER_NAMES \ +{"%g0", "%g1", "%g2", "%g3", "%g4", "%g5", "%g6", "%g7", \ + "%o0", "%o1", "%o2", "%o3", "%o4", "%o5", "%sp", "%o7", \ + "%l0", "%l1", "%l2", "%l3", "%l4", "%l5", "%l6", "%l7", \ + "%i0", "%i1", "%i2", "%i3", "%i4", "%i5", "%fp", "%i7", \ + "%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7", \ + "%f8", "%f9", "%f10", "%f11", "%f12", "%f13", "%f14", "%f15", \ + "%f16", "%f17", "%f18", "%f19", "%f20", "%f21", "%f22", "%f23", \ + "%f24", "%f25", "%f26", "%f27", "%f28", "%f29", "%f30", "%f31", \ + "%f32", "%f33", "%f34", "%f35", "%f36", "%f37", "%f38", "%f39", \ + "%f40", "%f41", "%f42", "%f43", "%f44", "%f45", "%f46", "%f47", \ + "%f48", "%f49", "%f50", "%f51", "%f52", "%f53", "%f54", "%f55", \ + "%f56", "%f57", "%f58", "%f59", "%f60", "%f61", "%f62", "%f63", \ + "%fcc0", "%fcc1", "%fcc2", "%fcc3", "%icc"} + +/* Define additional names for use in asm clobbers and asm declarations. */ + +#define ADDITIONAL_REGISTER_NAMES \ +{{"ccr", SPARC_ICC_REG}, {"cc", SPARC_ICC_REG}} + +/* How to renumber registers for dbx and gdb. In the flat model, the frame + pointer is really %i7. */ + +#define DBX_REGISTER_NUMBER(REGNO) \ + (TARGET_FLAT && REGNO == FRAME_POINTER_REGNUM ? 31 : REGNO) + +/* On Sun 4, this limit is 2048. We use 1000 to be safe, since the length + can run past this up to a continuation point. Once we used 1500, but + a single entry in C++ can run more than 500 bytes, due to the length of + mangled symbol names. dbxout.c should really be fixed to do + continuations when they are actually needed instead of trying to + guess... */ +#define DBX_CONTIN_LENGTH 1000 + +/* This is how to output a note to DBX telling it the line number + to which the following sequence of instructions corresponds. + + This is needed for SunOS 4.0, and should not hurt for 3.2 + versions either. */ +#define ASM_OUTPUT_SOURCE_LINE(file, line) \ + { static int sym_lineno = 1; \ + fprintf (file, ".stabn 68,0,%d,LM%d\nLM%d:\n", \ + line, sym_lineno, sym_lineno); \ + sym_lineno += 1; } + +/* 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); fputs (":\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. */ + +#define ASM_GLOBALIZE_LABEL(FILE,NAME) \ + do { fputs ("\t.global ", FILE); assemble_name (FILE, NAME); fputs ("\n", FILE);} while (0) + +/* The prefix to add to user-visible assembler symbols. */ + +#define USER_LABEL_PREFIX "_" + +/* This is how to output a definition of 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, "%s%d:\n", PREFIX, 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), "*%s%ld", (PREFIX), (long)(NUM)) + +/* This is how to output an assembler line defining a `float' constant. + We always have to use a .long pseudo-op to do this because the native + SVR4 ELF assembler is buggy and it generates incorrect values when we + try to use the .float pseudo-op instead. */ + +#define ASM_OUTPUT_FLOAT(FILE,VALUE) \ + { \ + long t; \ + char str[30]; \ + REAL_VALUE_TO_TARGET_SINGLE ((VALUE), t); \ + REAL_VALUE_TO_DECIMAL ((VALUE), "%.20e", str); \ + fprintf (FILE, "\t%s\t0x%lx %s ~%s\n", ASM_LONG, t, \ + ASM_COMMENT_START, str); \ + } \ + +/* This is how to output an assembler line defining a `double' constant. + We always have to use a .long pseudo-op to do this because the native + SVR4 ELF assembler is buggy and it generates incorrect values when we + try to use the .float pseudo-op instead. */ + +#define ASM_OUTPUT_DOUBLE(FILE,VALUE) \ + { \ + long t[2]; \ + char str[30]; \ + REAL_VALUE_TO_TARGET_DOUBLE ((VALUE), t); \ + REAL_VALUE_TO_DECIMAL ((VALUE), "%.20e", str); \ + fprintf (FILE, "\t%s\t0x%lx %s ~%s\n", ASM_LONG, t[0], \ + ASM_COMMENT_START, str); \ + fprintf (FILE, "\t%s\t0x%lx\n", ASM_LONG, t[1]); \ + } + +/* This is how to output an assembler line defining a `long double' + constant. */ + +#define ASM_OUTPUT_LONG_DOUBLE(FILE,VALUE) \ + { \ + long t[4]; \ + char str[30]; \ + REAL_VALUE_TO_TARGET_LONG_DOUBLE ((VALUE), t); \ + REAL_VALUE_TO_DECIMAL ((VALUE), "%.20e", str); \ + fprintf (FILE, "\t%s\t0x%lx %s ~%s\n", ASM_LONG, t[0], \ + ASM_COMMENT_START, str); \ + fprintf (FILE, "\t%s\t0x%lx\n", ASM_LONG, t[1]); \ + fprintf (FILE, "\t%s\t0x%lx\n", ASM_LONG, t[2]); \ + fprintf (FILE, "\t%s\t0x%lx\n", ASM_LONG, t[3]); \ + } + +/* This is how to output an assembler line defining an `int' constant. */ + +#define ASM_OUTPUT_INT(FILE,VALUE) \ +( fprintf (FILE, "\t%s\t", ASM_LONG), \ + output_addr_const (FILE, (VALUE)), \ + fprintf (FILE, "\n")) + +/* This is how to output an assembler line defining a DImode constant. */ +#define ASM_OUTPUT_DOUBLE_INT(FILE,VALUE) \ + output_double_int (FILE, VALUE) + +/* Likewise for `char' and `short' constants. */ + +#define ASM_OUTPUT_SHORT(FILE,VALUE) \ +( fprintf (FILE, "\t%s\t", ASM_SHORT), \ + output_addr_const (FILE, (VALUE)), \ + fprintf (FILE, "\n")) + +#define ASM_OUTPUT_CHAR(FILE,VALUE) \ +( fprintf (FILE, "\t%s\t", ASM_BYTE_OP), \ + output_addr_const (FILE, (VALUE)), \ + fprintf (FILE, "\n")) + +/* This is how to output an assembler line for a numeric constant byte. */ + +#define ASM_OUTPUT_BYTE(FILE,VALUE) \ + fprintf (FILE, "\t%s\t0x%x\n", ASM_BYTE_OP, (VALUE)) + +/* This is how we hook in and defer the case-vector until the end of + the function. */ +extern void sparc_defer_case_vector (); + +#define ASM_OUTPUT_ADDR_VEC(LAB,VEC) \ + sparc_defer_case_vector ((LAB),(VEC), 0) + +#define ASM_OUTPUT_ADDR_DIFF_VEC(LAB,VEC) \ + sparc_defer_case_vector ((LAB),(VEC), 1) + +/* This is how to output an element of a case-vector that is absolute. */ + +#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \ +do { \ + char label[30]; \ + ASM_GENERATE_INTERNAL_LABEL (label, "L", VALUE); \ + if (CASE_VECTOR_MODE == SImode) \ + fprintf (FILE, "\t.word\t"); \ + else \ + fprintf (FILE, "\t.xword\t"); \ + assemble_name (FILE, label); \ + fputc ('\n', FILE); \ +} while (0) + +/* This is how to output an element of a case-vector that is relative. + (SPARC uses such vectors only when generating PIC.) */ + +#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \ +do { \ + char label[30]; \ + ASM_GENERATE_INTERNAL_LABEL (label, "L", (VALUE)); \ + if (CASE_VECTOR_MODE == SImode) \ + fprintf (FILE, "\t.word\t"); \ + else \ + fprintf (FILE, "\t.xword\t"); \ + assemble_name (FILE, label); \ + ASM_GENERATE_INTERNAL_LABEL (label, "L", (REL)); \ + fputc ('-', FILE); \ + assemble_name (FILE, label); \ + fputc ('\n', FILE); \ +} while (0) + +/* This is what to output before and after case-vector (both + relative and absolute). If .subsection -1 works, we put case-vectors + at the beginning of the current section. */ + +#ifdef HAVE_GAS_SUBSECTION_ORDERING + +#define ASM_OUTPUT_ADDR_VEC_START(FILE) \ + fprintf(FILE, "\t.subsection\t-1\n") + +#define ASM_OUTPUT_ADDR_VEC_END(FILE) \ + fprintf(FILE, "\t.previous\n") + +#endif + +/* 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) \ + if ((LOG) != 0) \ + fprintf (FILE, "\t.align %d\n", (1<<(LOG))) + +#define LABEL_ALIGN_AFTER_BARRIER(LABEL) (sparc_align_jumps) + +#define LOOP_ALIGN(LABEL) (sparc_align_loops) + +#define ASM_OUTPUT_SKIP(FILE,SIZE) \ + fprintf (FILE, "\t.skip %u\n", (SIZE)) + +/* This says how to output an assembler line + to define a global common symbol. */ + +#define ASM_OUTPUT_COMMON(FILE, NAME, SIZE, ROUNDED) \ +( fputs ("\t.common ", (FILE)), \ + assemble_name ((FILE), (NAME)), \ + fprintf ((FILE), ",%u,\"bss\"\n", (SIZE))) + +/* This says how to output an assembler line to define a local common + symbol. */ + +#define ASM_OUTPUT_ALIGNED_LOCAL(FILE, NAME, SIZE, ALIGNED) \ +( fputs ("\t.reserve ", (FILE)), \ + assemble_name ((FILE), (NAME)), \ + fprintf ((FILE), ",%u,\"bss\",%u\n", \ + (SIZE), ((ALIGNED) / BITS_PER_UNIT))) + +/* A C statement (sans semicolon) to output to the stdio stream + FILE the assembler definition of uninitialized global DECL named + NAME whose size is SIZE bytes and alignment is ALIGN bytes. + Try to use asm_output_aligned_bss to implement this macro. */ + +#define ASM_OUTPUT_ALIGNED_BSS(FILE, DECL, NAME, SIZE, ALIGN) \ + do { \ + fputs (".globl ", (FILE)); \ + assemble_name ((FILE), (NAME)); \ + fputs ("\n", (FILE)); \ + ASM_OUTPUT_ALIGNED_LOCAL (FILE, NAME, SIZE, ALIGN); \ + } while (0) + +/* 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)) + 10), \ + sprintf ((OUTPUT), "%s.%d", (NAME), (LABELNO))) + +#define IDENT_ASM_OP ".ident" + +/* Output #ident as a .ident. */ + +#define ASM_OUTPUT_IDENT(FILE, NAME) \ + fprintf (FILE, "\t%s\t\"%s\"\n", IDENT_ASM_OP, NAME); + +/* Output code to add DELTA to the first argument, and then jump to FUNCTION. + Used for C++ multiple inheritance. */ +#define ASM_OUTPUT_MI_THUNK(FILE, THUNK_FNDECL, DELTA, FUNCTION) \ +do { \ + int big_delta = (DELTA) >= 4096 || (DELTA) < -4096; \ + if (big_delta) \ + fprintf (FILE, "\tset %d,%%g1\n\tadd %%o0,%%g1,%%o0\n", (DELTA)); \ + /* Don't use the jmp solution unless we know the target is local to \ + the application or shared object. \ + XXX: Wimp out and don't actually check anything except if this is \ + an embedded target where we assume there are no shared libs. */ \ + if (!TARGET_CM_EMBMEDANY || flag_pic) \ + { \ + if (! big_delta) \ + fprintf (FILE, "\tadd %%o0,%d,%%o0\n", DELTA); \ + fprintf (FILE, "\tmov %%o7,%%g1\n"); \ + fprintf (FILE, "\tcall "); \ + assemble_name (FILE, XSTR (XEXP (DECL_RTL (FUNCTION), 0), 0)); \ + fprintf (FILE, ",0\n"); \ + } \ + else if (TARGET_CM_EMBMEDANY) \ + { \ + fprintf (FILE, "\tsetx "); \ + assemble_name (FILE, XSTR (XEXP (DECL_RTL (FUNCTION), 0), 0)); \ + fprintf (FILE, ",%%g5,%%g1\n\tjmp %%g1\n"); \ + } \ + else \ + { \ + fprintf (FILE, "\tsethi %%hi("); \ + assemble_name (FILE, XSTR (XEXP (DECL_RTL (FUNCTION), 0), 0)); \ + fprintf (FILE, "),%%g1\n\tjmp %%g1+%%lo("); \ + assemble_name (FILE, XSTR (XEXP (DECL_RTL (FUNCTION), 0), 0)); \ + fprintf (FILE, ")\n"); \ + } \ + if (!TARGET_CM_EMBMEDANY || flag_pic) \ + fprintf (FILE, "\tmov %%g1,%%o7\n"); \ + else if (big_delta) \ + fprintf (FILE, "\tnop\n"); \ + else \ + fprintf (FILE, "\tadd %%o0,%d,%%o0\n", DELTA); \ +} while (0) + +/* Define the parentheses used to group arithmetic operations + in assembler code. */ + +#define ASM_OPEN_PAREN "(" +#define ASM_CLOSE_PAREN ")" + +/* 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) == '(' || (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. */ + +#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 base, index = 0; \ + int offset = 0; \ + register rtx addr = ADDR; \ + if (GET_CODE (addr) == REG) \ + fputs (reg_names[REGNO (addr)], FILE); \ + else if (GET_CODE (addr) == 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 \ + base = XEXP (addr, 0), index = XEXP (addr, 1); \ + fputs (reg_names[REGNO (base)], FILE); \ + if (index == 0) \ + fprintf (FILE, "%+d", offset); \ + else if (GET_CODE (index) == REG) \ + fprintf (FILE, "+%s", reg_names[REGNO (index)]); \ + else if (GET_CODE (index) == SYMBOL_REF \ + || GET_CODE (index) == CONST) \ + fputc ('+', FILE), output_addr_const (FILE, index); \ + else abort (); \ + } \ + else if (GET_CODE (addr) == MINUS \ + && GET_CODE (XEXP (addr, 1)) == LABEL_REF) \ + { \ + output_addr_const (FILE, XEXP (addr, 0)); \ + fputs ("-(", FILE); \ + output_addr_const (FILE, XEXP (addr, 1)); \ + fputs ("-.)", FILE); \ + } \ + else if (GET_CODE (addr) == LO_SUM) \ + { \ + output_operand (XEXP (addr, 0), 0); \ + if (TARGET_CM_MEDMID) \ + fputs ("+%l44(", FILE); \ + else \ + fputs ("+%lo(", FILE); \ + output_address (XEXP (addr, 1)); \ + fputc (')', FILE); \ + } \ + else if (flag_pic && GET_CODE (addr) == CONST \ + && GET_CODE (XEXP (addr, 0)) == MINUS \ + && GET_CODE (XEXP (XEXP (addr, 0), 1)) == CONST \ + && GET_CODE (XEXP (XEXP (XEXP (addr, 0), 1), 0)) == MINUS \ + && XEXP (XEXP (XEXP (XEXP (addr, 0), 1), 0), 1) == pc_rtx) \ + { \ + addr = XEXP (addr, 0); \ + output_addr_const (FILE, XEXP (addr, 0)); \ + /* Group the args of the second CONST in parenthesis. */ \ + fputs ("-(", FILE); \ + /* Skip past the second CONST--it does nothing for us. */\ + output_addr_const (FILE, XEXP (XEXP (addr, 1), 0)); \ + /* Close the parenthesis. */ \ + fputc (')', FILE); \ + } \ + else \ + { \ + output_addr_const (FILE, addr); \ + } \ +} + +/* Define the codes that are matched by predicates in sparc.c. */ + +#define PREDICATE_CODES \ +{"reg_or_0_operand", {SUBREG, REG, CONST_INT, CONST_DOUBLE}}, \ +{"fp_zero_operand", {CONST_DOUBLE}}, \ +{"intreg_operand", {SUBREG, REG}}, \ +{"fcc_reg_operand", {REG}}, \ +{"icc_or_fcc_reg_operand", {REG}}, \ +{"restore_operand", {REG}}, \ +{"call_operand", {MEM}}, \ +{"call_operand_address", {SYMBOL_REF, LABEL_REF, CONST, CONST_DOUBLE, \ + ADDRESSOF, SUBREG, REG, PLUS, LO_SUM, CONST_INT}}, \ +{"symbolic_operand", {SYMBOL_REF, LABEL_REF, CONST, CONST_DOUBLE}}, \ +{"symbolic_memory_operand", {SUBREG, MEM}}, \ +{"label_ref_operand", {LABEL_REF}}, \ +{"sp64_medium_pic_operand", {CONST}}, \ +{"data_segment_operand", {SYMBOL_REF, PLUS, CONST}}, \ +{"text_segment_operand", {LABEL_REF, SYMBOL_REF, PLUS, CONST}}, \ +{"reg_or_nonsymb_mem_operand", {SUBREG, REG, MEM}}, \ +{"splittable_symbolic_memory_operand", {MEM}}, \ +{"splittable_immediate_memory_operand", {MEM}}, \ +{"eq_or_neq", {EQ, NE}}, \ +{"normal_comp_operator", {GE, GT, LE, LT, GTU, LEU}}, \ +{"noov_compare_op", {NE, EQ, GE, GT, LE, LT, GEU, GTU, LEU, LTU}}, \ +{"v9_regcmp_op", {EQ, NE, GE, LT, LE, GT}}, \ +{"extend_op", {SIGN_EXTEND, ZERO_EXTEND}}, \ +{"cc_arithop", {AND, IOR, XOR}}, \ +{"cc_arithopn", {AND, IOR}}, \ +{"arith_operand", {SUBREG, REG, CONST_INT}}, \ +{"arith_add_operand", {SUBREG, REG, CONST_INT}}, \ +{"arith11_operand", {SUBREG, REG, CONST_INT}}, \ +{"arith10_operand", {SUBREG, REG, CONST_INT}}, \ +{"arith_double_operand", {SUBREG, REG, CONST_INT, CONST_DOUBLE}}, \ +{"arith_double_add_operand", {SUBREG, REG, CONST_INT, CONST_DOUBLE}}, \ +{"arith11_double_operand", {SUBREG, REG, CONST_INT, CONST_DOUBLE}}, \ +{"arith10_double_operand", {SUBREG, REG, CONST_INT, CONST_DOUBLE}}, \ +{"small_int", {CONST_INT}}, \ +{"small_int_or_double", {CONST_INT, CONST_DOUBLE}}, \ +{"uns_small_int", {CONST_INT}}, \ +{"uns_arith_operand", {SUBREG, REG, CONST_INT}}, \ +{"clobbered_register", {REG}}, \ +{"input_operand", {SUBREG, REG, CONST_INT, MEM, CONST}}, \ +{"zero_operand", {CONST_INT}}, \ +{"const64_operand", {CONST_INT, CONST_DOUBLE}}, \ +{"const64_high_operand", {CONST_INT, CONST_DOUBLE}}, + +/* The number of Pmode words for the setjmp buffer. */ +#define JMP_BUF_SIZE 12 + +#define DONT_ACCESS_GBLS_AFTER_EPILOGUE (flag_pic) + +/* Declare functions defined in sparc.c and used in templates. */ + +extern void sparc_emit_set_const32 (); +extern void sparc_emit_set_const64 (); +extern void sparc_emit_set_symbolic_const64 (); +extern int sparc_splitdi_legitimate (); +extern int sparc_absnegfloat_split_legitimate (); + +extern char *output_cbranch (); +extern char *output_return (); +extern char *output_v9branch (); + +extern void emit_v9_brxx_insn (); +extern void finalize_pic (); +extern void order_regs_for_local_alloc (); +extern void output_double_int (); +extern void output_function_epilogue (); +extern void output_function_prologue (); +extern void print_operand (); +extern void sparc_flat_output_function_epilogue (); +extern void sparc_flat_output_function_prologue (); + +extern int addrs_ok_for_ldd_peep (); +extern int arith10_double_operand (); +extern int arith10_operand (); +extern int arith11_double_operand (); +extern int arith11_operand (); +extern int arith_double_operand (); +extern int arith_double_4096_operand (); +extern int arith_double_add_operand (); +extern int arith_operand (); +extern int arith_4096_operand (); +extern int arith_add_operand (); +extern int call_operand_address (); +extern int input_operand (); +extern int zero_operand (); +extern int const64_operand (); +extern int const64_high_operand (); +extern int cc_arithop (); +extern int cc_arithopn (); +extern int check_pic (); +extern int compute_frame_size (); +extern int data_segment_operand (); +extern int eligible_for_epilogue_delay (); +extern int eligible_for_return_delay (); +extern int emit_move_sequence (); +extern int extend_op (); +extern int fcc_reg_operand (); +extern int fp_zero_operand (); +extern int icc_or_fcc_reg_operand (); +extern int label_ref_operand (); +extern int mem_min_alignment (); +extern int noov_compare_op (); +extern int pic_address_needs_scratch (); +extern int reg_or_0_operand (); +extern int reg_or_nonsymb_mem_operand (); +extern int reg_unused_after (); +extern int register_ok_for_ldd (); +extern int registers_ok_for_ldd_peep (); +extern int restore_operand (); +extern int short_branch (); +extern int small_int (); +extern int small_int_or_double (); +extern int sp64_medium_pic_operand (); +extern int sparc_flat_eligible_for_epilogue_delay (); +extern int sparc_flat_epilogue_delay_slots (); +extern int sparc_issue_rate (); +extern int splittable_immediate_memory_operand (); +extern int splittable_symbolic_memory_operand (); +extern int sparc_adjust_cost (); +extern int symbolic_memory_operand (); +extern int symbolic_operand (); +extern int text_segment_operand (); +extern int uns_small_int (); +extern int v9_regcmp_op (); +extern int v9_regcmp_p (); + +extern unsigned long sparc_flat_compute_frame_size (); +extern unsigned long sparc_type_code (); + +extern char *sparc_v8plus_shift (); + +#ifdef __STDC__ +/* Function used for V8+ code generation. Returns 1 if the high + 32 bits of REG are 0 before INSN. */ +extern int sparc_check_64 (struct rtx_def *, struct rtx_def *); +extern int sparc_return_peephole_ok (struct rtx_def *, struct rtx_def *); +extern int compute_frame_size (int, int); +#endif + +/* Defined in flags.h, but insn-emit.c does not include flags.h. */ + +extern int flag_pic; diff --git a/gcc/config/sparc/sparc.md b/gcc/config/sparc/sparc.md new file mode 100755 index 0000000..1277e2b --- /dev/null +++ b/gcc/config/sparc/sparc.md @@ -0,0 +1,8242 @@ +;;- Machine description for SPARC chip for GNU C compiler +;; Copyright (C) 1987, 88, 89, 92-98, 1999 Free Software Foundation, Inc. +;; Contributed by Michael Tiemann (tiemann@cygnus.com) +;; 64 bit SPARC V9 support by Michael Tiemann, Jim Wilson, and Doug Evans, +;; at Cygnus Support. + +;; 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. + +;;- See file "rtl.def" for documentation on define_insn, match_*, et. al. + +;; Uses of UNSPEC and UNSPEC_VOLATILE in this file: +;; +;; UNSPEC: 0 movsi_{lo_sum,high}_pic +;; pic_lo_sum_di +;; pic_sethi_di +;; 1 update_return +;; 2 get_pc +;; 5 movsi_{,lo_sum_,high_}pic_label_ref +;; 6 seth44 +;; 7 setm44 +;; 8 setl44 +;; 9 sethh +;; 10 setlm +;; 11 embmedany_sethi, embmedany_brsum +;; 12 movsf_const_high +;; 13 embmedany_textuhi +;; 14 embmedany_texthi +;; 15 embmedany_textulo +;; 16 embmedany_textlo +;; 17 movsf_const_lo +;; 18 sethm +;; 19 setlo +;; +;; UNSPEC_VOLATILE: 0 blockage +;; 1 flush_register_windows +;; 2 goto_handler_and_restore +;; 3 goto_handler_and_restore_v9* +;; 4 flush +;; 5 nonlocal_goto_receiver +;; + +;; The upper 32 fp regs on the v9 can't hold SFmode values. To deal with this +;; a second register class, EXTRA_FP_REGS, exists for the v9 chip. The name +;; is a bit of a misnomer as it covers all 64 fp regs. The corresponding +;; constraint letter is 'e'. To avoid any confusion, 'e' is used instead of +;; 'f' for all DF/TFmode values, including those that are specific to the v8. +;; +;; -mlive-g0 is *not* supported for TARGET_ARCH64, so we don't bother to +;; test TARGET_LIVE_G0 if we have TARGET_ARCH64. + +;; Attribute for cpu type. +;; These must match the values for enum processor_type in sparc.h. +(define_attr "cpu" "v7,cypress,v8,supersparc,sparclite,f930,f934,hypersparc,sparclite86x,sparclet,tsc701,v9,ultrasparc" + (const (symbol_ref "sparc_cpu_attr"))) + +;; Attribute for the instruction set. +;; At present we only need to distinguish v9/!v9, but for clarity we +;; test TARGET_V8 too. +(define_attr "isa" "v6,v8,v9,sparclet" + (const + (cond [(symbol_ref "TARGET_V9") (const_string "v9") + (symbol_ref "TARGET_V8") (const_string "v8") + (symbol_ref "TARGET_SPARCLET") (const_string "sparclet")] + (const_string "v6")))) + +;; Architecture size. +(define_attr "arch" "arch32bit,arch64bit" + (const + (cond [(symbol_ref "TARGET_ARCH64") (const_string "arch64bit")] + (const_string "arch32bit")))) + +;; Whether -mlive-g0 is in effect. +(define_attr "live_g0" "no,yes" + (const + (cond [(symbol_ref "TARGET_LIVE_G0") (const_string "yes")] + (const_string "no")))) + +;; 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) +;; type "compare" insns have one or two input operands (0,1) and no output +;; type "call_no_delay_slot" is a call followed by an unimp instruction. + +(define_attr "type" + "move,unary,binary,compare,load,sload,store,ialu,shift,uncond_branch,branch,call,call_no_delay_slot,return,address,imul,fpload,fpstore,fp,fpmove,fpcmove,fpcmp,fpmul,fpdivs,fpdivd,fpsqrt,cmove,multi,misc" + (const_string "binary")) + +;; Set true if insn uses call-clobbered intermediate register. +(define_attr "use_clobbered" "false,true" + (if_then_else (and (eq_attr "type" "address") + (match_operand 0 "clobbered_register" "")) + (const_string "true") + (const_string "false"))) + +;; Length (in # of insns). +(define_attr "length" "" + (cond [(eq_attr "type" "load,sload,fpload") + (if_then_else (match_operand 1 "symbolic_memory_operand" "") + (const_int 2) (const_int 1)) + + (eq_attr "type" "store,fpstore") + (if_then_else (match_operand 0 "symbolic_memory_operand" "") + (const_int 2) (const_int 1)) + + (eq_attr "type" "address") (const_int 2) + + (eq_attr "type" "binary") + (if_then_else (ior (match_operand 2 "arith_operand" "") + (match_operand 2 "arith_double_operand" "")) + (const_int 1) (const_int 3)) + + (eq_attr "type" "multi") (const_int 2) + + (eq_attr "type" "move,unary") + (if_then_else (ior (match_operand 1 "arith_operand" "") + (match_operand 1 "arith_double_operand" "")) + (const_int 1) (const_int 2))] + + (const_int 1))) + +(define_asm_attributes + [(set_attr "length" "1") + (set_attr "type" "multi")]) + +;; Attributes for instruction and branch scheduling + +(define_attr "in_call_delay" "false,true" + (cond [(eq_attr "type" "uncond_branch,branch,call,call_no_delay_slot,return,multi") + (const_string "false") + (eq_attr "type" "load,fpload,store,fpstore") + (if_then_else (eq_attr "length" "1") + (const_string "true") + (const_string "false")) + (eq_attr "type" "address") + (if_then_else (eq_attr "use_clobbered" "false") + (const_string "true") + (const_string "false"))] + (if_then_else (eq_attr "length" "1") + (const_string "true") + (const_string "false")))) + +(define_delay (eq_attr "type" "call") + [(eq_attr "in_call_delay" "true") (nil) (nil)]) + +(define_attr "leaf_function" "false,true" + (const (symbol_ref "leaf_function"))) + +(define_attr "in_return_delay" "false,true" + (if_then_else (and (and (and (eq_attr "type" "move,load,sload,store,binary,ialu") + (eq_attr "length" "1")) + (eq_attr "leaf_function" "false")) + (match_insn "eligible_for_return_delay")) + (const_string "true") + (const_string "false"))) + +(define_delay (and (eq_attr "type" "return") + (eq_attr "isa" "v9")) + [(eq_attr "in_return_delay" "true") (nil) (nil)]) + +;; ??? Should implement the notion of predelay slots for floating point +;; branches. This would allow us to remove the nop always inserted before +;; a floating point branch. + +;; ??? It is OK for fill_simple_delay_slots to put load/store instructions +;; in a delay slot, but it is not OK for fill_eager_delay_slots to do so. +;; This is because doing so will add several pipeline stalls to the path +;; that the load/store did not come from. Unfortunately, there is no way +;; to prevent fill_eager_delay_slots from using load/store without completely +;; disabling them. For the SPEC benchmark set, this is a serious lose, +;; because it prevents us from moving back the final store of inner loops. + +(define_attr "in_branch_delay" "false,true" + (if_then_else (and (eq_attr "type" "!uncond_branch,branch,call,call_no_delay_slot,multi") + (eq_attr "length" "1")) + (const_string "true") + (const_string "false"))) + +(define_attr "in_uncond_branch_delay" "false,true" + (if_then_else (and (eq_attr "type" "!uncond_branch,branch,call,call_no_delay_slot,multi") + (eq_attr "length" "1")) + (const_string "true") + (const_string "false"))) + +(define_attr "in_annul_branch_delay" "false,true" + (if_then_else (and (eq_attr "type" "!uncond_branch,branch,call,call_no_delay_slot,multi") + (eq_attr "length" "1")) + (const_string "true") + (const_string "false"))) + +(define_delay (eq_attr "type" "branch") + [(eq_attr "in_branch_delay" "true") + (nil) (eq_attr "in_annul_branch_delay" "true")]) + +(define_delay (eq_attr "type" "uncond_branch") + [(eq_attr "in_uncond_branch_delay" "true") + (nil) (nil)]) + +;; Function units of the SPARC + +;; (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.) + +;; On the sparclite, integer multiply takes 1, 3, or 5 cycles depending on +;; the inputs. + +;; (define_function_unit "alu" 1 0 +;; (eq_attr "type" "unary,binary,move,address") 1 0) + +;; ---- cypress CY7C602 scheduling: +;; Memory with load-delay of 1 (i.e., 2 cycle load). + +(define_function_unit "memory" 1 0 + (and (eq_attr "cpu" "cypress") + (eq_attr "type" "load,sload,fpload")) + 2 2) + +;; SPARC has two floating-point units: the FP ALU, +;; and the FP MUL/DIV/SQRT unit. +;; Instruction timings on the CY7C602 are as follows +;; FABSs 4 +;; FADDs/d 5/5 +;; FCMPs/d 4/4 +;; FDIVs/d 23/37 +;; FMOVs 4 +;; FMULs/d 5/7 +;; FNEGs 4 +;; FSQRTs/d 34/63 +;; FSUBs/d 5/5 +;; FdTOi/s 5/5 +;; FsTOi/d 5/5 +;; FiTOs/d 9/5 + +;; The CY7C602 can only support 2 fp isnsn simultaneously. +;; More insns cause the chip to stall. + +(define_function_unit "fp_alu" 1 0 + (and (eq_attr "cpu" "cypress") + (eq_attr "type" "fp,fpmove")) + 5 5) + +(define_function_unit "fp_mds" 1 0 + (and (eq_attr "cpu" "cypress") + (eq_attr "type" "fpmul")) + 7 7) + +(define_function_unit "fp_mds" 1 0 + (and (eq_attr "cpu" "cypress") + (eq_attr "type" "fpdivs,fpdivd")) + 37 37) + +(define_function_unit "fp_mds" 1 0 + (and (eq_attr "cpu" "cypress") + (eq_attr "type" "fpsqrt")) + 63 63) + +;; ----- The TMS390Z55 scheduling +;; The Supersparc can issue 1 - 3 insns per cycle: up to two integer, +;; one ld/st, one fp. +;; Memory delivers its result in one cycle to IU, zero cycles to FP + +(define_function_unit "memory" 1 0 + (and (eq_attr "cpu" "supersparc") + (eq_attr "type" "load,sload")) + 1 1) + +(define_function_unit "memory" 1 0 + (and (eq_attr "cpu" "supersparc") + (eq_attr "type" "fpload")) + 0 1) + +(define_function_unit "memory" 1 0 + (and (eq_attr "cpu" "supersparc") + (eq_attr "type" "store,fpstore")) + 1 1) + +(define_function_unit "shift" 1 0 + (and (eq_attr "cpu" "supersparc") + (eq_attr "type" "shift")) + 1 1) + +;; There are only two write ports to the integer register file +;; A store also uses a write port + +(define_function_unit "iwport" 2 0 + (and (eq_attr "cpu" "supersparc") + (eq_attr "type" "load,sload,store,shift,ialu")) + 1 1) + +;; Timings; throughput/latency +;; FADD 1/3 add/sub, format conv, compar, abs, neg +;; FMUL 1/3 +;; FDIVs 4/6 +;; FDIVd 7/9 +;; FSQRTs 6/8 +;; FSQRTd 10/12 +;; IMUL 4/4 + +(define_function_unit "fp_alu" 1 0 + (and (eq_attr "cpu" "supersparc") + (eq_attr "type" "fp,fpmove,fpcmp")) + 3 1) + +(define_function_unit "fp_mds" 1 0 + (and (eq_attr "cpu" "supersparc") + (eq_attr "type" "fpmul")) + 3 1) + +(define_function_unit "fp_mds" 1 0 + (and (eq_attr "cpu" "supersparc") + (eq_attr "type" "fpdivs")) + 6 4) + +(define_function_unit "fp_mds" 1 0 + (and (eq_attr "cpu" "supersparc") + (eq_attr "type" "fpdivd")) + 9 7) + +(define_function_unit "fp_mds" 1 0 + (and (eq_attr "cpu" "supersparc") + (eq_attr "type" "fpsqrt")) + 12 10) + +(define_function_unit "fp_mds" 1 0 + (and (eq_attr "cpu" "supersparc") + (eq_attr "type" "imul")) + 4 4) + +;; ----- hypersparc/sparclite86x scheduling +;; The Hypersparc can issue 1 - 2 insns per cycle. The dual issue cases are: +;; L-Ld/St I-Int F-Float B-Branch LI/LF/LB/II/IF/IB/FF/FB +;; II/FF case is only when loading a 32 bit hi/lo constant +;; Single issue insns include call, jmpl, u/smul, u/sdiv, lda, sta, fcmp +;; Memory delivers its result in one cycle to IU + +(define_function_unit "memory" 1 0 + (and (ior (eq_attr "cpu" "hypersparc") (eq_attr "cpu" "sparclite86x")) + (eq_attr "type" "load,sload,fpload")) + 1 1) + +(define_function_unit "memory" 1 0 + (and (ior (eq_attr "cpu" "hypersparc") (eq_attr "cpu" "sparclite86x")) + (eq_attr "type" "store,fpstore")) + 2 1) + +(define_function_unit "fp_alu" 1 0 + (and (ior (eq_attr "cpu" "hypersparc") (eq_attr "cpu" "sparclite86x")) + (eq_attr "type" "fp,fpmove,fpcmp")) + 1 1) + +(define_function_unit "fp_mds" 1 0 + (and (ior (eq_attr "cpu" "hypersparc") (eq_attr "cpu" "sparclite86x")) + (eq_attr "type" "fpmul")) + 1 1) + +(define_function_unit "fp_mds" 1 0 + (and (ior (eq_attr "cpu" "hypersparc") (eq_attr "cpu" "sparclite86x")) + (eq_attr "type" "fpdivs")) + 8 6) + +(define_function_unit "fp_mds" 1 0 + (and (ior (eq_attr "cpu" "hypersparc") (eq_attr "cpu" "sparclite86x")) + (eq_attr "type" "fpdivd")) + 12 10) + +(define_function_unit "fp_mds" 1 0 + (and (ior (eq_attr "cpu" "hypersparc") (eq_attr "cpu" "sparclite86x")) + (eq_attr "type" "fpsqrt")) + 17 15) + +(define_function_unit "fp_mds" 1 0 + (and (ior (eq_attr "cpu" "hypersparc") (eq_attr "cpu" "sparclite86x")) + (eq_attr "type" "imul")) + 17 15) + +;; ----- sparclet tsc701 scheduling +;; The tsc701 issues 1 insn per cycle. +;; Results may be written back out of order. + +;; Loads take 2 extra cycles to complete and 4 can be buffered at a time. + +(define_function_unit "tsc701_load" 4 1 + (and (eq_attr "cpu" "tsc701") + (eq_attr "type" "load,sload")) + 3 1) + +;; Stores take 2(?) extra cycles to complete. +;; It is desirable to not have any memory operation in the following 2 cycles. +;; (??? or 2 memory ops in the case of std). + +(define_function_unit "tsc701_store" 1 0 + (and (eq_attr "cpu" "tsc701") + (eq_attr "type" "store")) + 3 3 + [(eq_attr "type" "load,sload,store")]) + +;; The multiply unit has a latency of 5. +(define_function_unit "tsc701_mul" 1 0 + (and (eq_attr "cpu" "tsc701") + (eq_attr "type" "imul")) + 5 5) + +;; ----- The UltraSPARC-1 scheduling +;; UltraSPARC has two integer units. Shift instructions can only execute +;; on IE0. Condition code setting instructions, call, and jmpl (including +;; the ret and retl pseudo-instructions) can only execute on IE1. +;; Branch on register uses IE1, but branch on condition code does not. +;; Conditional moves take 2 cycles. No other instruction can issue in the +;; same cycle as a conditional move. +;; Multiply and divide take many cycles during which no other instructions +;; can issue. +;; Memory delivers its result in two cycles (except for signed loads, +;; which take one cycle more). One memory instruction can be issued per +;; cycle. + +(define_function_unit "memory" 1 0 + (and (eq_attr "cpu" "ultrasparc") + (eq_attr "type" "load,fpload")) + 2 1) + +(define_function_unit "memory" 1 0 + (and (eq_attr "cpu" "ultrasparc") + (eq_attr "type" "sload")) + 3 1) + +(define_function_unit "memory" 1 0 + (and (eq_attr "cpu" "ultrasparc") + (eq_attr "type" "store,fpstore")) + 1 1) + +(define_function_unit "ieuN" 2 0 + (and (eq_attr "cpu" "ultrasparc") + (eq_attr "type" "ialu,binary,move,unary,shift,compare,call,call_no_delay_slot,uncond_branch")) + 1 1) + +(define_function_unit "ieu0" 1 0 + (and (eq_attr "cpu" "ultrasparc") + (eq_attr "type" "shift")) + 1 1) + +(define_function_unit "ieu0" 1 0 + (and (eq_attr "cpu" "ultrasparc") + (eq_attr "type" "cmove")) + 2 1) + +(define_function_unit "ieu1" 1 0 + (and (eq_attr "cpu" "ultrasparc") + (eq_attr "type" "compare,call,call_no_delay_slot,uncond_branch")) + 1 1) + +(define_function_unit "cti" 1 0 + (and (eq_attr "cpu" "ultrasparc") + (eq_attr "type" "branch")) + 1 1) + +;; Timings; throughput/latency +;; FMOV 1/1 fmov, fabs, fneg +;; FMOVcc 1/2 +;; FADD 1/4 add/sub, format conv, compar +;; FMUL 1/4 +;; FDIVs 12/12 +;; FDIVd 22/22 +;; FSQRTs 12/12 +;; FSQRTd 22/22 +;; FCMP takes 1 cycle to branch, 2 cycles to conditional move. +;; +;; ??? This is really bogus because the timings really depend upon +;; who uses the result. We should record who the user is with +;; more descriptive 'type' attribute names and account for these +;; issues in ultrasparc_adjust_cost. + +(define_function_unit "fadd" 1 0 + (and (eq_attr "cpu" "ultrasparc") + (eq_attr "type" "fpmove")) + 1 1) + +(define_function_unit "fadd" 1 0 + (and (eq_attr "cpu" "ultrasparc") + (eq_attr "type" "fpcmove")) + 2 1) + +(define_function_unit "fadd" 1 0 + (and (eq_attr "cpu" "ultrasparc") + (eq_attr "type" "fp")) + 4 1) + +(define_function_unit "fadd" 1 0 + (and (eq_attr "cpu" "ultrasparc") + (eq_attr "type" "fpcmp")) + 2 1) + +(define_function_unit "fmul" 1 0 + (and (eq_attr "cpu" "ultrasparc") + (eq_attr "type" "fpmul")) + 4 1) + +(define_function_unit "fadd" 1 0 + (and (eq_attr "cpu" "ultrasparc") + (eq_attr "type" "fpcmove")) + 2 1) + +(define_function_unit "fmul" 1 0 + (and (eq_attr "cpu" "ultrasparc") + (eq_attr "type" "fpdivs")) + 12 12) + +(define_function_unit "fmul" 1 0 + (and (eq_attr "cpu" "ultrasparc") + (eq_attr "type" "fpdivd")) + 22 22) + +(define_function_unit "fmul" 1 0 + (and (eq_attr "cpu" "ultrasparc") + (eq_attr "type" "fpsqrt")) + 12 12) + +;; 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. +;; +;; We do this because we want to generate different code for an sne and +;; seq insn. In those cases, if the second operand of the compare is not +;; const0_rtx, we want to compute the xor of the two operands and test +;; it against zero. +;; +;; We start with the DEFINE_EXPANDs, then the DEFINE_INSNs to match +;; the patterns. Finally, we have the DEFINE_SPLITs for some of the scc +;; insns that actually require more than one machine instruction. + +;; Put cmpsi first among compare insns so it matches two CONST_INT operands. + +(define_expand "cmpsi" + [(set (reg:CC 100) + (compare:CC (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "arith_operand" "")))] + "" + " +{ + sparc_compare_op0 = operands[0]; + sparc_compare_op1 = operands[1]; + DONE; +}") + +(define_expand "cmpdi" + [(set (reg:CCX 100) + (compare:CCX (match_operand:DI 0 "register_operand" "") + (match_operand:DI 1 "arith_double_operand" "")))] + "TARGET_ARCH64" + " +{ + sparc_compare_op0 = operands[0]; + sparc_compare_op1 = operands[1]; + DONE; +}") + +(define_expand "cmpsf" + ;; The 96 here isn't ever used by anyone. + [(set (reg:CCFP 96) + (compare:CCFP (match_operand:SF 0 "register_operand" "") + (match_operand:SF 1 "register_operand" "")))] + "TARGET_FPU" + " +{ + sparc_compare_op0 = operands[0]; + sparc_compare_op1 = operands[1]; + DONE; +}") + +(define_expand "cmpdf" + ;; The 96 here isn't ever used by anyone. + [(set (reg:CCFP 96) + (compare:CCFP (match_operand:DF 0 "register_operand" "") + (match_operand:DF 1 "register_operand" "")))] + "TARGET_FPU" + " +{ + sparc_compare_op0 = operands[0]; + sparc_compare_op1 = operands[1]; + DONE; +}") + +(define_expand "cmptf" + ;; The 96 here isn't ever used by anyone. + [(set (reg:CCFP 96) + (compare:CCFP (match_operand:TF 0 "register_operand" "") + (match_operand:TF 1 "register_operand" "")))] + "TARGET_FPU" + " +{ + sparc_compare_op0 = operands[0]; + sparc_compare_op1 = operands[1]; + DONE; +}") + +;; Now the compare DEFINE_INSNs. + +(define_insn "*cmpsi_insn" + [(set (reg:CC 100) + (compare:CC (match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "arith_operand" "rI")))] + "" + "cmp\\t%0, %1" + [(set_attr "type" "compare")]) + +(define_insn "*cmpdi_sp64" + [(set (reg:CCX 100) + (compare:CCX (match_operand:DI 0 "register_operand" "r") + (match_operand:DI 1 "arith_double_operand" "rHI")))] + "TARGET_ARCH64" + "cmp\\t%0, %1" + [(set_attr "type" "compare")]) + +(define_insn "*cmpsf_fpe" + [(set (match_operand:CCFPE 0 "fcc_reg_operand" "=c") + (compare:CCFPE (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f")))] + "TARGET_FPU" + "* +{ + if (TARGET_V9) + return \"fcmpes\\t%0, %1, %2\"; + return \"fcmpes\\t%1, %2\"; +}" + [(set_attr "type" "fpcmp")]) + +(define_insn "*cmpdf_fpe" + [(set (match_operand:CCFPE 0 "fcc_reg_operand" "=c") + (compare:CCFPE (match_operand:DF 1 "register_operand" "e") + (match_operand:DF 2 "register_operand" "e")))] + "TARGET_FPU" + "* +{ + if (TARGET_V9) + return \"fcmped\\t%0, %1, %2\"; + return \"fcmped\\t%1, %2\"; +}" + [(set_attr "type" "fpcmp")]) + +(define_insn "*cmptf_fpe" + [(set (match_operand:CCFPE 0 "fcc_reg_operand" "=c") + (compare:CCFPE (match_operand:TF 1 "register_operand" "e") + (match_operand:TF 2 "register_operand" "e")))] + "TARGET_FPU && TARGET_HARD_QUAD" + "* +{ + if (TARGET_V9) + return \"fcmpeq\\t%0, %1, %2\"; + return \"fcmpeq\\t%1, %2\"; +}" + [(set_attr "type" "fpcmp")]) + +(define_insn "*cmpsf_fp" + [(set (match_operand:CCFP 0 "fcc_reg_operand" "=c") + (compare:CCFP (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f")))] + "TARGET_FPU" + "* +{ + if (TARGET_V9) + return \"fcmps\\t%0, %1, %2\"; + return \"fcmps\\t%1, %2\"; +}" + [(set_attr "type" "fpcmp")]) + +(define_insn "*cmpdf_fp" + [(set (match_operand:CCFP 0 "fcc_reg_operand" "=c") + (compare:CCFP (match_operand:DF 1 "register_operand" "e") + (match_operand:DF 2 "register_operand" "e")))] + "TARGET_FPU" + "* +{ + if (TARGET_V9) + return \"fcmpd\\t%0, %1, %2\"; + return \"fcmpd\\t%1, %2\"; +}" + [(set_attr "type" "fpcmp")]) + +(define_insn "*cmptf_fp" + [(set (match_operand:CCFP 0 "fcc_reg_operand" "=c") + (compare:CCFP (match_operand:TF 1 "register_operand" "e") + (match_operand:TF 2 "register_operand" "e")))] + "TARGET_FPU && TARGET_HARD_QUAD" + "* +{ + if (TARGET_V9) + return \"fcmpq\\t%0, %1, %2\"; + return \"fcmpq\\t%1, %2\"; +}" + [(set_attr "type" "fpcmp")]) + +;; Next come the scc insns. For seq, sne, sgeu, and sltu, we can do this +;; without jumps using the addx/subx instructions. For seq/sne on v9 we use +;; the same code as v8 (the addx/subx method has more applications). The +;; exception to this is "reg != 0" which can be done in one instruction on v9 +;; (so we do it). For the rest, on v9 we use conditional moves; on v8, we do +;; branches. + +;; Seq_special[_xxx] and sne_special[_xxx] clobber the CC reg, because they +;; generate addcc/subcc instructions. + +(define_expand "seqsi_special" + [(set (match_dup 3) + (xor:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "register_operand" ""))) + (parallel [(set (match_operand:SI 0 "register_operand" "") + (eq:SI (match_dup 3) (const_int 0))) + (clobber (reg:CC 100))])] + "! TARGET_LIVE_G0" + "{ operands[3] = gen_reg_rtx (SImode); }") + +(define_expand "seqdi_special" + [(set (match_dup 3) + (xor:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "register_operand" ""))) + (set (match_operand:DI 0 "register_operand" "") + (eq:DI (match_dup 3) (const_int 0)))] + "TARGET_ARCH64" + "{ operands[3] = gen_reg_rtx (DImode); }") + +(define_expand "snesi_special" + [(set (match_dup 3) + (xor:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "register_operand" ""))) + (parallel [(set (match_operand:SI 0 "register_operand" "") + (ne:SI (match_dup 3) (const_int 0))) + (clobber (reg:CC 100))])] + "! TARGET_LIVE_G0" + "{ operands[3] = gen_reg_rtx (SImode); }") + +(define_expand "snedi_special" + [(set (match_dup 3) + (xor:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "register_operand" ""))) + (set (match_operand:DI 0 "register_operand" "") + (ne:DI (match_dup 3) (const_int 0)))] + "TARGET_ARCH64" + "{ operands[3] = gen_reg_rtx (DImode); }") + +(define_expand "seqdi_special_trunc" + [(set (match_dup 3) + (xor:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "register_operand" ""))) + (set (match_operand:SI 0 "register_operand" "") + (eq:SI (match_dup 3) (const_int 0)))] + "TARGET_ARCH64" + "{ operands[3] = gen_reg_rtx (DImode); }") + +(define_expand "snedi_special_trunc" + [(set (match_dup 3) + (xor:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "register_operand" ""))) + (set (match_operand:SI 0 "register_operand" "") + (ne:SI (match_dup 3) (const_int 0)))] + "TARGET_ARCH64" + "{ operands[3] = gen_reg_rtx (DImode); }") + +(define_expand "seqsi_special_extend" + [(set (match_dup 3) + (xor:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "register_operand" ""))) + (parallel [(set (match_operand:DI 0 "register_operand" "") + (eq:DI (match_dup 3) (const_int 0))) + (clobber (reg:CC 100))])] + "TARGET_ARCH64" + "{ operands[3] = gen_reg_rtx (SImode); }") + +(define_expand "snesi_special_extend" + [(set (match_dup 3) + (xor:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "register_operand" ""))) + (parallel [(set (match_operand:DI 0 "register_operand" "") + (ne:DI (match_dup 3) (const_int 0))) + (clobber (reg:CC 100))])] + "TARGET_ARCH64" + "{ operands[3] = gen_reg_rtx (SImode); }") + +;; ??? v9: Operand 0 needs a mode, so SImode was chosen. +;; However, the code handles both SImode and DImode. +(define_expand "seq" + [(set (match_operand:SI 0 "intreg_operand" "") + (eq:SI (match_dup 1) (const_int 0)))] + "! TARGET_LIVE_G0" + " +{ + if (GET_MODE (sparc_compare_op0) == SImode) + { + rtx pat; + + if (GET_MODE (operands[0]) == SImode) + pat = gen_seqsi_special (operands[0], sparc_compare_op0, + sparc_compare_op1); + else if (! TARGET_ARCH64) + FAIL; + else + pat = gen_seqsi_special_extend (operands[0], sparc_compare_op0, + sparc_compare_op1); + emit_insn (pat); + DONE; + } + else if (GET_MODE (sparc_compare_op0) == DImode) + { + rtx pat; + + if (! TARGET_ARCH64) + FAIL; + else if (GET_MODE (operands[0]) == SImode) + pat = gen_seqdi_special_trunc (operands[0], sparc_compare_op0, + sparc_compare_op1); + else + pat = gen_seqdi_special (operands[0], sparc_compare_op0, + sparc_compare_op1); + emit_insn (pat); + DONE; + } + else if (GET_MODE (sparc_compare_op0) == TFmode && ! TARGET_HARD_QUAD) + { + emit_float_lib_cmp (sparc_compare_op0, sparc_compare_op1, EQ); + emit_insn (gen_sne (operands[0])); + DONE; + } + else if (TARGET_V9) + { + if (gen_v9_scc (EQ, operands)) + DONE; + /* fall through */ + } + FAIL; +}") + +;; ??? v9: Operand 0 needs a mode, so SImode was chosen. +;; However, the code handles both SImode and DImode. +(define_expand "sne" + [(set (match_operand:SI 0 "intreg_operand" "") + (ne:SI (match_dup 1) (const_int 0)))] + "! TARGET_LIVE_G0" + " +{ + if (GET_MODE (sparc_compare_op0) == SImode) + { + rtx pat; + + if (GET_MODE (operands[0]) == SImode) + pat = gen_snesi_special (operands[0], sparc_compare_op0, + sparc_compare_op1); + else if (! TARGET_ARCH64) + FAIL; + else + pat = gen_snesi_special_extend (operands[0], sparc_compare_op0, + sparc_compare_op1); + emit_insn (pat); + DONE; + } + else if (GET_MODE (sparc_compare_op0) == DImode) + { + rtx pat; + + if (! TARGET_ARCH64) + FAIL; + else if (GET_MODE (operands[0]) == SImode) + pat = gen_snedi_special_trunc (operands[0], sparc_compare_op0, + sparc_compare_op1); + else + pat = gen_snedi_special (operands[0], sparc_compare_op0, + sparc_compare_op1); + emit_insn (pat); + DONE; + } + else if (GET_MODE (sparc_compare_op0) == TFmode && ! TARGET_HARD_QUAD) + { + emit_float_lib_cmp (sparc_compare_op0, sparc_compare_op1, NE); + emit_insn (gen_sne (operands[0])); + DONE; + } + else if (TARGET_V9) + { + if (gen_v9_scc (NE, operands)) + DONE; + /* fall through */ + } + FAIL; +}") + +(define_expand "sgt" + [(set (match_operand:SI 0 "intreg_operand" "") + (gt:SI (match_dup 1) (const_int 0)))] + "! TARGET_LIVE_G0" + " +{ + if (GET_MODE (sparc_compare_op0) == TFmode && ! TARGET_HARD_QUAD) + { + emit_float_lib_cmp (sparc_compare_op0, sparc_compare_op1, GT); + emit_insn (gen_sne (operands[0])); + DONE; + } + else if (TARGET_V9) + { + if (gen_v9_scc (GT, operands)) + DONE; + /* fall through */ + } + FAIL; +}") + +(define_expand "slt" + [(set (match_operand:SI 0 "intreg_operand" "") + (lt:SI (match_dup 1) (const_int 0)))] + "! TARGET_LIVE_G0" + " +{ + if (GET_MODE (sparc_compare_op0) == TFmode && ! TARGET_HARD_QUAD) + { + emit_float_lib_cmp (sparc_compare_op0, sparc_compare_op1, LT); + emit_insn (gen_sne (operands[0])); + DONE; + } + else if (TARGET_V9) + { + if (gen_v9_scc (LT, operands)) + DONE; + /* fall through */ + } + FAIL; +}") + +(define_expand "sge" + [(set (match_operand:SI 0 "intreg_operand" "") + (ge:SI (match_dup 1) (const_int 0)))] + "! TARGET_LIVE_G0" + " +{ + if (GET_MODE (sparc_compare_op0) == TFmode && ! TARGET_HARD_QUAD) + { + emit_float_lib_cmp (sparc_compare_op0, sparc_compare_op1, GE); + emit_insn (gen_sne (operands[0])); + DONE; + } + else if (TARGET_V9) + { + if (gen_v9_scc (GE, operands)) + DONE; + /* fall through */ + } + FAIL; +}") + +(define_expand "sle" + [(set (match_operand:SI 0 "intreg_operand" "") + (le:SI (match_dup 1) (const_int 0)))] + "! TARGET_LIVE_G0" + " +{ + if (GET_MODE (sparc_compare_op0) == TFmode && ! TARGET_HARD_QUAD) + { + emit_float_lib_cmp (sparc_compare_op0, sparc_compare_op1, LE); + emit_insn (gen_sne (operands[0])); + DONE; + } + else if (TARGET_V9) + { + if (gen_v9_scc (LE, operands)) + DONE; + /* fall through */ + } + FAIL; +}") + +(define_expand "sgtu" + [(set (match_operand:SI 0 "intreg_operand" "") + (gtu:SI (match_dup 1) (const_int 0)))] + "! TARGET_LIVE_G0" + " +{ + if (! TARGET_V9) + { + rtx tem, pat; + + /* We can do ltu easily, so if both operands are registers, swap them and + do a LTU. */ + if ((GET_CODE (sparc_compare_op0) == REG + || GET_CODE (sparc_compare_op0) == SUBREG) + && (GET_CODE (sparc_compare_op1) == REG + || GET_CODE (sparc_compare_op1) == SUBREG)) + { + tem = sparc_compare_op0; + sparc_compare_op0 = sparc_compare_op1; + sparc_compare_op1 = tem; + pat = gen_sltu (operands[0]); + if (pat == NULL_RTX) + FAIL; + emit_insn (pat); + DONE; + } + } + else + { + if (gen_v9_scc (GTU, operands)) + DONE; + } + FAIL; +}") + +(define_expand "sltu" + [(set (match_operand:SI 0 "intreg_operand" "") + (ltu:SI (match_dup 1) (const_int 0)))] + "! TARGET_LIVE_G0" + " +{ + if (TARGET_V9) + { + if (gen_v9_scc (LTU, operands)) + DONE; + } + operands[1] = gen_compare_reg (LTU, sparc_compare_op0, sparc_compare_op1); +}") + +(define_expand "sgeu" + [(set (match_operand:SI 0 "intreg_operand" "") + (geu:SI (match_dup 1) (const_int 0)))] + "! TARGET_LIVE_G0" + " +{ + if (TARGET_V9) + { + if (gen_v9_scc (GEU, operands)) + DONE; + } + operands[1] = gen_compare_reg (GEU, sparc_compare_op0, sparc_compare_op1); +}") + +(define_expand "sleu" + [(set (match_operand:SI 0 "intreg_operand" "") + (leu:SI (match_dup 1) (const_int 0)))] + "! TARGET_LIVE_G0" + " +{ + if (! TARGET_V9) + { + rtx tem, pat; + + /* We can do geu easily, so if both operands are registers, swap them and + do a GEU. */ + if ((GET_CODE (sparc_compare_op0) == REG + || GET_CODE (sparc_compare_op0) == SUBREG) + && (GET_CODE (sparc_compare_op1) == REG + || GET_CODE (sparc_compare_op1) == SUBREG)) + { + tem = sparc_compare_op0; + sparc_compare_op0 = sparc_compare_op1; + sparc_compare_op1 = tem; + pat = gen_sgeu (operands[0]); + if (pat == NULL_RTX) + FAIL; + emit_insn (pat); + DONE; + } + } + else + { + if (gen_v9_scc (LEU, operands)) + DONE; + } + FAIL; +}") + +;; Now the DEFINE_INSNs for the scc cases. + +;; The SEQ and SNE patterns are special because they can be done +;; without any branching and do not involve a COMPARE. We want +;; them to always use the splitz below so the results can be +;; scheduled. + +(define_insn "*snesi_zero" + [(set (match_operand:SI 0 "register_operand" "=r") + (ne:SI (match_operand:SI 1 "register_operand" "r") + (const_int 0))) + (clobber (reg:CC 100))] + "! TARGET_LIVE_G0" + "#" + [(set_attr "length" "2")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (ne:SI (match_operand:SI 1 "register_operand" "") + (const_int 0))) + (clobber (reg:CC 100))] + "" + [(set (reg:CC_NOOV 100) (compare:CC_NOOV (neg:SI (match_dup 1)) + (const_int 0))) + (set (match_dup 0) (ltu:SI (reg:CC 100) (const_int 0)))] + "") + +(define_insn "*neg_snesi_zero" + [(set (match_operand:SI 0 "register_operand" "=r") + (neg:SI (ne:SI (match_operand:SI 1 "register_operand" "r") + (const_int 0)))) + (clobber (reg:CC 100))] + "! TARGET_LIVE_G0" + "#" + [(set_attr "length" "2")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (neg:SI (ne:SI (match_operand:SI 1 "register_operand" "") + (const_int 0)))) + (clobber (reg:CC 100))] + "" + [(set (reg:CC_NOOV 100) (compare:CC_NOOV (neg:SI (match_dup 1)) + (const_int 0))) + (set (match_dup 0) (neg:SI (ltu:SI (reg:CC 100) (const_int 0))))] + "") + +(define_insn "*snesi_zero_extend" + [(set (match_operand:DI 0 "register_operand" "=r") + (ne:DI (match_operand:SI 1 "register_operand" "r") + (const_int 0))) + (clobber (reg:CC 100))] + "TARGET_ARCH64" + "#" + [(set_attr "type" "unary") + (set_attr "length" "2")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (ne:DI (match_operand:SI 1 "register_operand" "") + (const_int 0))) + (clobber (reg:CC 100))] + "TARGET_ARCH64" + [(set (reg:CC_NOOV 100) (compare:CC_NOOV (minus:SI (const_int 0) (match_dup 1)) + (const_int 0))) + (set (match_dup 0) (zero_extend:DI (plus:SI (plus:SI (const_int 0) + (const_int 0)) + (ltu:SI (reg:CC_NOOV 100) + (const_int 0)))))] + "") + +(define_insn "*snedi_zero" + [(set (match_operand:DI 0 "register_operand" "=&r") + (ne:DI (match_operand:DI 1 "register_operand" "r") + (const_int 0)))] + "TARGET_ARCH64" + "#" + [(set_attr "type" "cmove") + (set_attr "length" "2")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (ne:DI (match_operand:DI 1 "register_operand" "") + (const_int 0)))] + "TARGET_ARCH64" + [(set (match_dup 0) (const_int 0)) + (set (match_dup 0) (if_then_else:DI (ne:DI (match_dup 1) + (const_int 0)) + (const_int 1) + (match_dup 0)))] + "") + +(define_insn "*neg_snedi_zero" + [(set (match_operand:DI 0 "register_operand" "=&r") + (neg:DI (ne:DI (match_operand:DI 1 "register_operand" "r") + (const_int 0))))] + "TARGET_ARCH64" + "#" + [(set_attr "type" "cmove") + (set_attr "length" "2")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (neg:DI (ne:DI (match_operand:DI 1 "register_operand" "") + (const_int 0))))] + "TARGET_ARCH64" + [(set (match_dup 0) (const_int 0)) + (set (match_dup 0) (if_then_else:DI (ne:DI (match_dup 1) + (const_int 0)) + (const_int -1) + (match_dup 0)))] + "") + +(define_insn "*snedi_zero_trunc" + [(set (match_operand:SI 0 "register_operand" "=&r") + (ne:SI (match_operand:DI 1 "register_operand" "r") + (const_int 0)))] + "TARGET_ARCH64" + "#" + [(set_attr "type" "cmove") + (set_attr "length" "2")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (ne:SI (match_operand:DI 1 "register_operand" "") + (const_int 0)))] + "TARGET_ARCH64" + [(set (match_dup 0) (const_int 0)) + (set (match_dup 0) (if_then_else:SI (ne:DI (match_dup 1) + (const_int 0)) + (const_int 1) + (match_dup 0)))] + "") + +(define_insn "*seqsi_zero" + [(set (match_operand:SI 0 "register_operand" "=r") + (eq:SI (match_operand:SI 1 "register_operand" "r") + (const_int 0))) + (clobber (reg:CC 100))] + "! TARGET_LIVE_G0" + "#" + [(set_attr "length" "2")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (eq:SI (match_operand:SI 1 "register_operand" "") + (const_int 0))) + (clobber (reg:CC 100))] + "" + [(set (reg:CC_NOOV 100) (compare:CC_NOOV (neg:SI (match_dup 1)) + (const_int 0))) + (set (match_dup 0) (geu:SI (reg:CC 100) (const_int 0)))] + "") + +(define_insn "*neg_seqsi_zero" + [(set (match_operand:SI 0 "register_operand" "=r") + (neg:SI (eq:SI (match_operand:SI 1 "register_operand" "r") + (const_int 0)))) + (clobber (reg:CC 100))] + "! TARGET_LIVE_G0" + "#" + [(set_attr "length" "2")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (neg:SI (eq:SI (match_operand:SI 1 "register_operand" "") + (const_int 0)))) + (clobber (reg:CC 100))] + "" + [(set (reg:CC_NOOV 100) (compare:CC_NOOV (neg:SI (match_dup 1)) + (const_int 0))) + (set (match_dup 0) (neg:SI (geu:SI (reg:CC 100) (const_int 0))))] + "") + +(define_insn "*seqsi_zero_extend" + [(set (match_operand:DI 0 "register_operand" "=r") + (eq:DI (match_operand:SI 1 "register_operand" "r") + (const_int 0))) + (clobber (reg:CC 100))] + "TARGET_ARCH64" + "#" + [(set_attr "type" "unary") + (set_attr "length" "2")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (eq:DI (match_operand:SI 1 "register_operand" "") + (const_int 0))) + (clobber (reg:CC 100))] + "TARGET_ARCH64" + [(set (reg:CC_NOOV 100) (compare:CC_NOOV (minus:SI (const_int 0) (match_dup 1)) + (const_int 0))) + (set (match_dup 0) (zero_extend:DI (minus:SI (minus:SI (const_int 0) + (const_int -1)) + (ltu:SI (reg:CC_NOOV 100) + (const_int 0)))))] + "") + +(define_insn "*seqdi_zero" + [(set (match_operand:DI 0 "register_operand" "=&r") + (eq:DI (match_operand:DI 1 "register_operand" "r") + (const_int 0)))] + "TARGET_ARCH64" + "#" + [(set_attr "type" "cmove") + (set_attr "length" "2")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (eq:DI (match_operand:DI 1 "register_operand" "") + (const_int 0)))] + "TARGET_ARCH64" + [(set (match_dup 0) (const_int 0)) + (set (match_dup 0) (if_then_else:DI (eq:DI (match_dup 1) + (const_int 0)) + (const_int 1) + (match_dup 0)))] + "") + +(define_insn "*neg_seqdi_zero" + [(set (match_operand:DI 0 "register_operand" "=&r") + (neg:DI (eq:DI (match_operand:DI 1 "register_operand" "r") + (const_int 0))))] + "TARGET_ARCH64" + "#" + [(set_attr "type" "cmove") + (set_attr "length" "2")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (neg:DI (eq:DI (match_operand:DI 1 "register_operand" "") + (const_int 0))))] + "TARGET_ARCH64" + [(set (match_dup 0) (const_int 0)) + (set (match_dup 0) (if_then_else:DI (eq:DI (match_dup 1) + (const_int 0)) + (const_int -1) + (match_dup 0)))] + "") + +(define_insn "*seqdi_zero_trunc" + [(set (match_operand:SI 0 "register_operand" "=&r") + (eq:SI (match_operand:DI 1 "register_operand" "r") + (const_int 0)))] + "TARGET_ARCH64" + "#" + [(set_attr "type" "cmove") + (set_attr "length" "2")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (eq:SI (match_operand:DI 1 "register_operand" "") + (const_int 0)))] + "TARGET_ARCH64" + [(set (match_dup 0) (const_int 0)) + (set (match_dup 0) (if_then_else:SI (eq:DI (match_dup 1) + (const_int 0)) + (const_int 1) + (match_dup 0)))] + "") + +;; We can also do (x + (i == 0)) and related, so put them in. +;; ??? The addx/subx insns use the 32 bit carry flag so there are no DImode +;; versions for v9. + +(define_insn "*x_plus_i_ne_0" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (ne:SI (match_operand:SI 1 "register_operand" "r") + (const_int 0)) + (match_operand:SI 2 "register_operand" "r"))) + (clobber (reg:CC 100))] + "! TARGET_LIVE_G0" + "#" + [(set_attr "length" "2")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (plus:SI (ne:SI (match_operand:SI 1 "register_operand" "") + (const_int 0)) + (match_operand:SI 2 "register_operand" ""))) + (clobber (reg:CC 100))] + "! TARGET_LIVE_G0" + [(set (reg:CC_NOOV 100) (compare:CC_NOOV (neg:SI (match_dup 1)) + (const_int 0))) + (set (match_dup 0) (plus:SI (ltu:SI (reg:CC 100) (const_int 0)) + (match_dup 2)))] + "") + +(define_insn "*x_minus_i_ne_0" + [(set (match_operand:SI 0 "register_operand" "=r") + (minus:SI (match_operand:SI 2 "register_operand" "r") + (ne:SI (match_operand:SI 1 "register_operand" "r") + (const_int 0)))) + (clobber (reg:CC 100))] + "! TARGET_LIVE_G0" + "#" + [(set_attr "length" "2")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (minus:SI (match_operand:SI 2 "register_operand" "") + (ne:SI (match_operand:SI 1 "register_operand" "") + (const_int 0)))) + (clobber (reg:CC 100))] + "! TARGET_LIVE_G0" + [(set (reg:CC_NOOV 100) (compare:CC_NOOV (neg:SI (match_dup 1)) + (const_int 0))) + (set (match_dup 0) (minus:SI (match_dup 2) + (ltu:SI (reg:CC 100) (const_int 0))))] + "") + +(define_insn "*x_plus_i_eq_0" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (eq:SI (match_operand:SI 1 "register_operand" "r") + (const_int 0)) + (match_operand:SI 2 "register_operand" "r"))) + (clobber (reg:CC 100))] + "! TARGET_LIVE_G0" + "#" + [(set_attr "length" "2")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (plus:SI (eq:SI (match_operand:SI 1 "register_operand" "") + (const_int 0)) + (match_operand:SI 2 "register_operand" ""))) + (clobber (reg:CC 100))] + "! TARGET_LIVE_G0" + [(set (reg:CC_NOOV 100) (compare:CC_NOOV (neg:SI (match_dup 1)) + (const_int 0))) + (set (match_dup 0) (plus:SI (geu:SI (reg:CC 100) (const_int 0)) + (match_dup 2)))] + "") + +(define_insn "*x_minus_i_eq_0" + [(set (match_operand:SI 0 "register_operand" "=r") + (minus:SI (match_operand:SI 2 "register_operand" "r") + (eq:SI (match_operand:SI 1 "register_operand" "r") + (const_int 0)))) + (clobber (reg:CC 100))] + "! TARGET_LIVE_G0" + "#" + [(set_attr "length" "2")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (minus:SI (match_operand:SI 2 "register_operand" "") + (eq:SI (match_operand:SI 1 "register_operand" "") + (const_int 0)))) + (clobber (reg:CC 100))] + "! TARGET_LIVE_G0" + [(set (reg:CC_NOOV 100) (compare:CC_NOOV (neg:SI (match_dup 1)) + (const_int 0))) + (set (match_dup 0) (minus:SI (match_dup 2) + (geu:SI (reg:CC 100) (const_int 0))))] + "") + +;; We can also do GEU and LTU directly, but these operate after a compare. +;; ??? The addx/subx insns use the 32 bit carry flag so there are no DImode +;; versions for v9. + +(define_insn "*sltu_insn" + [(set (match_operand:SI 0 "register_operand" "=r") + (ltu:SI (reg:CC 100) (const_int 0)))] + "! TARGET_LIVE_G0" + "addx\\t%%g0, 0, %0" + [(set_attr "type" "misc") + (set_attr "length" "1")]) + +(define_insn "*neg_sltu_insn" + [(set (match_operand:SI 0 "register_operand" "=r") + (neg:SI (ltu:SI (reg:CC 100) (const_int 0))))] + "! TARGET_LIVE_G0" + "subx\\t%%g0, 0, %0" + [(set_attr "type" "misc") + (set_attr "length" "1")]) + +;; ??? Combine should canonicalize these next two to the same pattern. +(define_insn "*neg_sltu_minus_x" + [(set (match_operand:SI 0 "register_operand" "=r") + (minus:SI (neg:SI (ltu:SI (reg:CC 100) (const_int 0))) + (match_operand:SI 1 "arith_operand" "rI")))] + "! TARGET_LIVE_G0" + "subx\\t%%g0, %1, %0" + [(set_attr "type" "misc") + (set_attr "length" "1")]) + +(define_insn "*neg_sltu_plus_x" + [(set (match_operand:SI 0 "register_operand" "=r") + (neg:SI (plus:SI (ltu:SI (reg:CC 100) (const_int 0)) + (match_operand:SI 1 "arith_operand" "rI"))))] + "! TARGET_LIVE_G0" + "subx\\t%%g0, %1, %0" + [(set_attr "type" "misc") + (set_attr "length" "1")]) + +(define_insn "*sgeu_insn" + [(set (match_operand:SI 0 "register_operand" "=r") + (geu:SI (reg:CC 100) (const_int 0)))] + "! TARGET_LIVE_G0" + "subx\\t%%g0, -1, %0" + [(set_attr "type" "misc") + (set_attr "length" "1")]) + +(define_insn "*neg_sgeu_insn" + [(set (match_operand:SI 0 "register_operand" "=r") + (neg:SI (geu:SI (reg:CC 100) (const_int 0))))] + "! TARGET_LIVE_G0" + "addx\\t%%g0, -1, %0" + [(set_attr "type" "misc") + (set_attr "length" "1")]) + +;; We can also do (x + ((unsigned) i >= 0)) and related, so put them in. +;; ??? The addx/subx insns use the 32 bit carry flag so there are no DImode +;; versions for v9. + +(define_insn "*sltu_plus_x" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (ltu:SI (reg:CC 100) (const_int 0)) + (match_operand:SI 1 "arith_operand" "rI")))] + "! TARGET_LIVE_G0" + "addx\\t%%g0, %1, %0" + [(set_attr "type" "misc") + (set_attr "length" "1")]) + +(define_insn "*sltu_plus_x_plus_y" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (ltu:SI (reg:CC 100) (const_int 0)) + (plus:SI (match_operand:SI 1 "arith_operand" "%r") + (match_operand:SI 2 "arith_operand" "rI"))))] + "" + "addx\\t%1, %2, %0" + [(set_attr "type" "misc") + (set_attr "length" "1")]) + +(define_insn "*x_minus_sltu" + [(set (match_operand:SI 0 "register_operand" "=r") + (minus:SI (match_operand:SI 1 "register_operand" "r") + (ltu:SI (reg:CC 100) (const_int 0))))] + "" + "subx\\t%1, 0, %0" + [(set_attr "type" "misc") + (set_attr "length" "1")]) + +;; ??? Combine should canonicalize these next two to the same pattern. +(define_insn "*x_minus_y_minus_sltu" + [(set (match_operand:SI 0 "register_operand" "=r") + (minus:SI (minus:SI (match_operand:SI 1 "reg_or_0_operand" "rJ") + (match_operand:SI 2 "arith_operand" "rI")) + (ltu:SI (reg:CC 100) (const_int 0))))] + "" + "subx\\t%r1, %2, %0" + [(set_attr "type" "misc") + (set_attr "length" "1")]) + +(define_insn "*x_minus_sltu_plus_y" + [(set (match_operand:SI 0 "register_operand" "=r") + (minus:SI (match_operand:SI 1 "reg_or_0_operand" "rJ") + (plus:SI (ltu:SI (reg:CC 100) (const_int 0)) + (match_operand:SI 2 "arith_operand" "rI"))))] + "" + "subx\\t%r1, %2, %0" + [(set_attr "type" "misc") + (set_attr "length" "1")]) + +(define_insn "*sgeu_plus_x" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (geu:SI (reg:CC 100) (const_int 0)) + (match_operand:SI 1 "register_operand" "r")))] + "" + "subx\\t%1, -1, %0" + [(set_attr "type" "misc") + (set_attr "length" "1")]) + +(define_insn "*x_minus_sgeu" + [(set (match_operand:SI 0 "register_operand" "=r") + (minus:SI (match_operand:SI 1 "register_operand" "r") + (geu:SI (reg:CC 100) (const_int 0))))] + "" + "addx\\t%1, -1, %0" + [(set_attr "type" "misc") + (set_attr "length" "1")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "=r") + (match_operator:SI 2 "noov_compare_op" + [(match_operand 1 "icc_or_fcc_reg_operand" "") + (const_int 0)]))] + ;; 32 bit LTU/GEU are better implemented using addx/subx + "TARGET_V9 && REGNO (operands[1]) == SPARC_ICC_REG + && (GET_MODE (operands[1]) == CCXmode + || (GET_CODE (operands[2]) != LTU && GET_CODE (operands[2]) != GEU))" + [(set (match_dup 0) (const_int 0)) + (set (match_dup 0) + (if_then_else:SI (match_op_dup:SI 2 [(match_dup 1) (const_int 0)]) + (const_int 1) + (match_dup 0)))] + "") + + +;; These control RTL generation for conditional jump insns + +;; The quad-word fp compare library routines all return nonzero to indicate +;; true, which is different from the equivalent libgcc routines, so we must +;; handle them specially here. + +(define_expand "beq" + [(set (pc) + (if_then_else (eq (match_dup 1) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (TARGET_ARCH64 && sparc_compare_op1 == const0_rtx + && GET_CODE (sparc_compare_op0) == REG + && GET_MODE (sparc_compare_op0) == DImode) + { + emit_v9_brxx_insn (EQ, sparc_compare_op0, operands[0]); + DONE; + } + else if (GET_MODE (sparc_compare_op0) == TFmode && ! TARGET_HARD_QUAD) + { + emit_float_lib_cmp (sparc_compare_op0, sparc_compare_op1, EQ); + emit_jump_insn (gen_bne (operands[0])); + DONE; + } + operands[1] = gen_compare_reg (EQ, sparc_compare_op0, sparc_compare_op1); +}") + +(define_expand "bne" + [(set (pc) + (if_then_else (ne (match_dup 1) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (TARGET_ARCH64 && sparc_compare_op1 == const0_rtx + && GET_CODE (sparc_compare_op0) == REG + && GET_MODE (sparc_compare_op0) == DImode) + { + emit_v9_brxx_insn (NE, sparc_compare_op0, operands[0]); + DONE; + } + else if (GET_MODE (sparc_compare_op0) == TFmode && ! TARGET_HARD_QUAD) + { + emit_float_lib_cmp (sparc_compare_op0, sparc_compare_op1, NE); + emit_jump_insn (gen_bne (operands[0])); + DONE; + } + operands[1] = gen_compare_reg (NE, sparc_compare_op0, sparc_compare_op1); +}") + +(define_expand "bgt" + [(set (pc) + (if_then_else (gt (match_dup 1) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (TARGET_ARCH64 && sparc_compare_op1 == const0_rtx + && GET_CODE (sparc_compare_op0) == REG + && GET_MODE (sparc_compare_op0) == DImode) + { + emit_v9_brxx_insn (GT, sparc_compare_op0, operands[0]); + DONE; + } + else if (GET_MODE (sparc_compare_op0) == TFmode && ! TARGET_HARD_QUAD) + { + emit_float_lib_cmp (sparc_compare_op0, sparc_compare_op1, GT); + emit_jump_insn (gen_bne (operands[0])); + DONE; + } + operands[1] = gen_compare_reg (GT, sparc_compare_op0, sparc_compare_op1); +}") + +(define_expand "bgtu" + [(set (pc) + (if_then_else (gtu (match_dup 1) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ operands[1] = gen_compare_reg (GTU, sparc_compare_op0, sparc_compare_op1); +}") + +(define_expand "blt" + [(set (pc) + (if_then_else (lt (match_dup 1) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (TARGET_ARCH64 && sparc_compare_op1 == const0_rtx + && GET_CODE (sparc_compare_op0) == REG + && GET_MODE (sparc_compare_op0) == DImode) + { + emit_v9_brxx_insn (LT, sparc_compare_op0, operands[0]); + DONE; + } + else if (GET_MODE (sparc_compare_op0) == TFmode && ! TARGET_HARD_QUAD) + { + emit_float_lib_cmp (sparc_compare_op0, sparc_compare_op1, LT); + emit_jump_insn (gen_bne (operands[0])); + DONE; + } + operands[1] = gen_compare_reg (LT, sparc_compare_op0, sparc_compare_op1); +}") + +(define_expand "bltu" + [(set (pc) + (if_then_else (ltu (match_dup 1) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ operands[1] = gen_compare_reg (LTU, sparc_compare_op0, sparc_compare_op1); +}") + +(define_expand "bge" + [(set (pc) + (if_then_else (ge (match_dup 1) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (TARGET_ARCH64 && sparc_compare_op1 == const0_rtx + && GET_CODE (sparc_compare_op0) == REG + && GET_MODE (sparc_compare_op0) == DImode) + { + emit_v9_brxx_insn (GE, sparc_compare_op0, operands[0]); + DONE; + } + else if (GET_MODE (sparc_compare_op0) == TFmode && ! TARGET_HARD_QUAD) + { + emit_float_lib_cmp (sparc_compare_op0, sparc_compare_op1, GE); + emit_jump_insn (gen_bne (operands[0])); + DONE; + } + operands[1] = gen_compare_reg (GE, sparc_compare_op0, sparc_compare_op1); +}") + +(define_expand "bgeu" + [(set (pc) + (if_then_else (geu (match_dup 1) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ operands[1] = gen_compare_reg (GEU, sparc_compare_op0, sparc_compare_op1); +}") + +(define_expand "ble" + [(set (pc) + (if_then_else (le (match_dup 1) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (TARGET_ARCH64 && sparc_compare_op1 == const0_rtx + && GET_CODE (sparc_compare_op0) == REG + && GET_MODE (sparc_compare_op0) == DImode) + { + emit_v9_brxx_insn (LE, sparc_compare_op0, operands[0]); + DONE; + } + else if (GET_MODE (sparc_compare_op0) == TFmode && ! TARGET_HARD_QUAD) + { + emit_float_lib_cmp (sparc_compare_op0, sparc_compare_op1, LE); + emit_jump_insn (gen_bne (operands[0])); + DONE; + } + operands[1] = gen_compare_reg (LE, sparc_compare_op0, sparc_compare_op1); +}") + +(define_expand "bleu" + [(set (pc) + (if_then_else (leu (match_dup 1) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ operands[1] = gen_compare_reg (LEU, sparc_compare_op0, sparc_compare_op1); +}") + +;; Now match both normal and inverted jump. + +;; XXX fpcmp nop braindamage +(define_insn "*normal_branch" + [(set (pc) + (if_then_else (match_operator 0 "noov_compare_op" + [(reg 100) (const_int 0)]) + (label_ref (match_operand 1 "" "")) + (pc)))] + "" + "* +{ + return output_cbranch (operands[0], 1, 0, + final_sequence && INSN_ANNULLED_BRANCH_P (insn), + ! final_sequence, insn); +}" + [(set_attr "type" "branch")]) + +;; XXX fpcmp nop braindamage +(define_insn "*inverted_branch" + [(set (pc) + (if_then_else (match_operator 0 "noov_compare_op" + [(reg 100) (const_int 0)]) + (pc) + (label_ref (match_operand 1 "" ""))))] + "" + "* +{ + return output_cbranch (operands[0], 1, 1, + final_sequence && INSN_ANNULLED_BRANCH_P (insn), + ! final_sequence, insn); +}" + [(set_attr "type" "branch")]) + +;; XXX fpcmp nop braindamage +(define_insn "*normal_fp_branch" + [(set (pc) + (if_then_else (match_operator 1 "comparison_operator" + [(match_operand:CCFP 0 "fcc_reg_operand" "c") + (const_int 0)]) + (label_ref (match_operand 2 "" "")) + (pc)))] + "" + "* +{ + return output_cbranch (operands[1], 2, 0, + final_sequence && INSN_ANNULLED_BRANCH_P (insn), + ! final_sequence, insn); +}" + [(set_attr "type" "branch")]) + +;; XXX fpcmp nop braindamage +(define_insn "*inverted_fp_branch" + [(set (pc) + (if_then_else (match_operator 1 "comparison_operator" + [(match_operand:CCFP 0 "fcc_reg_operand" "c") + (const_int 0)]) + (pc) + (label_ref (match_operand 2 "" ""))))] + "" + "* +{ + return output_cbranch (operands[1], 2, 1, + final_sequence && INSN_ANNULLED_BRANCH_P (insn), + ! final_sequence, insn); +}" + [(set_attr "type" "branch")]) + +;; XXX fpcmp nop braindamage +(define_insn "*normal_fpe_branch" + [(set (pc) + (if_then_else (match_operator 1 "comparison_operator" + [(match_operand:CCFPE 0 "fcc_reg_operand" "c") + (const_int 0)]) + (label_ref (match_operand 2 "" "")) + (pc)))] + "" + "* +{ + return output_cbranch (operands[1], 2, 0, + final_sequence && INSN_ANNULLED_BRANCH_P (insn), + ! final_sequence, insn); +}" + [(set_attr "type" "branch")]) + +;; XXX fpcmp nop braindamage +(define_insn "*inverted_fpe_branch" + [(set (pc) + (if_then_else (match_operator 1 "comparison_operator" + [(match_operand:CCFPE 0 "fcc_reg_operand" "c") + (const_int 0)]) + (pc) + (label_ref (match_operand 2 "" ""))))] + "" + "* +{ + return output_cbranch (operands[1], 2, 1, + final_sequence && INSN_ANNULLED_BRANCH_P (insn), + ! final_sequence, insn); +}" + [(set_attr "type" "branch")]) + +;; Sparc V9-specific jump insns. None of these are guaranteed to be +;; in the architecture. + +;; There are no 32 bit brreg insns. + +;; XXX +(define_insn "*normal_int_branch_sp64" + [(set (pc) + (if_then_else (match_operator 0 "v9_regcmp_op" + [(match_operand:DI 1 "register_operand" "r") + (const_int 0)]) + (label_ref (match_operand 2 "" "")) + (pc)))] + "TARGET_ARCH64" + "* +{ + return output_v9branch (operands[0], 1, 2, 0, + final_sequence && INSN_ANNULLED_BRANCH_P (insn), + ! final_sequence, insn); +}" + [(set_attr "type" "branch")]) + +;; XXX +(define_insn "*inverted_int_branch_sp64" + [(set (pc) + (if_then_else (match_operator 0 "v9_regcmp_op" + [(match_operand:DI 1 "register_operand" "r") + (const_int 0)]) + (pc) + (label_ref (match_operand 2 "" ""))))] + "TARGET_ARCH64" + "* +{ + return output_v9branch (operands[0], 1, 2, 1, + final_sequence && INSN_ANNULLED_BRANCH_P (insn), + ! final_sequence, insn); +}" + [(set_attr "type" "branch")]) + +;; Load program counter insns. + +(define_insn "get_pc" + [(clobber (reg:SI 15)) + (set (match_operand 0 "register_operand" "=r") + (unspec [(match_operand 1 "" "") (match_operand 2 "" "")] 2))] + "flag_pic && REGNO (operands[0]) == 23" + "sethi\\t%%hi(%a1-4), %0\\n\\tcall\\t%a2\\n\\tadd\\t%0, %%lo(%a1+4), %0" + [(set_attr "length" "3")]) + +;; Currently unused... +;; (define_insn "get_pc_via_rdpc" +;; [(set (match_operand 0 "register_operand" "=r") (pc))] +;; "TARGET_V9" +;; "rd\\t%%pc, %0" +;; [(set_attr "type" "move")]) + + +;; Move instructions + +(define_expand "movqi" + [(set (match_operand:QI 0 "general_operand" "") + (match_operand:QI 1 "general_operand" ""))] + "" + " +{ + /* Working with CONST_INTs is easier, so convert + a double if needed. */ + if (GET_CODE (operands[1]) == CONST_DOUBLE) + { + operands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1]) & 0xff); + } + else if (GET_CODE (operands[1]) == CONST_INT) + { + /* And further, we know for all QI cases that only the + low byte is significant, which we can always process + in a single insn. So mask it now. */ + operands[1] = GEN_INT (INTVAL (operands[1]) & 0xff); + } + + /* Handle sets of MEM first. */ + if (GET_CODE (operands[0]) == MEM) + { + /* This checks TARGET_LIVE_G0 for us. */ + if (reg_or_0_operand (operands[1], QImode)) + goto movqi_is_ok; + + if (! reload_in_progress) + { + operands[0] = validize_mem (operands[0]); + operands[1] = force_reg (QImode, operands[1]); + } + } + + /* Fixup PIC cases. */ + if (flag_pic) + { + if (CONSTANT_P (operands[1]) + && pic_address_needs_scratch (operands[1])) + operands[1] = legitimize_pic_address (operands[1], QImode, 0); + + if (symbolic_operand (operands[1], QImode)) + { + operands[1] = legitimize_pic_address (operands[1], + QImode, + (reload_in_progress ? + operands[0] : + NULL_RTX)); + goto movqi_is_ok; + } + } + + /* All QI constants require only one insn, so proceed. */ + + movqi_is_ok: + ; +}") + +(define_insn "*movqi_insn" + [(set (match_operand:QI 0 "general_operand" "=r,r,m") + (match_operand:QI 1 "input_operand" "rI,m,rJ"))] + "(register_operand (operands[0], QImode) + || reg_or_0_operand (operands[1], QImode))" + "@ + mov\\t%1, %0 + ldub\\t%1, %0 + stb\\t%r1, %0" + [(set_attr "type" "move,load,store") + (set_attr "length" "1")]) + +(define_expand "movhi" + [(set (match_operand:HI 0 "general_operand" "") + (match_operand:HI 1 "general_operand" ""))] + "" + " +{ + /* Working with CONST_INTs is easier, so convert + a double if needed. */ + if (GET_CODE (operands[1]) == CONST_DOUBLE) + operands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1])); + + /* Handle sets of MEM first. */ + if (GET_CODE (operands[0]) == MEM) + { + /* This checks TARGET_LIVE_G0 for us. */ + if (reg_or_0_operand (operands[1], HImode)) + goto movhi_is_ok; + + if (! reload_in_progress) + { + operands[0] = validize_mem (operands[0]); + operands[1] = force_reg (HImode, operands[1]); + } + } + + /* Fixup PIC cases. */ + if (flag_pic) + { + if (CONSTANT_P (operands[1]) + && pic_address_needs_scratch (operands[1])) + operands[1] = legitimize_pic_address (operands[1], HImode, 0); + + if (symbolic_operand (operands[1], HImode)) + { + operands[1] = legitimize_pic_address (operands[1], + HImode, + (reload_in_progress ? + operands[0] : + NULL_RTX)); + goto movhi_is_ok; + } + } + + /* This makes sure we will not get rematched due to splittage. */ + if (! CONSTANT_P (operands[1]) || input_operand (operands[1], HImode)) + ; + else if (CONSTANT_P (operands[1]) + && GET_CODE (operands[1]) != HIGH + && GET_CODE (operands[1]) != LO_SUM) + { + sparc_emit_set_const32 (operands[0], operands[1]); + DONE; + } + movhi_is_ok: + ; +}") + +(define_insn "*movhi_const64_special" + [(set (match_operand:HI 0 "register_operand" "=r") + (match_operand:HI 1 "const64_high_operand" ""))] + "TARGET_ARCH64" + "sethi\\t%%hi(%a1), %0" + [(set_attr "type" "move") + (set_attr "length" "1")]) + +(define_insn "*movhi_insn" + [(set (match_operand:HI 0 "general_operand" "=r,r,r,m") + (match_operand:HI 1 "input_operand" "rI,K,m,rJ"))] + "(register_operand (operands[0], HImode) + || reg_or_0_operand (operands[1], HImode))" + "@ + mov\\t%1, %0 + sethi\\t%%hi(%a1), %0 + lduh\\t%1, %0 + sth\\t%r1, %0" + [(set_attr "type" "move,move,load,store") + (set_attr "length" "1")]) + +;; We always work with constants here. +(define_insn "*movhi_lo_sum" + [(set (match_operand:HI 0 "register_operand" "=r") + (ior:HI (match_operand:HI 1 "arith_operand" "%r") + (match_operand:HI 2 "arith_operand" "I")))] + "" + "or\\t%1, %2, %0" + [(set_attr "type" "ialu") + (set_attr "length" "1")]) + +(define_expand "movsi" + [(set (match_operand:SI 0 "general_operand" "") + (match_operand:SI 1 "general_operand" ""))] + "" + " +{ + /* Working with CONST_INTs is easier, so convert + a double if needed. */ + if (GET_CODE (operands[1]) == CONST_DOUBLE) + operands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1])); + + /* Handle sets of MEM first. */ + if (GET_CODE (operands[0]) == MEM) + { + /* This checks TARGET_LIVE_G0 for us. */ + if (reg_or_0_operand (operands[1], SImode)) + goto movsi_is_ok; + + if (! reload_in_progress) + { + operands[0] = validize_mem (operands[0]); + operands[1] = force_reg (SImode, operands[1]); + } + } + + /* Fixup PIC cases. */ + if (flag_pic) + { + if (CONSTANT_P (operands[1]) + && pic_address_needs_scratch (operands[1])) + operands[1] = legitimize_pic_address (operands[1], SImode, 0); + + if (GET_CODE (operands[1]) == LABEL_REF) + { + /* shit */ + emit_insn (gen_movsi_pic_label_ref (operands[0], operands[1])); + DONE; + } + + if (symbolic_operand (operands[1], SImode)) + { + operands[1] = legitimize_pic_address (operands[1], + SImode, + (reload_in_progress ? + operands[0] : + NULL_RTX)); + goto movsi_is_ok; + } + } + + /* If we are trying to toss an integer constant into the + FPU registers, force it into memory. */ + if (GET_CODE (operands[0]) == REG + && REGNO (operands[0]) >= SPARC_FIRST_FP_REG + && REGNO (operands[0]) <= SPARC_LAST_V9_FP_REG + && CONSTANT_P (operands[1])) + operands[1] = validize_mem (force_const_mem (GET_MODE (operands[0]), + operands[1])); + + /* This makes sure we will not get rematched due to splittage. */ + if (! CONSTANT_P (operands[1]) || input_operand (operands[1], SImode)) + ; + else if (CONSTANT_P (operands[1]) + && GET_CODE (operands[1]) != HIGH + && GET_CODE (operands[1]) != LO_SUM) + { + sparc_emit_set_const32 (operands[0], operands[1]); + DONE; + } + movsi_is_ok: + ; +}") + +;; Special LIVE_G0 pattern to obtain zero in a register. +(define_insn "*movsi_zero_liveg0" + [(set (match_operand:SI 0 "register_operand" "=r") + (match_operand:SI 1 "zero_operand" "J"))] + "TARGET_LIVE_G0" + "and\\t%0, 0, %0" + [(set_attr "type" "binary") + (set_attr "length" "1")]) + +;; This is needed to show CSE exactly which bits are set +;; in a 64-bit register by sethi instructions. +(define_insn "*movsi_const64_special" + [(set (match_operand:SI 0 "register_operand" "=r") + (match_operand:SI 1 "const64_high_operand" ""))] + "TARGET_ARCH64" + "sethi\\t%%hi(%a1), %0" + [(set_attr "type" "move") + (set_attr "length" "1")]) + +(define_insn "*movsi_insn" + [(set (match_operand:SI 0 "general_operand" "=r,f,r,r,r,f,m,m,d") + (match_operand:SI 1 "input_operand" "rI,!f,K,J,m,!m,rJ,!f,J"))] + "(register_operand (operands[0], SImode) + || reg_or_0_operand (operands[1], SImode))" + "@ + mov\\t%1, %0 + fmovs\\t%1, %0 + sethi\\t%%hi(%a1), %0 + clr\\t%0 + ld\\t%1, %0 + ld\\t%1, %0 + st\\t%r1, %0 + st\\t%1, %0 + fzeros\\t%0" + [(set_attr "type" "move,fpmove,move,move,load,fpload,store,fpstore,fpmove") + (set_attr "length" "1")]) + +(define_insn "*movsi_lo_sum" + [(set (match_operand:SI 0 "register_operand" "=r") + (lo_sum:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "immediate_operand" "in")))] + "" + "or\\t%1, %%lo(%a2), %0" + [(set_attr "type" "ialu") + (set_attr "length" "1")]) + +(define_insn "*movsi_high" + [(set (match_operand:SI 0 "register_operand" "=r") + (high:SI (match_operand:SI 1 "immediate_operand" "in")))] + "" + "sethi\\t%%hi(%a1), %0" + [(set_attr "type" "move") + (set_attr "length" "1")]) + +;; The next two patterns must wrap the SYMBOL_REF in an UNSPEC +;; so that CSE won't optimize the address computation away. +(define_insn "movsi_lo_sum_pic" + [(set (match_operand:SI 0 "register_operand" "=r") + (lo_sum:SI (match_operand:SI 1 "register_operand" "r") + (unspec:SI [(match_operand:SI 2 "immediate_operand" "in")] 0)))] + "flag_pic" + "or\\t%1, %%lo(%a2), %0" + [(set_attr "type" "ialu") + (set_attr "length" "1")]) + +(define_insn "movsi_high_pic" + [(set (match_operand:SI 0 "register_operand" "=r") + (high:SI (unspec:SI [(match_operand 1 "" "")] 0)))] + "flag_pic && check_pic (1)" + "sethi\\t%%hi(%a1), %0" + [(set_attr "type" "move") + (set_attr "length" "1")]) + +(define_expand "movsi_pic_label_ref" + [(set (match_dup 3) (high:SI + (unspec:SI [(match_operand:SI 1 "label_ref_operand" "") + (match_dup 2)] 5))) + (set (match_dup 4) (lo_sum:SI (match_dup 3) + (unspec:SI [(match_dup 1) (match_dup 2)] 5))) + (set (match_operand:SI 0 "register_operand" "=r") + (minus:SI (match_dup 5) (match_dup 4)))] + "flag_pic" + " +{ + current_function_uses_pic_offset_table = 1; + operands[2] = gen_rtx_SYMBOL_REF (Pmode, \"_GLOBAL_OFFSET_TABLE_\"); + operands[3] = gen_reg_rtx (SImode); + operands[4] = gen_reg_rtx (SImode); + operands[5] = pic_offset_table_rtx; +}") + +(define_insn "*movsi_high_pic_label_ref" + [(set (match_operand:SI 0 "register_operand" "=r") + (high:SI + (unspec:SI [(match_operand:SI 1 "label_ref_operand" "") + (match_operand:SI 2 "" "")] 5)))] + "flag_pic" + "sethi\\t%%hi(%a2-(%a1-.)), %0" + [(set_attr "type" "move") + (set_attr "length" "1")]) + +(define_insn "*movsi_lo_sum_pic_label_ref" + [(set (match_operand:SI 0 "register_operand" "=r") + (lo_sum:SI (match_operand:SI 1 "register_operand" "r") + (unspec:SI [(match_operand:SI 2 "label_ref_operand" "") + (match_operand:SI 3 "" "")] 5)))] + "flag_pic" + "or\\t%1, %%lo(%a3-(%a2-.)), %0" + [(set_attr "type" "ialu") + (set_attr "length" "1")]) + +(define_expand "movdi" + [(set (match_operand:DI 0 "reg_or_nonsymb_mem_operand" "") + (match_operand:DI 1 "general_operand" ""))] + "" + " +{ + /* Where possible, convert CONST_DOUBLE into a CONST_INT. */ + if (GET_CODE (operands[1]) == CONST_DOUBLE +#if HOST_BITS_PER_WIDE_INT != 64 + && ((CONST_DOUBLE_HIGH (operands[1]) == 0 + && (CONST_DOUBLE_LOW (operands[1]) & 0x80000000) == 0) + || (CONST_DOUBLE_HIGH (operands[1]) == 0xffffffff + && (CONST_DOUBLE_LOW (operands[1]) & 0x80000000) != 0)) +#endif + ) + operands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1])); + + /* Handle MEM cases first. */ + if (GET_CODE (operands[0]) == MEM) + { + /* If it's a REG, we can always do it. + The const zero case is more complex, on v9 + we can always perform it. */ + if (register_operand (operands[1], DImode) + || (TARGET_ARCH64 + && (operands[1] == const0_rtx))) + goto movdi_is_ok; + + if (! reload_in_progress) + { + operands[0] = validize_mem (operands[0]); + operands[1] = force_reg (DImode, operands[1]); + } + } + + if (flag_pic) + { + if (CONSTANT_P (operands[1]) + && pic_address_needs_scratch (operands[1])) + operands[1] = legitimize_pic_address (operands[1], DImode, 0); + + if (GET_CODE (operands[1]) == LABEL_REF) + { + if (! TARGET_ARCH64) + abort (); + emit_insn (gen_movdi_pic_label_ref (operands[0], operands[1])); + DONE; + } + + if (symbolic_operand (operands[1], DImode)) + { + operands[1] = legitimize_pic_address (operands[1], + DImode, + (reload_in_progress ? + operands[0] : + NULL_RTX)); + goto movdi_is_ok; + } + } + + /* If we are trying to toss an integer constant into the + FPU registers, force it into memory. */ + if (GET_CODE (operands[0]) == REG + && REGNO (operands[0]) >= SPARC_FIRST_FP_REG + && REGNO (operands[0]) <= SPARC_LAST_V9_FP_REG + && CONSTANT_P (operands[1])) + operands[1] = validize_mem (force_const_mem (GET_MODE (operands[0]), + operands[1])); + + /* This makes sure we will not get rematched due to splittage. */ + if (! CONSTANT_P (operands[1]) || input_operand (operands[1], DImode)) + ; + else if (TARGET_ARCH64 + && CONSTANT_P (operands[1]) + && GET_CODE (operands[1]) != HIGH + && GET_CODE (operands[1]) != LO_SUM) + { + sparc_emit_set_const64 (operands[0], operands[1]); + DONE; + } + + movdi_is_ok: + ; +}") + +;; Be careful, fmovd does not exist when !arch64. +;; We match MEM moves directly when we have correct even +;; numbered registers, but fall into splits otherwise. +;; The constraint ordering here is really important to +;; avoid insane problems in reload, especially for patterns +;; of the form: +;; +;; (set (mem:DI (plus:SI (reg:SI 30 %fp) +;; (const_int -5016))) +;; (reg:DI 2 %g2)) +;; +(define_insn "*movdi_insn_sp32" + [(set (match_operand:DI 0 "general_operand" "=T,U,o,r,r,r,?T,?f,?f,?o,?f") + (match_operand:DI 1 "input_operand" "U,T,r,o,i,r,f,T,o,f,f"))] + "! TARGET_ARCH64 && + (register_operand (operands[0], DImode) + || register_operand (operands[1], DImode))" + "@ + std\\t%1, %0 + ldd\\t%1, %0 + # + # + # + # + std\\t%1, %0 + ldd\\t%1, %0 + # + # + #" + [(set_attr "type" "store,load,*,*,*,*,fpstore,fpload,*,*,*") + (set_attr "length" "1,1,2,2,2,2,1,1,2,2,2")]) + +;; The following are generated by sparc_emit_set_const64 +(define_insn "*movdi_sp64_dbl" + [(set (match_operand:DI 0 "register_operand" "=r") + (match_operand:DI 1 "const64_operand" ""))] + "(TARGET_ARCH64 + && HOST_BITS_PER_WIDE_INT != 64)" + "mov\\t%1, %0" + [(set_attr "type" "move") + (set_attr "length" "1")]) + +;; This is needed to show CSE exactly which bits are set +;; in a 64-bit register by sethi instructions. +(define_insn "*movdi_const64_special" + [(set (match_operand:DI 0 "register_operand" "=r") + (match_operand:DI 1 "const64_high_operand" ""))] + "TARGET_ARCH64" + "sethi\\t%%hi(%a1), %0" + [(set_attr "type" "move") + (set_attr "length" "1")]) + +(define_insn "*movdi_insn_sp64" + [(set (match_operand:DI 0 "general_operand" "=r,r,r,r,m,?e,?e,?m,b") + (match_operand:DI 1 "input_operand" "rI,K,J,m,rJ,e,m,e,J"))] + "TARGET_ARCH64 && + (register_operand (operands[0], DImode) + || reg_or_0_operand (operands[1], DImode))" + "@ + mov\\t%1, %0 + sethi\\t%%hi(%a1), %0 + clr\\t%0 + ldx\\t%1, %0 + stx\\t%r1, %0 + fmovd\\t%1, %0 + ldd\\t%1, %0 + std\\t%1, %0 + fzero\\t%0" + [(set_attr "type" "move,move,move,load,store,fpmove,fpload,fpstore,fpmove") + (set_attr "length" "1")]) + +(define_expand "movdi_pic_label_ref" + [(set (match_dup 3) (high:DI + (unspec:DI [(match_operand:DI 1 "label_ref_operand" "") + (match_dup 2)] 5))) + (set (match_dup 4) (lo_sum:DI (match_dup 3) + (unspec:DI [(match_dup 1) (match_dup 2)] 5))) + (set (match_operand:DI 0 "register_operand" "=r") + (minus:DI (match_dup 5) (match_dup 4)))] + "TARGET_ARCH64 && flag_pic" + " +{ + current_function_uses_pic_offset_table = 1; + operands[2] = gen_rtx_SYMBOL_REF (Pmode, \"_GLOBAL_OFFSET_TABLE_\"); + operands[3] = gen_reg_rtx (DImode); + operands[4] = gen_reg_rtx (DImode); + operands[5] = pic_offset_table_rtx; +}") + +(define_insn "*movdi_high_pic_label_ref" + [(set (match_operand:DI 0 "register_operand" "=r") + (high:DI + (unspec:DI [(match_operand:DI 1 "label_ref_operand" "") + (match_operand:DI 2 "" "")] 5)))] + "TARGET_ARCH64 && flag_pic" + "sethi\\t%%hi(%a2-(%a1-.)), %0" + [(set_attr "type" "move") + (set_attr "length" "1")]) + +(define_insn "*movdi_lo_sum_pic_label_ref" + [(set (match_operand:DI 0 "register_operand" "=r") + (lo_sum:DI (match_operand:DI 1 "register_operand" "r") + (unspec:DI [(match_operand:DI 2 "label_ref_operand" "") + (match_operand:DI 3 "" "")] 5)))] + "TARGET_ARCH64 && flag_pic" + "or\\t%1, %%lo(%a3-(%a2-.)), %0" + [(set_attr "type" "ialu") + (set_attr "length" "1")]) + +;; Sparc-v9 code model support insns. See sparc_emit_set_symbolic_const64 +;; in sparc.c to see what is going on here... PIC stuff comes first. + +(define_insn "movdi_lo_sum_pic" + [(set (match_operand:DI 0 "register_operand" "=r") + (lo_sum:DI (match_operand:DI 1 "register_operand" "r") + (unspec:DI [(match_operand:DI 2 "immediate_operand" "in")] 0)))] + "TARGET_ARCH64 && flag_pic" + "or\\t%1, %%lo(%a2), %0" + [(set_attr "type" "ialu") + (set_attr "length" "1")]) + +(define_insn "movdi_high_pic" + [(set (match_operand:DI 0 "register_operand" "=r") + (high:DI (unspec:DI [(match_operand 1 "" "")] 0)))] + "TARGET_ARCH64 && flag_pic && check_pic (1)" + "sethi\\t%%hi(%a1), %0" + [(set_attr "type" "move") + (set_attr "length" "1")]) + +(define_insn "*sethi_di_medlow_embmedany_pic" + [(set (match_operand:DI 0 "register_operand" "=r") + (high:DI (match_operand:DI 1 "sp64_medium_pic_operand" "")))] + "(TARGET_CM_MEDLOW || TARGET_CM_EMBMEDANY) && check_pic (1)" + "sethi\\t%%lo(%a1), %0" + [(set_attr "type" "move") + (set_attr "length" "1")]) + +(define_insn "*sethi_di_medlow" + [(set (match_operand:DI 0 "register_operand" "=r") + (high:DI (match_operand:DI 1 "symbolic_operand" "")))] + "TARGET_CM_MEDLOW && check_pic (1)" + "sethi\\t%%hi(%a1), %0" + [(set_attr "type" "move") + (set_attr "length" "1")]) + +(define_insn "*losum_di_medlow" + [(set (match_operand:DI 0 "register_operand" "=r") + (lo_sum:DI (match_operand:DI 1 "register_operand" "r") + (match_operand:DI 2 "symbolic_operand" "")))] + "TARGET_CM_MEDLOW" + "or\\t%1, %%lo(%a2), %0" + [(set_attr "type" "ialu") + (set_attr "length" "1")]) + +(define_insn "seth44" + [(set (match_operand:DI 0 "register_operand" "=r") + (high:DI (unspec:DI [(match_operand:DI 1 "symbolic_operand" "")] 6)))] + "TARGET_CM_MEDMID" + "sethi\\t%%h44(%a1), %0" + [(set_attr "type" "move") + (set_attr "length" "1")]) + +(define_insn "setm44" + [(set (match_operand:DI 0 "register_operand" "=r") + (lo_sum:DI (match_operand:DI 1 "register_operand" "r") + (unspec:DI [(match_operand:DI 2 "symbolic_operand" "")] 7)))] + "TARGET_CM_MEDMID" + "or\\t%1, %%m44(%a2), %0" + [(set_attr "type" "move") + (set_attr "length" "1")]) + +(define_insn "setl44" + [(set (match_operand:DI 0 "register_operand" "=r") + (lo_sum:DI (match_operand:DI 1 "register_operand" "r") + (match_operand:DI 2 "symbolic_operand" "")))] + "TARGET_CM_MEDMID" + "or\\t%1, %%l44(%a2), %0" + [(set_attr "type" "ialu") + (set_attr "length" "1")]) + +(define_insn "sethh" + [(set (match_operand:DI 0 "register_operand" "=r") + (high:DI (unspec:DI [(match_operand:DI 1 "symbolic_operand" "")] 9)))] + "TARGET_CM_MEDANY" + "sethi\\t%%hh(%a1), %0" + [(set_attr "type" "move") + (set_attr "length" "1")]) + +(define_insn "setlm" + [(set (match_operand:DI 0 "register_operand" "=r") + (high:DI (unspec:DI [(match_operand:DI 1 "symbolic_operand" "")] 10)))] + "TARGET_CM_MEDANY" + "sethi\\t%%lm(%a1), %0" + [(set_attr "type" "move") + (set_attr "length" "1")]) + +(define_insn "sethm" + [(set (match_operand:DI 0 "register_operand" "=r") + (lo_sum:DI (match_operand:DI 1 "register_operand" "r") + (unspec:DI [(match_operand:DI 2 "symbolic_operand" "")] 18)))] + "TARGET_CM_MEDANY" + "or\\t%1, %%hm(%a2), %0" + [(set_attr "type" "ialu") + (set_attr "length" "1")]) + +(define_insn "setlo" + [(set (match_operand:DI 0 "register_operand" "=r") + (lo_sum:DI (match_operand:DI 1 "register_operand" "r") + (match_operand:DI 2 "symbolic_operand" "")))] + "TARGET_CM_MEDANY" + "or\\t%1, %%lo(%a2), %0" + [(set_attr "type" "ialu") + (set_attr "length" "1")]) + +(define_insn "embmedany_sethi" + [(set (match_operand:DI 0 "register_operand" "=r") + (high:DI (unspec:DI [(match_operand:DI 1 "data_segment_operand" "")] 11)))] + "TARGET_CM_EMBMEDANY && check_pic (1)" + "sethi\\t%%hi(%a1), %0" + [(set_attr "type" "move") + (set_attr "length" "1")]) + +(define_insn "embmedany_losum" + [(set (match_operand:DI 0 "register_operand" "=r") + (lo_sum:DI (match_operand:DI 1 "register_operand" "r") + (match_operand:DI 2 "data_segment_operand" "")))] + "TARGET_CM_EMBMEDANY" + "add\\t%1, %%lo(%a2), %0" + [(set_attr "type" "ialu") + (set_attr "length" "1")]) + +(define_insn "embmedany_brsum" + [(set (match_operand:DI 0 "register_operand" "=r") + (unspec:DI [(match_operand:DI 1 "register_operand" "r")] 11))] + "TARGET_CM_EMBMEDANY" + "add\\t%1, %_, %0" + [(set_attr "length" "1")]) + +(define_insn "embmedany_textuhi" + [(set (match_operand:DI 0 "register_operand" "=r") + (high:DI (unspec:DI [(match_operand:DI 1 "text_segment_operand" "")] 13)))] + "TARGET_CM_EMBMEDANY && check_pic (1)" + "sethi\\t%%uhi(%a1), %0" + [(set_attr "type" "move") + (set_attr "length" "1")]) + +(define_insn "embmedany_texthi" + [(set (match_operand:DI 0 "register_operand" "=r") + (high:DI (unspec:DI [(match_operand:DI 1 "text_segment_operand" "")] 14)))] + "TARGET_CM_EMBMEDANY && check_pic (1)" + "sethi\\t%%hi(%a1), %0" + [(set_attr "type" "move") + (set_attr "length" "1")]) + +(define_insn "embmedany_textulo" + [(set (match_operand:DI 0 "register_operand" "=r") + (lo_sum:DI (match_operand:DI 1 "register_operand" "r") + (unspec:DI [(match_operand:DI 2 "text_segment_operand" "")] 15)))] + "TARGET_CM_EMBMEDANY" + "or\\t%1, %%ulo(%a2), %0" + [(set_attr "type" "ialu") + (set_attr "length" "1")]) + +(define_insn "embmedany_textlo" + [(set (match_operand:DI 0 "register_operand" "=r") + (lo_sum:DI (match_operand:DI 1 "register_operand" "r") + (match_operand:DI 2 "text_segment_operand" "")))] + "TARGET_CM_EMBMEDANY" + "or\\t%1, %%lo(%a2), %0" + [(set_attr "type" "ialu") + (set_attr "length" "1")]) + +;; Now some patterns to help reload out a bit. +(define_expand "reload_indi" + [(parallel [(match_operand:DI 0 "register_operand" "=r") + (match_operand:DI 1 "immediate_operand" "") + (match_operand:TI 2 "register_operand" "=&r")])] + "(TARGET_CM_MEDANY + || TARGET_CM_EMBMEDANY) + && ! flag_pic" + " +{ + sparc_emit_set_symbolic_const64 (operands[0], operands[1], + gen_rtx_REG (DImode, REGNO (operands[2]))); + DONE; +}") + +(define_expand "reload_outdi" + [(parallel [(match_operand:DI 0 "register_operand" "=r") + (match_operand:DI 1 "immediate_operand" "") + (match_operand:TI 2 "register_operand" "=&r")])] + "(TARGET_CM_MEDANY + || TARGET_CM_EMBMEDANY) + && ! flag_pic" + " +{ + sparc_emit_set_symbolic_const64 (operands[0], operands[1], + gen_rtx_REG (DImode, REGNO (operands[2]))); + DONE; +}") + +;; Split up putting CONSTs and REGs into DI regs when !arch64 +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (match_operand:DI 1 "const_int_operand" ""))] + "! TARGET_ARCH64 && reload_completed" + [(clobber (const_int 0))] + " +{ + emit_insn (gen_movsi (gen_highpart (SImode, operands[0]), + (INTVAL (operands[1]) < 0) ? + constm1_rtx : + const0_rtx)); + emit_insn (gen_movsi (gen_lowpart (SImode, operands[0]), + operands[1])); + DONE; +}") + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (match_operand:DI 1 "const_double_operand" ""))] + "! TARGET_ARCH64 && reload_completed" + [(clobber (const_int 0))] + " +{ + emit_insn (gen_movsi (gen_highpart (SImode, operands[0]), + GEN_INT (CONST_DOUBLE_HIGH (operands[1])))); + + /* Slick... but this trick loses if this subreg constant part + can be done in one insn. */ + if (CONST_DOUBLE_LOW (operands[1]) == CONST_DOUBLE_HIGH (operands[1]) + && !(SPARC_SETHI_P (CONST_DOUBLE_HIGH (operands[1])) + || SPARC_SIMM13_P (CONST_DOUBLE_HIGH (operands[1])))) + { + emit_insn (gen_movsi (gen_lowpart (SImode, operands[0]), + gen_highpart (SImode, operands[0]))); + } + else + { + emit_insn (gen_movsi (gen_lowpart (SImode, operands[0]), + GEN_INT (CONST_DOUBLE_LOW (operands[1])))); + } + DONE; +}") + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (match_operand:DI 1 "register_operand" ""))] + "! TARGET_ARCH64 && reload_completed" + [(clobber (const_int 0))] + " +{ + rtx set_dest = operands[0]; + rtx set_src = operands[1]; + rtx dest1, dest2; + rtx src1, src2; + + if (GET_CODE (set_dest) == SUBREG) + set_dest = alter_subreg (set_dest); + if (GET_CODE (set_src) == SUBREG) + set_src = alter_subreg (set_src); + + dest1 = gen_highpart (SImode, set_dest); + dest2 = gen_lowpart (SImode, set_dest); + src1 = gen_highpart (SImode, set_src); + src2 = gen_lowpart (SImode, set_src); + + /* Now emit using the real source and destination we found, swapping + the order if we detect overlap. */ + if (reg_overlap_mentioned_p (dest1, src2)) + { + emit_insn (gen_movsi (dest2, src2)); + emit_insn (gen_movsi (dest1, src1)); + } + else + { + emit_insn (gen_movsi (dest1, src1)); + emit_insn (gen_movsi (dest2, src2)); + } + DONE; +}") + +;; Now handle the cases of memory moves from/to non-even +;; DI mode register pairs. +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (match_operand:DI 1 "memory_operand" ""))] + "(! TARGET_ARCH64 + && reload_completed + && sparc_splitdi_legitimate (operands[0], operands[1]))" + [(clobber (const_int 0))] + " +{ + rtx word0 = change_address (operands[1], SImode, NULL_RTX); + rtx word1 = change_address (operands[1], SImode, + plus_constant_for_output (XEXP (word0, 0), 4)); + rtx high_part = gen_highpart (SImode, operands[0]); + rtx low_part = gen_lowpart (SImode, operands[0]); + + if (reg_overlap_mentioned_p (high_part, word1)) + { + emit_insn (gen_movsi (low_part, word1)); + emit_insn (gen_movsi (high_part, word0)); + } + else + { + emit_insn (gen_movsi (high_part, word0)); + emit_insn (gen_movsi (low_part, word1)); + } + DONE; +}") + +(define_split + [(set (match_operand:DI 0 "memory_operand" "") + (match_operand:DI 1 "register_operand" ""))] + "(! TARGET_ARCH64 + && reload_completed + && sparc_splitdi_legitimate (operands[1], operands[0]))" + [(clobber (const_int 0))] + " +{ + rtx word0 = change_address (operands[0], SImode, NULL_RTX); + rtx word1 = change_address (operands[0], SImode, + plus_constant_for_output (XEXP (word0, 0), 4)); + rtx high_part = gen_highpart (SImode, operands[1]); + rtx low_part = gen_lowpart (SImode, operands[1]); + + emit_insn (gen_movsi (word0, high_part)); + emit_insn (gen_movsi (word1, low_part)); + DONE; +}") + + +;; Floating point move insns + +(define_insn "*clear_sf" + [(set (match_operand:SF 0 "general_operand" "=f") + (match_operand:SF 1 "" ""))] + "TARGET_VIS + && GET_CODE (operands[1]) == CONST_DOUBLE + && GET_CODE (operands[0]) == REG + && fp_zero_operand (operands[1])" + "fzeros\\t%0" + [(set_attr "type" "fpmove") + (set_attr "length" "1")]) + +(define_insn "*movsf_const_intreg" + [(set (match_operand:SF 0 "general_operand" "=f,r") + (match_operand:SF 1 "" "m,F"))] + "TARGET_FPU + && GET_CODE (operands[1]) == CONST_DOUBLE + && GET_CODE (operands[0]) == REG" + "* +{ + REAL_VALUE_TYPE r; + long i; + + if (which_alternative == 0) + return \"ld\\t%1, %0\"; + + REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]); + REAL_VALUE_TO_TARGET_SINGLE (r, i); + if (SPARC_SIMM13_P (i) || SPARC_SETHI_P (i)) + { + operands[1] = GEN_INT (i); + if (SPARC_SIMM13_P (INTVAL (operands[1]))) + return \"mov\\t%1, %0\"; + else if (SPARC_SETHI_P (INTVAL (operands[1]))) + return \"sethi\\t%%hi(%a1), %0\"; + else + abort (); + } + else + return \"#\"; +}" + [(set_attr "type" "move") + (set_attr "length" "1")]) + +;; There isn't much I can do about this, if I change the +;; mode then flow info gets really confused because the +;; destination no longer looks the same. Ho hum... +(define_insn "*movsf_const_high" + [(set (match_operand:SF 0 "register_operand" "=r") + (unspec:SF [(match_operand 1 "const_int_operand" "")] 12))] + "" + "sethi\\t%%hi(%a1), %0" + [(set_attr "type" "move") + (set_attr "length" "1")]) + +(define_insn "*movsf_const_lo" + [(set (match_operand:SF 0 "register_operand" "=r") + (unspec:SF [(match_operand 1 "register_operand" "r") + (match_operand 2 "const_int_operand" "")] 17))] + "" + "or\\t%1, %%lo(%a2), %0" + [(set_attr "type" "move") + (set_attr "length" "1")]) + +(define_split + [(set (match_operand:SF 0 "register_operand" "") + (match_operand:SF 1 "const_double_operand" ""))] + "TARGET_FPU + && (GET_CODE (operands[0]) == REG + && REGNO (operands[0]) < 32)" + [(set (match_dup 0) (unspec:SF [(match_dup 1)] 12)) + (set (match_dup 0) (unspec:SF [(match_dup 0) (match_dup 1)] 17))] + " +{ + REAL_VALUE_TYPE r; + long i; + + REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]); + REAL_VALUE_TO_TARGET_SINGLE (r, i); + operands[1] = GEN_INT (i); +}") + +(define_expand "movsf" + [(set (match_operand:SF 0 "general_operand" "") + (match_operand:SF 1 "general_operand" ""))] + "" + " +{ + /* Force SFmode constants into memory. */ + if (GET_CODE (operands[0]) == REG + && CONSTANT_P (operands[1])) + { + if (TARGET_VIS + && GET_CODE (operands[1]) == CONST_DOUBLE + && fp_zero_operand (operands[1])) + goto movsf_is_ok; + + /* emit_group_store will send such bogosity to us when it is + not storing directly into memory. So fix this up to avoid + crashes in output_constant_pool. */ + if (operands [1] == const0_rtx) + operands[1] = CONST0_RTX (SFmode); + operands[1] = validize_mem (force_const_mem (GET_MODE (operands[0]), + operands[1])); + } + + /* Handle sets of MEM first. */ + if (GET_CODE (operands[0]) == MEM) + { + if (register_operand (operands[1], SFmode)) + goto movsf_is_ok; + + if (! reload_in_progress) + { + operands[0] = validize_mem (operands[0]); + operands[1] = force_reg (SFmode, operands[1]); + } + } + + /* Fixup PIC cases. */ + if (flag_pic) + { + if (CONSTANT_P (operands[1]) + && pic_address_needs_scratch (operands[1])) + operands[1] = legitimize_pic_address (operands[1], SFmode, 0); + + if (symbolic_operand (operands[1], SFmode)) + { + operands[1] = legitimize_pic_address (operands[1], + SFmode, + (reload_in_progress ? + operands[0] : + NULL_RTX)); + } + } + + movsf_is_ok: + ; +}") + +(define_insn "*movsf_insn" + [(set (match_operand:SF 0 "general_operand" "=f,f,m,r,r,m") + (match_operand:SF 1 "input_operand" "f,m,f,r,m,r"))] + "TARGET_FPU + && (register_operand (operands[0], SFmode) + || register_operand (operands[1], SFmode))" + "@ + fmovs\\t%1, %0 + ld\\t%1, %0 + st\\t%1, %0 + mov\\t%1, %0 + ld\\t%1, %0 + st\\t%1, %0" + [(set_attr "type" "fpmove,fpload,fpstore,move,load,store") + (set_attr "length" "1")]) + +;; Exactly the same as above, except that all `f' cases are deleted. +;; This is necessary to prevent reload from ever trying to use a `f' reg +;; when -mno-fpu. + +(define_insn "*movsf_no_f_insn" + [(set (match_operand:SF 0 "general_operand" "=r,r,m") + (match_operand:SF 1 "input_operand" "r,m,r"))] + "! TARGET_FPU + && (register_operand (operands[0], SFmode) + || register_operand (operands[1], SFmode))" + "@ + mov\\t%1, %0 + ld\\t%1, %0 + st\\t%1, %0" + [(set_attr "type" "move,load,store") + (set_attr "length" "1")]) + +(define_insn "*clear_df" + [(set (match_operand:DF 0 "general_operand" "=e") + (match_operand:DF 1 "" ""))] + "TARGET_VIS + && GET_CODE (operands[1]) == CONST_DOUBLE + && GET_CODE (operands[0]) == REG + && fp_zero_operand (operands[1])" + "fzero\\t%0" + [(set_attr "type" "fpmove") + (set_attr "length" "1")]) + +(define_insn "*movdf_const_intreg_sp32" + [(set (match_operand:DF 0 "general_operand" "=e,e,r") + (match_operand:DF 1 "" "T,o,F"))] + "TARGET_FPU && ! TARGET_ARCH64 + && GET_CODE (operands[1]) == CONST_DOUBLE + && GET_CODE (operands[0]) == REG" + "* +{ + if (which_alternative == 0) + return \"ldd\\t%1, %0\"; + else + return \"#\"; +}" + [(set_attr "type" "move") + (set_attr "length" "1,2,2")]) + +;; Now that we redo life analysis with a clean slate after +;; instruction splitting for sched2 this can work. +(define_insn "*movdf_const_intreg_sp64" + [(set (match_operand:DF 0 "general_operand" "=e,e,r") + (match_operand:DF 1 "" "m,o,F"))] + "TARGET_FPU + && TARGET_ARCH64 + && GET_CODE (operands[1]) == CONST_DOUBLE + && GET_CODE (operands[0]) == REG" + "* +{ + if (which_alternative == 0) + return \"ldd\\t%1, %0\"; + else + return \"#\"; +}" + [(set_attr "type" "move") + (set_attr "length" "1")]) + +(define_split + [(set (match_operand:DF 0 "register_operand" "") + (match_operand:DF 1 "const_double_operand" ""))] + "TARGET_FPU + && GET_CODE (operands[1]) == CONST_DOUBLE + && (GET_CODE (operands[0]) == REG + && REGNO (operands[0]) < 32) + && reload_completed" + [(clobber (const_int 0))] + " +{ + REAL_VALUE_TYPE r; + long l[2]; + + REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]); + REAL_VALUE_TO_TARGET_DOUBLE (r, l); + if (GET_CODE (operands[0]) == SUBREG) + operands[0] = alter_subreg (operands[0]); + operands[0] = gen_rtx_raw_REG (DImode, REGNO (operands[0])); + + if (TARGET_ARCH64) + { +#if HOST_BITS_PER_WIDE_INT == 64 + HOST_WIDE_INT val; + + val = ((HOST_WIDE_INT)l[1] | + ((HOST_WIDE_INT)l[0] << 32)); + emit_insn (gen_movdi (operands[0], GEN_INT (val))); +#else + emit_insn (gen_movdi (operands[0], + gen_rtx_CONST_DOUBLE (VOIDmode, const0_rtx, + l[1], l[0]))); +#endif + } + else + { + emit_insn (gen_movsi (gen_highpart (SImode, operands[0]), + GEN_INT (l[0]))); + + /* Slick... but this trick loses if this subreg constant part + can be done in one insn. */ + if (l[1] == l[0] + && !(SPARC_SETHI_P (l[0]) + || SPARC_SIMM13_P (l[0]))) + { + emit_insn (gen_movsi (gen_lowpart (SImode, operands[0]), + gen_highpart (SImode, operands[0]))); + } + else + { + emit_insn (gen_movsi (gen_lowpart (SImode, operands[0]), + GEN_INT (l[1]))); + } + } + DONE; +}") + +(define_expand "movdf" + [(set (match_operand:DF 0 "general_operand" "") + (match_operand:DF 1 "general_operand" ""))] + "" + " +{ + /* Force DFmode constants into memory. */ + if (GET_CODE (operands[0]) == REG + && CONSTANT_P (operands[1])) + { + if (TARGET_VIS + && GET_CODE (operands[1]) == CONST_DOUBLE + && fp_zero_operand (operands[1])) + goto movdf_is_ok; + + /* emit_group_store will send such bogosity to us when it is + not storing directly into memory. So fix this up to avoid + crashes in output_constant_pool. */ + if (operands [1] == const0_rtx) + operands[1] = CONST0_RTX (DFmode); + operands[1] = validize_mem (force_const_mem (GET_MODE (operands[0]), + operands[1])); + } + + /* Handle MEM cases first. */ + if (GET_CODE (operands[0]) == MEM) + { + if (register_operand (operands[1], DFmode)) + goto movdf_is_ok; + + if (! reload_in_progress) + { + operands[0] = validize_mem (operands[0]); + operands[1] = force_reg (DFmode, operands[1]); + } + } + + /* Fixup PIC cases. */ + if (flag_pic) + { + if (CONSTANT_P (operands[1]) + && pic_address_needs_scratch (operands[1])) + operands[1] = legitimize_pic_address (operands[1], DFmode, 0); + + if (symbolic_operand (operands[1], DFmode)) + { + operands[1] = legitimize_pic_address (operands[1], + DFmode, + (reload_in_progress ? + operands[0] : + NULL_RTX)); + } + } + + movdf_is_ok: + ; +}") + +;; Be careful, fmovd does not exist when !v9. +(define_insn "*movdf_insn_sp32" + [(set (match_operand:DF 0 "general_operand" "=e,T,U,T,e,r,r,o,e,o") + (match_operand:DF 1 "input_operand" "T,e,T,U,e,r,o,r,o,e"))] + "TARGET_FPU + && ! TARGET_V9 + && (register_operand (operands[0], DFmode) + || register_operand (operands[1], DFmode))" + "@ + ldd\\t%1, %0 + std\\t%1, %0 + ldd\\t%1, %0 + std\\t%1, %0 + # + # + # + # + # + #" + [(set_attr "type" "fpload,fpstore,load,store,*,*,*,*,*,*") + (set_attr "length" "1,1,1,1,2,2,2,2,2,2")]) + +(define_insn "*movdf_no_e_insn_sp32" + [(set (match_operand:DF 0 "general_operand" "=U,T,r,r,o") + (match_operand:DF 1 "input_operand" "T,U,r,o,r"))] + "! TARGET_FPU + && ! TARGET_ARCH64 + && (register_operand (operands[0], DFmode) + || register_operand (operands[1], DFmode))" + "@ + ldd\\t%1, %0 + std\\t%1, %0 + # + # + #" + [(set_attr "type" "load,store,*,*,*") + (set_attr "length" "1,1,2,2,2")]) + +;; We have available v9 double floats but not 64-bit +;; integer registers. +(define_insn "*movdf_insn_v9only" + [(set (match_operand:DF 0 "general_operand" "=e,e,m,U,T,r,r,o") + (match_operand:DF 1 "input_operand" "e,m,e,T,U,r,o,r"))] + "TARGET_FPU + && TARGET_V9 + && ! TARGET_ARCH64 + && (register_operand (operands[0], DFmode) + || register_operand (operands[1], DFmode))" + "@ + fmovd\\t%1, %0 + ldd\\t%1, %0 + std\\t%1, %0 + ldd\\t%1, %0 + std\\t%1, %0 + # + # + #" + [(set_attr "type" "fpmove,load,store,load,store,*,*,*") + (set_attr "length" "1,1,1,1,1,2,2,2")]) + +;; We have available both v9 double floats and 64-bit +;; integer registers. +(define_insn "*movdf_insn_sp64" + [(set (match_operand:DF 0 "general_operand" "=e,e,m,r,r,m") + (match_operand:DF 1 "input_operand" "e,m,e,r,m,r"))] + "TARGET_FPU + && TARGET_V9 + && TARGET_ARCH64 + && (register_operand (operands[0], DFmode) + || register_operand (operands[1], DFmode))" + "@ + fmovd\\t%1, %0 + ldd\\t%1, %0 + std\\t%1, %0 + mov\\t%1, %0 + ldx\\t%1, %0 + stx\\t%1, %0" + [(set_attr "type" "fpmove,load,store,move,load,store") + (set_attr "length" "1")]) + +(define_insn "*movdf_no_e_insn_sp64" + [(set (match_operand:DF 0 "general_operand" "=r,r,m") + (match_operand:DF 1 "input_operand" "r,m,r"))] + "! TARGET_FPU + && TARGET_ARCH64 + && (register_operand (operands[0], DFmode) + || register_operand (operands[1], DFmode))" + "@ + mov\\t%1, %0 + ldx\\t%1, %0 + stx\\t%1, %0" + [(set_attr "type" "move,load,store") + (set_attr "length" "1")]) + +;; Ok, now the splits to handle all the multi insn and +;; mis-aligned memory address cases. +;; In these splits please take note that we must be +;; careful when V9 but not ARCH64 because the integer +;; register DFmode cases must be handled. +(define_split + [(set (match_operand:DF 0 "register_operand" "") + (match_operand:DF 1 "register_operand" ""))] + "(! TARGET_V9 + || (! TARGET_ARCH64 + && ((GET_CODE (operands[0]) == REG + && REGNO (operands[0]) < 32) + || (GET_CODE (operands[0]) == SUBREG + && GET_CODE (SUBREG_REG (operands[0])) == REG + && REGNO (SUBREG_REG (operands[0])) < 32)))) + && reload_completed" + [(clobber (const_int 0))] + " +{ + rtx set_dest = operands[0]; + rtx set_src = operands[1]; + rtx dest1, dest2; + rtx src1, src2; + + if (GET_CODE (set_dest) == SUBREG) + set_dest = alter_subreg (set_dest); + if (GET_CODE (set_src) == SUBREG) + set_src = alter_subreg (set_src); + + dest1 = gen_highpart (SFmode, set_dest); + dest2 = gen_lowpart (SFmode, set_dest); + src1 = gen_highpart (SFmode, set_src); + src2 = gen_lowpart (SFmode, set_src); + + /* Now emit using the real source and destination we found, swapping + the order if we detect overlap. */ + if (reg_overlap_mentioned_p (dest1, src2)) + { + emit_insn (gen_movsf (dest2, src2)); + emit_insn (gen_movsf (dest1, src1)); + } + else + { + emit_insn (gen_movsf (dest1, src1)); + emit_insn (gen_movsf (dest2, src2)); + } + DONE; +}") + +(define_split + [(set (match_operand:DF 0 "register_operand" "") + (match_operand:DF 1 "memory_operand" ""))] + "((! TARGET_V9 + || (! TARGET_ARCH64 + && ((GET_CODE (operands[0]) == REG + && REGNO (operands[0]) < 32) + || (GET_CODE (operands[0]) == SUBREG + && GET_CODE (SUBREG_REG (operands[0])) == REG + && REGNO (SUBREG_REG (operands[0])) < 32)))) + && (reload_completed + && (((REGNO (operands[0])) % 2) != 0 + || ! mem_min_alignment (operands[1], 8)) + && offsettable_memref_p (operands[1])))" + [(clobber (const_int 0))] + " +{ + rtx word0 = change_address (operands[1], SFmode, NULL_RTX); + rtx word1 = change_address (operands[1], SFmode, + plus_constant_for_output (XEXP (word0, 0), 4)); + rtx src1, src2; + + if (GET_CODE (operands[0]) == SUBREG) + operands[0] = alter_subreg (operands[0]); + + if (GET_CODE (operands[0]) == REG) + { + src1 = gen_rtx_REG (SFmode, REGNO (operands[0])); + src2 = gen_rtx_REG (SFmode, REGNO (operands[0]) + 1); + } + else + { + src1 = gen_highpart (SFmode, operands[0]); + src2 = gen_lowpart (SFmode, operands[0]); + } + + if (reg_overlap_mentioned_p (src1, word1)) + { + emit_insn (gen_movsf (src2, word1)); + emit_insn (gen_movsf (src1, word0)); + } + else + { + emit_insn (gen_movsf (src1, word0)); + emit_insn (gen_movsf (src2, word1)); + } + DONE; +}") + +(define_split + [(set (match_operand:DF 0 "memory_operand" "") + (match_operand:DF 1 "register_operand" ""))] + "((! TARGET_V9 + || (! TARGET_ARCH64 + && ((GET_CODE (operands[1]) == REG + && REGNO (operands[1]) < 32) + || (GET_CODE (operands[1]) == SUBREG + && GET_CODE (SUBREG_REG (operands[1])) == REG + && REGNO (SUBREG_REG (operands[1])) < 32)))) + && (reload_completed + && (((REGNO (operands[1])) % 2) != 0 + || ! mem_min_alignment (operands[0], 8)) + && offsettable_memref_p (operands[0])))" + [(clobber (const_int 0))] + " +{ + rtx word0 = change_address (operands[0], SFmode, NULL_RTX); + rtx word1 = change_address (operands[0], SFmode, + plus_constant_for_output (XEXP (word0, 0), 4)); + rtx dest1, dest2; + + if (GET_CODE (operands[1]) == SUBREG) + operands[1] = alter_subreg (operands[1]); + + if (GET_CODE (operands[1]) == REG) + { + dest1 = gen_rtx_REG (SFmode, REGNO (operands[1])); + dest2 = gen_rtx_REG (SFmode, REGNO (operands[1]) + 1); + } + else + { + dest1 = gen_highpart (SFmode, operands[1]); + dest2 = gen_lowpart (SFmode, operands[1]); + } + + emit_insn (gen_movsf (word0, dest1)); + emit_insn (gen_movsf (word1, dest2)); + DONE; +}") + +(define_expand "movtf" + [(set (match_operand:TF 0 "general_operand" "") + (match_operand:TF 1 "general_operand" ""))] + "" + " +{ + /* Force TFmode constants into memory. */ + if (GET_CODE (operands[0]) == REG + && CONSTANT_P (operands[1])) + { + /* emit_group_store will send such bogosity to us when it is + not storing directly into memory. So fix this up to avoid + crashes in output_constant_pool. */ + if (operands [1] == const0_rtx) + operands[1] = CONST0_RTX (TFmode); + operands[1] = validize_mem (force_const_mem (GET_MODE (operands[0]), + operands[1])); + } + + /* Handle MEM cases first, note that only v9 guarentees + full 16-byte alignment for quads. */ + if (GET_CODE (operands[0]) == MEM) + { + if (register_operand (operands[1], TFmode)) + goto movtf_is_ok; + + if (! reload_in_progress) + { + operands[0] = validize_mem (operands[0]); + operands[1] = force_reg (TFmode, operands[1]); + } + } + + /* Fixup PIC cases. */ + if (flag_pic) + { + if (CONSTANT_P (operands[1]) + && pic_address_needs_scratch (operands[1])) + operands[1] = legitimize_pic_address (operands[1], TFmode, 0); + + if (symbolic_operand (operands[1], TFmode)) + { + operands[1] = legitimize_pic_address (operands[1], + TFmode, + (reload_in_progress ? + operands[0] : + NULL_RTX)); + } + } + + movtf_is_ok: + ; +}") + +;; Be careful, fmovq and {st,ld}{x,q} do not exist when !arch64 so +;; we must split them all. :-( +(define_insn "*movtf_insn_sp32" + [(set (match_operand:TF 0 "general_operand" "=e,o,U,o,e,r,r,o") + (match_operand:TF 1 "input_operand" "o,e,o,U,e,r,o,r"))] + "TARGET_FPU + && ! TARGET_ARCH64 + && (register_operand (operands[0], TFmode) + || register_operand (operands[1], TFmode))" + "#" + [(set_attr "length" "4")]) + +;; Exactly the same as above, except that all `e' cases are deleted. +;; This is necessary to prevent reload from ever trying to use a `e' reg +;; when -mno-fpu. + +(define_insn "*movtf_no_e_insn_sp32" + [(set (match_operand:TF 0 "general_operand" "=U,o,r,r,o") + (match_operand:TF 1 "input_operand" "o,U,r,o,r"))] + "! TARGET_FPU + && ! TARGET_ARCH64 + && (register_operand (operands[0], TFmode) + || register_operand (operands[1], TFmode))" + "#" + [(set_attr "length" "4")]) + +;; Now handle the float reg cases directly when arch64, +;; hard_quad, and proper reg number alignment are all true. +(define_insn "*movtf_insn_hq_sp64" + [(set (match_operand:TF 0 "general_operand" "=e,e,m,r,r,o") + (match_operand:TF 1 "input_operand" "e,m,e,r,o,r"))] + "TARGET_FPU + && TARGET_ARCH64 + && TARGET_V9 + && TARGET_HARD_QUAD + && (register_operand (operands[0], TFmode) + || register_operand (operands[1], TFmode))" + "@ + fmovq\\t%1, %0 + ldq\\t%1, %0 + stq\\t%1, %0 + # + # + #" + [(set_attr "type" "fpmove,fpload,fpstore,*,*,*") + (set_attr "length" "1,1,1,2,2,2")]) + +;; Now we allow the integer register cases even when +;; only arch64 is true. +(define_insn "*movtf_insn_sp64" + [(set (match_operand:TF 0 "general_operand" "=e,o,r,o,e,r") + (match_operand:TF 1 "input_operand" "o,e,o,r,e,r"))] + "TARGET_FPU + && TARGET_ARCH64 + && ! TARGET_HARD_QUAD + && (register_operand (operands[0], TFmode) + || register_operand (operands[1], TFmode))" + "#" + [(set_attr "length" "2")]) + +(define_insn "*movtf_no_e_insn_sp64" + [(set (match_operand:TF 0 "general_operand" "=r,o,r") + (match_operand:TF 1 "input_operand" "o,r,r"))] + "! TARGET_FPU + && TARGET_ARCH64 + && (register_operand (operands[0], TFmode) + || register_operand (operands[1], TFmode))" + "#" + [(set_attr "length" "2")]) + +;; Now all the splits to handle multi-insn TF mode moves. +(define_split + [(set (match_operand:TF 0 "register_operand" "") + (match_operand:TF 1 "register_operand" ""))] + "reload_completed + && (! TARGET_ARCH64 + || (TARGET_FPU + && ! TARGET_HARD_QUAD))" + [(clobber (const_int 0))] + " +{ + rtx set_dest = operands[0]; + rtx set_src = operands[1]; + rtx dest1, dest2; + rtx src1, src2; + + if (GET_CODE (set_dest) == SUBREG) + set_dest = alter_subreg (set_dest); + if (GET_CODE (set_src) == SUBREG) + set_src = alter_subreg (set_src); + + /* Ugly, but gen_highpart will crap out here for 32-bit targets. */ + dest1 = gen_rtx_SUBREG (DFmode, set_dest, WORDS_BIG_ENDIAN == 0); + dest2 = gen_rtx_SUBREG (DFmode, set_dest, WORDS_BIG_ENDIAN != 0); + src1 = gen_rtx_SUBREG (DFmode, set_src, WORDS_BIG_ENDIAN == 0); + src2 = gen_rtx_SUBREG (DFmode, set_src, WORDS_BIG_ENDIAN != 0); + + /* Now emit using the real source and destination we found, swapping + the order if we detect overlap. */ + if (reg_overlap_mentioned_p (dest1, src2)) + { + emit_insn (gen_movdf (dest2, src2)); + emit_insn (gen_movdf (dest1, src1)); + } + else + { + emit_insn (gen_movdf (dest1, src1)); + emit_insn (gen_movdf (dest2, src2)); + } + DONE; +}") + +(define_split + [(set (match_operand:TF 0 "register_operand" "") + (match_operand:TF 1 "memory_operand" ""))] + "(reload_completed + && offsettable_memref_p (operands[1]))" + [(clobber (const_int 0))] + " +{ + rtx word0 = change_address (operands[1], DFmode, NULL_RTX); + rtx word1 = change_address (operands[1], DFmode, + plus_constant_for_output (XEXP (word0, 0), 8)); + rtx dest1, dest2; + + /* Ugly, but gen_highpart will crap out here for 32-bit targets. */ + dest1 = gen_rtx_SUBREG (DFmode, operands[0], WORDS_BIG_ENDIAN == 0); + dest2 = gen_rtx_SUBREG (DFmode, operands[0], WORDS_BIG_ENDIAN != 0); + + /* Now output, ordering such that we don't clobber any registers + mentioned in the address. */ + if (reg_overlap_mentioned_p (dest1, word1)) + + { + emit_insn (gen_movdf (dest2, word1)); + emit_insn (gen_movdf (dest1, word0)); + } + else + { + emit_insn (gen_movdf (dest1, word0)); + emit_insn (gen_movdf (dest2, word1)); + } + DONE; +}") + +(define_split + [(set (match_operand:TF 0 "memory_operand" "") + (match_operand:TF 1 "register_operand" ""))] + "(reload_completed + && offsettable_memref_p (operands[0]))" + [(clobber (const_int 0))] + " +{ + rtx word0 = change_address (operands[0], DFmode, NULL_RTX); + rtx word1 = change_address (operands[0], DFmode, + plus_constant_for_output (XEXP (word0, 0), 8)); + rtx src1, src2; + + /* Ugly, but gen_highpart will crap out here for 32-bit targets. */ + src1 = gen_rtx_SUBREG (DFmode, operands[1], WORDS_BIG_ENDIAN == 0); + src2 = gen_rtx_SUBREG (DFmode, operands[1], WORDS_BIG_ENDIAN != 0); + emit_insn (gen_movdf (word0, src1)); + emit_insn (gen_movdf (word1, src2)); + DONE; +}") + +;; Sparc V9 conditional move instructions. + +;; We can handle larger constants here for some flavors, but for now we keep +;; it simple and only allow those constants supported by all flavours. +;; Note that emit_conditional_move canonicalizes operands 2,3 so that operand +;; 3 contains the constant if one is present, but we handle either for +;; generality (sparc.c puts a constant in operand 2). + +(define_expand "movqicc" + [(set (match_operand:QI 0 "register_operand" "") + (if_then_else:QI (match_operand 1 "comparison_operator" "") + (match_operand:QI 2 "arith10_operand" "") + (match_operand:QI 3 "arith10_operand" "")))] + "TARGET_V9" + " +{ + enum rtx_code code = GET_CODE (operands[1]); + + if (GET_MODE (sparc_compare_op0) == DImode + && ! TARGET_ARCH64) + FAIL; + + if (sparc_compare_op1 == const0_rtx + && GET_CODE (sparc_compare_op0) == REG + && GET_MODE (sparc_compare_op0) == DImode + && v9_regcmp_p (code)) + { + operands[1] = gen_rtx_fmt_ee (code, DImode, + sparc_compare_op0, sparc_compare_op1); + } + else + { + rtx cc_reg = gen_compare_reg (code, + sparc_compare_op0, sparc_compare_op1); + operands[1] = gen_rtx_fmt_ee (code, GET_MODE (cc_reg), cc_reg, const0_rtx); + } +}") + +(define_expand "movhicc" + [(set (match_operand:HI 0 "register_operand" "") + (if_then_else:HI (match_operand 1 "comparison_operator" "") + (match_operand:HI 2 "arith10_operand" "") + (match_operand:HI 3 "arith10_operand" "")))] + "TARGET_V9" + " +{ + enum rtx_code code = GET_CODE (operands[1]); + + if (GET_MODE (sparc_compare_op0) == DImode + && ! TARGET_ARCH64) + FAIL; + + if (sparc_compare_op1 == const0_rtx + && GET_CODE (sparc_compare_op0) == REG + && GET_MODE (sparc_compare_op0) == DImode + && v9_regcmp_p (code)) + { + operands[1] = gen_rtx_fmt_ee (code, DImode, + sparc_compare_op0, sparc_compare_op1); + } + else + { + rtx cc_reg = gen_compare_reg (code, + sparc_compare_op0, sparc_compare_op1); + operands[1] = gen_rtx_fmt_ee (code, GET_MODE (cc_reg), cc_reg, const0_rtx); + } +}") + +(define_expand "movsicc" + [(set (match_operand:SI 0 "register_operand" "") + (if_then_else:SI (match_operand 1 "comparison_operator" "") + (match_operand:SI 2 "arith10_operand" "") + (match_operand:SI 3 "arith10_operand" "")))] + "TARGET_V9" + " +{ + enum rtx_code code = GET_CODE (operands[1]); + enum machine_mode op0_mode = GET_MODE (sparc_compare_op0); + + if (sparc_compare_op1 == const0_rtx + && GET_CODE (sparc_compare_op0) == REG + && (TARGET_ARCH64 && op0_mode == DImode && v9_regcmp_p (code))) + { + operands[1] = gen_rtx_fmt_ee (code, op0_mode, + sparc_compare_op0, sparc_compare_op1); + } + else + { + rtx cc_reg = gen_compare_reg (code, + sparc_compare_op0, sparc_compare_op1); + operands[1] = gen_rtx_fmt_ee (code, GET_MODE (cc_reg), + cc_reg, const0_rtx); + } +}") + +(define_expand "movdicc" + [(set (match_operand:DI 0 "register_operand" "") + (if_then_else:DI (match_operand 1 "comparison_operator" "") + (match_operand:DI 2 "arith10_double_operand" "") + (match_operand:DI 3 "arith10_double_operand" "")))] + "TARGET_ARCH64" + " +{ + enum rtx_code code = GET_CODE (operands[1]); + + if (sparc_compare_op1 == const0_rtx + && GET_CODE (sparc_compare_op0) == REG + && GET_MODE (sparc_compare_op0) == DImode + && v9_regcmp_p (code)) + { + operands[1] = gen_rtx_fmt_ee (code, DImode, + sparc_compare_op0, sparc_compare_op1); + } + else + { + rtx cc_reg = gen_compare_reg (code, + sparc_compare_op0, sparc_compare_op1); + operands[1] = gen_rtx_fmt_ee (code, GET_MODE (cc_reg), + cc_reg, const0_rtx); + } +}") + +(define_expand "movsfcc" + [(set (match_operand:SF 0 "register_operand" "") + (if_then_else:SF (match_operand 1 "comparison_operator" "") + (match_operand:SF 2 "register_operand" "") + (match_operand:SF 3 "register_operand" "")))] + "TARGET_V9 && TARGET_FPU" + " +{ + enum rtx_code code = GET_CODE (operands[1]); + + if (GET_MODE (sparc_compare_op0) == DImode + && ! TARGET_ARCH64) + FAIL; + + if (sparc_compare_op1 == const0_rtx + && GET_CODE (sparc_compare_op0) == REG + && GET_MODE (sparc_compare_op0) == DImode + && v9_regcmp_p (code)) + { + operands[1] = gen_rtx_fmt_ee (code, DImode, + sparc_compare_op0, sparc_compare_op1); + } + else + { + rtx cc_reg = gen_compare_reg (code, + sparc_compare_op0, sparc_compare_op1); + operands[1] = gen_rtx_fmt_ee (code, GET_MODE (cc_reg), cc_reg, const0_rtx); + } +}") + +(define_expand "movdfcc" + [(set (match_operand:DF 0 "register_operand" "") + (if_then_else:DF (match_operand 1 "comparison_operator" "") + (match_operand:DF 2 "register_operand" "") + (match_operand:DF 3 "register_operand" "")))] + "TARGET_V9 && TARGET_FPU" + " +{ + enum rtx_code code = GET_CODE (operands[1]); + + if (GET_MODE (sparc_compare_op0) == DImode + && ! TARGET_ARCH64) + FAIL; + + if (sparc_compare_op1 == const0_rtx + && GET_CODE (sparc_compare_op0) == REG + && GET_MODE (sparc_compare_op0) == DImode + && v9_regcmp_p (code)) + { + operands[1] = gen_rtx_fmt_ee (code, DImode, + sparc_compare_op0, sparc_compare_op1); + } + else + { + rtx cc_reg = gen_compare_reg (code, + sparc_compare_op0, sparc_compare_op1); + operands[1] = gen_rtx_fmt_ee (code, GET_MODE (cc_reg), cc_reg, const0_rtx); + } +}") + +(define_expand "movtfcc" + [(set (match_operand:TF 0 "register_operand" "") + (if_then_else:TF (match_operand 1 "comparison_operator" "") + (match_operand:TF 2 "register_operand" "") + (match_operand:TF 3 "register_operand" "")))] + "TARGET_V9 && TARGET_FPU" + " +{ + enum rtx_code code = GET_CODE (operands[1]); + + if (GET_MODE (sparc_compare_op0) == DImode + && ! TARGET_ARCH64) + FAIL; + + if (sparc_compare_op1 == const0_rtx + && GET_CODE (sparc_compare_op0) == REG + && GET_MODE (sparc_compare_op0) == DImode + && v9_regcmp_p (code)) + { + operands[1] = gen_rtx_fmt_ee (code, DImode, + sparc_compare_op0, sparc_compare_op1); + } + else + { + rtx cc_reg = gen_compare_reg (code, + sparc_compare_op0, sparc_compare_op1); + operands[1] = gen_rtx_fmt_ee (code, GET_MODE (cc_reg), cc_reg, const0_rtx); + } +}") + +;; Conditional move define_insns. + +(define_insn "*movqi_cc_sp64" + [(set (match_operand:QI 0 "register_operand" "=r,r") + (if_then_else:QI (match_operator 1 "comparison_operator" + [(match_operand 2 "icc_or_fcc_reg_operand" "X,X") + (const_int 0)]) + (match_operand:QI 3 "arith11_operand" "rL,0") + (match_operand:QI 4 "arith11_operand" "0,rL")))] + "TARGET_V9" + "@ + mov%C1\\t%x2, %3, %0 + mov%c1\\t%x2, %4, %0" + [(set_attr "type" "cmove") + (set_attr "length" "1")]) + +(define_insn "*movhi_cc_sp64" + [(set (match_operand:HI 0 "register_operand" "=r,r") + (if_then_else:HI (match_operator 1 "comparison_operator" + [(match_operand 2 "icc_or_fcc_reg_operand" "X,X") + (const_int 0)]) + (match_operand:HI 3 "arith11_operand" "rL,0") + (match_operand:HI 4 "arith11_operand" "0,rL")))] + "TARGET_V9" + "@ + mov%C1\\t%x2, %3, %0 + mov%c1\\t%x2, %4, %0" + [(set_attr "type" "cmove") + (set_attr "length" "1")]) + +(define_insn "*movsi_cc_sp64" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (if_then_else:SI (match_operator 1 "comparison_operator" + [(match_operand 2 "icc_or_fcc_reg_operand" "X,X") + (const_int 0)]) + (match_operand:SI 3 "arith11_operand" "rL,0") + (match_operand:SI 4 "arith11_operand" "0,rL")))] + "TARGET_V9" + "@ + mov%C1\\t%x2, %3, %0 + mov%c1\\t%x2, %4, %0" + [(set_attr "type" "cmove") + (set_attr "length" "1")]) + +;; ??? The constraints of operands 3,4 need work. +(define_insn "*movdi_cc_sp64" + [(set (match_operand:DI 0 "register_operand" "=r,r") + (if_then_else:DI (match_operator 1 "comparison_operator" + [(match_operand 2 "icc_or_fcc_reg_operand" "X,X") + (const_int 0)]) + (match_operand:DI 3 "arith11_double_operand" "rLH,0") + (match_operand:DI 4 "arith11_double_operand" "0,rLH")))] + "TARGET_ARCH64" + "@ + mov%C1\\t%x2, %3, %0 + mov%c1\\t%x2, %4, %0" + [(set_attr "type" "cmove") + (set_attr "length" "1")]) + +(define_insn "*movdi_cc_sp64_trunc" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (if_then_else:SI (match_operator 1 "comparison_operator" + [(match_operand 2 "icc_or_fcc_reg_operand" "X,X") + (const_int 0)]) + (match_operand:SI 3 "arith11_double_operand" "rLH,0") + (match_operand:SI 4 "arith11_double_operand" "0,rLH")))] + "TARGET_ARCH64" + "@ + mov%C1\\t%x2, %3, %0 + mov%c1\\t%x2, %4, %0" + [(set_attr "type" "cmove") + (set_attr "length" "1")]) + +(define_insn "*movsf_cc_sp64" + [(set (match_operand:SF 0 "register_operand" "=f,f") + (if_then_else:SF (match_operator 1 "comparison_operator" + [(match_operand 2 "icc_or_fcc_reg_operand" "X,X") + (const_int 0)]) + (match_operand:SF 3 "register_operand" "f,0") + (match_operand:SF 4 "register_operand" "0,f")))] + "TARGET_V9 && TARGET_FPU" + "@ + fmovs%C1\\t%x2, %3, %0 + fmovs%c1\\t%x2, %4, %0" + [(set_attr "type" "fpcmove") + (set_attr "length" "1")]) + +(define_insn "*movdf_cc_sp64" + [(set (match_operand:DF 0 "register_operand" "=e,e") + (if_then_else:DF (match_operator 1 "comparison_operator" + [(match_operand 2 "icc_or_fcc_reg_operand" "X,X") + (const_int 0)]) + (match_operand:DF 3 "register_operand" "e,0") + (match_operand:DF 4 "register_operand" "0,e")))] + "TARGET_V9 && TARGET_FPU" + "@ + fmovd%C1\\t%x2, %3, %0 + fmovd%c1\\t%x2, %4, %0" + [(set_attr "type" "fpcmove") + (set_attr "length" "1")]) + +(define_insn "*movtf_cc_sp64" + [(set (match_operand:TF 0 "register_operand" "=e,e") + (if_then_else:TF (match_operator 1 "comparison_operator" + [(match_operand 2 "icc_or_fcc_reg_operand" "X,X") + (const_int 0)]) + (match_operand:TF 3 "register_operand" "e,0") + (match_operand:TF 4 "register_operand" "0,e")))] + "TARGET_V9 && TARGET_FPU && TARGET_HARD_QUAD" + "@ + fmovq%C1\\t%x2, %3, %0 + fmovq%c1\\t%x2, %4, %0" + [(set_attr "type" "fpcmove") + (set_attr "length" "1")]) + +(define_insn "*movqi_cc_reg_sp64" + [(set (match_operand:QI 0 "register_operand" "=r,r") + (if_then_else:QI (match_operator 1 "v9_regcmp_op" + [(match_operand:DI 2 "register_operand" "r,r") + (const_int 0)]) + (match_operand:QI 3 "arith10_operand" "rM,0") + (match_operand:QI 4 "arith10_operand" "0,rM")))] + "TARGET_ARCH64" + "@ + movr%D1\\t%2, %r3, %0 + movr%d1\\t%2, %r4, %0" + [(set_attr "type" "cmove") + (set_attr "length" "1")]) + +(define_insn "*movhi_cc_reg_sp64" + [(set (match_operand:HI 0 "register_operand" "=r,r") + (if_then_else:HI (match_operator 1 "v9_regcmp_op" + [(match_operand:DI 2 "register_operand" "r,r") + (const_int 0)]) + (match_operand:HI 3 "arith10_operand" "rM,0") + (match_operand:HI 4 "arith10_operand" "0,rM")))] + "TARGET_ARCH64" + "@ + movr%D1\\t%2, %r3, %0 + movr%d1\\t%2, %r4, %0" + [(set_attr "type" "cmove") + (set_attr "length" "1")]) + +(define_insn "*movsi_cc_reg_sp64" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (if_then_else:SI (match_operator 1 "v9_regcmp_op" + [(match_operand:DI 2 "register_operand" "r,r") + (const_int 0)]) + (match_operand:SI 3 "arith10_operand" "rM,0") + (match_operand:SI 4 "arith10_operand" "0,rM")))] + "TARGET_ARCH64" + "@ + movr%D1\\t%2, %r3, %0 + movr%d1\\t%2, %r4, %0" + [(set_attr "type" "cmove") + (set_attr "length" "1")]) + +;; ??? The constraints of operands 3,4 need work. +(define_insn "*movdi_cc_reg_sp64" + [(set (match_operand:DI 0 "register_operand" "=r,r") + (if_then_else:DI (match_operator 1 "v9_regcmp_op" + [(match_operand:DI 2 "register_operand" "r,r") + (const_int 0)]) + (match_operand:DI 3 "arith10_double_operand" "rMH,0") + (match_operand:DI 4 "arith10_double_operand" "0,rMH")))] + "TARGET_ARCH64" + "@ + movr%D1\\t%2, %r3, %0 + movr%d1\\t%2, %r4, %0" + [(set_attr "type" "cmove") + (set_attr "length" "1")]) + +(define_insn "*movdi_cc_reg_sp64_trunc" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (if_then_else:SI (match_operator 1 "v9_regcmp_op" + [(match_operand:DI 2 "register_operand" "r,r") + (const_int 0)]) + (match_operand:SI 3 "arith10_double_operand" "rMH,0") + (match_operand:SI 4 "arith10_double_operand" "0,rMH")))] + "TARGET_ARCH64" + "@ + movr%D1\\t%2, %r3, %0 + movr%d1\\t%2, %r4, %0" + [(set_attr "type" "cmove") + (set_attr "length" "1")]) + +(define_insn "*movsf_cc_reg_sp64" + [(set (match_operand:SF 0 "register_operand" "=f,f") + (if_then_else:SF (match_operator 1 "v9_regcmp_op" + [(match_operand:DI 2 "register_operand" "r,r") + (const_int 0)]) + (match_operand:SF 3 "register_operand" "f,0") + (match_operand:SF 4 "register_operand" "0,f")))] + "TARGET_ARCH64 && TARGET_FPU" + "@ + fmovrs%D1\\t%2, %3, %0 + fmovrs%d1\\t%2, %4, %0" + [(set_attr "type" "fpcmove") + (set_attr "length" "1")]) + +(define_insn "*movdf_cc_reg_sp64" + [(set (match_operand:DF 0 "register_operand" "=e,e") + (if_then_else:DF (match_operator 1 "v9_regcmp_op" + [(match_operand:DI 2 "register_operand" "r,r") + (const_int 0)]) + (match_operand:DF 3 "register_operand" "e,0") + (match_operand:DF 4 "register_operand" "0,e")))] + "TARGET_ARCH64 && TARGET_FPU" + "@ + fmovrd%D1\\t%2, %3, %0 + fmovrd%d1\\t%2, %4, %0" + [(set_attr "type" "fpcmove") + (set_attr "length" "1")]) + +(define_insn "*movtf_cc_reg_sp64" + [(set (match_operand:TF 0 "register_operand" "=e,e") + (if_then_else:TF (match_operator 1 "v9_regcmp_op" + [(match_operand:DI 2 "register_operand" "r,r") + (const_int 0)]) + (match_operand:TF 3 "register_operand" "e,0") + (match_operand:TF 4 "register_operand" "0,e")))] + "TARGET_ARCH64 && TARGET_FPU" + "@ + fmovrq%D1\\t%2, %3, %0 + fmovrq%d1\\t%2, %4, %0" + [(set_attr "type" "fpcmove") + (set_attr "length" "1")]) + +;;- zero extension instructions + +;; These patterns originally accepted general_operands, however, slightly +;; better code is generated by only accepting register_operands, and then +;; letting combine generate the ldu[hb] insns. + +(define_expand "zero_extendhisi2" + [(set (match_operand:SI 0 "register_operand" "") + (zero_extend:SI (match_operand:HI 1 "register_operand" "")))] + "" + " +{ + rtx temp = gen_reg_rtx (SImode); + rtx shift_16 = GEN_INT (16); + int op1_subword = 0; + + if (GET_CODE (operand1) == SUBREG) + { + op1_subword = SUBREG_WORD (operand1); + operand1 = XEXP (operand1, 0); + } + + emit_insn (gen_ashlsi3 (temp, gen_rtx_SUBREG (SImode, operand1, + op1_subword), + shift_16)); + emit_insn (gen_lshrsi3 (operand0, temp, shift_16)); + DONE; +}") + +(define_insn "*zero_extendhisi2_insn" + [(set (match_operand:SI 0 "register_operand" "=r") + (zero_extend:SI (match_operand:HI 1 "memory_operand" "m")))] + "" + "lduh\\t%1, %0" + [(set_attr "type" "load") + (set_attr "length" "1")]) + +(define_expand "zero_extendqihi2" + [(set (match_operand:HI 0 "register_operand" "") + (zero_extend:HI (match_operand:QI 1 "register_operand" "")))] + "" + "") + +(define_insn "*zero_extendqihi2_insn" + [(set (match_operand:HI 0 "register_operand" "=r,r") + (zero_extend:HI (match_operand:QI 1 "input_operand" "r,m")))] + "GET_CODE (operands[1]) != CONST_INT" + "@ + and\\t%1, 0xff, %0 + ldub\\t%1, %0" + [(set_attr "type" "unary,load") + (set_attr "length" "1")]) + +(define_expand "zero_extendqisi2" + [(set (match_operand:SI 0 "register_operand" "") + (zero_extend:SI (match_operand:QI 1 "register_operand" "")))] + "" + "") + +(define_insn "*zero_extendqisi2_insn" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (zero_extend:SI (match_operand:QI 1 "input_operand" "r,m")))] + "GET_CODE (operands[1]) != CONST_INT" + "@ + and\\t%1, 0xff, %0 + ldub\\t%1, %0" + [(set_attr "type" "unary,load") + (set_attr "length" "1")]) + +(define_expand "zero_extendqidi2" + [(set (match_operand:DI 0 "register_operand" "") + (zero_extend:DI (match_operand:QI 1 "register_operand" "")))] + "TARGET_ARCH64" + "") + +(define_insn "*zero_extendqidi2_insn" + [(set (match_operand:DI 0 "register_operand" "=r,r") + (zero_extend:DI (match_operand:QI 1 "input_operand" "r,m")))] + "TARGET_ARCH64 && GET_CODE (operands[1]) != CONST_INT" + "@ + and\\t%1, 0xff, %0 + ldub\\t%1, %0" + [(set_attr "type" "unary,load") + (set_attr "length" "1")]) + +(define_expand "zero_extendhidi2" + [(set (match_operand:DI 0 "register_operand" "") + (zero_extend:DI (match_operand:HI 1 "register_operand" "")))] + "TARGET_ARCH64" + " +{ + rtx temp = gen_reg_rtx (DImode); + rtx shift_48 = GEN_INT (48); + int op1_subword = 0; + + if (GET_CODE (operand1) == SUBREG) + { + op1_subword = SUBREG_WORD (operand1); + operand1 = XEXP (operand1, 0); + } + + emit_insn (gen_ashldi3 (temp, gen_rtx_SUBREG (DImode, operand1, + op1_subword), + shift_48)); + emit_insn (gen_lshrdi3 (operand0, temp, shift_48)); + DONE; +}") + +(define_insn "*zero_extendhidi2_insn" + [(set (match_operand:DI 0 "register_operand" "=r") + (zero_extend:DI (match_operand:HI 1 "memory_operand" "m")))] + "TARGET_ARCH64" + "lduh\\t%1, %0" + [(set_attr "type" "load") + (set_attr "length" "1")]) + + +;; ??? Write truncdisi pattern using sra? + +(define_expand "zero_extendsidi2" + [(set (match_operand:DI 0 "register_operand" "") + (zero_extend:DI (match_operand:SI 1 "register_operand" "")))] + "" + "") + +(define_insn "*zero_extendsidi2_insn_sp64" + [(set (match_operand:DI 0 "register_operand" "=r,r") + (zero_extend:DI (match_operand:SI 1 "input_operand" "r,m")))] + "TARGET_ARCH64 && GET_CODE (operands[1]) != CONST_INT" + "@ + srl\\t%1, 0, %0 + lduw\\t%1, %0" + [(set_attr "type" "shift,load") + (set_attr "length" "1")]) + +(define_insn "*zero_extendsidi2_insn_sp32" + [(set (match_operand:DI 0 "register_operand" "=r") + (zero_extend:DI (match_operand:SI 1 "register_operand" "r")))] + "! TARGET_ARCH64" + "#" + [(set_attr "type" "unary") + (set_attr "length" "2")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (zero_extend:DI (match_operand:SI 1 "register_operand" "")))] + "! TARGET_ARCH64 && reload_completed" + [(set (match_dup 2) (match_dup 3)) + (set (match_dup 4) (match_dup 5))] + " +{ + rtx dest1, dest2; + + if (GET_CODE (operands[0]) == SUBREG) + operands[0] = alter_subreg (operands[0]); + + dest1 = gen_highpart (SImode, operands[0]); + dest2 = gen_lowpart (SImode, operands[0]); + + /* Swap the order in case of overlap. */ + if (REGNO (dest1) == REGNO (operands[1])) + { + operands[2] = dest2; + operands[3] = operands[1]; + operands[4] = dest1; + operands[5] = const0_rtx; + } + else + { + operands[2] = dest1; + operands[3] = const0_rtx; + operands[4] = dest2; + operands[5] = operands[1]; + } +}") + +;; Simplify comparisons of extended values. + +(define_insn "*cmp_zero_extendqisi2" + [(set (reg:CC 100) + (compare:CC (zero_extend:SI (match_operand:QI 0 "register_operand" "r")) + (const_int 0)))] + "! TARGET_LIVE_G0" + "andcc\\t%0, 0xff, %%g0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_zero_extendqisi2_set" + [(set (reg:CC 100) + (compare:CC (zero_extend:SI (match_operand:QI 1 "register_operand" "r")) + (const_int 0))) + (set (match_operand:SI 0 "register_operand" "=r") + (zero_extend:SI (match_dup 1)))] + "" + "andcc\\t%1, 0xff, %0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_zero_extendqidi2" + [(set (reg:CCX 100) + (compare:CCX (zero_extend:DI (match_operand:QI 0 "register_operand" "r")) + (const_int 0)))] + "TARGET_ARCH64" + "andcc\\t%0, 0xff, %%g0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_zero_extendqidi2_set" + [(set (reg:CCX 100) + (compare:CCX (zero_extend:DI (match_operand:QI 1 "register_operand" "r")) + (const_int 0))) + (set (match_operand:DI 0 "register_operand" "=r") + (zero_extend:DI (match_dup 1)))] + "TARGET_ARCH64" + "andcc\\t%1, 0xff, %0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +;; Similarly, handle {SI,DI}->QI mode truncation followed by a compare. + +(define_insn "*cmp_siqi_trunc" + [(set (reg:CC 100) + (compare:CC (subreg:QI (match_operand:SI 0 "register_operand" "r") 0) + (const_int 0)))] + "! TARGET_LIVE_G0" + "andcc\\t%0, 0xff, %%g0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_siqi_trunc_set" + [(set (reg:CC 100) + (compare:CC (subreg:QI (match_operand:SI 1 "register_operand" "r") 0) + (const_int 0))) + (set (match_operand:QI 0 "register_operand" "=r") + (subreg:QI (match_dup 1) 0))] + "" + "andcc\\t%1, 0xff, %0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_diqi_trunc" + [(set (reg:CC 100) + (compare:CC (subreg:QI (match_operand:DI 0 "register_operand" "r") 0) + (const_int 0)))] + "TARGET_ARCH64" + "andcc\\t%0, 0xff, %%g0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_diqi_trunc_set" + [(set (reg:CC 100) + (compare:CC (subreg:QI (match_operand:DI 1 "register_operand" "r") 0) + (const_int 0))) + (set (match_operand:QI 0 "register_operand" "=r") + (subreg:QI (match_dup 1) 0))] + "TARGET_ARCH64" + "andcc\\t%1, 0xff, %0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +;;- sign extension instructions + +;; These patterns originally accepted general_operands, however, slightly +;; better code is generated by only accepting register_operands, and then +;; letting combine generate the lds[hb] insns. + +(define_expand "extendhisi2" + [(set (match_operand:SI 0 "register_operand" "") + (sign_extend:SI (match_operand:HI 1 "register_operand" "")))] + "" + " +{ + rtx temp = gen_reg_rtx (SImode); + rtx shift_16 = GEN_INT (16); + int op1_subword = 0; + + if (GET_CODE (operand1) == SUBREG) + { + op1_subword = SUBREG_WORD (operand1); + operand1 = XEXP (operand1, 0); + } + + emit_insn (gen_ashlsi3 (temp, gen_rtx_SUBREG (SImode, operand1, + op1_subword), + shift_16)); + emit_insn (gen_ashrsi3 (operand0, temp, shift_16)); + DONE; +}") + +(define_insn "*sign_extendhisi2_insn" + [(set (match_operand:SI 0 "register_operand" "=r") + (sign_extend:SI (match_operand:HI 1 "memory_operand" "m")))] + "" + "ldsh\\t%1, %0" + [(set_attr "type" "sload") + (set_attr "length" "1")]) + +(define_expand "extendqihi2" + [(set (match_operand:HI 0 "register_operand" "") + (sign_extend:HI (match_operand:QI 1 "register_operand" "")))] + "" + " +{ + rtx temp = gen_reg_rtx (SImode); + rtx shift_24 = GEN_INT (24); + int op1_subword = 0; + int op0_subword = 0; + + if (GET_CODE (operand1) == SUBREG) + { + op1_subword = SUBREG_WORD (operand1); + operand1 = XEXP (operand1, 0); + } + if (GET_CODE (operand0) == SUBREG) + { + op0_subword = SUBREG_WORD (operand0); + operand0 = XEXP (operand0, 0); + } + emit_insn (gen_ashlsi3 (temp, gen_rtx_SUBREG (SImode, operand1, + op1_subword), + shift_24)); + if (GET_MODE (operand0) != SImode) + operand0 = gen_rtx_SUBREG (SImode, operand0, op0_subword); + emit_insn (gen_ashrsi3 (operand0, temp, shift_24)); + DONE; +}") + +(define_insn "*sign_extendqihi2_insn" + [(set (match_operand:HI 0 "register_operand" "=r") + (sign_extend:HI (match_operand:QI 1 "memory_operand" "m")))] + "" + "ldsb\\t%1, %0" + [(set_attr "type" "sload") + (set_attr "length" "1")]) + +(define_expand "extendqisi2" + [(set (match_operand:SI 0 "register_operand" "") + (sign_extend:SI (match_operand:QI 1 "register_operand" "")))] + "" + " +{ + rtx temp = gen_reg_rtx (SImode); + rtx shift_24 = GEN_INT (24); + int op1_subword = 0; + + if (GET_CODE (operand1) == SUBREG) + { + op1_subword = SUBREG_WORD (operand1); + operand1 = XEXP (operand1, 0); + } + + emit_insn (gen_ashlsi3 (temp, gen_rtx_SUBREG (SImode, operand1, + op1_subword), + shift_24)); + emit_insn (gen_ashrsi3 (operand0, temp, shift_24)); + DONE; +}") + +(define_insn "*sign_extendqisi2_insn" + [(set (match_operand:SI 0 "register_operand" "=r") + (sign_extend:SI (match_operand:QI 1 "memory_operand" "m")))] + "" + "ldsb\\t%1, %0" + [(set_attr "type" "sload") + (set_attr "length" "1")]) + +(define_expand "extendqidi2" + [(set (match_operand:DI 0 "register_operand" "") + (sign_extend:DI (match_operand:QI 1 "register_operand" "")))] + "TARGET_ARCH64" + " +{ + rtx temp = gen_reg_rtx (DImode); + rtx shift_56 = GEN_INT (56); + int op1_subword = 0; + + if (GET_CODE (operand1) == SUBREG) + { + op1_subword = SUBREG_WORD (operand1); + operand1 = XEXP (operand1, 0); + } + + emit_insn (gen_ashldi3 (temp, gen_rtx_SUBREG (DImode, operand1, + op1_subword), + shift_56)); + emit_insn (gen_ashrdi3 (operand0, temp, shift_56)); + DONE; +}") + +(define_insn "*sign_extendqidi2_insn" + [(set (match_operand:DI 0 "register_operand" "=r") + (sign_extend:DI (match_operand:QI 1 "memory_operand" "m")))] + "TARGET_ARCH64" + "ldsb\\t%1, %0" + [(set_attr "type" "sload") + (set_attr "length" "1")]) + +(define_expand "extendhidi2" + [(set (match_operand:DI 0 "register_operand" "") + (sign_extend:DI (match_operand:HI 1 "register_operand" "")))] + "TARGET_ARCH64" + " +{ + rtx temp = gen_reg_rtx (DImode); + rtx shift_48 = GEN_INT (48); + int op1_subword = 0; + + if (GET_CODE (operand1) == SUBREG) + { + op1_subword = SUBREG_WORD (operand1); + operand1 = XEXP (operand1, 0); + } + + emit_insn (gen_ashldi3 (temp, gen_rtx_SUBREG (DImode, operand1, + op1_subword), + shift_48)); + emit_insn (gen_ashrdi3 (operand0, temp, shift_48)); + DONE; +}") + +(define_insn "*sign_extendhidi2_insn" + [(set (match_operand:DI 0 "register_operand" "=r") + (sign_extend:DI (match_operand:HI 1 "memory_operand" "m")))] + "TARGET_ARCH64" + "ldsh\\t%1, %0" + [(set_attr "type" "sload") + (set_attr "length" "1")]) + +(define_expand "extendsidi2" + [(set (match_operand:DI 0 "register_operand" "") + (sign_extend:DI (match_operand:SI 1 "register_operand" "")))] + "TARGET_ARCH64" + "") + +(define_insn "*sign_extendsidi2_insn" + [(set (match_operand:DI 0 "register_operand" "=r,r") + (sign_extend:DI (match_operand:SI 1 "input_operand" "r,m")))] + "TARGET_ARCH64" + "@ + sra\\t%1, 0, %0 + ldsw\\t%1, %0" + [(set_attr "type" "shift,sload") + (set_attr "length" "1")]) + +;; Special pattern for optimizing bit-field compares. This is needed +;; because combine uses this as a canonical form. + +(define_insn "*cmp_zero_extract" + [(set (reg:CC 100) + (compare:CC + (zero_extract:SI (match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "small_int_or_double" "n") + (match_operand:SI 2 "small_int_or_double" "n")) + (const_int 0)))] + "! TARGET_LIVE_G0 + && ((GET_CODE (operands[2]) == CONST_INT + && INTVAL (operands[2]) > 19) + || (GET_CODE (operands[2]) == CONST_DOUBLE + && CONST_DOUBLE_LOW (operands[2]) > 19))" + "* +{ + int len = (GET_CODE (operands[1]) == CONST_INT + ? INTVAL (operands[1]) + : CONST_DOUBLE_LOW (operands[1])); + int pos = 32 - + (GET_CODE (operands[2]) == CONST_INT + ? INTVAL (operands[2]) + : CONST_DOUBLE_LOW (operands[2])) - len; + HOST_WIDE_INT mask = ((1 << len) - 1) << pos; + + operands[1] = GEN_INT (mask); + return \"andcc\\t%0, %1, %%g0\"; +}" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_zero_extract_sp64" + [(set (reg:CCX 100) + (compare:CCX + (zero_extract:DI (match_operand:DI 0 "register_operand" "r") + (match_operand:SI 1 "small_int_or_double" "n") + (match_operand:SI 2 "small_int_or_double" "n")) + (const_int 0)))] + "TARGET_ARCH64 + && ((GET_CODE (operands[2]) == CONST_INT + && INTVAL (operands[2]) > 51) + || (GET_CODE (operands[2]) == CONST_DOUBLE + && CONST_DOUBLE_LOW (operands[2]) > 51))" + "* +{ + int len = (GET_CODE (operands[1]) == CONST_INT + ? INTVAL (operands[1]) + : CONST_DOUBLE_LOW (operands[1])); + int pos = 64 - + (GET_CODE (operands[2]) == CONST_INT + ? INTVAL (operands[2]) + : CONST_DOUBLE_LOW (operands[2])) - len; + HOST_WIDE_INT mask = (((unsigned HOST_WIDE_INT) 1 << len) - 1) << pos; + + operands[1] = GEN_INT (mask); + return \"andcc\\t%0, %1, %%g0\"; +}" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +;; Conversions between float, double and long double. + +(define_insn "extendsfdf2" + [(set (match_operand:DF 0 "register_operand" "=e") + (float_extend:DF + (match_operand:SF 1 "register_operand" "f")))] + "TARGET_FPU" + "fstod\\t%1, %0" + [(set_attr "type" "fp") + (set_attr "length" "1")]) + +(define_insn "extendsftf2" + [(set (match_operand:TF 0 "register_operand" "=e") + (float_extend:TF + (match_operand:SF 1 "register_operand" "f")))] + "TARGET_FPU && TARGET_HARD_QUAD" + "fstoq\\t%1, %0" + [(set_attr "type" "fp") + (set_attr "length" "1")]) + +(define_insn "extenddftf2" + [(set (match_operand:TF 0 "register_operand" "=e") + (float_extend:TF + (match_operand:DF 1 "register_operand" "e")))] + "TARGET_FPU && TARGET_HARD_QUAD" + "fdtoq\\t%1, %0" + [(set_attr "type" "fp") + (set_attr "length" "1")]) + +(define_insn "truncdfsf2" + [(set (match_operand:SF 0 "register_operand" "=f") + (float_truncate:SF + (match_operand:DF 1 "register_operand" "e")))] + "TARGET_FPU" + "fdtos\\t%1, %0" + [(set_attr "type" "fp") + (set_attr "length" "1")]) + +(define_insn "trunctfsf2" + [(set (match_operand:SF 0 "register_operand" "=f") + (float_truncate:SF + (match_operand:TF 1 "register_operand" "e")))] + "TARGET_FPU && TARGET_HARD_QUAD" + "fqtos\\t%1, %0" + [(set_attr "type" "fp") + (set_attr "length" "1")]) + +(define_insn "trunctfdf2" + [(set (match_operand:DF 0 "register_operand" "=e") + (float_truncate:DF + (match_operand:TF 1 "register_operand" "e")))] + "TARGET_FPU && TARGET_HARD_QUAD" + "fqtod\\t%1, %0" + [(set_attr "type" "fp") + (set_attr "length" "1")]) + +;; Conversion between fixed point and floating point. + +(define_insn "floatsisf2" + [(set (match_operand:SF 0 "register_operand" "=f") + (float:SF (match_operand:SI 1 "register_operand" "f")))] + "TARGET_FPU" + "fitos\\t%1, %0" + [(set_attr "type" "fp") + (set_attr "length" "1")]) + +(define_insn "floatsidf2" + [(set (match_operand:DF 0 "register_operand" "=e") + (float:DF (match_operand:SI 1 "register_operand" "f")))] + "TARGET_FPU" + "fitod\\t%1, %0" + [(set_attr "type" "fp") + (set_attr "length" "1")]) + +(define_insn "floatsitf2" + [(set (match_operand:TF 0 "register_operand" "=e") + (float:TF (match_operand:SI 1 "register_operand" "f")))] + "TARGET_FPU && TARGET_HARD_QUAD" + "fitoq\\t%1, %0" + [(set_attr "type" "fp") + (set_attr "length" "1")]) + +;; Now the same for 64 bit sources. + +(define_insn "floatdisf2" + [(set (match_operand:SF 0 "register_operand" "=f") + (float:SF (match_operand:DI 1 "register_operand" "e")))] + "TARGET_V9 && TARGET_FPU" + "fxtos\\t%1, %0" + [(set_attr "type" "fp") + (set_attr "length" "1")]) + +(define_insn "floatdidf2" + [(set (match_operand:DF 0 "register_operand" "=e") + (float:DF (match_operand:DI 1 "register_operand" "e")))] + "TARGET_V9 && TARGET_FPU" + "fxtod\\t%1, %0" + [(set_attr "type" "fp") + (set_attr "length" "1")]) + +(define_insn "floatditf2" + [(set (match_operand:TF 0 "register_operand" "=e") + (float:TF (match_operand:DI 1 "register_operand" "e")))] + "TARGET_V9 && TARGET_FPU && TARGET_HARD_QUAD" + "fxtoq\\t%1, %0" + [(set_attr "type" "fp") + (set_attr "length" "1")]) + +;; 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_FPU" + "fstoi\\t%1, %0" + [(set_attr "type" "fp") + (set_attr "length" "1")]) + +(define_insn "fix_truncdfsi2" + [(set (match_operand:SI 0 "register_operand" "=f") + (fix:SI (fix:DF (match_operand:DF 1 "register_operand" "e"))))] + "TARGET_FPU" + "fdtoi\\t%1, %0" + [(set_attr "type" "fp") + (set_attr "length" "1")]) + +(define_insn "fix_trunctfsi2" + [(set (match_operand:SI 0 "register_operand" "=f") + (fix:SI (fix:TF (match_operand:TF 1 "register_operand" "e"))))] + "TARGET_FPU && TARGET_HARD_QUAD" + "fqtoi\\t%1, %0" + [(set_attr "type" "fp") + (set_attr "length" "1")]) + +;; Now the same, for V9 targets + +(define_insn "fix_truncsfdi2" + [(set (match_operand:DI 0 "register_operand" "=e") + (fix:DI (fix:SF (match_operand:SF 1 "register_operand" "f"))))] + "TARGET_V9 && TARGET_FPU" + "fstox\\t%1, %0" + [(set_attr "type" "fp") + (set_attr "length" "1")]) + +(define_insn "fix_truncdfdi2" + [(set (match_operand:DI 0 "register_operand" "=e") + (fix:DI (fix:DF (match_operand:DF 1 "register_operand" "e"))))] + "TARGET_V9 && TARGET_FPU" + "fdtox\\t%1, %0" + [(set_attr "type" "fp") + (set_attr "length" "1")]) + +(define_insn "fix_trunctfdi2" + [(set (match_operand:DI 0 "register_operand" "=e") + (fix:DI (fix:TF (match_operand:TF 1 "register_operand" "e"))))] + "TARGET_V9 && TARGET_FPU && TARGET_HARD_QUAD" + "fqtox\\t%1, %0" + [(set_attr "type" "fp") + (set_attr "length" "1")]) + +;;- arithmetic instructions + +(define_expand "adddi3" + [(set (match_operand:DI 0 "register_operand" "=r") + (plus:DI (match_operand:DI 1 "arith_double_operand" "%r") + (match_operand:DI 2 "arith_double_add_operand" "rHI")))] + "" + " +{ + if (! TARGET_ARCH64) + { + emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, + gen_rtx_SET (VOIDmode, operands[0], + gen_rtx_PLUS (DImode, operands[1], + operands[2])), + gen_rtx_CLOBBER (VOIDmode, + gen_rtx_REG (CCmode, SPARC_ICC_REG))))); + DONE; + } + if (arith_double_4096_operand(operands[2], DImode)) + { + emit_insn (gen_rtx_SET (VOIDmode, operands[0], + gen_rtx_MINUS (DImode, operands[1], + GEN_INT(-4096)))); + DONE; + } +}") + +(define_insn "adddi3_insn_sp32" + [(set (match_operand:DI 0 "register_operand" "=r") + (plus:DI (match_operand:DI 1 "arith_double_operand" "%r") + (match_operand:DI 2 "arith_double_operand" "rHI"))) + (clobber (reg:CC 100))] + "! TARGET_ARCH64" + "#" + [(set_attr "length" "2")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "=r") + (plus:DI (match_operand:DI 1 "arith_double_operand" "%r") + (match_operand:DI 2 "arith_double_operand" "rHI"))) + (clobber (reg:CC 100))] + "! TARGET_ARCH64 && reload_completed" + [(parallel [(set (reg:CC_NOOV 100) + (compare:CC_NOOV (plus:SI (match_dup 4) + (match_dup 5)) + (const_int 0))) + (set (match_dup 3) + (plus:SI (match_dup 4) (match_dup 5)))]) + (set (match_dup 6) + (plus:SI (plus:SI (match_dup 7) + (match_dup 8)) + (ltu:SI (reg:CC_NOOV 100) (const_int 0))))] + " +{ + operands[3] = gen_lowpart (SImode, operands[0]); + operands[4] = gen_lowpart (SImode, operands[1]); + operands[5] = gen_lowpart (SImode, operands[2]); + operands[6] = gen_highpart (SImode, operands[0]); + operands[7] = gen_highpart (SImode, operands[1]); + if (GET_CODE (operands[2]) == CONST_INT) + { + if (INTVAL (operands[2]) < 0) + operands[8] = constm1_rtx; + else + operands[8] = const0_rtx; + } + else + operands[8] = gen_highpart (SImode, operands[2]); +}") + +(define_split + [(set (match_operand:DI 0 "register_operand" "=r") + (minus:DI (match_operand:DI 1 "arith_double_operand" "r") + (match_operand:DI 2 "arith_double_operand" "rHI"))) + (clobber (reg:CC 100))] + "! TARGET_ARCH64 && reload_completed" + [(parallel [(set (reg:CC_NOOV 100) + (compare:CC_NOOV (minus:SI (match_dup 4) + (match_dup 5)) + (const_int 0))) + (set (match_dup 3) + (minus:SI (match_dup 4) (match_dup 5)))]) + (set (match_dup 6) + (minus:SI (minus:SI (match_dup 7) + (match_dup 8)) + (ltu:SI (reg:CC_NOOV 100) (const_int 0))))] + " +{ + operands[3] = gen_lowpart (SImode, operands[0]); + operands[4] = gen_lowpart (SImode, operands[1]); + operands[5] = gen_lowpart (SImode, operands[2]); + operands[6] = gen_highpart (SImode, operands[0]); + operands[7] = gen_highpart (SImode, operands[1]); + if (GET_CODE (operands[2]) == CONST_INT) + { + if (INTVAL (operands[2]) < 0) + operands[8] = constm1_rtx; + else + operands[8] = const0_rtx; + } + else + operands[8] = gen_highpart (SImode, operands[2]); +}") + +;; LTU here means "carry set" +(define_insn "addx" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (plus:SI (match_operand:SI 1 "arith_operand" "%r") + (match_operand:SI 2 "arith_operand" "rI")) + (ltu:SI (reg:CC_NOOV 100) (const_int 0))))] + "" + "addx\\t%1, %2, %0" + [(set_attr "type" "unary") + (set_attr "length" "1")]) + +(define_insn "*addx_extend_sp32" + [(set (match_operand:DI 0 "register_operand" "=r") + (zero_extend:DI (plus:SI (plus:SI (match_operand:SI 1 "reg_or_0_operand" "%rJ") + (match_operand:SI 2 "arith_operand" "rI")) + (ltu:SI (reg:CC_NOOV 100) (const_int 0)))))] + "! TARGET_ARCH64" + "#" + [(set_attr "type" "unary") + (set_attr "length" "2")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (zero_extend:DI (plus:SI (plus:SI (match_operand:SI 1 "reg_or_0_operand" "") + (match_operand:SI 2 "arith_operand" "")) + (ltu:SI (reg:CC_NOOV 100) (const_int 0)))))] + "! TARGET_ARCH64 && reload_completed" + [(set (match_dup 3) (plus:SI (plus:SI (match_dup 1) (match_dup 2)) + (ltu:SI (reg:CC_NOOV 100) (const_int 0)))) + (set (match_dup 4) (const_int 0))] + "operands[3] = gen_lowpart (SImode, operands[0]); + operands[4] = gen_highpart (SImode, operands[1]);") + +(define_insn "*addx_extend_sp64" + [(set (match_operand:DI 0 "register_operand" "=r") + (zero_extend:DI (plus:SI (plus:SI (match_operand:SI 1 "reg_or_0_operand" "%rJ") + (match_operand:SI 2 "arith_operand" "rI")) + (ltu:SI (reg:CC_NOOV 100) (const_int 0)))))] + "TARGET_ARCH64" + "addx\\t%r1, %2, %0" + [(set_attr "type" "misc") + (set_attr "length" "1")]) + +(define_insn "subx" + [(set (match_operand:SI 0 "register_operand" "=r") + (minus:SI (minus:SI (match_operand:SI 1 "reg_or_0_operand" "rJ") + (match_operand:SI 2 "arith_operand" "rI")) + (ltu:SI (reg:CC_NOOV 100) (const_int 0))))] + "" + "subx\\t%r1, %2, %0" + [(set_attr "type" "misc") + (set_attr "length" "1")]) + +(define_insn "*subx_extend_sp64" + [(set (match_operand:DI 0 "register_operand" "=r") + (zero_extend:DI (minus:SI (minus:SI (match_operand:SI 1 "reg_or_0_operand" "rJ") + (match_operand:SI 2 "arith_operand" "rI")) + (ltu:SI (reg:CC_NOOV 100) (const_int 0)))))] + "TARGET_ARCH64" + "subx\\t%r1, %2, %0" + [(set_attr "type" "misc") + (set_attr "length" "1")]) + +(define_insn "*subx_extend" + [(set (match_operand:DI 0 "register_operand" "=r") + (zero_extend:DI (minus:SI (minus:SI (match_operand:SI 1 "reg_or_0_operand" "rJ") + (match_operand:SI 2 "arith_operand" "rI")) + (ltu:SI (reg:CC_NOOV 100) (const_int 0)))))] + "! TARGET_ARCH64" + "#" + [(set_attr "type" "unary") + (set_attr "length" "2")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "=r") + (zero_extend:DI (minus:SI (minus:SI (match_operand:SI 1 "reg_or_0_operand" "rJ") + (match_operand:SI 2 "arith_operand" "rI")) + (ltu:SI (reg:CC_NOOV 100) (const_int 0)))))] + "! TARGET_ARCH64 && reload_completed" + [(set (match_dup 3) (minus:SI (minus:SI (match_dup 1) (match_dup 2)) + (ltu:SI (reg:CC_NOOV 100) (const_int 0)))) + (set (match_dup 4) (const_int 0))] + "operands[3] = gen_lowpart (SImode, operands[0]); + operands[4] = gen_highpart (SImode, operands[0]);") + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=r") + (plus:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "r")) + (match_operand:DI 2 "register_operand" "r"))) + (clobber (reg:CC 100))] + "! TARGET_ARCH64" + "#" + [(set_attr "type" "multi") + (set_attr "length" "2")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (plus:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "")) + (match_operand:DI 2 "register_operand" ""))) + (clobber (reg:CC 100))] + "! TARGET_ARCH64 && reload_completed" + [(parallel [(set (reg:CC_NOOV 100) + (compare:CC_NOOV (plus:SI (match_dup 3) (match_dup 1)) + (const_int 0))) + (set (match_dup 5) (plus:SI (match_dup 3) (match_dup 1)))]) + (set (match_dup 6) + (plus:SI (plus:SI (match_dup 4) (const_int 0)) + (ltu:SI (reg:CC_NOOV 100) (const_int 0))))] + "operands[3] = gen_lowpart (SImode, operands[2]); + operands[4] = gen_highpart (SImode, operands[2]); + operands[5] = gen_lowpart (SImode, operands[0]); + operands[6] = gen_highpart (SImode, operands[0]);") + +(define_insn "*adddi3_sp64" + [(set (match_operand:DI 0 "register_operand" "=r") + (plus:DI (match_operand:DI 1 "arith_double_operand" "%r") + (match_operand:DI 2 "arith_double_operand" "rHI")))] + "TARGET_ARCH64" + "add\\t%1, %2, %0" + [(set_attr "type" "binary") + (set_attr "length" "1")]) + +(define_expand "addsi3" + [(set (match_operand:SI 0 "register_operand" "=r,d") + (plus:SI (match_operand:SI 1 "arith_operand" "%r,d") + (match_operand:SI 2 "arith_add_operand" "rI,d")))] + "" + " +{ + if (arith_4096_operand(operands[2], DImode)) + { + emit_insn (gen_rtx_SET (VOIDmode, operands[0], + gen_rtx_MINUS (SImode, operands[1], + GEN_INT(-4096)))); + DONE; + } +}") + +(define_insn "*addsi3" + [(set (match_operand:SI 0 "register_operand" "=r,d") + (plus:SI (match_operand:SI 1 "arith_operand" "%r,d") + (match_operand:SI 2 "arith_operand" "rI,d")))] + "" + "@ + add\\t%1, %2, %0 + fpadd32s\\t%1, %2, %0" + [(set_attr "type" "ialu,fp") + (set_attr "length" "1")]) + +(define_insn "*cmp_cc_plus" + [(set (reg:CC_NOOV 100) + (compare:CC_NOOV (plus:SI (match_operand:SI 0 "arith_operand" "%r") + (match_operand:SI 1 "arith_operand" "rI")) + (const_int 0)))] + "! TARGET_LIVE_G0" + "addcc\\t%0, %1, %%g0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_ccx_plus" + [(set (reg:CCX_NOOV 100) + (compare:CCX_NOOV (plus:DI (match_operand:DI 0 "arith_double_operand" "%r") + (match_operand:DI 1 "arith_double_operand" "rHI")) + (const_int 0)))] + "TARGET_ARCH64" + "addcc\\t%0, %1, %%g0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_cc_plus_set" + [(set (reg:CC_NOOV 100) + (compare:CC_NOOV (plus:SI (match_operand:SI 1 "arith_operand" "%r") + (match_operand:SI 2 "arith_operand" "rI")) + (const_int 0))) + (set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (match_dup 1) (match_dup 2)))] + "" + "addcc\\t%1, %2, %0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_ccx_plus_set" + [(set (reg:CCX_NOOV 100) + (compare:CCX_NOOV (plus:DI (match_operand:DI 1 "arith_double_operand" "%r") + (match_operand:DI 2 "arith_double_operand" "rHI")) + (const_int 0))) + (set (match_operand:DI 0 "register_operand" "=r") + (plus:DI (match_dup 1) (match_dup 2)))] + "TARGET_ARCH64" + "addcc\\t%1, %2, %0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_expand "subdi3" + [(set (match_operand:DI 0 "register_operand" "=r") + (minus:DI (match_operand:DI 1 "register_operand" "r") + (match_operand:DI 2 "arith_double_add_operand" "rHI")))] + "" + " +{ + if (! TARGET_ARCH64) + { + emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, + gen_rtx_SET (VOIDmode, operands[0], + gen_rtx_MINUS (DImode, operands[1], + operands[2])), + gen_rtx_CLOBBER (VOIDmode, + gen_rtx_REG (CCmode, SPARC_ICC_REG))))); + DONE; + } + if (arith_double_4096_operand(operands[2], DImode)) + { + emit_insn (gen_rtx_SET (VOIDmode, operands[0], + gen_rtx_PLUS (DImode, operands[1], + GEN_INT(-4096)))); + DONE; + } +}") + +(define_insn "*subdi3_sp32" + [(set (match_operand:DI 0 "register_operand" "=r") + (minus:DI (match_operand:DI 1 "register_operand" "r") + (match_operand:DI 2 "arith_double_operand" "rHI"))) + (clobber (reg:CC 100))] + "! TARGET_ARCH64" + "#" + [(set_attr "length" "2")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (minus:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "arith_double_operand" ""))) + (clobber (reg:CC 100))] + "! TARGET_ARCH64 + && reload_completed + && (GET_CODE (operands[2]) == CONST_INT + || GET_CODE (operands[2]) == CONST_DOUBLE)" + [(clobber (const_int 0))] + " +{ + rtx highp, lowp; + + highp = gen_highpart (SImode, operands[2]); + lowp = gen_lowpart (SImode, operands[2]); + if ((lowp == const0_rtx) + && (operands[0] == operands[1])) + { + emit_insn (gen_rtx_SET (VOIDmode, + gen_highpart (SImode, operands[0]), + gen_rtx_MINUS (SImode, + gen_highpart (SImode, operands[1]), + highp))); + } + else + { + emit_insn (gen_cmp_minus_cc_set (gen_lowpart (SImode, operands[0]), + gen_lowpart (SImode, operands[1]), + lowp)); + emit_insn (gen_subx (gen_highpart (SImode, operands[0]), + gen_highpart (SImode, operands[1]), + highp)); + } + DONE; +}") + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (minus:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "register_operand" ""))) + (clobber (reg:CC 100))] + "! TARGET_ARCH64 + && reload_completed" + [(clobber (const_int 0))] + " +{ + emit_insn (gen_cmp_minus_cc_set (gen_lowpart (SImode, operands[0]), + gen_lowpart (SImode, operands[1]), + gen_lowpart (SImode, operands[2]))); + emit_insn (gen_subx (gen_highpart (SImode, operands[0]), + gen_highpart (SImode, operands[1]), + gen_highpart (SImode, operands[2]))); + DONE; +}") + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=r") + (minus:DI (match_operand:DI 1 "register_operand" "r") + (zero_extend:DI (match_operand:SI 2 "register_operand" "r")))) + (clobber (reg:CC 100))] + "! TARGET_ARCH64" + "#" + [(set_attr "type" "multi") + (set_attr "length" "2")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (minus:DI (match_operand:DI 1 "register_operand" "") + (zero_extend:DI (match_operand:SI 2 "register_operand" "")))) + (clobber (reg:CC 100))] + "! TARGET_ARCH64 && reload_completed" + [(parallel [(set (reg:CC_NOOV 100) + (compare:CC_NOOV (minus:SI (match_dup 3) (match_dup 2)) + (const_int 0))) + (set (match_dup 5) (minus:SI (match_dup 3) (match_dup 2)))]) + (set (match_dup 6) + (minus:SI (minus:SI (match_dup 4) (const_int 0)) + (ltu:SI (reg:CC_NOOV 100) (const_int 0))))] + "operands[3] = gen_lowpart (SImode, operands[1]); + operands[4] = gen_highpart (SImode, operands[1]); + operands[5] = gen_lowpart (SImode, operands[0]); + operands[6] = gen_highpart (SImode, operands[0]);") + +(define_insn "*subdi3_sp64" + [(set (match_operand:DI 0 "register_operand" "=r") + (minus:DI (match_operand:DI 1 "register_operand" "r") + (match_operand:DI 2 "arith_double_operand" "rHI")))] + "TARGET_ARCH64" + "sub\\t%1, %2, %0" + [(set_attr "type" "binary") + (set_attr "length" "1")]) + +(define_expand "subsi3" + [(set (match_operand:SI 0 "register_operand" "=r,d") + (minus:SI (match_operand:SI 1 "register_operand" "r,d") + (match_operand:SI 2 "arith_add_operand" "rI,d")))] + "" + " +{ + if (arith_4096_operand(operands[2], DImode)) + { + emit_insn (gen_rtx_SET (VOIDmode, operands[0], + gen_rtx_PLUS (SImode, operands[1], + GEN_INT(-4096)))); + DONE; + } +}") + +(define_insn "*subsi3" + [(set (match_operand:SI 0 "register_operand" "=r,d") + (minus:SI (match_operand:SI 1 "register_operand" "r,d") + (match_operand:SI 2 "arith_operand" "rI,d")))] + "" + "@ + sub\\t%1, %2, %0 + fpsub32s\\t%1, %2, %0" + [(set_attr "type" "ialu,fp") + (set_attr "length" "1")]) + +(define_insn "*cmp_minus_cc" + [(set (reg:CC_NOOV 100) + (compare:CC_NOOV (minus:SI (match_operand:SI 0 "reg_or_0_operand" "rJ") + (match_operand:SI 1 "arith_operand" "rI")) + (const_int 0)))] + "! TARGET_LIVE_G0" + "subcc\\t%r0, %1, %%g0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_minus_ccx" + [(set (reg:CCX_NOOV 100) + (compare:CCX_NOOV (minus:DI (match_operand:DI 0 "register_operand" "r") + (match_operand:DI 1 "arith_double_operand" "rHI")) + (const_int 0)))] + "TARGET_ARCH64" + "subcc\\t%0, %1, %%g0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "cmp_minus_cc_set" + [(set (reg:CC_NOOV 100) + (compare:CC_NOOV (minus:SI (match_operand:SI 1 "reg_or_0_operand" "rJ") + (match_operand:SI 2 "arith_operand" "rI")) + (const_int 0))) + (set (match_operand:SI 0 "register_operand" "=r") + (minus:SI (match_dup 1) (match_dup 2)))] + "" + "subcc\\t%r1, %2, %0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_minus_ccx_set" + [(set (reg:CCX_NOOV 100) + (compare:CCX_NOOV (minus:DI (match_operand:DI 1 "register_operand" "r") + (match_operand:DI 2 "arith_double_operand" "rHI")) + (const_int 0))) + (set (match_operand:DI 0 "register_operand" "=r") + (minus:DI (match_dup 1) (match_dup 2)))] + "TARGET_ARCH64" + "subcc\\t%1, %2, %0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +;; Integer Multiply/Divide. + +;; The 32 bit multiply/divide instructions are deprecated on v9 and shouldn't +;; we used. We still use them in 32 bit v9 compilers. +;; The 64 bit v9 compiler will (/should) widen the args and use muldi3. + +(define_insn "mulsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (mult:SI (match_operand:SI 1 "arith_operand" "%r") + (match_operand:SI 2 "arith_operand" "rI")))] + "TARGET_HARD_MUL" + "smul\\t%1, %2, %0" + [(set_attr "type" "imul") + (set_attr "length" "1")]) + +(define_expand "muldi3" + [(set (match_operand:DI 0 "register_operand" "=r") + (mult:DI (match_operand:DI 1 "arith_double_operand" "%r") + (match_operand:DI 2 "arith_double_operand" "rHI")))] + "TARGET_ARCH64 || TARGET_V8PLUS" + " +{ + if (TARGET_V8PLUS) + { + emit_insn (gen_muldi3_v8plus (operands[0], operands[1], operands[2])); + DONE; + } +}") + +(define_insn "*muldi3_sp64" + [(set (match_operand:DI 0 "register_operand" "=r") + (mult:DI (match_operand:DI 1 "arith_double_operand" "%r") + (match_operand:DI 2 "arith_double_operand" "rHI")))] + "TARGET_ARCH64" + "mulx\\t%1, %2, %0" + [(set_attr "type" "imul") + (set_attr "length" "1")]) + +;; V8plus wide multiply. +;; XXX +(define_insn "muldi3_v8plus" + [(set (match_operand:DI 0 "register_operand" "=r,h") + (mult:DI (match_operand:DI 1 "arith_double_operand" "%r,0") + (match_operand:DI 2 "arith_double_operand" "rHI,rHI"))) + (clobber (match_scratch:SI 3 "=&h,X")) + (clobber (match_scratch:SI 4 "=&h,X"))] + "TARGET_V8PLUS" + "* +{ + if (sparc_check_64 (operands[1], insn) <= 0) + output_asm_insn (\"srl\\t%L1, 0, %L1\", operands); + if (which_alternative == 1) + output_asm_insn (\"sllx\\t%H1, 32, %H1\", operands); + if (sparc_check_64 (operands[2], insn) <= 0) + output_asm_insn (\"srl\\t%L2, 0, %L2\", operands); + if (which_alternative == 1) + return \"or\\t%L1, %H1, %H1\\n\\tsllx\\t%H2, 32, %L1\\n\\tor\\t%L2, %L1, %L1\\n\\tmulx\\t%H1, %L1, %L0\;srlx\\t%L0, 32, %H0\"; + else + return \"sllx\\t%H1, 32, %3\\n\\tsllx\\t%H2, 32, %4\\n\\tor\\t%L1, %3, %3\\n\\tor\\t%L2, %4, %4\\n\\tmulx\\t%3, %4, %3\\n\\tsrlx\\t%3, 32, %H0\\n\\tmov\\t%3, %L0\"; +}" + [(set_attr "length" "9,8")]) + +;; It is not known whether this will match. + +(define_insn "*cmp_mul_set" + [(set (match_operand:SI 0 "register_operand" "=r") + (mult:SI (match_operand:SI 1 "arith_operand" "%r") + (match_operand:SI 2 "arith_operand" "rI"))) + (set (reg:CC_NOOV 100) + (compare:CC_NOOV (mult:SI (match_dup 1) (match_dup 2)) + (const_int 0)))] + "TARGET_V8 || TARGET_SPARCLITE || TARGET_DEPRECATED_V8_INSNS" + "smulcc\\t%1, %2, %0" + [(set_attr "type" "imul") + (set_attr "length" "1")]) + +(define_expand "mulsidi3" + [(set (match_operand:DI 0 "register_operand" "") + (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "")) + (sign_extend:DI (match_operand:SI 2 "arith_operand" ""))))] + "TARGET_HARD_MUL" + " +{ + if (CONSTANT_P (operands[2])) + { + if (TARGET_V8PLUS) + { + emit_insn (gen_const_mulsidi3_v8plus (operands[0], operands[1], + operands[2])); + DONE; + } + emit_insn (gen_const_mulsidi3 (operands[0], operands[1], operands[2])); + DONE; + } + if (TARGET_V8PLUS) + { + emit_insn (gen_mulsidi3_v8plus (operands[0], operands[1], operands[2])); + DONE; + } +}") + +;; V9 puts the 64 bit product in a 64 bit register. Only out or global +;; registers can hold 64 bit values in the V8plus environment. +;; XXX +(define_insn "mulsidi3_v8plus" + [(set (match_operand:DI 0 "register_operand" "=h,r") + (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "r,r")) + (sign_extend:DI (match_operand:SI 2 "register_operand" "r,r")))) + (clobber (match_scratch:SI 3 "=X,&h"))] + "TARGET_V8PLUS" + "@ + smul\\t%1, %2, %L0\\n\\tsrlx\\t%L0, 32, %H0 + smul\\t%1, %2, %3\\n\\tsrlx\\t%3, 32, %H0\\n\\tmov\\t%3, %L0" + [(set_attr "length" "2,3")]) + +;; XXX +(define_insn "const_mulsidi3_v8plus" + [(set (match_operand:DI 0 "register_operand" "=h,r") + (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "r,r")) + (match_operand:SI 2 "small_int" "I,I"))) + (clobber (match_scratch:SI 3 "=X,&h"))] + "TARGET_V8PLUS" + "@ + smul\\t%1, %2, %L0\\n\\tsrlx\\t%L0, 32, %H0 + smul\\t%1, %2, %3\\n\\tsrlx\\t%3, 32, %H0\\n\\tmov\\t%3, %L0" + [(set_attr "length" "2,3")]) + +;; XXX +(define_insn "*mulsidi3_sp32" + [(set (match_operand:DI 0 "register_operand" "=r") + (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "r")) + (sign_extend:DI (match_operand:SI 2 "register_operand" "r"))))] + "TARGET_HARD_MUL32" + "* +{ + return TARGET_SPARCLET ? \"smuld\\t%1, %2, %L0\" : \"smul\\t%1, %2, %L0\\n\\trd\\t%%y, %H0\"; +}" + [(set (attr "length") + (if_then_else (eq_attr "isa" "sparclet") + (const_int 1) (const_int 2)))]) + +;; Extra pattern, because sign_extend of a constant isn't valid. + +;; XXX +(define_insn "const_mulsidi3" + [(set (match_operand:DI 0 "register_operand" "=r") + (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "r")) + (match_operand:SI 2 "small_int" "I")))] + "TARGET_HARD_MUL" + "* +{ + return TARGET_SPARCLET ? \"smuld\\t%1, %2, %L0\" : \"smul\\t%1, %2, %L0\\n\\trd\\t%%y, %H0\"; +}" + [(set (attr "length") + (if_then_else (eq_attr "isa" "sparclet") + (const_int 1) (const_int 2)))]) + +(define_expand "smulsi3_highpart" + [(set (match_operand:SI 0 "register_operand" "") + (truncate:SI + (lshiftrt:DI (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "")) + (sign_extend:DI (match_operand:SI 2 "arith_operand" ""))) + (const_int 32))))] + "TARGET_HARD_MUL" + " +{ + if (CONSTANT_P (operands[2])) + { + if (TARGET_V8PLUS) + { + emit_insn (gen_const_smulsi3_highpart_v8plus (operands[0], + operands[1], + operands[2], + GEN_INT (32))); + DONE; + } + emit_insn (gen_const_smulsi3_highpart (operands[0], operands[1], operands[2])); + DONE; + } + if (TARGET_V8PLUS) + { + emit_insn (gen_smulsi3_highpart_v8plus (operands[0], operands[1], + operands[2], GEN_INT (32))); + DONE; + } +}") + +;; XXX +(define_insn "smulsi3_highpart_v8plus" + [(set (match_operand:SI 0 "register_operand" "=h,r") + (truncate:SI + (lshiftrt:DI (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "r,r")) + (sign_extend:DI (match_operand:SI 2 "register_operand" "r,r"))) + (match_operand:SI 3 "const_int_operand" "i,i")))) + (clobber (match_scratch:SI 4 "=X,&h"))] + "TARGET_V8PLUS" + "@ + smul %1,%2,%0\;srlx %0,%3,%0 + smul %1,%2,%4\;srlx %4,%3,%0" + [(set_attr "length" "2")]) + +;; The combiner changes TRUNCATE in the previous pattern to SUBREG. +;; XXX +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=h,r") + (subreg:SI + (lshiftrt:DI + (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "r,r")) + (sign_extend:DI (match_operand:SI 2 "register_operand" "r,r"))) + (match_operand:SI 3 "const_int_operand" "i,i")) + 1)) + (clobber (match_scratch:SI 4 "=X,&h"))] + "TARGET_V8PLUS" + "@ + smul\\t%1, %2, %0\\n\\tsrlx\\t%0, %3, %0 + smul\\t%1, %2, %4\\n\\tsrlx\\t%4, %3, %0" + [(set_attr "length" "2")]) + +;; XXX +(define_insn "const_smulsi3_highpart_v8plus" + [(set (match_operand:SI 0 "register_operand" "=h,r") + (truncate:SI + (lshiftrt:DI (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "r,r")) + (match_operand 2 "small_int" "i,i")) + (match_operand:SI 3 "const_int_operand" "i,i")))) + (clobber (match_scratch:SI 4 "=X,&h"))] + "TARGET_V8PLUS" + "@ + smul\\t%1, %2, %0\\n\\tsrlx\\t%0, %3, %0 + smul\\t%1, %2, %4\\n\\tsrlx\\t%4, %3, %0" + [(set_attr "length" "2")]) + +;; XXX +(define_insn "*smulsi3_highpart_sp32" + [(set (match_operand:SI 0 "register_operand" "=r") + (truncate:SI + (lshiftrt:DI (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "r")) + (sign_extend:DI (match_operand:SI 2 "register_operand" "r"))) + (const_int 32))))] + "TARGET_HARD_MUL32 + && ! TARGET_LIVE_G0" + "smul\\t%1, %2, %%g0\\n\\trd\\t%%y, %0" + [(set_attr "length" "2")]) + +;; XXX +(define_insn "const_smulsi3_highpart" + [(set (match_operand:SI 0 "register_operand" "=r") + (truncate:SI + (lshiftrt:DI (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "r")) + (match_operand:SI 2 "register_operand" "r")) + (const_int 32))))] + "TARGET_HARD_MUL32 + && ! TARGET_LIVE_G0" + "smul\\t%1, %2, %%g0\\n\\trd\\t%%y, %0" + [(set_attr "length" "2")]) + +(define_expand "umulsidi3" + [(set (match_operand:DI 0 "register_operand" "") + (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "")) + (zero_extend:DI (match_operand:SI 2 "uns_arith_operand" ""))))] + "TARGET_HARD_MUL" + " +{ + if (CONSTANT_P (operands[2])) + { + if (TARGET_V8PLUS) + { + emit_insn (gen_const_umulsidi3_v8plus (operands[0], operands[1], + operands[2])); + DONE; + } + emit_insn (gen_const_umulsidi3 (operands[0], operands[1], operands[2])); + DONE; + } + if (TARGET_V8PLUS) + { + emit_insn (gen_umulsidi3_v8plus (operands[0], operands[1], operands[2])); + DONE; + } +}") + +;; XXX +(define_insn "umulsidi3_v8plus" + [(set (match_operand:DI 0 "register_operand" "=h,r") + (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "r,r")) + (zero_extend:DI (match_operand:SI 2 "register_operand" "r,r")))) + (clobber (match_scratch:SI 3 "=X,&h"))] + "TARGET_V8PLUS" + "@ + umul\\t%1, %2, %L0\\n\\tsrlx\\t%L0, 32, %H0 + umul\\t%1, %2, %3\\n\\tsrlx\\t%3, 32, %H0\\n\\tmov\\t%3, %L0" + [(set_attr "length" "2,3")]) + +;; XXX +(define_insn "*umulsidi3_sp32" + [(set (match_operand:DI 0 "register_operand" "=r") + (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "r")) + (zero_extend:DI (match_operand:SI 2 "register_operand" "r"))))] + "TARGET_HARD_MUL32" + "* +{ + return TARGET_SPARCLET ? \"umuld\\t%1, %2, %L0\" : \"umul\\t%1, %2, %L0\\n\\trd\\t%%y, %H0\"; +}" + [(set (attr "length") + (if_then_else (eq_attr "isa" "sparclet") + (const_int 1) (const_int 2)))]) + +;; Extra pattern, because sign_extend of a constant isn't valid. + +;; XXX +(define_insn "const_umulsidi3" + [(set (match_operand:DI 0 "register_operand" "=r") + (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "r")) + (match_operand:SI 2 "uns_small_int" "")))] + "TARGET_HARD_MUL32" + "* +{ + return TARGET_SPARCLET ? \"umuld\\t%1, %2, %L0\" : \"umul\\t%1, %2, %L0\\n\\trd\\t%%y, %H0\"; +}" + [(set (attr "length") + (if_then_else (eq_attr "isa" "sparclet") + (const_int 1) (const_int 2)))]) + +;; XXX +(define_insn "const_umulsidi3_v8plus" + [(set (match_operand:DI 0 "register_operand" "=h,r") + (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "r,r")) + (match_operand:SI 2 "uns_small_int" ""))) + (clobber (match_scratch:SI 3 "=X,h"))] + "TARGET_V8PLUS" + "@ + umul\\t%1, %2, %L0\\n\\tsrlx\\t%L0, 32, %H0 + umul\\t%1, %2, %3\\n\\tsrlx\\t%3, 32, %H0\\n\\tmov\\t%3, %L0" + [(set_attr "length" "2,3")]) + +(define_expand "umulsi3_highpart" + [(set (match_operand:SI 0 "register_operand" "") + (truncate:SI + (lshiftrt:DI (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "")) + (zero_extend:DI (match_operand:SI 2 "uns_arith_operand" ""))) + (const_int 32))))] + "TARGET_HARD_MUL" + " +{ + if (CONSTANT_P (operands[2])) + { + if (TARGET_V8PLUS) + { + emit_insn (gen_const_umulsi3_highpart_v8plus (operands[0], + operands[1], + operands[2], + GEN_INT (32))); + DONE; + } + emit_insn (gen_const_umulsi3_highpart (operands[0], operands[1], operands[2])); + DONE; + } + if (TARGET_V8PLUS) + { + emit_insn (gen_umulsi3_highpart_v8plus (operands[0], operands[1], + operands[2], GEN_INT (32))); + DONE; + } +}") + +;; XXX +(define_insn "umulsi3_highpart_v8plus" + [(set (match_operand:SI 0 "register_operand" "=h,r") + (truncate:SI + (lshiftrt:DI (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "r,r")) + (zero_extend:DI (match_operand:SI 2 "register_operand" "r,r"))) + (match_operand:SI 3 "const_int_operand" "i,i")))) + (clobber (match_scratch:SI 4 "=X,h"))] + "TARGET_V8PLUS" + "@ + umul\\t%1, %2, %0\\n\\tsrlx\\t%0, %3, %0 + umul\\t%1, %2, %4\\n\\tsrlx\\t%4, %3, %0" + [(set_attr "length" "2")]) + +;; XXX +(define_insn "const_umulsi3_highpart_v8plus" + [(set (match_operand:SI 0 "register_operand" "=h,r") + (truncate:SI + (lshiftrt:DI (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "r,r")) + (match_operand:SI 2 "uns_small_int" "")) + (match_operand:SI 3 "const_int_operand" "i,i")))) + (clobber (match_scratch:SI 4 "=X,h"))] + "TARGET_V8PLUS" + "@ + umul\\t%1, %2, %0\\n\\tsrlx\\t%0, %3, %0 + umul\\t%1, %2, %4\\n\\tsrlx\\t%4, %3, %0" + [(set_attr "length" "2")]) + +;; XXX +(define_insn "*umulsi3_highpart_sp32" + [(set (match_operand:SI 0 "register_operand" "=r") + (truncate:SI + (lshiftrt:DI (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "r")) + (zero_extend:DI (match_operand:SI 2 "register_operand" "r"))) + (const_int 32))))] + "TARGET_HARD_MUL32 + && ! TARGET_LIVE_G0" + "umul\\t%1, %2, %%g0\\n\\trd\\t%%y, %0" + [(set_attr "length" "2")]) + +;; XXX +(define_insn "const_umulsi3_highpart" + [(set (match_operand:SI 0 "register_operand" "=r") + (truncate:SI + (lshiftrt:DI (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "r")) + (match_operand:SI 2 "uns_small_int" "")) + (const_int 32))))] + "TARGET_HARD_MUL32 + && ! TARGET_LIVE_G0" + "umul\\t%1, %2, %%g0\\n\\trd\\t%%y, %0" + [(set_attr "length" "2")]) + +;; The v8 architecture specifies that there must be 3 instructions between +;; a y register write and a use of it for correct results. + +;; XXX SHEESH +(define_insn "divsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (div:SI (match_operand:SI 1 "register_operand" "r,r") + (match_operand:SI 2 "input_operand" "rI,m"))) + (clobber (match_scratch:SI 3 "=&r,&r"))] + "(TARGET_V8 + || TARGET_DEPRECATED_V8_INSNS) + && ! TARGET_LIVE_G0" + "* +{ + if (which_alternative == 0) + if (TARGET_V9) + return \"sra\\t%1, 31, %3\\n\\twr\\t%%g0, %3, %%y\\n\\tsdiv\\t%1, %2, %0\"; + else + return \"sra\\t%1, 31, %3\\n\\twr\\t%%g0, %3, %%y\\n\\tnop\\n\\tnop\\n\\tnop\\n\\tsdiv\\t%1, %2, %0\"; + else + if (TARGET_V9) + return \"sra\\t%1, 31, %3\\n\\twr\\t%%g0, %3, %%y\\n\\tld\\t%2, %3\\n\\tsdiv\\t%1, %3, %0\"; + else + return \"sra\\t%1, 31, %3\\n\\twr\\t%%g0, %3, %%y\\n\\tld\\t%2, %3\\n\\tnop\\n\\tnop\\n\\tsdiv\\t%1, %3, %0\"; +}" + [(set (attr "length") + (if_then_else (eq_attr "isa" "v9") + (const_int 4) (const_int 7)))]) + +(define_insn "divdi3" + [(set (match_operand:DI 0 "register_operand" "=r") + (div:DI (match_operand:DI 1 "register_operand" "r") + (match_operand:DI 2 "arith_double_operand" "rHI")))] + "TARGET_ARCH64" + "sdivx\\t%1, %2, %0") + +;; It is not known whether this will match. + +;; XXX I hope it doesn't fucking match... +(define_insn "*cmp_sdiv_cc_set" + [(set (match_operand:SI 0 "register_operand" "=r") + (div:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "arith_operand" "rI"))) + (set (reg:CC 100) + (compare:CC (div:SI (match_dup 1) (match_dup 2)) + (const_int 0))) + (clobber (match_scratch:SI 3 "=&r"))] + "(TARGET_V8 + || TARGET_DEPRECATED_V8_INSNS) + && ! TARGET_LIVE_G0" + "* +{ + if (TARGET_V9) + return \"sra\\t%1, 31, %3\\n\\twr\\t%%g0, %3, %%y\\n\\tsdivcc\\t%1, %2, %0\"; + else + return \"sra\\t%1, 31, %3\\n\\twr\\t%%g0, %3, %%y\\n\\tnop\\n\\tnop\\n\\tnop\\n\\tsdivcc\\t%1, %2, %0\"; +}" + [(set (attr "length") + (if_then_else (eq_attr "isa" "v9") + (const_int 3) (const_int 6)))]) + +;; XXX +(define_insn "udivsi3" + [(set (match_operand:SI 0 "register_operand" "=r,&r,&r") + (udiv:SI (match_operand:SI 1 "reg_or_nonsymb_mem_operand" "r,r,m") + (match_operand:SI 2 "input_operand" "rI,m,r")))] + "(TARGET_V8 + || TARGET_DEPRECATED_V8_INSNS) + && ! TARGET_LIVE_G0" + "* +{ + output_asm_insn (\"wr\\t%%g0, %%g0, %%y\", operands); + switch (which_alternative) + { + default: + if (TARGET_V9) + return \"udiv\\t%1, %2, %0\"; + return \"nop\\n\\tnop\\n\\tnop\\n\\tudiv\\t%1, %2, %0\"; + case 1: + return \"ld\\t%2, %0\\n\\tnop\\n\\tnop\\n\\tudiv\\t%1, %0, %0\"; + case 2: + return \"ld\\t%1, %0\\n\\tnop\\n\\tnop\\n\\tudiv\\t%0, %2, %0\"; + } +}" + [(set (attr "length") + (if_then_else (and (eq_attr "isa" "v9") + (eq_attr "alternative" "0")) + (const_int 2) (const_int 5)))]) + +(define_insn "udivdi3" + [(set (match_operand:DI 0 "register_operand" "=r") + (udiv:DI (match_operand:DI 1 "register_operand" "r") + (match_operand:DI 2 "arith_double_operand" "rHI")))] + "TARGET_ARCH64" + "udivx\\t%1, %2, %0") + +;; It is not known whether this will match. + +;; XXX I hope it doesn't fucking match... +(define_insn "*cmp_udiv_cc_set" + [(set (match_operand:SI 0 "register_operand" "=r") + (udiv:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "arith_operand" "rI"))) + (set (reg:CC 100) + (compare:CC (udiv:SI (match_dup 1) (match_dup 2)) + (const_int 0)))] + "(TARGET_V8 + || TARGET_DEPRECATED_V8_INSNS) + && ! TARGET_LIVE_G0" + "* +{ + if (TARGET_V9) + return \"wr\\t%%g0, %%g0, %%y\\n\\tudivcc\\t%1, %2, %0\"; + else + return \"wr\\t%%g0, %%g0, %%y\\n\\tnop\\n\\tnop\\n\\tnop\\n\\tudivcc\\t%1, %2, %0\"; +}" + [(set (attr "length") + (if_then_else (eq_attr "isa" "v9") + (const_int 2) (const_int 5)))]) + +; sparclet multiply/accumulate insns + +(define_insn "*smacsi" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (mult:SI (match_operand:SI 1 "register_operand" "%r") + (match_operand:SI 2 "arith_operand" "rI")) + (match_operand:SI 3 "register_operand" "0")))] + "TARGET_SPARCLET" + "smac\\t%1, %2, %0" + [(set_attr "type" "imul") + (set_attr "length" "1")]) + +(define_insn "*smacdi" + [(set (match_operand:DI 0 "register_operand" "=r") + (plus:DI (mult:DI (sign_extend:DI + (match_operand:SI 1 "register_operand" "%r")) + (sign_extend:DI + (match_operand:SI 2 "register_operand" "r"))) + (match_operand:DI 3 "register_operand" "0")))] + "TARGET_SPARCLET" + "smacd\\t%1, %2, %L0" + [(set_attr "type" "imul") + (set_attr "length" "1")]) + +(define_insn "*umacdi" + [(set (match_operand:DI 0 "register_operand" "=r") + (plus:DI (mult:DI (zero_extend:DI + (match_operand:SI 1 "register_operand" "%r")) + (zero_extend:DI + (match_operand:SI 2 "register_operand" "r"))) + (match_operand:DI 3 "register_operand" "0")))] + "TARGET_SPARCLET" + "umacd\\t%1, %2, %L0" + [(set_attr "type" "imul") + (set_attr "length" "1")]) + +;;- Boolean 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" "")))] + "" + "") + +(define_insn "*anddi3_sp32" + [(set (match_operand:DI 0 "register_operand" "=r,b") + (and:DI (match_operand:DI 1 "arith_double_operand" "%r,b") + (match_operand:DI 2 "arith_double_operand" "rHI,b")))] + "! TARGET_ARCH64" + "@ + # + fand\\t%1, %2, %0" + [(set_attr "type" "ialu,fp") + (set_attr "length" "2,1")]) + +(define_insn "*anddi3_sp64" + [(set (match_operand:DI 0 "register_operand" "=r,b") + (and:DI (match_operand:DI 1 "arith_double_operand" "%r,b") + (match_operand:DI 2 "arith_double_operand" "rHI,b")))] + "TARGET_ARCH64" + "@ + and\\t%1, %2, %0 + fand\\t%1, %2, %0" + [(set_attr "type" "ialu,fp") + (set_attr "length" "1,1")]) + +(define_insn "andsi3" + [(set (match_operand:SI 0 "register_operand" "=r,d") + (and:SI (match_operand:SI 1 "arith_operand" "%r,d") + (match_operand:SI 2 "arith_operand" "rI,d")))] + "" + "@ + and\\t%1, %2, %0 + fands\\t%1, %2, %0" + [(set_attr "type" "ialu,fp") + (set_attr "length" "1,1")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (and:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "" ""))) + (clobber (match_operand:SI 3 "register_operand" ""))] + "GET_CODE (operands[2]) == CONST_INT + && !SMALL_INT32 (operands[2]) + && (INTVAL (operands[2]) & 0x3ff) == 0x3ff" + [(set (match_dup 3) (match_dup 4)) + (set (match_dup 0) (and:SI (not:SI (match_dup 3)) (match_dup 1)))] + " +{ + operands[4] = GEN_INT (~INTVAL (operands[2]) & 0xffffffff); +}") + +;; Split DImode logical operations requiring two instructions. +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (match_operator:DI 1 "cc_arithop" ; AND, IOR, XOR + [(match_operand:DI 2 "register_operand" "") + (match_operand:DI 3 "arith_double_operand" "")]))] + "! TARGET_ARCH64 + && reload_completed + && ((GET_CODE (operands[0]) == REG + && REGNO (operands[0]) < 32) + || (GET_CODE (operands[0]) == SUBREG + && GET_CODE (SUBREG_REG (operands[0])) == REG + && REGNO (SUBREG_REG (operands[0])) < 32))" + [(set (match_dup 4) (match_op_dup:SI 1 [(match_dup 6) (match_dup 8)])) + (set (match_dup 5) (match_op_dup:SI 1 [(match_dup 7) (match_dup 9)]))] + " +{ + if (GET_CODE (operands[0]) == SUBREG) + operands[0] = alter_subreg (operands[0]); + operands[4] = gen_highpart (SImode, operands[0]); + operands[5] = gen_lowpart (SImode, operands[0]); + operands[6] = gen_highpart (SImode, operands[2]); + operands[7] = gen_lowpart (SImode, operands[2]); + if (GET_CODE (operands[3]) == CONST_INT) + { + if (INTVAL (operands[3]) < 0) + operands[8] = constm1_rtx; + else + operands[8] = const0_rtx; + } + else + operands[8] = gen_highpart (SImode, operands[3]); + operands[9] = gen_lowpart (SImode, operands[3]); +}") + +(define_insn "*and_not_di_sp32" + [(set (match_operand:DI 0 "register_operand" "=r,b") + (and:DI (not:DI (match_operand:DI 1 "register_operand" "r,b")) + (match_operand:DI 2 "register_operand" "r,b")))] + "! TARGET_ARCH64" + "@ + # + fandnot1\\t%1, %2, %0" + [(set_attr "type" "ialu,fp") + (set_attr "length" "2,1")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (and:DI (not:DI (match_operand:DI 1 "register_operand" "")) + (match_operand:DI 2 "register_operand" "")))] + "! TARGET_ARCH64 + && reload_completed + && ((GET_CODE (operands[0]) == REG + && REGNO (operands[0]) < 32) + || (GET_CODE (operands[0]) == SUBREG + && GET_CODE (SUBREG_REG (operands[0])) == REG + && REGNO (SUBREG_REG (operands[0])) < 32))" + [(set (match_dup 3) (and:SI (not:SI (match_dup 4)) (match_dup 5))) + (set (match_dup 6) (and:SI (not:SI (match_dup 7)) (match_dup 8)))] + "if (GET_CODE (operands[0]) == SUBREG) + operands[0] = alter_subreg (operands[0]); + operands[3] = gen_highpart (SImode, operands[0]); + operands[4] = gen_highpart (SImode, operands[1]); + operands[5] = gen_highpart (SImode, operands[2]); + operands[6] = gen_lowpart (SImode, operands[0]); + operands[7] = gen_lowpart (SImode, operands[1]); + operands[8] = gen_lowpart (SImode, operands[2]);") + +(define_insn "*and_not_di_sp64" + [(set (match_operand:DI 0 "register_operand" "=r,b") + (and:DI (not:DI (match_operand:DI 1 "register_operand" "r,b")) + (match_operand:DI 2 "register_operand" "r,b")))] + "TARGET_ARCH64" + "@ + andn\\t%2, %1, %0 + fandnot1\\t%1, %2, %0" + [(set_attr "type" "ialu,fp") + (set_attr "length" "1,1")]) + +(define_insn "*and_not_si" + [(set (match_operand:SI 0 "register_operand" "=r,d") + (and:SI (not:SI (match_operand:SI 1 "register_operand" "r,d")) + (match_operand:SI 2 "register_operand" "r,d")))] + "" + "@ + andn\\t%2, %1, %0 + fandnot1s\\t%1, %2, %0" + [(set_attr "type" "ialu,fp") + (set_attr "length" "1,1")]) + +(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" "")))] + "" + "") + +(define_insn "*iordi3_sp32" + [(set (match_operand:DI 0 "register_operand" "=r,b") + (ior:DI (match_operand:DI 1 "arith_double_operand" "%r,b") + (match_operand:DI 2 "arith_double_operand" "rHI,b")))] + "! TARGET_ARCH64" + "@ + # + for\\t%1, %2, %0" + [(set_attr "type" "ialu,fp") + (set_attr "length" "2,1")]) + +(define_insn "*iordi3_sp64" + [(set (match_operand:DI 0 "register_operand" "=r,b") + (ior:DI (match_operand:DI 1 "arith_double_operand" "%r,b") + (match_operand:DI 2 "arith_double_operand" "rHI,b")))] + "TARGET_ARCH64" + "@ + or\\t%1, %2, %0 + for\\t%1, %2, %0" + [(set_attr "type" "ialu,fp") + (set_attr "length" "1,1")]) + +(define_insn "iorsi3" + [(set (match_operand:SI 0 "register_operand" "=r,d") + (ior:SI (match_operand:SI 1 "arith_operand" "%r,d") + (match_operand:SI 2 "arith_operand" "rI,d")))] + "" + "@ + or\\t%1, %2, %0 + fors\\t%1, %2, %0" + [(set_attr "type" "ialu,fp") + (set_attr "length" "1,1")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (ior:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "" ""))) + (clobber (match_operand:SI 3 "register_operand" ""))] + "GET_CODE (operands[2]) == CONST_INT + && !SMALL_INT32 (operands[2]) + && (INTVAL (operands[2]) & 0x3ff) == 0x3ff" + [(set (match_dup 3) (match_dup 4)) + (set (match_dup 0) (ior:SI (not:SI (match_dup 3)) (match_dup 1)))] + " +{ + operands[4] = GEN_INT (~INTVAL (operands[2]) & 0xffffffff); +}") + +(define_insn "*or_not_di_sp32" + [(set (match_operand:DI 0 "register_operand" "=r,b") + (ior:DI (not:DI (match_operand:DI 1 "register_operand" "r,b")) + (match_operand:DI 2 "register_operand" "r,b")))] + "! TARGET_ARCH64" + "@ + # + fornot1\\t%1, %2, %0" + [(set_attr "type" "ialu,fp") + (set_attr "length" "2,1")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (ior:DI (not:DI (match_operand:DI 1 "register_operand" "")) + (match_operand:DI 2 "register_operand" "")))] + "! TARGET_ARCH64 + && reload_completed + && ((GET_CODE (operands[0]) == REG + && REGNO (operands[0]) < 32) + || (GET_CODE (operands[0]) == SUBREG + && GET_CODE (SUBREG_REG (operands[0])) == REG + && REGNO (SUBREG_REG (operands[0])) < 32))" + [(set (match_dup 3) (ior:SI (not:SI (match_dup 4)) (match_dup 5))) + (set (match_dup 6) (ior:SI (not:SI (match_dup 7)) (match_dup 8)))] + "if (GET_CODE (operands[0]) == SUBREG) + operands[0] = alter_subreg (operands[0]); + operands[3] = gen_highpart (SImode, operands[0]); + operands[4] = gen_highpart (SImode, operands[1]); + operands[5] = gen_highpart (SImode, operands[2]); + operands[6] = gen_lowpart (SImode, operands[0]); + operands[7] = gen_lowpart (SImode, operands[1]); + operands[8] = gen_lowpart (SImode, operands[2]);") + +(define_insn "*or_not_di_sp64" + [(set (match_operand:DI 0 "register_operand" "=r,b") + (ior:DI (not:DI (match_operand:DI 1 "register_operand" "r,b")) + (match_operand:DI 2 "register_operand" "r,b")))] + "TARGET_ARCH64" + "@ + orn\\t%2, %1, %0 + fornot1\\t%1, %2, %0" + [(set_attr "type" "ialu,fp") + (set_attr "length" "1,1")]) + +(define_insn "*or_not_si" + [(set (match_operand:SI 0 "register_operand" "=r,d") + (ior:SI (not:SI (match_operand:SI 1 "register_operand" "r,d")) + (match_operand:SI 2 "register_operand" "r,d")))] + "" + "@ + orn\\t%2, %1, %0 + fornot1s\\t%1, %2, %0" + [(set_attr "type" "ialu,fp") + (set_attr "length" "1,1")]) + +(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" "")))] + "" + "") + +(define_insn "*xordi3_sp32" + [(set (match_operand:DI 0 "register_operand" "=r,b") + (xor:DI (match_operand:DI 1 "arith_double_operand" "%r,b") + (match_operand:DI 2 "arith_double_operand" "rHI,b")))] + "! TARGET_ARCH64" + "@ + # + fxor\\t%1, %2, %0" + [(set_attr "length" "2,1") + (set_attr "type" "ialu,fp")]) + +(define_insn "*xordi3_sp64" + [(set (match_operand:DI 0 "register_operand" "=r,b") + (xor:DI (match_operand:DI 1 "arith_double_operand" "%rJ,b") + (match_operand:DI 2 "arith_double_operand" "rHI,b")))] + "TARGET_ARCH64" + "@ + xor\\t%r1, %2, %0 + fxor\\t%1, %2, %0" + [(set_attr "type" "ialu,fp") + (set_attr "length" "1,1")]) + +(define_insn "*xordi3_sp64_dbl" + [(set (match_operand:DI 0 "register_operand" "=r") + (xor:DI (match_operand:DI 1 "register_operand" "r") + (match_operand:DI 2 "const64_operand" "")))] + "(TARGET_ARCH64 + && HOST_BITS_PER_WIDE_INT != 64)" + "xor\\t%1, %2, %0" + [(set_attr "type" "ialu") + (set_attr "length" "1")]) + +(define_insn "xorsi3" + [(set (match_operand:SI 0 "register_operand" "=r,d") + (xor:SI (match_operand:SI 1 "arith_operand" "%rJ,d") + (match_operand:SI 2 "arith_operand" "rI,d")))] + "" + "@ + xor\\t%r1, %2, %0 + fxors\\t%1, %2, %0" + [(set_attr "type" "ialu,fp") + (set_attr "length" "1,1")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (xor:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "" ""))) + (clobber (match_operand:SI 3 "register_operand" ""))] + "GET_CODE (operands[2]) == CONST_INT + && !SMALL_INT32 (operands[2]) + && (INTVAL (operands[2]) & 0x3ff) == 0x3ff" + [(set (match_dup 3) (match_dup 4)) + (set (match_dup 0) (not:SI (xor:SI (match_dup 3) (match_dup 1))))] + " +{ + operands[4] = GEN_INT (~INTVAL (operands[2]) & 0xffffffff); +}") + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (not:SI (xor:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "" "")))) + (clobber (match_operand:SI 3 "register_operand" ""))] + "GET_CODE (operands[2]) == CONST_INT + && !SMALL_INT32 (operands[2]) + && (INTVAL (operands[2]) & 0x3ff) == 0x3ff" + [(set (match_dup 3) (match_dup 4)) + (set (match_dup 0) (xor:SI (match_dup 3) (match_dup 1)))] + " +{ + operands[4] = GEN_INT (~INTVAL (operands[2]) & 0xffffffff); +}") + +;; xnor patterns. Note that (a ^ ~b) == (~a ^ b) == ~(a ^ b). +;; Combine now canonicalizes to the rightmost expression. +(define_insn "*xor_not_di_sp32" + [(set (match_operand:DI 0 "register_operand" "=r,b") + (not:DI (xor:DI (match_operand:DI 1 "register_operand" "r,b") + (match_operand:DI 2 "register_operand" "r,b"))))] + "! TARGET_ARCH64" + "@ + # + fxnor\\t%1, %2, %0" + [(set_attr "length" "2,1") + (set_attr "type" "ialu,fp")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (not:DI (xor:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "register_operand" ""))))] + "! TARGET_ARCH64 + && reload_completed + && ((GET_CODE (operands[0]) == REG + && REGNO (operands[0]) < 32) + || (GET_CODE (operands[0]) == SUBREG + && GET_CODE (SUBREG_REG (operands[0])) == REG + && REGNO (SUBREG_REG (operands[0])) < 32))" + [(set (match_dup 3) (not:SI (xor:SI (match_dup 4) (match_dup 5)))) + (set (match_dup 6) (not:SI (xor:SI (match_dup 7) (match_dup 8))))] + "if (GET_CODE (operands[0]) == SUBREG) + operands[0] = alter_subreg (operands[0]); + operands[3] = gen_highpart (SImode, operands[0]); + operands[4] = gen_highpart (SImode, operands[1]); + operands[5] = gen_highpart (SImode, operands[2]); + operands[6] = gen_lowpart (SImode, operands[0]); + operands[7] = gen_lowpart (SImode, operands[1]); + operands[8] = gen_lowpart (SImode, operands[2]);") + +(define_insn "*xor_not_di_sp64" + [(set (match_operand:DI 0 "register_operand" "=r,b") + (not:DI (xor:DI (match_operand:DI 1 "reg_or_0_operand" "rJ,b") + (match_operand:DI 2 "arith_double_operand" "rHI,b"))))] + "TARGET_ARCH64" + "@ + xnor\\t%r1, %2, %0 + fxnor\\t%1, %2, %0" + [(set_attr "type" "ialu,fp") + (set_attr "length" "1,1")]) + +(define_insn "*xor_not_si" + [(set (match_operand:SI 0 "register_operand" "=r,d") + (not:SI (xor:SI (match_operand:SI 1 "reg_or_0_operand" "rJ,d") + (match_operand:SI 2 "arith_operand" "rI,d"))))] + "" + "@ + xnor\\t%r1, %2, %0 + fxnors\\t%1, %2, %0" + [(set_attr "type" "ialu,fp") + (set_attr "length" "1,1")]) + +;; These correspond to the above in the case where we also (or only) +;; want to set the condition code. + +(define_insn "*cmp_cc_arith_op" + [(set (reg:CC 100) + (compare:CC + (match_operator:SI 2 "cc_arithop" + [(match_operand:SI 0 "arith_operand" "%r") + (match_operand:SI 1 "arith_operand" "rI")]) + (const_int 0)))] + "! TARGET_LIVE_G0" + "%A2cc\\t%0, %1, %%g0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_ccx_arith_op" + [(set (reg:CCX 100) + (compare:CCX + (match_operator:DI 2 "cc_arithop" + [(match_operand:DI 0 "arith_double_operand" "%r") + (match_operand:DI 1 "arith_double_operand" "rHI")]) + (const_int 0)))] + "TARGET_ARCH64" + "%A2cc\\t%0, %1, %%g0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_cc_arith_op_set" + [(set (reg:CC 100) + (compare:CC + (match_operator:SI 3 "cc_arithop" + [(match_operand:SI 1 "arith_operand" "%r") + (match_operand:SI 2 "arith_operand" "rI")]) + (const_int 0))) + (set (match_operand:SI 0 "register_operand" "=r") + (match_dup 3))] + "" + "%A3cc\\t%1, %2, %0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_ccx_arith_op_set" + [(set (reg:CCX 100) + (compare:CCX + (match_operator:DI 3 "cc_arithop" + [(match_operand:DI 1 "arith_double_operand" "%r") + (match_operand:DI 2 "arith_double_operand" "rHI")]) + (const_int 0))) + (set (match_operand:DI 0 "register_operand" "=r") + (match_dup 3))] + "TARGET_ARCH64" + "%A3cc\\t%1, %2, %0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_cc_xor_not" + [(set (reg:CC 100) + (compare:CC + (not:SI (xor:SI (match_operand:SI 0 "reg_or_0_operand" "%rJ") + (match_operand:SI 1 "arith_operand" "rI"))) + (const_int 0)))] + "! TARGET_LIVE_G0" + "xnorcc\\t%r0, %1, %%g0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_ccx_xor_not" + [(set (reg:CCX 100) + (compare:CCX + (not:DI (xor:DI (match_operand:DI 0 "reg_or_0_operand" "%rJ") + (match_operand:DI 1 "arith_double_operand" "rHI"))) + (const_int 0)))] + "TARGET_ARCH64" + "xnorcc\\t%r0, %1, %%g0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_cc_xor_not_set" + [(set (reg:CC 100) + (compare:CC + (not:SI (xor:SI (match_operand:SI 1 "reg_or_0_operand" "%rJ") + (match_operand:SI 2 "arith_operand" "rI"))) + (const_int 0))) + (set (match_operand:SI 0 "register_operand" "=r") + (not:SI (xor:SI (match_dup 1) (match_dup 2))))] + "" + "xnorcc\\t%r1, %2, %0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_ccx_xor_not_set" + [(set (reg:CCX 100) + (compare:CCX + (not:DI (xor:DI (match_operand:DI 1 "reg_or_0_operand" "%rJ") + (match_operand:DI 2 "arith_double_operand" "rHI"))) + (const_int 0))) + (set (match_operand:DI 0 "register_operand" "=r") + (not:DI (xor:DI (match_dup 1) (match_dup 2))))] + "TARGET_ARCH64" + "xnorcc\\t%r1, %2, %0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_cc_arith_op_not" + [(set (reg:CC 100) + (compare:CC + (match_operator:SI 2 "cc_arithopn" + [(not:SI (match_operand:SI 0 "arith_operand" "rI")) + (match_operand:SI 1 "reg_or_0_operand" "rJ")]) + (const_int 0)))] + "! TARGET_LIVE_G0" + "%B2cc\\t%r1, %0, %%g0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_ccx_arith_op_not" + [(set (reg:CCX 100) + (compare:CCX + (match_operator:DI 2 "cc_arithopn" + [(not:DI (match_operand:DI 0 "arith_double_operand" "rHI")) + (match_operand:DI 1 "reg_or_0_operand" "rJ")]) + (const_int 0)))] + "TARGET_ARCH64" + "%B2cc\\t%r1, %0, %%g0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_cc_arith_op_not_set" + [(set (reg:CC 100) + (compare:CC + (match_operator:SI 3 "cc_arithopn" + [(not:SI (match_operand:SI 1 "arith_operand" "rI")) + (match_operand:SI 2 "reg_or_0_operand" "rJ")]) + (const_int 0))) + (set (match_operand:SI 0 "register_operand" "=r") + (match_dup 3))] + "" + "%B3cc\\t%r2, %1, %0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_ccx_arith_op_not_set" + [(set (reg:CCX 100) + (compare:CCX + (match_operator:DI 3 "cc_arithopn" + [(not:DI (match_operand:DI 1 "arith_double_operand" "rHI")) + (match_operand:DI 2 "reg_or_0_operand" "rJ")]) + (const_int 0))) + (set (match_operand:DI 0 "register_operand" "=r") + (match_dup 3))] + "TARGET_ARCH64" + "%B3cc\\t%r2, %1, %0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +;; We cannot use the "neg" pseudo insn because the Sun assembler +;; does not know how to make it work for constants. + +(define_expand "negdi2" + [(set (match_operand:DI 0 "register_operand" "=r") + (neg:DI (match_operand:DI 1 "register_operand" "r")))] + "" + " +{ + if (! TARGET_ARCH64) + { + emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, + gen_rtx_SET (VOIDmode, operand0, + gen_rtx_NEG (DImode, operand1)), + gen_rtx_CLOBBER (VOIDmode, + gen_rtx_REG (CCmode, SPARC_ICC_REG))))); + DONE; + } +}") + +(define_insn "*negdi2_sp32" + [(set (match_operand:DI 0 "register_operand" "=r") + (neg:DI (match_operand:DI 1 "register_operand" "r"))) + (clobber (reg:CC 100))] + "! TARGET_ARCH64 + && ! TARGET_LIVE_G0" + "#" + [(set_attr "type" "unary") + (set_attr "length" "2")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (neg:DI (match_operand:DI 1 "register_operand" ""))) + (clobber (reg:CC 100))] + "! TARGET_ARCH64 + && ! TARGET_LIVE_G0 + && reload_completed" + [(parallel [(set (reg:CC_NOOV 100) + (compare:CC_NOOV (minus:SI (const_int 0) (match_dup 5)) + (const_int 0))) + (set (match_dup 4) (minus:SI (const_int 0) (match_dup 5)))]) + (set (match_dup 2) (minus:SI (minus:SI (const_int 0) (match_dup 3)) + (ltu:SI (reg:CC 100) (const_int 0))))] + "operands[2] = gen_highpart (SImode, operands[0]); + operands[3] = gen_highpart (SImode, operands[1]); + operands[4] = gen_lowpart (SImode, operands[0]); + operands[5] = gen_lowpart (SImode, operands[1]);") + +(define_insn "*negdi2_sp64" + [(set (match_operand:DI 0 "register_operand" "=r") + (neg:DI (match_operand:DI 1 "register_operand" "r")))] + "TARGET_ARCH64" + "sub\\t%%g0, %1, %0" + [(set_attr "type" "unary") + (set_attr "length" "1")]) + +(define_expand "negsi2" + [(set (match_operand:SI 0 "register_operand" "") + (neg:SI (match_operand:SI 1 "arith_operand" "")))] + "" + " +{ + if (TARGET_LIVE_G0) + { + rtx zero_reg = gen_reg_rtx (SImode); + + emit_insn (gen_rtx_SET (VOIDmode, zero_reg, const0_rtx)); + emit_insn (gen_rtx_SET (VOIDmode, operands[0], + gen_rtx_MINUS (SImode, zero_reg, + operands[1]))); + DONE; + } +}") + +(define_insn "*negsi2_not_liveg0" + [(set (match_operand:SI 0 "register_operand" "=r") + (neg:SI (match_operand:SI 1 "arith_operand" "rI")))] + "! TARGET_LIVE_G0" + "sub\\t%%g0, %1, %0" + [(set_attr "type" "unary") + (set_attr "length" "1")]) + +(define_insn "*cmp_cc_neg" + [(set (reg:CC_NOOV 100) + (compare:CC_NOOV (neg:SI (match_operand:SI 0 "arith_operand" "rI")) + (const_int 0)))] + "! TARGET_LIVE_G0" + "subcc\\t%%g0, %0, %%g0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_ccx_neg" + [(set (reg:CCX_NOOV 100) + (compare:CCX_NOOV (neg:DI (match_operand:DI 0 "arith_double_operand" "rHI")) + (const_int 0)))] + "TARGET_ARCH64" + "subcc\\t%%g0, %0, %%g0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_cc_set_neg" + [(set (reg:CC_NOOV 100) + (compare:CC_NOOV (neg:SI (match_operand:SI 1 "arith_operand" "rI")) + (const_int 0))) + (set (match_operand:SI 0 "register_operand" "=r") + (neg:SI (match_dup 1)))] + "! TARGET_LIVE_G0" + "subcc\\t%%g0, %1, %0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_ccx_set_neg" + [(set (reg:CCX_NOOV 100) + (compare:CCX_NOOV (neg:DI (match_operand:DI 1 "arith_double_operand" "rHI")) + (const_int 0))) + (set (match_operand:DI 0 "register_operand" "=r") + (neg:DI (match_dup 1)))] + "TARGET_ARCH64" + "subcc\\t%%g0, %1, %0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +;; We cannot use the "not" pseudo insn because the Sun assembler +;; does not know how to make it work for constants. +(define_expand "one_cmpldi2" + [(set (match_operand:DI 0 "register_operand" "") + (not:DI (match_operand:DI 1 "register_operand" "")))] + "" + "") + +(define_insn "*one_cmpldi2_sp32" + [(set (match_operand:DI 0 "register_operand" "=r,b") + (not:DI (match_operand:DI 1 "register_operand" "r,b")))] + "! TARGET_ARCH64" + "@ + # + fnot1\\t%1, %0" + [(set_attr "type" "unary,fp") + (set_attr "length" "2,1")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (not:DI (match_operand:DI 1 "register_operand" "")))] + "! TARGET_ARCH64 + && reload_completed + && ((GET_CODE (operands[0]) == REG + && REGNO (operands[0]) < 32) + || (GET_CODE (operands[0]) == SUBREG + && GET_CODE (SUBREG_REG (operands[0])) == REG + && REGNO (SUBREG_REG (operands[0])) < 32))" + [(set (match_dup 2) (not:SI (xor:SI (match_dup 3) (const_int 0)))) + (set (match_dup 4) (not:SI (xor:SI (match_dup 5) (const_int 0))))] + "if (GET_CODE (operands[0]) == SUBREG) + operands[0] = alter_subreg (operands[0]); + operands[2] = gen_highpart (SImode, operands[0]); + operands[3] = gen_highpart (SImode, operands[1]); + operands[4] = gen_lowpart (SImode, operands[0]); + operands[5] = gen_lowpart (SImode, operands[1]);") + +(define_insn "*one_cmpldi2_sp64" + [(set (match_operand:DI 0 "register_operand" "=r,b") + (not:DI (match_operand:DI 1 "arith_double_operand" "rHI,b")))] + "TARGET_ARCH64" + "@ + xnor\\t%%g0, %1, %0 + fnot1\\t%1, %0" + [(set_attr "type" "unary,fp") + (set_attr "length" "1")]) + +(define_expand "one_cmplsi2" + [(set (match_operand:SI 0 "register_operand" "") + (not:SI (match_operand:SI 1 "arith_operand" "")))] + "" + " +{ + if (TARGET_LIVE_G0 + && GET_CODE (operands[1]) == CONST_INT) + { + rtx zero_reg = gen_reg_rtx (SImode); + + emit_insn (gen_rtx_SET (VOIDmode, zero_reg, const0_rtx)); + emit_insn (gen_rtx_SET (VOIDmode, + operands[0], + gen_rtx_NOT (SImode, + gen_rtx_XOR (SImode, + zero_reg, + operands[1])))); + DONE; + } +}") + +(define_insn "*one_cmplsi2_not_liveg0" + [(set (match_operand:SI 0 "register_operand" "=r,d") + (not:SI (match_operand:SI 1 "arith_operand" "rI,d")))] + "! TARGET_LIVE_G0" + "@ + xnor\\t%%g0, %1, %0 + fnot1s\\t%1, %0" + [(set_attr "type" "unary,fp") + (set_attr "length" "1,1")]) + +(define_insn "*one_cmplsi2_liveg0" + [(set (match_operand:SI 0 "register_operand" "=r,d") + (not:SI (match_operand:SI 1 "arith_operand" "r,d")))] + "TARGET_LIVE_G0" + "@ + xnor\\t%1, 0, %0 + fnot1s\\t%1, %0" + [(set_attr "type" "unary,fp") + (set_attr "length" "1,1")]) + +(define_insn "*cmp_cc_not" + [(set (reg:CC 100) + (compare:CC (not:SI (match_operand:SI 0 "arith_operand" "rI")) + (const_int 0)))] + "! TARGET_LIVE_G0" + "xnorcc\\t%%g0, %0, %%g0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_ccx_not" + [(set (reg:CCX 100) + (compare:CCX (not:DI (match_operand:DI 0 "arith_double_operand" "rHI")) + (const_int 0)))] + "TARGET_ARCH64" + "xnorcc\\t%%g0, %0, %%g0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_cc_set_not" + [(set (reg:CC 100) + (compare:CC (not:SI (match_operand:SI 1 "arith_operand" "rI")) + (const_int 0))) + (set (match_operand:SI 0 "register_operand" "=r") + (not:SI (match_dup 1)))] + "! TARGET_LIVE_G0" + "xnorcc\\t%%g0, %1, %0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_ccx_set_not" + [(set (reg:CCX 100) + (compare:CCX (not:DI (match_operand:DI 1 "arith_double_operand" "rHI")) + (const_int 0))) + (set (match_operand:DI 0 "register_operand" "=r") + (not:DI (match_dup 1)))] + "TARGET_ARCH64" + "xnorcc\\t%%g0, %1, %0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +;; Floating point arithmetic instructions. + +(define_insn "addtf3" + [(set (match_operand:TF 0 "register_operand" "=e") + (plus:TF (match_operand:TF 1 "register_operand" "e") + (match_operand:TF 2 "register_operand" "e")))] + "TARGET_FPU && TARGET_HARD_QUAD" + "faddq\\t%1, %2, %0" + [(set_attr "type" "fp") + (set_attr "length" "1")]) + +(define_insn "adddf3" + [(set (match_operand:DF 0 "register_operand" "=e") + (plus:DF (match_operand:DF 1 "register_operand" "e") + (match_operand:DF 2 "register_operand" "e")))] + "TARGET_FPU" + "faddd\\t%1, %2, %0" + [(set_attr "type" "fp") + (set_attr "length" "1")]) + +(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_FPU" + "fadds\\t%1, %2, %0" + [(set_attr "type" "fp") + (set_attr "length" "1")]) + +(define_insn "subtf3" + [(set (match_operand:TF 0 "register_operand" "=e") + (minus:TF (match_operand:TF 1 "register_operand" "e") + (match_operand:TF 2 "register_operand" "e")))] + "TARGET_FPU && TARGET_HARD_QUAD" + "fsubq\\t%1, %2, %0" + [(set_attr "type" "fp") + (set_attr "length" "1")]) + +(define_insn "subdf3" + [(set (match_operand:DF 0 "register_operand" "=e") + (minus:DF (match_operand:DF 1 "register_operand" "e") + (match_operand:DF 2 "register_operand" "e")))] + "TARGET_FPU" + "fsubd\\t%1, %2, %0" + [(set_attr "type" "fp") + (set_attr "length" "1")]) + +(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_FPU" + "fsubs\\t%1, %2, %0" + [(set_attr "type" "fp") + (set_attr "length" "1")]) + +(define_insn "multf3" + [(set (match_operand:TF 0 "register_operand" "=e") + (mult:TF (match_operand:TF 1 "register_operand" "e") + (match_operand:TF 2 "register_operand" "e")))] + "TARGET_FPU && TARGET_HARD_QUAD" + "fmulq\\t%1, %2, %0" + [(set_attr "type" "fpmul") + (set_attr "length" "1")]) + +(define_insn "muldf3" + [(set (match_operand:DF 0 "register_operand" "=e") + (mult:DF (match_operand:DF 1 "register_operand" "e") + (match_operand:DF 2 "register_operand" "e")))] + "TARGET_FPU" + "fmuld\\t%1, %2, %0" + [(set_attr "type" "fpmul") + (set_attr "length" "1")]) + +(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_FPU" + "fmuls\\t%1, %2, %0" + [(set_attr "type" "fpmul") + (set_attr "length" "1")]) + +(define_insn "*muldf3_extend" + [(set (match_operand:DF 0 "register_operand" "=e") + (mult:DF (float_extend:DF (match_operand:SF 1 "register_operand" "f")) + (float_extend:DF (match_operand:SF 2 "register_operand" "f"))))] + "(TARGET_V8 || TARGET_V9) && TARGET_FPU" + "fsmuld\\t%1, %2, %0" + [(set_attr "type" "fpmul") + (set_attr "length" "1")]) + +(define_insn "*multf3_extend" + [(set (match_operand:TF 0 "register_operand" "=e") + (mult:TF (float_extend:TF (match_operand:DF 1 "register_operand" "e")) + (float_extend:TF (match_operand:DF 2 "register_operand" "e"))))] + "(TARGET_V8 || TARGET_V9) && TARGET_FPU && TARGET_HARD_QUAD" + "fdmulq\\t%1, %2, %0" + [(set_attr "type" "fpmul") + (set_attr "length" "1")]) + +;; don't have timing for quad-prec. divide. +(define_insn "divtf3" + [(set (match_operand:TF 0 "register_operand" "=e") + (div:TF (match_operand:TF 1 "register_operand" "e") + (match_operand:TF 2 "register_operand" "e")))] + "TARGET_FPU && TARGET_HARD_QUAD" + "fdivq\\t%1, %2, %0" + [(set_attr "type" "fpdivd") + (set_attr "length" "1")]) + +(define_insn "divdf3" + [(set (match_operand:DF 0 "register_operand" "=e") + (div:DF (match_operand:DF 1 "register_operand" "e") + (match_operand:DF 2 "register_operand" "e")))] + "TARGET_FPU" + "fdivd\\t%1, %2, %0" + [(set_attr "type" "fpdivd") + (set_attr "length" "1")]) + +(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_FPU" + "fdivs\\t%1, %2, %0" + [(set_attr "type" "fpdivs") + (set_attr "length" "1")]) + +(define_expand "negtf2" + [(set (match_operand:TF 0 "register_operand" "=e,e") + (neg:TF (match_operand:TF 1 "register_operand" "0,e")))] + "TARGET_FPU" + "") + +(define_insn "*negtf2_notv9" + [(set (match_operand:TF 0 "register_operand" "=e,e") + (neg:TF (match_operand:TF 1 "register_operand" "0,e")))] + ; We don't use quad float insns here so we don't need TARGET_HARD_QUAD. + "TARGET_FPU + && ! TARGET_V9" + "@ + fnegs\\t%0, %0 + #" + [(set_attr "type" "fpmove") + (set_attr "length" "1,2")]) + +(define_split + [(set (match_operand:TF 0 "register_operand" "") + (neg:TF (match_operand:TF 1 "register_operand" "")))] + "TARGET_FPU + && ! TARGET_V9 + && reload_completed + && sparc_absnegfloat_split_legitimate (operands[0], operands[1])" + [(set (match_dup 2) (neg:SF (match_dup 3))) + (set (match_dup 4) (match_dup 5)) + (set (match_dup 6) (match_dup 7))] + "if (GET_CODE (operands[0]) == SUBREG) + operands[0] = alter_subreg (operands[0]); + if (GET_CODE (operands[1]) == SUBREG) + operands[1] = alter_subreg (operands[1]); + operands[2] = gen_rtx_raw_REG (SFmode, REGNO (operands[0])); + operands[3] = gen_rtx_raw_REG (SFmode, REGNO (operands[1])); + operands[4] = gen_rtx_raw_REG (SFmode, REGNO (operands[0]) + 1); + operands[5] = gen_rtx_raw_REG (SFmode, REGNO (operands[1]) + 1); + operands[6] = gen_rtx_raw_REG (DFmode, REGNO (operands[0]) + 2); + operands[7] = gen_rtx_raw_REG (DFmode, REGNO (operands[1]) + 2);") + +(define_insn "*negtf2_v9" + [(set (match_operand:TF 0 "register_operand" "=e,e") + (neg:TF (match_operand:TF 1 "register_operand" "0,e")))] + ; We don't use quad float insns here so we don't need TARGET_HARD_QUAD. + "TARGET_FPU && TARGET_V9" + "@ + fnegd\\t%0, %0 + #" + [(set_attr "type" "fpmove") + (set_attr "length" "1,2")]) + +(define_split + [(set (match_operand:TF 0 "register_operand" "") + (neg:TF (match_operand:TF 1 "register_operand" "")))] + "TARGET_FPU + && TARGET_V9 + && reload_completed + && sparc_absnegfloat_split_legitimate (operands[0], operands[1])" + [(set (match_dup 2) (neg:DF (match_dup 3))) + (set (match_dup 4) (match_dup 5))] + "if (GET_CODE (operands[0]) == SUBREG) + operands[0] = alter_subreg (operands[0]); + if (GET_CODE (operands[1]) == SUBREG) + operands[1] = alter_subreg (operands[1]); + operands[2] = gen_rtx_raw_REG (DFmode, REGNO (operands[0])); + operands[3] = gen_rtx_raw_REG (DFmode, REGNO (operands[1])); + operands[4] = gen_rtx_raw_REG (DFmode, REGNO (operands[0]) + 2); + operands[5] = gen_rtx_raw_REG (DFmode, REGNO (operands[1]) + 2);") + +(define_expand "negdf2" + [(set (match_operand:DF 0 "register_operand" "") + (neg:DF (match_operand:DF 1 "register_operand" "")))] + "TARGET_FPU" + "") + +(define_insn "*negdf2_notv9" + [(set (match_operand:DF 0 "register_operand" "=e,e") + (neg:DF (match_operand:DF 1 "register_operand" "0,e")))] + "TARGET_FPU && ! TARGET_V9" + "@ + fnegs\\t%0, %0 + #" + [(set_attr "type" "fpmove") + (set_attr "length" "1,2")]) + +(define_split + [(set (match_operand:DF 0 "register_operand" "") + (neg:DF (match_operand:DF 1 "register_operand" "")))] + "TARGET_FPU + && ! TARGET_V9 + && reload_completed + && sparc_absnegfloat_split_legitimate (operands[0], operands[1])" + [(set (match_dup 2) (neg:SF (match_dup 3))) + (set (match_dup 4) (match_dup 5))] + "if (GET_CODE (operands[0]) == SUBREG) + operands[0] = alter_subreg (operands[0]); + if (GET_CODE (operands[1]) == SUBREG) + operands[1] = alter_subreg (operands[1]); + operands[2] = gen_rtx_raw_REG (SFmode, REGNO (operands[0])); + operands[3] = gen_rtx_raw_REG (SFmode, REGNO (operands[1])); + operands[4] = gen_rtx_raw_REG (SFmode, REGNO (operands[0]) + 1); + operands[5] = gen_rtx_raw_REG (SFmode, REGNO (operands[1]) + 1);") + +(define_insn "*negdf2_v9" + [(set (match_operand:DF 0 "register_operand" "=e") + (neg:DF (match_operand:DF 1 "register_operand" "e")))] + "TARGET_FPU && TARGET_V9" + "fnegd\\t%1, %0" + [(set_attr "type" "fpmove") + (set_attr "length" "1")]) + +(define_insn "negsf2" + [(set (match_operand:SF 0 "register_operand" "=f") + (neg:SF (match_operand:SF 1 "register_operand" "f")))] + "TARGET_FPU" + "fnegs\\t%1, %0" + [(set_attr "type" "fpmove") + (set_attr "length" "1")]) + +(define_insn "abstf2" + [(set (match_operand:TF 0 "register_operand" "") + (abs:TF (match_operand:TF 1 "register_operand" "")))] + "TARGET_FPU" + "") + +(define_insn "*abstf2_notv9" + [(set (match_operand:TF 0 "register_operand" "=e,e") + (abs:TF (match_operand:TF 1 "register_operand" "0,e")))] + ; We don't use quad float insns here so we don't need TARGET_HARD_QUAD. + "TARGET_FPU && ! TARGET_V9" + "@ + fabss\\t%0, %0 + #" + [(set_attr "type" "fpmove") + (set_attr "length" "1,2")]) + +(define_split + [(set (match_operand:TF 0 "register_operand" "=e,e") + (abs:TF (match_operand:TF 1 "register_operand" "0,e")))] + "TARGET_FPU + && ! TARGET_V9 + && reload_completed + && sparc_absnegfloat_split_legitimate (operands[0], operands[1])" + [(set (match_dup 2) (abs:SF (match_dup 3))) + (set (match_dup 4) (match_dup 5)) + (set (match_dup 6) (match_dup 7))] + "if (GET_CODE (operands[0]) == SUBREG) + operands[0] = alter_subreg (operands[0]); + if (GET_CODE (operands[1]) == SUBREG) + operands[1] = alter_subreg (operands[1]); + operands[2] = gen_rtx_raw_REG (SFmode, REGNO (operands[0])); + operands[3] = gen_rtx_raw_REG (SFmode, REGNO (operands[1])); + operands[4] = gen_rtx_raw_REG (SFmode, REGNO (operands[0]) + 1); + operands[5] = gen_rtx_raw_REG (SFmode, REGNO (operands[1]) + 1); + operands[6] = gen_rtx_raw_REG (SFmode, REGNO (operands[0]) + 2); + operands[7] = gen_rtx_raw_REG (SFmode, REGNO (operands[1]) + 2);") + +(define_insn "*abstf2_v9" + [(set (match_operand:TF 0 "register_operand" "=e,e") + (abs:TF (match_operand:TF 1 "register_operand" "0,e")))] + ; We don't use quad float insns here so we don't need TARGET_HARD_QUAD. + "TARGET_FPU && TARGET_V9" + "@ + fabsd\\t%0, %0 + #" + [(set_attr "type" "fpmove") + (set_attr "length" "1,2")]) + +(define_split + [(set (match_operand:TF 0 "register_operand" "=e,e") + (abs:TF (match_operand:TF 1 "register_operand" "0,e")))] + "TARGET_FPU + && TARGET_V9 + && reload_completed + && sparc_absnegfloat_split_legitimate (operands[0], operands[1])" + [(set (match_dup 2) (abs:DF (match_dup 3))) + (set (match_dup 4) (match_dup 5))] + "if (GET_CODE (operands[0]) == SUBREG) + operands[0] = alter_subreg (operands[0]); + if (GET_CODE (operands[1]) == SUBREG) + operands[1] = alter_subreg (operands[1]); + operands[2] = gen_rtx_raw_REG (DFmode, REGNO (operands[0])); + operands[3] = gen_rtx_raw_REG (DFmode, REGNO (operands[1])); + operands[4] = gen_rtx_raw_REG (DFmode, REGNO (operands[0]) + 2); + operands[5] = gen_rtx_raw_REG (DFmode, REGNO (operands[1]) + 2);") + +(define_expand "absdf2" + [(set (match_operand:DF 0 "register_operand" "") + (abs:DF (match_operand:DF 1 "register_operand" "")))] + "TARGET_FPU" + "") + +(define_insn "*absdf2_notv9" + [(set (match_operand:DF 0 "register_operand" "=e,e") + (abs:DF (match_operand:DF 1 "register_operand" "0,e")))] + "TARGET_FPU && ! TARGET_V9" + "@ + fabss\\t%0, %0 + #" + [(set_attr "type" "fpmove") + (set_attr "length" "1,2")]) + +(define_split + [(set (match_operand:DF 0 "register_operand" "=e,e") + (abs:DF (match_operand:DF 1 "register_operand" "0,e")))] + "TARGET_FPU + && ! TARGET_V9 + && reload_completed + && sparc_absnegfloat_split_legitimate (operands[0], operands[1])" + [(set (match_dup 2) (abs:SF (match_dup 3))) + (set (match_dup 4) (match_dup 5))] + "if (GET_CODE (operands[0]) == SUBREG) + operands[0] = alter_subreg (operands[0]); + if (GET_CODE (operands[1]) == SUBREG) + operands[1] = alter_subreg (operands[1]); + operands[2] = gen_rtx_raw_REG (SFmode, REGNO (operands[0])); + operands[3] = gen_rtx_raw_REG (SFmode, REGNO (operands[1])); + operands[4] = gen_rtx_raw_REG (SFmode, REGNO (operands[0]) + 1); + operands[5] = gen_rtx_raw_REG (SFmode, REGNO (operands[1]) + 1);") + +(define_insn "*absdf2_v9" + [(set (match_operand:DF 0 "register_operand" "=e") + (abs:DF (match_operand:DF 1 "register_operand" "e")))] + "TARGET_FPU && TARGET_V9" + "fabsd\\t%0, %0" + [(set_attr "type" "fpmove") + (set_attr "length" "1")]) + +(define_insn "abssf2" + [(set (match_operand:SF 0 "register_operand" "=f") + (abs:SF (match_operand:SF 1 "register_operand" "f")))] + "TARGET_FPU" + "fabss\\t%1, %0" + [(set_attr "type" "fpmove") + (set_attr "length" "1")]) + +(define_insn "sqrttf2" + [(set (match_operand:TF 0 "register_operand" "=e") + (sqrt:TF (match_operand:TF 1 "register_operand" "e")))] + "TARGET_FPU && TARGET_HARD_QUAD" + "fsqrtq\\t%1, %0" + [(set_attr "type" "fpsqrt") + (set_attr "length" "1")]) + +(define_insn "sqrtdf2" + [(set (match_operand:DF 0 "register_operand" "=e") + (sqrt:DF (match_operand:DF 1 "register_operand" "e")))] + "TARGET_FPU" + "fsqrtd\\t%1, %0" + [(set_attr "type" "fpsqrt") + (set_attr "length" "1")]) + +(define_insn "sqrtsf2" + [(set (match_operand:SF 0 "register_operand" "=f") + (sqrt:SF (match_operand:SF 1 "register_operand" "f")))] + "TARGET_FPU" + "fsqrts\\t%1, %0" + [(set_attr "type" "fpsqrt") + (set_attr "length" "1")]) + +;;- arithmetic shift instructions + +(define_insn "ashlsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (ashift:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "arith_operand" "rI")))] + "" + "* +{ + if (GET_CODE (operands[2]) == CONST_INT + && (unsigned HOST_WIDE_INT) INTVAL (operands[2]) > 31) + operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f); + + return \"sll\\t%1, %2, %0\"; +}" + [(set_attr "type" "shift") + (set_attr "length" "1")]) + +;; We special case multiplication by two, as add can be done +;; in both ALUs, while shift only in IEU0 on UltraSPARC. +(define_insn "*ashlsi3_const1" + [(set (match_operand:SI 0 "register_operand" "=r") + (ashift:SI (match_operand:SI 1 "register_operand" "r") + (const_int 1)))] + "" + "add\\t%1, %1, %0" + [(set_attr "type" "binary") + (set_attr "length" "1")]) + +(define_expand "ashldi3" + [(set (match_operand:DI 0 "register_operand" "=r") + (ashift:DI (match_operand:DI 1 "register_operand" "r") + (match_operand:SI 2 "arith_operand" "rI")))] + "TARGET_ARCH64 || TARGET_V8PLUS" + " +{ + if (! TARGET_ARCH64) + { + if (GET_CODE (operands[2]) == CONST_INT) + FAIL; + emit_insn (gen_ashldi3_v8plus (operands[0], operands[1], operands[2])); + DONE; + } +}") + +;; We special case multiplication by two, as add can be done +;; in both ALUs, while shift only in IEU0 on UltraSPARC. +(define_insn "*ashldi3_const1" + [(set (match_operand:DI 0 "register_operand" "=r") + (ashift:DI (match_operand:DI 1 "register_operand" "r") + (const_int 1)))] + "TARGET_ARCH64" + "add\\t%1, %1, %0" + [(set_attr "type" "binary") + (set_attr "length" "1")]) + +(define_insn "*ashldi3_sp64" + [(set (match_operand:DI 0 "register_operand" "=r") + (ashift:DI (match_operand:DI 1 "register_operand" "r") + (match_operand:SI 2 "arith_operand" "rI")))] + "TARGET_ARCH64" + "* +{ + if (GET_CODE (operands[2]) == CONST_INT + && (unsigned HOST_WIDE_INT) INTVAL (operands[2]) > 63) + operands[2] = GEN_INT (INTVAL (operands[2]) & 0x3f); + + return \"sllx\\t%1, %2, %0\"; +}" + [(set_attr "type" "shift") + (set_attr "length" "1")]) + +;; XXX UGH! +(define_insn "ashldi3_v8plus" + [(set (match_operand:DI 0 "register_operand" "=&h,&h,r") + (ashift:DI (match_operand:DI 1 "arith_operand" "rI,0,rI") + (match_operand:SI 2 "arith_operand" "rI,rI,rI"))) + (clobber (match_scratch:SI 3 "=X,X,&h"))] + "TARGET_V8PLUS" + "*return sparc_v8plus_shift (operands, insn, \"sllx\");" + [(set_attr "length" "5,5,6")]) + +;; Optimize (1LL<<x)-1 +;; XXX this also needs to be fixed to handle equal subregs +;; XXX first before we could re-enable it. +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=h") + (plus:DI (ashift:DI (const_int 1) + (match_operand:SI 2 "arith_operand" "rI")) + (const_int -1)))] + "0 && TARGET_V8PLUS" + "* +{ + if (GET_CODE (operands[2]) == REG && REGNO (operands[2]) == REGNO (operands[0])) + return \"mov 1,%L0\;sllx %L0,%2,%L0\;sub %L0,1,%L0\;srlx %L0,32,%H0\"; + return \"mov 1,%H0\;sllx %H0,%2,%L0\;sub %L0,1,%L0\;srlx %L0,32,%H0\"; +}" + [(set_attr "length" "4")]) + +(define_insn "*cmp_cc_ashift_1" + [(set (reg:CC_NOOV 100) + (compare:CC_NOOV (ashift:SI (match_operand:SI 0 "register_operand" "r") + (const_int 1)) + (const_int 0)))] + "! TARGET_LIVE_G0" + "addcc\\t%0, %0, %%g0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "*cmp_cc_set_ashift_1" + [(set (reg:CC_NOOV 100) + (compare:CC_NOOV (ashift:SI (match_operand:SI 1 "register_operand" "r") + (const_int 1)) + (const_int 0))) + (set (match_operand:SI 0 "register_operand" "=r") + (ashift:SI (match_dup 1) (const_int 1)))] + "" + "addcc\\t%1, %1, %0" + [(set_attr "type" "compare") + (set_attr "length" "1")]) + +(define_insn "ashrsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (ashiftrt:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "arith_operand" "rI")))] + "" + "* +{ + if (GET_CODE (operands[2]) == CONST_INT + && (unsigned HOST_WIDE_INT) INTVAL (operands[2]) > 31) + operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f); + + return \"sra\\t%1, %2, %0\"; +}" + [(set_attr "type" "shift") + (set_attr "length" "1")]) + +(define_insn "*ashrsi3_extend" + [(set (match_operand:DI 0 "register_operand" "") + (sign_extend:DI (ashiftrt:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "arith_operand" "r"))))] + "TARGET_ARCH64" + "sra\\t%1, %2, %0" + [(set_attr "type" "shift") + (set_attr "length" "1")]) + +;; This handles the case as above, but with constant shift instead of +;; register. Combiner "simplifies" it for us a little bit though. +(define_insn "*ashrsi3_extend2" + [(set (match_operand:DI 0 "register_operand" "") + (ashiftrt:DI (ashift:DI (subreg:DI (match_operand:SI 1 "register_operand" "r") 0) + (const_int 32)) + (match_operand:SI 2 "small_int_or_double" "n")))] + "TARGET_ARCH64 + && ((GET_CODE (operands[2]) == CONST_INT + && INTVAL (operands[2]) >= 32 && INTVAL (operands[2]) < 64) + || (GET_CODE (operands[2]) == CONST_DOUBLE + && !CONST_DOUBLE_HIGH (operands[2]) + && CONST_DOUBLE_LOW (operands[2]) >= 32 + && CONST_DOUBLE_LOW (operands[2]) < 64))" + "* +{ + operands[2] = GEN_INT (INTVAL (operands[2]) - 32); + + return \"sra\\t%1, %2, %0\"; +}" + [(set_attr "type" "shift") + (set_attr "length" "1")]) + +(define_expand "ashrdi3" + [(set (match_operand:DI 0 "register_operand" "=r") + (ashiftrt:DI (match_operand:DI 1 "register_operand" "r") + (match_operand:SI 2 "arith_operand" "rI")))] + "TARGET_ARCH64 || TARGET_V8PLUS" + " +{ + if (! TARGET_ARCH64) + { + if (GET_CODE (operands[2]) == CONST_INT) + FAIL; /* prefer generic code in this case */ + emit_insn (gen_ashrdi3_v8plus (operands[0], operands[1], operands[2])); + DONE; + } +}") + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=r") + (ashiftrt:DI (match_operand:DI 1 "register_operand" "r") + (match_operand:SI 2 "arith_operand" "rI")))] + "TARGET_ARCH64" + "* +{ + if (GET_CODE (operands[2]) == CONST_INT + && (unsigned HOST_WIDE_INT) INTVAL (operands[2]) > 63) + operands[2] = GEN_INT (INTVAL (operands[2]) & 0x3f); + + return \"srax\\t%1, %2, %0\"; +}" + [(set_attr "type" "shift") + (set_attr "length" "1")]) + +;; XXX +(define_insn "ashrdi3_v8plus" + [(set (match_operand:DI 0 "register_operand" "=&h,&h,r") + (ashiftrt:DI (match_operand:DI 1 "arith_operand" "rI,0,rI") + (match_operand:SI 2 "arith_operand" "rI,rI,rI"))) + (clobber (match_scratch:SI 3 "=X,X,&h"))] + "TARGET_V8PLUS" + "*return sparc_v8plus_shift (operands, insn, \"srax\");" + [(set_attr "length" "5,5,6")]) + +(define_insn "lshrsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (lshiftrt:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "arith_operand" "rI")))] + "" + "* +{ + if (GET_CODE (operands[2]) == CONST_INT + && (unsigned HOST_WIDE_INT) INTVAL (operands[2]) > 31) + operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f); + + return \"srl\\t%1, %2, %0\"; +}" + [(set_attr "type" "shift") + (set_attr "length" "1")]) + +;; This handles the case where +;; (zero_extend:DI (lshiftrt:SI (match_operand:SI) (match_operand:SI))), +;; but combiner "simplifies" it for us. +(define_insn "*lshrsi3_extend" + [(set (match_operand:DI 0 "register_operand" "") + (and:DI (subreg:DI (lshiftrt:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "arith_operand" "r")) 0) + (match_operand 3 "" "")))] + "TARGET_ARCH64 + && ((GET_CODE (operands[3]) == CONST_DOUBLE + && CONST_DOUBLE_HIGH (operands[3]) == 0 + && CONST_DOUBLE_LOW (operands[3]) == 0xffffffff) +#if HOST_BITS_PER_WIDE_INT >= 64 + || (GET_CODE (operands[3]) == CONST_INT + && (unsigned HOST_WIDE_INT) INTVAL (operands[3]) == 0xffffffff) +#endif + )" + "srl\\t%1, %2, %0" + [(set_attr "type" "shift") + (set_attr "length" "1")]) + +;; This handles the case where +;; (lshiftrt:DI (zero_extend:DI (match_operand:SI)) (const_int >=0 < 32)) +;; but combiner "simplifies" it for us. +(define_insn "*lshrsi3_extend2" + [(set (match_operand:DI 0 "register_operand" "") + (zero_extract:DI (subreg:DI (match_operand:SI 1 "register_operand" "r") 0) + (match_operand 2 "small_int_or_double" "n") + (const_int 32)))] + "TARGET_ARCH64 + && ((GET_CODE (operands[2]) == CONST_INT + && (unsigned HOST_WIDE_INT) INTVAL (operands[2]) < 32) + || (GET_CODE (operands[2]) == CONST_DOUBLE + && CONST_DOUBLE_HIGH (operands[2]) == 0 + && (unsigned HOST_WIDE_INT) CONST_DOUBLE_LOW (operands[2]) < 32))" + "* +{ + operands[2] = GEN_INT (32 - INTVAL (operands[2])); + + return \"srl\\t%1, %2, %0\"; +}" + [(set_attr "type" "shift") + (set_attr "length" "1")]) + +(define_expand "lshrdi3" + [(set (match_operand:DI 0 "register_operand" "=r") + (lshiftrt:DI (match_operand:DI 1 "register_operand" "r") + (match_operand:SI 2 "arith_operand" "rI")))] + "TARGET_ARCH64 || TARGET_V8PLUS" + " +{ + if (! TARGET_ARCH64) + { + if (GET_CODE (operands[2]) == CONST_INT) + FAIL; + emit_insn (gen_lshrdi3_v8plus (operands[0], operands[1], operands[2])); + DONE; + } +}") + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=r") + (lshiftrt:DI (match_operand:DI 1 "register_operand" "r") + (match_operand:SI 2 "arith_operand" "rI")))] + "TARGET_ARCH64" + "* +{ + if (GET_CODE (operands[2]) == CONST_INT + && (unsigned HOST_WIDE_INT) INTVAL (operands[2]) > 63) + operands[2] = GEN_INT (INTVAL (operands[2]) & 0x3f); + + return \"srlx\\t%1, %2, %0\"; +}" + [(set_attr "type" "shift") + (set_attr "length" "1")]) + +;; XXX +(define_insn "lshrdi3_v8plus" + [(set (match_operand:DI 0 "register_operand" "=&h,&h,r") + (lshiftrt:DI (match_operand:DI 1 "arith_operand" "rI,0,rI") + (match_operand:SI 2 "arith_operand" "rI,rI,rI"))) + (clobber (match_scratch:SI 3 "=X,X,&h"))] + "TARGET_V8PLUS" + "*return sparc_v8plus_shift (operands, insn, \"srlx\");" + [(set_attr "length" "5,5,6")]) + +;; Unconditional and other jump instructions +;; On the Sparc, by setting the annul bit on an unconditional branch, the +;; following insn is never executed. This saves us a nop. Dbx does not +;; handle such branches though, so we only use them when optimizing. +(define_insn "jump" + [(set (pc) (label_ref (match_operand 0 "" "")))] + "" + "* +{ + /* TurboSparc is reported to have problems with + with + foo: b,a foo + i.e. an empty loop with the annul bit set. The workaround is to use + foo: b foo; nop + instead. */ + + if (! TARGET_V9 && flag_delayed_branch + && (insn_addresses[INSN_UID (operands[0])] + == insn_addresses[INSN_UID (insn)])) + return \"b\\t%l0%#\"; + else + return TARGET_V9 ? \"ba,pt%*\\t%%xcc, %l0%(\" : \"b%*\\t%l0%(\"; +}" + [(set_attr "type" "uncond_branch")]) + +(define_expand "tablejump" + [(parallel [(set (pc) (match_operand 0 "register_operand" "r")) + (use (label_ref (match_operand 1 "" "")))])] + "" + " +{ + if (GET_MODE (operands[0]) != CASE_VECTOR_MODE) + abort (); + + /* In pic mode, our address differences are against the base of the + table. Add that base value back in; CSE ought to be able to combine + the two address loads. */ + if (flag_pic) + { + rtx tmp, tmp2; + tmp = gen_rtx_LABEL_REF (Pmode, operands[1]); + tmp2 = operands[0]; + if (CASE_VECTOR_MODE != Pmode) + tmp2 = gen_rtx_SIGN_EXTEND (Pmode, tmp2); + tmp = gen_rtx_PLUS (Pmode, tmp2, tmp); + operands[0] = memory_address (Pmode, tmp); + } +}") + +(define_insn "*tablejump_sp32" + [(set (pc) (match_operand:SI 0 "address_operand" "p")) + (use (label_ref (match_operand 1 "" "")))] + "! TARGET_PTR64" + "jmp\\t%a0%#" + [(set_attr "type" "uncond_branch")]) + +(define_insn "*tablejump_sp64" + [(set (pc) (match_operand:DI 0 "address_operand" "p")) + (use (label_ref (match_operand 1 "" "")))] + "TARGET_PTR64" + "jmp\\t%a0%#" + [(set_attr "type" "uncond_branch")]) + +;; This pattern recognizes the "instruction" that appears in +;; a function call that wants a structure value, +;; to inform the called function if compiled with Sun CC. +;(define_insn "*unimp_insn" +; [(match_operand:SI 0 "immediate_operand" "")] +; "GET_CODE (operands[0]) == CONST_INT && INTVAL (operands[0]) > 0" +; "unimp\\t%0" +; [(set_attr "type" "marker")]) + +;;- jump to subroutine +(define_expand "call" + ;; Note that this expression is not used for generating RTL. + ;; All the RTL is generated explicitly below. + [(call (match_operand 0 "call_operand" "") + (match_operand 3 "" "i"))] + ;; operands[2] is next_arg_register + ;; operands[3] is struct_value_size_rtx. + "" + " +{ + rtx fn_rtx, nregs_rtx; + + if (GET_MODE (operands[0]) != FUNCTION_MODE) + abort (); + + if (GET_CODE (XEXP (operands[0], 0)) == LABEL_REF) + { + /* This is really a PIC sequence. We want to represent + it as a funny jump so its delay slots can be filled. + + ??? But if this really *is* a CALL, will not it clobber the + call-clobbered registers? We lose this if it is a JUMP_INSN. + Why cannot we have delay slots filled if it were a CALL? */ + + if (! TARGET_ARCH64 && INTVAL (operands[3]) != 0) + emit_jump_insn + (gen_rtx_PARALLEL (VOIDmode, + gen_rtvec (3, + gen_rtx_SET (VOIDmode, pc_rtx, + XEXP (operands[0], 0)), + GEN_INT (INTVAL (operands[3]) & 0xfff), + gen_rtx_CLOBBER (VOIDmode, + gen_rtx_REG (Pmode, 15))))); + else + emit_jump_insn + (gen_rtx_PARALLEL (VOIDmode, + gen_rtvec (2, + gen_rtx_SET (VOIDmode, pc_rtx, + XEXP (operands[0], 0)), + gen_rtx_CLOBBER (VOIDmode, + gen_rtx_REG (Pmode, 15))))); + goto finish_call; + } + + fn_rtx = operands[0]; + + /* Count the number of parameter registers being used by this call. + if that argument is NULL, it means we are using them all, which + means 6 on the sparc. */ +#if 0 + if (operands[2]) + nregs_rtx = GEN_INT (REGNO (operands[2]) - 8); + else + nregs_rtx = GEN_INT (6); +#else + nregs_rtx = const0_rtx; +#endif + + if (! TARGET_ARCH64 && INTVAL (operands[3]) != 0) + emit_call_insn + (gen_rtx_PARALLEL (VOIDmode, + gen_rtvec (3, gen_rtx_CALL (VOIDmode, fn_rtx, nregs_rtx), + GEN_INT (INTVAL (operands[3]) & 0xfff), + gen_rtx_CLOBBER (VOIDmode, + gen_rtx_REG (Pmode, 15))))); + else + emit_call_insn + (gen_rtx_PARALLEL (VOIDmode, + gen_rtvec (2, gen_rtx_CALL (VOIDmode, fn_rtx, nregs_rtx), + gen_rtx_CLOBBER (VOIDmode, + gen_rtx_REG (Pmode, 15))))); + + finish_call: +#if 0 + /* If this call wants a structure value, + emit an unimp insn to let the called function know about this. */ + if (! TARGET_ARCH64 && INTVAL (operands[3]) > 0) + { + rtx insn = emit_insn (operands[3]); + SCHED_GROUP_P (insn) = 1; + } +#endif + + DONE; +}") + +;; We can't use the same pattern for these two insns, because then registers +;; in the address may not be properly reloaded. + +(define_insn "*call_address_sp32" + [(call (mem:SI (match_operand:SI 0 "address_operand" "p")) + (match_operand 1 "" "")) + (clobber (reg:SI 15))] + ;;- Do not use operand 1 for most machines. + "! TARGET_PTR64" + "call\\t%a0, %1%#" + [(set_attr "type" "call")]) + +(define_insn "*call_symbolic_sp32" + [(call (mem:SI (match_operand:SI 0 "symbolic_operand" "s")) + (match_operand 1 "" "")) + (clobber (reg:SI 15))] + ;;- Do not use operand 1 for most machines. + "! TARGET_PTR64" + "call\\t%a0, %1%#" + [(set_attr "type" "call")]) + +(define_insn "*call_address_sp64" + [(call (mem:SI (match_operand:DI 0 "address_operand" "p")) + (match_operand 1 "" "")) + (clobber (reg:DI 15))] + ;;- Do not use operand 1 for most machines. + "TARGET_PTR64" + "call\\t%a0, %1%#" + [(set_attr "type" "call")]) + +(define_insn "*call_symbolic_sp64" + [(call (mem:SI (match_operand:DI 0 "symbolic_operand" "s")) + (match_operand 1 "" "")) + (clobber (reg:DI 15))] + ;;- Do not use operand 1 for most machines. + "TARGET_PTR64" + "call\\t%a0, %1%#" + [(set_attr "type" "call")]) + +;; This is a call that wants a structure value. +;; There is no such critter for v9 (??? we may need one anyway). +(define_insn "*call_address_struct_value_sp32" + [(call (mem:SI (match_operand:SI 0 "address_operand" "p")) + (match_operand 1 "" "")) + (match_operand 2 "immediate_operand" "") + (clobber (reg:SI 15))] + ;;- Do not use operand 1 for most machines. + "! TARGET_ARCH64 && GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) >= 0" + "call\\t%a0, %1\\n\\tnop\\n\\tunimp\\t%2" + [(set_attr "type" "call_no_delay_slot")]) + +;; This is a call that wants a structure value. +;; There is no such critter for v9 (??? we may need one anyway). +(define_insn "*call_symbolic_struct_value_sp32" + [(call (mem:SI (match_operand:SI 0 "symbolic_operand" "s")) + (match_operand 1 "" "")) + (match_operand 2 "immediate_operand" "") + (clobber (reg:SI 15))] + ;;- Do not use operand 1 for most machines. + "! TARGET_ARCH64 && GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) >= 0" + "call\\t%a0, %1\\n\\tnop\\n\\tunimp\\t%2" + [(set_attr "type" "call_no_delay_slot")]) + +;; This is a call that may want a structure value. This is used for +;; untyped_calls. +(define_insn "*call_address_untyped_struct_value_sp32" + [(call (mem:SI (match_operand:SI 0 "address_operand" "p")) + (match_operand 1 "" "")) + (match_operand 2 "immediate_operand" "") + (clobber (reg:SI 15))] + ;;- Do not use operand 1 for most machines. + "! TARGET_ARCH64 && GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0" + "call\\t%a0, %1\\n\\tnop\\n\\tnop" + [(set_attr "type" "call_no_delay_slot")]) + +;; This is a call that wants a structure value. +(define_insn "*call_symbolic_untyped_struct_value_sp32" + [(call (mem:SI (match_operand:SI 0 "symbolic_operand" "s")) + (match_operand 1 "" "")) + (match_operand 2 "immediate_operand" "") + (clobber (reg:SI 15))] + ;;- Do not use operand 1 for most machines. + "! TARGET_ARCH64 && GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0" + "call\\t%a0, %1\\n\\tnop\\n\\tnop" + [(set_attr "type" "call_no_delay_slot")]) + +(define_expand "call_value" + ;; Note that this expression is not used for generating RTL. + ;; All the RTL is generated explicitly below. + [(set (match_operand 0 "register_operand" "=rf") + (call (match_operand:SI 1 "" "") + (match_operand 4 "" "")))] + ;; operand 2 is stack_size_rtx + ;; operand 3 is next_arg_register + "" + " +{ + rtx fn_rtx, nregs_rtx; + rtvec vec; + + if (GET_MODE (operands[1]) != FUNCTION_MODE) + abort (); + + fn_rtx = operands[1]; + +#if 0 + if (operands[3]) + nregs_rtx = GEN_INT (REGNO (operands[3]) - 8); + else + nregs_rtx = GEN_INT (6); +#else + nregs_rtx = const0_rtx; +#endif + + vec = gen_rtvec (2, + gen_rtx_SET (VOIDmode, operands[0], + gen_rtx_CALL (VOIDmode, fn_rtx, nregs_rtx)), + gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, 15))); + + emit_call_insn (gen_rtx_PARALLEL (VOIDmode, vec)); + + DONE; +}") + +(define_insn "*call_value_address_sp32" + [(set (match_operand 0 "" "=rf") + (call (mem:SI (match_operand:SI 1 "address_operand" "p")) + (match_operand 2 "" ""))) + (clobber (reg:SI 15))] + ;;- Do not use operand 2 for most machines. + "! TARGET_PTR64" + "call\\t%a1, %2%#" + [(set_attr "type" "call")]) + +(define_insn "*call_value_symbolic_sp32" + [(set (match_operand 0 "" "=rf") + (call (mem:SI (match_operand:SI 1 "symbolic_operand" "s")) + (match_operand 2 "" ""))) + (clobber (reg:SI 15))] + ;;- Do not use operand 2 for most machines. + "! TARGET_PTR64" + "call\\t%a1, %2%#" + [(set_attr "type" "call")]) + +(define_insn "*call_value_address_sp64" + [(set (match_operand 0 "" "") + (call (mem:SI (match_operand:DI 1 "address_operand" "p")) + (match_operand 2 "" ""))) + (clobber (reg:DI 15))] + ;;- Do not use operand 2 for most machines. + "TARGET_PTR64" + "call\\t%a1, %2%#" + [(set_attr "type" "call")]) + +(define_insn "*call_value_symbolic_sp64" + [(set (match_operand 0 "" "") + (call (mem:SI (match_operand:DI 1 "symbolic_operand" "s")) + (match_operand 2 "" ""))) + (clobber (reg:DI 15))] + ;;- Do not use operand 2 for most machines. + "TARGET_PTR64" + "call\\t%a1, %2%#" + [(set_attr "type" "call")]) + +(define_expand "untyped_call" + [(parallel [(call (match_operand 0 "" "") + (const_int 0)) + (match_operand 1 "" "") + (match_operand 2 "" "")])] + "" + " +{ + int i; + + /* Pass constm1 to indicate that it may expect a structure value, but + we don't know what size it is. */ + emit_call_insn (gen_call (operands[0], const0_rtx, NULL, constm1_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; +}") + +;; UNSPEC_VOLATILE is considered to use and clobber all hard registers and +;; all of memory. This blocks insns from being moved across this point. + +(define_insn "blockage" + [(unspec_volatile [(const_int 0)] 0)] + "" + "") + +;; Prepare to return any type including a structure value. + +(define_expand "untyped_return" + [(match_operand:BLK 0 "memory_operand" "") + (match_operand 1 "" "")] + "" + " +{ + rtx valreg1 = gen_rtx_REG (DImode, 24); + rtx valreg2 = gen_rtx_REG (TARGET_ARCH64 ? TFmode : DFmode, 32); + rtx result = operands[0]; + + if (! TARGET_ARCH64) + { + rtx rtnreg = gen_rtx_REG (SImode, (leaf_function ? 15 : 31)); + rtx value = gen_reg_rtx (SImode); + + /* Fetch the instruction where we will return to and see if it's an unimp + instruction (the most significant 10 bits will be zero). If so, + update the return address to skip the unimp instruction. */ + emit_move_insn (value, + gen_rtx_MEM (SImode, plus_constant (rtnreg, 8))); + emit_insn (gen_lshrsi3 (value, value, GEN_INT (22))); + emit_insn (gen_update_return (rtnreg, value)); + } + + /* Reload the function value registers. */ + emit_move_insn (valreg1, change_address (result, DImode, XEXP (result, 0))); + emit_move_insn (valreg2, + change_address (result, TARGET_ARCH64 ? TFmode : DFmode, + plus_constant (XEXP (result, 0), 8))); + + /* Put USE insns before the return. */ + emit_insn (gen_rtx_USE (VOIDmode, valreg1)); + emit_insn (gen_rtx_USE (VOIDmode, valreg2)); + + /* Construct the return. */ + expand_null_return (); + + DONE; +}") + +;; This is a bit of a hack. We're incrementing a fixed register (%i7), +;; and parts of the compiler don't want to believe that the add is needed. + +(define_insn "update_return" + [(unspec:SI [(match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "register_operand" "r")] 1)] + "! TARGET_ARCH64" + "cmp %1,0\;be,a .+8\;add %0,4,%0" + [(set_attr "type" "multi")]) + +(define_insn "return" + [(return) + (use (reg:SI 31))] + "! TARGET_EPILOGUE" + "* return output_return (operands);" + [(set_attr "type" "return")]) + +(define_peephole + [(set (match_operand:SI 0 "register_operand" "=r") + (match_operand:SI 1 "arith_operand" "rI")) + (parallel [(return) + (use (reg:SI 31))])] + "sparc_return_peephole_ok (operands[0], operands[1])" + "return\\t%%i7+8\\n\\tmov\\t%Y1, %Y0") + +(define_insn "nop" + [(const_int 0)] + "" + "nop" + [(set_attr "type" "ialu") + (set_attr "length" "1")]) + +(define_expand "indirect_jump" + [(set (pc) (match_operand 0 "address_operand" "p"))] + "" + "") + +(define_insn "*branch_sp32" + [(set (pc) (match_operand:SI 0 "address_operand" "p"))] + "! TARGET_PTR64" + "jmp\\t%a0%#" + [(set_attr "type" "uncond_branch")]) + +(define_insn "*branch_sp64" + [(set (pc) (match_operand:DI 0 "address_operand" "p"))] + "TARGET_PTR64" + "jmp\\t%a0%#" + [(set_attr "type" "uncond_branch")]) + +;; ??? Doesn't work with -mflat. +(define_expand "nonlocal_goto" + [(match_operand:SI 0 "general_operand" "") + (match_operand:SI 1 "general_operand" "") + (match_operand:SI 2 "general_operand" "") + (match_operand:SI 3 "" "")] + "" + " +{ + rtx chain = operands[0]; + rtx fp = operands[1]; + rtx stack = operands[2]; + rtx lab = operands[3]; + rtx labreg; + + /* Trap instruction to flush all the register windows. */ + emit_insn (gen_flush_register_windows ()); + + /* Load the fp value for the containing fn into %fp. This is needed + because STACK refers to %fp. Note that virtual register instantiation + fails if the virtual %fp isn't set from a register. */ + if (GET_CODE (fp) != REG) + fp = force_reg (Pmode, fp); + emit_move_insn (virtual_stack_vars_rtx, fp); + + /* Find the containing function's current nonlocal goto handler, + which will do any cleanups and then jump to the label. */ + labreg = gen_rtx_REG (Pmode, 8); + emit_move_insn (labreg, lab); + + /* Restore %fp from stack pointer value for containing function. + The restore insn that follows will move this to %sp, + and reload the appropriate value into %fp. */ + emit_move_insn (frame_pointer_rtx, stack); + + /* USE of frame_pointer_rtx added for consistency; not clear if + really needed. */ + /*emit_insn (gen_rtx_USE (VOIDmode, frame_pointer_rtx));*/ + emit_insn (gen_rtx_USE (VOIDmode, stack_pointer_rtx)); + +#if 0 + /* Return, restoring reg window and jumping to goto handler. */ + if (TARGET_V9 && GET_CODE (chain) == CONST_INT + && ! (INTVAL (chain) & ~(HOST_WIDE_INT)0xffffffff)) + { + emit_insn (gen_goto_handler_and_restore_v9 (labreg, static_chain_rtx, + chain)); + emit_barrier (); + DONE; + } + /* Put in the static chain register the nonlocal label address. */ + emit_move_insn (static_chain_rtx, chain); +#endif + + emit_insn (gen_rtx_USE (VOIDmode, static_chain_rtx)); + emit_insn (gen_goto_handler_and_restore (labreg)); + emit_barrier (); + DONE; +}") + +;; Special trap insn to flush register windows. +(define_insn "flush_register_windows" + [(unspec_volatile [(const_int 0)] 1)] + "" + "* return TARGET_V9 ? \"flushw\" : \"ta\\t3\";" + [(set_attr "type" "misc") + (set_attr "length" "1")]) + +(define_insn "goto_handler_and_restore" + [(unspec_volatile [(match_operand 0 "register_operand" "=r")] 2)] + "" + "jmp\\t%0+0\\n\\trestore" + [(set_attr "type" "misc") + (set_attr "length" "2")]) + +;;(define_insn "goto_handler_and_restore_v9" +;; [(unspec_volatile [(match_operand:SI 0 "register_operand" "=r,r") +;; (match_operand:SI 1 "register_operand" "=r,r") +;; (match_operand:SI 2 "const_int_operand" "I,n")] 3)] +;; "TARGET_V9 && ! TARGET_ARCH64" +;; "@ +;; return\\t%0+0\\n\\tmov\\t%2, %Y1 +;; sethi\\t%%hi(%2), %1\\n\\treturn\\t%0+0\\n\\tor\\t%Y1, %%lo(%2), %Y1" +;; [(set_attr "type" "misc") +;; (set_attr "length" "2,3")]) +;; +;;(define_insn "*goto_handler_and_restore_v9_sp64" +;; [(unspec_volatile [(match_operand:DI 0 "register_operand" "=r,r") +;; (match_operand:DI 1 "register_operand" "=r,r") +;; (match_operand:SI 2 "const_int_operand" "I,n")] 3)] +;; "TARGET_V9 && TARGET_ARCH64" +;; "@ +;; return\\t%0+0\\n\\tmov\\t%2, %Y1 +;; sethi\\t%%hi(%2), %1\\n\\treturn\\t%0+0\\n\\tor\\t%Y1, %%lo(%2), %Y1" +;; [(set_attr "type" "misc") +;; (set_attr "length" "2,3")]) + +;; Pattern for use after a setjmp to store FP and the return register +;; into the stack area. + +(define_expand "setjmp" + [(const_int 0)] + "" + " +{ + if (TARGET_ARCH64) + emit_insn (gen_setjmp_64 ()); + else + emit_insn (gen_setjmp_32 ()); + DONE; +}") + +(define_expand "setjmp_32" + [(set (mem:SI (plus:SI (reg:SI 14) (const_int 56))) (match_dup 0)) + (set (mem:SI (plus:SI (reg:SI 14) (const_int 60))) (reg:SI 31))] + "" + " +{ operands[0] = frame_pointer_rtx; }") + +(define_expand "setjmp_64" + [(set (mem:DI (plus:DI (reg:DI 14) (const_int 112))) (match_dup 0)) + (set (mem:DI (plus:DI (reg:DI 14) (const_int 120))) (reg:DI 31))] + "" + " +{ operands[0] = frame_pointer_rtx; }") + +;; Special pattern for the FLUSH instruction. + +(define_insn "flush" + [(unspec_volatile [(match_operand 0 "memory_operand" "m")] 4)] + "" + "* return TARGET_V9 ? \"flush\\t%f0\" : \"iflush\\t%f0\";" + [(set_attr "type" "misc") + (set_attr "length" "1")]) + +;; find first set. + +;; The scan instruction searches from the most significant bit while ffs +;; searches from the least significant bit. The bit index and treatment of +;; zero also differ. It takes at least 7 instructions to get the proper +;; result. Here is an obvious 8 instruction sequence. + +;; XXX +(define_insn "ffssi2" + [(set (match_operand:SI 0 "register_operand" "=&r") + (ffs:SI (match_operand:SI 1 "register_operand" "r"))) + (clobber (match_scratch:SI 2 "=&r"))] + "TARGET_SPARCLITE || TARGET_SPARCLET" + "* +{ + if (TARGET_LIVE_G0) + output_asm_insn (\"and %%g0,0,%%g0\", operands); + return \"sub %%g0,%1,%0\;and %0,%1,%0\;scan %0,0,%0\;mov 32,%2\;sub %2,%0,%0\;sra %0,31,%2\;and %2,31,%2\;add %2,%0,%0\"; +}" + [(set_attr "type" "multi") + (set_attr "length" "8")]) + +;; ??? This should be a define expand, so that the extra instruction have +;; a chance of being optimized away. + +;; Disabled because none of the UltraSparcs implement popc. The HAL R1 +;; does, but no one uses that and we don't have a switch for it. +; +;(define_insn "ffsdi2" +; [(set (match_operand:DI 0 "register_operand" "=&r") +; (ffs:DI (match_operand:DI 1 "register_operand" "r"))) +; (clobber (match_scratch:DI 2 "=&r"))] +; "TARGET_ARCH64" +; "neg %1,%2\;xnor %1,%2,%2\;popc %2,%0\;movzr %1,0,%0" +; [(set_attr "type" "multi") +; (set_attr "length" "4")]) + + +;; Peepholes go at the end. + +;; Optimize consecutive loads or stores into ldd and std when possible. +;; The conditions in which we do this are very restricted and are +;; explained in the code for {registers,memory}_ok_for_ldd functions. + +(define_peephole + [(set (match_operand:SI 0 "memory_operand" "") + (const_int 0)) + (set (match_operand:SI 1 "memory_operand" "") + (const_int 0))] + "TARGET_V9 + && ! MEM_VOLATILE_P (operands[0]) + && ! MEM_VOLATILE_P (operands[1]) + && addrs_ok_for_ldd_peep (XEXP (operands[0], 0), XEXP (operands[1], 0))" + "stx\\t%%g0, %0") + +(define_peephole + [(set (match_operand:SI 0 "memory_operand" "") + (const_int 0)) + (set (match_operand:SI 1 "memory_operand" "") + (const_int 0))] + "TARGET_V9 + && ! MEM_VOLATILE_P (operands[0]) + && ! MEM_VOLATILE_P (operands[1]) + && addrs_ok_for_ldd_peep (XEXP (operands[1], 0), XEXP (operands[0], 0))" + "stx\\t%%g0, %1") + +(define_peephole + [(set (match_operand:SI 0 "register_operand" "=rf") + (match_operand:SI 1 "memory_operand" "")) + (set (match_operand:SI 2 "register_operand" "=rf") + (match_operand:SI 3 "memory_operand" ""))] + "registers_ok_for_ldd_peep (operands[0], operands[2]) + && ! MEM_VOLATILE_P (operands[1]) + && ! MEM_VOLATILE_P (operands[3]) + && addrs_ok_for_ldd_peep (XEXP (operands[1], 0), XEXP (operands[3], 0))" + "ldd\\t%1, %0") + +(define_peephole + [(set (match_operand:SI 0 "memory_operand" "") + (match_operand:SI 1 "register_operand" "rf")) + (set (match_operand:SI 2 "memory_operand" "") + (match_operand:SI 3 "register_operand" "rf"))] + "registers_ok_for_ldd_peep (operands[1], operands[3]) + && ! MEM_VOLATILE_P (operands[0]) + && ! MEM_VOLATILE_P (operands[2]) + && addrs_ok_for_ldd_peep (XEXP (operands[0], 0), XEXP (operands[2], 0))" + "std\\t%1, %0") + +(define_peephole + [(set (match_operand:SF 0 "register_operand" "=fr") + (match_operand:SF 1 "memory_operand" "")) + (set (match_operand:SF 2 "register_operand" "=fr") + (match_operand:SF 3 "memory_operand" ""))] + "registers_ok_for_ldd_peep (operands[0], operands[2]) + && ! MEM_VOLATILE_P (operands[1]) + && ! MEM_VOLATILE_P (operands[3]) + && addrs_ok_for_ldd_peep (XEXP (operands[1], 0), XEXP (operands[3], 0))" + "ldd\\t%1, %0") + +(define_peephole + [(set (match_operand:SF 0 "memory_operand" "") + (match_operand:SF 1 "register_operand" "fr")) + (set (match_operand:SF 2 "memory_operand" "") + (match_operand:SF 3 "register_operand" "fr"))] + "registers_ok_for_ldd_peep (operands[1], operands[3]) + && ! MEM_VOLATILE_P (operands[0]) + && ! MEM_VOLATILE_P (operands[2]) + && addrs_ok_for_ldd_peep (XEXP (operands[0], 0), XEXP (operands[2], 0))" + "std\\t%1, %0") + +(define_peephole + [(set (match_operand:SI 0 "register_operand" "=rf") + (match_operand:SI 1 "memory_operand" "")) + (set (match_operand:SI 2 "register_operand" "=rf") + (match_operand:SI 3 "memory_operand" ""))] + "registers_ok_for_ldd_peep (operands[2], operands[0]) + && ! MEM_VOLATILE_P (operands[3]) + && ! MEM_VOLATILE_P (operands[1]) + && addrs_ok_for_ldd_peep (XEXP (operands[3], 0), XEXP (operands[1], 0))" + "ldd\\t%3, %2") + +(define_peephole + [(set (match_operand:SI 0 "memory_operand" "") + (match_operand:SI 1 "register_operand" "rf")) + (set (match_operand:SI 2 "memory_operand" "") + (match_operand:SI 3 "register_operand" "rf"))] + "registers_ok_for_ldd_peep (operands[3], operands[1]) + && ! MEM_VOLATILE_P (operands[2]) + && ! MEM_VOLATILE_P (operands[0]) + && addrs_ok_for_ldd_peep (XEXP (operands[2], 0), XEXP (operands[0], 0))" + "std\\t%3, %2") + +(define_peephole + [(set (match_operand:SF 0 "register_operand" "=fr") + (match_operand:SF 1 "memory_operand" "")) + (set (match_operand:SF 2 "register_operand" "=fr") + (match_operand:SF 3 "memory_operand" ""))] + "registers_ok_for_ldd_peep (operands[2], operands[0]) + && ! MEM_VOLATILE_P (operands[3]) + && ! MEM_VOLATILE_P (operands[1]) + && addrs_ok_for_ldd_peep (XEXP (operands[3], 0), XEXP (operands[1], 0))" + "ldd\\t%3, %2") + +(define_peephole + [(set (match_operand:SF 0 "memory_operand" "") + (match_operand:SF 1 "register_operand" "fr")) + (set (match_operand:SF 2 "memory_operand" "") + (match_operand:SF 3 "register_operand" "fr"))] + "registers_ok_for_ldd_peep (operands[3], operands[1]) + && ! MEM_VOLATILE_P (operands[2]) + && ! MEM_VOLATILE_P (operands[0]) + && addrs_ok_for_ldd_peep (XEXP (operands[2], 0), XEXP (operands[0], 0))" + "std\\t%3, %2") + +;; Optimize the case of following a reg-reg move with a test +;; of reg just moved. Don't allow floating point regs for operand 0 or 1. +;; This can result from a float to fix conversion. + +(define_peephole + [(set (match_operand:SI 0 "register_operand" "=r") + (match_operand:SI 1 "register_operand" "r")) + (set (reg:CC 100) + (compare:CC (match_operand:SI 2 "register_operand" "r") + (const_int 0)))] + "(rtx_equal_p (operands[2], operands[0]) + || rtx_equal_p (operands[2], operands[1])) + && ! FP_REG_P (operands[0]) + && ! FP_REG_P (operands[1])" + "orcc\\t%1, 0, %0") + +(define_peephole + [(set (match_operand:DI 0 "register_operand" "=r") + (match_operand:DI 1 "register_operand" "r")) + (set (reg:CCX 100) + (compare:CCX (match_operand:DI 2 "register_operand" "r") + (const_int 0)))] + "TARGET_ARCH64 + && (rtx_equal_p (operands[2], operands[0]) + || rtx_equal_p (operands[2], operands[1])) + && ! FP_REG_P (operands[0]) + && ! FP_REG_P (operands[1])" + "orcc\\t%1, 0, %0") + +;; Return peepholes. First the "normal" ones. +;; These are necessary to catch insns ending up in the epilogue delay list. + +(define_insn "*return_qi" + [(set (match_operand:QI 0 "restore_operand" "") + (match_operand:QI 1 "arith_operand" "rI")) + (return)] + "! TARGET_EPILOGUE && ! TARGET_LIVE_G0" + "* +{ + if (! TARGET_ARCH64 && current_function_returns_struct) + return \"jmp\\t%%i7+12\\n\\trestore %%g0, %1, %Y0\"; + else if (TARGET_V9 && (GET_CODE (operands[1]) == CONST_INT + || IN_OR_GLOBAL_P (operands[1]))) + return \"return\\t%%i7+8\\n\\tmov\\t%Y1, %Y0\"; + else + return \"ret\\n\\trestore %%g0, %1, %Y0\"; +}" + [(set_attr "type" "multi")]) + +(define_insn "*return_hi" + [(set (match_operand:HI 0 "restore_operand" "") + (match_operand:HI 1 "arith_operand" "rI")) + (return)] + "! TARGET_EPILOGUE && ! TARGET_LIVE_G0" + "* +{ + if (! TARGET_ARCH64 && current_function_returns_struct) + return \"jmp\\t%%i7+12\\n\\trestore %%g0, %1, %Y0\"; + else if (TARGET_V9 && (GET_CODE (operands[1]) == CONST_INT + || IN_OR_GLOBAL_P (operands[1]))) + return \"return\\t%%i7+8\\n\\tmov\\t%Y1, %Y0\"; + else + return \"ret\;restore %%g0, %1, %Y0\"; +}" + [(set_attr "type" "multi")]) + +(define_insn "*return_si" + [(set (match_operand:SI 0 "restore_operand" "") + (match_operand:SI 1 "arith_operand" "rI")) + (return)] + "! TARGET_EPILOGUE && ! TARGET_LIVE_G0" + "* +{ + if (! TARGET_ARCH64 && current_function_returns_struct) + return \"jmp\\t%%i7+12\\n\\trestore %%g0, %1, %Y0\"; + else if (TARGET_V9 && (GET_CODE (operands[1]) == CONST_INT + || IN_OR_GLOBAL_P (operands[1]))) + return \"return\\t%%i7+8\\n\\tmov\\t%Y1, %Y0\"; + else + return \"ret\;restore %%g0, %1, %Y0\"; +}" + [(set_attr "type" "multi")]) + +;; The following pattern is only generated by delayed-branch scheduling, +;; when the insn winds up in the epilogue. This can happen not only when +;; ! TARGET_FPU because we move complex types around by parts using +;; SF mode SUBREGs. +(define_insn "*return_sf_no_fpu" + [(set (match_operand:SF 0 "restore_operand" "r") + (match_operand:SF 1 "register_operand" "r")) + (return)] + "! TARGET_EPILOGUE && ! TARGET_LIVE_G0" + "* +{ + if (! TARGET_ARCH64 && current_function_returns_struct) + return \"jmp\\t%%i7+12\\n\\trestore %%g0, %1, %Y0\"; + else if (TARGET_V9 && IN_OR_GLOBAL_P (operands[1])) + return \"return\\t%%i7+8\\n\\tmov\\t%Y1, %Y0\"; + else + return \"ret\;restore %%g0, %1, %Y0\"; +}" + [(set_attr "type" "multi")]) + +(define_insn "*return_addsi" + [(set (match_operand:SI 0 "restore_operand" "") + (plus:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "arith_operand" "rI"))) + (return)] + "! TARGET_EPILOGUE && ! TARGET_LIVE_G0" + "* +{ + if (! TARGET_ARCH64 && current_function_returns_struct) + return \"jmp\\t%%i7+12\\n\\trestore %r1, %2, %Y0\"; + /* If operands are global or in registers, can use return */ + else if (TARGET_V9 && IN_OR_GLOBAL_P (operands[1]) + && (GET_CODE (operands[2]) == CONST_INT + || IN_OR_GLOBAL_P (operands[2]))) + return \"return\\t%%i7+8\\n\\tadd\\t%Y1, %Y2, %Y0\"; + else + return \"ret\;restore %r1, %2, %Y0\"; +}" + [(set_attr "type" "multi")]) + +(define_insn "*return_di" + [(set (match_operand:DI 0 "restore_operand" "") + (match_operand:DI 1 "arith_double_operand" "rHI")) + (return)] + "TARGET_ARCH64 && ! TARGET_EPILOGUE" + "ret\;restore %%g0, %1, %Y0" + [(set_attr "type" "multi")]) + +(define_insn "*return_adddi" + [(set (match_operand:DI 0 "restore_operand" "") + (plus:DI (match_operand:DI 1 "arith_operand" "%r") + (match_operand:DI 2 "arith_double_operand" "rHI"))) + (return)] + "TARGET_ARCH64 && ! TARGET_EPILOGUE" + "ret\;restore %r1, %2, %Y0" + [(set_attr "type" "multi")]) + +;; The following pattern is only generated by delayed-branch scheduling, +;; when the insn winds up in the epilogue. +(define_insn "*return_sf" + [(set (reg:SF 32) + (match_operand:SF 0 "register_operand" "f")) + (return)] + "! TARGET_EPILOGUE" + "ret\;fmovs\\t%0, %%f0" + [(set_attr "type" "multi")]) + +;; Now peepholes to do a call followed by a jump. + +(define_peephole + [(parallel [(set (match_operand 0 "" "") + (call (mem:SI (match_operand:SI 1 "call_operand_address" "ps")) + (match_operand 2 "" ""))) + (clobber (reg:SI 15))]) + (set (pc) (label_ref (match_operand 3 "" "")))] + "short_branch (INSN_UID (insn), INSN_UID (operands[3])) + && in_same_eh_region (insn, operands[3]) + && in_same_eh_region (insn, ins1)" + "call\\t%a1, %2\\n\\tadd\\t%%o7, (%l3-.-4), %%o7") + +(define_peephole + [(parallel [(call (mem:SI (match_operand:SI 0 "call_operand_address" "ps")) + (match_operand 1 "" "")) + (clobber (reg:SI 15))]) + (set (pc) (label_ref (match_operand 2 "" "")))] + "short_branch (INSN_UID (insn), INSN_UID (operands[2])) + && in_same_eh_region (insn, operands[2]) + && in_same_eh_region (insn, ins1)" + "call\\t%a0, %1\\n\\tadd\\t%%o7, (%l2-.-4), %%o7") + +(define_peephole + [(parallel [(set (match_operand 0 "" "") + (call (mem:SI (match_operand:DI 1 "call_operand_address" "ps")) + (match_operand 2 "" ""))) + (clobber (reg:DI 15))]) + (set (pc) (label_ref (match_operand 3 "" "")))] + "TARGET_ARCH64 + && short_branch (INSN_UID (insn), INSN_UID (operands[3])) + && in_same_eh_region (insn, operands[3]) + && in_same_eh_region (insn, ins1)" + "call\\t%a1, %2\\n\\tadd\\t%%o7, (%l3-.-4), %%o7") + +(define_peephole + [(parallel [(call (mem:SI (match_operand:DI 0 "call_operand_address" "ps")) + (match_operand 1 "" "")) + (clobber (reg:DI 15))]) + (set (pc) (label_ref (match_operand 2 "" "")))] + "TARGET_ARCH64 + && short_branch (INSN_UID (insn), INSN_UID (operands[2])) + && in_same_eh_region (insn, operands[2]) + && in_same_eh_region (insn, ins1)" + "call\\t%a0, %1\\n\\tadd\\t%%o7, (%l2-.-4), %%o7") + +;; After a nonlocal goto, we need to restore the PIC register, but only +;; if we need it. So do nothing much here, but we'll check for this in +;; finalize_pic. + +;; Make sure this unspec_volatile number agrees with finalize_pic. +(define_insn "nonlocal_goto_receiver" + [(unspec_volatile [(const_int 0)] 5)] + "flag_pic" + "") + +(define_insn "trap" + [(trap_if (const_int 1) (const_int 5))] + "" + "ta\\t5" + [(set_attr "type" "misc") + (set_attr "length" "1")]) + +(define_expand "conditional_trap" + [(trap_if (match_operator 0 "noov_compare_op" + [(match_dup 2) (match_dup 3)]) + (match_operand:SI 1 "arith_operand" ""))] + "" + "operands[2] = gen_compare_reg (GET_CODE (operands[0]), + sparc_compare_op0, sparc_compare_op1); + operands[3] = const0_rtx;") + +(define_insn "" + [(trap_if (match_operator 0 "noov_compare_op" [(reg:CC 100) (const_int 0)]) + (match_operand:SI 1 "arith_operand" "rM"))] + "" + "t%C0\\t%1" + [(set_attr "type" "misc") + (set_attr "length" "1")]) + +(define_insn "" + [(trap_if (match_operator 0 "noov_compare_op" [(reg:CCX 100) (const_int 0)]) + (match_operand:SI 1 "arith_operand" "rM"))] + "TARGET_V9" + "t%C0\\t%%xcc, %1" + [(set_attr "type" "misc") + (set_attr "length" "1")]) + diff --git a/gcc/config/sparc/splet.h b/gcc/config/sparc/splet.h new file mode 100755 index 0000000..50dbb58 --- /dev/null +++ b/gcc/config/sparc/splet.h @@ -0,0 +1,69 @@ +/* Definitions of target machine for GNU compiler, for SPARClet. + Copyright (C) 1996, 1997 Free Software Foundation, Inc. + Contributed by Doug Evans (dje@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. */ + +#include "sparc/aout.h" + +/* -mbroken-saverestore is not included here because the long term + default is -mno-broken-saverestore. */ +#undef TARGET_DEFAULT +#define TARGET_DEFAULT (MASK_APP_REGS + MASK_EPILOGUE) + +/* -mlive-g0 is only supported on the sparclet. */ +#undef SUBTARGET_SWITCHES +#define SUBTARGET_SWITCHES \ +{"big-endian", -MASK_LITTLE_ENDIAN}, \ +{"little-endian", MASK_LITTLE_ENDIAN}, \ +{"live-g0", MASK_LIVE_G0}, \ +{"no-live-g0", -MASK_LIVE_G0}, \ +{"broken-saverestore", MASK_BROKEN_SAVERESTORE}, \ +{"no-broken-saverestore", -MASK_BROKEN_SAVERESTORE}, + +#undef ASM_SPEC +#define ASM_SPEC "%{mlittle-endian:-EL} %(asm_cpu)" + +/* Require the user to supply crt0.o. */ +#undef STARTFILE_SPEC +#define STARTFILE_SPEC "" + +#undef LINK_SPEC +#define LINK_SPEC "%{mlittle-endian:-EL}" + +/* sparclet chips are bi-endian. */ +#undef BYTES_BIG_ENDIAN +#define BYTES_BIG_ENDIAN (! TARGET_LITTLE_ENDIAN) +#undef WORDS_BIG_ENDIAN +#define WORDS_BIG_ENDIAN (! TARGET_LITTLE_ENDIAN) + +#undef SUBTARGET_OVERRIDE_OPTIONS +#define SUBTARGET_OVERRIDE_OPTIONS \ + do { \ + if (TARGET_LIVE_G0) \ + { \ + warning ("Option '-mlive-g0' deprecated."); \ + target_flags &= ~MASK_LIVE_G0; \ + } \ + else if (TARGET_BROKEN_SAVERESTORE) \ + { \ + warning ("Option '-mbroken-saverestore' deprecated."); \ + target_flags &= ~MASK_BROKEN_SAVERESTORE; \ + } \ + } while (0) + diff --git a/gcc/config/sparc/sun4gas.h b/gcc/config/sparc/sun4gas.h new file mode 100755 index 0000000..3cea956 --- /dev/null +++ b/gcc/config/sparc/sun4gas.h @@ -0,0 +1,27 @@ +/* Definitions of target machine for GNU compiler, for SunOS 4.x with gas + Copyright (C) 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. */ + +/* gas supports unaligned data. */ +#define UNALIGNED_DOUBLE_INT_ASM_OP ".uaxword" +#define UNALIGNED_INT_ASM_OP ".uaword" +#define UNALIGNED_SHORT_ASM_OP ".uahalf" + +/* defaults.h will define DWARF2_UNWIND_INFO for us. */ +#undef DWARF2_UNWIND_INFO diff --git a/gcc/config/sparc/sun4o3.h b/gcc/config/sparc/sun4o3.h new file mode 100755 index 0000000..10c7391 --- /dev/null +++ b/gcc/config/sparc/sun4o3.h @@ -0,0 +1,29 @@ +#include "sparc/sparc.h" + +#undef FUNCTION_PROFILER +#define FUNCTION_PROFILER(FILE, LABELNO) \ + fprintf (FILE, "\tsethi %%hi(LP%d),%%o0\n\tcall .mcount\n\tor %%lo(LP%d),%%o0,%%o0\n", \ + (LABELNO), (LABELNO)) + +/* LINK_SPEC is needed only for SunOS 4. */ + +#undef LINK_SPEC + +/* Override MACHINE_STATE_{SAVE,RESTORE} because we have special + traps available which can get and set the condition codes + reliably. */ +#undef MACHINE_STATE_SAVE +#define MACHINE_STATE_SAVE(ID) \ + unsigned long int ms_flags, ms_saveret; \ + asm volatile("ta 0x20\n\t" \ + "mov %%g1, %0\n\t" \ + "mov %%g2, %1\n\t" \ + : "=r" (ms_flags), "=r" (ms_saveret)); + +#undef MACHINE_STATE_RESTORE +#define MACHINE_STATE_RESTORE(ID) \ + asm volatile("mov %0, %%g1\n\t" \ + "mov %1, %%g2\n\t" \ + "ta 0x21\n\t" \ + : /* no outputs */ \ + : "r" (ms_flags), "r" (ms_saveret)); diff --git a/gcc/config/sparc/sunos4.h b/gcc/config/sparc/sunos4.h new file mode 100755 index 0000000..14c7a43 --- /dev/null +++ b/gcc/config/sparc/sunos4.h @@ -0,0 +1,49 @@ +/* Definitions of target machine for GNU compiler, for SunOS 4.x + Copyright (C) 1994 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. */ + +#define SUNOS4_SHARED_LIBRARIES 1 + +/* Use N_BINCL stabs. */ + +#define DBX_USE_BINCL + +#include "sparc/sparc.h" + +/* The Sun as doesn't like unaligned data. */ +#define DWARF2_UNWIND_INFO 0 + +/* Override MACHINE_STATE_{SAVE,RESTORE} because we have special + traps available which can get and set the condition codes + reliably. */ +#undef MACHINE_STATE_SAVE +#define MACHINE_STATE_SAVE(ID) \ + unsigned long int ms_flags, ms_saveret; \ + asm volatile("ta 0x20\n\t" \ + "mov %%g1, %0\n\t" \ + "mov %%g2, %1\n\t" \ + : "=r" (ms_flags), "=r" (ms_saveret)); + +#undef MACHINE_STATE_RESTORE +#define MACHINE_STATE_RESTORE(ID) \ + asm volatile("mov %0, %%g1\n\t" \ + "mov %1, %%g2\n\t" \ + "ta 0x21\n\t" \ + : /* no outputs */ \ + : "r" (ms_flags), "r" (ms_saveret)); diff --git a/gcc/config/sparc/sysv4.h b/gcc/config/sparc/sysv4.h new file mode 100755 index 0000000..572b9dc --- /dev/null +++ b/gcc/config/sparc/sysv4.h @@ -0,0 +1,233 @@ +/* Target definitions for GNU compiler for Sparc running System V.4 + Copyright (C) 1991, 92, 95, 96, 97, 1998 Free Software Foundation, Inc. + Contributed by Ron Guilmette (rfg@monkeys.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. */ + +#include "sparc/sparc.h" + +/* Undefine some symbols which are defined in "sparc.h" but which are + appropriate only for SunOS 4.x, and not for svr4. */ + +#undef WORD_SWITCH_TAKES_ARG +#undef ASM_OUTPUT_SOURCE_LINE +#undef SELECT_SECTION +#undef ASM_DECLARE_FUNCTION_NAME +#undef TEXT_SECTION_ASM_OP +#undef DATA_SECTION_ASM_OP + +#include "svr4.h" + +/* ??? Put back the SIZE_TYPE/PTRDIFF_TYPE definitions set by sparc.h. + Why, exactly, is svr4.h messing with this? Seems like the chip + would know best. */ + +#undef SIZE_TYPE +#define SIZE_TYPE (TARGET_ARCH64 ? "long unsigned int" : "unsigned int") + +#undef PTRDIFF_TYPE +#define PTRDIFF_TYPE (TARGET_ARCH64 ? "long int" : "int") + +/* Undefined some symbols which are defined in "svr4.h" but which are + appropriate only for typical svr4 systems, but not for the specific + case of svr4 running on a Sparc. */ + +#undef INIT_SECTION_ASM_OP +#undef FINI_SECTION_ASM_OP +#undef CONST_SECTION_ASM_OP +#undef TYPE_OPERAND_FMT +#undef PUSHSECTION_FORMAT +#undef STRING_ASM_OP +#undef COMMON_ASM_OP +#undef SKIP_ASM_OP +#undef SET_ASM_OP /* Has no equivalent. See ASM_OUTPUT_DEF below. */ + +/* Provide a set of pre-definitions and pre-assertions appropriate for + the Sparc running svr4. __svr4__ is our extension. */ + +#define CPP_PREDEFINES \ +"-Dsparc -Dunix -D__svr4__ -Asystem(unix) -Asystem(svr4)" + +/* The native assembler can't compute differences between symbols in different + sections when generating pic code, so we must put jump tables in the + text section. */ +/* But we now defer the tables to the end of the function, so we make + this 0 to not confuse the branch shortening code. */ +#define JUMP_TABLES_IN_TEXT_SECTION 0 + +/* Pass -K to the assembler when PIC. */ +#undef ASM_SPEC +#define ASM_SPEC \ + "%{v:-V} %{Qy:} %{!Qn:-Qy} %{n} %{T} %{Ym,*} %{Yd,*} %{Wa,*:%*} \ + %{fpic:-K PIC} %{fPIC:-K PIC} %(asm_cpu)" + +/* Must use data section for relocatable constants when pic. */ +#undef SELECT_RTX_SECTION +#define SELECT_RTX_SECTION(MODE,RTX) \ +{ \ + if (flag_pic && symbolic_operand (RTX)) \ + data_section (); \ + else \ + const_section (); \ +} + +/* Define the names of various pseudo-op used by the Sparc/svr4 assembler. + Note that many of these are different from the typical pseudo-ops used + by most svr4 assemblers. That is probably due to a (misguided?) attempt + to keep the Sparc/svr4 assembler somewhat compatible with the Sparc/SunOS + assembler. */ + +#define STRING_ASM_OP ".asciz" +#define COMMON_ASM_OP ".common" +#define SKIP_ASM_OP ".skip" +#define UNALIGNED_DOUBLE_INT_ASM_OP ".uaxword" +#define UNALIGNED_INT_ASM_OP ".uaword" +#define UNALIGNED_SHORT_ASM_OP ".uahalf" +#define PUSHSECTION_ASM_OP ".pushsection" +#define POPSECTION_ASM_OP ".popsection" + +/* This is defined in sparc.h but is not used by svr4.h. */ +#undef ASM_LONG +#define ASM_LONG ".long" + +/* This is the format used to print the second operand of a .type pseudo-op + for the Sparc/svr4 assembler. */ + +#define TYPE_OPERAND_FMT "#%s" + +/* This is the format used to print a .pushsection pseudo-op (and its operand) + for the Sparc/svr4 assembler. */ + +#define PUSHSECTION_FORMAT "\t%s\t\"%s\"\n" + +#undef ASM_OUTPUT_CASE_LABEL +#define ASM_OUTPUT_CASE_LABEL(FILE, PREFIX, NUM, JUMPTABLE) \ +do { ASM_OUTPUT_ALIGN ((FILE), Pmode == SImode ? 2 : 3); \ + ASM_OUTPUT_INTERNAL_LABEL ((FILE), PREFIX, NUM); \ + } while (0) + +/* This is how to equate one symbol to another symbol. The syntax used is + `SYM1=SYM2'. Note that this is different from the way equates are done + with most svr4 assemblers, where the syntax is `.set SYM1,SYM2'. */ + +#define ASM_OUTPUT_DEF(FILE,LABEL1,LABEL2) \ + do { fprintf ((FILE), "\t"); \ + assemble_name (FILE, LABEL1); \ + fprintf (FILE, " = "); \ + assemble_name (FILE, LABEL2); \ + fprintf (FILE, "\n"); \ + } while (0) + +/* Define how the Sparc registers should be numbered for Dwarf output. + The numbering provided here should be compatible with the native + svr4 SDB debugger in the Sparc/svr4 reference port. The numbering + is as follows: + + Assembly name gcc internal regno Dwarf regno + ---------------------------------------------------------- + g0-g7 0-7 0-7 + o0-o7 8-15 8-15 + l0-l7 16-23 16-23 + i0-i7 24-31 24-31 + f0-f31 32-63 40-71 +*/ + +#define DBX_REGISTER_NUMBER(REGNO) ((REGNO) < 32 ? (REGNO) : (REGNO) + 8) + +/* A set of symbol definitions for assembly pseudo-ops which will + get us switched to various sections of interest. These are used + in all places where we simply want to switch to a section, and + *not* to push the previous section name onto the assembler's + section names stack (as we do often in dwarfout.c). */ + +#define TEXT_SECTION_ASM_OP ".section\t\".text\"" +#define DATA_SECTION_ASM_OP ".section\t\".data\"" +#define BSS_SECTION_ASM_OP ".section\t\".bss\"" +#define CONST_SECTION_ASM_OP ".section\t\".rodata\"" +#define INIT_SECTION_ASM_OP ".section\t\".init\"" +#define FINI_SECTION_ASM_OP ".section\t\".fini\"" + +/* 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. (Note that on SVR4, you may wish to use the + `-z text' option to the ELF linker, when building a shared library, as + an additional check that you are doing everything right. But if you do + use the `-z text' option when building a shared library, you will get + errors unless the .ctors and .dtors sections are marked as writable + via the SHF_WRITE attribute.) */ + +#undef CTORS_SECTION_ASM_OP +#define CTORS_SECTION_ASM_OP ".section\t\".ctors\",#alloc,#write" +#undef DTORS_SECTION_ASM_OP +#define DTORS_SECTION_ASM_OP ".section\t\".dtors\",#alloc,#write" +#undef EH_FRAME_SECTION_ASM_OP +#define EH_FRAME_SECTION_ASM_OP ".section\t\".eh_frame\",#alloc,#write" + +/* A C statement to output something to the assembler file to switch to section + NAME for object DECL which is either a FUNCTION_DECL, a VAR_DECL or + NULL_TREE. Some target formats do not support arbitrary sections. Do not + define this macro in such cases. */ + +#undef ASM_OUTPUT_SECTION_NAME /* Override svr4.h's definition. */ +#define ASM_OUTPUT_SECTION_NAME(FILE, DECL, NAME, RELOC) \ +do { \ + if ((DECL) && TREE_CODE (DECL) == FUNCTION_DECL) \ + fprintf (FILE, ".section\t\"%s\",#alloc,#execinstr\n", \ + (NAME)); \ + else if ((DECL) && DECL_READONLY_SECTION (DECL, RELOC)) \ + fprintf (FILE, ".section\t\"%s\",#alloc\n", (NAME)); \ + else \ + fprintf (FILE, ".section\t\"%s\",#alloc,#write\n", (NAME)); \ +} while (0) + +/* Output assembler code to FILE to initialize this source file's + basic block profiling info, if that has not already been done. */ + +#undef FUNCTION_BLOCK_PROFILER +#define FUNCTION_BLOCK_PROFILER(FILE, LABELNO) \ + do { \ + fprintf (FILE, "\tsethi %%hi(.LLPBX0),%%o0\n\tld [%%lo(.LLPBX0)+%%o0],%%o1\n\ttst %%o1\n\tbne LPY%d\n\tadd %%o0,%%lo(.LLPBX0),%%o0\n\tcall __bb_init_func\n\tnop\nLPY%d:\n", \ + (LABELNO), (LABELNO)); \ + } while (0) + +/* Output assembler code to FILE to increment the entry-count for + the BLOCKNO'th basic block in this source file. */ + +#undef BLOCK_PROFILER +#define BLOCK_PROFILER(FILE, BLOCKNO) \ +{ \ + int blockn = (BLOCKNO); \ + fprintf (FILE, "\tsethi %%hi(.LLPBX2+%d),%%g1\n\tld [%%lo(.LLPBX2+%d)+%%g1],%%g2\n\ +\tadd %%g2,1,%%g2\n\tst %%g2,[%%lo(.LLPBX2+%d)+%%g1]\n", \ + 4 * blockn, 4 * blockn, 4 * blockn); \ +} + +/* A C statement (sans semicolon) to output to the stdio stream + FILE the assembler definition of uninitialized global DECL named + NAME whose size is SIZE bytes and alignment is ALIGN bytes. + Try to use asm_output_aligned_bss to implement this macro. */ + +#undef ASM_OUTPUT_ALIGNED_BSS +#define ASM_OUTPUT_ALIGNED_BSS(FILE, DECL, NAME, SIZE, ALIGN) \ + asm_output_aligned_bss (FILE, DECL, NAME, SIZE, ALIGN) diff --git a/gcc/config/sparc/t-elf b/gcc/config/sparc/t-elf new file mode 100755 index 0000000..da9df38 --- /dev/null +++ b/gcc/config/sparc/t-elf @@ -0,0 +1,39 @@ +# we need to supply our own assembly versions of libgcc1.c files, +# since the user may not have native 'cc' available + +CROSS_LIBGCC1 = libgcc1-asm.a +LIB1ASMSRC = sparc/lb1spc.asm +LIB1ASMFUNCS = _mulsi3 _divsi3 _modsi3 + +# crt0 is built elsewhere +LIBGCC1_TEST = + +# These are really part of libgcc1, but this will cause them to be +# built correctly, so... + +LIB2FUNCS_EXTRA = fp-bit.c dp-bit.c + +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 + +# MULTILIB_OPTIONS should have msparclite too, but we'd have to make +# gas build... +#MULTILIB_OPTIONS = msoft-float mcpu=v8 +MULTILIB_OPTIONS = msoft-float +#MULTILIB_DIRNAMES = soft v8 +MULTILIB_DIRNAMES = soft +#MULTILIB_MATCHES = msoft-float=mno-fpu mcpu?v8=mv8 +MULTILIB_MATCHES = msoft-float=mno-fpu + +LIBGCC = stmp-multilib +INSTALL_LIBGCC = install-multilib + +# Assemble startup files. +crti.o: $(srcdir)/config/sparc/sol2-ci.asm $(GCC_PASSES) + $(GCC_FOR_TARGET) -c -o crti.o -x assembler $(srcdir)/config/sparc/sol2-ci.asm +crtn.o: $(srcdir)/config/sparc/sol2-cn.asm $(GCC_PASSES) + $(GCC_FOR_TARGET) -c -o crtn.o -x assembler $(srcdir)/config/sparc/sol2-cn.asm diff --git a/gcc/config/sparc/t-halos b/gcc/config/sparc/t-halos new file mode 100755 index 0000000..0bd5496 --- /dev/null +++ b/gcc/config/sparc/t-halos @@ -0,0 +1,2 @@ +# For a native HALOS compile, we need to set -e1 for the assembler +AS=as -e1 diff --git a/gcc/config/sparc/t-linux b/gcc/config/sparc/t-linux new file mode 100755 index 0000000..6e37a02 --- /dev/null +++ b/gcc/config/sparc/t-linux @@ -0,0 +1,6 @@ +LIBGCC1 = +CROSS_LIBGCC1 = + +# We don't want to build .umul, etc., because gnu/Linux provides them, +# which means that libgcc1-test will fail for cross-compiler. +LIBGCC1_TEST = diff --git a/gcc/config/sparc/t-linux64 b/gcc/config/sparc/t-linux64 new file mode 100755 index 0000000..077cf69 --- /dev/null +++ b/gcc/config/sparc/t-linux64 @@ -0,0 +1,21 @@ +MULTILIB_OPTIONS = m64/m32 +MULTILIB_DIRNAMES = 64 32 +MULTILIB_MATCHES = + +LIBGCC = stmp-multilib +INSTALL_LIBGCC = install-multilib + +EXTRA_MULTILIB_PARTS=crtbegin.o crtend.o crtbeginS.o crtendS.o + +tcrtbeginS.o: crtstuff.c $(GCC_PASSES) $(CONFIG_H) \ + defaults.h frame.h gbl-ctors.h + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) $(MULTILIB_CFLAGS) -g0 \ + -finhibit-size-directive -fno-inline-functions -fno-exceptions $(CRTSTUFF_T_CFLAGS_S) \ + -c $(srcdir)/crtstuff.c -DCRT_BEGIN -o tcrtbeginS$(objext) + +tcrtendS.o: crtstuff.c $(GCC_PASSES) $(CONFIG_H) \ + defaults.h frame.h gbl-ctors.h + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) $(MULTILIB_CFLAGS) -g0 \ + -finhibit-size-directive -fno-inline-functions -fno-exceptions $(CRTSTUFF_T_CFLAGS_S) \ + -c $(srcdir)/crtstuff.c -DCRT_END -o tcrtendS$(objext) + diff --git a/gcc/config/sparc/t-sol2 b/gcc/config/sparc/t-sol2 new file mode 100755 index 0000000..a9b6ee1 --- /dev/null +++ b/gcc/config/sparc/t-sol2 @@ -0,0 +1,30 @@ +# we need to supply our own assembly versions of libgcc1.c files, +# since the user may not have native 'cc' available + +LIBGCC1 = +CROSS_LIBGCC1 = +LIBGCC1_TEST = + +# gmon build rule: +$(T)gmon.o: $(srcdir)/config/sparc/gmon-sol2.c $(GCC_PASSES) $(CONFIG_H) stmp-int-hdrs + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) $(MULTILIB_CFLAGS) \ + -c $(srcdir)/config/sparc/gmon-sol2.c -o $(T)gmon.o + +# Assemble startup files. +$(T)crt1.o: $(srcdir)/config/sparc/sol2-c1.asm $(GCC_PASSES) + $(GCC_FOR_TARGET) $(MULTILIB_CFLAGS) -c -o $(T)crt1.o -x assembler-with-cpp $(srcdir)/config/sparc/sol2-c1.asm +$(T)crti.o: $(srcdir)/config/sparc/sol2-ci.asm $(GCC_PASSES) + $(GCC_FOR_TARGET) $(MULTILIB_CFLAGS) -c -o $(T)crti.o -x assembler-with-cpp $(srcdir)/config/sparc/sol2-ci.asm +$(T)crtn.o: $(srcdir)/config/sparc/sol2-cn.asm $(GCC_PASSES) + $(GCC_FOR_TARGET) $(MULTILIB_CFLAGS) -c -o $(T)crtn.o -x assembler-with-cpp $(srcdir)/config/sparc/sol2-cn.asm +$(T)gcrt1.o: $(srcdir)/config/sparc/sol2-c1.asm $(GCC_PASSES) + $(GCC_FOR_TARGET) $(MULTILIB_CFLAGS) -c -DGCRT1 -o $(T)gcrt1.o -x assembler-with-cpp $(srcdir)/config/sparc/sol2-c1.asm + +# We need to use -fPIC when we are using gcc to compile the routines in +# crtstuff.c. This is only really needed when we are going to use gcc/g++ +# to produce a shared library, but since we don't know ahead of time when +# we will be doing that, we just always use -fPIC when compiling the +# routines in crtstuff.c. + +CRTSTUFF_T_CFLAGS = -fPIC +TARGET_LIBGCC2_CFLAGS = -fPIC diff --git a/gcc/config/sparc/t-sol2-64 b/gcc/config/sparc/t-sol2-64 new file mode 100755 index 0000000..8d42c44 --- /dev/null +++ b/gcc/config/sparc/t-sol2-64 @@ -0,0 +1,8 @@ +MULTILIB_OPTIONS = m32/m64 +MULTILIB_DIRNAMES = sparcv7 sparcv9 +MULTILIB_MATCHES = + +LIBGCC = stmp-multilib +INSTALL_LIBGCC = install-multilib + +EXTRA_MULTILIB_PARTS=crtbegin.o crtend.o gmon.o crt1.o crti.o crtn.o gcrt1.o diff --git a/gcc/config/sparc/t-sp64 b/gcc/config/sparc/t-sp64 new file mode 100755 index 0000000..99acd5d --- /dev/null +++ b/gcc/config/sparc/t-sp64 @@ -0,0 +1,2 @@ +LIBGCC1 = +CROSS_LIBGCC1 = diff --git a/gcc/config/sparc/t-sp86x b/gcc/config/sparc/t-sp86x new file mode 100755 index 0000000..933fe09 --- /dev/null +++ b/gcc/config/sparc/t-sp86x @@ -0,0 +1,21 @@ +CROSS_LIBGCC1 = libgcc1-asm.a +LIB1ASMSRC = sparc/lb1spc.asm +LIB1ASMFUNCS = _divsi3 _modsi3 + +LIB2FUNCS_EXTRA = fp-bit.c dp-bit.c + +dp-bit.c: $(srcdir)/config/fp-bit.c + echo '#define US_SOFTWARE_GOFAST' > dp-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 + echo '#define US_SOFTWARE_GOFAST' >> fp-bit.c + cat $(srcdir)/config/fp-bit.c >> fp-bit.c + +MULTILIB_OPTIONS = mlittle-endian-data +MULTILIB_DIRNAMES = little + +LIBGCC = stmp-multilib +INSTALL_LIBGCC = install-multilib + diff --git a/gcc/config/sparc/t-sparcbare b/gcc/config/sparc/t-sparcbare new file mode 100755 index 0000000..8bd978b --- /dev/null +++ b/gcc/config/sparc/t-sparcbare @@ -0,0 +1,26 @@ +# configuration file for a bare sparc cpu + +CROSS_LIBGCC1 = libgcc1-asm.a +LIB1ASMSRC = sparc/lb1spc.asm +LIB1ASMFUNCS = _mulsi3 _divsi3 _modsi3 + +# These are really part of libgcc1, but this will cause them to be +# built correctly, so... + +LIB2FUNCS_EXTRA = fp-bit.c dp-bit.c + +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 + +# MULTILIB_OPTIONS should have msparclite too, but we'd have to make +# gas build... +MULTILIB_OPTIONS = msoft-float mcpu=v8 +MULTILIB_DIRNAMES = soft v8 +MULTILIB_MATCHES = msoft-float=mno-fpu mcpu?v8=mv8 + +LIBGCC = stmp-multilib +INSTALL_LIBGCC = install-multilib diff --git a/gcc/config/sparc/t-sparclite b/gcc/config/sparc/t-sparclite new file mode 100755 index 0000000..7cdfbb0 --- /dev/null +++ b/gcc/config/sparc/t-sparclite @@ -0,0 +1,24 @@ +CROSS_LIBGCC1 = libgcc1-asm.a +LIB1ASMSRC = sparc/lb1spl.asm +LIB1ASMFUNCS = _divsi3 _udivsi3 _modsi3 _umodsi3 + +# These are really part of libgcc1, but this will cause them to be +# built correctly, so... + +LIB2FUNCS_EXTRA = fp-bit.c dp-bit.c + +dp-bit.c: $(srcdir)/config/fp-bit.c + echo '#define US_SOFTWARE_GOFAST' > dp-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 + echo '#define US_SOFTWARE_GOFAST' >> fp-bit.c + cat $(srcdir)/config/fp-bit.c >> fp-bit.c + +MULTILIB_OPTIONS = mfpu mflat +MULTILIB_DIRNAMES = +MULTILIB_MATCHES = mfpu=mhard-float mfpu=mcpu?f934 + +LIBGCC = stmp-multilib +INSTALL_LIBGCC = install-multilib diff --git a/gcc/config/sparc/t-splet b/gcc/config/sparc/t-splet new file mode 100755 index 0000000..3329e0b --- /dev/null +++ b/gcc/config/sparc/t-splet @@ -0,0 +1,22 @@ +# configuration file for a bare sparclet cpu, aout format files + +CROSS_LIBGCC1 = libgcc1-asm.a +LIB1ASMSRC = sparc/lb1spc.asm +LIB1ASMFUNCS = _mulsi3 _divsi3 _modsi3 + +# These are really part of libgcc1, but this will cause them to be +# built correctly, so... + +LIB2FUNCS_EXTRA = fp-bit.c dp-bit.c + +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 + +MULTILIB_OPTIONS = mlittle-endian mflat +MULTILIB_DIRNAMES = little flat +LIBGCC = stmp-multilib +INSTALL_LIBGCC = install-multilib diff --git a/gcc/config/sparc/t-sunos40 b/gcc/config/sparc/t-sunos40 new file mode 100755 index 0000000..3e10575 --- /dev/null +++ b/gcc/config/sparc/t-sunos40 @@ -0,0 +1,7 @@ +# SunOS 4.0.* +# /bin/as doesn't recognize the v8 instructions, so we can't do a v8 +# multilib build. + +LIBGCC1 = +CROSS_LIBGCC1 = +LIBGCC1_TEST = diff --git a/gcc/config/sparc/t-sunos41 b/gcc/config/sparc/t-sunos41 new file mode 100755 index 0000000..5783d6a --- /dev/null +++ b/gcc/config/sparc/t-sunos41 @@ -0,0 +1,16 @@ +# SunOS 4.1.* + +LIBGCC1 = +CROSS_LIBGCC1 = +LIBGCC1_TEST = + +MULTILIB_OPTIONS = fpic/fPIC mcpu=v8 +MULTILIB_DIRNAMES = pic ucpic v8 +MULTILIB_MATCHES = mcpu?v8=mv8 + +LIBGCC = stmp-multilib +INSTALL_LIBGCC = install-multilib + +# The native linker doesn't handle linking -fpic code with -fPIC code. Ugh. +# We cope by building both variants of libgcc. +#TARGET_LIBGCC2_CFLAGS = -fPIC diff --git a/gcc/config/sparc/t-vxsparc b/gcc/config/sparc/t-vxsparc new file mode 100755 index 0000000..0c7a14a --- /dev/null +++ b/gcc/config/sparc/t-vxsparc @@ -0,0 +1,17 @@ +LIBGCC1 = +CROSS_LIBGCC1 = + +# We don't want to build .umul, etc., because VxWorks provides them, +# which means that libgcc1-test will fail. +LIBGCC1_TEST = + +# We don't want to put exit in libgcc.a for VxWorks, because VxWorks +# does not have _exit. +TARGET_LIBGCC2_CFLAGS = -Dexit=unused_exit + +MULTILIB_OPTIONS=msoft-float mv8 +MULTILIB_DIRNAMES=soft v8 +MULTILIB_MATCHES=msoft-float=mno-fpu + +LIBGCC = stmp-multilib +INSTALL_LIBGCC = install-multilib diff --git a/gcc/config/sparc/vxsim.h b/gcc/config/sparc/vxsim.h new file mode 100755 index 0000000..6c80375 --- /dev/null +++ b/gcc/config/sparc/vxsim.h @@ -0,0 +1,131 @@ +/* Definitions of target machine for GNU compiler, for SPARC VxSim + Copyright 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. */ + +/* Supposedly the same as vanilla sparc svr4, except for the stuff below: */ +#include "sparc/sysv4.h" + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES \ + "-DCPU=SIMSPARCSOLARIS -D__vxworks -D__vxworks__ -Dsparc -D__svr4__ -D__SVR4 \ + -Asystem(embedded) -Asystem(svr4) -Acpu(sparc) -Amachine(sparc)\ + -D__GCC_NEW_VARARGS__" + +#undef CPP_SPEC +#define CPP_SPEC "" + +#undef CC1_SPEC +#define CC1_SPEC "-fno-builtin %{sun4:} %{target:}" + +/* The sun bundled assembler doesn't accept -Yd, (and neither does gas). + It's safe to pass -s always, even if -g is not used. */ +#undef ASM_SPEC +#define ASM_SPEC \ + "%{V} %{v:%{!V:-V}} %{Qy:} %{!Qn:-Qy} %{n} %{T} %{Ym,*} %{Wa,*:%*} -s \ + %{fpic:-K PIC} %{fPIC:-K PIC}" + +/* However it appears that Solaris 2.0 uses the same reg numbering as + the old BSD-style system did. */ + +#undef DBX_REGISTER_NUMBER +/* Same as sparc.h */ +#define DBX_REGISTER_NUMBER(REGNO) (REGNO) + +/* We use stabs-in-elf for debugging, because that is what the native + toolchain uses. */ +#undef PREFERRED_DEBUGGING_TYPE +#define PREFERRED_DEBUGGING_TYPE DBX_DEBUG + +/* The Solaris 2 assembler uses .skip, not .zero, so put this back. */ +#undef ASM_OUTPUT_SKIP +#define ASM_OUTPUT_SKIP(FILE,SIZE) \ + fprintf (FILE, "\t.skip %u\n", (SIZE)) + +#undef ASM_OUTPUT_ALIGNED_LOCAL +#define ASM_OUTPUT_ALIGNED_LOCAL(FILE, NAME, SIZE, ALIGN) \ +do { \ + fputs ("\t.local\t", (FILE)); \ + assemble_name ((FILE), (NAME)); \ + putc ('\n', (FILE)); \ + ASM_OUTPUT_ALIGNED_COMMON (FILE, NAME, SIZE, ALIGN); \ +} while (0) + +#undef COMMON_ASM_OP +#define COMMON_ASM_OP "\t.common" + +/* This is how to output a definition of an internal numbered label where + PREFIX is the class of label and NUM is the number within the class. */ + +#undef ASM_OUTPUT_INTERNAL_LABEL +#define ASM_OUTPUT_INTERNAL_LABEL(FILE,PREFIX,NUM) \ + fprintf (FILE, ".L%s%d:\n", PREFIX, NUM) + +/* This is how to output a reference to an internal numbered label where + PREFIX is the class of label and NUM is the number within the class. */ + +#undef ASM_OUTPUT_INTERNAL_LABELREF +#define ASM_OUTPUT_INTERNAL_LABELREF(FILE,PREFIX,NUM) \ + fprintf (FILE, ".L%s%d", PREFIX, 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'. */ + +#undef ASM_GENERATE_INTERNAL_LABEL +#define ASM_GENERATE_INTERNAL_LABEL(LABEL,PREFIX,NUM) \ + sprintf (LABEL, "*.L%s%d", PREFIX, NUM) + + + +#undef LIB_SPEC +#define LIB_SPEC "" + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC "" + +#undef ENDFILE_SPEC +#define ENDFILE_SPEC "" + +#undef LINK_SPEC +#define LINK_SPEC "-r" + +/* This defines which switch letters take arguments. + It is as in svr4.h but with -R added. */ + +#undef SWITCH_TAKES_ARG +#define SWITCH_TAKES_ARG(CHAR) \ + ( (CHAR) == 'D' \ + || (CHAR) == 'U' \ + || (CHAR) == 'o' \ + || (CHAR) == 'e' \ + || (CHAR) == 'u' \ + || (CHAR) == 'I' \ + || (CHAR) == 'm' \ + || (CHAR) == 'L' \ + || (CHAR) == 'R' \ + || (CHAR) == 'A' \ + || (CHAR) == 'h' \ + || (CHAR) == 'z') + +/* ??? This does not work in SunOS 4.x, so it is not enabled in sparc.h. + Instead, it is enabled here, because it does work under Solaris. */ +/* Define for support of TFmode long double and REAL_ARITHMETIC. + Sparc ABI says that long double is 4 words. */ +#define LONG_DOUBLE_TYPE_SIZE 64 diff --git a/gcc/config/sparc/vxsparc.h b/gcc/config/sparc/vxsparc.h new file mode 100755 index 0000000..18ce6ed --- /dev/null +++ b/gcc/config/sparc/vxsparc.h @@ -0,0 +1,61 @@ +/* Definitions of target machine for GNU compiler. Vxworks SPARC version. + Copyright (C) 1994, 1996 Free Software Foundation, Inc. + Contributed by David Henkel-Wallace (gumby@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. */ + +#include "sparc/aout.h" + +/* Specify what to link with. */ +/* VxWorks does all the library stuff itself. */ + +#undef LIB_SPEC +#define LIB_SPEC "" + +/* Provide required defaults for linker -e. */ +#undef LINK_SPEC +#define LINK_SPEC "%{!nostdlib:%{!r*:%{!e*:-e start}}}" + +/* VxWorks provides the functionality of crt0.o and friends itself. */ +#undef STARTFILE_SPEC +#define STARTFILE_SPEC "" + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dsparc -Acpu(sparc) -Amachine(sparc)" + +/* Note that we define CPU here even if the user has specified -ansi. + This violates user namespace, but the VxWorks headers, and potentially + user code, all explicitly rely upon the definition of CPU in order to get + the proper processor information. */ +#undef CPP_SPEC +#define CPP_SPEC "%(cpp_cpu) -DCPU=SPARC" + +#undef PTRDIFF_TYPE +#undef SIZE_TYPE +#undef WCHAR_TYPE +#undef WCHAR_TYPE_SIZE + +#define PTRDIFF_TYPE "long int" +#define SIZE_TYPE "unsigned int" +#define WCHAR_TYPE "char" +#define WCHAR_TYPE_SIZE 8 + +/* US Software GOFAST library support. */ +#include "gofast.h" +#undef INIT_SUBTARGET_OPTABS +#define INIT_SUBTARGET_OPTABS INIT_GOFAST_OPTABS diff --git a/gcc/config/sparc/x-sysv4 b/gcc/config/sparc/x-sysv4 new file mode 100755 index 0000000..2a661e3 --- /dev/null +++ b/gcc/config/sparc/x-sysv4 @@ -0,0 +1,2 @@ +X_CFLAGS=-DSVR4 +ALLOCA=alloca.o diff --git a/gcc/config/sparc/xm-linux.h b/gcc/config/sparc/xm-linux.h new file mode 100755 index 0000000..691c7d1 --- /dev/null +++ b/gcc/config/sparc/xm-linux.h @@ -0,0 +1,26 @@ +/* Configuration for GCC for SPARC running Linux-based GNU systems. + Copyright (C) 1996, 1997 Free Software Foundation, Inc. + Contributed by Eddie C. Dost (ecd@skynet.be) + +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 inhibit_libc +#include <alloca.h> +#include <stdlib.h> +#include <string.h> +#endif diff --git a/gcc/config/sparc/xm-lynx.h b/gcc/config/sparc/xm-lynx.h new file mode 100755 index 0000000..90fef85 --- /dev/null +++ b/gcc/config/sparc/xm-lynx.h @@ -0,0 +1,39 @@ +/* Configuration for GNU C-compiler for sparc platforms running LynxOS. + 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. + +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 <xm-lynx.h> + +/* 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 + +#define HOST_WORDS_BIG_ENDIAN 1 + +/* Include <sys/wait.h> to define the exit status access macros. */ +#include <sys/types.h> +#include <sys/wait.h> + +/* target machine dependencies. + tm.h is a symbolic link to the actual target specific file. */ + +#include "tm.h" diff --git a/gcc/config/sparc/xm-netbsd.h b/gcc/config/sparc/xm-netbsd.h new file mode 100755 index 0000000..5f11b8d --- /dev/null +++ b/gcc/config/sparc/xm-netbsd.h @@ -0,0 +1,4 @@ +/* Configuration for GCC for Sun SPARC running NetBSD as host. */ + +#include <sparc/xm-sparc.h> +#include <xm-netbsd.h> diff --git a/gcc/config/sparc/xm-pbd.h b/gcc/config/sparc/xm-pbd.h new file mode 100755 index 0000000..1c3f475 --- /dev/null +++ b/gcc/config/sparc/xm-pbd.h @@ -0,0 +1,10 @@ +/* Host environment for the tti "Unicom" PBB 68020 boards */ + +#include "sparc/xm-sparc.h" + +#define USG + +#ifndef __GNUC__ +#define USE_C_ALLOCA +#endif + diff --git a/gcc/config/sparc/xm-sol2.h b/gcc/config/sparc/xm-sol2.h new file mode 100755 index 0000000..5613b08 --- /dev/null +++ b/gcc/config/sparc/xm-sol2.h @@ -0,0 +1,4 @@ +/* If not compiled with GNU C, include the system's <alloca.h> header. */ +#ifndef __GNUC__ +#include <alloca.h> +#endif diff --git a/gcc/config/sparc/xm-sp64.h b/gcc/config/sparc/xm-sp64.h new file mode 100755 index 0000000..b673161 --- /dev/null +++ b/gcc/config/sparc/xm-sp64.h @@ -0,0 +1,27 @@ +/* Configuration for GCC for Sparc v9 running 64-bit native. + Copyright (C) 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. */ + +#include <sparc/xm-sparc.h> + +/* This describes the machine the compiler is hosted on. */ +#if defined(__arch64__) || defined(__sparc_v9__) || defined(__sparcv9) +#undef HOST_BITS_PER_LONG +#define HOST_BITS_PER_LONG 64 +#endif diff --git a/gcc/config/sparc/xm-sparc.h b/gcc/config/sparc/xm-sparc.h new file mode 100755 index 0000000..e553a0d --- /dev/null +++ b/gcc/config/sparc/xm-sparc.h @@ -0,0 +1,49 @@ +/* Configuration for GNU C-compiler for Sun Sparc. + Copyright (C) 1988, 1993, 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. */ + + +/* #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 + +/* 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 + +/* If compiled with Sun CC, the use of alloca requires this #include. */ +#ifndef __GNUC__ +#include "alloca.h" +#endif diff --git a/gcc/config/sparc/xm-sysv4-64.h b/gcc/config/sparc/xm-sysv4-64.h new file mode 100755 index 0000000..c506d22 --- /dev/null +++ b/gcc/config/sparc/xm-sysv4-64.h @@ -0,0 +1,27 @@ +/* Configuration for GCC for Sparc v9 running 64-bit native. + 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. */ + +#include <sparc/xm-sysv4.h> + +/* This describes the machine the compiler is hosted on. */ +#if defined(__arch64__) || defined(__sparc_v9__) || defined(__sparcv9) +#undef HOST_BITS_PER_LONG +#define HOST_BITS_PER_LONG 64 +#endif diff --git a/gcc/config/sparc/xm-sysv4.h b/gcc/config/sparc/xm-sysv4.h new file mode 100755 index 0000000..6e663d1 --- /dev/null +++ b/gcc/config/sparc/xm-sysv4.h @@ -0,0 +1,48 @@ +/* Configuration for GNU C-compiler for Sun Sparc running System V.4. + Copyright (C) 1992, 1993, 1998 Free Software Foundation, Inc. + Contributed by Ron Guilmette (rfg@netcom.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. */ + + +/* #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 + +/* 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 + +#ifndef __GNUC__ +#define ONLY_INT_FIELDS +#endif |