diff options
| author | cvs2svn <cvs2svn@FreeBSD.org> | 2000-11-20 04:41:45 +0000 |
|---|---|---|
| committer | cvs2svn <cvs2svn@FreeBSD.org> | 2000-11-20 04:41:45 +0000 |
| commit | 0d497cf6e989f7498cf5036a29fe7003a89abf83 (patch) | |
| tree | 48a3225dfba4179815fb512c8ba7492fa4c83fbc /contrib | |
| parent | 94bb9d581468116c659950d31288ce30f661f3d4 (diff) | |
Diffstat (limited to 'contrib')
521 files changed, 84492 insertions, 1013 deletions
diff --git a/contrib/amd/FREEBSD-Xlist b/contrib/amd/FREEBSD-Xlist index 2cfa5cabcc8f..b178dea8277b 100644 --- a/contrib/amd/FREEBSD-Xlist +++ b/contrib/amd/FREEBSD-Xlist @@ -1,4 +1,4 @@ -$FreeBSD$ +$FreeBSD: src/contrib/amd/FREEBSD-Xlist,v 1.1 1999/09/18 09:25:20 obrien Exp $ *Makefile.* *README.autofs *aux diff --git a/contrib/amd/FREEBSD-upgrade b/contrib/amd/FREEBSD-upgrade index 618e962ca067..ecf348fda05e 100644 --- a/contrib/amd/FREEBSD-upgrade +++ b/contrib/amd/FREEBSD-upgrade @@ -1,5 +1,5 @@ # ex:ts=8 -$FreeBSD$ +$FreeBSD: src/contrib/amd/FREEBSD-upgrade,v 1.11 1999/11/05 11:59:47 obrien Exp $ AMD (am-utils) 6.0.3s1 originals can be found at: ftp://shekel.mcl.cs.columbia.edu/pub/am-utils/ diff --git a/contrib/amd/amd/amd.8 b/contrib/amd/amd/amd.8 index 8069df427ac2..b63d4fc0f4c2 100644 --- a/contrib/amd/amd/amd.8 +++ b/contrib/amd/amd/amd.8 @@ -39,7 +39,7 @@ .\" %W% (Berkeley) %G% .\" .\" $Id: amd.8,v 1.3 1999/09/30 21:01:29 ezk Exp $ -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/amd/amd/amd.8,v 1.6.2.5 2000/10/30 10:45:18 obrien Exp $ .\" .Dd April 19, 1994 .Dt AMD 8 diff --git a/contrib/amd/amd/amd.c b/contrib/amd/amd/amd.c index a89d8d427cd9..54d3fdcd7fcb 100644 --- a/contrib/amd/amd/amd.c +++ b/contrib/amd/amd/amd.c @@ -39,7 +39,7 @@ * %W% (Berkeley) %G% * * $Id: amd.c,v 1.6 1999/09/30 21:01:29 ezk Exp $ - * $FreeBSD$ + * $FreeBSD: src/contrib/amd/amd/amd.c,v 1.6.2.1 2000/09/20 02:17:04 jkh Exp $ * */ diff --git a/contrib/amd/amd/amq_subr.c b/contrib/amd/amd/amq_subr.c index 4fb0541772e0..50fd578ca409 100644 --- a/contrib/amd/amd/amq_subr.c +++ b/contrib/amd/amd/amq_subr.c @@ -39,7 +39,7 @@ * %W% (Berkeley) %G% * * $Id: amq_subr.c,v 1.5 1999/08/24 21:31:06 ezk Exp $ - * $FreeBSD$ + * $FreeBSD: src/contrib/amd/amd/amq_subr.c,v 1.5 1999/09/15 05:45:13 obrien Exp $ * */ /* diff --git a/contrib/amd/amd/get_args.c b/contrib/amd/amd/get_args.c index a5f5a4bfca90..5d29e8d89bbf 100644 --- a/contrib/amd/amd/get_args.c +++ b/contrib/amd/amd/get_args.c @@ -39,7 +39,7 @@ * %W% (Berkeley) %G% * * $Id: get_args.c,v 1.4 1999/09/30 21:01:31 ezk Exp $ - * $FreeBSD$ + * $FreeBSD: src/contrib/amd/amd/get_args.c,v 1.5 1999/11/05 11:58:05 obrien Exp $ * */ diff --git a/contrib/amd/amd/srvr_nfs.c b/contrib/amd/amd/srvr_nfs.c index b5425bf7a724..2129222852f9 100644 --- a/contrib/amd/amd/srvr_nfs.c +++ b/contrib/amd/amd/srvr_nfs.c @@ -39,7 +39,7 @@ * %W% (Berkeley) %G% * * $Id: srvr_nfs.c,v 1.5 1999/09/08 23:36:39 ezk Exp $ - * $FreeBSD$ + * $FreeBSD: src/contrib/amd/amd/srvr_nfs.c,v 1.4 1999/09/15 05:45:13 obrien Exp $ * */ diff --git a/contrib/amd/amq/amq.8 b/contrib/amd/amq/amq.8 index 37d978274a48..7f7d2677a989 100644 --- a/contrib/amd/amq/amq.8 +++ b/contrib/amd/amq/amq.8 @@ -39,7 +39,7 @@ .\" %W% (Berkeley) %G% .\" .\" $Id: amq.8,v 1.2 1999/01/10 21:53:58 ezk Exp $ -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/amd/amq/amq.8,v 1.5.2.1 2000/07/28 19:54:44 marko Exp $ .\" .Dd March 16, 1991 .Dt AMQ 8 diff --git a/contrib/amd/amq/amq.c b/contrib/amd/amq/amq.c index 98711ebe7392..22ec8547d282 100644 --- a/contrib/amd/amq/amq.c +++ b/contrib/amd/amq/amq.c @@ -39,7 +39,7 @@ * %W% (Berkeley) %G% * * $Id: amq.c,v 1.6 1999/09/08 23:36:40 ezk Exp $ - * $FreeBSD$ + * $FreeBSD: src/contrib/amd/amq/amq.c,v 1.5 1999/09/15 05:45:14 obrien Exp $ * */ diff --git a/contrib/amd/amq/pawd.1 b/contrib/amd/amq/pawd.1 index 6713907ac108..96315ddb29fa 100644 --- a/contrib/amd/amq/pawd.1 +++ b/contrib/amd/amq/pawd.1 @@ -39,7 +39,7 @@ .\" %W% (Berkeley) %G% .\" .\" $Id: pawd.1,v 1.2 1998/09/05 06:56:36 obrien Exp $ -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/amd/amq/pawd.1,v 1.4 1999/09/15 05:45:14 obrien Exp $ .\" .Dd January 6, 1998 .Dt PAWD 1 diff --git a/contrib/amd/conf/nfs_prot/nfs_prot_freebsd2.h b/contrib/amd/conf/nfs_prot/nfs_prot_freebsd2.h index e719e9985cd4..447143d1ff38 100644 --- a/contrib/amd/conf/nfs_prot/nfs_prot_freebsd2.h +++ b/contrib/amd/conf/nfs_prot/nfs_prot_freebsd2.h @@ -39,7 +39,7 @@ * %W% (Berkeley) %G% * * $Id: nfs_prot_freebsd2.h,v 1.2 1999/01/10 21:54:14 ezk Exp $ - * $FreeBSD$ + * $FreeBSD: src/contrib/amd/conf/nfs_prot/nfs_prot_freebsd2.h,v 1.4 1999/09/15 05:45:14 obrien Exp $ * */ diff --git a/contrib/amd/conf/nfs_prot/nfs_prot_freebsd3.h b/contrib/amd/conf/nfs_prot/nfs_prot_freebsd3.h index 29ce9496093e..9f7c1ca3e622 100644 --- a/contrib/amd/conf/nfs_prot/nfs_prot_freebsd3.h +++ b/contrib/amd/conf/nfs_prot/nfs_prot_freebsd3.h @@ -39,7 +39,7 @@ * %W% (Berkeley) %G% * * $Id: nfs_prot_freebsd3.h,v 1.4 1999/03/30 17:22:54 ezk Exp $ - * $FreeBSD$ + * $FreeBSD: src/contrib/amd/conf/nfs_prot/nfs_prot_freebsd3.h,v 1.7 1999/09/15 05:45:14 obrien Exp $ * */ diff --git a/contrib/amd/fixmount/fixmount.8 b/contrib/amd/fixmount/fixmount.8 index 14915affd7df..bba73cc9094e 100644 --- a/contrib/amd/fixmount/fixmount.8 +++ b/contrib/amd/fixmount/fixmount.8 @@ -39,7 +39,7 @@ .\" %W% (Berkeley) %G% .\" .\" $Id: fixmount.8,v 1.2 1999/01/10 21:54:26 ezk Exp $ -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/amd/fixmount/fixmount.8,v 1.5 1999/09/15 05:45:14 obrien Exp $ .\" .Dd February 26, 1993 .Dt FIXMOUNT 8 diff --git a/contrib/amd/fixmount/fixmount.c b/contrib/amd/fixmount/fixmount.c index 628e645fc0d0..8a8481111d6c 100644 --- a/contrib/amd/fixmount/fixmount.c +++ b/contrib/amd/fixmount/fixmount.c @@ -39,7 +39,7 @@ * %W% (Berkeley) %G% * * $Id: fixmount.c,v 1.4 1999/02/04 07:24:42 ezk Exp $ - * $FreeBSD$ + * $FreeBSD: src/contrib/amd/fixmount/fixmount.c,v 1.5 1999/09/15 05:45:14 obrien Exp $ * */ diff --git a/contrib/amd/fsinfo/fsinfo.8 b/contrib/amd/fsinfo/fsinfo.8 index 2bc2d51044c4..1f603638537e 100644 --- a/contrib/amd/fsinfo/fsinfo.8 +++ b/contrib/amd/fsinfo/fsinfo.8 @@ -34,7 +34,7 @@ .\" .\" from: @(#)fsinfo.8 8.1 (Berkeley) 6/28/93 .\" $Id: fsinfo.8,v 1.2 1999/01/10 21:54:28 ezk Exp $ -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/amd/fsinfo/fsinfo.8,v 1.4 1999/09/15 05:45:15 obrien Exp $ .\" .Dd June 26, 1999 .Dt FSINFO 8 diff --git a/contrib/amd/fsinfo/fsinfo.c b/contrib/amd/fsinfo/fsinfo.c index 111d205f63f3..35fe24cd7ffd 100644 --- a/contrib/amd/fsinfo/fsinfo.c +++ b/contrib/amd/fsinfo/fsinfo.c @@ -39,7 +39,7 @@ * %W% (Berkeley) %G% * * $Id: fsinfo.c,v 1.4 1999/02/04 07:24:44 ezk Exp $ - * $FreeBSD$ + * $FreeBSD: src/contrib/amd/fsinfo/fsinfo.c,v 1.4 1999/09/15 05:45:15 obrien Exp $ * */ diff --git a/contrib/amd/hlfsd/hlfsd.8 b/contrib/amd/hlfsd/hlfsd.8 index e98fbc9e0232..a054d30f2fc7 100644 --- a/contrib/amd/hlfsd/hlfsd.8 +++ b/contrib/amd/hlfsd/hlfsd.8 @@ -37,7 +37,7 @@ .\" SUCH DAMAGE. .\" .\" $Id: hlfsd.8,v 1.2 1999/01/10 21:54:32 ezk Exp $ -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/amd/hlfsd/hlfsd.8,v 1.5.2.2 2000/08/16 22:21:00 jhb Exp $ .\" .\" HLFSD was written at Columbia University Computer Science Department, by .\" Erez Zadok <ezk@cs.columbia.edu> and Alexander Dupuy <dupuy@smarts.com> diff --git a/contrib/amd/hlfsd/hlfsd.c b/contrib/amd/hlfsd/hlfsd.c index dc5b2ace0713..61f9dd0ccc85 100644 --- a/contrib/amd/hlfsd/hlfsd.c +++ b/contrib/amd/hlfsd/hlfsd.c @@ -39,7 +39,7 @@ * %W% (Berkeley) %G% * * $Id: hlfsd.c,v 1.5 1999/09/08 23:36:51 ezk Exp $ - * $FreeBSD$ + * $FreeBSD: src/contrib/amd/hlfsd/hlfsd.c,v 1.5 1999/09/15 05:45:15 obrien Exp $ * * HLFSD was written at Columbia University Computer Science Department, by * Erez Zadok <ezk@cs.columbia.edu> and Alexander Dupuy <dupuy@cs.columbia.edu> diff --git a/contrib/amd/hlfsd/homedir.c b/contrib/amd/hlfsd/homedir.c index 582acc831a9d..8abe9d606cd5 100644 --- a/contrib/amd/hlfsd/homedir.c +++ b/contrib/amd/hlfsd/homedir.c @@ -39,7 +39,7 @@ * %W% (Berkeley) %G% * * $Id: homedir.c,v 1.4 1999/01/13 23:31:19 ezk Exp $ - * $FreeBSD$ + * $FreeBSD: src/contrib/amd/hlfsd/homedir.c,v 1.4 1999/09/15 05:45:15 obrien Exp $ * * HLFSD was written at Columbia University Computer Science Department, by * Erez Zadok <ezk@cs.columbia.edu> and Alexander Dupuy <dupuy@cs.columbia.edu> diff --git a/contrib/amd/include/am_defs.h b/contrib/amd/include/am_defs.h index 36c695eada4c..854f8f291dd3 100644 --- a/contrib/amd/include/am_defs.h +++ b/contrib/amd/include/am_defs.h @@ -39,7 +39,7 @@ * %W% (Berkeley) %G% * * $Id: am_defs.h,v 1.11 1999/09/18 08:38:05 ezk Exp $ - * $FreeBSD$ + * $FreeBSD: src/contrib/amd/include/am_defs.h,v 1.8 1999/11/05 11:58:03 obrien Exp $ * */ diff --git a/contrib/amd/libamu/mount_fs.c b/contrib/amd/libamu/mount_fs.c index 1a4deeee07d5..21ba86cf26d9 100644 --- a/contrib/amd/libamu/mount_fs.c +++ b/contrib/amd/libamu/mount_fs.c @@ -39,7 +39,7 @@ * %W% (Berkeley) %G% * * $Id: mount_fs.c,v 1.8 1999/09/18 08:38:06 ezk Exp $ - * $FreeBSD$ + * $FreeBSD: src/contrib/amd/libamu/mount_fs.c,v 1.4 1999/09/23 05:36:01 obrien Exp $ * */ diff --git a/contrib/amd/mk-amd-map/mk-amd-map.8 b/contrib/amd/mk-amd-map/mk-amd-map.8 index 99f93e122be5..6e69aef6c7a2 100644 --- a/contrib/amd/mk-amd-map/mk-amd-map.8 +++ b/contrib/amd/mk-amd-map/mk-amd-map.8 @@ -34,7 +34,7 @@ .\" .\" from: @(#)mk-amd-map.8 8.1 (Berkeley) 6/28/93 .\" $Id: mk-amd-map.8,v 1.2 1999/01/10 21:54:41 ezk Exp $ -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/amd/mk-amd-map/mk-amd-map.8,v 1.3 1999/09/15 05:45:16 obrien Exp $ .\" .Dd June 28, 1993 .Dt MK-AMD-MAP 8 diff --git a/contrib/amd/mk-amd-map/mk-amd-map.c b/contrib/amd/mk-amd-map/mk-amd-map.c index 755d5bf22b4c..38760575d611 100644 --- a/contrib/amd/mk-amd-map/mk-amd-map.c +++ b/contrib/amd/mk-amd-map/mk-amd-map.c @@ -39,7 +39,7 @@ * %W% (Berkeley) %G% * * $Id: mk-amd-map.c,v 1.4 1999/02/04 07:24:50 ezk Exp $ - * $FreeBSD$ + * $FreeBSD: src/contrib/amd/mk-amd-map/mk-amd-map.c,v 1.7 1999/09/15 05:45:16 obrien Exp $ */ /* diff --git a/contrib/amd/scripts/amd.conf.5 b/contrib/amd/scripts/amd.conf.5 index d788c97a8569..9c15ab900672 100644 --- a/contrib/amd/scripts/amd.conf.5 +++ b/contrib/amd/scripts/amd.conf.5 @@ -39,7 +39,7 @@ .\" %W% (Berkeley) %G% .\" .\" $Id: amd.conf.5,v 1.3 1999/03/30 17:23:08 ezk Exp $ -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/amd/scripts/amd.conf.5,v 1.9.2.1 2000/05/09 14:12:51 sheldonh Exp $ .\" .Dd April 7, 1997 .Dt AMD.CONF 5 diff --git a/contrib/amd/wire-test/wire-test.8 b/contrib/amd/wire-test/wire-test.8 index dfb8a862a2bb..14a7b3fd7ff8 100644 --- a/contrib/amd/wire-test/wire-test.8 +++ b/contrib/amd/wire-test/wire-test.8 @@ -39,7 +39,7 @@ .\" %W% (Berkeley) %G% .\" .\" $Id: wire-test.8,v 1.2 1999/01/10 21:54:44 ezk Exp $ -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/amd/wire-test/wire-test.8,v 1.3 1999/09/15 05:45:17 obrien Exp $ .\" .Dd February 26, 1993 .Dt WIRE-TEST 8 diff --git a/contrib/amd/wire-test/wire-test.c b/contrib/amd/wire-test/wire-test.c index c55764707e15..d4ff1214631f 100644 --- a/contrib/amd/wire-test/wire-test.c +++ b/contrib/amd/wire-test/wire-test.c @@ -39,7 +39,7 @@ * %W% (Berkeley) %G% * * $Id: wire-test.c,v 1.4 1999/02/04 07:24:54 ezk Exp $ - * $FreeBSD$ + * $FreeBSD: src/contrib/amd/wire-test/wire-test.c,v 1.5 1999/09/15 05:45:17 obrien Exp $ * */ diff --git a/contrib/awk/FREEBSD-upgrade b/contrib/awk/FREEBSD-upgrade index 97e4270e182c..0340b3510c71 100644 --- a/contrib/awk/FREEBSD-upgrade +++ b/contrib/awk/FREEBSD-upgrade @@ -1,4 +1,4 @@ -$FreeBSD$ +$FreeBSD: src/contrib/awk/FREEBSD-upgrade,v 1.6 1999/09/27 09:09:08 sheldonh Exp $ Import of GNU awk 3.0.4 diff --git a/contrib/awk/awk.h b/contrib/awk/awk.h index 621a98109574..1a9486245511 100644 --- a/contrib/awk/awk.h +++ b/contrib/awk/awk.h @@ -22,7 +22,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $FreeBSD$ + * $FreeBSD: src/contrib/awk/awk.h,v 1.5 1999/09/27 08:56:57 sheldonh Exp $ */ /* ------------------------------ Includes ------------------------------ */ diff --git a/contrib/awk/builtin.c b/contrib/awk/builtin.c index 3de6f2ecddd0..20d6d64aa170 100644 --- a/contrib/awk/builtin.c +++ b/contrib/awk/builtin.c @@ -22,7 +22,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $FreeBSD$ + * $FreeBSD: src/contrib/awk/builtin.c,v 1.7 1999/12/23 18:24:31 sheldonh Exp $ */ diff --git a/contrib/awk/doc/awk.1 b/contrib/awk/doc/awk.1 index 807f79f156b9..3f52141b2c96 100644 --- a/contrib/awk/doc/awk.1 +++ b/contrib/awk/doc/awk.1 @@ -1,4 +1,4 @@ -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/awk/doc/awk.1,v 1.5 1999/09/27 08:57:04 sheldonh Exp $ .ds PX \s-1POSIX\s+1 .ds UX \s-1UNIX\s+1 .ds AN \s-1ANSI\s+1 diff --git a/contrib/awk/eval.c b/contrib/awk/eval.c index 37c5fef86789..37d738a2ea28 100644 --- a/contrib/awk/eval.c +++ b/contrib/awk/eval.c @@ -22,7 +22,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $FreeBSD$ + * $FreeBSD: src/contrib/awk/eval.c,v 1.4 1999/09/27 08:56:57 sheldonh Exp $ */ #include "awk.h" diff --git a/contrib/awk/field.c b/contrib/awk/field.c index 72cbd78e8c20..bba847aa64ab 100644 --- a/contrib/awk/field.c +++ b/contrib/awk/field.c @@ -22,7 +22,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $FreeBSD$ + * $FreeBSD: src/contrib/awk/field.c,v 1.4 1999/09/27 08:56:57 sheldonh Exp $ */ #include "awk.h" diff --git a/contrib/awk/io.c b/contrib/awk/io.c index 0a24f8af30ce..460928630b01 100644 --- a/contrib/awk/io.c +++ b/contrib/awk/io.c @@ -22,7 +22,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $FreeBSD$ + * $FreeBSD: src/contrib/awk/io.c,v 1.4 1999/09/27 08:56:57 sheldonh Exp $ */ #include "awk.h" diff --git a/contrib/awk/main.c b/contrib/awk/main.c index 27452f00aed3..bb36f71996a0 100644 --- a/contrib/awk/main.c +++ b/contrib/awk/main.c @@ -22,7 +22,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $FreeBSD$ + * $FreeBSD: src/contrib/awk/main.c,v 1.4 1999/09/27 08:56:57 sheldonh Exp $ */ #include "awk.h" diff --git a/contrib/awk/node.c b/contrib/awk/node.c index a8892a35678b..1762a4339f47 100644 --- a/contrib/awk/node.c +++ b/contrib/awk/node.c @@ -22,7 +22,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $FreeBSD$ + * $FreeBSD: src/contrib/awk/node.c,v 1.4 1999/09/27 08:56:57 sheldonh Exp $ */ #include "awk.h" diff --git a/contrib/bc/FREEBSD-upgrade b/contrib/bc/FREEBSD-upgrade index 6552a9c2b203..b6bfc7b51974 100644 --- a/contrib/bc/FREEBSD-upgrade +++ b/contrib/bc/FREEBSD-upgrade @@ -1,4 +1,4 @@ -$FreeBSD$ +$FreeBSD: src/contrib/bc/FREEBSD-upgrade,v 1.5 2000/01/16 10:22:31 obrien Exp $ bc 1.0.5a originals can be found at: ftp://prep.ai.mit.edu/pub/gnu/ diff --git a/contrib/bc/bc/main.c b/contrib/bc/bc/main.c index bd4efcb95913..14e81e51514d 100644 --- a/contrib/bc/bc/main.c +++ b/contrib/bc/bc/main.c @@ -24,7 +24,7 @@ Western Washington University Bellingham, WA 98226-9062 -$FreeBSD$ +$FreeBSD: src/contrib/bc/bc/main.c,v 1.4 2000/01/16 10:10:15 obrien Exp $ *************************************************************************/ diff --git a/contrib/bc/doc/dc.1 b/contrib/bc/doc/dc.1 index 856b6bb53c80..480a0d7c1c8a 100644 --- a/contrib/bc/doc/dc.1 +++ b/contrib/bc/doc/dc.1 @@ -18,7 +18,7 @@ .\" along with this program; see the file COPYING. If not, write to .\" the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. .\" -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/bc/doc/dc.1,v 1.3 2000/01/16 10:14:50 obrien Exp $ .\" .TH DC 1 "1997-03-25" "GNU Project" .ds dc \fIdc\fP diff --git a/contrib/bind/bin/named-xfer/named-xfer.c b/contrib/bind/bin/named-xfer/named-xfer.c index 79b79ca35e21..fc384ba6388b 100644 --- a/contrib/bind/bin/named-xfer/named-xfer.c +++ b/contrib/bind/bin/named-xfer/named-xfer.c @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/bind/bin/named-xfer/named-xfer.c,v 1.3.2.2 2000/11/02 14:07:49 asmodai Exp $ */ /* * The original version of named-xfer by Kevin Dunlap. * Completed and integrated with named by David Waitzman diff --git a/contrib/bind/bin/named/Makefile b/contrib/bind/bin/named/Makefile index 9288ad247d55..2c6bd77c5224 100644 --- a/contrib/bind/bin/named/Makefile +++ b/contrib/bind/bin/named/Makefile @@ -14,7 +14,7 @@ ## SOFTWARE. # $Id: Makefile,v 8.50 2000/07/11 06:41:32 vixie Exp $ -# $FreeBSD$ +# $FreeBSD: src/contrib/bind/bin/named/Makefile,v 1.3.2.2 2000/11/02 14:07:41 asmodai Exp $ DESTDIR= CC= cc diff --git a/contrib/bind/bin/nslookup/commands.l b/contrib/bind/bin/nslookup/commands.l index 991ed660d10f..9452137e61fa 100644 --- a/contrib/bind/bin/nslookup/commands.l +++ b/contrib/bind/bin/nslookup/commands.l @@ -1,5 +1,5 @@ %{ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/bind/bin/nslookup/commands.l,v 1.4.2.1 2000/07/03 22:47:07 ps Exp $ */ /* * Copyright (c) 1985 * The Regents of the University of California. All rights reserved. diff --git a/contrib/bind/bin/nslookup/debug.c b/contrib/bind/bin/nslookup/debug.c index 4e559543ffee..20c75c2d9b8f 100644 --- a/contrib/bind/bin/nslookup/debug.c +++ b/contrib/bind/bin/nslookup/debug.c @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/bind/bin/nslookup/debug.c,v 1.3.2.1 2000/11/02 14:07:55 asmodai Exp $ */ /* * Copyright (c) 1985, 1989 * The Regents of the University of California. All rights reserved. diff --git a/contrib/bind/bin/nslookup/getinfo.c b/contrib/bind/bin/nslookup/getinfo.c index 6d025d26b06b..b6ad2db368a7 100644 --- a/contrib/bind/bin/nslookup/getinfo.c +++ b/contrib/bind/bin/nslookup/getinfo.c @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/bind/bin/nslookup/getinfo.c,v 1.3.2.1 2000/11/02 14:07:55 asmodai Exp $ */ /* * Copyright (c) 1985, 1989 * The Regents of the University of California. All rights reserved. diff --git a/contrib/bind/bin/nslookup/main.c b/contrib/bind/bin/nslookup/main.c index c5ae700489d9..022998baf1e7 100644 --- a/contrib/bind/bin/nslookup/main.c +++ b/contrib/bind/bin/nslookup/main.c @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/bind/bin/nslookup/main.c,v 1.3.2.1 2000/07/03 22:47:07 ps Exp $ */ /* * Copyright (c) 1985, 1989 * The Regents of the University of California. All rights reserved. diff --git a/contrib/bind/doc/man/dig.1 b/contrib/bind/doc/man/dig.1 index 3e59b056c58c..7f9f1ff3488d 100644 --- a/contrib/bind/doc/man/dig.1 +++ b/contrib/bind/doc/man/dig.1 @@ -1,4 +1,4 @@ -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/bind/doc/man/dig.1,v 1.3 1999/11/30 02:52:54 peter Exp $ .\" $Id: dig.1,v 8.4 1999/10/15 21:29:58 vixie Exp $ .\" .\" ++Copyright++ 1993 diff --git a/contrib/bind/doc/man/host.1 b/contrib/bind/doc/man/host.1 index 17c78ff5ae37..596e88b2e66f 100644 --- a/contrib/bind/doc/man/host.1 +++ b/contrib/bind/doc/man/host.1 @@ -51,7 +51,7 @@ .\" - .\" --Copyright-- .\" $Id: host.1,v 8.4 2000/02/29 03:50:47 vixie Exp $ -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/bind/doc/man/host.1,v 1.3.2.1 2000/11/02 14:08:07 asmodai Exp $ .\" .Dd December 15, 1994 .Dt HOST @CMD_EXT_U@ diff --git a/contrib/bind/doc/man/nslookup.8 b/contrib/bind/doc/man/nslookup.8 index 95d5dce3c0cf..61dc5e56bd24 100644 --- a/contrib/bind/doc/man/nslookup.8 +++ b/contrib/bind/doc/man/nslookup.8 @@ -53,7 +53,7 @@ .\" --Copyright-- .\" .\" @(#)nslookup.8 5.3 (Berkeley) 6/24/90 -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/bind/doc/man/nslookup.8,v 1.2.2.1 2000/11/02 14:08:07 asmodai Exp $ .\" .Dd June 24, 1990 .Dt NSLOOKUP @SYS_OPS_EXT_U@ diff --git a/contrib/bind/port/freebsd/include/port_after.h b/contrib/bind/port/freebsd/include/port_after.h index 5203050dc8cb..6941fa74bfa1 100644 --- a/contrib/bind/port/freebsd/include/port_after.h +++ b/contrib/bind/port/freebsd/include/port_after.h @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/bind/port/freebsd/include/port_after.h,v 1.6.2.1 2000/07/03 22:47:21 ps Exp $ */ #ifndef PORT_AFTER_H #define PORT_AFTER_H #define USE_POSIX diff --git a/contrib/binutils/FREEBSD-upgrade b/contrib/binutils/FREEBSD-upgrade index 5f40eaf9b66a..a467acec3962 100644 --- a/contrib/binutils/FREEBSD-upgrade +++ b/contrib/binutils/FREEBSD-upgrade @@ -1,4 +1,4 @@ -$FreeBSD$ +$FreeBSD: src/contrib/binutils/FREEBSD-upgrade,v 1.2.2.2 2000/07/07 05:32:59 obrien Exp $ To get a copy of the Binutils source from the Sourceware CVS repository this command line was used: diff --git a/contrib/binutils/bfd/VERSION b/contrib/binutils/bfd/VERSION new file mode 100644 index 000000000000..dbe590065479 --- /dev/null +++ b/contrib/binutils/bfd/VERSION @@ -0,0 +1 @@ +2.8.1 diff --git a/contrib/binutils/bfd/acconfig.h b/contrib/binutils/bfd/acconfig.h new file mode 100644 index 000000000000..1d5e819ec24e --- /dev/null +++ b/contrib/binutils/bfd/acconfig.h @@ -0,0 +1,34 @@ + +/* Name of package. */ +#undef PACKAGE + +/* Version of package. */ +#undef VERSION + +/* Whether strstr must be declared even if <string.h> is included. */ +#undef NEED_DECLARATION_STRSTR + +/* Whether malloc must be declared even if <stdlib.h> is included. */ +#undef NEED_DECLARATION_MALLOC + +/* Whether realloc must be declared even if <stdlib.h> is included. */ +#undef NEED_DECLARATION_REALLOC + +/* Whether free must be declared even if <stdlib.h> is included. */ +#undef NEED_DECLARATION_FREE + +/* Whether getenv must be declared even if <stdlib.h> is included. */ +#undef NEED_DECLARATION_GETENV +@TOP@ + +/* Do we need to use the b modifier when opening binary files? */ +#undef USE_BINARY_FOPEN + +/* Name of host specific header file to include in trad-core.c. */ +#undef TRAD_HEADER + +/* Define only if <sys/procfs.h> is available *and* it defines prstatus_t. */ +#undef HAVE_SYS_PROCFS_H + +/* Do we really want to use mmap if it's available? */ +#undef USE_MMAP diff --git a/contrib/binutils/bfd/doc/bfd.texi b/contrib/binutils/bfd/doc/bfd.texi new file mode 100644 index 000000000000..ea0ca9e56dc9 --- /dev/null +++ b/contrib/binutils/bfd/doc/bfd.texi @@ -0,0 +1,585 @@ +@section @code{typedef bfd} +A BFD has type @code{bfd}; objects of this type are the +cornerstone of any application using BFD. Using BFD +consists of making references though the BFD and to data in the BFD. + +Here is the structure that defines the type @code{bfd}. It +contains the major data about the file and pointers +to the rest of the data. +@* +. +@example +struct _bfd +@{ + /* The filename the application opened the BFD with. */ + CONST char *filename; + + /* A pointer to the target jump table. */ + const struct bfd_target *xvec; + + /* To avoid dragging too many header files into every file that + includes `@code{bfd.h}', IOSTREAM has been declared as a "char + *", and MTIME as a "long". Their correct types, to which they + are cast when used, are "FILE *" and "time_t". The iostream + is the result of an fopen on the filename. However, if the + BFD_IN_MEMORY flag is set, then iostream is actually a pointer + to a bfd_in_memory struct. */ + PTR iostream; + + /* Is the file descriptor being cached? That is, can it be closed as + needed, and re-opened when accessed later? */ + + boolean cacheable; + + /* Marks whether there was a default target specified when the + BFD was opened. This is used to select which matching algorithm + to use to choose the back end. */ + + boolean target_defaulted; + + /* The caching routines use these to maintain a + least-recently-used list of BFDs */ + + struct _bfd *lru_prev, *lru_next; + + /* When a file is closed by the caching routines, BFD retains + state information on the file here: */ + + file_ptr where; + + /* and here: (``once'' means at least once) */ + + boolean opened_once; + + /* Set if we have a locally maintained mtime value, rather than + getting it from the file each time: */ + + boolean mtime_set; + + /* File modified time, if mtime_set is true: */ + + long mtime; + + /* Reserved for an unimplemented file locking extension.*/ + + int ifd; + + /* The format which belongs to the BFD. (object, core, etc.) */ + + bfd_format format; + + /* The direction the BFD was opened with*/ + + enum bfd_direction @{no_direction = 0, + read_direction = 1, + write_direction = 2, + both_direction = 3@} direction; + + /* Format_specific flags*/ + + flagword flags; + + /* Currently my_archive is tested before adding origin to + anything. I believe that this can become always an add of + origin, with origin set to 0 for non archive files. */ + + file_ptr origin; + + /* Remember when output has begun, to stop strange things + from happening. */ + boolean output_has_begun; + + /* Pointer to linked list of sections*/ + struct sec *sections; + + /* The number of sections */ + unsigned int section_count; + + /* Stuff only useful for object files: + The start address. */ + bfd_vma start_address; + + /* Used for input and output*/ + unsigned int symcount; + + /* Symbol table for output BFD (with symcount entries) */ + struct symbol_cache_entry **outsymbols; + + /* Pointer to structure which contains architecture information*/ + const struct bfd_arch_info *arch_info; + + /* Stuff only useful for archives:*/ + PTR arelt_data; + struct _bfd *my_archive; /* The containing archive BFD. */ + struct _bfd *next; /* The next BFD in the archive. */ + struct _bfd *archive_head; /* The first BFD in the archive. */ + boolean has_armap; + + /* A chain of BFD structures involved in a link. */ + struct _bfd *link_next; + + /* A field used by _bfd_generic_link_add_archive_symbols. This will + be used only for archive elements. */ + int archive_pass; + + /* Used by the back end to hold private data. */ + + union + @{ + struct aout_data_struct *aout_data; + struct artdata *aout_ar_data; + struct _oasys_data *oasys_obj_data; + struct _oasys_ar_data *oasys_ar_data; + struct coff_tdata *coff_obj_data; + struct pe_tdata *pe_obj_data; + struct xcoff_tdata *xcoff_obj_data; + struct ecoff_tdata *ecoff_obj_data; + struct ieee_data_struct *ieee_data; + struct ieee_ar_data_struct *ieee_ar_data; + struct srec_data_struct *srec_data; + struct ihex_data_struct *ihex_data; + struct tekhex_data_struct *tekhex_data; + struct elf_obj_tdata *elf_obj_data; + struct nlm_obj_tdata *nlm_obj_data; + struct bout_data_struct *bout_data; + struct sun_core_struct *sun_core_data; + struct trad_core_struct *trad_core_data; + struct som_data_struct *som_data; + struct hpux_core_struct *hpux_core_data; + struct hppabsd_core_struct *hppabsd_core_data; + struct sgi_core_struct *sgi_core_data; + struct lynx_core_struct *lynx_core_data; + struct osf_core_struct *osf_core_data; + struct cisco_core_struct *cisco_core_data; + struct versados_data_struct *versados_data; + struct netbsd_core_struct *netbsd_core_data; + PTR any; + @} tdata; + + /* Used by the application to hold private data*/ + PTR usrdata; + + /* Where all the allocated stuff under this BFD goes. This is a + struct objalloc *, but we use PTR to avoid requiring the inclusion of + objalloc.h. */ + PTR memory; +@}; + +@end example +@section Error reporting +Most BFD functions return nonzero on success (check their +individual documentation for precise semantics). On an error, +they call @code{bfd_set_error} to set an error condition that callers +can check by calling @code{bfd_get_error}. +If that returns @code{bfd_error_system_call}, then check +@code{errno}. + +The easiest way to report a BFD error to the user is to +use @code{bfd_perror}. +@* +@subsection Type @code{bfd_error_type} +The values returned by @code{bfd_get_error} are defined by the +enumerated type @code{bfd_error_type}. +@* +. +@example +typedef enum bfd_error +@{ + bfd_error_no_error = 0, + bfd_error_system_call, + bfd_error_invalid_target, + bfd_error_wrong_format, + bfd_error_invalid_operation, + bfd_error_no_memory, + bfd_error_no_symbols, + bfd_error_no_armap, + bfd_error_no_more_archived_files, + bfd_error_malformed_archive, + bfd_error_file_not_recognized, + bfd_error_file_ambiguously_recognized, + bfd_error_no_contents, + bfd_error_nonrepresentable_section, + bfd_error_no_debug_section, + bfd_error_bad_value, + bfd_error_file_truncated, + bfd_error_file_too_big, + bfd_error_invalid_error_code +@} bfd_error_type; + +@end example +@findex bfd_get_error +@subsubsection @code{bfd_get_error} +@strong{Synopsis} +@example +bfd_error_type bfd_get_error (void); +@end example +@strong{Description}@* +Return the current BFD error condition. +@* +@findex bfd_set_error +@subsubsection @code{bfd_set_error} +@strong{Synopsis} +@example +void bfd_set_error (bfd_error_type error_tag); +@end example +@strong{Description}@* +Set the BFD error condition to be @var{error_tag}. +@* +@findex bfd_errmsg +@subsubsection @code{bfd_errmsg} +@strong{Synopsis} +@example +CONST char *bfd_errmsg (bfd_error_type error_tag); +@end example +@strong{Description}@* +Return a string describing the error @var{error_tag}, or +the system error if @var{error_tag} is @code{bfd_error_system_call}. +@* +@findex bfd_perror +@subsubsection @code{bfd_perror} +@strong{Synopsis} +@example +void bfd_perror (CONST char *message); +@end example +@strong{Description}@* +Print to the standard error stream a string describing the +last BFD error that occurred, or the last system error if +the last BFD error was a system call failure. If @var{message} +is non-NULL and non-empty, the error string printed is preceded +by @var{message}, a colon, and a space. It is followed by a newline. +@* +@subsection BFD error handler +Some BFD functions want to print messages describing the +problem. They call a BFD error handler function. This +function may be overriden by the program. + +The BFD error handler acts like printf. +@* +. +@example +typedef void (*bfd_error_handler_type) PARAMS ((const char *, ...)); + +@end example +@findex bfd_set_error_handler +@subsubsection @code{bfd_set_error_handler} +@strong{Synopsis} +@example +bfd_error_handler_type bfd_set_error_handler (bfd_error_handler_type); +@end example +@strong{Description}@* +Set the BFD error handler function. Returns the previous +function. +@* +@findex bfd_set_error_program_name +@subsubsection @code{bfd_set_error_program_name} +@strong{Synopsis} +@example +void bfd_set_error_program_name (const char *); +@end example +@strong{Description}@* +Set the program name to use when printing a BFD error. This +is printed before the error message followed by a colon and +space. The string must not be changed after it is passed to +this function. +@* +@section Symbols + +@* +@findex bfd_get_reloc_upper_bound +@subsubsection @code{bfd_get_reloc_upper_bound} +@strong{Synopsis} +@example +long bfd_get_reloc_upper_bound(bfd *abfd, asection *sect); +@end example +@strong{Description}@* +Return the number of bytes required to store the +relocation information associated with section @var{sect} +attached to bfd @var{abfd}. If an error occurs, return -1. +@* +@findex bfd_canonicalize_reloc +@subsubsection @code{bfd_canonicalize_reloc} +@strong{Synopsis} +@example +long bfd_canonicalize_reloc + (bfd *abfd, + asection *sec, + arelent **loc, + asymbol **syms); +@end example +@strong{Description}@* +Call the back end associated with the open BFD +@var{abfd} and translate the external form of the relocation +information attached to @var{sec} into the internal canonical +form. Place the table into memory at @var{loc}, which has +been preallocated, usually by a call to +@code{bfd_get_reloc_upper_bound}. Returns the number of relocs, or +-1 on error. + +The @var{syms} table is also needed for horrible internal magic +reasons. +@* +@findex bfd_set_reloc +@subsubsection @code{bfd_set_reloc} +@strong{Synopsis} +@example +void bfd_set_reloc + (bfd *abfd, asection *sec, arelent **rel, unsigned int count) +@end example +@strong{Description}@* +Set the relocation pointer and count within +section @var{sec} to the values @var{rel} and @var{count}. +The argument @var{abfd} is ignored. +@* +@findex bfd_set_file_flags +@subsubsection @code{bfd_set_file_flags} +@strong{Synopsis} +@example +boolean bfd_set_file_flags(bfd *abfd, flagword flags); +@end example +@strong{Description}@* +Set the flag word in the BFD @var{abfd} to the value @var{flags}. + +Possible errors are: +@itemize @bullet + +@item +@code{bfd_error_wrong_format} - The target bfd was not of object format. +@item +@code{bfd_error_invalid_operation} - The target bfd was open for reading. +@item +@code{bfd_error_invalid_operation} - +The flag word contained a bit which was not applicable to the +type of file. E.g., an attempt was made to set the @code{D_PAGED} bit +on a BFD format which does not support demand paging. +@end itemize +@* +@findex bfd_set_start_address +@subsubsection @code{bfd_set_start_address} +@strong{Synopsis} +@example +boolean bfd_set_start_address(bfd *abfd, bfd_vma vma); +@end example +@strong{Description}@* +Make @var{vma} the entry point of output BFD @var{abfd}. +@* +@strong{Returns}@* +Returns @code{true} on success, @code{false} otherwise. +@* +@findex bfd_get_mtime +@subsubsection @code{bfd_get_mtime} +@strong{Synopsis} +@example +long bfd_get_mtime(bfd *abfd); +@end example +@strong{Description}@* +Return the file modification time (as read from the file system, or +from the archive header for archive members). +@* +@findex bfd_get_size +@subsubsection @code{bfd_get_size} +@strong{Synopsis} +@example +long bfd_get_size(bfd *abfd); +@end example +@strong{Description}@* +Return the file size (as read from file system) for the file +associated with BFD @var{abfd}. + +The initial motivation for, and use of, this routine is not +so we can get the exact size of the object the BFD applies to, since +that might not be generally possible (archive members for example). +It would be ideal if someone could eventually modify +it so that such results were guaranteed. + +Instead, we want to ask questions like "is this NNN byte sized +object I'm about to try read from file offset YYY reasonable?" +As as example of where we might do this, some object formats +use string tables for which the first @code{sizeof(long)} bytes of the +table contain the size of the table itself, including the size bytes. +If an application tries to read what it thinks is one of these +string tables, without some way to validate the size, and for +some reason the size is wrong (byte swapping error, wrong location +for the string table, etc.), the only clue is likely to be a read +error when it tries to read the table, or a "virtual memory +exhausted" error when it tries to allocate 15 bazillon bytes +of space for the 15 bazillon byte table it is about to read. +This function at least allows us to answer the quesion, "is the +size reasonable?". +@* +@findex bfd_get_gp_size +@subsubsection @code{bfd_get_gp_size} +@strong{Synopsis} +@example +int bfd_get_gp_size(bfd *abfd); +@end example +@strong{Description}@* +Return the maximum size of objects to be optimized using the GP +register under MIPS ECOFF. This is typically set by the @code{-G} +argument to the compiler, assembler or linker. +@* +@findex bfd_set_gp_size +@subsubsection @code{bfd_set_gp_size} +@strong{Synopsis} +@example +void bfd_set_gp_size(bfd *abfd, int i); +@end example +@strong{Description}@* +Set the maximum size of objects to be optimized using the GP +register under ECOFF or MIPS ELF. This is typically set by +the @code{-G} argument to the compiler, assembler or linker. +@* +@findex bfd_scan_vma +@subsubsection @code{bfd_scan_vma} +@strong{Synopsis} +@example +bfd_vma bfd_scan_vma(CONST char *string, CONST char **end, int base); +@end example +@strong{Description}@* +Convert, like @code{strtoul}, a numerical expression +@var{string} into a @code{bfd_vma} integer, and return that integer. +(Though without as many bells and whistles as @code{strtoul}.) +The expression is assumed to be unsigned (i.e., positive). +If given a @var{base}, it is used as the base for conversion. +A base of 0 causes the function to interpret the string +in hex if a leading "0x" or "0X" is found, otherwise +in octal if a leading zero is found, otherwise in decimal. + +Overflow is not detected. +@* +@findex bfd_copy_private_bfd_data +@subsubsection @code{bfd_copy_private_bfd_data} +@strong{Synopsis} +@example +boolean bfd_copy_private_bfd_data(bfd *ibfd, bfd *obfd); +@end example +@strong{Description}@* +Copy private BFD information from the BFD @var{ibfd} to the +the BFD @var{obfd}. Return @code{true} on success, @code{false} on error. +Possible error returns are: + +@itemize @bullet + +@item +@code{bfd_error_no_memory} - +Not enough memory exists to create private data for @var{obfd}. +@end itemize +@example +#define bfd_copy_private_bfd_data(ibfd, obfd) \ + BFD_SEND (obfd, _bfd_copy_private_bfd_data, \ + (ibfd, obfd)) +@end example +@* +@findex bfd_merge_private_bfd_data +@subsubsection @code{bfd_merge_private_bfd_data} +@strong{Synopsis} +@example +boolean bfd_merge_private_bfd_data(bfd *ibfd, bfd *obfd); +@end example +@strong{Description}@* +Merge private BFD information from the BFD @var{ibfd} to the +the output file BFD @var{obfd} when linking. Return @code{true} +on success, @code{false} on error. Possible error returns are: + +@itemize @bullet + +@item +@code{bfd_error_no_memory} - +Not enough memory exists to create private data for @var{obfd}. +@end itemize +@example +#define bfd_merge_private_bfd_data(ibfd, obfd) \ + BFD_SEND (obfd, _bfd_merge_private_bfd_data, \ + (ibfd, obfd)) +@end example +@* +@findex bfd_set_private_flags +@subsubsection @code{bfd_set_private_flags} +@strong{Synopsis} +@example +boolean bfd_set_private_flags(bfd *abfd, flagword flags); +@end example +@strong{Description}@* +Set private BFD flag information in the BFD @var{abfd}. +Return @code{true} on success, @code{false} on error. Possible error +returns are: + +@itemize @bullet + +@item +@code{bfd_error_no_memory} - +Not enough memory exists to create private data for @var{obfd}. +@end itemize +@example +#define bfd_set_private_flags(abfd, flags) \ + BFD_SEND (abfd, _bfd_set_private_flags, \ + (abfd, flags)) +@end example +@* +@findex stuff +@subsubsection @code{stuff} +@strong{Description}@* +Stuff which should be documented: +@example +#define bfd_sizeof_headers(abfd, reloc) \ + BFD_SEND (abfd, _bfd_sizeof_headers, (abfd, reloc)) + +#define bfd_find_nearest_line(abfd, sec, syms, off, file, func, line) \ + BFD_SEND (abfd, _bfd_find_nearest_line, (abfd, sec, syms, off, file, func, line)) + + /* Do these three do anything useful at all, for any back end? */ +#define bfd_debug_info_start(abfd) \ + BFD_SEND (abfd, _bfd_debug_info_start, (abfd)) + +#define bfd_debug_info_end(abfd) \ + BFD_SEND (abfd, _bfd_debug_info_end, (abfd)) + +#define bfd_debug_info_accumulate(abfd, section) \ + BFD_SEND (abfd, _bfd_debug_info_accumulate, (abfd, section)) + + +#define bfd_stat_arch_elt(abfd, stat) \ + BFD_SEND (abfd, _bfd_stat_arch_elt,(abfd, stat)) + +#define bfd_update_armap_timestamp(abfd) \ + BFD_SEND (abfd, _bfd_update_armap_timestamp, (abfd)) + +#define bfd_set_arch_mach(abfd, arch, mach)\ + BFD_SEND ( abfd, _bfd_set_arch_mach, (abfd, arch, mach)) + +#define bfd_relax_section(abfd, section, link_info, again) \ + BFD_SEND (abfd, _bfd_relax_section, (abfd, section, link_info, again)) + +#define bfd_link_hash_table_create(abfd) \ + BFD_SEND (abfd, _bfd_link_hash_table_create, (abfd)) + +#define bfd_link_add_symbols(abfd, info) \ + BFD_SEND (abfd, _bfd_link_add_symbols, (abfd, info)) + +#define bfd_final_link(abfd, info) \ + BFD_SEND (abfd, _bfd_final_link, (abfd, info)) + +#define bfd_free_cached_info(abfd) \ + BFD_SEND (abfd, _bfd_free_cached_info, (abfd)) + +#define bfd_get_dynamic_symtab_upper_bound(abfd) \ + BFD_SEND (abfd, _bfd_get_dynamic_symtab_upper_bound, (abfd)) + +#define bfd_print_private_bfd_data(abfd, file)\ + BFD_SEND (abfd, _bfd_print_private_bfd_data, (abfd, file)) + +#define bfd_canonicalize_dynamic_symtab(abfd, asymbols) \ + BFD_SEND (abfd, _bfd_canonicalize_dynamic_symtab, (abfd, asymbols)) + +#define bfd_get_dynamic_reloc_upper_bound(abfd) \ + BFD_SEND (abfd, _bfd_get_dynamic_reloc_upper_bound, (abfd)) + +#define bfd_canonicalize_dynamic_reloc(abfd, arels, asyms) \ + BFD_SEND (abfd, _bfd_canonicalize_dynamic_reloc, (abfd, arels, asyms)) + +extern bfd_byte *bfd_get_relocated_section_contents + PARAMS ((bfd *, struct bfd_link_info *, + struct bfd_link_order *, bfd_byte *, + boolean, asymbol **)); + +@end example +@* diff --git a/contrib/binutils/bfd/elf.c b/contrib/binutils/bfd/elf.c index 25080502de9c..7c6027f8e25d 100644 --- a/contrib/binutils/bfd/elf.c +++ b/contrib/binutils/bfd/elf.c @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/binutils/bfd/elf.c,v 1.3.6.3 2000/07/07 05:33:09 obrien Exp $ */ /* diff --git a/contrib/binutils/bfd/elf32-i386.c b/contrib/binutils/bfd/elf32-i386.c index f0f45cb8041f..0e6bc4b93065 100644 --- a/contrib/binutils/bfd/elf32-i386.c +++ b/contrib/binutils/bfd/elf32-i386.c @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/binutils/bfd/elf32-i386.c,v 1.4.2.4 2000/10/25 19:28:38 obrien Exp $ */ #include "bfd.h" #include "sysdep.h" diff --git a/contrib/binutils/bfd/elf64-alpha.c b/contrib/binutils/bfd/elf64-alpha.c index 972450becdaf..a6b799a3e061 100644 --- a/contrib/binutils/bfd/elf64-alpha.c +++ b/contrib/binutils/bfd/elf64-alpha.c @@ -18,7 +18,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/binutils/bfd/elf64-alpha.c,v 1.3.2.2 2000/07/07 05:33:12 obrien Exp $ */ /* We need a published ABI spec for this. Until one comes out, don't assume this'll remain unchanged forever. */ diff --git a/contrib/binutils/bfd/elflink.h b/contrib/binutils/bfd/elflink.h index ef9276862210..d16c0c41fb27 100644 --- a/contrib/binutils/bfd/elflink.h +++ b/contrib/binutils/bfd/elflink.h @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/binutils/bfd/elflink.h,v 1.2.6.2 2000/07/07 05:33:13 obrien Exp $ */ /* ELF linker code. */ diff --git a/contrib/binutils/bfd/freebsd.h b/contrib/binutils/bfd/freebsd.h index ffe7f2a85e06..886b3a173055 100644 --- a/contrib/binutils/bfd/freebsd.h +++ b/contrib/binutils/bfd/freebsd.h @@ -18,7 +18,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/binutils/bfd/freebsd.h,v 1.2.6.2 2000/07/07 05:33:14 obrien Exp $ */ /* FreeBSD QMAGIC files have the header in the text. */ #define N_HEADER_IN_TEXT(x) 1 diff --git a/contrib/binutils/bfd/hosts/mipsbsd.h b/contrib/binutils/bfd/hosts/mipsbsd.h new file mode 100644 index 000000000000..a2fad21fcf7a --- /dev/null +++ b/contrib/binutils/bfd/hosts/mipsbsd.h @@ -0,0 +1,12 @@ +#include <machine/param.h> +#include <machine/vmparam.h> +#undef ALIGN + +#define HOST_PAGE_SIZE NBPG +/* #define HOST_SEGMENT_SIZE NBPG -- we use HOST_DATA_START_ADDR */ +#define HOST_MACHINE_ARCH bfd_arch_mips +/* #define HOST_MACHINE_MACHINE */ + +#define HOST_TEXT_START_ADDR USRTEXT +#define HOST_STACK_END_ADDR USRSTACK +#define NO_CORE_COMMAND diff --git a/contrib/binutils/binutils/nm.c b/contrib/binutils/binutils/nm.c index b97c538be04b..edf2a59acaaf 100644 --- a/contrib/binutils/binutils/nm.c +++ b/contrib/binutils/binutils/nm.c @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/binutils/binutils/nm.c,v 1.3.6.3 2000/07/07 05:33:30 obrien Exp $ */ #include "bfd.h" #include "progress.h" diff --git a/contrib/binutils/binutils/objcopy.1 b/contrib/binutils/binutils/objcopy.1 index aa98278920cc..676fb1b3432b 100644 --- a/contrib/binutils/binutils/objcopy.1 +++ b/contrib/binutils/binutils/objcopy.1 @@ -1,6 +1,6 @@ .\" Copyright (c) 1991, 93, 94, 95, 96, 97, 98, 99, 2000 Free Software Foundation .\" See section COPYING for conditions for redistribution -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/binutils/binutils/objcopy.1,v 1.2.6.2 2000/07/07 05:33:30 obrien Exp $ .TH objcopy 1 "05 April 2000" "Cygnus Solutions" "GNU Development Tools" .de BP .sp diff --git a/contrib/binutils/binutils/objdump.1 b/contrib/binutils/binutils/objdump.1 index 0894119dbd58..29e114eeed1d 100644 --- a/contrib/binutils/binutils/objdump.1 +++ b/contrib/binutils/binutils/objdump.1 @@ -1,6 +1,6 @@ .\" Copyright (c) 1991, 92, 93, 94, 95, 96, 97, 1998 Free Software Foundation .\" See section COPYING for conditions for redistribution -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/binutils/binutils/objdump.1,v 1.3.6.2 2000/07/07 05:33:31 obrien Exp $ .TH objdump 1 "5 November 1991" "cygnus support" "GNU Development Tools" .de BP .sp diff --git a/contrib/binutils/binutils/strings.c b/contrib/binutils/binutils/strings.c index 8bc618aa2e50..3d2b8026f98d 100644 --- a/contrib/binutils/binutils/strings.c +++ b/contrib/binutils/binutils/strings.c @@ -17,7 +17,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/binutils/binutils/strings.c,v 1.2.6.2 2000/07/07 05:33:33 obrien Exp $ */ /* Usage: strings [options] file... diff --git a/contrib/binutils/binutils/strip.1 b/contrib/binutils/binutils/strip.1 index b3f6c84f8ecd..b47d14c6aa5d 100644 --- a/contrib/binutils/binutils/strip.1 +++ b/contrib/binutils/binutils/strip.1 @@ -1,6 +1,6 @@ .\" Copyright (c) 1991, 92, 93, 94, 95, 96, 97, 1998 Free Software Foundation .\" See section COPYING for conditions for redistribution -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/binutils/binutils/strip.1,v 1.2.6.2 2000/07/07 05:33:33 obrien Exp $ .TH strip 1 "5 November 1991" "cygnus support" "GNU Development Tools" .de BP .sp diff --git a/contrib/binutils/gas/conf.in b/contrib/binutils/gas/conf.in new file mode 100644 index 000000000000..d56807cd88d5 --- /dev/null +++ b/contrib/binutils/gas/conf.in @@ -0,0 +1,127 @@ +/* conf.in. Generated automatically from configure.in by autoheader. */ + +/* Define if using alloca.c. */ +#undef C_ALLOCA + +/* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems. + This function is required for alloca.c support on those systems. */ +#undef CRAY_STACKSEG_END + +/* Define if you have alloca, as a function or macro. */ +#undef HAVE_ALLOCA + +/* Define if you have <alloca.h> and it should be used (not on Ultrix). */ +#undef HAVE_ALLOCA_H + +/* Define as __inline if that's what the C compiler calls it. */ +#undef inline + +/* If using the C implementation of alloca, define if you know the + direction of stack growth for your system; otherwise it will be + automatically deduced at run-time. + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown + */ +#undef STACK_DIRECTION + +/* Should gas use high-level BFD interfaces? */ +#undef BFD_ASSEMBLER + +/* Some assert/preprocessor combinations are incapable of handling + certain kinds of constructs in the argument of assert. For example, + quoted strings (if requoting isn't done right) or newlines. */ +#undef BROKEN_ASSERT + +/* If we aren't doing cross-assembling, some operations can be optimized, + since byte orders and value sizes don't need to be adjusted. */ +#undef CROSS_COMPILE + +/* Some gas code wants to know these parameters. */ +#undef TARGET_ALIAS +#undef TARGET_CPU +#undef TARGET_CANONICAL +#undef TARGET_OS +#undef TARGET_VENDOR + +/* Sometimes the system header files don't declare strstr. */ +#undef NEED_DECLARATION_STRSTR + +/* Sometimes the system header files don't declare malloc and realloc. */ +#undef NEED_DECLARATION_MALLOC + +/* Sometimes the system header files don't declare free. */ +#undef NEED_DECLARATION_FREE + +/* Sometimes the system header files don't declare sbrk. */ +#undef NEED_DECLARATION_SBRK + +/* Sometimes errno.h doesn't declare errno itself. */ +#undef NEED_DECLARATION_ERRNO + +#undef MANY_SEGMENTS + +/* Needed only for sparc configuration. */ +#undef SPARC_V9 +#undef SPARC_ARCH64 + +/* Defined if using CGEN. */ +#undef USING_CGEN + +/* Needed only for some configurations that can produce multiple output + formats. */ +#undef DEFAULT_EMULATION +#undef EMULATIONS +#undef USE_EMULATIONS +#undef OBJ_MAYBE_AOUT +#undef OBJ_MAYBE_BOUT +#undef OBJ_MAYBE_COFF +#undef OBJ_MAYBE_ECOFF +#undef OBJ_MAYBE_ELF +#undef OBJ_MAYBE_GENERIC +#undef OBJ_MAYBE_HP300 +#undef OBJ_MAYBE_IEEE +#undef OBJ_MAYBE_SOM +#undef OBJ_MAYBE_VMS + +/* Used for some of the COFF configurations, when the COFF code needs + to select something based on the CPU type before it knows it... */ +#undef I386COFF +#undef M68KCOFF +#undef M88KCOFF + +/* Define if you have the remove function. */ +#undef HAVE_REMOVE + +/* Define if you have the sbrk function. */ +#undef HAVE_SBRK + +/* Define if you have the unlink function. */ +#undef HAVE_UNLINK + +/* Define if you have the <errno.h> header file. */ +#undef HAVE_ERRNO_H + +/* Define if you have the <memory.h> header file. */ +#undef HAVE_MEMORY_H + +/* Define if you have the <stdarg.h> header file. */ +#undef HAVE_STDARG_H + +/* Define if you have the <stdlib.h> header file. */ +#undef HAVE_STDLIB_H + +/* Define if you have the <string.h> header file. */ +#undef HAVE_STRING_H + +/* Define if you have the <strings.h> header file. */ +#undef HAVE_STRINGS_H + +/* Define if you have the <sys/types.h> header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define if you have the <unistd.h> header file. */ +#undef HAVE_UNISTD_H + +/* Define if you have the <varargs.h> header file. */ +#undef HAVE_VARARGS_H diff --git a/contrib/binutils/gas/config/e-mipsecoff.c b/contrib/binutils/gas/config/e-mipsecoff.c new file mode 100644 index 000000000000..be2f71b7d46a --- /dev/null +++ b/contrib/binutils/gas/config/e-mipsecoff.c @@ -0,0 +1,37 @@ +#include "as.h" +#include "emul.h" + +static const char *mipsecoff_bfd_name PARAMS ((void)); + +static const char * +mipsecoff_bfd_name () +{ + abort (); + return NULL; +} + +#define emul_bfd_name mipsecoff_bfd_name +#define emul_format &ecoff_format_ops + +#define emul_name "mipsbecoff" +#define emul_struct_name mipsbecoff +#define emul_default_endian 1 +#include "emul-target.h" + +#undef emul_name +#undef emul_struct_name +#undef emul_default_endian + +#define emul_name "mipslecoff" +#define emul_struct_name mipslecoff +#define emul_default_endian 0 +#include "emul-target.h" + +#undef emul_name +#undef emul_struct_name +#undef emul_default_endian + +#define emul_name "mipsecoff" +#define emul_struct_name mipsecoff +#define emul_default_endian 2 +#include "emul-target.h" diff --git a/contrib/binutils/gas/config/e-mipself.c b/contrib/binutils/gas/config/e-mipself.c new file mode 100644 index 000000000000..eea72f5165c7 --- /dev/null +++ b/contrib/binutils/gas/config/e-mipself.c @@ -0,0 +1,37 @@ +#include "as.h" +#include "emul.h" + +static const char *mipself_bfd_name PARAMS ((void)); + +static const char * +mipself_bfd_name () +{ + abort (); + return NULL; +} + +#define emul_bfd_name mipself_bfd_name +#define emul_format &elf_format_ops + +#define emul_name "mipsbelf" +#define emul_struct_name mipsbelf +#define emul_default_endian 1 +#include "emul-target.h" + +#undef emul_name +#undef emul_struct_name +#undef emul_default_endian + +#define emul_name "mipslelf" +#define emul_struct_name mipslelf +#define emul_default_endian 0 +#include "emul-target.h" + +#undef emul_name +#undef emul_struct_name +#undef emul_default_endian + +#define emul_name "mipself" +#define emul_struct_name mipself +#define emul_default_endian 2 +#include "emul-target.h" diff --git a/contrib/binutils/gas/config/i386coff.mt b/contrib/binutils/gas/config/i386coff.mt new file mode 100644 index 000000000000..efda83365181 --- /dev/null +++ b/contrib/binutils/gas/config/i386coff.mt @@ -0,0 +1 @@ +TDEFINES=-DI386COFF diff --git a/contrib/binutils/gas/config/itbl-mips.h b/contrib/binutils/gas/config/itbl-mips.h new file mode 100644 index 000000000000..f6482bd1f75e --- /dev/null +++ b/contrib/binutils/gas/config/itbl-mips.h @@ -0,0 +1,47 @@ + +/* itbl-mips.h + + Copyright (C) 1997 Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS 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. + + GAS 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 GAS; see the file COPYING. If not, write to the Free + Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +/* Defines for Mips itbl cop support */ + +#include "opcode/mips.h" + +/* Values for processors will be from 0 to NUMBER_OF_PROCESSORS-1 */ +#define NUMBER_OF_PROCESSORS 4 +#define MAX_BITPOS 31 + +/* Mips specifics */ +#define MIPS_OPCODE_COP0 (0x21) /* COPz+CO, bits 31-25: 0100zz1 */ +#define MIPS_ENCODE_COP_NUM(z) ((MIPS_OPCODE_COP0|z<<1)<<25) +#define MIPS_IS_COP_INSN(insn) ((MIPS_OPCODE_COP0&(insn>>25)) \ + == MIPS_OPCODE_COP0) +#define MIPS_DECODE_COP_NUM(insn) ((~MIPS_OPCODE_COP0&(insn>>25))>>1) +#define MIPS_DECODE_COP_COFUN(insn) ((~MIPS_ENCODE_COP_NUM(3))&(insn)) + +/* definitions required by generic code */ +#define ITBL_IS_INSN(insn) MIPS_IS_COP_INSN(insn) +#define ITBL_DECODE_PNUM(insn) MIPS_DECODE_COP_NUM(insn) +#define ITBL_ENCODE_PNUM(pnum) MIPS_ENCODE_COP_NUM(pnum) + +#define ITBL_OPCODE_STRUCT mips_opcode +#define ITBL_OPCODES mips_opcodes +#define ITBL_NUM_OPCODES NUMOPCODES +#define ITBL_NUM_MACROS M_NUM_MACROS diff --git a/contrib/binutils/gas/config/sco5.mt b/contrib/binutils/gas/config/sco5.mt new file mode 100644 index 000000000000..8879320c4e1e --- /dev/null +++ b/contrib/binutils/gas/config/sco5.mt @@ -0,0 +1 @@ +TDEFINES=-DSCO_ELF diff --git a/contrib/binutils/gas/config/tc-i386.c b/contrib/binutils/gas/config/tc-i386.c index 7953327438cf..399f5ab2b719 100644 --- a/contrib/binutils/gas/config/tc-i386.c +++ b/contrib/binutils/gas/config/tc-i386.c @@ -19,7 +19,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/binutils/gas/config/tc-i386.c,v 1.4.2.2 2000/07/07 05:33:51 obrien Exp $ */ /* Intel 80386 machine specific gas. diff --git a/contrib/binutils/gas/config/tc-i386.h b/contrib/binutils/gas/config/tc-i386.h index bfd4698a6a8f..9b3a4f0e7979 100644 --- a/contrib/binutils/gas/config/tc-i386.h +++ b/contrib/binutils/gas/config/tc-i386.h @@ -19,7 +19,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/binutils/gas/config/tc-i386.h,v 1.3.6.2 2000/07/07 05:33:52 obrien Exp $ */ #ifndef TC_I386 #define TC_I386 1 diff --git a/contrib/binutils/gas/config/tc-mips.c b/contrib/binutils/gas/config/tc-mips.c new file mode 100644 index 000000000000..5daf89d394f3 --- /dev/null +++ b/contrib/binutils/gas/config/tc-mips.c @@ -0,0 +1,11935 @@ +/* tc-mips.c -- assemble code for a MIPS chip. + Copyright (C) 1993, 94, 95, 96, 97, 98, 1999, 2000 Free Software Foundation, Inc. + Contributed by the OSF and Ralph Campbell. + Written by Keith Knowles and Ralph Campbell, working independently. + Modified for ECOFF and R4000 support by Ian Lance Taylor of Cygnus + Support. + + This file is part of GAS. + + GAS 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. + + GAS 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 GAS; see the file COPYING. If not, write to the Free + Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +#include "as.h" +#include "config.h" +#include "subsegs.h" + +#include <ctype.h> + +#ifdef USE_STDARG +#include <stdarg.h> +#endif +#ifdef USE_VARARGS +#include <varargs.h> +#endif + +#include "opcode/mips.h" +#include "itbl-ops.h" + +#ifdef DEBUG +#define DBG(x) printf x +#else +#define DBG(x) +#endif + +#ifdef OBJ_MAYBE_ELF +/* Clean up namespace so we can include obj-elf.h too. */ +static int mips_output_flavor PARAMS ((void)); +static int mips_output_flavor () { return OUTPUT_FLAVOR; } +#undef OBJ_PROCESS_STAB +#undef OUTPUT_FLAVOR +#undef S_GET_ALIGN +#undef S_GET_SIZE +#undef S_SET_ALIGN +#undef S_SET_SIZE +#undef obj_frob_file +#undef obj_frob_file_after_relocs +#undef obj_frob_symbol +#undef obj_pop_insert +#undef obj_sec_sym_ok_for_reloc +#undef OBJ_COPY_SYMBOL_ATTRIBUTES + +#include "obj-elf.h" +/* Fix any of them that we actually care about. */ +#undef OUTPUT_FLAVOR +#define OUTPUT_FLAVOR mips_output_flavor() +#endif + +#if defined (OBJ_ELF) +#include "elf/mips.h" +#endif + +#ifndef ECOFF_DEBUGGING +#define NO_ECOFF_DEBUGGING +#define ECOFF_DEBUGGING 0 +#endif + +#include "ecoff.h" + +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) +static char *mips_regmask_frag; +#endif + +#define AT 1 +#define TREG 24 +#define PIC_CALL_REG 25 +#define KT0 26 +#define KT1 27 +#define GP 28 +#define SP 29 +#define FP 30 +#define RA 31 + +#define ILLEGAL_REG (32) + +/* Allow override of standard little-endian ECOFF format. */ + +#ifndef ECOFF_LITTLE_FORMAT +#define ECOFF_LITTLE_FORMAT "ecoff-littlemips" +#endif + +extern int target_big_endian; + +/* 1 is we should use the 64 bit MIPS ELF ABI, 0 if we should use the + 32 bit ABI. This has no meaning for ECOFF. + Note that the default is always 32 bit, even if "configured" for + 64 bit [e.g. --target=mips64-elf]. */ +static int mips_64; + +/* The default target format to use. */ +const char * +mips_target_format () +{ + switch (OUTPUT_FLAVOR) + { + case bfd_target_aout_flavour: + return target_big_endian ? "a.out-mips-big" : "a.out-mips-little"; + case bfd_target_ecoff_flavour: + return target_big_endian ? "ecoff-bigmips" : ECOFF_LITTLE_FORMAT; + case bfd_target_coff_flavour: + return "pe-mips"; + case bfd_target_elf_flavour: + return (target_big_endian + ? (mips_64 ? "elf64-bigmips" : "elf32-bigmips") + : (mips_64 ? "elf64-littlemips" : "elf32-littlemips")); + default: + abort (); + return NULL; + } +} + +/* The name of the readonly data section. */ +#define RDATA_SECTION_NAME (OUTPUT_FLAVOR == bfd_target_aout_flavour \ + ? ".data" \ + : OUTPUT_FLAVOR == bfd_target_ecoff_flavour \ + ? ".rdata" \ + : OUTPUT_FLAVOR == bfd_target_coff_flavour \ + ? ".rdata" \ + : OUTPUT_FLAVOR == bfd_target_elf_flavour \ + ? ".rodata" \ + : (abort (), "")) + +/* This is the set of options which may be modified by the .set + pseudo-op. We use a struct so that .set push and .set pop are more + reliable. */ + +struct mips_set_options +{ + /* MIPS ISA (Instruction Set Architecture) level. This is set to -1 + if it has not been initialized. Changed by `.set mipsN', and the + -mipsN command line option, and the default CPU. */ + int isa; + /* Whether we are assembling for the mips16 processor. 0 if we are + not, 1 if we are, and -1 if the value has not been initialized. + Changed by `.set mips16' and `.set nomips16', and the -mips16 and + -nomips16 command line options, and the default CPU. */ + int mips16; + /* Non-zero if we should not reorder instructions. Changed by `.set + reorder' and `.set noreorder'. */ + int noreorder; + /* Non-zero if we should not permit the $at ($1) register to be used + in instructions. Changed by `.set at' and `.set noat'. */ + int noat; + /* Non-zero if we should warn when a macro instruction expands into + more than one machine instruction. Changed by `.set nomacro' and + `.set macro'. */ + int warn_about_macros; + /* Non-zero if we should not move instructions. Changed by `.set + move', `.set volatile', `.set nomove', and `.set novolatile'. */ + int nomove; + /* Non-zero if we should not optimize branches by moving the target + of the branch into the delay slot. Actually, we don't perform + this optimization anyhow. Changed by `.set bopt' and `.set + nobopt'. */ + int nobopt; + /* Non-zero if we should not autoextend mips16 instructions. + Changed by `.set autoextend' and `.set noautoextend'. */ + int noautoextend; +}; + +/* This is the struct we use to hold the current set of options. Note + that we must set the isa and mips16 fields to -1 to indicate that + they have not been initialized. */ + +static struct mips_set_options mips_opts = { -1, -1 }; + +/* These variables are filled in with the masks of registers used. + The object format code reads them and puts them in the appropriate + place. */ +unsigned long mips_gprmask; +unsigned long mips_cprmask[4]; + +/* MIPS ISA we are using for this output file. */ +static int file_mips_isa; + +/* The CPU type as a number: 2000, 3000, 4000, 4400, etc. */ +static int mips_cpu = -1; + +/* The argument of the -mabi= flag. */ +static char* mips_abi_string = 0; + +/* Wether we should mark the file EABI64 or EABI32. */ +static int mips_eabi64 = 0; + +/* If they asked for mips1 or mips2 and a cpu that is + mips3 or greater, then mark the object file 32BITMODE. */ +static int mips_32bitmode = 0; + +/* True if -mgp32 was passed. */ +static int mips_gp32 = 0; + +/* Some ISA's have delay slots for instructions which read or write + from a coprocessor (eg. mips1-mips3); some don't (eg mips4). + Return true if instructions marked INSN_LOAD_COPROC_DELAY, + INSN_COPROC_MOVE_DELAY, or INSN_WRITE_COND_CODE actually have a + delay slot in this ISA. The uses of this macro assume that any + ISA that has delay slots for one of these, has them for all. They + also assume that ISAs which don't have delays for these insns, don't + have delays for the INSN_LOAD_MEMORY_DELAY instructions either. */ +#define ISA_HAS_COPROC_DELAYS(ISA) ( \ + (ISA) == 1 \ + || (ISA) == 2 \ + || (ISA) == 3 \ + ) + +/* Return true if ISA supports 64 bit gp register instructions. */ +#define ISA_HAS_64BIT_REGS(ISA) ( \ + (ISA) == 3 \ + || (ISA) == 4 \ + ) + +/* Whether the processor uses hardware interlocks to protect + reads from the HI and LO registers, and thus does not + require nops to be inserted. + + FIXME: GCC makes a distinction between -mcpu=FOO and -mFOO: + -mcpu=FOO schedules for FOO, but still produces code that meets the + requirements of MIPS ISA I. For example, it won't generate any + FOO-specific instructions, and it will still assume that any + scheduling hazards described in MIPS ISA I are there, even if FOO + has interlocks. -mFOO gives GCC permission to generate code that + will only run on a FOO; it will generate FOO-specific instructions, + and assume interlocks provided by a FOO. + + However, GAS currently doesn't make this distinction; before Jan 28 + 1999, GAS's -mcpu=FOO implied -mFOO, which violates GCC's + assumptions. The GCC driver passes these flags through to GAS, so + if GAS actually does anything that doesn't meet MIPS ISA I with + -mFOO, then GCC's -mcpu=FOO flag isn't going to work. + + And furthermore, it did not assume that -mFOO implied -mcpu=FOO, + which seems senseless --- why generate code which will only run on + a FOO, but schedule for something else? + + So now, at least, -mcpu=FOO and -mFOO are exactly equivalent. + + -- Jim Blandy <jimb@cygnus.com> */ + +#define hilo_interlocks (mips_cpu == 4010 \ + ) + +/* Whether the processor uses hardware interlocks to protect reads + from the GPRs, and thus does not require nops to be inserted. */ +#define gpr_interlocks \ + (mips_opts.isa != 1 \ + || mips_cpu == 3900) + +/* As with other "interlocks" this is used by hardware that has FP + (co-processor) interlocks. */ +/* Itbl support may require additional care here. */ +#define cop_interlocks (mips_cpu == 4300 \ + ) + +/* Is this a mfhi or mflo instruction? */ +#define MF_HILO_INSN(PINFO) \ + ((PINFO & INSN_READ_HI) || (PINFO & INSN_READ_LO)) + +/* MIPS PIC level. */ + +enum mips_pic_level +{ + /* Do not generate PIC code. */ + NO_PIC, + + /* Generate PIC code as in Irix 4. This is not implemented, and I'm + not sure what it is supposed to do. */ + IRIX4_PIC, + + /* Generate PIC code as in the SVR4 MIPS ABI. */ + SVR4_PIC, + + /* Generate PIC code without using a global offset table: the data + segment has a maximum size of 64K, all data references are off + the $gp register, and all text references are PC relative. This + is used on some embedded systems. */ + EMBEDDED_PIC +}; + +static enum mips_pic_level mips_pic; + +/* 1 if we should generate 32 bit offsets from the GP register in + SVR4_PIC mode. Currently has no meaning in other modes. */ +static int mips_big_got; + +/* 1 if trap instructions should used for overflow rather than break + instructions. */ +static int mips_trap; + +/* Non-zero if any .set noreorder directives were used. */ + +static int mips_any_noreorder; + +/* Non-zero if nops should be inserted when the register referenced in + an mfhi/mflo instruction is read in the next two instructions. */ +static int mips_7000_hilo_fix; + +/* The size of the small data section. */ +static int g_switch_value = 8; +/* Whether the -G option was used. */ +static int g_switch_seen = 0; + +#define N_RMASK 0xc4 +#define N_VFP 0xd4 + +/* If we can determine in advance that GP optimization won't be + possible, we can skip the relaxation stuff that tries to produce + GP-relative references. This makes delay slot optimization work + better. + + This function can only provide a guess, but it seems to work for + gcc output. It needs to guess right for gcc, otherwise gcc + will put what it thinks is a GP-relative instruction in a branch + delay slot. + + I don't know if a fix is needed for the SVR4_PIC mode. I've only + fixed it for the non-PIC mode. KR 95/04/07 */ +static int nopic_need_relax PARAMS ((symbolS *, int)); + +/* handle of the OPCODE hash table */ +static struct hash_control *op_hash = NULL; + +/* The opcode hash table we use for the mips16. */ +static struct hash_control *mips16_op_hash = NULL; + +/* This array holds the chars that always start a comment. If the + pre-processor is disabled, these aren't very useful */ +const char comment_chars[] = "#"; + +/* This array holds the chars that only start a comment at the beginning of + a line. If the line seems to have the form '# 123 filename' + .line and .file directives will appear in the pre-processed output */ +/* Note that input_file.c hand checks for '#' at the beginning of the + first line of the input file. This is because the compiler outputs + #NO_APP at the beginning of its output. */ +/* Also note that C style comments are always supported. */ +const char line_comment_chars[] = "#"; + +/* This array holds machine specific line separator characters. */ +const char line_separator_chars[] = ""; + +/* Chars that can be used to separate mant from exp in floating point nums */ +const char EXP_CHARS[] = "eE"; + +/* Chars that mean this number is a floating point constant */ +/* As in 0f12.456 */ +/* or 0d1.2345e12 */ +const char FLT_CHARS[] = "rRsSfFdDxXpP"; + +/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be + changed in read.c . Ideally it shouldn't have to know about it at all, + but nothing is ideal around here. + */ + +static char *insn_error; + +static int auto_align = 1; + +/* When outputting SVR4 PIC code, the assembler needs to know the + offset in the stack frame from which to restore the $gp register. + This is set by the .cprestore pseudo-op, and saved in this + variable. */ +static offsetT mips_cprestore_offset = -1; + +/* This is the register which holds the stack frame, as set by the + .frame pseudo-op. This is needed to implement .cprestore. */ +static int mips_frame_reg = SP; + +/* To output NOP instructions correctly, we need to keep information + about the previous two instructions. */ + +/* Whether we are optimizing. The default value of 2 means to remove + unneeded NOPs and swap branch instructions when possible. A value + of 1 means to not swap branches. A value of 0 means to always + insert NOPs. */ +static int mips_optimize = 2; + +/* Debugging level. -g sets this to 2. -gN sets this to N. -g0 is + equivalent to seeing no -g option at all. */ +static int mips_debug = 0; + +/* The previous instruction. */ +static struct mips_cl_insn prev_insn; + +/* The instruction before prev_insn. */ +static struct mips_cl_insn prev_prev_insn; + +/* If we don't want information for prev_insn or prev_prev_insn, we + point the insn_mo field at this dummy integer. */ +static const struct mips_opcode dummy_opcode = { 0 }; + +/* Non-zero if prev_insn is valid. */ +static int prev_insn_valid; + +/* The frag for the previous instruction. */ +static struct frag *prev_insn_frag; + +/* The offset into prev_insn_frag for the previous instruction. */ +static long prev_insn_where; + +/* The reloc type for the previous instruction, if any. */ +static bfd_reloc_code_real_type prev_insn_reloc_type; + +/* The reloc for the previous instruction, if any. */ +static fixS *prev_insn_fixp; + +/* Non-zero if the previous instruction was in a delay slot. */ +static int prev_insn_is_delay_slot; + +/* Non-zero if the previous instruction was in a .set noreorder. */ +static int prev_insn_unreordered; + +/* Non-zero if the previous instruction uses an extend opcode (if + mips16). */ +static int prev_insn_extended; + +/* Non-zero if the previous previous instruction was in a .set + noreorder. */ +static int prev_prev_insn_unreordered; + +/* If this is set, it points to a frag holding nop instructions which + were inserted before the start of a noreorder section. If those + nops turn out to be unnecessary, the size of the frag can be + decreased. */ +static fragS *prev_nop_frag; + +/* The number of nop instructions we created in prev_nop_frag. */ +static int prev_nop_frag_holds; + +/* The number of nop instructions that we know we need in + prev_nop_frag. */ +static int prev_nop_frag_required; + +/* The number of instructions we've seen since prev_nop_frag. */ +static int prev_nop_frag_since; + +/* For ECOFF and ELF, relocations against symbols are done in two + parts, with a HI relocation and a LO relocation. Each relocation + has only 16 bits of space to store an addend. This means that in + order for the linker to handle carries correctly, it must be able + to locate both the HI and the LO relocation. This means that the + relocations must appear in order in the relocation table. + + In order to implement this, we keep track of each unmatched HI + relocation. We then sort them so that they immediately precede the + corresponding LO relocation. */ + +struct mips_hi_fixup +{ + /* Next HI fixup. */ + struct mips_hi_fixup *next; + /* This fixup. */ + fixS *fixp; + /* The section this fixup is in. */ + segT seg; +}; + +/* The list of unmatched HI relocs. */ + +static struct mips_hi_fixup *mips_hi_fixup_list; + +/* Map normal MIPS register numbers to mips16 register numbers. */ + +#define X ILLEGAL_REG +static const int mips32_to_16_reg_map[] = +{ + X, X, 2, 3, 4, 5, 6, 7, + X, X, X, X, X, X, X, X, + 0, 1, X, X, X, X, X, X, + X, X, X, X, X, X, X, X +}; +#undef X + +/* Map mips16 register numbers to normal MIPS register numbers. */ + +static const int mips16_to_32_reg_map[] = +{ + 16, 17, 2, 3, 4, 5, 6, 7 +}; + +/* Since the MIPS does not have multiple forms of PC relative + instructions, we do not have to do relaxing as is done on other + platforms. However, we do have to handle GP relative addressing + correctly, which turns out to be a similar problem. + + Every macro that refers to a symbol can occur in (at least) two + forms, one with GP relative addressing and one without. For + example, loading a global variable into a register generally uses + a macro instruction like this: + lw $4,i + If i can be addressed off the GP register (this is true if it is in + the .sbss or .sdata section, or if it is known to be smaller than + the -G argument) this will generate the following instruction: + lw $4,i($gp) + This instruction will use a GPREL reloc. If i can not be addressed + off the GP register, the following instruction sequence will be used: + lui $at,i + lw $4,i($at) + In this case the first instruction will have a HI16 reloc, and the + second reloc will have a LO16 reloc. Both relocs will be against + the symbol i. + + The issue here is that we may not know whether i is GP addressable + until after we see the instruction that uses it. Therefore, we + want to be able to choose the final instruction sequence only at + the end of the assembly. This is similar to the way other + platforms choose the size of a PC relative instruction only at the + end of assembly. + + When generating position independent code we do not use GP + addressing in quite the same way, but the issue still arises as + external symbols and local symbols must be handled differently. + + We handle these issues by actually generating both possible + instruction sequences. The longer one is put in a frag_var with + type rs_machine_dependent. We encode what to do with the frag in + the subtype field. We encode (1) the number of existing bytes to + replace, (2) the number of new bytes to use, (3) the offset from + the start of the existing bytes to the first reloc we must generate + (that is, the offset is applied from the start of the existing + bytes after they are replaced by the new bytes, if any), (4) the + offset from the start of the existing bytes to the second reloc, + (5) whether a third reloc is needed (the third reloc is always four + bytes after the second reloc), and (6) whether to warn if this + variant is used (this is sometimes needed if .set nomacro or .set + noat is in effect). All these numbers are reasonably small. + + Generating two instruction sequences must be handled carefully to + ensure that delay slots are handled correctly. Fortunately, there + are a limited number of cases. When the second instruction + sequence is generated, append_insn is directed to maintain the + existing delay slot information, so it continues to apply to any + code after the second instruction sequence. This means that the + second instruction sequence must not impose any requirements not + required by the first instruction sequence. + + These variant frags are then handled in functions called by the + machine independent code. md_estimate_size_before_relax returns + the final size of the frag. md_convert_frag sets up the final form + of the frag. tc_gen_reloc adjust the first reloc and adds a second + one if needed. */ +#define RELAX_ENCODE(old, new, reloc1, reloc2, reloc3, warn) \ + ((relax_substateT) \ + (((old) << 23) \ + | ((new) << 16) \ + | (((reloc1) + 64) << 9) \ + | (((reloc2) + 64) << 2) \ + | ((reloc3) ? (1 << 1) : 0) \ + | ((warn) ? 1 : 0))) +#define RELAX_OLD(i) (((i) >> 23) & 0x7f) +#define RELAX_NEW(i) (((i) >> 16) & 0x7f) +#define RELAX_RELOC1(i) ((bfd_vma)(((i) >> 9) & 0x7f) - 64) +#define RELAX_RELOC2(i) ((bfd_vma)(((i) >> 2) & 0x7f) - 64) +#define RELAX_RELOC3(i) (((i) >> 1) & 1) +#define RELAX_WARN(i) ((i) & 1) + +/* For mips16 code, we use an entirely different form of relaxation. + mips16 supports two versions of most instructions which take + immediate values: a small one which takes some small value, and a + larger one which takes a 16 bit value. Since branches also follow + this pattern, relaxing these values is required. + + We can assemble both mips16 and normal MIPS code in a single + object. Therefore, we need to support this type of relaxation at + the same time that we support the relaxation described above. We + use the high bit of the subtype field to distinguish these cases. + + The information we store for this type of relaxation is the + argument code found in the opcode file for this relocation, whether + the user explicitly requested a small or extended form, and whether + the relocation is in a jump or jal delay slot. That tells us the + size of the value, and how it should be stored. We also store + whether the fragment is considered to be extended or not. We also + store whether this is known to be a branch to a different section, + whether we have tried to relax this frag yet, and whether we have + ever extended a PC relative fragment because of a shift count. */ +#define RELAX_MIPS16_ENCODE(type, small, ext, dslot, jal_dslot) \ + (0x80000000 \ + | ((type) & 0xff) \ + | ((small) ? 0x100 : 0) \ + | ((ext) ? 0x200 : 0) \ + | ((dslot) ? 0x400 : 0) \ + | ((jal_dslot) ? 0x800 : 0)) +#define RELAX_MIPS16_P(i) (((i) & 0x80000000) != 0) +#define RELAX_MIPS16_TYPE(i) ((i) & 0xff) +#define RELAX_MIPS16_USER_SMALL(i) (((i) & 0x100) != 0) +#define RELAX_MIPS16_USER_EXT(i) (((i) & 0x200) != 0) +#define RELAX_MIPS16_DSLOT(i) (((i) & 0x400) != 0) +#define RELAX_MIPS16_JAL_DSLOT(i) (((i) & 0x800) != 0) +#define RELAX_MIPS16_EXTENDED(i) (((i) & 0x1000) != 0) +#define RELAX_MIPS16_MARK_EXTENDED(i) ((i) | 0x1000) +#define RELAX_MIPS16_CLEAR_EXTENDED(i) ((i) &~ 0x1000) +#define RELAX_MIPS16_LONG_BRANCH(i) (((i) & 0x2000) != 0) +#define RELAX_MIPS16_MARK_LONG_BRANCH(i) ((i) | 0x2000) +#define RELAX_MIPS16_CLEAR_LONG_BRANCH(i) ((i) &~ 0x2000) + +/* Prototypes for static functions. */ + +#ifdef __STDC__ +#define internalError() \ + as_fatal (_("internal Error, line %d, %s"), __LINE__, __FILE__) +#else +#define internalError() as_fatal (_("MIPS internal Error")); +#endif + +enum mips_regclass { MIPS_GR_REG, MIPS_FP_REG, MIPS16_REG }; + +static int insn_uses_reg PARAMS ((struct mips_cl_insn *ip, + unsigned int reg, enum mips_regclass class)); +static int reg_needs_delay PARAMS ((int)); +static void mips16_mark_labels PARAMS ((void)); +static void append_insn PARAMS ((char *place, + struct mips_cl_insn * ip, + expressionS * p, + bfd_reloc_code_real_type r, + boolean)); +static void mips_no_prev_insn PARAMS ((int)); +static void mips_emit_delays PARAMS ((boolean)); +#ifdef USE_STDARG +static void macro_build PARAMS ((char *place, int *counter, expressionS * ep, + const char *name, const char *fmt, + ...)); +#else +static void macro_build (); +#endif +static void mips16_macro_build PARAMS ((char *, int *, expressionS *, + const char *, const char *, + va_list)); +static void macro_build_lui PARAMS ((char *place, int *counter, + expressionS * ep, int regnum)); +static void set_at PARAMS ((int *counter, int reg, int unsignedp)); +static void check_absolute_expr PARAMS ((struct mips_cl_insn * ip, + expressionS *)); +static void load_register PARAMS ((int *, int, expressionS *, int)); +static void load_address PARAMS ((int *counter, int reg, expressionS *ep)); +static void macro PARAMS ((struct mips_cl_insn * ip)); +static void mips16_macro PARAMS ((struct mips_cl_insn * ip)); +#ifdef LOSING_COMPILER +static void macro2 PARAMS ((struct mips_cl_insn * ip)); +#endif +static void mips_ip PARAMS ((char *str, struct mips_cl_insn * ip)); +static void mips16_ip PARAMS ((char *str, struct mips_cl_insn * ip)); +static void mips16_immed PARAMS ((char *, unsigned int, int, offsetT, boolean, + boolean, boolean, unsigned long *, + boolean *, unsigned short *)); +static int my_getSmallExpression PARAMS ((expressionS * ep, char *str)); +static void my_getExpression PARAMS ((expressionS * ep, char *str)); +static symbolS *get_symbol PARAMS ((void)); +static void mips_align PARAMS ((int to, int fill, symbolS *label)); +static void s_align PARAMS ((int)); +static void s_change_sec PARAMS ((int)); +static void s_cons PARAMS ((int)); +static void s_float_cons PARAMS ((int)); +static void s_mips_globl PARAMS ((int)); +static void s_option PARAMS ((int)); +static void s_mipsset PARAMS ((int)); +static void s_abicalls PARAMS ((int)); +static void s_cpload PARAMS ((int)); +static void s_cprestore PARAMS ((int)); +static void s_gpword PARAMS ((int)); +static void s_cpadd PARAMS ((int)); +static void s_insn PARAMS ((int)); +static void md_obj_begin PARAMS ((void)); +static void md_obj_end PARAMS ((void)); +static long get_number PARAMS ((void)); +static void s_mips_ent PARAMS ((int)); +static void s_mips_end PARAMS ((int)); +static void s_mips_frame PARAMS ((int)); +static void s_mips_mask PARAMS ((int)); +static void s_mips_stab PARAMS ((int)); +static void s_mips_weakext PARAMS ((int)); +static void s_file PARAMS ((int)); +static int mips16_extended_frag PARAMS ((fragS *, asection *, long)); + + +static int validate_mips_insn PARAMS ((const struct mips_opcode *)); + +/* Pseudo-op table. + + The following pseudo-ops from the Kane and Heinrich MIPS book + should be defined here, but are currently unsupported: .alias, + .galive, .gjaldef, .gjrlive, .livereg, .noalias. + + The following pseudo-ops from the Kane and Heinrich MIPS book are + specific to the type of debugging information being generated, and + should be defined by the object format: .aent, .begin, .bend, + .bgnb, .end, .endb, .ent, .fmask, .frame, .loc, .mask, .verstamp, + .vreg. + + The following pseudo-ops from the Kane and Heinrich MIPS book are + not MIPS CPU specific, but are also not specific to the object file + format. This file is probably the best place to define them, but + they are not currently supported: .asm0, .endr, .lab, .repeat, + .struct. */ + +static const pseudo_typeS mips_pseudo_table[] = +{ + /* MIPS specific pseudo-ops. */ + {"option", s_option, 0}, + {"set", s_mipsset, 0}, + {"rdata", s_change_sec, 'r'}, + {"sdata", s_change_sec, 's'}, + {"livereg", s_ignore, 0}, + {"abicalls", s_abicalls, 0}, + {"cpload", s_cpload, 0}, + {"cprestore", s_cprestore, 0}, + {"gpword", s_gpword, 0}, + {"cpadd", s_cpadd, 0}, + {"insn", s_insn, 0}, + + /* Relatively generic pseudo-ops that happen to be used on MIPS + chips. */ + {"asciiz", stringer, 1}, + {"bss", s_change_sec, 'b'}, + {"err", s_err, 0}, + {"half", s_cons, 1}, + {"dword", s_cons, 3}, + {"weakext", s_mips_weakext, 0}, + + /* These pseudo-ops are defined in read.c, but must be overridden + here for one reason or another. */ + {"align", s_align, 0}, + {"byte", s_cons, 0}, + {"data", s_change_sec, 'd'}, + {"double", s_float_cons, 'd'}, + {"float", s_float_cons, 'f'}, + {"globl", s_mips_globl, 0}, + {"global", s_mips_globl, 0}, + {"hword", s_cons, 1}, + {"int", s_cons, 2}, + {"long", s_cons, 2}, + {"octa", s_cons, 4}, + {"quad", s_cons, 3}, + {"short", s_cons, 1}, + {"single", s_float_cons, 'f'}, + {"stabn", s_mips_stab, 'n'}, + {"text", s_change_sec, 't'}, + {"word", s_cons, 2}, + { 0 }, +}; + +static const pseudo_typeS mips_nonecoff_pseudo_table[] = { + /* These pseudo-ops should be defined by the object file format. + However, a.out doesn't support them, so we have versions here. */ + {"aent", s_mips_ent, 1}, + {"bgnb", s_ignore, 0}, + {"end", s_mips_end, 0}, + {"endb", s_ignore, 0}, + {"ent", s_mips_ent, 0}, + {"file", s_file, 0}, + {"fmask", s_mips_mask, 'F'}, + {"frame", s_mips_frame, 0}, + {"loc", s_ignore, 0}, + {"mask", s_mips_mask, 'R'}, + {"verstamp", s_ignore, 0}, + { 0 }, +}; + +extern void pop_insert PARAMS ((const pseudo_typeS *)); + +void +mips_pop_insert () +{ + pop_insert (mips_pseudo_table); + if (! ECOFF_DEBUGGING) + pop_insert (mips_nonecoff_pseudo_table); +} + +/* Symbols labelling the current insn. */ + +struct insn_label_list +{ + struct insn_label_list *next; + symbolS *label; +}; + +static struct insn_label_list *insn_labels; +static struct insn_label_list *free_insn_labels; + +static void mips_clear_insn_labels PARAMS ((void)); + +static inline void +mips_clear_insn_labels () +{ + register struct insn_label_list **pl; + + for (pl = &free_insn_labels; *pl != NULL; pl = &(*pl)->next) + ; + *pl = insn_labels; + insn_labels = NULL; +} + +static char *expr_end; + +/* Expressions which appear in instructions. These are set by + mips_ip. */ + +static expressionS imm_expr; +static expressionS offset_expr; + +/* Relocs associated with imm_expr and offset_expr. */ + +static bfd_reloc_code_real_type imm_reloc; +static bfd_reloc_code_real_type offset_reloc; + +/* This is set by mips_ip if imm_reloc is an unmatched HI16_S reloc. */ + +static boolean imm_unmatched_hi; + +/* These are set by mips16_ip if an explicit extension is used. */ + +static boolean mips16_small, mips16_ext; + +#ifdef MIPS_STABS_ELF +/* The pdr segment for per procedure frame/regmask info */ + +static segT pdr_seg; +#endif + +/* + * This function is called once, at assembler startup time. It should + * set up all the tables, etc. that the MD part of the assembler will need. + */ +void +md_begin () +{ + boolean ok = false; + register const char *retval = NULL; + register unsigned int i = 0; + const char *cpu; + char *a = NULL; + int broken = 0; + int mips_isa_from_cpu; + + /* GP relative stuff not working for PE */ + if (strncmp (TARGET_OS, "pe", 2) == 0 + && g_switch_value != 0) + { + if (g_switch_seen) + as_bad (_("-G not supported in this configuration.")); + g_switch_value = 0; + } + + cpu = TARGET_CPU; + if (strcmp (cpu + (sizeof TARGET_CPU) - 3, "el") == 0) + { + a = xmalloc (sizeof TARGET_CPU); + strcpy (a, TARGET_CPU); + a[(sizeof TARGET_CPU) - 3] = '\0'; + cpu = a; + } + + if (mips_cpu < 0) + { + /* Set mips_cpu based on TARGET_CPU, unless TARGET_CPU is + just the generic 'mips', in which case set mips_cpu based + on the given ISA, if any. */ + + if (strcmp (cpu, "mips") == 0) + { + if (mips_opts.isa < 0) + mips_cpu = 3000; + + else if (mips_opts.isa == 2) + mips_cpu = 6000; + + else if (mips_opts.isa == 3) + mips_cpu = 4000; + + else if (mips_opts.isa == 4) + mips_cpu = 8000; + + else + mips_cpu = 3000; + } + + else if (strcmp (cpu, "r3900") == 0 + || strcmp (cpu, "mipstx39") == 0 + ) + mips_cpu = 3900; + + else if (strcmp (cpu, "r6000") == 0 + || strcmp (cpu, "mips2") == 0) + mips_cpu = 6000; + + else if (strcmp (cpu, "mips64") == 0 + || strcmp (cpu, "r4000") == 0 + || strcmp (cpu, "mips3") == 0) + mips_cpu = 4000; + + else if (strcmp (cpu, "r4400") == 0) + mips_cpu = 4400; + + else if (strcmp (cpu, "mips64orion") == 0 + || strcmp (cpu, "r4600") == 0) + mips_cpu = 4600; + + else if (strcmp (cpu, "r4650") == 0) + mips_cpu = 4650; + + else if (strcmp (cpu, "mips64vr4300") == 0) + mips_cpu = 4300; + + else if (strcmp (cpu, "mips64vr4111") == 0) + mips_cpu = 4111; + + else if (strcmp (cpu, "mips64vr4100") == 0) + mips_cpu = 4100; + + else if (strcmp (cpu, "r4010") == 0) + mips_cpu = 4010; + + + else if (strcmp (cpu, "r5000") == 0 + || strcmp (cpu, "mips64vr5000") == 0) + mips_cpu = 5000; + + + + else if (strcmp (cpu, "r8000") == 0 + || strcmp (cpu, "mips4") == 0) + mips_cpu = 8000; + + else if (strcmp (cpu, "r10000") == 0) + mips_cpu = 10000; + + else if (strcmp (cpu, "mips16") == 0) + mips_cpu = 0; /* FIXME */ + + else + mips_cpu = 3000; + } + + if (mips_cpu == 3000 + || mips_cpu == 3900) + mips_isa_from_cpu = 1; + + else if (mips_cpu == 6000 + || mips_cpu == 4010) + mips_isa_from_cpu = 2; + + else if (mips_cpu == 4000 + || mips_cpu == 4100 + || mips_cpu == 4111 + || mips_cpu == 4400 + || mips_cpu == 4300 + || mips_cpu == 4600 + || mips_cpu == 4650) + mips_isa_from_cpu = 3; + + else if (mips_cpu == 5000 + || mips_cpu == 8000 + || mips_cpu == 10000) + mips_isa_from_cpu = 4; + + else + mips_isa_from_cpu = -1; + + if (mips_opts.isa == -1) + { + if (mips_isa_from_cpu != -1) + mips_opts.isa = mips_isa_from_cpu; + else + mips_opts.isa = 1; + } + + if (mips_opts.mips16 < 0) + { + if (strncmp (TARGET_CPU, "mips16", sizeof "mips16" - 1) == 0) + mips_opts.mips16 = 1; + else + mips_opts.mips16 = 0; + } + + /* End of TARGET_CPU processing, get rid of malloced memory + if necessary. */ + cpu = NULL; + if (a != NULL) + { + free (a); + a = NULL; + } + + if (mips_opts.isa == 1 && mips_trap) + as_bad (_("trap exception not supported at ISA 1")); + + /* Set the EABI kind based on the ISA before the user gets + to change the ISA with directives. This isn't really + the best, but then neither is basing the abi on the isa. */ + if (ISA_HAS_64BIT_REGS (mips_opts.isa) + && mips_abi_string + && 0 == strcmp (mips_abi_string,"eabi")) + mips_eabi64 = 1; + + if (mips_cpu != 0 && mips_cpu != -1) + { + ok = bfd_set_arch_mach (stdoutput, bfd_arch_mips, mips_cpu); + + /* If they asked for mips1 or mips2 and a cpu that is + mips3 or greater, then mark the object file 32BITMODE. */ + if (mips_isa_from_cpu != -1 + && ! ISA_HAS_64BIT_REGS (mips_opts.isa) + && ISA_HAS_64BIT_REGS (mips_isa_from_cpu)) + mips_32bitmode = 1; + } + else + { + switch (mips_opts.isa) + { + case 1: + ok = bfd_set_arch_mach (stdoutput, bfd_arch_mips, 3000); + break; + case 2: + ok = bfd_set_arch_mach (stdoutput, bfd_arch_mips, 6000); + break; + case 3: + ok = bfd_set_arch_mach (stdoutput, bfd_arch_mips, 4000); + break; + case 4: + ok = bfd_set_arch_mach (stdoutput, bfd_arch_mips, 8000); + break; + } + } + + if (! ok) + as_warn (_("Could not set architecture and machine")); + + file_mips_isa = mips_opts.isa; + + op_hash = hash_new (); + + for (i = 0; i < NUMOPCODES;) + { + const char *name = mips_opcodes[i].name; + + retval = hash_insert (op_hash, name, (PTR) &mips_opcodes[i]); + if (retval != NULL) + { + fprintf (stderr, _("internal error: can't hash `%s': %s\n"), + mips_opcodes[i].name, retval); + /* Probably a memory allocation problem? Give up now. */ + as_fatal (_("Broken assembler. No assembly attempted.")); + } + do + { + if (mips_opcodes[i].pinfo != INSN_MACRO) + { + if (!validate_mips_insn (&mips_opcodes[i])) + broken = 1; + } + ++i; + } + while ((i < NUMOPCODES) && !strcmp (mips_opcodes[i].name, name)); + } + + mips16_op_hash = hash_new (); + + i = 0; + while (i < bfd_mips16_num_opcodes) + { + const char *name = mips16_opcodes[i].name; + + retval = hash_insert (mips16_op_hash, name, (PTR) &mips16_opcodes[i]); + if (retval != NULL) + as_fatal (_("internal: can't hash `%s': %s"), + mips16_opcodes[i].name, retval); + do + { + if (mips16_opcodes[i].pinfo != INSN_MACRO + && ((mips16_opcodes[i].match & mips16_opcodes[i].mask) + != mips16_opcodes[i].match)) + { + fprintf (stderr, _("internal error: bad mips16 opcode: %s %s\n"), + mips16_opcodes[i].name, mips16_opcodes[i].args); + broken = 1; + } + ++i; + } + while (i < bfd_mips16_num_opcodes + && strcmp (mips16_opcodes[i].name, name) == 0); + } + + if (broken) + as_fatal (_("Broken assembler. No assembly attempted.")); + + /* We add all the general register names to the symbol table. This + helps us detect invalid uses of them. */ + for (i = 0; i < 32; i++) + { + char buf[5]; + + sprintf (buf, "$%d", i); + symbol_table_insert (symbol_new (buf, reg_section, i, + &zero_address_frag)); + } + symbol_table_insert (symbol_new ("$fp", reg_section, FP, + &zero_address_frag)); + symbol_table_insert (symbol_new ("$sp", reg_section, SP, + &zero_address_frag)); + symbol_table_insert (symbol_new ("$gp", reg_section, GP, + &zero_address_frag)); + symbol_table_insert (symbol_new ("$at", reg_section, AT, + &zero_address_frag)); + symbol_table_insert (symbol_new ("$kt0", reg_section, KT0, + &zero_address_frag)); + symbol_table_insert (symbol_new ("$kt1", reg_section, KT1, + &zero_address_frag)); + symbol_table_insert (symbol_new ("$pc", reg_section, -1, + &zero_address_frag)); + + mips_no_prev_insn (false); + + mips_gprmask = 0; + mips_cprmask[0] = 0; + mips_cprmask[1] = 0; + mips_cprmask[2] = 0; + mips_cprmask[3] = 0; + + /* set the default alignment for the text section (2**2) */ + record_alignment (text_section, 2); + + if (USE_GLOBAL_POINTER_OPT) + bfd_set_gp_size (stdoutput, g_switch_value); + + if (OUTPUT_FLAVOR == bfd_target_elf_flavour) + { + /* On a native system, sections must be aligned to 16 byte + boundaries. When configured for an embedded ELF target, we + don't bother. */ + if (strcmp (TARGET_OS, "elf") != 0) + { + (void) bfd_set_section_alignment (stdoutput, text_section, 4); + (void) bfd_set_section_alignment (stdoutput, data_section, 4); + (void) bfd_set_section_alignment (stdoutput, bss_section, 4); + } + + /* Create a .reginfo section for register masks and a .mdebug + section for debugging information. */ + { + segT seg; + subsegT subseg; + flagword flags; + segT sec; + + seg = now_seg; + subseg = now_subseg; + + /* The ABI says this section should be loaded so that the + running program can access it. However, we don't load it + if we are configured for an embedded target */ + flags = SEC_READONLY | SEC_DATA; + if (strcmp (TARGET_OS, "elf") != 0) + flags |= SEC_ALLOC | SEC_LOAD; + + if (! mips_64) + { + sec = subseg_new (".reginfo", (subsegT) 0); + + + (void) bfd_set_section_flags (stdoutput, sec, flags); + (void) bfd_set_section_alignment (stdoutput, sec, 2); + +#ifdef OBJ_ELF + mips_regmask_frag = frag_more (sizeof (Elf32_External_RegInfo)); +#endif + } + else + { + /* The 64-bit ABI uses a .MIPS.options section rather than + .reginfo section. */ + sec = subseg_new (".MIPS.options", (subsegT) 0); + (void) bfd_set_section_flags (stdoutput, sec, flags); + (void) bfd_set_section_alignment (stdoutput, sec, 3); + +#ifdef OBJ_ELF + /* Set up the option header. */ + { + Elf_Internal_Options opthdr; + char *f; + + opthdr.kind = ODK_REGINFO; + opthdr.size = (sizeof (Elf_External_Options) + + sizeof (Elf64_External_RegInfo)); + opthdr.section = 0; + opthdr.info = 0; + f = frag_more (sizeof (Elf_External_Options)); + bfd_mips_elf_swap_options_out (stdoutput, &opthdr, + (Elf_External_Options *) f); + + mips_regmask_frag = frag_more (sizeof (Elf64_External_RegInfo)); + } +#endif + } + + if (ECOFF_DEBUGGING) + { + sec = subseg_new (".mdebug", (subsegT) 0); + (void) bfd_set_section_flags (stdoutput, sec, + SEC_HAS_CONTENTS | SEC_READONLY); + (void) bfd_set_section_alignment (stdoutput, sec, 2); + } + +#ifdef MIPS_STABS_ELF + pdr_seg = subseg_new (".pdr", (subsegT) 0); + (void) bfd_set_section_flags (stdoutput, pdr_seg, + SEC_READONLY | SEC_RELOC | SEC_DEBUGGING); + (void) bfd_set_section_alignment (stdoutput, pdr_seg, 2); +#endif + + subseg_set (seg, subseg); + } + } + + if (! ECOFF_DEBUGGING) + md_obj_begin (); +} + +void +md_mips_end () +{ + if (! ECOFF_DEBUGGING) + md_obj_end (); +} + +void +md_assemble (str) + char *str; +{ + struct mips_cl_insn insn; + + imm_expr.X_op = O_absent; + imm_reloc = BFD_RELOC_UNUSED; + imm_unmatched_hi = false; + offset_expr.X_op = O_absent; + offset_reloc = BFD_RELOC_UNUSED; + + if (mips_opts.mips16) + mips16_ip (str, &insn); + else + { + mips_ip (str, &insn); + DBG((_("returned from mips_ip(%s) insn_opcode = 0x%x\n"), + str, insn.insn_opcode)); + } + + if (insn_error) + { + as_bad ("%s `%s'", insn_error, str); + return; + } + + if (insn.insn_mo->pinfo == INSN_MACRO) + { + if (mips_opts.mips16) + mips16_macro (&insn); + else + macro (&insn); + } + else + { + if (imm_expr.X_op != O_absent) + append_insn ((char *) NULL, &insn, &imm_expr, imm_reloc, + imm_unmatched_hi); + else if (offset_expr.X_op != O_absent) + append_insn ((char *) NULL, &insn, &offset_expr, offset_reloc, false); + else + append_insn ((char *) NULL, &insn, NULL, BFD_RELOC_UNUSED, false); + } +} + +/* See whether instruction IP reads register REG. CLASS is the type + of register. */ + +static int +insn_uses_reg (ip, reg, class) + struct mips_cl_insn *ip; + unsigned int reg; + enum mips_regclass class; +{ + if (class == MIPS16_REG) + { + assert (mips_opts.mips16); + reg = mips16_to_32_reg_map[reg]; + class = MIPS_GR_REG; + } + + /* Don't report on general register 0, since it never changes. */ + if (class == MIPS_GR_REG && reg == 0) + return 0; + + if (class == MIPS_FP_REG) + { + assert (! mips_opts.mips16); + /* If we are called with either $f0 or $f1, we must check $f0. + This is not optimal, because it will introduce an unnecessary + NOP between "lwc1 $f0" and "swc1 $f1". To fix this we would + need to distinguish reading both $f0 and $f1 or just one of + them. Note that we don't have to check the other way, + because there is no instruction that sets both $f0 and $f1 + and requires a delay. */ + if ((ip->insn_mo->pinfo & INSN_READ_FPR_S) + && ((((ip->insn_opcode >> OP_SH_FS) & OP_MASK_FS) &~(unsigned)1) + == (reg &~ (unsigned) 1))) + return 1; + if ((ip->insn_mo->pinfo & INSN_READ_FPR_T) + && ((((ip->insn_opcode >> OP_SH_FT) & OP_MASK_FT) &~(unsigned)1) + == (reg &~ (unsigned) 1))) + return 1; + } + else if (! mips_opts.mips16) + { + if ((ip->insn_mo->pinfo & INSN_READ_GPR_S) + && ((ip->insn_opcode >> OP_SH_RS) & OP_MASK_RS) == reg) + return 1; + if ((ip->insn_mo->pinfo & INSN_READ_GPR_T) + && ((ip->insn_opcode >> OP_SH_RT) & OP_MASK_RT) == reg) + return 1; + } + else + { + if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_X) + && (mips16_to_32_reg_map[((ip->insn_opcode >> MIPS16OP_SH_RX) + & MIPS16OP_MASK_RX)] + == reg)) + return 1; + if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_Y) + && (mips16_to_32_reg_map[((ip->insn_opcode >> MIPS16OP_SH_RY) + & MIPS16OP_MASK_RY)] + == reg)) + return 1; + if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_Z) + && (mips16_to_32_reg_map[((ip->insn_opcode >> MIPS16OP_SH_MOVE32Z) + & MIPS16OP_MASK_MOVE32Z)] + == reg)) + return 1; + if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_T) && reg == TREG) + return 1; + if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_SP) && reg == SP) + return 1; + if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_31) && reg == RA) + return 1; + if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_GPR_X) + && ((ip->insn_opcode >> MIPS16OP_SH_REGR32) + & MIPS16OP_MASK_REGR32) == reg) + return 1; + } + + return 0; +} + +/* This function returns true if modifying a register requires a + delay. */ + +static int +reg_needs_delay (reg) + int reg; +{ + unsigned long prev_pinfo; + + prev_pinfo = prev_insn.insn_mo->pinfo; + if (! mips_opts.noreorder + && ISA_HAS_COPROC_DELAYS (mips_opts.isa) + && ((prev_pinfo & INSN_LOAD_COPROC_DELAY) + || (! gpr_interlocks + && (prev_pinfo & INSN_LOAD_MEMORY_DELAY)))) + { + /* A load from a coprocessor or from memory. All load + delays delay the use of general register rt for one + instruction on the r3000. The r6000 and r4000 use + interlocks. */ + /* Itbl support may require additional care here. */ + know (prev_pinfo & INSN_WRITE_GPR_T); + if (reg == ((prev_insn.insn_opcode >> OP_SH_RT) & OP_MASK_RT)) + return 1; + } + + return 0; +} + +/* Mark instruction labels in mips16 mode. This permits the linker to + handle them specially, such as generating jalx instructions when + needed. We also make them odd for the duration of the assembly, in + order to generate the right sort of code. We will make them even + in the adjust_symtab routine, while leaving them marked. This is + convenient for the debugger and the disassembler. The linker knows + to make them odd again. */ + +static void +mips16_mark_labels () +{ + if (mips_opts.mips16) + { + struct insn_label_list *l; + + for (l = insn_labels; l != NULL; l = l->next) + { +#ifdef OBJ_ELF + if (OUTPUT_FLAVOR == bfd_target_elf_flavour) + S_SET_OTHER (l->label, STO_MIPS16); +#endif + if ((S_GET_VALUE (l->label) & 1) == 0) + S_SET_VALUE (l->label, S_GET_VALUE (l->label) + 1); + } + } +} + +/* Output an instruction. PLACE is where to put the instruction; if + it is NULL, this uses frag_more to get room. IP is the instruction + information. ADDRESS_EXPR is an operand of the instruction to be + used with RELOC_TYPE. */ + +static void +append_insn (place, ip, address_expr, reloc_type, unmatched_hi) + char *place; + struct mips_cl_insn *ip; + expressionS *address_expr; + bfd_reloc_code_real_type reloc_type; + boolean unmatched_hi; +{ + register unsigned long prev_pinfo, pinfo; + char *f; + fixS *fixp; + int nops = 0; + + /* Mark instruction labels in mips16 mode. */ + if (mips_opts.mips16) + mips16_mark_labels (); + + prev_pinfo = prev_insn.insn_mo->pinfo; + pinfo = ip->insn_mo->pinfo; + + if (place == NULL && (! mips_opts.noreorder || prev_nop_frag != NULL)) + { + int prev_prev_nop; + + /* If the previous insn required any delay slots, see if we need + to insert a NOP or two. There are eight kinds of possible + hazards, of which an instruction can have at most one type. + (1) a load from memory delay + (2) a load from a coprocessor delay + (3) an unconditional branch delay + (4) a conditional branch delay + (5) a move to coprocessor register delay + (6) a load coprocessor register from memory delay + (7) a coprocessor condition code delay + (8) a HI/LO special register delay + + There are a lot of optimizations we could do that we don't. + In particular, we do not, in general, reorder instructions. + If you use gcc with optimization, it will reorder + instructions and generally do much more optimization then we + do here; repeating all that work in the assembler would only + benefit hand written assembly code, and does not seem worth + it. */ + + /* This is how a NOP is emitted. */ +#define emit_nop() \ + (mips_opts.mips16 \ + ? md_number_to_chars (frag_more (2), 0x6500, 2) \ + : md_number_to_chars (frag_more (4), 0, 4)) + + /* The previous insn might require a delay slot, depending upon + the contents of the current insn. */ + if (! mips_opts.mips16 + && ISA_HAS_COPROC_DELAYS (mips_opts.isa) + && (((prev_pinfo & INSN_LOAD_COPROC_DELAY) + && ! cop_interlocks) + || (! gpr_interlocks + && (prev_pinfo & INSN_LOAD_MEMORY_DELAY)))) + { + /* A load from a coprocessor or from memory. All load + delays delay the use of general register rt for one + instruction on the r3000. The r6000 and r4000 use + interlocks. */ + /* Itbl support may require additional care here. */ + know (prev_pinfo & INSN_WRITE_GPR_T); + if (mips_optimize == 0 + || insn_uses_reg (ip, + ((prev_insn.insn_opcode >> OP_SH_RT) + & OP_MASK_RT), + MIPS_GR_REG)) + ++nops; + } + else if (! mips_opts.mips16 + && ISA_HAS_COPROC_DELAYS (mips_opts.isa) + && (((prev_pinfo & INSN_COPROC_MOVE_DELAY) + && ! cop_interlocks) + || (mips_opts.isa == 1 + && (prev_pinfo & INSN_COPROC_MEMORY_DELAY)))) + { + /* A generic coprocessor delay. The previous instruction + modified a coprocessor general or control register. If + it modified a control register, we need to avoid any + coprocessor instruction (this is probably not always + required, but it sometimes is). If it modified a general + register, we avoid using that register. + + On the r6000 and r4000 loading a coprocessor register + from memory is interlocked, and does not require a delay. + + This case is not handled very well. There is no special + knowledge of CP0 handling, and the coprocessors other + than the floating point unit are not distinguished at + all. */ + /* Itbl support may require additional care here. FIXME! + Need to modify this to include knowledge about + user specified delays! */ + if (prev_pinfo & INSN_WRITE_FPR_T) + { + if (mips_optimize == 0 + || insn_uses_reg (ip, + ((prev_insn.insn_opcode >> OP_SH_FT) + & OP_MASK_FT), + MIPS_FP_REG)) + ++nops; + } + else if (prev_pinfo & INSN_WRITE_FPR_S) + { + if (mips_optimize == 0 + || insn_uses_reg (ip, + ((prev_insn.insn_opcode >> OP_SH_FS) + & OP_MASK_FS), + MIPS_FP_REG)) + ++nops; + } + else + { + /* We don't know exactly what the previous instruction + does. If the current instruction uses a coprocessor + register, we must insert a NOP. If previous + instruction may set the condition codes, and the + current instruction uses them, we must insert two + NOPS. */ + /* Itbl support may require additional care here. */ + if (mips_optimize == 0 + || ((prev_pinfo & INSN_WRITE_COND_CODE) + && (pinfo & INSN_READ_COND_CODE))) + nops += 2; + else if (pinfo & INSN_COP) + ++nops; + } + } + else if (! mips_opts.mips16 + && ISA_HAS_COPROC_DELAYS (mips_opts.isa) + && (prev_pinfo & INSN_WRITE_COND_CODE) + && ! cop_interlocks) + { + /* The previous instruction sets the coprocessor condition + codes, but does not require a general coprocessor delay + (this means it is a floating point comparison + instruction). If this instruction uses the condition + codes, we need to insert a single NOP. */ + /* Itbl support may require additional care here. */ + if (mips_optimize == 0 + || (pinfo & INSN_READ_COND_CODE)) + ++nops; + } + + /* If we're fixing up mfhi/mflo for the r7000 and the + previous insn was an mfhi/mflo and the current insn + reads the register that the mfhi/mflo wrote to, then + insert two nops. */ + + else if (mips_7000_hilo_fix + && MF_HILO_INSN (prev_pinfo) + && insn_uses_reg (ip, ((prev_insn.insn_opcode >> OP_SH_RD) + & OP_MASK_RD), + MIPS_GR_REG)) + + { + nops += 2; + } + + /* If we're fixing up mfhi/mflo for the r7000 and the + 2nd previous insn was an mfhi/mflo and the current insn + reads the register that the mfhi/mflo wrote to, then + insert one nop. */ + + else if (mips_7000_hilo_fix + && MF_HILO_INSN (prev_prev_insn.insn_opcode) + && insn_uses_reg (ip, ((prev_prev_insn.insn_opcode >> OP_SH_RD) + & OP_MASK_RD), + MIPS_GR_REG)) + + { + nops += 1; + } + + else if (prev_pinfo & INSN_READ_LO) + { + /* The previous instruction reads the LO register; if the + current instruction writes to the LO register, we must + insert two NOPS. Some newer processors have interlocks. + Also the tx39's multiply instructions can be exectuted + immediatly after a read from HI/LO (without the delay), + though the tx39's divide insns still do require the + delay. */ + if (! (hilo_interlocks + || (mips_cpu == 3900 && (pinfo & INSN_MULT))) + && (mips_optimize == 0 + || (pinfo & INSN_WRITE_LO))) + nops += 2; + /* Most mips16 branch insns don't have a delay slot. + If a read from LO is immediately followed by a branch + to a write to LO we have a read followed by a write + less than 2 insns away. We assume the target of + a branch might be a write to LO, and insert a nop + between a read and an immediately following branch. */ + else if (mips_opts.mips16 + && (mips_optimize == 0 + || (pinfo & MIPS16_INSN_BRANCH))) + nops += 1; + } + else if (prev_insn.insn_mo->pinfo & INSN_READ_HI) + { + /* The previous instruction reads the HI register; if the + current instruction writes to the HI register, we must + insert a NOP. Some newer processors have interlocks. + Also the note tx39's multiply above. */ + if (! (hilo_interlocks + || (mips_cpu == 3900 && (pinfo & INSN_MULT))) + && (mips_optimize == 0 + || (pinfo & INSN_WRITE_HI))) + nops += 2; + /* Most mips16 branch insns don't have a delay slot. + If a read from HI is immediately followed by a branch + to a write to HI we have a read followed by a write + less than 2 insns away. We assume the target of + a branch might be a write to HI, and insert a nop + between a read and an immediately following branch. */ + else if (mips_opts.mips16 + && (mips_optimize == 0 + || (pinfo & MIPS16_INSN_BRANCH))) + nops += 1; + } + + /* If the previous instruction was in a noreorder section, then + we don't want to insert the nop after all. */ + /* Itbl support may require additional care here. */ + if (prev_insn_unreordered) + nops = 0; + + /* There are two cases which require two intervening + instructions: 1) setting the condition codes using a move to + coprocessor instruction which requires a general coprocessor + delay and then reading the condition codes 2) reading the HI + or LO register and then writing to it (except on processors + which have interlocks). If we are not already emitting a NOP + instruction, we must check for these cases compared to the + instruction previous to the previous instruction. */ + if ((! mips_opts.mips16 + && ISA_HAS_COPROC_DELAYS (mips_opts.isa) + && (prev_prev_insn.insn_mo->pinfo & INSN_COPROC_MOVE_DELAY) + && (prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE) + && (pinfo & INSN_READ_COND_CODE) + && ! cop_interlocks) + || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_LO) + && (pinfo & INSN_WRITE_LO) + && ! (hilo_interlocks + || (mips_cpu == 3900 && (pinfo & INSN_MULT)))) + || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI) + && (pinfo & INSN_WRITE_HI) + && ! (hilo_interlocks + || (mips_cpu == 3900 && (pinfo & INSN_MULT))))) + prev_prev_nop = 1; + else + prev_prev_nop = 0; + + if (prev_prev_insn_unreordered) + prev_prev_nop = 0; + + if (prev_prev_nop && nops == 0) + ++nops; + + /* If we are being given a nop instruction, don't bother with + one of the nops we would otherwise output. This will only + happen when a nop instruction is used with mips_optimize set + to 0. */ + if (nops > 0 + && ! mips_opts.noreorder + && ip->insn_opcode == (mips_opts.mips16 ? 0x6500 : 0)) + --nops; + + /* Now emit the right number of NOP instructions. */ + if (nops > 0 && ! mips_opts.noreorder) + { + fragS *old_frag; + unsigned long old_frag_offset; + int i; + struct insn_label_list *l; + + old_frag = frag_now; + old_frag_offset = frag_now_fix (); + + for (i = 0; i < nops; i++) + emit_nop (); + + if (listing) + { + listing_prev_line (); + /* We may be at the start of a variant frag. In case we + are, make sure there is enough space for the frag + after the frags created by listing_prev_line. The + argument to frag_grow here must be at least as large + as the argument to all other calls to frag_grow in + this file. We don't have to worry about being in the + middle of a variant frag, because the variants insert + all needed nop instructions themselves. */ + frag_grow (40); + } + + for (l = insn_labels; l != NULL; l = l->next) + { + assert (S_GET_SEGMENT (l->label) == now_seg); + symbol_set_frag (l->label, frag_now); + S_SET_VALUE (l->label, (valueT) frag_now_fix ()); + /* mips16 text labels are stored as odd. */ + if (mips_opts.mips16) + S_SET_VALUE (l->label, S_GET_VALUE (l->label) + 1); + } + +#ifndef NO_ECOFF_DEBUGGING + if (ECOFF_DEBUGGING) + ecoff_fix_loc (old_frag, old_frag_offset); +#endif + } + else if (prev_nop_frag != NULL) + { + /* We have a frag holding nops we may be able to remove. If + we don't need any nops, we can decrease the size of + prev_nop_frag by the size of one instruction. If we do + need some nops, we count them in prev_nops_required. */ + if (prev_nop_frag_since == 0) + { + if (nops == 0) + { + prev_nop_frag->fr_fix -= mips_opts.mips16 ? 2 : 4; + --prev_nop_frag_holds; + } + else + prev_nop_frag_required += nops; + } + else + { + if (prev_prev_nop == 0) + { + prev_nop_frag->fr_fix -= mips_opts.mips16 ? 2 : 4; + --prev_nop_frag_holds; + } + else + ++prev_nop_frag_required; + } + + if (prev_nop_frag_holds <= prev_nop_frag_required) + prev_nop_frag = NULL; + + ++prev_nop_frag_since; + + /* Sanity check: by the time we reach the second instruction + after prev_nop_frag, we should have used up all the nops + one way or another. */ + assert (prev_nop_frag_since <= 1 || prev_nop_frag == NULL); + } + } + + if (reloc_type > BFD_RELOC_UNUSED) + { + /* We need to set up a variant frag. */ + assert (mips_opts.mips16 && address_expr != NULL); + f = frag_var (rs_machine_dependent, 4, 0, + RELAX_MIPS16_ENCODE (reloc_type - BFD_RELOC_UNUSED, + mips16_small, mips16_ext, + (prev_pinfo + & INSN_UNCOND_BRANCH_DELAY), + (prev_insn_reloc_type + == BFD_RELOC_MIPS16_JMP)), + make_expr_symbol (address_expr), (offsetT) 0, + (char *) NULL); + } + else if (place != NULL) + f = place; + else if (mips_opts.mips16 + && ! ip->use_extend + && reloc_type != BFD_RELOC_MIPS16_JMP) + { + /* Make sure there is enough room to swap this instruction with + a following jump instruction. */ + frag_grow (6); + f = frag_more (2); + } + else + { + if (mips_opts.mips16 + && mips_opts.noreorder + && (prev_pinfo & INSN_UNCOND_BRANCH_DELAY) != 0) + as_warn (_("extended instruction in delay slot")); + + f = frag_more (4); + } + + fixp = NULL; + if (address_expr != NULL && reloc_type < BFD_RELOC_UNUSED) + { + if (address_expr->X_op == O_constant) + { + switch (reloc_type) + { + case BFD_RELOC_32: + ip->insn_opcode |= address_expr->X_add_number; + break; + + case BFD_RELOC_LO16: + ip->insn_opcode |= address_expr->X_add_number & 0xffff; + break; + + case BFD_RELOC_MIPS_JMP: + if ((address_expr->X_add_number & 3) != 0) + as_bad (_("jump to misaligned address (0x%lx)"), + (unsigned long) address_expr->X_add_number); + ip->insn_opcode |= (address_expr->X_add_number >> 2) & 0x3ffffff; + break; + + case BFD_RELOC_MIPS16_JMP: + if ((address_expr->X_add_number & 3) != 0) + as_bad (_("jump to misaligned address (0x%lx)"), + (unsigned long) address_expr->X_add_number); + ip->insn_opcode |= + (((address_expr->X_add_number & 0x7c0000) << 3) + | ((address_expr->X_add_number & 0xf800000) >> 7) + | ((address_expr->X_add_number & 0x3fffc) >> 2)); + break; + + + case BFD_RELOC_16_PCREL_S2: + goto need_reloc; + + default: + internalError (); + } + } + else + { + need_reloc: + /* Don't generate a reloc if we are writing into a variant + frag. */ + if (place == NULL) + { + fixp = fix_new_exp (frag_now, f - frag_now->fr_literal, 4, + address_expr, + reloc_type == BFD_RELOC_16_PCREL_S2, + reloc_type); + if (unmatched_hi) + { + struct mips_hi_fixup *hi_fixup; + + assert (reloc_type == BFD_RELOC_HI16_S); + hi_fixup = ((struct mips_hi_fixup *) + xmalloc (sizeof (struct mips_hi_fixup))); + hi_fixup->fixp = fixp; + hi_fixup->seg = now_seg; + hi_fixup->next = mips_hi_fixup_list; + mips_hi_fixup_list = hi_fixup; + } + } + } + } + + if (! mips_opts.mips16) + md_number_to_chars (f, ip->insn_opcode, 4); + else if (reloc_type == BFD_RELOC_MIPS16_JMP) + { + md_number_to_chars (f, ip->insn_opcode >> 16, 2); + md_number_to_chars (f + 2, ip->insn_opcode & 0xffff, 2); + } + else + { + if (ip->use_extend) + { + md_number_to_chars (f, 0xf000 | ip->extend, 2); + f += 2; + } + md_number_to_chars (f, ip->insn_opcode, 2); + } + + /* Update the register mask information. */ + if (! mips_opts.mips16) + { + if (pinfo & INSN_WRITE_GPR_D) + mips_gprmask |= 1 << ((ip->insn_opcode >> OP_SH_RD) & OP_MASK_RD); + if ((pinfo & (INSN_WRITE_GPR_T | INSN_READ_GPR_T)) != 0) + mips_gprmask |= 1 << ((ip->insn_opcode >> OP_SH_RT) & OP_MASK_RT); + if (pinfo & INSN_READ_GPR_S) + mips_gprmask |= 1 << ((ip->insn_opcode >> OP_SH_RS) & OP_MASK_RS); + if (pinfo & INSN_WRITE_GPR_31) + mips_gprmask |= 1 << 31; + if (pinfo & INSN_WRITE_FPR_D) + mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FD) & OP_MASK_FD); + if ((pinfo & (INSN_WRITE_FPR_S | INSN_READ_FPR_S)) != 0) + mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FS) & OP_MASK_FS); + if ((pinfo & (INSN_WRITE_FPR_T | INSN_READ_FPR_T)) != 0) + mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FT) & OP_MASK_FT); + if ((pinfo & INSN_READ_FPR_R) != 0) + mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FR) & OP_MASK_FR); + if (pinfo & INSN_COP) + { + /* We don't keep enough information to sort these cases out. + The itbl support does keep this information however, although + we currently don't support itbl fprmats as part of the cop + instruction. May want to add this support in the future. */ + } + /* Never set the bit for $0, which is always zero. */ + mips_gprmask &=~ 1 << 0; + } + else + { + if (pinfo & (MIPS16_INSN_WRITE_X | MIPS16_INSN_READ_X)) + mips_gprmask |= 1 << ((ip->insn_opcode >> MIPS16OP_SH_RX) + & MIPS16OP_MASK_RX); + if (pinfo & (MIPS16_INSN_WRITE_Y | MIPS16_INSN_READ_Y)) + mips_gprmask |= 1 << ((ip->insn_opcode >> MIPS16OP_SH_RY) + & MIPS16OP_MASK_RY); + if (pinfo & MIPS16_INSN_WRITE_Z) + mips_gprmask |= 1 << ((ip->insn_opcode >> MIPS16OP_SH_RZ) + & MIPS16OP_MASK_RZ); + if (pinfo & (MIPS16_INSN_WRITE_T | MIPS16_INSN_READ_T)) + mips_gprmask |= 1 << TREG; + if (pinfo & (MIPS16_INSN_WRITE_SP | MIPS16_INSN_READ_SP)) + mips_gprmask |= 1 << SP; + if (pinfo & (MIPS16_INSN_WRITE_31 | MIPS16_INSN_READ_31)) + mips_gprmask |= 1 << RA; + if (pinfo & MIPS16_INSN_WRITE_GPR_Y) + mips_gprmask |= 1 << MIPS16OP_EXTRACT_REG32R (ip->insn_opcode); + if (pinfo & MIPS16_INSN_READ_Z) + mips_gprmask |= 1 << ((ip->insn_opcode >> MIPS16OP_SH_MOVE32Z) + & MIPS16OP_MASK_MOVE32Z); + if (pinfo & MIPS16_INSN_READ_GPR_X) + mips_gprmask |= 1 << ((ip->insn_opcode >> MIPS16OP_SH_REGR32) + & MIPS16OP_MASK_REGR32); + } + + if (place == NULL && ! mips_opts.noreorder) + { + /* Filling the branch delay slot is more complex. We try to + switch the branch with the previous instruction, which we can + do if the previous instruction does not set up a condition + that the branch tests and if the branch is not itself the + target of any branch. */ + if ((pinfo & INSN_UNCOND_BRANCH_DELAY) + || (pinfo & INSN_COND_BRANCH_DELAY)) + { + if (mips_optimize < 2 + /* If we have seen .set volatile or .set nomove, don't + optimize. */ + || mips_opts.nomove != 0 + /* If we had to emit any NOP instructions, then we + already know we can not swap. */ + || nops != 0 + /* If we don't even know the previous insn, we can not + swap. */ + || ! prev_insn_valid + /* If the previous insn is already in a branch delay + slot, then we can not swap. */ + || prev_insn_is_delay_slot + /* If the previous previous insn was in a .set + noreorder, we can't swap. Actually, the MIPS + assembler will swap in this situation. However, gcc + configured -with-gnu-as will generate code like + .set noreorder + lw $4,XXX + .set reorder + INSN + bne $4,$0,foo + in which we can not swap the bne and INSN. If gcc is + not configured -with-gnu-as, it does not output the + .set pseudo-ops. We don't have to check + prev_insn_unreordered, because prev_insn_valid will + be 0 in that case. We don't want to use + prev_prev_insn_valid, because we do want to be able + to swap at the start of a function. */ + || prev_prev_insn_unreordered + /* If the branch is itself the target of a branch, we + can not swap. We cheat on this; all we check for is + whether there is a label on this instruction. If + there are any branches to anything other than a + label, users must use .set noreorder. */ + || insn_labels != NULL + /* If the previous instruction is in a variant frag, we + can not do the swap. This does not apply to the + mips16, which uses variant frags for different + purposes. */ + || (! mips_opts.mips16 + && prev_insn_frag->fr_type == rs_machine_dependent) + /* If the branch reads the condition codes, we don't + even try to swap, because in the sequence + ctc1 $X,$31 + INSN + INSN + bc1t LABEL + we can not swap, and I don't feel like handling that + case. */ + || (! mips_opts.mips16 + && ISA_HAS_COPROC_DELAYS (mips_opts.isa) + && (pinfo & INSN_READ_COND_CODE)) + /* We can not swap with an instruction that requires a + delay slot, becase the target of the branch might + interfere with that instruction. */ + || (! mips_opts.mips16 + && ISA_HAS_COPROC_DELAYS (mips_opts.isa) + && (prev_pinfo + /* Itbl support may require additional care here. */ + & (INSN_LOAD_COPROC_DELAY + | INSN_COPROC_MOVE_DELAY + | INSN_WRITE_COND_CODE))) + || (! (hilo_interlocks + || (mips_cpu == 3900 && (pinfo & INSN_MULT))) + && (prev_pinfo + & (INSN_READ_LO + | INSN_READ_HI))) + || (! mips_opts.mips16 + && ! gpr_interlocks + && (prev_pinfo & INSN_LOAD_MEMORY_DELAY)) + || (! mips_opts.mips16 + && mips_opts.isa == 1 + /* Itbl support may require additional care here. */ + && (prev_pinfo & INSN_COPROC_MEMORY_DELAY)) + /* We can not swap with a branch instruction. */ + || (prev_pinfo + & (INSN_UNCOND_BRANCH_DELAY + | INSN_COND_BRANCH_DELAY + | INSN_COND_BRANCH_LIKELY)) + /* We do not swap with a trap instruction, since it + complicates trap handlers to have the trap + instruction be in a delay slot. */ + || (prev_pinfo & INSN_TRAP) + /* If the branch reads a register that the previous + instruction sets, we can not swap. */ + || (! mips_opts.mips16 + && (prev_pinfo & INSN_WRITE_GPR_T) + && insn_uses_reg (ip, + ((prev_insn.insn_opcode >> OP_SH_RT) + & OP_MASK_RT), + MIPS_GR_REG)) + || (! mips_opts.mips16 + && (prev_pinfo & INSN_WRITE_GPR_D) + && insn_uses_reg (ip, + ((prev_insn.insn_opcode >> OP_SH_RD) + & OP_MASK_RD), + MIPS_GR_REG)) + || (mips_opts.mips16 + && (((prev_pinfo & MIPS16_INSN_WRITE_X) + && insn_uses_reg (ip, + ((prev_insn.insn_opcode + >> MIPS16OP_SH_RX) + & MIPS16OP_MASK_RX), + MIPS16_REG)) + || ((prev_pinfo & MIPS16_INSN_WRITE_Y) + && insn_uses_reg (ip, + ((prev_insn.insn_opcode + >> MIPS16OP_SH_RY) + & MIPS16OP_MASK_RY), + MIPS16_REG)) + || ((prev_pinfo & MIPS16_INSN_WRITE_Z) + && insn_uses_reg (ip, + ((prev_insn.insn_opcode + >> MIPS16OP_SH_RZ) + & MIPS16OP_MASK_RZ), + MIPS16_REG)) + || ((prev_pinfo & MIPS16_INSN_WRITE_T) + && insn_uses_reg (ip, TREG, MIPS_GR_REG)) + || ((prev_pinfo & MIPS16_INSN_WRITE_31) + && insn_uses_reg (ip, RA, MIPS_GR_REG)) + || ((prev_pinfo & MIPS16_INSN_WRITE_GPR_Y) + && insn_uses_reg (ip, + MIPS16OP_EXTRACT_REG32R (prev_insn. + insn_opcode), + MIPS_GR_REG)))) + /* If the branch writes a register that the previous + instruction sets, we can not swap (we know that + branches write only to RD or to $31). */ + || (! mips_opts.mips16 + && (prev_pinfo & INSN_WRITE_GPR_T) + && (((pinfo & INSN_WRITE_GPR_D) + && (((prev_insn.insn_opcode >> OP_SH_RT) & OP_MASK_RT) + == ((ip->insn_opcode >> OP_SH_RD) & OP_MASK_RD))) + || ((pinfo & INSN_WRITE_GPR_31) + && (((prev_insn.insn_opcode >> OP_SH_RT) + & OP_MASK_RT) + == 31)))) + || (! mips_opts.mips16 + && (prev_pinfo & INSN_WRITE_GPR_D) + && (((pinfo & INSN_WRITE_GPR_D) + && (((prev_insn.insn_opcode >> OP_SH_RD) & OP_MASK_RD) + == ((ip->insn_opcode >> OP_SH_RD) & OP_MASK_RD))) + || ((pinfo & INSN_WRITE_GPR_31) + && (((prev_insn.insn_opcode >> OP_SH_RD) + & OP_MASK_RD) + == 31)))) + || (mips_opts.mips16 + && (pinfo & MIPS16_INSN_WRITE_31) + && ((prev_pinfo & MIPS16_INSN_WRITE_31) + || ((prev_pinfo & MIPS16_INSN_WRITE_GPR_Y) + && (MIPS16OP_EXTRACT_REG32R (prev_insn.insn_opcode) + == RA)))) + /* If the branch writes a register that the previous + instruction reads, we can not swap (we know that + branches only write to RD or to $31). */ + || (! mips_opts.mips16 + && (pinfo & INSN_WRITE_GPR_D) + && insn_uses_reg (&prev_insn, + ((ip->insn_opcode >> OP_SH_RD) + & OP_MASK_RD), + MIPS_GR_REG)) + || (! mips_opts.mips16 + && (pinfo & INSN_WRITE_GPR_31) + && insn_uses_reg (&prev_insn, 31, MIPS_GR_REG)) + || (mips_opts.mips16 + && (pinfo & MIPS16_INSN_WRITE_31) + && insn_uses_reg (&prev_insn, RA, MIPS_GR_REG)) + /* If we are generating embedded PIC code, the branch + might be expanded into a sequence which uses $at, so + we can't swap with an instruction which reads it. */ + || (mips_pic == EMBEDDED_PIC + && insn_uses_reg (&prev_insn, AT, MIPS_GR_REG)) + /* If the previous previous instruction has a load + delay, and sets a register that the branch reads, we + can not swap. */ + || (! mips_opts.mips16 + && ISA_HAS_COPROC_DELAYS (mips_opts.isa) + /* Itbl support may require additional care here. */ + && ((prev_prev_insn.insn_mo->pinfo & INSN_LOAD_COPROC_DELAY) + || (! gpr_interlocks + && (prev_prev_insn.insn_mo->pinfo + & INSN_LOAD_MEMORY_DELAY))) + && insn_uses_reg (ip, + ((prev_prev_insn.insn_opcode >> OP_SH_RT) + & OP_MASK_RT), + MIPS_GR_REG)) + /* If one instruction sets a condition code and the + other one uses a condition code, we can not swap. */ + || ((pinfo & INSN_READ_COND_CODE) + && (prev_pinfo & INSN_WRITE_COND_CODE)) + || ((pinfo & INSN_WRITE_COND_CODE) + && (prev_pinfo & INSN_READ_COND_CODE)) + /* If the previous instruction uses the PC, we can not + swap. */ + || (mips_opts.mips16 + && (prev_pinfo & MIPS16_INSN_READ_PC)) + /* If the previous instruction was extended, we can not + swap. */ + || (mips_opts.mips16 && prev_insn_extended) + /* If the previous instruction had a fixup in mips16 + mode, we can not swap. This normally means that the + previous instruction was a 4 byte branch anyhow. */ + || (mips_opts.mips16 && prev_insn_fixp) + /* If the previous instruction is a sync, sync.l, or + sync.p, we can not swap. */ + || (prev_pinfo & INSN_SYNC)) + { + /* We could do even better for unconditional branches to + portions of this object file; we could pick up the + instruction at the destination, put it in the delay + slot, and bump the destination address. */ + emit_nop (); + /* Update the previous insn information. */ + prev_prev_insn = *ip; + prev_insn.insn_mo = &dummy_opcode; + } + else + { + /* It looks like we can actually do the swap. */ + if (! mips_opts.mips16) + { + char *prev_f; + char temp[4]; + + prev_f = prev_insn_frag->fr_literal + prev_insn_where; + memcpy (temp, prev_f, 4); + memcpy (prev_f, f, 4); + memcpy (f, temp, 4); + if (prev_insn_fixp) + { + prev_insn_fixp->fx_frag = frag_now; + prev_insn_fixp->fx_where = f - frag_now->fr_literal; + } + if (fixp) + { + fixp->fx_frag = prev_insn_frag; + fixp->fx_where = prev_insn_where; + } + } + else + { + char *prev_f; + char temp[2]; + + assert (prev_insn_fixp == NULL); + prev_f = prev_insn_frag->fr_literal + prev_insn_where; + memcpy (temp, prev_f, 2); + memcpy (prev_f, f, 2); + if (reloc_type != BFD_RELOC_MIPS16_JMP) + { + assert (reloc_type == BFD_RELOC_UNUSED); + memcpy (f, temp, 2); + } + else + { + memcpy (f, f + 2, 2); + memcpy (f + 2, temp, 2); + } + if (fixp) + { + fixp->fx_frag = prev_insn_frag; + fixp->fx_where = prev_insn_where; + } + } + + /* Update the previous insn information; leave prev_insn + unchanged. */ + prev_prev_insn = *ip; + } + prev_insn_is_delay_slot = 1; + + /* If that was an unconditional branch, forget the previous + insn information. */ + if (pinfo & INSN_UNCOND_BRANCH_DELAY) + { + prev_prev_insn.insn_mo = &dummy_opcode; + prev_insn.insn_mo = &dummy_opcode; + } + + prev_insn_fixp = NULL; + prev_insn_reloc_type = BFD_RELOC_UNUSED; + prev_insn_extended = 0; + } + else if (pinfo & INSN_COND_BRANCH_LIKELY) + { + /* We don't yet optimize a branch likely. What we should do + is look at the target, copy the instruction found there + into the delay slot, and increment the branch to jump to + the next instruction. */ + emit_nop (); + /* Update the previous insn information. */ + prev_prev_insn = *ip; + prev_insn.insn_mo = &dummy_opcode; + prev_insn_fixp = NULL; + prev_insn_reloc_type = BFD_RELOC_UNUSED; + prev_insn_extended = 0; + } + else + { + /* Update the previous insn information. */ + if (nops > 0) + prev_prev_insn.insn_mo = &dummy_opcode; + else + prev_prev_insn = prev_insn; + prev_insn = *ip; + + /* Any time we see a branch, we always fill the delay slot + immediately; since this insn is not a branch, we know it + is not in a delay slot. */ + prev_insn_is_delay_slot = 0; + + prev_insn_fixp = fixp; + prev_insn_reloc_type = reloc_type; + if (mips_opts.mips16) + prev_insn_extended = (ip->use_extend + || reloc_type > BFD_RELOC_UNUSED); + } + + prev_prev_insn_unreordered = prev_insn_unreordered; + prev_insn_unreordered = 0; + prev_insn_frag = frag_now; + prev_insn_where = f - frag_now->fr_literal; + prev_insn_valid = 1; + } + else if (place == NULL) + { + /* We need to record a bit of information even when we are not + reordering, in order to determine the base address for mips16 + PC relative relocs. */ + prev_prev_insn = prev_insn; + prev_insn = *ip; + prev_insn_reloc_type = reloc_type; + prev_prev_insn_unreordered = prev_insn_unreordered; + prev_insn_unreordered = 1; + } + + /* We just output an insn, so the next one doesn't have a label. */ + mips_clear_insn_labels (); + + /* We must ensure that a fixup associated with an unmatched %hi + reloc does not become a variant frag. Otherwise, the + rearrangement of %hi relocs in frob_file may confuse + tc_gen_reloc. */ + if (unmatched_hi) + { + frag_wane (frag_now); + frag_new (0); + } +} + +/* This function forgets that there was any previous instruction or + label. If PRESERVE is non-zero, it remembers enough information to + know whether nops are needed before a noreorder section. */ + +static void +mips_no_prev_insn (preserve) + int preserve; +{ + if (! preserve) + { + prev_insn.insn_mo = &dummy_opcode; + prev_prev_insn.insn_mo = &dummy_opcode; + prev_nop_frag = NULL; + prev_nop_frag_holds = 0; + prev_nop_frag_required = 0; + prev_nop_frag_since = 0; + } + prev_insn_valid = 0; + prev_insn_is_delay_slot = 0; + prev_insn_unreordered = 0; + prev_insn_extended = 0; + prev_insn_reloc_type = BFD_RELOC_UNUSED; + prev_prev_insn_unreordered = 0; + mips_clear_insn_labels (); +} + +/* This function must be called whenever we turn on noreorder or emit + something other than instructions. It inserts any NOPS which might + be needed by the previous instruction, and clears the information + kept for the previous instructions. The INSNS parameter is true if + instructions are to follow. */ + +static void +mips_emit_delays (insns) + boolean insns; +{ + if (! mips_opts.noreorder) + { + int nops; + + nops = 0; + if ((! mips_opts.mips16 + && ISA_HAS_COPROC_DELAYS (mips_opts.isa) + && (! cop_interlocks + && (prev_insn.insn_mo->pinfo + & (INSN_LOAD_COPROC_DELAY + | INSN_COPROC_MOVE_DELAY + | INSN_WRITE_COND_CODE)))) + || (! hilo_interlocks + && (prev_insn.insn_mo->pinfo + & (INSN_READ_LO + | INSN_READ_HI))) + || (! mips_opts.mips16 + && ! gpr_interlocks + && (prev_insn.insn_mo->pinfo + & INSN_LOAD_MEMORY_DELAY)) + || (! mips_opts.mips16 + && mips_opts.isa == 1 + && (prev_insn.insn_mo->pinfo + & INSN_COPROC_MEMORY_DELAY))) + { + /* Itbl support may require additional care here. */ + ++nops; + if ((! mips_opts.mips16 + && ISA_HAS_COPROC_DELAYS (mips_opts.isa) + && (! cop_interlocks + && prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)) + || (! hilo_interlocks + && ((prev_insn.insn_mo->pinfo & INSN_READ_HI) + || (prev_insn.insn_mo->pinfo & INSN_READ_LO)))) + ++nops; + + if (prev_insn_unreordered) + nops = 0; + } + else if ((! mips_opts.mips16 + && ISA_HAS_COPROC_DELAYS (mips_opts.isa) + && (! cop_interlocks + && prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)) + || (! hilo_interlocks + && ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI) + || (prev_prev_insn.insn_mo->pinfo & INSN_READ_LO)))) + { + /* Itbl support may require additional care here. */ + if (! prev_prev_insn_unreordered) + ++nops; + } + + if (nops > 0) + { + struct insn_label_list *l; + + if (insns) + { + /* Record the frag which holds the nop instructions, so + that we can remove them if we don't need them. */ + frag_grow (mips_opts.mips16 ? nops * 2 : nops * 4); + prev_nop_frag = frag_now; + prev_nop_frag_holds = nops; + prev_nop_frag_required = 0; + prev_nop_frag_since = 0; + } + + for (; nops > 0; --nops) + emit_nop (); + + if (insns) + { + /* Move on to a new frag, so that it is safe to simply + decrease the size of prev_nop_frag. */ + frag_wane (frag_now); + frag_new (0); + } + + for (l = insn_labels; l != NULL; l = l->next) + { + assert (S_GET_SEGMENT (l->label) == now_seg); + symbol_set_frag (l->label, frag_now); + S_SET_VALUE (l->label, (valueT) frag_now_fix ()); + /* mips16 text labels are stored as odd. */ + if (mips_opts.mips16) + S_SET_VALUE (l->label, S_GET_VALUE (l->label) + 1); + } + } + } + + /* Mark instruction labels in mips16 mode. */ + if (mips_opts.mips16 && insns) + mips16_mark_labels (); + + mips_no_prev_insn (insns); +} + +/* Build an instruction created by a macro expansion. This is passed + a pointer to the count of instructions created so far, an + expression, the name of the instruction to build, an operand format + string, and corresponding arguments. */ + +#ifdef USE_STDARG +static void +macro_build (char *place, + int *counter, + expressionS * ep, + const char *name, + const char *fmt, + ...) +#else +static void +macro_build (place, counter, ep, name, fmt, va_alist) + char *place; + int *counter; + expressionS *ep; + const char *name; + const char *fmt; + va_dcl +#endif +{ + struct mips_cl_insn insn; + bfd_reloc_code_real_type r; + va_list args; + +#ifdef USE_STDARG + va_start (args, fmt); +#else + va_start (args); +#endif + + /* + * If the macro is about to expand into a second instruction, + * print a warning if needed. We need to pass ip as a parameter + * to generate a better warning message here... + */ + if (mips_opts.warn_about_macros && place == NULL && *counter == 1) + as_warn (_("Macro instruction expanded into multiple instructions")); + + if (place == NULL) + *counter += 1; /* bump instruction counter */ + + if (mips_opts.mips16) + { + mips16_macro_build (place, counter, ep, name, fmt, args); + va_end (args); + return; + } + + r = BFD_RELOC_UNUSED; + insn.insn_mo = (struct mips_opcode *) hash_find (op_hash, name); + assert (insn.insn_mo); + assert (strcmp (name, insn.insn_mo->name) == 0); + + /* Search until we get a match for NAME. */ + while (1) + { + if (strcmp (fmt, insn.insn_mo->args) == 0 + && insn.insn_mo->pinfo != INSN_MACRO + && OPCODE_IS_MEMBER (insn.insn_mo, mips_opts.isa, mips_cpu, + mips_gp32) + && (mips_cpu != 4650 || (insn.insn_mo->pinfo & FP_D) == 0)) + break; + + ++insn.insn_mo; + assert (insn.insn_mo->name); + assert (strcmp (name, insn.insn_mo->name) == 0); + } + + insn.insn_opcode = insn.insn_mo->match; + for (;;) + { + switch (*fmt++) + { + case '\0': + break; + + case ',': + case '(': + case ')': + continue; + + case 't': + case 'w': + case 'E': + insn.insn_opcode |= va_arg (args, int) << 16; + continue; + + case 'c': + case 'T': + case 'W': + insn.insn_opcode |= va_arg (args, int) << 16; + continue; + + case 'd': + case 'G': + insn.insn_opcode |= va_arg (args, int) << 11; + continue; + + case 'V': + case 'S': + insn.insn_opcode |= va_arg (args, int) << 11; + continue; + + case 'z': + continue; + + case '<': + insn.insn_opcode |= va_arg (args, int) << 6; + continue; + + case 'D': + insn.insn_opcode |= va_arg (args, int) << 6; + continue; + + case 'B': + insn.insn_opcode |= va_arg (args, int) << 6; + continue; + + case 'q': + insn.insn_opcode |= va_arg (args, int) << 6; + continue; + + case 'b': + case 's': + case 'r': + case 'v': + insn.insn_opcode |= va_arg (args, int) << 21; + continue; + + case 'i': + case 'j': + case 'o': + r = (bfd_reloc_code_real_type) va_arg (args, int); + assert (r == BFD_RELOC_MIPS_GPREL + || r == BFD_RELOC_MIPS_LITERAL + || r == BFD_RELOC_LO16 + || r == BFD_RELOC_MIPS_GOT16 + || r == BFD_RELOC_MIPS_CALL16 + || r == BFD_RELOC_MIPS_GOT_LO16 + || r == BFD_RELOC_MIPS_CALL_LO16 + || (ep->X_op == O_subtract + && r == BFD_RELOC_PCREL_LO16)); + continue; + + case 'u': + r = (bfd_reloc_code_real_type) va_arg (args, int); + assert (ep != NULL + && (ep->X_op == O_constant + || (ep->X_op == O_symbol + && (r == BFD_RELOC_HI16_S + || r == BFD_RELOC_HI16 + || r == BFD_RELOC_MIPS_GOT_HI16 + || r == BFD_RELOC_MIPS_CALL_HI16)) + || (ep->X_op == O_subtract + && r == BFD_RELOC_PCREL_HI16_S))); + if (ep->X_op == O_constant) + { + insn.insn_opcode |= (ep->X_add_number >> 16) & 0xffff; + ep = NULL; + r = BFD_RELOC_UNUSED; + } + continue; + + case 'p': + assert (ep != NULL); + /* + * This allows macro() to pass an immediate expression for + * creating short branches without creating a symbol. + * Note that the expression still might come from the assembly + * input, in which case the value is not checked for range nor + * is a relocation entry generated (yuck). + */ + if (ep->X_op == O_constant) + { + insn.insn_opcode |= (ep->X_add_number >> 2) & 0xffff; + ep = NULL; + } + else + r = BFD_RELOC_16_PCREL_S2; + continue; + + case 'a': + assert (ep != NULL); + r = BFD_RELOC_MIPS_JMP; + continue; + + case 'C': + insn.insn_opcode |= va_arg (args, unsigned long); + continue; + + default: + internalError (); + } + break; + } + va_end (args); + assert (r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL); + + append_insn (place, &insn, ep, r, false); +} + +static void +mips16_macro_build (place, counter, ep, name, fmt, args) + char *place; + int *counter; + expressionS *ep; + const char *name; + const char *fmt; + va_list args; +{ + struct mips_cl_insn insn; + bfd_reloc_code_real_type r; + + r = BFD_RELOC_UNUSED; + insn.insn_mo = (struct mips_opcode *) hash_find (mips16_op_hash, name); + assert (insn.insn_mo); + assert (strcmp (name, insn.insn_mo->name) == 0); + + while (strcmp (fmt, insn.insn_mo->args) != 0 + || insn.insn_mo->pinfo == INSN_MACRO) + { + ++insn.insn_mo; + assert (insn.insn_mo->name); + assert (strcmp (name, insn.insn_mo->name) == 0); + } + + insn.insn_opcode = insn.insn_mo->match; + insn.use_extend = false; + + for (;;) + { + int c; + + c = *fmt++; + switch (c) + { + case '\0': + break; + + case ',': + case '(': + case ')': + continue; + + case 'y': + case 'w': + insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_RY; + continue; + + case 'x': + case 'v': + insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_RX; + continue; + + case 'z': + insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_RZ; + continue; + + case 'Z': + insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_MOVE32Z; + continue; + + case '0': + case 'S': + case 'P': + case 'R': + continue; + + case 'X': + insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_REGR32; + continue; + + case 'Y': + { + int regno; + + regno = va_arg (args, int); + regno = ((regno & 7) << 2) | ((regno & 0x18) >> 3); + insn.insn_opcode |= regno << MIPS16OP_SH_REG32R; + } + continue; + + case '<': + case '>': + case '4': + case '5': + case 'H': + case 'W': + case 'D': + case 'j': + case '8': + case 'V': + case 'C': + case 'U': + case 'k': + case 'K': + case 'p': + case 'q': + { + assert (ep != NULL); + + if (ep->X_op != O_constant) + r = BFD_RELOC_UNUSED + c; + else + { + mips16_immed ((char *) NULL, 0, c, ep->X_add_number, false, + false, false, &insn.insn_opcode, + &insn.use_extend, &insn.extend); + ep = NULL; + r = BFD_RELOC_UNUSED; + } + } + continue; + + case '6': + insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_IMM6; + continue; + } + + break; + } + + assert (r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL); + + append_insn (place, &insn, ep, r, false); +} + +/* + * Generate a "lui" instruction. + */ +static void +macro_build_lui (place, counter, ep, regnum) + char *place; + int *counter; + expressionS *ep; + int regnum; +{ + expressionS high_expr; + struct mips_cl_insn insn; + bfd_reloc_code_real_type r; + CONST char *name = "lui"; + CONST char *fmt = "t,u"; + + assert (! mips_opts.mips16); + + if (place == NULL) + high_expr = *ep; + else + { + high_expr.X_op = O_constant; + high_expr.X_add_number = ep->X_add_number; + } + + if (high_expr.X_op == O_constant) + { + /* we can compute the instruction now without a relocation entry */ + if (high_expr.X_add_number & 0x8000) + high_expr.X_add_number += 0x10000; + high_expr.X_add_number = + ((unsigned long) high_expr.X_add_number >> 16) & 0xffff; + r = BFD_RELOC_UNUSED; + } + else + { + assert (ep->X_op == O_symbol); + /* _gp_disp is a special case, used from s_cpload. */ + assert (mips_pic == NO_PIC + || strcmp (S_GET_NAME (ep->X_add_symbol), "_gp_disp") == 0); + r = BFD_RELOC_HI16_S; + } + + /* + * If the macro is about to expand into a second instruction, + * print a warning if needed. We need to pass ip as a parameter + * to generate a better warning message here... + */ + if (mips_opts.warn_about_macros && place == NULL && *counter == 1) + as_warn (_("Macro instruction expanded into multiple instructions")); + + if (place == NULL) + *counter += 1; /* bump instruction counter */ + + insn.insn_mo = (struct mips_opcode *) hash_find (op_hash, name); + assert (insn.insn_mo); + assert (strcmp (name, insn.insn_mo->name) == 0); + assert (strcmp (fmt, insn.insn_mo->args) == 0); + + insn.insn_opcode = insn.insn_mo->match | (regnum << OP_SH_RT); + if (r == BFD_RELOC_UNUSED) + { + insn.insn_opcode |= high_expr.X_add_number; + append_insn (place, &insn, NULL, r, false); + } + else + append_insn (place, &insn, &high_expr, r, false); +} + +/* set_at() + * Generates code to set the $at register to true (one) + * if reg is less than the immediate expression. + */ +static void +set_at (counter, reg, unsignedp) + int *counter; + int reg; + int unsignedp; +{ + if (imm_expr.X_op == O_constant + && imm_expr.X_add_number >= -0x8000 + && imm_expr.X_add_number < 0x8000) + macro_build ((char *) NULL, counter, &imm_expr, + unsignedp ? "sltiu" : "slti", + "t,r,j", AT, reg, (int) BFD_RELOC_LO16); + else + { + load_register (counter, AT, &imm_expr, 0); + macro_build ((char *) NULL, counter, NULL, + unsignedp ? "sltu" : "slt", + "d,v,t", AT, reg, AT); + } +} + +/* Warn if an expression is not a constant. */ + +static void +check_absolute_expr (ip, ex) + struct mips_cl_insn *ip; + expressionS *ex; +{ + if (ex->X_op == O_big) + as_bad (_("unsupported large constant")); + else if (ex->X_op != O_constant) + as_bad (_("Instruction %s requires absolute expression"), ip->insn_mo->name); +} + +/* Count the leading zeroes by performing a binary chop. This is a + bulky bit of source, but performance is a LOT better for the + majority of values than a simple loop to count the bits: + for (lcnt = 0; (lcnt < 32); lcnt++) + if ((v) & (1 << (31 - lcnt))) + break; + However it is not code size friendly, and the gain will drop a bit + on certain cached systems. +*/ +#define COUNT_TOP_ZEROES(v) \ + (((v) & ~0xffff) == 0 \ + ? ((v) & ~0xff) == 0 \ + ? ((v) & ~0xf) == 0 \ + ? ((v) & ~0x3) == 0 \ + ? ((v) & ~0x1) == 0 \ + ? !(v) \ + ? 32 \ + : 31 \ + : 30 \ + : ((v) & ~0x7) == 0 \ + ? 29 \ + : 28 \ + : ((v) & ~0x3f) == 0 \ + ? ((v) & ~0x1f) == 0 \ + ? 27 \ + : 26 \ + : ((v) & ~0x7f) == 0 \ + ? 25 \ + : 24 \ + : ((v) & ~0xfff) == 0 \ + ? ((v) & ~0x3ff) == 0 \ + ? ((v) & ~0x1ff) == 0 \ + ? 23 \ + : 22 \ + : ((v) & ~0x7ff) == 0 \ + ? 21 \ + : 20 \ + : ((v) & ~0x3fff) == 0 \ + ? ((v) & ~0x1fff) == 0 \ + ? 19 \ + : 18 \ + : ((v) & ~0x7fff) == 0 \ + ? 17 \ + : 16 \ + : ((v) & ~0xffffff) == 0 \ + ? ((v) & ~0xfffff) == 0 \ + ? ((v) & ~0x3ffff) == 0 \ + ? ((v) & ~0x1ffff) == 0 \ + ? 15 \ + : 14 \ + : ((v) & ~0x7ffff) == 0 \ + ? 13 \ + : 12 \ + : ((v) & ~0x3fffff) == 0 \ + ? ((v) & ~0x1fffff) == 0 \ + ? 11 \ + : 10 \ + : ((v) & ~0x7fffff) == 0 \ + ? 9 \ + : 8 \ + : ((v) & ~0xfffffff) == 0 \ + ? ((v) & ~0x3ffffff) == 0 \ + ? ((v) & ~0x1ffffff) == 0 \ + ? 7 \ + : 6 \ + : ((v) & ~0x7ffffff) == 0 \ + ? 5 \ + : 4 \ + : ((v) & ~0x3fffffff) == 0 \ + ? ((v) & ~0x1fffffff) == 0 \ + ? 3 \ + : 2 \ + : ((v) & ~0x7fffffff) == 0 \ + ? 1 \ + : 0) + +/* load_register() + * This routine generates the least number of instructions neccessary to load + * an absolute expression value into a register. + */ +static void +load_register (counter, reg, ep, dbl) + int *counter; + int reg; + expressionS *ep; + int dbl; +{ + int freg; + expressionS hi32, lo32; + + if (ep->X_op != O_big) + { + assert (ep->X_op == O_constant); + if (ep->X_add_number < 0x8000 + && (ep->X_add_number >= 0 + || (ep->X_add_number >= -0x8000 + && (! dbl + || ! ep->X_unsigned + || sizeof (ep->X_add_number) > 4)))) + { + /* We can handle 16 bit signed values with an addiu to + $zero. No need to ever use daddiu here, since $zero and + the result are always correct in 32 bit mode. */ + macro_build ((char *) NULL, counter, ep, "addiu", "t,r,j", reg, 0, + (int) BFD_RELOC_LO16); + return; + } + else if (ep->X_add_number >= 0 && ep->X_add_number < 0x10000) + { + /* We can handle 16 bit unsigned values with an ori to + $zero. */ + macro_build ((char *) NULL, counter, ep, "ori", "t,r,i", reg, 0, + (int) BFD_RELOC_LO16); + return; + } + else if ((((ep->X_add_number &~ (offsetT) 0x7fffffff) == 0 + || ((ep->X_add_number &~ (offsetT) 0x7fffffff) + == ~ (offsetT) 0x7fffffff)) + && (! dbl + || ! ep->X_unsigned + || sizeof (ep->X_add_number) > 4 + || (ep->X_add_number & 0x80000000) == 0)) + || ((! ISA_HAS_64BIT_REGS (mips_opts.isa) || ! dbl) + && (ep->X_add_number &~ (offsetT) 0xffffffff) == 0) + || (! ISA_HAS_64BIT_REGS (mips_opts.isa) + && ! dbl + && ((ep->X_add_number &~ (offsetT) 0xffffffff) + == ~ (offsetT) 0xffffffff))) + { + /* 32 bit values require an lui. */ + macro_build ((char *) NULL, counter, ep, "lui", "t,u", reg, + (int) BFD_RELOC_HI16); + if ((ep->X_add_number & 0xffff) != 0) + macro_build ((char *) NULL, counter, ep, "ori", "t,r,i", reg, reg, + (int) BFD_RELOC_LO16); + return; + } + } + + /* The value is larger than 32 bits. */ + + if (! ISA_HAS_64BIT_REGS (mips_opts.isa)) + { + as_bad (_("Number larger than 32 bits")); + macro_build ((char *) NULL, counter, ep, "addiu", "t,r,j", reg, 0, + (int) BFD_RELOC_LO16); + return; + } + + if (ep->X_op != O_big) + { + hi32 = *ep; + hi32.X_add_number = (valueT) hi32.X_add_number >> 16; + hi32.X_add_number = (valueT) hi32.X_add_number >> 16; + hi32.X_add_number &= 0xffffffff; + lo32 = *ep; + lo32.X_add_number &= 0xffffffff; + } + else + { + assert (ep->X_add_number > 2); + if (ep->X_add_number == 3) + generic_bignum[3] = 0; + else if (ep->X_add_number > 4) + as_bad (_("Number larger than 64 bits")); + lo32.X_op = O_constant; + lo32.X_add_number = generic_bignum[0] + (generic_bignum[1] << 16); + hi32.X_op = O_constant; + hi32.X_add_number = generic_bignum[2] + (generic_bignum[3] << 16); + } + + if (hi32.X_add_number == 0) + freg = 0; + else + { + int shift, bit; + unsigned long hi, lo; + + if (hi32.X_add_number == 0xffffffff) + { + if ((lo32.X_add_number & 0xffff8000) == 0xffff8000) + { + macro_build ((char *) NULL, counter, &lo32, "addiu", "t,r,j", + reg, 0, (int) BFD_RELOC_LO16); + return; + } + if (lo32.X_add_number & 0x80000000) + { + macro_build ((char *) NULL, counter, &lo32, "lui", "t,u", reg, + (int) BFD_RELOC_HI16); + if (lo32.X_add_number & 0xffff) + macro_build ((char *) NULL, counter, &lo32, "ori", "t,r,i", + reg, reg, (int) BFD_RELOC_LO16); + return; + } + } + + /* Check for 16bit shifted constant. We know that hi32 is + non-zero, so start the mask on the first bit of the hi32 + value. */ + shift = 17; + do + { + unsigned long himask, lomask; + + if (shift < 32) + { + himask = 0xffff >> (32 - shift); + lomask = (0xffff << shift) & 0xffffffff; + } + else + { + himask = 0xffff << (shift - 32); + lomask = 0; + } + if ((hi32.X_add_number & ~ (offsetT) himask) == 0 + && (lo32.X_add_number & ~ (offsetT) lomask) == 0) + { + expressionS tmp; + + tmp.X_op = O_constant; + if (shift < 32) + tmp.X_add_number = ((hi32.X_add_number << (32 - shift)) + | (lo32.X_add_number >> shift)); + else + tmp.X_add_number = hi32.X_add_number >> (shift - 32); + macro_build ((char *) NULL, counter, &tmp, "ori", "t,r,i", reg, 0, + (int) BFD_RELOC_LO16); + macro_build ((char *) NULL, counter, NULL, + (shift >= 32) ? "dsll32" : "dsll", + "d,w,<", reg, reg, + (shift >= 32) ? shift - 32 : shift); + return; + } + shift++; + } while (shift <= (64 - 16)); + + /* Find the bit number of the lowest one bit, and store the + shifted value in hi/lo. */ + hi = (unsigned long) (hi32.X_add_number & 0xffffffff); + lo = (unsigned long) (lo32.X_add_number & 0xffffffff); + if (lo != 0) + { + bit = 0; + while ((lo & 1) == 0) + { + lo >>= 1; + ++bit; + } + lo |= (hi & (((unsigned long) 1 << bit) - 1)) << (32 - bit); + hi >>= bit; + } + else + { + bit = 32; + while ((hi & 1) == 0) + { + hi >>= 1; + ++bit; + } + lo = hi; + hi = 0; + } + + /* Optimize if the shifted value is a (power of 2) - 1. */ + if ((hi == 0 && ((lo + 1) & lo) == 0) + || (lo == 0xffffffff && ((hi + 1) & hi) == 0)) + { + shift = COUNT_TOP_ZEROES ((unsigned int) hi32.X_add_number); + if (shift != 0) + { + expressionS tmp; + + /* This instruction will set the register to be all + ones. */ + tmp.X_op = O_constant; + tmp.X_add_number = (offsetT) -1; + macro_build ((char *) NULL, counter, &tmp, "addiu", "t,r,j", + reg, 0, (int) BFD_RELOC_LO16); + if (bit != 0) + { + bit += shift; + macro_build ((char *) NULL, counter, NULL, + (bit >= 32) ? "dsll32" : "dsll", + "d,w,<", reg, reg, + (bit >= 32) ? bit - 32 : bit); + } + macro_build ((char *) NULL, counter, NULL, + (shift >= 32) ? "dsrl32" : "dsrl", + "d,w,<", reg, reg, + (shift >= 32) ? shift - 32 : shift); + return; + } + } + + /* Sign extend hi32 before calling load_register, because we can + generally get better code when we load a sign extended value. */ + if ((hi32.X_add_number & 0x80000000) != 0) + hi32.X_add_number |= ~ (offsetT) 0xffffffff; + load_register (counter, reg, &hi32, 0); + freg = reg; + } + if ((lo32.X_add_number & 0xffff0000) == 0) + { + if (freg != 0) + { + macro_build ((char *) NULL, counter, NULL, "dsll32", "d,w,<", reg, + freg, 0); + freg = reg; + } + } + else + { + expressionS mid16; + + if ((freg == 0) && (lo32.X_add_number == 0xffffffff)) + { + macro_build ((char *) NULL, counter, &lo32, "lui", "t,u", reg, + (int) BFD_RELOC_HI16); + macro_build ((char *) NULL, counter, NULL, "dsrl32", "d,w,<", reg, + reg, 0); + return; + } + + if (freg != 0) + { + macro_build ((char *) NULL, counter, NULL, "dsll", "d,w,<", reg, + freg, 16); + freg = reg; + } + mid16 = lo32; + mid16.X_add_number >>= 16; + macro_build ((char *) NULL, counter, &mid16, "ori", "t,r,i", reg, + freg, (int) BFD_RELOC_LO16); + macro_build ((char *) NULL, counter, NULL, "dsll", "d,w,<", reg, + reg, 16); + freg = reg; + } + if ((lo32.X_add_number & 0xffff) != 0) + macro_build ((char *) NULL, counter, &lo32, "ori", "t,r,i", reg, freg, + (int) BFD_RELOC_LO16); +} + +/* Load an address into a register. */ + +static void +load_address (counter, reg, ep) + int *counter; + int reg; + expressionS *ep; +{ + char *p; + + if (ep->X_op != O_constant + && ep->X_op != O_symbol) + { + as_bad (_("expression too complex")); + ep->X_op = O_constant; + } + + if (ep->X_op == O_constant) + { + load_register (counter, reg, ep, 0); + return; + } + + if (mips_pic == NO_PIC) + { + /* If this is a reference to a GP relative symbol, we want + addiu $reg,$gp,<sym> (BFD_RELOC_MIPS_GPREL) + Otherwise we want + lui $reg,<sym> (BFD_RELOC_HI16_S) + addiu $reg,$reg,<sym> (BFD_RELOC_LO16) + If we have an addend, we always use the latter form. */ + if ((valueT) ep->X_add_number >= MAX_GPREL_OFFSET + || nopic_need_relax (ep->X_add_symbol, 1)) + p = NULL; + else + { + frag_grow (20); + macro_build ((char *) NULL, counter, ep, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addiu" : "daddiu"), + "t,r,j", reg, GP, (int) BFD_RELOC_MIPS_GPREL); + p = frag_var (rs_machine_dependent, 8, 0, + RELAX_ENCODE (4, 8, 0, 4, 0, + mips_opts.warn_about_macros), + ep->X_add_symbol, (offsetT) 0, (char *) NULL); + } + macro_build_lui (p, counter, ep, reg); + if (p != NULL) + p += 4; + macro_build (p, counter, ep, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addiu" : "daddiu"), + "t,r,j", reg, reg, (int) BFD_RELOC_LO16); + } + else if (mips_pic == SVR4_PIC && ! mips_big_got) + { + expressionS ex; + + /* If this is a reference to an external symbol, we want + lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT16) + Otherwise we want + lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT16) + nop + addiu $reg,$reg,<sym> (BFD_RELOC_LO16) + If there is a constant, it must be added in after. */ + ex.X_add_number = ep->X_add_number; + ep->X_add_number = 0; + frag_grow (20); + macro_build ((char *) NULL, counter, ep, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "lw" : "ld"), + "t,o(b)", reg, (int) BFD_RELOC_MIPS_GOT16, GP); + macro_build ((char *) NULL, counter, (expressionS *) NULL, "nop", ""); + p = frag_var (rs_machine_dependent, 4, 0, + RELAX_ENCODE (0, 4, -8, 0, 0, mips_opts.warn_about_macros), + ep->X_add_symbol, (offsetT) 0, (char *) NULL); + macro_build (p, counter, ep, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addiu" : "daddiu"), + "t,r,j", reg, reg, (int) BFD_RELOC_LO16); + if (ex.X_add_number != 0) + { + if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000) + as_bad (_("PIC code offset overflow (max 16 signed bits)")); + ex.X_op = O_constant; + macro_build ((char *) NULL, counter, &ex, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addiu" : "daddiu"), + "t,r,j", reg, reg, (int) BFD_RELOC_LO16); + } + } + else if (mips_pic == SVR4_PIC) + { + expressionS ex; + int off; + + /* This is the large GOT case. If this is a reference to an + external symbol, we want + lui $reg,<sym> (BFD_RELOC_MIPS_GOT_HI16) + addu $reg,$reg,$gp + lw $reg,<sym>($reg) (BFD_RELOC_MIPS_GOT_LO16) + Otherwise, for a reference to a local symbol, we want + lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT16) + nop + addiu $reg,$reg,<sym> (BFD_RELOC_LO16) + If there is a constant, it must be added in after. */ + ex.X_add_number = ep->X_add_number; + ep->X_add_number = 0; + if (reg_needs_delay (GP)) + off = 4; + else + off = 0; + frag_grow (32); + macro_build ((char *) NULL, counter, ep, "lui", "t,u", reg, + (int) BFD_RELOC_MIPS_GOT_HI16); + macro_build ((char *) NULL, counter, (expressionS *) NULL, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addu" : "daddu"), + "d,v,t", reg, reg, GP); + macro_build ((char *) NULL, counter, ep, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "lw" : "ld"), + "t,o(b)", reg, (int) BFD_RELOC_MIPS_GOT_LO16, reg); + p = frag_var (rs_machine_dependent, 12 + off, 0, + RELAX_ENCODE (12, 12 + off, off, 8 + off, 0, + mips_opts.warn_about_macros), + ep->X_add_symbol, (offsetT) 0, (char *) NULL); + if (off > 0) + { + /* We need a nop before loading from $gp. This special + check is required because the lui which starts the main + instruction stream does not refer to $gp, and so will not + insert the nop which may be required. */ + macro_build (p, counter, (expressionS *) NULL, "nop", ""); + p += 4; + } + macro_build (p, counter, ep, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "lw" : "ld"), + "t,o(b)", reg, (int) BFD_RELOC_MIPS_GOT16, GP); + p += 4; + macro_build (p, counter, (expressionS *) NULL, "nop", ""); + p += 4; + macro_build (p, counter, ep, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addiu" : "daddiu"), + "t,r,j", reg, reg, (int) BFD_RELOC_LO16); + if (ex.X_add_number != 0) + { + if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000) + as_bad (_("PIC code offset overflow (max 16 signed bits)")); + ex.X_op = O_constant; + macro_build ((char *) NULL, counter, &ex, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addiu" : "daddiu"), + "t,r,j", reg, reg, (int) BFD_RELOC_LO16); + } + } + else if (mips_pic == EMBEDDED_PIC) + { + /* We always do + addiu $reg,$gp,<sym> (BFD_RELOC_MIPS_GPREL) + */ + macro_build ((char *) NULL, counter, ep, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addiu" : "daddiu"), + "t,r,j", reg, GP, (int) BFD_RELOC_MIPS_GPREL); + } + else + abort (); +} + +/* + * Build macros + * This routine implements the seemingly endless macro or synthesized + * instructions and addressing modes in the mips assembly language. Many + * of these macros are simple and are similar to each other. These could + * probably be handled by some kind of table or grammer aproach instead of + * this verbose method. Others are not simple macros but are more like + * optimizing code generation. + * One interesting optimization is when several store macros appear + * consecutivly that would load AT with the upper half of the same address. + * The ensuing load upper instructions are ommited. This implies some kind + * of global optimization. We currently only optimize within a single macro. + * For many of the load and store macros if the address is specified as a + * constant expression in the first 64k of memory (ie ld $2,0x4000c) we + * first load register 'at' with zero and use it as the base register. The + * mips assembler simply uses register $zero. Just one tiny optimization + * we're missing. + */ +static void +macro (ip) + struct mips_cl_insn *ip; +{ + register int treg, sreg, dreg, breg; + int tempreg; + int mask; + int icnt = 0; + int used_at; + expressionS expr1; + const char *s; + const char *s2; + const char *fmt; + int likely = 0; + int dbl = 0; + int coproc = 0; + int lr = 0; + int imm = 0; + offsetT maxnum; + int off; + bfd_reloc_code_real_type r; + char *p; + int hold_mips_optimize; + + assert (! mips_opts.mips16); + + treg = (ip->insn_opcode >> 16) & 0x1f; + dreg = (ip->insn_opcode >> 11) & 0x1f; + sreg = breg = (ip->insn_opcode >> 21) & 0x1f; + mask = ip->insn_mo->mask; + + expr1.X_op = O_constant; + expr1.X_op_symbol = NULL; + expr1.X_add_symbol = NULL; + expr1.X_add_number = 1; + + switch (mask) + { + case M_DABS: + dbl = 1; + case M_ABS: + /* bgez $a0,.+12 + move v0,$a0 + sub v0,$zero,$a0 + */ + + mips_emit_delays (true); + ++mips_opts.noreorder; + mips_any_noreorder = 1; + + expr1.X_add_number = 8; + macro_build ((char *) NULL, &icnt, &expr1, "bgez", "s,p", sreg); + if (dreg == sreg) + macro_build ((char *) NULL, &icnt, NULL, "nop", "", 0); + else + macro_build ((char *) NULL, &icnt, NULL, "move", "d,s", dreg, sreg, 0); + macro_build ((char *) NULL, &icnt, NULL, + dbl ? "dsub" : "sub", + "d,v,t", dreg, 0, sreg); + + --mips_opts.noreorder; + return; + + case M_ADD_I: + s = "addi"; + s2 = "add"; + goto do_addi; + case M_ADDU_I: + s = "addiu"; + s2 = "addu"; + goto do_addi; + case M_DADD_I: + dbl = 1; + s = "daddi"; + s2 = "dadd"; + goto do_addi; + case M_DADDU_I: + dbl = 1; + s = "daddiu"; + s2 = "daddu"; + do_addi: + if (imm_expr.X_op == O_constant + && imm_expr.X_add_number >= -0x8000 + && imm_expr.X_add_number < 0x8000) + { + macro_build ((char *) NULL, &icnt, &imm_expr, s, "t,r,j", treg, sreg, + (int) BFD_RELOC_LO16); + return; + } + load_register (&icnt, AT, &imm_expr, dbl); + macro_build ((char *) NULL, &icnt, NULL, s2, "d,v,t", treg, sreg, AT); + break; + + case M_AND_I: + s = "andi"; + s2 = "and"; + goto do_bit; + case M_OR_I: + s = "ori"; + s2 = "or"; + goto do_bit; + case M_NOR_I: + s = ""; + s2 = "nor"; + goto do_bit; + case M_XOR_I: + s = "xori"; + s2 = "xor"; + do_bit: + if (imm_expr.X_op == O_constant + && imm_expr.X_add_number >= 0 + && imm_expr.X_add_number < 0x10000) + { + if (mask != M_NOR_I) + macro_build ((char *) NULL, &icnt, &imm_expr, s, "t,r,i", treg, + sreg, (int) BFD_RELOC_LO16); + else + { + macro_build ((char *) NULL, &icnt, &imm_expr, "ori", "t,r,i", + treg, sreg, (int) BFD_RELOC_LO16); + macro_build ((char *) NULL, &icnt, NULL, "nor", "d,v,t", + treg, treg, 0); + } + return; + } + + load_register (&icnt, AT, &imm_expr, 0); + macro_build ((char *) NULL, &icnt, NULL, s2, "d,v,t", treg, sreg, AT); + break; + + case M_BEQ_I: + s = "beq"; + goto beq_i; + case M_BEQL_I: + s = "beql"; + likely = 1; + goto beq_i; + case M_BNE_I: + s = "bne"; + goto beq_i; + case M_BNEL_I: + s = "bnel"; + likely = 1; + beq_i: + if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0) + { + macro_build ((char *) NULL, &icnt, &offset_expr, s, "s,t,p", sreg, + 0); + return; + } + load_register (&icnt, AT, &imm_expr, 0); + macro_build ((char *) NULL, &icnt, &offset_expr, s, "s,t,p", sreg, AT); + break; + + case M_BGEL: + likely = 1; + case M_BGE: + if (treg == 0) + { + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "bgezl" : "bgez", + "s,p", sreg); + return; + } + if (sreg == 0) + { + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "blezl" : "blez", + "s,p", treg); + return; + } + macro_build ((char *) NULL, &icnt, NULL, "slt", "d,v,t", AT, sreg, treg); + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "beql" : "beq", + "s,t,p", AT, 0); + break; + + case M_BGTL_I: + likely = 1; + case M_BGT_I: + /* check for > max integer */ + maxnum = 0x7fffffff; + if (ISA_HAS_64BIT_REGS (mips_opts.isa) && sizeof (maxnum) > 4) + { + maxnum <<= 16; + maxnum |= 0xffff; + maxnum <<= 16; + maxnum |= 0xffff; + } + if (imm_expr.X_op == O_constant + && imm_expr.X_add_number >= maxnum + && (! ISA_HAS_64BIT_REGS (mips_opts.isa) || sizeof (maxnum) > 4)) + { + do_false: + /* result is always false */ + if (! likely) + { + as_warn (_("Branch %s is always false (nop)"), ip->insn_mo->name); + macro_build ((char *) NULL, &icnt, NULL, "nop", "", 0); + } + else + { + as_warn (_("Branch likely %s is always false"), ip->insn_mo->name); + macro_build ((char *) NULL, &icnt, &offset_expr, "bnel", + "s,t,p", 0, 0); + } + return; + } + if (imm_expr.X_op != O_constant) + as_bad (_("Unsupported large constant")); + imm_expr.X_add_number++; + /* FALLTHROUGH */ + case M_BGE_I: + case M_BGEL_I: + if (mask == M_BGEL_I) + likely = 1; + if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0) + { + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "bgezl" : "bgez", + "s,p", sreg); + return; + } + if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1) + { + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "bgtzl" : "bgtz", + "s,p", sreg); + return; + } + maxnum = 0x7fffffff; + if (ISA_HAS_64BIT_REGS (mips_opts.isa) && sizeof (maxnum) > 4) + { + maxnum <<= 16; + maxnum |= 0xffff; + maxnum <<= 16; + maxnum |= 0xffff; + } + maxnum = - maxnum - 1; + if (imm_expr.X_op == O_constant + && imm_expr.X_add_number <= maxnum + && (! ISA_HAS_64BIT_REGS (mips_opts.isa) || sizeof (maxnum) > 4)) + { + do_true: + /* result is always true */ + as_warn (_("Branch %s is always true"), ip->insn_mo->name); + macro_build ((char *) NULL, &icnt, &offset_expr, "b", "p"); + return; + } + set_at (&icnt, sreg, 0); + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "beql" : "beq", + "s,t,p", AT, 0); + break; + + case M_BGEUL: + likely = 1; + case M_BGEU: + if (treg == 0) + goto do_true; + if (sreg == 0) + { + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "beql" : "beq", + "s,t,p", 0, treg); + return; + } + macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", AT, sreg, + treg); + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "beql" : "beq", + "s,t,p", AT, 0); + break; + + case M_BGTUL_I: + likely = 1; + case M_BGTU_I: + if (sreg == 0 + || (! ISA_HAS_64BIT_REGS (mips_opts.isa) + && imm_expr.X_op == O_constant + && imm_expr.X_add_number == 0xffffffff)) + goto do_false; + if (imm_expr.X_op != O_constant) + as_bad (_("Unsupported large constant")); + imm_expr.X_add_number++; + /* FALLTHROUGH */ + case M_BGEU_I: + case M_BGEUL_I: + if (mask == M_BGEUL_I) + likely = 1; + if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0) + goto do_true; + if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1) + { + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "bnel" : "bne", + "s,t,p", sreg, 0); + return; + } + set_at (&icnt, sreg, 1); + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "beql" : "beq", + "s,t,p", AT, 0); + break; + + case M_BGTL: + likely = 1; + case M_BGT: + if (treg == 0) + { + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "bgtzl" : "bgtz", + "s,p", sreg); + return; + } + if (sreg == 0) + { + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "bltzl" : "bltz", + "s,p", treg); + return; + } + macro_build ((char *) NULL, &icnt, NULL, "slt", "d,v,t", AT, treg, sreg); + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "bnel" : "bne", + "s,t,p", AT, 0); + break; + + case M_BGTUL: + likely = 1; + case M_BGTU: + if (treg == 0) + { + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "bnel" : "bne", + "s,t,p", sreg, 0); + return; + } + if (sreg == 0) + goto do_false; + macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", AT, treg, + sreg); + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "bnel" : "bne", + "s,t,p", AT, 0); + break; + + case M_BLEL: + likely = 1; + case M_BLE: + if (treg == 0) + { + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "blezl" : "blez", + "s,p", sreg); + return; + } + if (sreg == 0) + { + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "bgezl" : "bgez", + "s,p", treg); + return; + } + macro_build ((char *) NULL, &icnt, NULL, "slt", "d,v,t", AT, treg, sreg); + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "beql" : "beq", + "s,t,p", AT, 0); + break; + + case M_BLEL_I: + likely = 1; + case M_BLE_I: + maxnum = 0x7fffffff; + if (ISA_HAS_64BIT_REGS (mips_opts.isa) && sizeof (maxnum) > 4) + { + maxnum <<= 16; + maxnum |= 0xffff; + maxnum <<= 16; + maxnum |= 0xffff; + } + if (imm_expr.X_op == O_constant + && imm_expr.X_add_number >= maxnum + && (! ISA_HAS_64BIT_REGS (mips_opts.isa) || sizeof (maxnum) > 4)) + goto do_true; + if (imm_expr.X_op != O_constant) + as_bad (_("Unsupported large constant")); + imm_expr.X_add_number++; + /* FALLTHROUGH */ + case M_BLT_I: + case M_BLTL_I: + if (mask == M_BLTL_I) + likely = 1; + if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0) + { + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "bltzl" : "bltz", + "s,p", sreg); + return; + } + if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1) + { + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "blezl" : "blez", + "s,p", sreg); + return; + } + set_at (&icnt, sreg, 0); + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "bnel" : "bne", + "s,t,p", AT, 0); + break; + + case M_BLEUL: + likely = 1; + case M_BLEU: + if (treg == 0) + { + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "beql" : "beq", + "s,t,p", sreg, 0); + return; + } + if (sreg == 0) + goto do_true; + macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", AT, treg, + sreg); + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "beql" : "beq", + "s,t,p", AT, 0); + break; + + case M_BLEUL_I: + likely = 1; + case M_BLEU_I: + if (sreg == 0 + || (! ISA_HAS_64BIT_REGS (mips_opts.isa) + && imm_expr.X_op == O_constant + && imm_expr.X_add_number == 0xffffffff)) + goto do_true; + if (imm_expr.X_op != O_constant) + as_bad (_("Unsupported large constant")); + imm_expr.X_add_number++; + /* FALLTHROUGH */ + case M_BLTU_I: + case M_BLTUL_I: + if (mask == M_BLTUL_I) + likely = 1; + if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0) + goto do_false; + if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1) + { + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "beql" : "beq", + "s,t,p", sreg, 0); + return; + } + set_at (&icnt, sreg, 1); + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "bnel" : "bne", + "s,t,p", AT, 0); + break; + + case M_BLTL: + likely = 1; + case M_BLT: + if (treg == 0) + { + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "bltzl" : "bltz", + "s,p", sreg); + return; + } + if (sreg == 0) + { + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "bgtzl" : "bgtz", + "s,p", treg); + return; + } + macro_build ((char *) NULL, &icnt, NULL, "slt", "d,v,t", AT, sreg, treg); + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "bnel" : "bne", + "s,t,p", AT, 0); + break; + + case M_BLTUL: + likely = 1; + case M_BLTU: + if (treg == 0) + goto do_false; + if (sreg == 0) + { + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "bnel" : "bne", + "s,t,p", 0, treg); + return; + } + macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", AT, sreg, + treg); + macro_build ((char *) NULL, &icnt, &offset_expr, + likely ? "bnel" : "bne", + "s,t,p", AT, 0); + break; + + case M_DDIV_3: + dbl = 1; + case M_DIV_3: + s = "mflo"; + goto do_div3; + case M_DREM_3: + dbl = 1; + case M_REM_3: + s = "mfhi"; + do_div3: + if (treg == 0) + { + as_warn (_("Divide by zero.")); + if (mips_trap) + macro_build ((char *) NULL, &icnt, NULL, "teq", "s,t", 0, 0); + else + macro_build ((char *) NULL, &icnt, NULL, "break", "c", 7); + return; + } + + mips_emit_delays (true); + ++mips_opts.noreorder; + mips_any_noreorder = 1; + if (mips_trap) + { + macro_build ((char *) NULL, &icnt, NULL, "teq", "s,t", treg, 0); + macro_build ((char *) NULL, &icnt, NULL, + dbl ? "ddiv" : "div", + "z,s,t", sreg, treg); + } + else + { + expr1.X_add_number = 8; + macro_build ((char *) NULL, &icnt, &expr1, "bne", "s,t,p", treg, 0); + macro_build ((char *) NULL, &icnt, NULL, + dbl ? "ddiv" : "div", + "z,s,t", sreg, treg); + macro_build ((char *) NULL, &icnt, NULL, "break", "c", 7); + } + expr1.X_add_number = -1; + macro_build ((char *) NULL, &icnt, &expr1, + dbl ? "daddiu" : "addiu", + "t,r,j", AT, 0, (int) BFD_RELOC_LO16); + expr1.X_add_number = mips_trap ? (dbl ? 12 : 8) : (dbl ? 20 : 16); + macro_build ((char *) NULL, &icnt, &expr1, "bne", "s,t,p", treg, AT); + if (dbl) + { + expr1.X_add_number = 1; + macro_build ((char *) NULL, &icnt, &expr1, "daddiu", "t,r,j", AT, 0, + (int) BFD_RELOC_LO16); + macro_build ((char *) NULL, &icnt, NULL, "dsll32", "d,w,<", AT, AT, + 31); + } + else + { + expr1.X_add_number = 0x80000000; + macro_build ((char *) NULL, &icnt, &expr1, "lui", "t,u", AT, + (int) BFD_RELOC_HI16); + } + if (mips_trap) + { + macro_build ((char *) NULL, &icnt, NULL, "teq", "s,t", sreg, AT); + /* We want to close the noreorder block as soon as possible, so + that later insns are available for delay slot filling. */ + --mips_opts.noreorder; + } + else + { + expr1.X_add_number = 8; + macro_build ((char *) NULL, &icnt, &expr1, "bne", "s,t,p", sreg, AT); + macro_build ((char *) NULL, &icnt, NULL, "nop", "", 0); + + /* We want to close the noreorder block as soon as possible, so + that later insns are available for delay slot filling. */ + --mips_opts.noreorder; + + macro_build ((char *) NULL, &icnt, NULL, "break", "c", 6); + } + macro_build ((char *) NULL, &icnt, NULL, s, "d", dreg); + break; + + case M_DIV_3I: + s = "div"; + s2 = "mflo"; + goto do_divi; + case M_DIVU_3I: + s = "divu"; + s2 = "mflo"; + goto do_divi; + case M_REM_3I: + s = "div"; + s2 = "mfhi"; + goto do_divi; + case M_REMU_3I: + s = "divu"; + s2 = "mfhi"; + goto do_divi; + case M_DDIV_3I: + dbl = 1; + s = "ddiv"; + s2 = "mflo"; + goto do_divi; + case M_DDIVU_3I: + dbl = 1; + s = "ddivu"; + s2 = "mflo"; + goto do_divi; + case M_DREM_3I: + dbl = 1; + s = "ddiv"; + s2 = "mfhi"; + goto do_divi; + case M_DREMU_3I: + dbl = 1; + s = "ddivu"; + s2 = "mfhi"; + do_divi: + if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0) + { + as_warn (_("Divide by zero.")); + if (mips_trap) + macro_build ((char *) NULL, &icnt, NULL, "teq", "s,t", 0, 0); + else + macro_build ((char *) NULL, &icnt, NULL, "break", "c", 7); + return; + } + if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1) + { + if (strcmp (s2, "mflo") == 0) + macro_build ((char *) NULL, &icnt, NULL, "move", "d,s", dreg, + sreg); + else + macro_build ((char *) NULL, &icnt, NULL, "move", "d,s", dreg, 0); + return; + } + if (imm_expr.X_op == O_constant + && imm_expr.X_add_number == -1 + && s[strlen (s) - 1] != 'u') + { + if (strcmp (s2, "mflo") == 0) + { + if (dbl) + macro_build ((char *) NULL, &icnt, NULL, "dneg", "d,w", dreg, + sreg); + else + macro_build ((char *) NULL, &icnt, NULL, "neg", "d,w", dreg, + sreg); + } + else + macro_build ((char *) NULL, &icnt, NULL, "move", "d,s", dreg, 0); + return; + } + + load_register (&icnt, AT, &imm_expr, dbl); + macro_build ((char *) NULL, &icnt, NULL, s, "z,s,t", sreg, AT); + macro_build ((char *) NULL, &icnt, NULL, s2, "d", dreg); + break; + + case M_DIVU_3: + s = "divu"; + s2 = "mflo"; + goto do_divu3; + case M_REMU_3: + s = "divu"; + s2 = "mfhi"; + goto do_divu3; + case M_DDIVU_3: + s = "ddivu"; + s2 = "mflo"; + goto do_divu3; + case M_DREMU_3: + s = "ddivu"; + s2 = "mfhi"; + do_divu3: + mips_emit_delays (true); + ++mips_opts.noreorder; + mips_any_noreorder = 1; + if (mips_trap) + { + macro_build ((char *) NULL, &icnt, NULL, "teq", "s,t", treg, 0); + macro_build ((char *) NULL, &icnt, NULL, s, "z,s,t", sreg, treg); + /* We want to close the noreorder block as soon as possible, so + that later insns are available for delay slot filling. */ + --mips_opts.noreorder; + } + else + { + expr1.X_add_number = 8; + macro_build ((char *) NULL, &icnt, &expr1, "bne", "s,t,p", treg, 0); + macro_build ((char *) NULL, &icnt, NULL, s, "z,s,t", sreg, treg); + + /* We want to close the noreorder block as soon as possible, so + that later insns are available for delay slot filling. */ + --mips_opts.noreorder; + macro_build ((char *) NULL, &icnt, NULL, "break", "c", 7); + } + macro_build ((char *) NULL, &icnt, NULL, s2, "d", dreg); + return; + + case M_DLA_AB: + dbl = 1; + case M_LA_AB: + /* Load the address of a symbol into a register. If breg is not + zero, we then add a base register to it. */ + + /* When generating embedded PIC code, we permit expressions of + the form + la $4,foo-bar + where bar is an address in the current section. These are used + when getting the addresses of functions. We don't permit + X_add_number to be non-zero, because if the symbol is + external the relaxing code needs to know that any addend is + purely the offset to X_op_symbol. */ + if (mips_pic == EMBEDDED_PIC + && offset_expr.X_op == O_subtract + && (symbol_constant_p (offset_expr.X_op_symbol) + ? S_GET_SEGMENT (offset_expr.X_op_symbol) == now_seg + : (symbol_equated_p (offset_expr.X_op_symbol) + && (S_GET_SEGMENT + (symbol_get_value_expression (offset_expr.X_op_symbol) + ->X_add_symbol) + == now_seg))) + && breg == 0 + && (offset_expr.X_add_number == 0 + || OUTPUT_FLAVOR == bfd_target_elf_flavour)) + { + macro_build ((char *) NULL, &icnt, &offset_expr, "lui", "t,u", + treg, (int) BFD_RELOC_PCREL_HI16_S); + macro_build ((char *) NULL, &icnt, &offset_expr, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addiu" : "daddiu"), + "t,r,j", treg, treg, (int) BFD_RELOC_PCREL_LO16); + return; + } + + if (offset_expr.X_op != O_symbol + && offset_expr.X_op != O_constant) + { + as_bad (_("expression too complex")); + offset_expr.X_op = O_constant; + } + + if (treg == breg) + { + tempreg = AT; + used_at = 1; + } + else + { + tempreg = treg; + used_at = 0; + } + + if (offset_expr.X_op == O_constant) + load_register (&icnt, tempreg, &offset_expr, dbl); + else if (mips_pic == NO_PIC) + { + /* If this is a reference to an GP relative symbol, we want + addiu $tempreg,$gp,<sym> (BFD_RELOC_MIPS_GPREL) + Otherwise we want + lui $tempreg,<sym> (BFD_RELOC_HI16_S) + addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16) + If we have a constant, we need two instructions anyhow, + so we may as well always use the latter form. */ + if ((valueT) offset_expr.X_add_number >= MAX_GPREL_OFFSET + || nopic_need_relax (offset_expr.X_add_symbol, 1)) + p = NULL; + else + { + frag_grow (20); + macro_build ((char *) NULL, &icnt, &offset_expr, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addiu" : "daddiu"), + "t,r,j", tempreg, GP, (int) BFD_RELOC_MIPS_GPREL); + p = frag_var (rs_machine_dependent, 8, 0, + RELAX_ENCODE (4, 8, 0, 4, 0, + mips_opts.warn_about_macros), + offset_expr.X_add_symbol, (offsetT) 0, + (char *) NULL); + } + macro_build_lui (p, &icnt, &offset_expr, tempreg); + if (p != NULL) + p += 4; + macro_build (p, &icnt, &offset_expr, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addiu" : "daddiu"), + "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16); + } + else if (mips_pic == SVR4_PIC && ! mips_big_got) + { + /* If this is a reference to an external symbol, and there + is no constant, we want + lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16) + For a local symbol, we want + lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16) + nop + addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16) + + If we have a small constant, and this is a reference to + an external symbol, we want + lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16) + nop + addiu $tempreg,$tempreg,<constant> + For a local symbol, we want the same instruction + sequence, but we output a BFD_RELOC_LO16 reloc on the + addiu instruction. + + If we have a large constant, and this is a reference to + an external symbol, we want + lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16) + lui $at,<hiconstant> + addiu $at,$at,<loconstant> + addu $tempreg,$tempreg,$at + For a local symbol, we want the same instruction + sequence, but we output a BFD_RELOC_LO16 reloc on the + addiu instruction. */ + expr1.X_add_number = offset_expr.X_add_number; + offset_expr.X_add_number = 0; + frag_grow (32); + macro_build ((char *) NULL, &icnt, &offset_expr, + dbl ? "ld" : "lw", + "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT16, GP); + if (expr1.X_add_number == 0) + { + int off; + + if (breg == 0) + off = 0; + else + { + /* We're going to put in an addu instruction using + tempreg, so we may as well insert the nop right + now. */ + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + "nop", ""); + off = 4; + } + p = frag_var (rs_machine_dependent, 8 - off, 0, + RELAX_ENCODE (0, 8 - off, -4 - off, 4 - off, 0, + (breg == 0 + ? mips_opts.warn_about_macros + : 0)), + offset_expr.X_add_symbol, (offsetT) 0, + (char *) NULL); + if (breg == 0) + { + macro_build (p, &icnt, (expressionS *) NULL, "nop", ""); + p += 4; + } + macro_build (p, &icnt, &expr1, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addiu" : "daddiu"), + "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16); + /* FIXME: If breg == 0, and the next instruction uses + $tempreg, then if this variant case is used an extra + nop will be generated. */ + } + else if (expr1.X_add_number >= -0x8000 + && expr1.X_add_number < 0x8000) + { + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + "nop", ""); + macro_build ((char *) NULL, &icnt, &expr1, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addiu" : "daddiu"), + "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16); + (void) frag_var (rs_machine_dependent, 0, 0, + RELAX_ENCODE (0, 0, -12, -4, 0, 0), + offset_expr.X_add_symbol, (offsetT) 0, + (char *) NULL); + } + else + { + int off1; + + /* If we are going to add in a base register, and the + target register and the base register are the same, + then we are using AT as a temporary register. Since + we want to load the constant into AT, we add our + current AT (from the global offset table) and the + register into the register now, and pretend we were + not using a base register. */ + if (breg != treg) + off1 = 0; + else + { + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + "nop", ""); + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addu" : "daddu"), + "d,v,t", treg, AT, breg); + breg = 0; + tempreg = treg; + off1 = -8; + } + + /* Set mips_optimize around the lui instruction to avoid + inserting an unnecessary nop after the lw. */ + hold_mips_optimize = mips_optimize; + mips_optimize = 2; + macro_build_lui ((char *) NULL, &icnt, &expr1, AT); + mips_optimize = hold_mips_optimize; + + macro_build ((char *) NULL, &icnt, &expr1, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addiu" : "daddiu"), + "t,r,j", AT, AT, (int) BFD_RELOC_LO16); + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addu" : "daddu"), + "d,v,t", tempreg, tempreg, AT); + (void) frag_var (rs_machine_dependent, 0, 0, + RELAX_ENCODE (0, 0, -16 + off1, -8, 0, 0), + offset_expr.X_add_symbol, (offsetT) 0, + (char *) NULL); + used_at = 1; + } + } + else if (mips_pic == SVR4_PIC) + { + int gpdel; + + /* This is the large GOT case. If this is a reference to an + external symbol, and there is no constant, we want + lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16) + addu $tempreg,$tempreg,$gp + lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16) + For a local symbol, we want + lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16) + nop + addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16) + + If we have a small constant, and this is a reference to + an external symbol, we want + lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16) + addu $tempreg,$tempreg,$gp + lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16) + nop + addiu $tempreg,$tempreg,<constant> + For a local symbol, we want + lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16) + nop + addiu $tempreg,$tempreg,<constant> (BFD_RELOC_LO16) + + If we have a large constant, and this is a reference to + an external symbol, we want + lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16) + addu $tempreg,$tempreg,$gp + lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16) + lui $at,<hiconstant> + addiu $at,$at,<loconstant> + addu $tempreg,$tempreg,$at + For a local symbol, we want + lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16) + lui $at,<hiconstant> + addiu $at,$at,<loconstant> (BFD_RELOC_LO16) + addu $tempreg,$tempreg,$at + */ + expr1.X_add_number = offset_expr.X_add_number; + offset_expr.X_add_number = 0; + frag_grow (52); + if (reg_needs_delay (GP)) + gpdel = 4; + else + gpdel = 0; + macro_build ((char *) NULL, &icnt, &offset_expr, "lui", "t,u", + tempreg, (int) BFD_RELOC_MIPS_GOT_HI16); + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addu" : "daddu"), + "d,v,t", tempreg, tempreg, GP); + macro_build ((char *) NULL, &icnt, &offset_expr, + dbl ? "ld" : "lw", + "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT_LO16, + tempreg); + if (expr1.X_add_number == 0) + { + int off; + + if (breg == 0) + off = 0; + else + { + /* We're going to put in an addu instruction using + tempreg, so we may as well insert the nop right + now. */ + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + "nop", ""); + off = 4; + } + + p = frag_var (rs_machine_dependent, 12 + gpdel, 0, + RELAX_ENCODE (12 + off, 12 + gpdel, gpdel, + 8 + gpdel, 0, + (breg == 0 + ? mips_opts.warn_about_macros + : 0)), + offset_expr.X_add_symbol, (offsetT) 0, + (char *) NULL); + } + else if (expr1.X_add_number >= -0x8000 + && expr1.X_add_number < 0x8000) + { + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + "nop", ""); + macro_build ((char *) NULL, &icnt, &expr1, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addiu" : "daddiu"), + "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16); + + p = frag_var (rs_machine_dependent, 12 + gpdel, 0, + RELAX_ENCODE (20, 12 + gpdel, gpdel, 8 + gpdel, 0, + (breg == 0 + ? mips_opts.warn_about_macros + : 0)), + offset_expr.X_add_symbol, (offsetT) 0, + (char *) NULL); + } + else + { + int adj, dreg; + + /* If we are going to add in a base register, and the + target register and the base register are the same, + then we are using AT as a temporary register. Since + we want to load the constant into AT, we add our + current AT (from the global offset table) and the + register into the register now, and pretend we were + not using a base register. */ + if (breg != treg) + { + adj = 0; + dreg = tempreg; + } + else + { + assert (tempreg == AT); + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + "nop", ""); + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addu" : "daddu"), + "d,v,t", treg, AT, breg); + dreg = treg; + adj = 8; + } + + /* Set mips_optimize around the lui instruction to avoid + inserting an unnecessary nop after the lw. */ + hold_mips_optimize = mips_optimize; + mips_optimize = 2; + macro_build_lui ((char *) NULL, &icnt, &expr1, AT); + mips_optimize = hold_mips_optimize; + + macro_build ((char *) NULL, &icnt, &expr1, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addiu" : "daddiu"), + "t,r,j", AT, AT, (int) BFD_RELOC_LO16); + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addu" : "daddu"), + "d,v,t", dreg, dreg, AT); + + p = frag_var (rs_machine_dependent, 16 + gpdel + adj, 0, + RELAX_ENCODE (24 + adj, 16 + gpdel + adj, gpdel, + 8 + gpdel, 0, + (breg == 0 + ? mips_opts.warn_about_macros + : 0)), + offset_expr.X_add_symbol, (offsetT) 0, + (char *) NULL); + + used_at = 1; + } + + if (gpdel > 0) + { + /* This is needed because this instruction uses $gp, but + the first instruction on the main stream does not. */ + macro_build (p, &icnt, (expressionS *) NULL, "nop", ""); + p += 4; + } + macro_build (p, &icnt, &offset_expr, + dbl ? "ld" : "lw", + "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT16, GP); + p += 4; + if (expr1.X_add_number >= -0x8000 + && expr1.X_add_number < 0x8000) + { + macro_build (p, &icnt, (expressionS *) NULL, "nop", ""); + p += 4; + macro_build (p, &icnt, &expr1, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addiu" : "daddiu"), + "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16); + /* FIXME: If add_number is 0, and there was no base + register, the external symbol case ended with a load, + so if the symbol turns out to not be external, and + the next instruction uses tempreg, an unnecessary nop + will be inserted. */ + } + else + { + if (breg == treg) + { + /* We must add in the base register now, as in the + external symbol case. */ + assert (tempreg == AT); + macro_build (p, &icnt, (expressionS *) NULL, "nop", ""); + p += 4; + macro_build (p, &icnt, (expressionS *) NULL, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addu" : "daddu"), + "d,v,t", treg, AT, breg); + p += 4; + tempreg = treg; + /* We set breg to 0 because we have arranged to add + it in in both cases. */ + breg = 0; + } + + macro_build_lui (p, &icnt, &expr1, AT); + p += 4; + macro_build (p, &icnt, &expr1, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addiu" : "daddiu"), + "t,r,j", AT, AT, (int) BFD_RELOC_LO16); + p += 4; + macro_build (p, &icnt, (expressionS *) NULL, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addu" : "daddu"), + "d,v,t", tempreg, tempreg, AT); + p += 4; + } + } + else if (mips_pic == EMBEDDED_PIC) + { + /* We use + addiu $tempreg,$gp,<sym> (BFD_RELOC_MIPS_GPREL) + */ + macro_build ((char *) NULL, &icnt, &offset_expr, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addiu" : "daddiu"), + "t,r,j", tempreg, GP, (int) BFD_RELOC_MIPS_GPREL); + } + else + abort (); + + if (breg != 0) + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addu" : "daddu"), + "d,v,t", treg, tempreg, breg); + + if (! used_at) + return; + + break; + + case M_J_A: + /* The j instruction may not be used in PIC code, since it + requires an absolute address. We convert it to a b + instruction. */ + if (mips_pic == NO_PIC) + macro_build ((char *) NULL, &icnt, &offset_expr, "j", "a"); + else + macro_build ((char *) NULL, &icnt, &offset_expr, "b", "p"); + return; + + /* The jal instructions must be handled as macros because when + generating PIC code they expand to multi-instruction + sequences. Normally they are simple instructions. */ + case M_JAL_1: + dreg = RA; + /* Fall through. */ + case M_JAL_2: + if (mips_pic == NO_PIC + || mips_pic == EMBEDDED_PIC) + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "jalr", + "d,s", dreg, sreg); + else if (mips_pic == SVR4_PIC) + { + if (sreg != PIC_CALL_REG) + as_warn (_("MIPS PIC call to register other than $25")); + + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "jalr", + "d,s", dreg, sreg); + if (mips_cprestore_offset < 0) + as_warn (_("No .cprestore pseudo-op used in PIC code")); + else + { + expr1.X_add_number = mips_cprestore_offset; + macro_build ((char *) NULL, &icnt, &expr1, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "lw" : "ld"), + "t,o(b)", GP, (int) BFD_RELOC_LO16, mips_frame_reg); + } + } + else + abort (); + + return; + + case M_JAL_A: + if (mips_pic == NO_PIC) + macro_build ((char *) NULL, &icnt, &offset_expr, "jal", "a"); + else if (mips_pic == SVR4_PIC) + { + /* If this is a reference to an external symbol, and we are + using a small GOT, we want + lw $25,<sym>($gp) (BFD_RELOC_MIPS_CALL16) + nop + jalr $25 + nop + lw $gp,cprestore($sp) + The cprestore value is set using the .cprestore + pseudo-op. If we are using a big GOT, we want + lui $25,<sym> (BFD_RELOC_MIPS_CALL_HI16) + addu $25,$25,$gp + lw $25,<sym>($25) (BFD_RELOC_MIPS_CALL_LO16) + nop + jalr $25 + nop + lw $gp,cprestore($sp) + If the symbol is not external, we want + lw $25,<sym>($gp) (BFD_RELOC_MIPS_GOT16) + nop + addiu $25,$25,<sym> (BFD_RELOC_LO16) + jalr $25 + nop + lw $gp,cprestore($sp) */ + frag_grow (40); + if (! mips_big_got) + { + macro_build ((char *) NULL, &icnt, &offset_expr, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "lw" : "ld"), + "t,o(b)", PIC_CALL_REG, + (int) BFD_RELOC_MIPS_CALL16, GP); + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + "nop", ""); + p = frag_var (rs_machine_dependent, 4, 0, + RELAX_ENCODE (0, 4, -8, 0, 0, 0), + offset_expr.X_add_symbol, (offsetT) 0, + (char *) NULL); + } + else + { + int gpdel; + + if (reg_needs_delay (GP)) + gpdel = 4; + else + gpdel = 0; + macro_build ((char *) NULL, &icnt, &offset_expr, "lui", "t,u", + PIC_CALL_REG, (int) BFD_RELOC_MIPS_CALL_HI16); + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addu" : "daddu"), + "d,v,t", PIC_CALL_REG, PIC_CALL_REG, GP); + macro_build ((char *) NULL, &icnt, &offset_expr, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "lw" : "ld"), + "t,o(b)", PIC_CALL_REG, + (int) BFD_RELOC_MIPS_CALL_LO16, PIC_CALL_REG); + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + "nop", ""); + p = frag_var (rs_machine_dependent, 12 + gpdel, 0, + RELAX_ENCODE (16, 12 + gpdel, gpdel, 8 + gpdel, + 0, 0), + offset_expr.X_add_symbol, (offsetT) 0, + (char *) NULL); + if (gpdel > 0) + { + macro_build (p, &icnt, (expressionS *) NULL, "nop", ""); + p += 4; + } + macro_build (p, &icnt, &offset_expr, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "lw" : "ld"), + "t,o(b)", PIC_CALL_REG, + (int) BFD_RELOC_MIPS_GOT16, GP); + p += 4; + macro_build (p, &icnt, (expressionS *) NULL, "nop", ""); + p += 4; + } + macro_build (p, &icnt, &offset_expr, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addiu" : "daddiu"), + "t,r,j", PIC_CALL_REG, PIC_CALL_REG, + (int) BFD_RELOC_LO16); + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + "jalr", "s", PIC_CALL_REG); + if (mips_cprestore_offset < 0) + as_warn (_("No .cprestore pseudo-op used in PIC code")); + else + { + if (mips_opts.noreorder) + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + "nop", ""); + expr1.X_add_number = mips_cprestore_offset; + macro_build ((char *) NULL, &icnt, &expr1, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "lw" : "ld"), + "t,o(b)", GP, (int) BFD_RELOC_LO16, + mips_frame_reg); + } + } + else if (mips_pic == EMBEDDED_PIC) + { + macro_build ((char *) NULL, &icnt, &offset_expr, "bal", "p"); + /* The linker may expand the call to a longer sequence which + uses $at, so we must break rather than return. */ + break; + } + else + abort (); + + return; + + case M_LB_AB: + s = "lb"; + goto ld; + case M_LBU_AB: + s = "lbu"; + goto ld; + case M_LH_AB: + s = "lh"; + goto ld; + case M_LHU_AB: + s = "lhu"; + goto ld; + case M_LW_AB: + s = "lw"; + goto ld; + case M_LWC0_AB: + s = "lwc0"; + /* Itbl support may require additional care here. */ + coproc = 1; + goto ld; + case M_LWC1_AB: + s = "lwc1"; + /* Itbl support may require additional care here. */ + coproc = 1; + goto ld; + case M_LWC2_AB: + s = "lwc2"; + /* Itbl support may require additional care here. */ + coproc = 1; + goto ld; + case M_LWC3_AB: + s = "lwc3"; + /* Itbl support may require additional care here. */ + coproc = 1; + goto ld; + case M_LWL_AB: + s = "lwl"; + lr = 1; + goto ld; + case M_LWR_AB: + s = "lwr"; + lr = 1; + goto ld; + case M_LDC1_AB: + if (mips_cpu == 4650) + { + as_bad (_("opcode not supported on this processor")); + return; + } + s = "ldc1"; + /* Itbl support may require additional care here. */ + coproc = 1; + goto ld; + case M_LDC2_AB: + s = "ldc2"; + /* Itbl support may require additional care here. */ + coproc = 1; + goto ld; + case M_LDC3_AB: + s = "ldc3"; + /* Itbl support may require additional care here. */ + coproc = 1; + goto ld; + case M_LDL_AB: + s = "ldl"; + lr = 1; + goto ld; + case M_LDR_AB: + s = "ldr"; + lr = 1; + goto ld; + case M_LL_AB: + s = "ll"; + goto ld; + case M_LLD_AB: + s = "lld"; + goto ld; + case M_LWU_AB: + s = "lwu"; + ld: + if (breg == treg || coproc || lr) + { + tempreg = AT; + used_at = 1; + } + else + { + tempreg = treg; + used_at = 0; + } + goto ld_st; + case M_SB_AB: + s = "sb"; + goto st; + case M_SH_AB: + s = "sh"; + goto st; + case M_SW_AB: + s = "sw"; + goto st; + case M_SWC0_AB: + s = "swc0"; + /* Itbl support may require additional care here. */ + coproc = 1; + goto st; + case M_SWC1_AB: + s = "swc1"; + /* Itbl support may require additional care here. */ + coproc = 1; + goto st; + case M_SWC2_AB: + s = "swc2"; + /* Itbl support may require additional care here. */ + coproc = 1; + goto st; + case M_SWC3_AB: + s = "swc3"; + /* Itbl support may require additional care here. */ + coproc = 1; + goto st; + case M_SWL_AB: + s = "swl"; + goto st; + case M_SWR_AB: + s = "swr"; + goto st; + case M_SC_AB: + s = "sc"; + goto st; + case M_SCD_AB: + s = "scd"; + goto st; + case M_SDC1_AB: + if (mips_cpu == 4650) + { + as_bad (_("opcode not supported on this processor")); + return; + } + s = "sdc1"; + coproc = 1; + /* Itbl support may require additional care here. */ + goto st; + case M_SDC2_AB: + s = "sdc2"; + /* Itbl support may require additional care here. */ + coproc = 1; + goto st; + case M_SDC3_AB: + s = "sdc3"; + /* Itbl support may require additional care here. */ + coproc = 1; + goto st; + case M_SDL_AB: + s = "sdl"; + goto st; + case M_SDR_AB: + s = "sdr"; + st: + tempreg = AT; + used_at = 1; + ld_st: + /* Itbl support may require additional care here. */ + if (mask == M_LWC1_AB + || mask == M_SWC1_AB + || mask == M_LDC1_AB + || mask == M_SDC1_AB + || mask == M_L_DAB + || mask == M_S_DAB) + fmt = "T,o(b)"; + else if (coproc) + fmt = "E,o(b)"; + else + fmt = "t,o(b)"; + + if (offset_expr.X_op != O_constant + && offset_expr.X_op != O_symbol) + { + as_bad (_("expression too complex")); + offset_expr.X_op = O_constant; + } + + /* A constant expression in PIC code can be handled just as it + is in non PIC code. */ + if (mips_pic == NO_PIC + || offset_expr.X_op == O_constant) + { + /* If this is a reference to a GP relative symbol, and there + is no base register, we want + <op> $treg,<sym>($gp) (BFD_RELOC_MIPS_GPREL) + Otherwise, if there is no base register, we want + lui $tempreg,<sym> (BFD_RELOC_HI16_S) + <op> $treg,<sym>($tempreg) (BFD_RELOC_LO16) + If we have a constant, we need two instructions anyhow, + so we always use the latter form. + + If we have a base register, and this is a reference to a + GP relative symbol, we want + addu $tempreg,$breg,$gp + <op> $treg,<sym>($tempreg) (BFD_RELOC_MIPS_GPREL) + Otherwise we want + lui $tempreg,<sym> (BFD_RELOC_HI16_S) + addu $tempreg,$tempreg,$breg + <op> $treg,<sym>($tempreg) (BFD_RELOC_LO16) + With a constant we always use the latter case. */ + if (breg == 0) + { + if ((valueT) offset_expr.X_add_number >= MAX_GPREL_OFFSET + || nopic_need_relax (offset_expr.X_add_symbol, 1)) + p = NULL; + else + { + frag_grow (20); + macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt, + treg, (int) BFD_RELOC_MIPS_GPREL, GP); + p = frag_var (rs_machine_dependent, 8, 0, + RELAX_ENCODE (4, 8, 0, 4, 0, + (mips_opts.warn_about_macros + || (used_at + && mips_opts.noat))), + offset_expr.X_add_symbol, (offsetT) 0, + (char *) NULL); + used_at = 0; + } + macro_build_lui (p, &icnt, &offset_expr, tempreg); + if (p != NULL) + p += 4; + macro_build (p, &icnt, &offset_expr, s, fmt, treg, + (int) BFD_RELOC_LO16, tempreg); + } + else + { + if ((valueT) offset_expr.X_add_number >= MAX_GPREL_OFFSET + || nopic_need_relax (offset_expr.X_add_symbol, 1)) + p = NULL; + else + { + frag_grow (28); + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addu" : "daddu"), + "d,v,t", tempreg, breg, GP); + macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt, + treg, (int) BFD_RELOC_MIPS_GPREL, tempreg); + p = frag_var (rs_machine_dependent, 12, 0, + RELAX_ENCODE (8, 12, 0, 8, 0, 0), + offset_expr.X_add_symbol, (offsetT) 0, + (char *) NULL); + } + macro_build_lui (p, &icnt, &offset_expr, tempreg); + if (p != NULL) + p += 4; + macro_build (p, &icnt, (expressionS *) NULL, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addu" : "daddu"), + "d,v,t", tempreg, tempreg, breg); + if (p != NULL) + p += 4; + macro_build (p, &icnt, &offset_expr, s, fmt, treg, + (int) BFD_RELOC_LO16, tempreg); + } + } + else if (mips_pic == SVR4_PIC && ! mips_big_got) + { + /* If this is a reference to an external symbol, we want + lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16) + nop + <op> $treg,0($tempreg) + Otherwise we want + lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16) + nop + addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16) + <op> $treg,0($tempreg) + If there is a base register, we add it to $tempreg before + the <op>. If there is a constant, we stick it in the + <op> instruction. We don't handle constants larger than + 16 bits, because we have no way to load the upper 16 bits + (actually, we could handle them for the subset of cases + in which we are not using $at). */ + assert (offset_expr.X_op == O_symbol); + expr1.X_add_number = offset_expr.X_add_number; + offset_expr.X_add_number = 0; + if (expr1.X_add_number < -0x8000 + || expr1.X_add_number >= 0x8000) + as_bad (_("PIC code offset overflow (max 16 signed bits)")); + frag_grow (20); + macro_build ((char *) NULL, &icnt, &offset_expr, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "lw" : "ld"), + "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT16, GP); + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", ""); + p = frag_var (rs_machine_dependent, 4, 0, + RELAX_ENCODE (0, 4, -8, 0, 0, 0), + offset_expr.X_add_symbol, (offsetT) 0, + (char *) NULL); + macro_build (p, &icnt, &offset_expr, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addiu" : "daddiu"), + "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16); + if (breg != 0) + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addu" : "daddu"), + "d,v,t", tempreg, tempreg, breg); + macro_build ((char *) NULL, &icnt, &expr1, s, fmt, treg, + (int) BFD_RELOC_LO16, tempreg); + } + else if (mips_pic == SVR4_PIC) + { + int gpdel; + + /* If this is a reference to an external symbol, we want + lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16) + addu $tempreg,$tempreg,$gp + lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16) + <op> $treg,0($tempreg) + Otherwise we want + lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16) + nop + addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16) + <op> $treg,0($tempreg) + If there is a base register, we add it to $tempreg before + the <op>. If there is a constant, we stick it in the + <op> instruction. We don't handle constants larger than + 16 bits, because we have no way to load the upper 16 bits + (actually, we could handle them for the subset of cases + in which we are not using $at). */ + assert (offset_expr.X_op == O_symbol); + expr1.X_add_number = offset_expr.X_add_number; + offset_expr.X_add_number = 0; + if (expr1.X_add_number < -0x8000 + || expr1.X_add_number >= 0x8000) + as_bad (_("PIC code offset overflow (max 16 signed bits)")); + if (reg_needs_delay (GP)) + gpdel = 4; + else + gpdel = 0; + frag_grow (36); + macro_build ((char *) NULL, &icnt, &offset_expr, "lui", "t,u", + tempreg, (int) BFD_RELOC_MIPS_GOT_HI16); + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addu" : "daddu"), + "d,v,t", tempreg, tempreg, GP); + macro_build ((char *) NULL, &icnt, &offset_expr, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "lw" : "ld"), + "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT_LO16, + tempreg); + p = frag_var (rs_machine_dependent, 12 + gpdel, 0, + RELAX_ENCODE (12, 12 + gpdel, gpdel, 8 + gpdel, 0, 0), + offset_expr.X_add_symbol, (offsetT) 0, (char *) NULL); + if (gpdel > 0) + { + macro_build (p, &icnt, (expressionS *) NULL, "nop", ""); + p += 4; + } + macro_build (p, &icnt, &offset_expr, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "lw" : "ld"), + "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT16, GP); + p += 4; + macro_build (p, &icnt, (expressionS *) NULL, "nop", ""); + p += 4; + macro_build (p, &icnt, &offset_expr, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addiu" : "daddiu"), + "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16); + if (breg != 0) + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addu" : "daddu"), + "d,v,t", tempreg, tempreg, breg); + macro_build ((char *) NULL, &icnt, &expr1, s, fmt, treg, + (int) BFD_RELOC_LO16, tempreg); + } + else if (mips_pic == EMBEDDED_PIC) + { + /* If there is no base register, we want + <op> $treg,<sym>($gp) (BFD_RELOC_MIPS_GPREL) + If there is a base register, we want + addu $tempreg,$breg,$gp + <op> $treg,<sym>($tempreg) (BFD_RELOC_MIPS_GPREL) + */ + assert (offset_expr.X_op == O_symbol); + if (breg == 0) + { + macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt, + treg, (int) BFD_RELOC_MIPS_GPREL, GP); + used_at = 0; + } + else + { + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addu" : "daddu"), + "d,v,t", tempreg, breg, GP); + macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt, + treg, (int) BFD_RELOC_MIPS_GPREL, tempreg); + } + } + else + abort (); + + if (! used_at) + return; + + break; + + case M_LI: + case M_LI_S: + load_register (&icnt, treg, &imm_expr, 0); + return; + + case M_DLI: + load_register (&icnt, treg, &imm_expr, 1); + return; + + case M_LI_SS: + if (imm_expr.X_op == O_constant) + { + load_register (&icnt, AT, &imm_expr, 0); + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + "mtc1", "t,G", AT, treg); + break; + } + else + { + assert (offset_expr.X_op == O_symbol + && strcmp (segment_name (S_GET_SEGMENT + (offset_expr.X_add_symbol)), + ".lit4") == 0 + && offset_expr.X_add_number == 0); + macro_build ((char *) NULL, &icnt, &offset_expr, "lwc1", "T,o(b)", + treg, (int) BFD_RELOC_MIPS_LITERAL, GP); + return; + } + + case M_LI_D: + /* If we have a constant in IMM_EXPR, then in mips3 mode it is + the entire value, and in mips1 mode it is the high order 32 + bits of the value and the low order 32 bits are either zero + or in offset_expr. */ + if (imm_expr.X_op == O_constant || imm_expr.X_op == O_big) + { + if (ISA_HAS_64BIT_REGS (mips_opts.isa)) + load_register (&icnt, treg, &imm_expr, 1); + else + { + int hreg, lreg; + + if (target_big_endian) + { + hreg = treg; + lreg = treg + 1; + } + else + { + hreg = treg + 1; + lreg = treg; + } + + if (hreg <= 31) + load_register (&icnt, hreg, &imm_expr, 0); + if (lreg <= 31) + { + if (offset_expr.X_op == O_absent) + macro_build ((char *) NULL, &icnt, NULL, "move", "d,s", + lreg, 0); + else + { + assert (offset_expr.X_op == O_constant); + load_register (&icnt, lreg, &offset_expr, 0); + } + } + } + return; + } + + /* We know that sym is in the .rdata section. First we get the + upper 16 bits of the address. */ + if (mips_pic == NO_PIC) + { + /* FIXME: This won't work for a 64 bit address. */ + macro_build_lui ((char *) NULL, &icnt, &offset_expr, AT); + } + else if (mips_pic == SVR4_PIC) + { + macro_build ((char *) NULL, &icnt, &offset_expr, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "lw" : "ld"), + "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT16, GP); + } + else if (mips_pic == EMBEDDED_PIC) + { + /* For embedded PIC we pick up the entire address off $gp in + a single instruction. */ + macro_build ((char *) NULL, &icnt, &offset_expr, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addiu" : "daddiu"), + "t,r,j", AT, GP, (int) BFD_RELOC_MIPS_GPREL); + offset_expr.X_op = O_constant; + offset_expr.X_add_number = 0; + } + else + abort (); + + /* Now we load the register(s). */ + if (ISA_HAS_64BIT_REGS (mips_opts.isa)) + macro_build ((char *) NULL, &icnt, &offset_expr, "ld", "t,o(b)", + treg, (int) BFD_RELOC_LO16, AT); + else + { + macro_build ((char *) NULL, &icnt, &offset_expr, "lw", "t,o(b)", + treg, (int) BFD_RELOC_LO16, AT); + if (treg != 31) + { + /* FIXME: How in the world do we deal with the possible + overflow here? */ + offset_expr.X_add_number += 4; + macro_build ((char *) NULL, &icnt, &offset_expr, "lw", "t,o(b)", + treg + 1, (int) BFD_RELOC_LO16, AT); + } + } + + /* To avoid confusion in tc_gen_reloc, we must ensure that this + does not become a variant frag. */ + frag_wane (frag_now); + frag_new (0); + + break; + + case M_LI_DD: + /* If we have a constant in IMM_EXPR, then in mips3 mode it is + the entire value, and in mips1 mode it is the high order 32 + bits of the value and the low order 32 bits are either zero + or in offset_expr. */ + if (imm_expr.X_op == O_constant || imm_expr.X_op == O_big) + { + load_register (&icnt, AT, &imm_expr, ISA_HAS_64BIT_REGS (mips_opts.isa)); + if (ISA_HAS_64BIT_REGS (mips_opts.isa)) + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + "dmtc1", "t,S", AT, treg); + else + { + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + "mtc1", "t,G", AT, treg + 1); + if (offset_expr.X_op == O_absent) + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + "mtc1", "t,G", 0, treg); + else + { + assert (offset_expr.X_op == O_constant); + load_register (&icnt, AT, &offset_expr, 0); + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + "mtc1", "t,G", AT, treg); + } + } + break; + } + + assert (offset_expr.X_op == O_symbol + && offset_expr.X_add_number == 0); + s = segment_name (S_GET_SEGMENT (offset_expr.X_add_symbol)); + if (strcmp (s, ".lit8") == 0) + { + if (mips_opts.isa != 1) + { + macro_build ((char *) NULL, &icnt, &offset_expr, "ldc1", + "T,o(b)", treg, (int) BFD_RELOC_MIPS_LITERAL, GP); + return; + } + breg = GP; + r = BFD_RELOC_MIPS_LITERAL; + goto dob; + } + else + { + assert (strcmp (s, RDATA_SECTION_NAME) == 0); + if (mips_pic == SVR4_PIC) + macro_build ((char *) NULL, &icnt, &offset_expr, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "lw" : "ld"), + "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT16, GP); + else + { + /* FIXME: This won't work for a 64 bit address. */ + macro_build_lui ((char *) NULL, &icnt, &offset_expr, AT); + } + + if (mips_opts.isa != 1) + { + macro_build ((char *) NULL, &icnt, &offset_expr, "ldc1", + "T,o(b)", treg, (int) BFD_RELOC_LO16, AT); + + /* To avoid confusion in tc_gen_reloc, we must ensure + that this does not become a variant frag. */ + frag_wane (frag_now); + frag_new (0); + + break; + } + breg = AT; + r = BFD_RELOC_LO16; + goto dob; + } + + case M_L_DOB: + if (mips_cpu == 4650) + { + as_bad (_("opcode not supported on this processor")); + return; + } + /* Even on a big endian machine $fn comes before $fn+1. We have + to adjust when loading from memory. */ + r = BFD_RELOC_LO16; + dob: + assert (mips_opts.isa == 1); + macro_build ((char *) NULL, &icnt, &offset_expr, "lwc1", "T,o(b)", + target_big_endian ? treg + 1 : treg, + (int) r, breg); + /* FIXME: A possible overflow which I don't know how to deal + with. */ + offset_expr.X_add_number += 4; + macro_build ((char *) NULL, &icnt, &offset_expr, "lwc1", "T,o(b)", + target_big_endian ? treg : treg + 1, + (int) r, breg); + + /* To avoid confusion in tc_gen_reloc, we must ensure that this + does not become a variant frag. */ + frag_wane (frag_now); + frag_new (0); + + if (breg != AT) + return; + break; + + case M_L_DAB: + /* + * The MIPS assembler seems to check for X_add_number not + * being double aligned and generating: + * lui at,%hi(foo+1) + * addu at,at,v1 + * addiu at,at,%lo(foo+1) + * lwc1 f2,0(at) + * lwc1 f3,4(at) + * But, the resulting address is the same after relocation so why + * generate the extra instruction? + */ + if (mips_cpu == 4650) + { + as_bad (_("opcode not supported on this processor")); + return; + } + /* Itbl support may require additional care here. */ + coproc = 1; + if (mips_opts.isa != 1) + { + s = "ldc1"; + goto ld; + } + + s = "lwc1"; + fmt = "T,o(b)"; + goto ldd_std; + + case M_S_DAB: + if (mips_cpu == 4650) + { + as_bad (_("opcode not supported on this processor")); + return; + } + + if (mips_opts.isa != 1) + { + s = "sdc1"; + goto st; + } + + s = "swc1"; + fmt = "T,o(b)"; + /* Itbl support may require additional care here. */ + coproc = 1; + goto ldd_std; + + case M_LD_AB: + if (ISA_HAS_64BIT_REGS (mips_opts.isa)) + { + s = "ld"; + goto ld; + } + + s = "lw"; + fmt = "t,o(b)"; + goto ldd_std; + + case M_SD_AB: + if (ISA_HAS_64BIT_REGS (mips_opts.isa)) + { + s = "sd"; + goto st; + } + + s = "sw"; + fmt = "t,o(b)"; + + ldd_std: + if (offset_expr.X_op != O_symbol + && offset_expr.X_op != O_constant) + { + as_bad (_("expression too complex")); + offset_expr.X_op = O_constant; + } + + /* Even on a big endian machine $fn comes before $fn+1. We have + to adjust when loading from memory. We set coproc if we must + load $fn+1 first. */ + /* Itbl support may require additional care here. */ + if (! target_big_endian) + coproc = 0; + + if (mips_pic == NO_PIC + || offset_expr.X_op == O_constant) + { + /* If this is a reference to a GP relative symbol, we want + <op> $treg,<sym>($gp) (BFD_RELOC_MIPS_GPREL) + <op> $treg+1,<sym>+4($gp) (BFD_RELOC_MIPS_GPREL) + If we have a base register, we use this + addu $at,$breg,$gp + <op> $treg,<sym>($at) (BFD_RELOC_MIPS_GPREL) + <op> $treg+1,<sym>+4($at) (BFD_RELOC_MIPS_GPREL) + If this is not a GP relative symbol, we want + lui $at,<sym> (BFD_RELOC_HI16_S) + <op> $treg,<sym>($at) (BFD_RELOC_LO16) + <op> $treg+1,<sym>+4($at) (BFD_RELOC_LO16) + If there is a base register, we add it to $at after the + lui instruction. If there is a constant, we always use + the last case. */ + if ((valueT) offset_expr.X_add_number >= MAX_GPREL_OFFSET + || nopic_need_relax (offset_expr.X_add_symbol, 1)) + { + p = NULL; + used_at = 1; + } + else + { + int off; + + if (breg == 0) + { + frag_grow (28); + tempreg = GP; + off = 0; + used_at = 0; + } + else + { + frag_grow (36); + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addu" : "daddu"), + "d,v,t", AT, breg, GP); + tempreg = AT; + off = 4; + used_at = 1; + } + + /* Itbl support may require additional care here. */ + macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt, + coproc ? treg + 1 : treg, + (int) BFD_RELOC_MIPS_GPREL, tempreg); + offset_expr.X_add_number += 4; + + /* Set mips_optimize to 2 to avoid inserting an + undesired nop. */ + hold_mips_optimize = mips_optimize; + mips_optimize = 2; + /* Itbl support may require additional care here. */ + macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt, + coproc ? treg : treg + 1, + (int) BFD_RELOC_MIPS_GPREL, tempreg); + mips_optimize = hold_mips_optimize; + + p = frag_var (rs_machine_dependent, 12 + off, 0, + RELAX_ENCODE (8 + off, 12 + off, 0, 4 + off, 1, + used_at && mips_opts.noat), + offset_expr.X_add_symbol, (offsetT) 0, + (char *) NULL); + + /* We just generated two relocs. When tc_gen_reloc + handles this case, it will skip the first reloc and + handle the second. The second reloc already has an + extra addend of 4, which we added above. We must + subtract it out, and then subtract another 4 to make + the first reloc come out right. The second reloc + will come out right because we are going to add 4 to + offset_expr when we build its instruction below. + + If we have a symbol, then we don't want to include + the offset, because it will wind up being included + when we generate the reloc. */ + + if (offset_expr.X_op == O_constant) + offset_expr.X_add_number -= 8; + else + { + offset_expr.X_add_number = -4; + offset_expr.X_op = O_constant; + } + } + macro_build_lui (p, &icnt, &offset_expr, AT); + if (p != NULL) + p += 4; + if (breg != 0) + { + macro_build (p, &icnt, (expressionS *) NULL, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addu" : "daddu"), + "d,v,t", AT, breg, AT); + if (p != NULL) + p += 4; + } + /* Itbl support may require additional care here. */ + macro_build (p, &icnt, &offset_expr, s, fmt, + coproc ? treg + 1 : treg, + (int) BFD_RELOC_LO16, AT); + if (p != NULL) + p += 4; + /* FIXME: How do we handle overflow here? */ + offset_expr.X_add_number += 4; + /* Itbl support may require additional care here. */ + macro_build (p, &icnt, &offset_expr, s, fmt, + coproc ? treg : treg + 1, + (int) BFD_RELOC_LO16, AT); + } + else if (mips_pic == SVR4_PIC && ! mips_big_got) + { + int off; + + /* If this is a reference to an external symbol, we want + lw $at,<sym>($gp) (BFD_RELOC_MIPS_GOT16) + nop + <op> $treg,0($at) + <op> $treg+1,4($at) + Otherwise we want + lw $at,<sym>($gp) (BFD_RELOC_MIPS_GOT16) + nop + <op> $treg,<sym>($at) (BFD_RELOC_LO16) + <op> $treg+1,<sym>+4($at) (BFD_RELOC_LO16) + If there is a base register we add it to $at before the + lwc1 instructions. If there is a constant we include it + in the lwc1 instructions. */ + used_at = 1; + expr1.X_add_number = offset_expr.X_add_number; + offset_expr.X_add_number = 0; + if (expr1.X_add_number < -0x8000 + || expr1.X_add_number >= 0x8000 - 4) + as_bad (_("PIC code offset overflow (max 16 signed bits)")); + if (breg == 0) + off = 0; + else + off = 4; + frag_grow (24 + off); + macro_build ((char *) NULL, &icnt, &offset_expr, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "lw" : "ld"), + "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT16, GP); + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", ""); + if (breg != 0) + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addu" : "daddu"), + "d,v,t", AT, breg, AT); + /* Itbl support may require additional care here. */ + macro_build ((char *) NULL, &icnt, &expr1, s, fmt, + coproc ? treg + 1 : treg, + (int) BFD_RELOC_LO16, AT); + expr1.X_add_number += 4; + + /* Set mips_optimize to 2 to avoid inserting an undesired + nop. */ + hold_mips_optimize = mips_optimize; + mips_optimize = 2; + /* Itbl support may require additional care here. */ + macro_build ((char *) NULL, &icnt, &expr1, s, fmt, + coproc ? treg : treg + 1, + (int) BFD_RELOC_LO16, AT); + mips_optimize = hold_mips_optimize; + + (void) frag_var (rs_machine_dependent, 0, 0, + RELAX_ENCODE (0, 0, -16 - off, -8, 1, 0), + offset_expr.X_add_symbol, (offsetT) 0, + (char *) NULL); + } + else if (mips_pic == SVR4_PIC) + { + int gpdel, off; + + /* If this is a reference to an external symbol, we want + lui $at,<sym> (BFD_RELOC_MIPS_GOT_HI16) + addu $at,$at,$gp + lw $at,<sym>($at) (BFD_RELOC_MIPS_GOT_LO16) + nop + <op> $treg,0($at) + <op> $treg+1,4($at) + Otherwise we want + lw $at,<sym>($gp) (BFD_RELOC_MIPS_GOT16) + nop + <op> $treg,<sym>($at) (BFD_RELOC_LO16) + <op> $treg+1,<sym>+4($at) (BFD_RELOC_LO16) + If there is a base register we add it to $at before the + lwc1 instructions. If there is a constant we include it + in the lwc1 instructions. */ + used_at = 1; + expr1.X_add_number = offset_expr.X_add_number; + offset_expr.X_add_number = 0; + if (expr1.X_add_number < -0x8000 + || expr1.X_add_number >= 0x8000 - 4) + as_bad (_("PIC code offset overflow (max 16 signed bits)")); + if (reg_needs_delay (GP)) + gpdel = 4; + else + gpdel = 0; + if (breg == 0) + off = 0; + else + off = 4; + frag_grow (56); + macro_build ((char *) NULL, &icnt, &offset_expr, "lui", "t,u", + AT, (int) BFD_RELOC_MIPS_GOT_HI16); + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addu" : "daddu"), + "d,v,t", AT, AT, GP); + macro_build ((char *) NULL, &icnt, &offset_expr, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "lw" : "ld"), + "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT_LO16, AT); + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", ""); + if (breg != 0) + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addu" : "daddu"), + "d,v,t", AT, breg, AT); + /* Itbl support may require additional care here. */ + macro_build ((char *) NULL, &icnt, &expr1, s, fmt, + coproc ? treg + 1 : treg, + (int) BFD_RELOC_LO16, AT); + expr1.X_add_number += 4; + + /* Set mips_optimize to 2 to avoid inserting an undesired + nop. */ + hold_mips_optimize = mips_optimize; + mips_optimize = 2; + /* Itbl support may require additional care here. */ + macro_build ((char *) NULL, &icnt, &expr1, s, fmt, + coproc ? treg : treg + 1, + (int) BFD_RELOC_LO16, AT); + mips_optimize = hold_mips_optimize; + expr1.X_add_number -= 4; + + p = frag_var (rs_machine_dependent, 16 + gpdel + off, 0, + RELAX_ENCODE (24 + off, 16 + gpdel + off, gpdel, + 8 + gpdel + off, 1, 0), + offset_expr.X_add_symbol, (offsetT) 0, + (char *) NULL); + if (gpdel > 0) + { + macro_build (p, &icnt, (expressionS *) NULL, "nop", ""); + p += 4; + } + macro_build (p, &icnt, &offset_expr, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "lw" : "ld"), + "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT16, GP); + p += 4; + macro_build (p, &icnt, (expressionS *) NULL, "nop", ""); + p += 4; + if (breg != 0) + { + macro_build (p, &icnt, (expressionS *) NULL, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addu" : "daddu"), + "d,v,t", AT, breg, AT); + p += 4; + } + /* Itbl support may require additional care here. */ + macro_build (p, &icnt, &expr1, s, fmt, + coproc ? treg + 1 : treg, + (int) BFD_RELOC_LO16, AT); + p += 4; + expr1.X_add_number += 4; + + /* Set mips_optimize to 2 to avoid inserting an undesired + nop. */ + hold_mips_optimize = mips_optimize; + mips_optimize = 2; + /* Itbl support may require additional care here. */ + macro_build (p, &icnt, &expr1, s, fmt, + coproc ? treg : treg + 1, + (int) BFD_RELOC_LO16, AT); + mips_optimize = hold_mips_optimize; + } + else if (mips_pic == EMBEDDED_PIC) + { + /* If there is no base register, we use + <op> $treg,<sym>($gp) (BFD_RELOC_MIPS_GPREL) + <op> $treg+1,<sym>+4($gp) (BFD_RELOC_MIPS_GPREL) + If we have a base register, we use + addu $at,$breg,$gp + <op> $treg,<sym>($at) (BFD_RELOC_MIPS_GPREL) + <op> $treg+1,<sym>+4($at) (BFD_RELOC_MIPS_GPREL) + */ + if (breg == 0) + { + tempreg = GP; + used_at = 0; + } + else + { + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addu" : "daddu"), + "d,v,t", AT, breg, GP); + tempreg = AT; + used_at = 1; + } + + /* Itbl support may require additional care here. */ + macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt, + coproc ? treg + 1 : treg, + (int) BFD_RELOC_MIPS_GPREL, tempreg); + offset_expr.X_add_number += 4; + /* Itbl support may require additional care here. */ + macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt, + coproc ? treg : treg + 1, + (int) BFD_RELOC_MIPS_GPREL, tempreg); + } + else + abort (); + + if (! used_at) + return; + + break; + + case M_LD_OB: + s = "lw"; + goto sd_ob; + case M_SD_OB: + s = "sw"; + sd_ob: + assert (bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)); + macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg, + (int) BFD_RELOC_LO16, breg); + offset_expr.X_add_number += 4; + macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg + 1, + (int) BFD_RELOC_LO16, breg); + return; + + /* New code added to support COPZ instructions. + This code builds table entries out of the macros in mip_opcodes. + R4000 uses interlocks to handle coproc delays. + Other chips (like the R3000) require nops to be inserted for delays. + + FIXME: Currently, we require that the user handle delays. + In order to fill delay slots for non-interlocked chips, + we must have a way to specify delays based on the coprocessor. + Eg. 4 cycles if load coproc reg from memory, 1 if in cache, etc. + What are the side-effects of the cop instruction? + What cache support might we have and what are its effects? + Both coprocessor & memory require delays. how long??? + What registers are read/set/modified? + + If an itbl is provided to interpret cop instructions, + this knowledge can be encoded in the itbl spec. */ + + case M_COP0: + s = "c0"; + goto copz; + case M_COP1: + s = "c1"; + goto copz; + case M_COP2: + s = "c2"; + goto copz; + case M_COP3: + s = "c3"; + copz: + /* For now we just do C (same as Cz). The parameter will be + stored in insn_opcode by mips_ip. */ + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "C", + ip->insn_opcode); + return; + +#ifdef LOSING_COMPILER + default: + /* Try and see if this is a new itbl instruction. + This code builds table entries out of the macros in mip_opcodes. + FIXME: For now we just assemble the expression and pass it's + value along as a 32-bit immediate. + We may want to have the assembler assemble this value, + so that we gain the assembler's knowledge of delay slots, + symbols, etc. + Would it be more efficient to use mask (id) here? */ + if (itbl_have_entries + && (immed_expr = itbl_assemble (ip->insn_mo->name, ""))) + { + s = ip->insn_mo->name; + s2 = "cop3"; + coproc = ITBL_DECODE_PNUM (immed_expr);; + macro_build ((char *) NULL, &icnt, &immed_expr, s, "C"); + return; + } + macro2 (ip); + return; + } + if (mips_opts.noat) + as_warn (_("Macro used $at after \".set noat\"")); +} + +static void +macro2 (ip) + struct mips_cl_insn *ip; +{ + register int treg, sreg, dreg, breg; + int tempreg; + int mask; + int icnt = 0; + int used_at; + expressionS expr1; + const char *s; + const char *s2; + const char *fmt; + int likely = 0; + int dbl = 0; + int coproc = 0; + int lr = 0; + int imm = 0; + int off; + offsetT maxnum; + bfd_reloc_code_real_type r; + char *p; + + treg = (ip->insn_opcode >> 16) & 0x1f; + dreg = (ip->insn_opcode >> 11) & 0x1f; + sreg = breg = (ip->insn_opcode >> 21) & 0x1f; + mask = ip->insn_mo->mask; + + expr1.X_op = O_constant; + expr1.X_op_symbol = NULL; + expr1.X_add_symbol = NULL; + expr1.X_add_number = 1; + + switch (mask) + { +#endif /* LOSING_COMPILER */ + + case M_DMUL: + dbl = 1; + case M_MUL: + macro_build ((char *) NULL, &icnt, NULL, + dbl ? "dmultu" : "multu", + "s,t", sreg, treg); + macro_build ((char *) NULL, &icnt, NULL, "mflo", "d", dreg); + return; + + case M_DMUL_I: + dbl = 1; + case M_MUL_I: + /* The MIPS assembler some times generates shifts and adds. I'm + not trying to be that fancy. GCC should do this for us + anyway. */ + load_register (&icnt, AT, &imm_expr, dbl); + macro_build ((char *) NULL, &icnt, NULL, + dbl ? "dmult" : "mult", + "s,t", sreg, AT); + macro_build ((char *) NULL, &icnt, NULL, "mflo", "d", dreg); + break; + + case M_DMULO_I: + dbl = 1; + case M_MULO_I: + imm = 1; + goto do_mulo; + + case M_DMULO: + dbl = 1; + case M_MULO: + do_mulo: + mips_emit_delays (true); + ++mips_opts.noreorder; + mips_any_noreorder = 1; + if (imm) + load_register (&icnt, AT, &imm_expr, dbl); + macro_build ((char *) NULL, &icnt, NULL, + dbl ? "dmult" : "mult", + "s,t", sreg, imm ? AT : treg); + macro_build ((char *) NULL, &icnt, NULL, "mflo", "d", dreg); + macro_build ((char *) NULL, &icnt, NULL, + dbl ? "dsra32" : "sra", + "d,w,<", dreg, dreg, 31); + macro_build ((char *) NULL, &icnt, NULL, "mfhi", "d", AT); + if (mips_trap) + macro_build ((char *) NULL, &icnt, NULL, "tne", "s,t", dreg, AT); + else + { + expr1.X_add_number = 8; + macro_build ((char *) NULL, &icnt, &expr1, "beq", "s,t,p", dreg, AT); + macro_build ((char *) NULL, &icnt, NULL, "nop", "", 0); + macro_build ((char *) NULL, &icnt, NULL, "break", "c", 6); + } + --mips_opts.noreorder; + macro_build ((char *) NULL, &icnt, NULL, "mflo", "d", dreg); + break; + + case M_DMULOU_I: + dbl = 1; + case M_MULOU_I: + imm = 1; + goto do_mulou; + + case M_DMULOU: + dbl = 1; + case M_MULOU: + do_mulou: + mips_emit_delays (true); + ++mips_opts.noreorder; + mips_any_noreorder = 1; + if (imm) + load_register (&icnt, AT, &imm_expr, dbl); + macro_build ((char *) NULL, &icnt, NULL, + dbl ? "dmultu" : "multu", + "s,t", sreg, imm ? AT : treg); + macro_build ((char *) NULL, &icnt, NULL, "mfhi", "d", AT); + macro_build ((char *) NULL, &icnt, NULL, "mflo", "d", dreg); + if (mips_trap) + macro_build ((char *) NULL, &icnt, NULL, "tne", "s,t", AT, 0); + else + { + expr1.X_add_number = 8; + macro_build ((char *) NULL, &icnt, &expr1, "beq", "s,t,p", AT, 0); + macro_build ((char *) NULL, &icnt, NULL, "nop", "", 0); + macro_build ((char *) NULL, &icnt, NULL, "break", "c", 6); + } + --mips_opts.noreorder; + break; + + case M_ROL: + macro_build ((char *) NULL, &icnt, NULL, "subu", "d,v,t", AT, 0, treg); + macro_build ((char *) NULL, &icnt, NULL, "srlv", "d,t,s", AT, sreg, AT); + macro_build ((char *) NULL, &icnt, NULL, "sllv", "d,t,s", dreg, sreg, + treg); + macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", dreg, dreg, AT); + break; + + case M_ROL_I: + if (imm_expr.X_op != O_constant) + as_bad (_("rotate count too large")); + macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", AT, sreg, + (int) (imm_expr.X_add_number & 0x1f)); + macro_build ((char *) NULL, &icnt, NULL, "srl", "d,w,<", dreg, sreg, + (int) ((0 - imm_expr.X_add_number) & 0x1f)); + macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", dreg, dreg, AT); + break; + + case M_ROR: + macro_build ((char *) NULL, &icnt, NULL, "subu", "d,v,t", AT, 0, treg); + macro_build ((char *) NULL, &icnt, NULL, "sllv", "d,t,s", AT, sreg, AT); + macro_build ((char *) NULL, &icnt, NULL, "srlv", "d,t,s", dreg, sreg, + treg); + macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", dreg, dreg, AT); + break; + + case M_ROR_I: + if (imm_expr.X_op != O_constant) + as_bad (_("rotate count too large")); + macro_build ((char *) NULL, &icnt, NULL, "srl", "d,w,<", AT, sreg, + (int) (imm_expr.X_add_number & 0x1f)); + macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", dreg, sreg, + (int) ((0 - imm_expr.X_add_number) & 0x1f)); + macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", dreg, dreg, AT); + break; + + case M_S_DOB: + if (mips_cpu == 4650) + { + as_bad (_("opcode not supported on this processor")); + return; + } + assert (mips_opts.isa == 1); + /* Even on a big endian machine $fn comes before $fn+1. We have + to adjust when storing to memory. */ + macro_build ((char *) NULL, &icnt, &offset_expr, "swc1", "T,o(b)", + target_big_endian ? treg + 1 : treg, + (int) BFD_RELOC_LO16, breg); + offset_expr.X_add_number += 4; + macro_build ((char *) NULL, &icnt, &offset_expr, "swc1", "T,o(b)", + target_big_endian ? treg : treg + 1, + (int) BFD_RELOC_LO16, breg); + return; + + case M_SEQ: + if (sreg == 0) + macro_build ((char *) NULL, &icnt, &expr1, "sltiu", "t,r,j", dreg, + treg, (int) BFD_RELOC_LO16); + else if (treg == 0) + macro_build ((char *) NULL, &icnt, &expr1, "sltiu", "t,r,j", dreg, + sreg, (int) BFD_RELOC_LO16); + else + { + macro_build ((char *) NULL, &icnt, NULL, "xor", "d,v,t", dreg, + sreg, treg); + macro_build ((char *) NULL, &icnt, &expr1, "sltiu", "t,r,j", dreg, + dreg, (int) BFD_RELOC_LO16); + } + return; + + case M_SEQ_I: + if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0) + { + macro_build ((char *) NULL, &icnt, &expr1, "sltiu", "t,r,j", dreg, + sreg, (int) BFD_RELOC_LO16); + return; + } + if (sreg == 0) + { + as_warn (_("Instruction %s: result is always false"), + ip->insn_mo->name); + macro_build ((char *) NULL, &icnt, NULL, "move", "d,s", dreg, 0); + return; + } + if (imm_expr.X_op == O_constant + && imm_expr.X_add_number >= 0 + && imm_expr.X_add_number < 0x10000) + { + macro_build ((char *) NULL, &icnt, &imm_expr, "xori", "t,r,i", dreg, + sreg, (int) BFD_RELOC_LO16); + used_at = 0; + } + else if (imm_expr.X_op == O_constant + && imm_expr.X_add_number > -0x8000 + && imm_expr.X_add_number < 0) + { + imm_expr.X_add_number = -imm_expr.X_add_number; + macro_build ((char *) NULL, &icnt, &imm_expr, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addiu" : "daddiu"), + "t,r,j", dreg, sreg, + (int) BFD_RELOC_LO16); + used_at = 0; + } + else + { + load_register (&icnt, AT, &imm_expr, 0); + macro_build ((char *) NULL, &icnt, NULL, "xor", "d,v,t", dreg, + sreg, AT); + used_at = 1; + } + macro_build ((char *) NULL, &icnt, &expr1, "sltiu", "t,r,j", dreg, dreg, + (int) BFD_RELOC_LO16); + if (used_at) + break; + return; + + case M_SGE: /* sreg >= treg <==> not (sreg < treg) */ + s = "slt"; + goto sge; + case M_SGEU: + s = "sltu"; + sge: + macro_build ((char *) NULL, &icnt, NULL, s, "d,v,t", dreg, sreg, treg); + macro_build ((char *) NULL, &icnt, &expr1, "xori", "t,r,i", dreg, dreg, + (int) BFD_RELOC_LO16); + return; + + case M_SGE_I: /* sreg >= I <==> not (sreg < I) */ + case M_SGEU_I: + if (imm_expr.X_op == O_constant + && imm_expr.X_add_number >= -0x8000 + && imm_expr.X_add_number < 0x8000) + { + macro_build ((char *) NULL, &icnt, &imm_expr, + mask == M_SGE_I ? "slti" : "sltiu", + "t,r,j", dreg, sreg, (int) BFD_RELOC_LO16); + used_at = 0; + } + else + { + load_register (&icnt, AT, &imm_expr, 0); + macro_build ((char *) NULL, &icnt, NULL, + mask == M_SGE_I ? "slt" : "sltu", + "d,v,t", dreg, sreg, AT); + used_at = 1; + } + macro_build ((char *) NULL, &icnt, &expr1, "xori", "t,r,i", dreg, dreg, + (int) BFD_RELOC_LO16); + if (used_at) + break; + return; + + case M_SGT: /* sreg > treg <==> treg < sreg */ + s = "slt"; + goto sgt; + case M_SGTU: + s = "sltu"; + sgt: + macro_build ((char *) NULL, &icnt, NULL, s, "d,v,t", dreg, treg, sreg); + return; + + case M_SGT_I: /* sreg > I <==> I < sreg */ + s = "slt"; + goto sgti; + case M_SGTU_I: + s = "sltu"; + sgti: + load_register (&icnt, AT, &imm_expr, 0); + macro_build ((char *) NULL, &icnt, NULL, s, "d,v,t", dreg, AT, sreg); + break; + + case M_SLE: /* sreg <= treg <==> treg >= sreg <==> not (treg < sreg) */ + s = "slt"; + goto sle; + case M_SLEU: + s = "sltu"; + sle: + macro_build ((char *) NULL, &icnt, NULL, s, "d,v,t", dreg, treg, sreg); + macro_build ((char *) NULL, &icnt, &expr1, "xori", "t,r,i", dreg, dreg, + (int) BFD_RELOC_LO16); + return; + + case M_SLE_I: /* sreg <= I <==> I >= sreg <==> not (I < sreg) */ + s = "slt"; + goto slei; + case M_SLEU_I: + s = "sltu"; + slei: + load_register (&icnt, AT, &imm_expr, 0); + macro_build ((char *) NULL, &icnt, NULL, s, "d,v,t", dreg, AT, sreg); + macro_build ((char *) NULL, &icnt, &expr1, "xori", "t,r,i", dreg, dreg, + (int) BFD_RELOC_LO16); + break; + + case M_SLT_I: + if (imm_expr.X_op == O_constant + && imm_expr.X_add_number >= -0x8000 + && imm_expr.X_add_number < 0x8000) + { + macro_build ((char *) NULL, &icnt, &imm_expr, "slti", "t,r,j", + dreg, sreg, (int) BFD_RELOC_LO16); + return; + } + load_register (&icnt, AT, &imm_expr, 0); + macro_build ((char *) NULL, &icnt, NULL, "slt", "d,v,t", dreg, sreg, AT); + break; + + case M_SLTU_I: + if (imm_expr.X_op == O_constant + && imm_expr.X_add_number >= -0x8000 + && imm_expr.X_add_number < 0x8000) + { + macro_build ((char *) NULL, &icnt, &imm_expr, "sltiu", "t,r,j", + dreg, sreg, (int) BFD_RELOC_LO16); + return; + } + load_register (&icnt, AT, &imm_expr, 0); + macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", dreg, sreg, + AT); + break; + + case M_SNE: + if (sreg == 0) + macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", dreg, 0, + treg); + else if (treg == 0) + macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", dreg, 0, + sreg); + else + { + macro_build ((char *) NULL, &icnt, NULL, "xor", "d,v,t", dreg, + sreg, treg); + macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", dreg, 0, + dreg); + } + return; + + case M_SNE_I: + if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0) + { + macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", dreg, 0, + sreg); + return; + } + if (sreg == 0) + { + as_warn (_("Instruction %s: result is always true"), + ip->insn_mo->name); + macro_build ((char *) NULL, &icnt, &expr1, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addiu" : "daddiu"), + "t,r,j", dreg, 0, (int) BFD_RELOC_LO16); + return; + } + if (imm_expr.X_op == O_constant + && imm_expr.X_add_number >= 0 + && imm_expr.X_add_number < 0x10000) + { + macro_build ((char *) NULL, &icnt, &imm_expr, "xori", "t,r,i", + dreg, sreg, (int) BFD_RELOC_LO16); + used_at = 0; + } + else if (imm_expr.X_op == O_constant + && imm_expr.X_add_number > -0x8000 + && imm_expr.X_add_number < 0) + { + imm_expr.X_add_number = -imm_expr.X_add_number; + macro_build ((char *) NULL, &icnt, &imm_expr, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addiu" : "daddiu"), + "t,r,j", dreg, sreg, (int) BFD_RELOC_LO16); + used_at = 0; + } + else + { + load_register (&icnt, AT, &imm_expr, 0); + macro_build ((char *) NULL, &icnt, NULL, "xor", "d,v,t", dreg, + sreg, AT); + used_at = 1; + } + macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", dreg, 0, dreg); + if (used_at) + break; + return; + + case M_DSUB_I: + dbl = 1; + case M_SUB_I: + if (imm_expr.X_op == O_constant + && imm_expr.X_add_number > -0x8000 + && imm_expr.X_add_number <= 0x8000) + { + imm_expr.X_add_number = -imm_expr.X_add_number; + macro_build ((char *) NULL, &icnt, &imm_expr, + dbl ? "daddi" : "addi", + "t,r,j", dreg, sreg, (int) BFD_RELOC_LO16); + return; + } + load_register (&icnt, AT, &imm_expr, dbl); + macro_build ((char *) NULL, &icnt, NULL, + dbl ? "dsub" : "sub", + "d,v,t", dreg, sreg, AT); + break; + + case M_DSUBU_I: + dbl = 1; + case M_SUBU_I: + if (imm_expr.X_op == O_constant + && imm_expr.X_add_number > -0x8000 + && imm_expr.X_add_number <= 0x8000) + { + imm_expr.X_add_number = -imm_expr.X_add_number; + macro_build ((char *) NULL, &icnt, &imm_expr, + dbl ? "daddiu" : "addiu", + "t,r,j", dreg, sreg, (int) BFD_RELOC_LO16); + return; + } + load_register (&icnt, AT, &imm_expr, dbl); + macro_build ((char *) NULL, &icnt, NULL, + dbl ? "dsubu" : "subu", + "d,v,t", dreg, sreg, AT); + break; + + case M_TEQ_I: + s = "teq"; + goto trap; + case M_TGE_I: + s = "tge"; + goto trap; + case M_TGEU_I: + s = "tgeu"; + goto trap; + case M_TLT_I: + s = "tlt"; + goto trap; + case M_TLTU_I: + s = "tltu"; + goto trap; + case M_TNE_I: + s = "tne"; + trap: + load_register (&icnt, AT, &imm_expr, 0); + macro_build ((char *) NULL, &icnt, NULL, s, "s,t", sreg, AT); + break; + + case M_TRUNCWD: + case M_TRUNCWS: + assert (mips_opts.isa == 1); + sreg = (ip->insn_opcode >> 11) & 0x1f; /* floating reg */ + dreg = (ip->insn_opcode >> 06) & 0x1f; /* floating reg */ + + /* + * Is the double cfc1 instruction a bug in the mips assembler; + * or is there a reason for it? + */ + mips_emit_delays (true); + ++mips_opts.noreorder; + mips_any_noreorder = 1; + macro_build ((char *) NULL, &icnt, NULL, "cfc1", "t,G", treg, 31); + macro_build ((char *) NULL, &icnt, NULL, "cfc1", "t,G", treg, 31); + macro_build ((char *) NULL, &icnt, NULL, "nop", ""); + expr1.X_add_number = 3; + macro_build ((char *) NULL, &icnt, &expr1, "ori", "t,r,i", AT, treg, + (int) BFD_RELOC_LO16); + expr1.X_add_number = 2; + macro_build ((char *) NULL, &icnt, &expr1, "xori", "t,r,i", AT, AT, + (int) BFD_RELOC_LO16); + macro_build ((char *) NULL, &icnt, NULL, "ctc1", "t,G", AT, 31); + macro_build ((char *) NULL, &icnt, NULL, "nop", ""); + macro_build ((char *) NULL, &icnt, NULL, + mask == M_TRUNCWD ? "cvt.w.d" : "cvt.w.s", "D,S", dreg, sreg); + macro_build ((char *) NULL, &icnt, NULL, "ctc1", "t,G", treg, 31); + macro_build ((char *) NULL, &icnt, NULL, "nop", ""); + --mips_opts.noreorder; + break; + + case M_ULH: + s = "lb"; + goto ulh; + case M_ULHU: + s = "lbu"; + ulh: + if (offset_expr.X_add_number >= 0x7fff) + as_bad (_("operand overflow")); + /* avoid load delay */ + if (! target_big_endian) + offset_expr.X_add_number += 1; + macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg, + (int) BFD_RELOC_LO16, breg); + if (! target_big_endian) + offset_expr.X_add_number -= 1; + else + offset_expr.X_add_number += 1; + macro_build ((char *) NULL, &icnt, &offset_expr, "lbu", "t,o(b)", AT, + (int) BFD_RELOC_LO16, breg); + macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", treg, treg, 8); + macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", treg, treg, AT); + break; + + case M_ULD: + s = "ldl"; + s2 = "ldr"; + off = 7; + goto ulw; + case M_ULW: + s = "lwl"; + s2 = "lwr"; + off = 3; + ulw: + if (offset_expr.X_add_number >= 0x8000 - off) + as_bad (_("operand overflow")); + if (! target_big_endian) + offset_expr.X_add_number += off; + macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg, + (int) BFD_RELOC_LO16, breg); + if (! target_big_endian) + offset_expr.X_add_number -= off; + else + offset_expr.X_add_number += off; + macro_build ((char *) NULL, &icnt, &offset_expr, s2, "t,o(b)", treg, + (int) BFD_RELOC_LO16, breg); + return; + + case M_ULD_A: + s = "ldl"; + s2 = "ldr"; + off = 7; + goto ulwa; + case M_ULW_A: + s = "lwl"; + s2 = "lwr"; + off = 3; + ulwa: + load_address (&icnt, AT, &offset_expr); + if (breg != 0) + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addu" : "daddu"), + "d,v,t", AT, AT, breg); + if (! target_big_endian) + expr1.X_add_number = off; + else + expr1.X_add_number = 0; + macro_build ((char *) NULL, &icnt, &expr1, s, "t,o(b)", treg, + (int) BFD_RELOC_LO16, AT); + if (! target_big_endian) + expr1.X_add_number = 0; + else + expr1.X_add_number = off; + macro_build ((char *) NULL, &icnt, &expr1, s2, "t,o(b)", treg, + (int) BFD_RELOC_LO16, AT); + break; + + case M_ULH_A: + case M_ULHU_A: + load_address (&icnt, AT, &offset_expr); + if (breg != 0) + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addu" : "daddu"), + "d,v,t", AT, AT, breg); + if (target_big_endian) + expr1.X_add_number = 0; + macro_build ((char *) NULL, &icnt, &expr1, + mask == M_ULH_A ? "lb" : "lbu", "t,o(b)", treg, + (int) BFD_RELOC_LO16, AT); + if (target_big_endian) + expr1.X_add_number = 1; + else + expr1.X_add_number = 0; + macro_build ((char *) NULL, &icnt, &expr1, "lbu", "t,o(b)", AT, + (int) BFD_RELOC_LO16, AT); + macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", treg, + treg, 8); + macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", treg, + treg, AT); + break; + + case M_USH: + if (offset_expr.X_add_number >= 0x7fff) + as_bad (_("operand overflow")); + if (target_big_endian) + offset_expr.X_add_number += 1; + macro_build ((char *) NULL, &icnt, &offset_expr, "sb", "t,o(b)", treg, + (int) BFD_RELOC_LO16, breg); + macro_build ((char *) NULL, &icnt, NULL, "srl", "d,w,<", AT, treg, 8); + if (target_big_endian) + offset_expr.X_add_number -= 1; + else + offset_expr.X_add_number += 1; + macro_build ((char *) NULL, &icnt, &offset_expr, "sb", "t,o(b)", AT, + (int) BFD_RELOC_LO16, breg); + break; + + case M_USD: + s = "sdl"; + s2 = "sdr"; + off = 7; + goto usw; + case M_USW: + s = "swl"; + s2 = "swr"; + off = 3; + usw: + if (offset_expr.X_add_number >= 0x8000 - off) + as_bad (_("operand overflow")); + if (! target_big_endian) + offset_expr.X_add_number += off; + macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg, + (int) BFD_RELOC_LO16, breg); + if (! target_big_endian) + offset_expr.X_add_number -= off; + else + offset_expr.X_add_number += off; + macro_build ((char *) NULL, &icnt, &offset_expr, s2, "t,o(b)", treg, + (int) BFD_RELOC_LO16, breg); + return; + + case M_USD_A: + s = "sdl"; + s2 = "sdr"; + off = 7; + goto uswa; + case M_USW_A: + s = "swl"; + s2 = "swr"; + off = 3; + uswa: + load_address (&icnt, AT, &offset_expr); + if (breg != 0) + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addu" : "daddu"), + "d,v,t", AT, AT, breg); + if (! target_big_endian) + expr1.X_add_number = off; + else + expr1.X_add_number = 0; + macro_build ((char *) NULL, &icnt, &expr1, s, "t,o(b)", treg, + (int) BFD_RELOC_LO16, AT); + if (! target_big_endian) + expr1.X_add_number = 0; + else + expr1.X_add_number = off; + macro_build ((char *) NULL, &icnt, &expr1, s2, "t,o(b)", treg, + (int) BFD_RELOC_LO16, AT); + break; + + case M_USH_A: + load_address (&icnt, AT, &offset_expr); + if (breg != 0) + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addu" : "daddu"), + "d,v,t", AT, AT, breg); + if (! target_big_endian) + expr1.X_add_number = 0; + macro_build ((char *) NULL, &icnt, &expr1, "sb", "t,o(b)", treg, + (int) BFD_RELOC_LO16, AT); + macro_build ((char *) NULL, &icnt, NULL, "srl", "d,w,<", treg, + treg, 8); + if (! target_big_endian) + expr1.X_add_number = 1; + else + expr1.X_add_number = 0; + macro_build ((char *) NULL, &icnt, &expr1, "sb", "t,o(b)", treg, + (int) BFD_RELOC_LO16, AT); + if (! target_big_endian) + expr1.X_add_number = 0; + else + expr1.X_add_number = 1; + macro_build ((char *) NULL, &icnt, &expr1, "lbu", "t,o(b)", AT, + (int) BFD_RELOC_LO16, AT); + macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", treg, + treg, 8); + macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", treg, + treg, AT); + break; + + default: + /* FIXME: Check if this is one of the itbl macros, since they + are added dynamically. */ + as_bad (_("Macro %s not implemented yet"), ip->insn_mo->name); + break; + } + if (mips_opts.noat) + as_warn (_("Macro used $at after \".set noat\"")); +} + +/* Implement macros in mips16 mode. */ + +static void +mips16_macro (ip) + struct mips_cl_insn *ip; +{ + int mask; + int xreg, yreg, zreg, tmp; + int icnt; + expressionS expr1; + int dbl; + const char *s, *s2, *s3; + + mask = ip->insn_mo->mask; + + xreg = (ip->insn_opcode >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX; + yreg = (ip->insn_opcode >> MIPS16OP_SH_RY) & MIPS16OP_MASK_RY; + zreg = (ip->insn_opcode >> MIPS16OP_SH_RZ) & MIPS16OP_MASK_RZ; + + icnt = 0; + + expr1.X_op = O_constant; + expr1.X_op_symbol = NULL; + expr1.X_add_symbol = NULL; + expr1.X_add_number = 1; + + dbl = 0; + + switch (mask) + { + default: + internalError (); + + case M_DDIV_3: + dbl = 1; + case M_DIV_3: + s = "mflo"; + goto do_div3; + case M_DREM_3: + dbl = 1; + case M_REM_3: + s = "mfhi"; + do_div3: + mips_emit_delays (true); + ++mips_opts.noreorder; + mips_any_noreorder = 1; + macro_build ((char *) NULL, &icnt, NULL, + dbl ? "ddiv" : "div", + "0,x,y", xreg, yreg); + expr1.X_add_number = 2; + macro_build ((char *) NULL, &icnt, &expr1, "bnez", "x,p", yreg); + macro_build ((char *) NULL, &icnt, NULL, "break", "6", 7); + + /* FIXME: The normal code checks for of -1 / -0x80000000 here, + since that causes an overflow. We should do that as well, + but I don't see how to do the comparisons without a temporary + register. */ + --mips_opts.noreorder; + macro_build ((char *) NULL, &icnt, NULL, s, "x", zreg); + break; + + case M_DIVU_3: + s = "divu"; + s2 = "mflo"; + goto do_divu3; + case M_REMU_3: + s = "divu"; + s2 = "mfhi"; + goto do_divu3; + case M_DDIVU_3: + s = "ddivu"; + s2 = "mflo"; + goto do_divu3; + case M_DREMU_3: + s = "ddivu"; + s2 = "mfhi"; + do_divu3: + mips_emit_delays (true); + ++mips_opts.noreorder; + mips_any_noreorder = 1; + macro_build ((char *) NULL, &icnt, NULL, s, "0,x,y", xreg, yreg); + expr1.X_add_number = 2; + macro_build ((char *) NULL, &icnt, &expr1, "bnez", "x,p", yreg); + macro_build ((char *) NULL, &icnt, NULL, "break", "6", 7); + --mips_opts.noreorder; + macro_build ((char *) NULL, &icnt, NULL, s2, "x", zreg); + break; + + case M_DMUL: + dbl = 1; + case M_MUL: + macro_build ((char *) NULL, &icnt, NULL, + dbl ? "dmultu" : "multu", + "x,y", xreg, yreg); + macro_build ((char *) NULL, &icnt, NULL, "mflo", "x", zreg); + return; + + case M_DSUBU_I: + dbl = 1; + goto do_subu; + case M_SUBU_I: + do_subu: + if (imm_expr.X_op != O_constant) + as_bad (_("Unsupported large constant")); + imm_expr.X_add_number = -imm_expr.X_add_number; + macro_build ((char *) NULL, &icnt, &imm_expr, + dbl ? "daddiu" : "addiu", + "y,x,4", yreg, xreg); + break; + + case M_SUBU_I_2: + if (imm_expr.X_op != O_constant) + as_bad (_("Unsupported large constant")); + imm_expr.X_add_number = -imm_expr.X_add_number; + macro_build ((char *) NULL, &icnt, &imm_expr, "addiu", + "x,k", xreg); + break; + + case M_DSUBU_I_2: + if (imm_expr.X_op != O_constant) + as_bad (_("Unsupported large constant")); + imm_expr.X_add_number = -imm_expr.X_add_number; + macro_build ((char *) NULL, &icnt, &imm_expr, "daddiu", + "y,j", yreg); + break; + + case M_BEQ: + s = "cmp"; + s2 = "bteqz"; + goto do_branch; + case M_BNE: + s = "cmp"; + s2 = "btnez"; + goto do_branch; + case M_BLT: + s = "slt"; + s2 = "btnez"; + goto do_branch; + case M_BLTU: + s = "sltu"; + s2 = "btnez"; + goto do_branch; + case M_BLE: + s = "slt"; + s2 = "bteqz"; + goto do_reverse_branch; + case M_BLEU: + s = "sltu"; + s2 = "bteqz"; + goto do_reverse_branch; + case M_BGE: + s = "slt"; + s2 = "bteqz"; + goto do_branch; + case M_BGEU: + s = "sltu"; + s2 = "bteqz"; + goto do_branch; + case M_BGT: + s = "slt"; + s2 = "btnez"; + goto do_reverse_branch; + case M_BGTU: + s = "sltu"; + s2 = "btnez"; + + do_reverse_branch: + tmp = xreg; + xreg = yreg; + yreg = tmp; + + do_branch: + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "x,y", + xreg, yreg); + macro_build ((char *) NULL, &icnt, &offset_expr, s2, "p"); + break; + + case M_BEQ_I: + s = "cmpi"; + s2 = "bteqz"; + s3 = "x,U"; + goto do_branch_i; + case M_BNE_I: + s = "cmpi"; + s2 = "btnez"; + s3 = "x,U"; + goto do_branch_i; + case M_BLT_I: + s = "slti"; + s2 = "btnez"; + s3 = "x,8"; + goto do_branch_i; + case M_BLTU_I: + s = "sltiu"; + s2 = "btnez"; + s3 = "x,8"; + goto do_branch_i; + case M_BLE_I: + s = "slti"; + s2 = "btnez"; + s3 = "x,8"; + goto do_addone_branch_i; + case M_BLEU_I: + s = "sltiu"; + s2 = "btnez"; + s3 = "x,8"; + goto do_addone_branch_i; + case M_BGE_I: + s = "slti"; + s2 = "bteqz"; + s3 = "x,8"; + goto do_branch_i; + case M_BGEU_I: + s = "sltiu"; + s2 = "bteqz"; + s3 = "x,8"; + goto do_branch_i; + case M_BGT_I: + s = "slti"; + s2 = "bteqz"; + s3 = "x,8"; + goto do_addone_branch_i; + case M_BGTU_I: + s = "sltiu"; + s2 = "bteqz"; + s3 = "x,8"; + + do_addone_branch_i: + if (imm_expr.X_op != O_constant) + as_bad (_("Unsupported large constant")); + ++imm_expr.X_add_number; + + do_branch_i: + macro_build ((char *) NULL, &icnt, &imm_expr, s, s3, xreg); + macro_build ((char *) NULL, &icnt, &offset_expr, s2, "p"); + break; + + case M_ABS: + expr1.X_add_number = 0; + macro_build ((char *) NULL, &icnt, &expr1, "slti", "x,8", yreg); + if (xreg != yreg) + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + "move", "y,X", xreg, yreg); + expr1.X_add_number = 2; + macro_build ((char *) NULL, &icnt, &expr1, "bteqz", "p"); + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + "neg", "x,w", xreg, xreg); + } +} + +/* For consistency checking, verify that all bits are specified either + by the match/mask part of the instruction definition, or by the + operand list. */ +static int +validate_mips_insn (opc) + const struct mips_opcode *opc; +{ + const char *p = opc->args; + char c; + unsigned long used_bits = opc->mask; + + if ((used_bits & opc->match) != opc->match) + { + as_bad (_("internal: bad mips opcode (mask error): %s %s"), + opc->name, opc->args); + return 0; + } +#define USE_BITS(mask,shift) (used_bits |= ((mask) << (shift))) + while (*p) + switch (c = *p++) + { + case ',': break; + case '(': break; + case ')': break; + case '<': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break; + case '>': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break; + case 'A': break; + case 'B': USE_BITS (OP_MASK_SYSCALL, OP_SH_SYSCALL); break; + case 'C': USE_BITS (OP_MASK_COPZ, OP_SH_COPZ); break; + case 'D': USE_BITS (OP_MASK_FD, OP_SH_FD); break; + case 'E': USE_BITS (OP_MASK_RT, OP_SH_RT); break; + case 'F': break; + case 'G': USE_BITS (OP_MASK_RD, OP_SH_RD); break; + case 'I': break; + case 'L': break; + case 'M': USE_BITS (OP_MASK_CCC, OP_SH_CCC); break; + case 'N': USE_BITS (OP_MASK_BCC, OP_SH_BCC); break; + case 'R': USE_BITS (OP_MASK_FR, OP_SH_FR); break; + case 'S': USE_BITS (OP_MASK_FS, OP_SH_FS); break; + case 'T': USE_BITS (OP_MASK_FT, OP_SH_FT); break; + case 'V': USE_BITS (OP_MASK_FS, OP_SH_FS); break; + case 'W': USE_BITS (OP_MASK_FT, OP_SH_FT); break; + case 'a': USE_BITS (OP_MASK_TARGET, OP_SH_TARGET); break; + case 'b': USE_BITS (OP_MASK_RS, OP_SH_RS); break; + case 'c': USE_BITS (OP_MASK_CODE, OP_SH_CODE); break; + case 'd': USE_BITS (OP_MASK_RD, OP_SH_RD); break; + case 'f': break; + case 'h': USE_BITS (OP_MASK_PREFX, OP_SH_PREFX); break; + case 'i': USE_BITS (OP_MASK_IMMEDIATE, OP_SH_IMMEDIATE); break; + case 'j': USE_BITS (OP_MASK_DELTA, OP_SH_DELTA); break; + case 'k': USE_BITS (OP_MASK_CACHE, OP_SH_CACHE); break; + case 'l': break; + case 'o': USE_BITS (OP_MASK_DELTA, OP_SH_DELTA); break; + case 'p': USE_BITS (OP_MASK_DELTA, OP_SH_DELTA); break; + case 'q': USE_BITS (OP_MASK_CODE2, OP_SH_CODE2); break; + case 'r': USE_BITS (OP_MASK_RS, OP_SH_RS); break; + case 's': USE_BITS (OP_MASK_RS, OP_SH_RS); break; + case 't': USE_BITS (OP_MASK_RT, OP_SH_RT); break; + case 'u': USE_BITS (OP_MASK_IMMEDIATE, OP_SH_IMMEDIATE); break; + case 'v': USE_BITS (OP_MASK_RS, OP_SH_RS); break; + case 'w': USE_BITS (OP_MASK_RT, OP_SH_RT); break; + case 'x': break; + case 'z': break; + case 'P': USE_BITS (OP_MASK_PERFREG, OP_SH_PERFREG); break; + default: + as_bad (_("internal: bad mips opcode (unknown operand type `%c'): %s %s"), + c, opc->name, opc->args); + return 0; + } +#undef USE_BITS + if (used_bits != 0xffffffff) + { + as_bad (_("internal: bad mips opcode (bits 0x%lx undefined): %s %s"), + ~used_bits & 0xffffffff, opc->name, opc->args); + return 0; + } + return 1; +} + +/* This routine assembles an instruction into its binary format. As a + side effect, it sets one of the global variables imm_reloc or + offset_reloc to the type of relocation to do if one of the operands + is an address expression. */ + +static void +mips_ip (str, ip) + char *str; + struct mips_cl_insn *ip; +{ + char *s; + const char *args; + char c; + struct mips_opcode *insn; + char *argsStart; + unsigned int regno; + unsigned int lastregno = 0; + char *s_reset; + char save_c = 0; + int full_opcode_match = 1; + + insn_error = NULL; + + /* If the instruction contains a '.', we first try to match an instruction + including the '.'. Then we try again without the '.'. */ + insn = NULL; + for (s = str; *s != '\0' && !isspace ((unsigned char) *s); ++s) + continue; + + /* If we stopped on whitespace, then replace the whitespace with null for + the call to hash_find. Save the character we replaced just in case we + have to re-parse the instruction. */ + if (isspace ((unsigned char) *s)) + { + save_c = *s; + *s++ = '\0'; + } + + insn = (struct mips_opcode *) hash_find (op_hash, str); + + /* If we didn't find the instruction in the opcode table, try again, but + this time with just the instruction up to, but not including the + first '.'. */ + if (insn == NULL) + { + /* Restore the character we overwrite above (if any). */ + if (save_c) + *(--s) = save_c; + + /* Scan up to the first '.' or whitespace. */ + for (s = str; *s != '\0' && *s != '.' && !isspace ((unsigned char) *s); ++s) + continue; + + /* If we did not find a '.', then we can quit now. */ + if (*s != '.') + { + insn_error = "unrecognized opcode"; + return; + } + + /* Lookup the instruction in the hash table. */ + *s++ = '\0'; + if ((insn = (struct mips_opcode *) hash_find (op_hash, str)) == NULL) + { + insn_error = "unrecognized opcode"; + return; + } + + full_opcode_match = 0; + } + + argsStart = s; + for (;;) + { + boolean ok; + + assert (strcmp (insn->name, str) == 0); + + if (OPCODE_IS_MEMBER (insn, mips_opts.isa, mips_cpu, mips_gp32)) + ok = true; + else + ok = false; + + if (insn->pinfo != INSN_MACRO) + { + if (mips_cpu == 4650 && (insn->pinfo & FP_D) != 0) + ok = false; + } + + if (! ok) + { + if (insn + 1 < &mips_opcodes[NUMOPCODES] + && strcmp (insn->name, insn[1].name) == 0) + { + ++insn; + continue; + } + else + { + static char buf[100]; + sprintf (buf, + _("opcode not supported on this processor: %d (MIPS%d)"), + mips_cpu, mips_opts.isa); + + insn_error = buf; + return; + } + } + + ip->insn_mo = insn; + ip->insn_opcode = insn->match; + for (args = insn->args;; ++args) + { + if (*s == ' ') + ++s; + switch (*args) + { + case '\0': /* end of args */ + if (*s == '\0') + return; + break; + + case ',': + if (*s++ == *args) + continue; + s--; + switch (*++args) + { + case 'r': + case 'v': + ip->insn_opcode |= lastregno << 21; + continue; + + case 'w': + case 'W': + ip->insn_opcode |= lastregno << 16; + continue; + + case 'V': + ip->insn_opcode |= lastregno << 11; + continue; + } + break; + + case '(': + /* Handle optional base register. + Either the base register is omitted or + we must have a left paren. */ + /* This is dependent on the next operand specifier + is a base register specification. */ + assert (args[1] == 'b' || args[1] == '5' + || args[1] == '-' || args[1] == '4'); + if (*s == '\0') + return; + + case ')': /* these must match exactly */ + if (*s++ == *args) + continue; + break; + + case '<': /* must be at least one digit */ + /* + * According to the manual, if the shift amount is greater + * than 31 or less than 0 the the shift amount should be + * mod 32. In reality the mips assembler issues an error. + * We issue a warning and mask out all but the low 5 bits. + */ + my_getExpression (&imm_expr, s); + check_absolute_expr (ip, &imm_expr); + if ((unsigned long) imm_expr.X_add_number > 31) + { + as_warn (_("Improper shift amount (%ld)"), + (long) imm_expr.X_add_number); + imm_expr.X_add_number = imm_expr.X_add_number & 0x1f; + } + ip->insn_opcode |= imm_expr.X_add_number << 6; + imm_expr.X_op = O_absent; + s = expr_end; + continue; + + case '>': /* shift amount minus 32 */ + my_getExpression (&imm_expr, s); + check_absolute_expr (ip, &imm_expr); + if ((unsigned long) imm_expr.X_add_number < 32 + || (unsigned long) imm_expr.X_add_number > 63) + break; + ip->insn_opcode |= (imm_expr.X_add_number - 32) << 6; + imm_expr.X_op = O_absent; + s = expr_end; + continue; + + + case 'k': /* cache code */ + case 'h': /* prefx code */ + my_getExpression (&imm_expr, s); + check_absolute_expr (ip, &imm_expr); + if ((unsigned long) imm_expr.X_add_number > 31) + { + as_warn (_("Invalid value for `%s' (%lu)"), + ip->insn_mo->name, + (unsigned long) imm_expr.X_add_number); + imm_expr.X_add_number &= 0x1f; + } + if (*args == 'k') + ip->insn_opcode |= imm_expr.X_add_number << OP_SH_CACHE; + else + ip->insn_opcode |= imm_expr.X_add_number << OP_SH_PREFX; + imm_expr.X_op = O_absent; + s = expr_end; + continue; + + case 'c': /* break code */ + my_getExpression (&imm_expr, s); + check_absolute_expr (ip, &imm_expr); + if ((unsigned) imm_expr.X_add_number > 1023) + { + as_warn (_("Illegal break code (%ld)"), + (long) imm_expr.X_add_number); + imm_expr.X_add_number &= 0x3ff; + } + ip->insn_opcode |= imm_expr.X_add_number << 16; + imm_expr.X_op = O_absent; + s = expr_end; + continue; + + case 'q': /* lower break code */ + my_getExpression (&imm_expr, s); + check_absolute_expr (ip, &imm_expr); + if ((unsigned) imm_expr.X_add_number > 1023) + { + as_warn (_("Illegal lower break code (%ld)"), + (long) imm_expr.X_add_number); + imm_expr.X_add_number &= 0x3ff; + } + ip->insn_opcode |= imm_expr.X_add_number << 6; + imm_expr.X_op = O_absent; + s = expr_end; + continue; + + case 'B': /* syscall code */ + my_getExpression (&imm_expr, s); + check_absolute_expr (ip, &imm_expr); + if ((unsigned) imm_expr.X_add_number > 0xfffff) + as_warn (_("Illegal syscall code (%ld)"), + (long) imm_expr.X_add_number); + ip->insn_opcode |= imm_expr.X_add_number << 6; + imm_expr.X_op = O_absent; + s = expr_end; + continue; + + case 'C': /* Coprocessor code */ + my_getExpression (&imm_expr, s); + check_absolute_expr (ip, &imm_expr); + if ((unsigned long) imm_expr.X_add_number >= (1<<25)) + { + as_warn (_("Coproccesor code > 25 bits (%ld)"), + (long) imm_expr.X_add_number); + imm_expr.X_add_number &= ((1<<25) - 1); + } + ip->insn_opcode |= imm_expr.X_add_number; + imm_expr.X_op = O_absent; + s = expr_end; + continue; + + case 'P': /* Performance register */ + my_getExpression (&imm_expr, s); + check_absolute_expr (ip, &imm_expr); + if (imm_expr.X_add_number != 0 && imm_expr.X_add_number != 1) + { + as_warn (_("Invalidate performance regster (%ld)"), + (long) imm_expr.X_add_number); + imm_expr.X_add_number &= 1; + } + ip->insn_opcode |= (imm_expr.X_add_number << 1); + imm_expr.X_op = O_absent; + s = expr_end; + continue; + + case 'b': /* base register */ + case 'd': /* destination register */ + case 's': /* source register */ + case 't': /* target register */ + case 'r': /* both target and source */ + case 'v': /* both dest and source */ + case 'w': /* both dest and target */ + case 'E': /* coprocessor target register */ + case 'G': /* coprocessor destination register */ + case 'x': /* ignore register name */ + case 'z': /* must be zero register */ + s_reset = s; + if (s[0] == '$') + { + + if (isdigit ((unsigned char) s[1])) + { + ++s; + regno = 0; + do + { + regno *= 10; + regno += *s - '0'; + ++s; + } + while (isdigit ((unsigned char) *s)); + if (regno > 31) + as_bad (_("Invalid register number (%d)"), regno); + } + else if (*args == 'E' || *args == 'G') + goto notreg; + else + { + if (s[1] == 'f' && s[2] == 'p') + { + s += 3; + regno = FP; + } + else if (s[1] == 's' && s[2] == 'p') + { + s += 3; + regno = SP; + } + else if (s[1] == 'g' && s[2] == 'p') + { + s += 3; + regno = GP; + } + else if (s[1] == 'a' && s[2] == 't') + { + s += 3; + regno = AT; + } + else if (s[1] == 'k' && s[2] == 't' && s[3] == '0') + { + s += 4; + regno = KT0; + } + else if (s[1] == 'k' && s[2] == 't' && s[3] == '1') + { + s += 4; + regno = KT1; + } + else if (itbl_have_entries) + { + char *p, *n; + unsigned long r; + + p = s + 1; /* advance past '$' */ + n = itbl_get_field (&p); /* n is name */ + + /* See if this is a register defined in an + itbl entry. */ + if (itbl_get_reg_val (n, &r)) + { + /* Get_field advances to the start of + the next field, so we need to back + rack to the end of the last field. */ + if (p) + s = p - 1; + else + s = strchr (s, '\0'); + regno = r; + } + else + goto notreg; + } + else + goto notreg; + } + if (regno == AT + && ! mips_opts.noat + && *args != 'E' + && *args != 'G') + as_warn (_("Used $at without \".set noat\"")); + c = *args; + if (*s == ' ') + s++; + if (args[1] != *s) + { + if (c == 'r' || c == 'v' || c == 'w') + { + regno = lastregno; + s = s_reset; + args++; + } + } + /* 'z' only matches $0. */ + if (c == 'z' && regno != 0) + break; + + /* Now that we have assembled one operand, we use the args string + * to figure out where it goes in the instruction. */ + switch (c) + { + case 'r': + case 's': + case 'v': + case 'b': + ip->insn_opcode |= regno << 21; + break; + case 'd': + case 'G': + ip->insn_opcode |= regno << 11; + break; + case 'w': + case 't': + case 'E': + ip->insn_opcode |= regno << 16; + break; + case 'x': + /* This case exists because on the r3000 trunc + expands into a macro which requires a gp + register. On the r6000 or r4000 it is + assembled into a single instruction which + ignores the register. Thus the insn version + is MIPS_ISA2 and uses 'x', and the macro + version is MIPS_ISA1 and uses 't'. */ + break; + case 'z': + /* This case is for the div instruction, which + acts differently if the destination argument + is $0. This only matches $0, and is checked + outside the switch. */ + break; + case 'D': + /* Itbl operand; not yet implemented. FIXME ?? */ + break; + /* What about all other operands like 'i', which + can be specified in the opcode table? */ + } + lastregno = regno; + continue; + } + notreg: + switch (*args++) + { + case 'r': + case 'v': + ip->insn_opcode |= lastregno << 21; + continue; + case 'w': + ip->insn_opcode |= lastregno << 16; + continue; + } + break; + + case 'D': /* floating point destination register */ + case 'S': /* floating point source register */ + case 'T': /* floating point target register */ + case 'R': /* floating point source register */ + case 'V': + case 'W': + s_reset = s; + if (s[0] == '$' && s[1] == 'f' && isdigit ((unsigned char) s[2])) + { + s += 2; + regno = 0; + do + { + regno *= 10; + regno += *s - '0'; + ++s; + } + while (isdigit ((unsigned char) *s)); + + if (regno > 31) + as_bad (_("Invalid float register number (%d)"), regno); + + if ((regno & 1) != 0 + && ! ISA_HAS_64BIT_REGS (mips_opts.isa) + && ! (strcmp (str, "mtc1") == 0 + || strcmp (str, "mfc1") == 0 + || strcmp (str, "lwc1") == 0 + || strcmp (str, "swc1") == 0 + || strcmp (str, "l.s") == 0 + || strcmp (str, "s.s") == 0)) + as_warn (_("Float register should be even, was %d"), + regno); + + c = *args; + if (*s == ' ') + s++; + if (args[1] != *s) + { + if (c == 'V' || c == 'W') + { + regno = lastregno; + s = s_reset; + args++; + } + } + switch (c) + { + case 'D': + ip->insn_opcode |= regno << 6; + break; + case 'V': + case 'S': + ip->insn_opcode |= regno << 11; + break; + case 'W': + case 'T': + ip->insn_opcode |= regno << 16; + break; + case 'R': + ip->insn_opcode |= regno << 21; + break; + } + lastregno = regno; + continue; + } + + + switch (*args++) + { + case 'V': + ip->insn_opcode |= lastregno << 11; + continue; + case 'W': + ip->insn_opcode |= lastregno << 16; + continue; + } + break; + + case 'I': + my_getExpression (&imm_expr, s); + if (imm_expr.X_op != O_big + && imm_expr.X_op != O_constant) + insn_error = _("absolute expression required"); + s = expr_end; + continue; + + case 'A': + my_getExpression (&offset_expr, s); + imm_reloc = BFD_RELOC_32; + s = expr_end; + continue; + + case 'F': + case 'L': + case 'f': + case 'l': + { + int f64; + char *save_in; + char *err; + unsigned char temp[8]; + int len; + unsigned int length; + segT seg; + subsegT subseg; + char *p; + + /* These only appear as the last operand in an + instruction, and every instruction that accepts + them in any variant accepts them in all variants. + This means we don't have to worry about backing out + any changes if the instruction does not match. + + The difference between them is the size of the + floating point constant and where it goes. For 'F' + and 'L' the constant is 64 bits; for 'f' and 'l' it + is 32 bits. Where the constant is placed is based + on how the MIPS assembler does things: + F -- .rdata + L -- .lit8 + f -- immediate value + l -- .lit4 + + The .lit4 and .lit8 sections are only used if + permitted by the -G argument. + + When generating embedded PIC code, we use the + .lit8 section but not the .lit4 section (we can do + .lit4 inline easily; we need to put .lit8 + somewhere in the data segment, and using .lit8 + permits the linker to eventually combine identical + .lit8 entries). */ + + f64 = *args == 'F' || *args == 'L'; + + save_in = input_line_pointer; + input_line_pointer = s; + err = md_atof (f64 ? 'd' : 'f', (char *) temp, &len); + length = len; + s = input_line_pointer; + input_line_pointer = save_in; + if (err != NULL && *err != '\0') + { + as_bad (_("Bad floating point constant: %s"), err); + memset (temp, '\0', sizeof temp); + length = f64 ? 8 : 4; + } + + assert (length == (f64 ? 8 : 4)); + + if (*args == 'f' + || (*args == 'l' + && (! USE_GLOBAL_POINTER_OPT + || mips_pic == EMBEDDED_PIC + || g_switch_value < 4 + || (temp[0] == 0 && temp[1] == 0) + || (temp[2] == 0 && temp[3] == 0)))) + { + imm_expr.X_op = O_constant; + if (! target_big_endian) + imm_expr.X_add_number = bfd_getl32 (temp); + else + imm_expr.X_add_number = bfd_getb32 (temp); + } + else if (length > 4 + && ((temp[0] == 0 && temp[1] == 0) + || (temp[2] == 0 && temp[3] == 0)) + && ((temp[4] == 0 && temp[5] == 0) + || (temp[6] == 0 && temp[7] == 0))) + { + /* The value is simple enough to load with a + couple of instructions. In mips1 mode, set + imm_expr to the high order 32 bits and + offset_expr to the low order 32 bits. + Otherwise, set imm_expr to the entire 64 bit + constant. */ + if (! ISA_HAS_64BIT_REGS (mips_opts.isa)) + { + imm_expr.X_op = O_constant; + offset_expr.X_op = O_constant; + if (! target_big_endian) + { + imm_expr.X_add_number = bfd_getl32 (temp + 4); + offset_expr.X_add_number = bfd_getl32 (temp); + } + else + { + imm_expr.X_add_number = bfd_getb32 (temp); + offset_expr.X_add_number = bfd_getb32 (temp + 4); + } + if (offset_expr.X_add_number == 0) + offset_expr.X_op = O_absent; + } + else if (sizeof (imm_expr.X_add_number) > 4) + { + imm_expr.X_op = O_constant; + if (! target_big_endian) + imm_expr.X_add_number = bfd_getl64 (temp); + else + imm_expr.X_add_number = bfd_getb64 (temp); + } + else + { + imm_expr.X_op = O_big; + imm_expr.X_add_number = 4; + if (! target_big_endian) + { + generic_bignum[0] = bfd_getl16 (temp); + generic_bignum[1] = bfd_getl16 (temp + 2); + generic_bignum[2] = bfd_getl16 (temp + 4); + generic_bignum[3] = bfd_getl16 (temp + 6); + } + else + { + generic_bignum[0] = bfd_getb16 (temp + 6); + generic_bignum[1] = bfd_getb16 (temp + 4); + generic_bignum[2] = bfd_getb16 (temp + 2); + generic_bignum[3] = bfd_getb16 (temp); + } + } + } + else + { + const char *newname; + segT new_seg; + + /* Switch to the right section. */ + seg = now_seg; + subseg = now_subseg; + switch (*args) + { + default: /* unused default case avoids warnings. */ + case 'L': + newname = RDATA_SECTION_NAME; + if ((USE_GLOBAL_POINTER_OPT && g_switch_value >= 8) + || mips_pic == EMBEDDED_PIC) + newname = ".lit8"; + break; + case 'F': + if (mips_pic == EMBEDDED_PIC) + newname = ".lit8"; + else + newname = RDATA_SECTION_NAME; + break; + case 'l': + assert (!USE_GLOBAL_POINTER_OPT + || g_switch_value >= 4); + newname = ".lit4"; + break; + } + new_seg = subseg_new (newname, (subsegT) 0); + if (OUTPUT_FLAVOR == bfd_target_elf_flavour) + bfd_set_section_flags (stdoutput, new_seg, + (SEC_ALLOC + | SEC_LOAD + | SEC_READONLY + | SEC_DATA)); + frag_align (*args == 'l' ? 2 : 3, 0, 0); + if (OUTPUT_FLAVOR == bfd_target_elf_flavour + && strcmp (TARGET_OS, "elf") != 0) + record_alignment (new_seg, 4); + else + record_alignment (new_seg, *args == 'l' ? 2 : 3); + if (seg == now_seg) + as_bad (_("Can't use floating point insn in this section")); + + /* Set the argument to the current address in the + section. */ + offset_expr.X_op = O_symbol; + offset_expr.X_add_symbol = + symbol_new ("L0\001", now_seg, + (valueT) frag_now_fix (), frag_now); + offset_expr.X_add_number = 0; + + /* Put the floating point number into the section. */ + p = frag_more ((int) length); + memcpy (p, temp, length); + + /* Switch back to the original section. */ + subseg_set (seg, subseg); + } + } + continue; + + case 'i': /* 16 bit unsigned immediate */ + case 'j': /* 16 bit signed immediate */ + imm_reloc = BFD_RELOC_LO16; + c = my_getSmallExpression (&imm_expr, s); + if (c != '\0') + { + if (c != 'l') + { + if (imm_expr.X_op == O_constant) + imm_expr.X_add_number = + (imm_expr.X_add_number >> 16) & 0xffff; + else if (c == 'h') + { + imm_reloc = BFD_RELOC_HI16_S; + imm_unmatched_hi = true; + } + else + imm_reloc = BFD_RELOC_HI16; + } + else if (imm_expr.X_op == O_constant) + imm_expr.X_add_number &= 0xffff; + } + if (*args == 'i') + { + if ((c == '\0' && imm_expr.X_op != O_constant) + || ((imm_expr.X_add_number < 0 + || imm_expr.X_add_number >= 0x10000) + && imm_expr.X_op == O_constant)) + { + if (insn + 1 < &mips_opcodes[NUMOPCODES] && + !strcmp (insn->name, insn[1].name)) + break; + if (imm_expr.X_op != O_constant + && imm_expr.X_op != O_big) + insn_error = _("absolute expression required"); + else + as_bad (_("16 bit expression not in range 0..65535")); + } + } + else + { + int more; + offsetT max; + + /* The upper bound should be 0x8000, but + unfortunately the MIPS assembler accepts numbers + from 0x8000 to 0xffff and sign extends them, and + we want to be compatible. We only permit this + extended range for an instruction which does not + provide any further alternates, since those + alternates may handle other cases. People should + use the numbers they mean, rather than relying on + a mysterious sign extension. */ + more = (insn + 1 < &mips_opcodes[NUMOPCODES] && + strcmp (insn->name, insn[1].name) == 0); + if (more) + max = 0x8000; + else + max = 0x10000; + if ((c == '\0' && imm_expr.X_op != O_constant) + || ((imm_expr.X_add_number < -0x8000 + || imm_expr.X_add_number >= max) + && imm_expr.X_op == O_constant) + || (more + && imm_expr.X_add_number < 0 + && ISA_HAS_64BIT_REGS (mips_opts.isa) + && imm_expr.X_unsigned + && sizeof (imm_expr.X_add_number) <= 4)) + { + if (more) + break; + if (imm_expr.X_op != O_constant + && imm_expr.X_op != O_big) + insn_error = _("absolute expression required"); + else + as_bad (_("16 bit expression not in range -32768..32767")); + } + } + s = expr_end; + continue; + + case 'o': /* 16 bit offset */ + c = my_getSmallExpression (&offset_expr, s); + + /* If this value won't fit into a 16 bit offset, then go + find a macro that will generate the 32 bit offset + code pattern. As a special hack, we accept the + difference of two local symbols as a constant. This + is required to suppose embedded PIC switches, which + use an instruction which looks like + lw $4,$L12-$LS12($4) + The problem with handling this in a more general + fashion is that the macro function doesn't expect to + see anything which can be handled in a single + constant instruction. */ + if (c == 0 + && (offset_expr.X_op != O_constant + || offset_expr.X_add_number >= 0x8000 + || offset_expr.X_add_number < -0x8000) + && (mips_pic != EMBEDDED_PIC + || offset_expr.X_op != O_subtract + || (S_GET_SEGMENT (offset_expr.X_op_symbol) + != now_seg))) + break; + + if (c == 'h' || c == 'H') + { + if (offset_expr.X_op != O_constant) + break; + offset_expr.X_add_number = + (offset_expr.X_add_number >> 16) & 0xffff; + } + offset_reloc = BFD_RELOC_LO16; + s = expr_end; + continue; + + case 'p': /* pc relative offset */ + offset_reloc = BFD_RELOC_16_PCREL_S2; + my_getExpression (&offset_expr, s); + s = expr_end; + continue; + + case 'u': /* upper 16 bits */ + c = my_getSmallExpression (&imm_expr, s); + imm_reloc = BFD_RELOC_LO16; + if (c) + { + if (c != 'l') + { + if (imm_expr.X_op == O_constant) + imm_expr.X_add_number = + (imm_expr.X_add_number >> 16) & 0xffff; + else if (c == 'h') + { + imm_reloc = BFD_RELOC_HI16_S; + imm_unmatched_hi = true; + } + else + imm_reloc = BFD_RELOC_HI16; + } + else if (imm_expr.X_op == O_constant) + imm_expr.X_add_number &= 0xffff; + } + if (imm_expr.X_op == O_constant + && (imm_expr.X_add_number < 0 + || imm_expr.X_add_number >= 0x10000)) + as_bad (_("lui expression not in range 0..65535")); + s = expr_end; + continue; + + case 'a': /* 26 bit address */ + my_getExpression (&offset_expr, s); + s = expr_end; + offset_reloc = BFD_RELOC_MIPS_JMP; + continue; + + case 'N': /* 3 bit branch condition code */ + case 'M': /* 3 bit compare condition code */ + if (strncmp (s, "$fcc", 4) != 0) + break; + s += 4; + regno = 0; + do + { + regno *= 10; + regno += *s - '0'; + ++s; + } + while (isdigit ((unsigned char) *s)); + if (regno > 7) + as_bad (_("invalid condition code register $fcc%d"), regno); + if (*args == 'N') + ip->insn_opcode |= regno << OP_SH_BCC; + else + ip->insn_opcode |= regno << OP_SH_CCC; + continue; + + default: + as_bad (_("bad char = '%c'\n"), *args); + internalError (); + } + break; + } + /* Args don't match. */ + if (insn + 1 < &mips_opcodes[NUMOPCODES] && + !strcmp (insn->name, insn[1].name)) + { + ++insn; + s = argsStart; + continue; + } + insn_error = _("illegal operands"); + return; + } +} + +/* This routine assembles an instruction into its binary format when + assembling for the mips16. As a side effect, it sets one of the + global variables imm_reloc or offset_reloc to the type of + relocation to do if one of the operands is an address expression. + It also sets mips16_small and mips16_ext if the user explicitly + requested a small or extended instruction. */ + +static void +mips16_ip (str, ip) + char *str; + struct mips_cl_insn *ip; +{ + char *s; + const char *args; + struct mips_opcode *insn; + char *argsstart; + unsigned int regno; + unsigned int lastregno = 0; + char *s_reset; + + insn_error = NULL; + + mips16_small = false; + mips16_ext = false; + + for (s = str; islower ((unsigned char) *s); ++s) + ; + switch (*s) + { + case '\0': + break; + + case ' ': + *s++ = '\0'; + break; + + case '.': + if (s[1] == 't' && s[2] == ' ') + { + *s = '\0'; + mips16_small = true; + s += 3; + break; + } + else if (s[1] == 'e' && s[2] == ' ') + { + *s = '\0'; + mips16_ext = true; + s += 3; + break; + } + /* Fall through. */ + default: + insn_error = _("unknown opcode"); + return; + } + + if (mips_opts.noautoextend && ! mips16_ext) + mips16_small = true; + + if ((insn = (struct mips_opcode *) hash_find (mips16_op_hash, str)) == NULL) + { + insn_error = _("unrecognized opcode"); + return; + } + + argsstart = s; + for (;;) + { + assert (strcmp (insn->name, str) == 0); + + ip->insn_mo = insn; + ip->insn_opcode = insn->match; + ip->use_extend = false; + imm_expr.X_op = O_absent; + imm_reloc = BFD_RELOC_UNUSED; + offset_expr.X_op = O_absent; + offset_reloc = BFD_RELOC_UNUSED; + for (args = insn->args; 1; ++args) + { + int c; + + if (*s == ' ') + ++s; + + /* In this switch statement we call break if we did not find + a match, continue if we did find a match, or return if we + are done. */ + + c = *args; + switch (c) + { + case '\0': + if (*s == '\0') + { + /* Stuff the immediate value in now, if we can. */ + if (imm_expr.X_op == O_constant + && imm_reloc > BFD_RELOC_UNUSED + && insn->pinfo != INSN_MACRO) + { + mips16_immed ((char *) NULL, 0, + imm_reloc - BFD_RELOC_UNUSED, + imm_expr.X_add_number, true, mips16_small, + mips16_ext, &ip->insn_opcode, + &ip->use_extend, &ip->extend); + imm_expr.X_op = O_absent; + imm_reloc = BFD_RELOC_UNUSED; + } + + return; + } + break; + + case ',': + if (*s++ == c) + continue; + s--; + switch (*++args) + { + case 'v': + ip->insn_opcode |= lastregno << MIPS16OP_SH_RX; + continue; + case 'w': + ip->insn_opcode |= lastregno << MIPS16OP_SH_RY; + continue; + } + break; + + case '(': + case ')': + if (*s++ == c) + continue; + break; + + case 'v': + case 'w': + if (s[0] != '$') + { + if (c == 'v') + ip->insn_opcode |= lastregno << MIPS16OP_SH_RX; + else + ip->insn_opcode |= lastregno << MIPS16OP_SH_RY; + ++args; + continue; + } + /* Fall through. */ + case 'x': + case 'y': + case 'z': + case 'Z': + case '0': + case 'S': + case 'R': + case 'X': + case 'Y': + if (s[0] != '$') + break; + s_reset = s; + if (isdigit ((unsigned char) s[1])) + { + ++s; + regno = 0; + do + { + regno *= 10; + regno += *s - '0'; + ++s; + } + while (isdigit ((unsigned char) *s)); + if (regno > 31) + { + as_bad (_("invalid register number (%d)"), regno); + regno = 2; + } + } + else + { + if (s[1] == 'f' && s[2] == 'p') + { + s += 3; + regno = FP; + } + else if (s[1] == 's' && s[2] == 'p') + { + s += 3; + regno = SP; + } + else if (s[1] == 'g' && s[2] == 'p') + { + s += 3; + regno = GP; + } + else if (s[1] == 'a' && s[2] == 't') + { + s += 3; + regno = AT; + } + else if (s[1] == 'k' && s[2] == 't' && s[3] == '0') + { + s += 4; + regno = KT0; + } + else if (s[1] == 'k' && s[2] == 't' && s[3] == '1') + { + s += 4; + regno = KT1; + } + else + break; + } + + if (*s == ' ') + ++s; + if (args[1] != *s) + { + if (c == 'v' || c == 'w') + { + regno = mips16_to_32_reg_map[lastregno]; + s = s_reset; + args++; + } + } + + switch (c) + { + case 'x': + case 'y': + case 'z': + case 'v': + case 'w': + case 'Z': + regno = mips32_to_16_reg_map[regno]; + break; + + case '0': + if (regno != 0) + regno = ILLEGAL_REG; + break; + + case 'S': + if (regno != SP) + regno = ILLEGAL_REG; + break; + + case 'R': + if (regno != RA) + regno = ILLEGAL_REG; + break; + + case 'X': + case 'Y': + if (regno == AT && ! mips_opts.noat) + as_warn (_("used $at without \".set noat\"")); + break; + + default: + internalError (); + } + + if (regno == ILLEGAL_REG) + break; + + switch (c) + { + case 'x': + case 'v': + ip->insn_opcode |= regno << MIPS16OP_SH_RX; + break; + case 'y': + case 'w': + ip->insn_opcode |= regno << MIPS16OP_SH_RY; + break; + case 'z': + ip->insn_opcode |= regno << MIPS16OP_SH_RZ; + break; + case 'Z': + ip->insn_opcode |= regno << MIPS16OP_SH_MOVE32Z; + case '0': + case 'S': + case 'R': + break; + case 'X': + ip->insn_opcode |= regno << MIPS16OP_SH_REGR32; + break; + case 'Y': + regno = ((regno & 7) << 2) | ((regno & 0x18) >> 3); + ip->insn_opcode |= regno << MIPS16OP_SH_REG32R; + break; + default: + internalError (); + } + + lastregno = regno; + continue; + + case 'P': + if (strncmp (s, "$pc", 3) == 0) + { + s += 3; + continue; + } + break; + + case '<': + case '>': + case '[': + case ']': + case '4': + case '5': + case 'H': + case 'W': + case 'D': + case 'j': + case '8': + case 'V': + case 'C': + case 'U': + case 'k': + case 'K': + if (s[0] == '%' + && strncmp (s + 1, "gprel(", sizeof "gprel(" - 1) == 0) + { + /* This is %gprel(SYMBOL). We need to read SYMBOL, + and generate the appropriate reloc. If the text + inside %gprel is not a symbol name with an + optional offset, then we generate a normal reloc + and will probably fail later. */ + my_getExpression (&imm_expr, s + sizeof "%gprel" - 1); + if (imm_expr.X_op == O_symbol) + { + mips16_ext = true; + imm_reloc = BFD_RELOC_MIPS16_GPREL; + s = expr_end; + ip->use_extend = true; + ip->extend = 0; + continue; + } + } + else + { + /* Just pick up a normal expression. */ + my_getExpression (&imm_expr, s); + } + + if (imm_expr.X_op == O_register) + { + /* What we thought was an expression turned out to + be a register. */ + + if (s[0] == '(' && args[1] == '(') + { + /* It looks like the expression was omitted + before a register indirection, which means + that the expression is implicitly zero. We + still set up imm_expr, so that we handle + explicit extensions correctly. */ + imm_expr.X_op = O_constant; + imm_expr.X_add_number = 0; + imm_reloc = (int) BFD_RELOC_UNUSED + c; + continue; + } + + break; + } + + /* We need to relax this instruction. */ + imm_reloc = (int) BFD_RELOC_UNUSED + c; + s = expr_end; + continue; + + case 'p': + case 'q': + case 'A': + case 'B': + case 'E': + /* We use offset_reloc rather than imm_reloc for the PC + relative operands. This lets macros with both + immediate and address operands work correctly. */ + my_getExpression (&offset_expr, s); + + if (offset_expr.X_op == O_register) + break; + + /* We need to relax this instruction. */ + offset_reloc = (int) BFD_RELOC_UNUSED + c; + s = expr_end; + continue; + + case '6': /* break code */ + my_getExpression (&imm_expr, s); + check_absolute_expr (ip, &imm_expr); + if ((unsigned long) imm_expr.X_add_number > 63) + { + as_warn (_("Invalid value for `%s' (%lu)"), + ip->insn_mo->name, + (unsigned long) imm_expr.X_add_number); + imm_expr.X_add_number &= 0x3f; + } + ip->insn_opcode |= imm_expr.X_add_number << MIPS16OP_SH_IMM6; + imm_expr.X_op = O_absent; + s = expr_end; + continue; + + case 'a': /* 26 bit address */ + my_getExpression (&offset_expr, s); + s = expr_end; + offset_reloc = BFD_RELOC_MIPS16_JMP; + ip->insn_opcode <<= 16; + continue; + + case 'l': /* register list for entry macro */ + case 'L': /* register list for exit macro */ + { + int mask; + + if (c == 'l') + mask = 0; + else + mask = 7 << 3; + while (*s != '\0') + { + int freg, reg1, reg2; + + while (*s == ' ' || *s == ',') + ++s; + if (*s != '$') + { + as_bad (_("can't parse register list")); + break; + } + ++s; + if (*s != 'f') + freg = 0; + else + { + freg = 1; + ++s; + } + reg1 = 0; + while (isdigit ((unsigned char) *s)) + { + reg1 *= 10; + reg1 += *s - '0'; + ++s; + } + if (*s == ' ') + ++s; + if (*s != '-') + reg2 = reg1; + else + { + ++s; + if (*s != '$') + break; + ++s; + if (freg) + { + if (*s == 'f') + ++s; + else + { + as_bad (_("invalid register list")); + break; + } + } + reg2 = 0; + while (isdigit ((unsigned char) *s)) + { + reg2 *= 10; + reg2 += *s - '0'; + ++s; + } + } + if (freg && reg1 == 0 && reg2 == 0 && c == 'L') + { + mask &= ~ (7 << 3); + mask |= 5 << 3; + } + else if (freg && reg1 == 0 && reg2 == 1 && c == 'L') + { + mask &= ~ (7 << 3); + mask |= 6 << 3; + } + else if (reg1 == 4 && reg2 >= 4 && reg2 <= 7 && c != 'L') + mask |= (reg2 - 3) << 3; + else if (reg1 == 16 && reg2 >= 16 && reg2 <= 17) + mask |= (reg2 - 15) << 1; + else if (reg1 == 31 && reg2 == 31) + mask |= 1; + else + { + as_bad (_("invalid register list")); + break; + } + } + /* The mask is filled in in the opcode table for the + benefit of the disassembler. We remove it before + applying the actual mask. */ + ip->insn_opcode &= ~ ((7 << 3) << MIPS16OP_SH_IMM6); + ip->insn_opcode |= mask << MIPS16OP_SH_IMM6; + } + continue; + + case 'e': /* extend code */ + my_getExpression (&imm_expr, s); + check_absolute_expr (ip, &imm_expr); + if ((unsigned long) imm_expr.X_add_number > 0x7ff) + { + as_warn (_("Invalid value for `%s' (%lu)"), + ip->insn_mo->name, + (unsigned long) imm_expr.X_add_number); + imm_expr.X_add_number &= 0x7ff; + } + ip->insn_opcode |= imm_expr.X_add_number; + imm_expr.X_op = O_absent; + s = expr_end; + continue; + + default: + internalError (); + } + break; + } + + /* Args don't match. */ + if (insn + 1 < &mips16_opcodes[bfd_mips16_num_opcodes] && + strcmp (insn->name, insn[1].name) == 0) + { + ++insn; + s = argsstart; + continue; + } + + insn_error = _("illegal operands"); + + return; + } +} + +/* This structure holds information we know about a mips16 immediate + argument type. */ + +struct mips16_immed_operand +{ + /* The type code used in the argument string in the opcode table. */ + int type; + /* The number of bits in the short form of the opcode. */ + int nbits; + /* The number of bits in the extended form of the opcode. */ + int extbits; + /* The amount by which the short form is shifted when it is used; + for example, the sw instruction has a shift count of 2. */ + int shift; + /* The amount by which the short form is shifted when it is stored + into the instruction code. */ + int op_shift; + /* Non-zero if the short form is unsigned. */ + int unsp; + /* Non-zero if the extended form is unsigned. */ + int extu; + /* Non-zero if the value is PC relative. */ + int pcrel; +}; + +/* The mips16 immediate operand types. */ + +static const struct mips16_immed_operand mips16_immed_operands[] = +{ + { '<', 3, 5, 0, MIPS16OP_SH_RZ, 1, 1, 0 }, + { '>', 3, 5, 0, MIPS16OP_SH_RX, 1, 1, 0 }, + { '[', 3, 6, 0, MIPS16OP_SH_RZ, 1, 1, 0 }, + { ']', 3, 6, 0, MIPS16OP_SH_RX, 1, 1, 0 }, + { '4', 4, 15, 0, MIPS16OP_SH_IMM4, 0, 0, 0 }, + { '5', 5, 16, 0, MIPS16OP_SH_IMM5, 1, 0, 0 }, + { 'H', 5, 16, 1, MIPS16OP_SH_IMM5, 1, 0, 0 }, + { 'W', 5, 16, 2, MIPS16OP_SH_IMM5, 1, 0, 0 }, + { 'D', 5, 16, 3, MIPS16OP_SH_IMM5, 1, 0, 0 }, + { 'j', 5, 16, 0, MIPS16OP_SH_IMM5, 0, 0, 0 }, + { '8', 8, 16, 0, MIPS16OP_SH_IMM8, 1, 0, 0 }, + { 'V', 8, 16, 2, MIPS16OP_SH_IMM8, 1, 0, 0 }, + { 'C', 8, 16, 3, MIPS16OP_SH_IMM8, 1, 0, 0 }, + { 'U', 8, 16, 0, MIPS16OP_SH_IMM8, 1, 1, 0 }, + { 'k', 8, 16, 0, MIPS16OP_SH_IMM8, 0, 0, 0 }, + { 'K', 8, 16, 3, MIPS16OP_SH_IMM8, 0, 0, 0 }, + { 'p', 8, 16, 0, MIPS16OP_SH_IMM8, 0, 0, 1 }, + { 'q', 11, 16, 0, MIPS16OP_SH_IMM8, 0, 0, 1 }, + { 'A', 8, 16, 2, MIPS16OP_SH_IMM8, 1, 0, 1 }, + { 'B', 5, 16, 3, MIPS16OP_SH_IMM5, 1, 0, 1 }, + { 'E', 5, 16, 2, MIPS16OP_SH_IMM5, 1, 0, 1 } +}; + +#define MIPS16_NUM_IMMED \ + (sizeof mips16_immed_operands / sizeof mips16_immed_operands[0]) + +/* Handle a mips16 instruction with an immediate value. This or's the + small immediate value into *INSN. It sets *USE_EXTEND to indicate + whether an extended value is needed; if one is needed, it sets + *EXTEND to the value. The argument type is TYPE. The value is VAL. + If SMALL is true, an unextended opcode was explicitly requested. + If EXT is true, an extended opcode was explicitly requested. If + WARN is true, warn if EXT does not match reality. */ + +static void +mips16_immed (file, line, type, val, warn, small, ext, insn, use_extend, + extend) + char *file; + unsigned int line; + int type; + offsetT val; + boolean warn; + boolean small; + boolean ext; + unsigned long *insn; + boolean *use_extend; + unsigned short *extend; +{ + register const struct mips16_immed_operand *op; + int mintiny, maxtiny; + boolean needext; + + op = mips16_immed_operands; + while (op->type != type) + { + ++op; + assert (op < mips16_immed_operands + MIPS16_NUM_IMMED); + } + + if (op->unsp) + { + if (type == '<' || type == '>' || type == '[' || type == ']') + { + mintiny = 1; + maxtiny = 1 << op->nbits; + } + else + { + mintiny = 0; + maxtiny = (1 << op->nbits) - 1; + } + } + else + { + mintiny = - (1 << (op->nbits - 1)); + maxtiny = (1 << (op->nbits - 1)) - 1; + } + + /* Branch offsets have an implicit 0 in the lowest bit. */ + if (type == 'p' || type == 'q') + val /= 2; + + if ((val & ((1 << op->shift) - 1)) != 0 + || val < (mintiny << op->shift) + || val > (maxtiny << op->shift)) + needext = true; + else + needext = false; + + if (warn && ext && ! needext) + as_warn_where (file, line, _("extended operand requested but not required")); + if (small && needext) + as_bad_where (file, line, _("invalid unextended operand value")); + + if (small || (! ext && ! needext)) + { + int insnval; + + *use_extend = false; + insnval = ((val >> op->shift) & ((1 << op->nbits) - 1)); + insnval <<= op->op_shift; + *insn |= insnval; + } + else + { + long minext, maxext; + int extval; + + if (op->extu) + { + minext = 0; + maxext = (1 << op->extbits) - 1; + } + else + { + minext = - (1 << (op->extbits - 1)); + maxext = (1 << (op->extbits - 1)) - 1; + } + if (val < minext || val > maxext) + as_bad_where (file, line, + _("operand value out of range for instruction")); + + *use_extend = true; + if (op->extbits == 16) + { + extval = ((val >> 11) & 0x1f) | (val & 0x7e0); + val &= 0x1f; + } + else if (op->extbits == 15) + { + extval = ((val >> 11) & 0xf) | (val & 0x7f0); + val &= 0xf; + } + else + { + extval = ((val & 0x1f) << 6) | (val & 0x20); + val = 0; + } + + *extend = (unsigned short) extval; + *insn |= val; + } +} + +#define LP '(' +#define RP ')' + +static int +my_getSmallExpression (ep, str) + expressionS *ep; + char *str; +{ + char *sp; + int c = 0; + + if (*str == ' ') + str++; + if (*str == LP + || (*str == '%' && + ((str[1] == 'h' && str[2] == 'i') + || (str[1] == 'H' && str[2] == 'I') + || (str[1] == 'l' && str[2] == 'o')) + && str[3] == LP)) + { + if (*str == LP) + c = 0; + else + { + c = str[1]; + str += 3; + } + + /* + * A small expression may be followed by a base register. + * Scan to the end of this operand, and then back over a possible + * base register. Then scan the small expression up to that + * point. (Based on code in sparc.c...) + */ + for (sp = str; *sp && *sp != ','; sp++) + ; + if (sp - 4 >= str && sp[-1] == RP) + { + if (isdigit ((unsigned char) sp[-2])) + { + for (sp -= 3; sp >= str && isdigit ((unsigned char) *sp); sp--) + ; + if (*sp == '$' && sp > str && sp[-1] == LP) + { + sp--; + goto do_it; + } + } + else if (sp - 5 >= str + && sp[-5] == LP + && sp[-4] == '$' + && ((sp[-3] == 'f' && sp[-2] == 'p') + || (sp[-3] == 's' && sp[-2] == 'p') + || (sp[-3] == 'g' && sp[-2] == 'p') + || (sp[-3] == 'a' && sp[-2] == 't'))) + { + sp -= 5; + do_it: + if (sp == str) + { + /* no expression means zero offset */ + if (c) + { + /* %xx(reg) is an error */ + ep->X_op = O_absent; + expr_end = str - 3; + } + else + { + ep->X_op = O_constant; + expr_end = sp; + } + ep->X_add_symbol = NULL; + ep->X_op_symbol = NULL; + ep->X_add_number = 0; + } + else + { + *sp = '\0'; + my_getExpression (ep, str); + *sp = LP; + } + return c; + } + } + } + my_getExpression (ep, str); + return c; /* => %hi or %lo encountered */ +} + +static void +my_getExpression (ep, str) + expressionS *ep; + char *str; +{ + char *save_in; + + save_in = input_line_pointer; + input_line_pointer = str; + expression (ep); + expr_end = input_line_pointer; + input_line_pointer = save_in; + + /* If we are in mips16 mode, and this is an expression based on `.', + then we bump the value of the symbol by 1 since that is how other + text symbols are handled. We don't bother to handle complex + expressions, just `.' plus or minus a constant. */ + if (mips_opts.mips16 + && ep->X_op == O_symbol + && strcmp (S_GET_NAME (ep->X_add_symbol), FAKE_LABEL_NAME) == 0 + && S_GET_SEGMENT (ep->X_add_symbol) == now_seg + && symbol_get_frag (ep->X_add_symbol) == frag_now + && symbol_constant_p (ep->X_add_symbol) + && S_GET_VALUE (ep->X_add_symbol) == frag_now_fix ()) + S_SET_VALUE (ep->X_add_symbol, S_GET_VALUE (ep->X_add_symbol) + 1); +} + +/* Turn a string in input_line_pointer into a floating point constant + of type type, and store the appropriate bytes in *litP. The number + of LITTLENUMS emitted is stored in *sizeP . An error message is + returned, or NULL on OK. */ + +char * +md_atof (type, litP, sizeP) + int type; + char *litP; + int *sizeP; +{ + int prec; + LITTLENUM_TYPE words[4]; + char *t; + int i; + + switch (type) + { + case 'f': + prec = 2; + break; + + case 'd': + prec = 4; + break; + + default: + *sizeP = 0; + return _("bad call to md_atof"); + } + + t = atof_ieee (input_line_pointer, type, words); + if (t) + input_line_pointer = t; + + *sizeP = prec * 2; + + if (! target_big_endian) + { + for (i = prec - 1; i >= 0; i--) + { + md_number_to_chars (litP, (valueT) words[i], 2); + litP += 2; + } + } + else + { + for (i = 0; i < prec; i++) + { + md_number_to_chars (litP, (valueT) words[i], 2); + litP += 2; + } + } + + return NULL; +} + +void +md_number_to_chars (buf, val, n) + char *buf; + valueT val; + int n; +{ + if (target_big_endian) + number_to_chars_bigendian (buf, val, n); + else + number_to_chars_littleendian (buf, val, n); +} + +CONST char *md_shortopts = "O::g::G:"; + +struct option md_longopts[] = { +#define OPTION_MIPS1 (OPTION_MD_BASE + 1) + {"mips0", no_argument, NULL, OPTION_MIPS1}, + {"mips1", no_argument, NULL, OPTION_MIPS1}, +#define OPTION_MIPS2 (OPTION_MD_BASE + 2) + {"mips2", no_argument, NULL, OPTION_MIPS2}, +#define OPTION_MIPS3 (OPTION_MD_BASE + 3) + {"mips3", no_argument, NULL, OPTION_MIPS3}, +#define OPTION_MIPS4 (OPTION_MD_BASE + 4) + {"mips4", no_argument, NULL, OPTION_MIPS4}, +#define OPTION_MCPU (OPTION_MD_BASE + 5) + {"mcpu", required_argument, NULL, OPTION_MCPU}, +#define OPTION_MEMBEDDED_PIC (OPTION_MD_BASE + 6) + {"membedded-pic", no_argument, NULL, OPTION_MEMBEDDED_PIC}, +#define OPTION_TRAP (OPTION_MD_BASE + 9) + {"trap", no_argument, NULL, OPTION_TRAP}, + {"no-break", no_argument, NULL, OPTION_TRAP}, +#define OPTION_BREAK (OPTION_MD_BASE + 10) + {"break", no_argument, NULL, OPTION_BREAK}, + {"no-trap", no_argument, NULL, OPTION_BREAK}, +#define OPTION_EB (OPTION_MD_BASE + 11) + {"EB", no_argument, NULL, OPTION_EB}, +#define OPTION_EL (OPTION_MD_BASE + 12) + {"EL", no_argument, NULL, OPTION_EL}, +#define OPTION_M4650 (OPTION_MD_BASE + 13) + {"m4650", no_argument, NULL, OPTION_M4650}, +#define OPTION_NO_M4650 (OPTION_MD_BASE + 14) + {"no-m4650", no_argument, NULL, OPTION_NO_M4650}, +#define OPTION_M4010 (OPTION_MD_BASE + 15) + {"m4010", no_argument, NULL, OPTION_M4010}, +#define OPTION_NO_M4010 (OPTION_MD_BASE + 16) + {"no-m4010", no_argument, NULL, OPTION_NO_M4010}, +#define OPTION_M4100 (OPTION_MD_BASE + 17) + {"m4100", no_argument, NULL, OPTION_M4100}, +#define OPTION_NO_M4100 (OPTION_MD_BASE + 18) + {"no-m4100", no_argument, NULL, OPTION_NO_M4100}, +#define OPTION_MIPS16 (OPTION_MD_BASE + 22) + {"mips16", no_argument, NULL, OPTION_MIPS16}, +#define OPTION_NO_MIPS16 (OPTION_MD_BASE + 23) + {"no-mips16", no_argument, NULL, OPTION_NO_MIPS16}, +#define OPTION_M3900 (OPTION_MD_BASE + 26) + {"m3900", no_argument, NULL, OPTION_M3900}, +#define OPTION_NO_M3900 (OPTION_MD_BASE + 27) + {"no-m3900", no_argument, NULL, OPTION_NO_M3900}, + + +#define OPTION_MABI (OPTION_MD_BASE + 38) + {"mabi", required_argument, NULL, OPTION_MABI}, + +#define OPTION_M7000_HILO_FIX (OPTION_MD_BASE + 39) + {"mfix7000", no_argument, NULL, OPTION_M7000_HILO_FIX}, +#define OPTION_NO_M7000_HILO_FIX (OPTION_MD_BASE + 40) + {"no-fix-7000", no_argument, NULL, OPTION_NO_M7000_HILO_FIX}, + +#define OPTION_CALL_SHARED (OPTION_MD_BASE + 7) +#define OPTION_NON_SHARED (OPTION_MD_BASE + 8) +#define OPTION_XGOT (OPTION_MD_BASE + 19) +#define OPTION_32 (OPTION_MD_BASE + 20) +#define OPTION_64 (OPTION_MD_BASE + 21) +#ifdef OBJ_ELF + {"KPIC", no_argument, NULL, OPTION_CALL_SHARED}, + {"xgot", no_argument, NULL, OPTION_XGOT}, + {"call_shared", no_argument, NULL, OPTION_CALL_SHARED}, + {"non_shared", no_argument, NULL, OPTION_NON_SHARED}, + {"32", no_argument, NULL, OPTION_32}, + {"64", no_argument, NULL, OPTION_64}, +#endif + +#define OPTION_GP32 (OPTION_MD_BASE + 41) +#define OPTION_GP64 (OPTION_MD_BASE + 42) + {"mgp32", no_argument, NULL, OPTION_GP32}, + {"mgp64", no_argument, NULL, OPTION_GP64}, + + {NULL, no_argument, NULL, 0} +}; +size_t md_longopts_size = sizeof(md_longopts); + +int +md_parse_option (c, arg) + int c; + char *arg; +{ + switch (c) + { + case OPTION_TRAP: + mips_trap = 1; + break; + + case OPTION_BREAK: + mips_trap = 0; + break; + + case OPTION_EB: + target_big_endian = 1; + break; + + case OPTION_EL: + target_big_endian = 0; + break; + + case 'O': + if (arg && arg[1] == '0') + mips_optimize = 1; + else + mips_optimize = 2; + break; + + case 'g': + if (arg == NULL) + mips_debug = 2; + else + mips_debug = atoi (arg); + /* When the MIPS assembler sees -g or -g2, it does not do + optimizations which limit full symbolic debugging. We take + that to be equivalent to -O0. */ + if (mips_debug == 2) + mips_optimize = 1; + break; + + case OPTION_MIPS1: + mips_opts.isa = 1; + break; + + case OPTION_MIPS2: + mips_opts.isa = 2; + break; + + case OPTION_MIPS3: + mips_opts.isa = 3; + break; + + case OPTION_MIPS4: + mips_opts.isa = 4; + break; + + case OPTION_MCPU: + { + char *p; + + /* Identify the processor type */ + p = arg; + if (strcmp (p, "default") == 0 + || strcmp (p, "DEFAULT") == 0) + mips_cpu = -1; + else + { + int sv = 0; + + /* We need to cope with the various "vr" prefixes for the 4300 + processor. */ + if (*p == 'v' || *p == 'V') + { + sv = 1; + p++; + } + + if (*p == 'r' || *p == 'R') + p++; + + mips_cpu = -1; + switch (*p) + { + case '1': + if (strcmp (p, "10000") == 0 + || strcmp (p, "10k") == 0 + || strcmp (p, "10K") == 0) + mips_cpu = 10000; + break; + + case '2': + if (strcmp (p, "2000") == 0 + || strcmp (p, "2k") == 0 + || strcmp (p, "2K") == 0) + mips_cpu = 2000; + break; + + case '3': + if (strcmp (p, "3000") == 0 + || strcmp (p, "3k") == 0 + || strcmp (p, "3K") == 0) + mips_cpu = 3000; + else if (strcmp (p, "3900") == 0) + mips_cpu = 3900; + break; + + case '4': + if (strcmp (p, "4000") == 0 + || strcmp (p, "4k") == 0 + || strcmp (p, "4K") == 0) + mips_cpu = 4000; + else if (strcmp (p, "4100") == 0) + mips_cpu = 4100; + else if (strcmp (p, "4111") == 0) + mips_cpu = 4111; + else if (strcmp (p, "4300") == 0) + mips_cpu = 4300; + else if (strcmp (p, "4400") == 0) + mips_cpu = 4400; + else if (strcmp (p, "4600") == 0) + mips_cpu = 4600; + else if (strcmp (p, "4650") == 0) + mips_cpu = 4650; + else if (strcmp (p, "4010") == 0) + mips_cpu = 4010; + break; + + case '5': + if (strcmp (p, "5000") == 0 + || strcmp (p, "5k") == 0 + || strcmp (p, "5K") == 0) + mips_cpu = 5000; + break; + + case '6': + if (strcmp (p, "6000") == 0 + || strcmp (p, "6k") == 0 + || strcmp (p, "6K") == 0) + mips_cpu = 6000; + break; + + case '8': + if (strcmp (p, "8000") == 0 + || strcmp (p, "8k") == 0 + || strcmp (p, "8K") == 0) + mips_cpu = 8000; + break; + + case 'o': + if (strcmp (p, "orion") == 0) + mips_cpu = 4600; + break; + } + + if (sv + && (mips_cpu != 4300 + && mips_cpu != 4100 + && mips_cpu != 4111 + && mips_cpu != 5000)) + { + as_bad (_("ignoring invalid leading 'v' in -mcpu=%s switch"), arg); + return 0; + } + + if (mips_cpu == -1) + { + as_bad (_("invalid architecture -mcpu=%s"), arg); + return 0; + } + } + } + break; + + case OPTION_M4650: + mips_cpu = 4650; + break; + + case OPTION_NO_M4650: + break; + + case OPTION_M4010: + mips_cpu = 4010; + break; + + case OPTION_NO_M4010: + break; + + case OPTION_M4100: + mips_cpu = 4100; + break; + + case OPTION_NO_M4100: + break; + + + case OPTION_M3900: + mips_cpu = 3900; + break; + + case OPTION_NO_M3900: + break; + + case OPTION_MIPS16: + mips_opts.mips16 = 1; + mips_no_prev_insn (false); + break; + + case OPTION_NO_MIPS16: + mips_opts.mips16 = 0; + mips_no_prev_insn (false); + break; + + case OPTION_MEMBEDDED_PIC: + mips_pic = EMBEDDED_PIC; + if (USE_GLOBAL_POINTER_OPT && g_switch_seen) + { + as_bad (_("-G may not be used with embedded PIC code")); + return 0; + } + g_switch_value = 0x7fffffff; + break; + + /* When generating ELF code, we permit -KPIC and -call_shared to + select SVR4_PIC, and -non_shared to select no PIC. This is + intended to be compatible with Irix 5. */ + case OPTION_CALL_SHARED: + if (OUTPUT_FLAVOR != bfd_target_elf_flavour) + { + as_bad (_("-call_shared is supported only for ELF format")); + return 0; + } + mips_pic = SVR4_PIC; + if (g_switch_seen && g_switch_value != 0) + { + as_bad (_("-G may not be used with SVR4 PIC code")); + return 0; + } + g_switch_value = 0; + break; + + case OPTION_NON_SHARED: + if (OUTPUT_FLAVOR != bfd_target_elf_flavour) + { + as_bad (_("-non_shared is supported only for ELF format")); + return 0; + } + mips_pic = NO_PIC; + break; + + /* The -xgot option tells the assembler to use 32 offsets when + accessing the got in SVR4_PIC mode. It is for Irix + compatibility. */ + case OPTION_XGOT: + mips_big_got = 1; + break; + + case 'G': + if (! USE_GLOBAL_POINTER_OPT) + { + as_bad (_("-G is not supported for this configuration")); + return 0; + } + else if (mips_pic == SVR4_PIC || mips_pic == EMBEDDED_PIC) + { + as_bad (_("-G may not be used with SVR4 or embedded PIC code")); + return 0; + } + else + g_switch_value = atoi (arg); + g_switch_seen = 1; + break; + + /* The -32 and -64 options tell the assembler to output the 32 + bit or the 64 bit MIPS ELF format. */ + case OPTION_32: + mips_64 = 0; + break; + + case OPTION_64: + { + const char **list, **l; + + list = bfd_target_list (); + for (l = list; *l != NULL; l++) + if (strcmp (*l, "elf64-bigmips") == 0 + || strcmp (*l, "elf64-littlemips") == 0) + break; + if (*l == NULL) + as_fatal (_("No compiled in support for 64 bit object file format")); + free (list); + mips_64 = 1; + } + break; + + case OPTION_GP32: + mips_gp32 = 1; + mips_64 = 0; + + /* We deliberately don't allow "-gp32" to set the MIPS_32BITMODE + flag in object files because to do so would make it + impossible to link with libraries compiled without "-gp32". + This is unnecessarily restrictive. + + We could solve this problem by adding "-gp32" multilibs to + gcc, but to set this flag before gcc is built with such + multilibs will break too many systems. */ + +/* mips_32bitmode = 1; */ + break; + + case OPTION_GP64: + mips_gp32 = 0; + mips_64 = 1; +/* mips_32bitmode = 0; */ + break; + + case OPTION_MABI: + if (strcmp (arg,"32") == 0 + || strcmp (arg,"n32") == 0 + || strcmp (arg,"64") == 0 + || strcmp (arg,"o64") == 0 + || strcmp (arg,"eabi") == 0) + mips_abi_string = arg; + break; + + case OPTION_M7000_HILO_FIX: + mips_7000_hilo_fix = true; + break; + + case OPTION_NO_M7000_HILO_FIX: + mips_7000_hilo_fix = false; + break; + + default: + return 0; + } + + return 1; +} + + +static void +show (stream, string, col_p, first_p) + FILE *stream; + char *string; + int *col_p; + int *first_p; +{ + if (*first_p) + { + fprintf (stream, "%24s", ""); + *col_p = 24; + } + else + { + fprintf (stream, ", "); + *col_p += 2; + } + + if (*col_p + strlen (string) > 72) + { + fprintf (stream, "\n%24s", ""); + *col_p = 24; + } + + fprintf (stream, "%s", string); + *col_p += strlen (string); + + *first_p = 0; +} + + +void +md_show_usage (stream) + FILE *stream; +{ + int column, first; + + fprintf(stream, _("\ +MIPS options:\n\ +-membedded-pic generate embedded position independent code\n\ +-EB generate big endian output\n\ +-EL generate little endian output\n\ +-g, -g2 do not remove uneeded NOPs or swap branches\n\ +-G NUM allow referencing objects up to NUM bytes\n\ + implicitly with the gp register [default 8]\n")); + fprintf(stream, _("\ +-mips1 generate MIPS ISA I instructions\n\ +-mips2 generate MIPS ISA II instructions\n\ +-mips3 generate MIPS ISA III instructions\n\ +-mips4 generate MIPS ISA IV instructions\n\ +-mcpu=CPU generate code for CPU, where CPU is one of:\n")); + + first = 1; + + show (stream, "2000", &column, &first); + show (stream, "3000", &column, &first); + show (stream, "3900", &column, &first); + show (stream, "4000", &column, &first); + show (stream, "4010", &column, &first); + show (stream, "4100", &column, &first); + show (stream, "4111", &column, &first); + show (stream, "4300", &column, &first); + show (stream, "4400", &column, &first); + show (stream, "4600", &column, &first); + show (stream, "4650", &column, &first); + show (stream, "5000", &column, &first); + show (stream, "6000", &column, &first); + show (stream, "8000", &column, &first); + show (stream, "10000", &column, &first); + fputc ('\n', stream); + + fprintf (stream, _("\ +-mCPU equivalent to -mcpu=CPU.\n\ +-no-mCPU don't generate code specific to CPU.\n\ + For -mCPU and -no-mCPU, CPU must be one of:\n")); + + first = 1; + + show (stream, "3900", &column, &first); + show (stream, "4010", &column, &first); + show (stream, "4100", &column, &first); + show (stream, "4650", &column, &first); + fputc ('\n', stream); + + fprintf(stream, _("\ +-mips16 generate mips16 instructions\n\ +-no-mips16 do not generate mips16 instructions\n")); + fprintf(stream, _("\ +-O0 remove unneeded NOPs, do not swap branches\n\ +-O remove unneeded NOPs and swap branches\n\ +--trap, --no-break trap exception on div by 0 and mult overflow\n\ +--break, --no-trap break exception on div by 0 and mult overflow\n")); +#ifdef OBJ_ELF + fprintf(stream, _("\ +-KPIC, -call_shared generate SVR4 position independent code\n\ +-non_shared do not generate position independent code\n\ +-xgot assume a 32 bit GOT\n\ +-32 create 32 bit object file (default)\n\ +-64 create 64 bit object file\n")); +#endif +} + +void +mips_init_after_args () +{ + /* initialize opcodes */ + bfd_mips_num_opcodes = bfd_mips_num_builtin_opcodes; + mips_opcodes = (struct mips_opcode*) mips_builtin_opcodes; +} + +long +md_pcrel_from (fixP) + fixS *fixP; +{ + if (OUTPUT_FLAVOR != bfd_target_aout_flavour + && fixP->fx_addsy != (symbolS *) NULL + && ! S_IS_DEFINED (fixP->fx_addsy)) + { + /* This makes a branch to an undefined symbol be a branch to the + current location. */ + return 4; + } + + /* return the address of the delay slot */ + return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address; +} + +/* This is called by emit_expr via TC_CONS_FIX_NEW when creating a + reloc for a cons. We could use the definition there, except that + we want to handle 64 bit relocs specially. */ + +void +cons_fix_new_mips (frag, where, nbytes, exp) + fragS *frag; + int where; + unsigned int nbytes; + expressionS *exp; +{ +#ifndef OBJ_ELF + /* If we are assembling in 32 bit mode, turn an 8 byte reloc into a + 4 byte reloc. */ + if (nbytes == 8 && ! mips_64) + { + if (target_big_endian) + where += 4; + nbytes = 4; + } +#endif + + if (nbytes != 2 && nbytes != 4 && nbytes != 8) + as_bad (_("Unsupported reloc size %d"), nbytes); + + fix_new_exp (frag_now, where, (int) nbytes, exp, 0, + (nbytes == 2 + ? BFD_RELOC_16 + : (nbytes == 4 ? BFD_RELOC_32 : BFD_RELOC_64))); +} + +/* This is called before the symbol table is processed. In order to + work with gcc when using mips-tfile, we must keep all local labels. + However, in other cases, we want to discard them. If we were + called with -g, but we didn't see any debugging information, it may + mean that gcc is smuggling debugging information through to + mips-tfile, in which case we must generate all local labels. */ + +void +mips_frob_file_before_adjust () +{ +#ifndef NO_ECOFF_DEBUGGING + if (ECOFF_DEBUGGING + && mips_debug != 0 + && ! ecoff_debugging_seen) + flag_keep_locals = 1; +#endif +} + +/* Sort any unmatched HI16_S relocs so that they immediately precede + the corresponding LO reloc. This is called before md_apply_fix and + tc_gen_reloc. Unmatched HI16_S relocs can only be generated by + explicit use of the %hi modifier. */ + +void +mips_frob_file () +{ + struct mips_hi_fixup *l; + + for (l = mips_hi_fixup_list; l != NULL; l = l->next) + { + segment_info_type *seginfo; + int pass; + + assert (l->fixp->fx_r_type == BFD_RELOC_HI16_S); + + /* Check quickly whether the next fixup happens to be a matching + %lo. */ + if (l->fixp->fx_next != NULL + && l->fixp->fx_next->fx_r_type == BFD_RELOC_LO16 + && l->fixp->fx_addsy == l->fixp->fx_next->fx_addsy + && l->fixp->fx_offset == l->fixp->fx_next->fx_offset) + continue; + + /* Look through the fixups for this segment for a matching %lo. + When we find one, move the %hi just in front of it. We do + this in two passes. In the first pass, we try to find a + unique %lo. In the second pass, we permit multiple %hi + relocs for a single %lo (this is a GNU extension). */ + seginfo = seg_info (l->seg); + for (pass = 0; pass < 2; pass++) + { + fixS *f, *prev; + + prev = NULL; + for (f = seginfo->fix_root; f != NULL; f = f->fx_next) + { + /* Check whether this is a %lo fixup which matches l->fixp. */ + if (f->fx_r_type == BFD_RELOC_LO16 + && f->fx_addsy == l->fixp->fx_addsy + && f->fx_offset == l->fixp->fx_offset + && (pass == 1 + || prev == NULL + || prev->fx_r_type != BFD_RELOC_HI16_S + || prev->fx_addsy != f->fx_addsy + || prev->fx_offset != f->fx_offset)) + { + fixS **pf; + + /* Move l->fixp before f. */ + for (pf = &seginfo->fix_root; + *pf != l->fixp; + pf = &(*pf)->fx_next) + assert (*pf != NULL); + + *pf = l->fixp->fx_next; + + l->fixp->fx_next = f; + if (prev == NULL) + seginfo->fix_root = l->fixp; + else + prev->fx_next = l->fixp; + + break; + } + + prev = f; + } + + if (f != NULL) + break; + +#if 0 /* GCC code motion plus incomplete dead code elimination + can leave a %hi without a %lo. */ + if (pass == 1) + as_warn_where (l->fixp->fx_file, l->fixp->fx_line, + _("Unmatched %%hi reloc")); +#endif + } + } +} + +/* When generating embedded PIC code we need to use a special + relocation to represent the difference of two symbols in the .text + section (switch tables use a difference of this sort). See + include/coff/mips.h for details. This macro checks whether this + fixup requires the special reloc. */ +#define SWITCH_TABLE(fixp) \ + ((fixp)->fx_r_type == BFD_RELOC_32 \ + && OUTPUT_FLAVOR != bfd_target_elf_flavour \ + && (fixp)->fx_addsy != NULL \ + && (fixp)->fx_subsy != NULL \ + && S_GET_SEGMENT ((fixp)->fx_addsy) == text_section \ + && S_GET_SEGMENT ((fixp)->fx_subsy) == text_section) + +/* When generating embedded PIC code we must keep all PC relative + relocations, in case the linker has to relax a call. We also need + to keep relocations for switch table entries. */ + +/*ARGSUSED*/ +int +mips_force_relocation (fixp) + fixS *fixp; +{ + if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT + || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY) + return 1; + + return (mips_pic == EMBEDDED_PIC + && (fixp->fx_pcrel + || SWITCH_TABLE (fixp) + || fixp->fx_r_type == BFD_RELOC_PCREL_HI16_S + || fixp->fx_r_type == BFD_RELOC_PCREL_LO16)); +} + +/* Apply a fixup to the object file. */ + +int +md_apply_fix (fixP, valueP) + fixS *fixP; + valueT *valueP; +{ + unsigned char *buf; + long insn, value; + + assert (fixP->fx_size == 4 + || fixP->fx_r_type == BFD_RELOC_16 + || fixP->fx_r_type == BFD_RELOC_64 + || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT + || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY); + + value = *valueP; + + /* If we aren't adjusting this fixup to be against the section + symbol, we need to adjust the value. */ +#ifdef OBJ_ELF + if (fixP->fx_addsy != NULL && OUTPUT_FLAVOR == bfd_target_elf_flavour) + { + if (S_GET_OTHER (fixP->fx_addsy) == STO_MIPS16 + || S_IS_WEAK (fixP->fx_addsy) + || (symbol_used_in_reloc_p (fixP->fx_addsy) + && (((bfd_get_section_flags (stdoutput, + S_GET_SEGMENT (fixP->fx_addsy)) + & SEC_LINK_ONCE) != 0) + || !strncmp (segment_name (S_GET_SEGMENT (fixP->fx_addsy)), + ".gnu.linkonce", + sizeof (".gnu.linkonce") - 1)))) + + { + value -= S_GET_VALUE (fixP->fx_addsy); + if (value != 0 && ! fixP->fx_pcrel) + { + /* In this case, the bfd_install_relocation routine will + incorrectly add the symbol value back in. We just want + the addend to appear in the object file. + FIXME: If this makes VALUE zero, we're toast. */ + value -= S_GET_VALUE (fixP->fx_addsy); + } + } + + /* This code was generated using trial and error and so is + fragile and not trustworthy. If you change it, you should + rerun the elf-rel, elf-rel2, and empic testcases and ensure + they still pass. */ + if (fixP->fx_pcrel || fixP->fx_subsy != NULL) + { + value += fixP->fx_frag->fr_address + fixP->fx_where; + + /* BFD's REL handling, for MIPS, is _very_ weird. + This gives the right results, but it can't possibly + be the way things are supposed to work. */ + if (fixP->fx_r_type != BFD_RELOC_16_PCREL_S2 + || S_GET_SEGMENT (fixP->fx_addsy) != undefined_section) + value += fixP->fx_frag->fr_address + fixP->fx_where; + } + } +#endif + + fixP->fx_addnumber = value; /* Remember value for tc_gen_reloc */ + + if (fixP->fx_addsy == NULL && ! fixP->fx_pcrel) + fixP->fx_done = 1; + + switch (fixP->fx_r_type) + { + case BFD_RELOC_MIPS_JMP: + case BFD_RELOC_HI16: + case BFD_RELOC_HI16_S: + case BFD_RELOC_MIPS_GPREL: + case BFD_RELOC_MIPS_LITERAL: + case BFD_RELOC_MIPS_CALL16: + case BFD_RELOC_MIPS_GOT16: + case BFD_RELOC_MIPS_GPREL32: + case BFD_RELOC_MIPS_GOT_HI16: + case BFD_RELOC_MIPS_GOT_LO16: + case BFD_RELOC_MIPS_CALL_HI16: + case BFD_RELOC_MIPS_CALL_LO16: + case BFD_RELOC_MIPS16_GPREL: + if (fixP->fx_pcrel) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Invalid PC relative reloc")); + /* Nothing needed to do. The value comes from the reloc entry */ + break; + + case BFD_RELOC_MIPS16_JMP: + /* We currently always generate a reloc against a symbol, which + means that we don't want an addend even if the symbol is + defined. */ + fixP->fx_addnumber = 0; + break; + + case BFD_RELOC_PCREL_HI16_S: + /* The addend for this is tricky if it is internal, so we just + do everything here rather than in bfd_install_relocation. */ + if (OUTPUT_FLAVOR == bfd_target_elf_flavour + && !fixP->fx_done + && value != 0) + break; + if (fixP->fx_addsy + && (symbol_get_bfdsym (fixP->fx_addsy)->flags & BSF_SECTION_SYM) == 0) + { + /* For an external symbol adjust by the address to make it + pcrel_offset. We use the address of the RELLO reloc + which follows this one. */ + value += (fixP->fx_next->fx_frag->fr_address + + fixP->fx_next->fx_where); + } + if (value & 0x8000) + value += 0x10000; + value >>= 16; + buf = (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where; + if (target_big_endian) + buf += 2; + md_number_to_chars (buf, value, 2); + break; + + case BFD_RELOC_PCREL_LO16: + /* The addend for this is tricky if it is internal, so we just + do everything here rather than in bfd_install_relocation. */ + if (OUTPUT_FLAVOR == bfd_target_elf_flavour + && !fixP->fx_done + && value != 0) + break; + if (fixP->fx_addsy + && (symbol_get_bfdsym (fixP->fx_addsy)->flags & BSF_SECTION_SYM) == 0) + value += fixP->fx_frag->fr_address + fixP->fx_where; + buf = (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where; + if (target_big_endian) + buf += 2; + md_number_to_chars (buf, value, 2); + break; + + case BFD_RELOC_64: + /* This is handled like BFD_RELOC_32, but we output a sign + extended value if we are only 32 bits. */ + if (fixP->fx_done + || (mips_pic == EMBEDDED_PIC && SWITCH_TABLE (fixP))) + { + if (8 <= sizeof (valueT)) + md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where, + value, 8); + else + { + long w1, w2; + long hiv; + + w1 = w2 = fixP->fx_where; + if (target_big_endian) + w1 += 4; + else + w2 += 4; + md_number_to_chars (fixP->fx_frag->fr_literal + w1, value, 4); + if ((value & 0x80000000) != 0) + hiv = 0xffffffff; + else + hiv = 0; + md_number_to_chars (fixP->fx_frag->fr_literal + w2, hiv, 4); + } + } + break; + + case BFD_RELOC_RVA: + case BFD_RELOC_32: + /* If we are deleting this reloc entry, we must fill in the + value now. This can happen if we have a .word which is not + resolved when it appears but is later defined. We also need + to fill in the value if this is an embedded PIC switch table + entry. */ + if (fixP->fx_done + || (mips_pic == EMBEDDED_PIC && SWITCH_TABLE (fixP))) + md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where, + value, 4); + break; + + case BFD_RELOC_16: + /* If we are deleting this reloc entry, we must fill in the + value now. */ + assert (fixP->fx_size == 2); + if (fixP->fx_done) + md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where, + value, 2); + break; + + case BFD_RELOC_LO16: + /* When handling an embedded PIC switch statement, we can wind + up deleting a LO16 reloc. See the 'o' case in mips_ip. */ + if (fixP->fx_done) + { + if (value < -0x8000 || value > 0x7fff) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("relocation overflow")); + buf = (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where; + if (target_big_endian) + buf += 2; + md_number_to_chars (buf, value, 2); + } + break; + + case BFD_RELOC_16_PCREL_S2: + /* + * We need to save the bits in the instruction since fixup_segment() + * might be deleting the relocation entry (i.e., a branch within + * the current segment). + */ + if ((value & 0x3) != 0) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Branch to odd address (%lx)"), value); + + if (!fixP->fx_done && value != 0) + break; + /* If 'value' is zero, the remaining reloc code won't actually + do the store, so it must be done here. This is probably + a bug somewhere. */ + if (!fixP->fx_done) + value -= fixP->fx_frag->fr_address + fixP->fx_where; + + value >>= 2; + + /* update old instruction data */ + buf = (unsigned char *) (fixP->fx_where + fixP->fx_frag->fr_literal); + if (target_big_endian) + insn = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; + else + insn = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + + if (value >= -0x8000 && value < 0x8000) + insn |= value & 0xffff; + else + { + /* The branch offset is too large. If this is an + unconditional branch, and we are not generating PIC code, + we can convert it to an absolute jump instruction. */ + if (mips_pic == NO_PIC + && fixP->fx_done + && fixP->fx_frag->fr_address >= text_section->vma + && (fixP->fx_frag->fr_address + < text_section->vma + text_section->_raw_size) + && ((insn & 0xffff0000) == 0x10000000 /* beq $0,$0 */ + || (insn & 0xffff0000) == 0x04010000 /* bgez $0 */ + || (insn & 0xffff0000) == 0x04110000)) /* bgezal $0 */ + { + if ((insn & 0xffff0000) == 0x04110000) /* bgezal $0 */ + insn = 0x0c000000; /* jal */ + else + insn = 0x08000000; /* j */ + fixP->fx_r_type = BFD_RELOC_MIPS_JMP; + fixP->fx_done = 0; + fixP->fx_addsy = section_symbol (text_section); + fixP->fx_addnumber = (value << 2) + md_pcrel_from (fixP); + } + else + { + /* FIXME. It would be possible in principle to handle + conditional branches which overflow. They could be + transformed into a branch around a jump. This would + require setting up variant frags for each different + branch type. The native MIPS assembler attempts to + handle these cases, but it appears to do it + incorrectly. */ + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Branch out of range")); + } + } + + md_number_to_chars ((char *) buf, (valueT) insn, 4); + break; + + case BFD_RELOC_VTABLE_INHERIT: + fixP->fx_done = 0; + if (fixP->fx_addsy + && !S_IS_DEFINED (fixP->fx_addsy) + && !S_IS_WEAK (fixP->fx_addsy)) + S_SET_WEAK (fixP->fx_addsy); + break; + + case BFD_RELOC_VTABLE_ENTRY: + fixP->fx_done = 0; + break; + + default: + internalError (); + } + + return 1; +} + +#if 0 +void +printInsn (oc) + unsigned long oc; +{ + const struct mips_opcode *p; + int treg, sreg, dreg, shamt; + short imm; + const char *args; + int i; + + for (i = 0; i < NUMOPCODES; ++i) + { + p = &mips_opcodes[i]; + if (((oc & p->mask) == p->match) && (p->pinfo != INSN_MACRO)) + { + printf ("%08lx %s\t", oc, p->name); + treg = (oc >> 16) & 0x1f; + sreg = (oc >> 21) & 0x1f; + dreg = (oc >> 11) & 0x1f; + shamt = (oc >> 6) & 0x1f; + imm = oc; + for (args = p->args;; ++args) + { + switch (*args) + { + case '\0': + printf ("\n"); + break; + + case ',': + case '(': + case ')': + printf ("%c", *args); + continue; + + case 'r': + assert (treg == sreg); + printf ("$%d,$%d", treg, sreg); + continue; + + case 'd': + case 'G': + printf ("$%d", dreg); + continue; + + case 't': + case 'E': + printf ("$%d", treg); + continue; + + case 'k': + printf ("0x%x", treg); + continue; + + case 'b': + case 's': + printf ("$%d", sreg); + continue; + + case 'a': + printf ("0x%08lx", oc & 0x1ffffff); + continue; + + case 'i': + case 'j': + case 'o': + case 'u': + printf ("%d", imm); + continue; + + case '<': + case '>': + printf ("$%d", shamt); + continue; + + default: + internalError (); + } + break; + } + return; + } + } + printf (_("%08lx UNDEFINED\n"), oc); +} +#endif + +static symbolS * +get_symbol () +{ + int c; + char *name; + symbolS *p; + + name = input_line_pointer; + c = get_symbol_end (); + p = (symbolS *) symbol_find_or_make (name); + *input_line_pointer = c; + return p; +} + +/* Align the current frag to a given power of two. The MIPS assembler + also automatically adjusts any preceding label. */ + +static void +mips_align (to, fill, label) + int to; + int fill; + symbolS *label; +{ + mips_emit_delays (false); + frag_align (to, fill, 0); + record_alignment (now_seg, to); + if (label != NULL) + { + assert (S_GET_SEGMENT (label) == now_seg); + symbol_set_frag (label, frag_now); + S_SET_VALUE (label, (valueT) frag_now_fix ()); + } +} + +/* Align to a given power of two. .align 0 turns off the automatic + alignment used by the data creating pseudo-ops. */ + +static void +s_align (x) + int x; +{ + register int temp; + register long temp_fill; + long max_alignment = 15; + + /* + + o Note that the assembler pulls down any immediately preceeding label + to the aligned address. + o It's not documented but auto alignment is reinstated by + a .align pseudo instruction. + o Note also that after auto alignment is turned off the mips assembler + issues an error on attempt to assemble an improperly aligned data item. + We don't. + + */ + + temp = get_absolute_expression (); + if (temp > max_alignment) + as_bad (_("Alignment too large: %d. assumed."), temp = max_alignment); + else if (temp < 0) + { + as_warn (_("Alignment negative: 0 assumed.")); + temp = 0; + } + if (*input_line_pointer == ',') + { + input_line_pointer++; + temp_fill = get_absolute_expression (); + } + else + temp_fill = 0; + if (temp) + { + auto_align = 1; + mips_align (temp, (int) temp_fill, + insn_labels != NULL ? insn_labels->label : NULL); + } + else + { + auto_align = 0; + } + + demand_empty_rest_of_line (); +} + +void +mips_flush_pending_output () +{ + mips_emit_delays (false); + mips_clear_insn_labels (); +} + +static void +s_change_sec (sec) + int sec; +{ + segT seg; + + /* When generating embedded PIC code, we only use the .text, .lit8, + .sdata and .sbss sections. We change the .data and .rdata + pseudo-ops to use .sdata. */ + if (mips_pic == EMBEDDED_PIC + && (sec == 'd' || sec == 'r')) + sec = 's'; + +#ifdef OBJ_ELF + /* The ELF backend needs to know that we are changing sections, so + that .previous works correctly. We could do something like check + for a obj_section_change_hook macro, but that might be confusing + as it would not be appropriate to use it in the section changing + functions in read.c, since obj-elf.c intercepts those. FIXME: + This should be cleaner, somehow. */ + obj_elf_section_change_hook (); +#endif + + mips_emit_delays (false); + switch (sec) + { + case 't': + s_text (0); + break; + case 'd': + s_data (0); + break; + case 'b': + subseg_set (bss_section, (subsegT) get_absolute_expression ()); + demand_empty_rest_of_line (); + break; + + case 'r': + if (USE_GLOBAL_POINTER_OPT) + { + seg = subseg_new (RDATA_SECTION_NAME, + (subsegT) get_absolute_expression ()); + if (OUTPUT_FLAVOR == bfd_target_elf_flavour) + { + bfd_set_section_flags (stdoutput, seg, + (SEC_ALLOC + | SEC_LOAD + | SEC_READONLY + | SEC_RELOC + | SEC_DATA)); + if (strcmp (TARGET_OS, "elf") != 0) + bfd_set_section_alignment (stdoutput, seg, 4); + } + demand_empty_rest_of_line (); + } + else + { + as_bad (_("No read only data section in this object file format")); + demand_empty_rest_of_line (); + return; + } + break; + + case 's': + if (USE_GLOBAL_POINTER_OPT) + { + seg = subseg_new (".sdata", (subsegT) get_absolute_expression ()); + if (OUTPUT_FLAVOR == bfd_target_elf_flavour) + { + bfd_set_section_flags (stdoutput, seg, + SEC_ALLOC | SEC_LOAD | SEC_RELOC + | SEC_DATA); + if (strcmp (TARGET_OS, "elf") != 0) + bfd_set_section_alignment (stdoutput, seg, 4); + } + demand_empty_rest_of_line (); + break; + } + else + { + as_bad (_("Global pointers not supported; recompile -G 0")); + demand_empty_rest_of_line (); + return; + } + } + + auto_align = 1; +} + +void +mips_enable_auto_align () +{ + auto_align = 1; +} + +static void +s_cons (log_size) + int log_size; +{ + symbolS *label; + + label = insn_labels != NULL ? insn_labels->label : NULL; + mips_emit_delays (false); + if (log_size > 0 && auto_align) + mips_align (log_size, 0, label); + mips_clear_insn_labels (); + cons (1 << log_size); +} + +static void +s_float_cons (type) + int type; +{ + symbolS *label; + + label = insn_labels != NULL ? insn_labels->label : NULL; + + mips_emit_delays (false); + + if (auto_align) + { + if (type == 'd') + mips_align (3, 0, label); + else + mips_align (2, 0, label); + } + + mips_clear_insn_labels (); + + float_cons (type); +} + +/* Handle .globl. We need to override it because on Irix 5 you are + permitted to say + .globl foo .text + where foo is an undefined symbol, to mean that foo should be + considered to be the address of a function. */ + +static void +s_mips_globl (x) + int x; +{ + char *name; + int c; + symbolS *symbolP; + flagword flag; + + name = input_line_pointer; + c = get_symbol_end (); + symbolP = symbol_find_or_make (name); + *input_line_pointer = c; + SKIP_WHITESPACE (); + + /* On Irix 5, every global symbol that is not explicitly labelled as + being a function is apparently labelled as being an object. */ + flag = BSF_OBJECT; + + if (! is_end_of_line[(unsigned char) *input_line_pointer]) + { + char *secname; + asection *sec; + + secname = input_line_pointer; + c = get_symbol_end (); + sec = bfd_get_section_by_name (stdoutput, secname); + if (sec == NULL) + as_bad (_("%s: no such section"), secname); + *input_line_pointer = c; + + if (sec != NULL && (sec->flags & SEC_CODE) != 0) + flag = BSF_FUNCTION; + } + + symbol_get_bfdsym (symbolP)->flags |= flag; + + S_SET_EXTERNAL (symbolP); + demand_empty_rest_of_line (); +} + +static void +s_option (x) + int x; +{ + char *opt; + char c; + + opt = input_line_pointer; + c = get_symbol_end (); + + if (*opt == 'O') + { + /* FIXME: What does this mean? */ + } + else if (strncmp (opt, "pic", 3) == 0) + { + int i; + + i = atoi (opt + 3); + if (i == 0) + mips_pic = NO_PIC; + else if (i == 2) + mips_pic = SVR4_PIC; + else + as_bad (_(".option pic%d not supported"), i); + + if (USE_GLOBAL_POINTER_OPT && mips_pic == SVR4_PIC) + { + if (g_switch_seen && g_switch_value != 0) + as_warn (_("-G may not be used with SVR4 PIC code")); + g_switch_value = 0; + bfd_set_gp_size (stdoutput, 0); + } + } + else + as_warn (_("Unrecognized option \"%s\""), opt); + + *input_line_pointer = c; + demand_empty_rest_of_line (); +} + +/* This structure is used to hold a stack of .set values. */ + +struct mips_option_stack +{ + struct mips_option_stack *next; + struct mips_set_options options; +}; + +static struct mips_option_stack *mips_opts_stack; + +/* Handle the .set pseudo-op. */ + +static void +s_mipsset (x) + int x; +{ + char *name = input_line_pointer, ch; + + while (!is_end_of_line[(unsigned char) *input_line_pointer]) + input_line_pointer++; + ch = *input_line_pointer; + *input_line_pointer = '\0'; + + if (strcmp (name, "reorder") == 0) + { + if (mips_opts.noreorder && prev_nop_frag != NULL) + { + /* If we still have pending nops, we can discard them. The + usual nop handling will insert any that are still + needed. */ + prev_nop_frag->fr_fix -= (prev_nop_frag_holds + * (mips_opts.mips16 ? 2 : 4)); + prev_nop_frag = NULL; + } + mips_opts.noreorder = 0; + } + else if (strcmp (name, "noreorder") == 0) + { + mips_emit_delays (true); + mips_opts.noreorder = 1; + mips_any_noreorder = 1; + } + else if (strcmp (name, "at") == 0) + { + mips_opts.noat = 0; + } + else if (strcmp (name, "noat") == 0) + { + mips_opts.noat = 1; + } + else if (strcmp (name, "macro") == 0) + { + mips_opts.warn_about_macros = 0; + } + else if (strcmp (name, "nomacro") == 0) + { + if (mips_opts.noreorder == 0) + as_bad (_("`noreorder' must be set before `nomacro'")); + mips_opts.warn_about_macros = 1; + } + else if (strcmp (name, "move") == 0 || strcmp (name, "novolatile") == 0) + { + mips_opts.nomove = 0; + } + else if (strcmp (name, "nomove") == 0 || strcmp (name, "volatile") == 0) + { + mips_opts.nomove = 1; + } + else if (strcmp (name, "bopt") == 0) + { + mips_opts.nobopt = 0; + } + else if (strcmp (name, "nobopt") == 0) + { + mips_opts.nobopt = 1; + } + else if (strcmp (name, "mips16") == 0 + || strcmp (name, "MIPS-16") == 0) + mips_opts.mips16 = 1; + else if (strcmp (name, "nomips16") == 0 + || strcmp (name, "noMIPS-16") == 0) + mips_opts.mips16 = 0; + else if (strncmp (name, "mips", 4) == 0) + { + int isa; + + /* Permit the user to change the ISA on the fly. Needless to + say, misuse can cause serious problems. */ + isa = atoi (name + 4); + if (isa == 0) + mips_opts.isa = file_mips_isa; + else if (isa < 1 || isa > 4) + as_bad (_("unknown ISA level")); + else + mips_opts.isa = isa; + } + else if (strcmp (name, "autoextend") == 0) + mips_opts.noautoextend = 0; + else if (strcmp (name, "noautoextend") == 0) + mips_opts.noautoextend = 1; + else if (strcmp (name, "push") == 0) + { + struct mips_option_stack *s; + + s = (struct mips_option_stack *) xmalloc (sizeof *s); + s->next = mips_opts_stack; + s->options = mips_opts; + mips_opts_stack = s; + } + else if (strcmp (name, "pop") == 0) + { + struct mips_option_stack *s; + + s = mips_opts_stack; + if (s == NULL) + as_bad (_(".set pop with no .set push")); + else + { + /* If we're changing the reorder mode we need to handle + delay slots correctly. */ + if (s->options.noreorder && ! mips_opts.noreorder) + mips_emit_delays (true); + else if (! s->options.noreorder && mips_opts.noreorder) + { + if (prev_nop_frag != NULL) + { + prev_nop_frag->fr_fix -= (prev_nop_frag_holds + * (mips_opts.mips16 ? 2 : 4)); + prev_nop_frag = NULL; + } + } + + mips_opts = s->options; + mips_opts_stack = s->next; + free (s); + } + } + else + { + as_warn (_("Tried to set unrecognized symbol: %s\n"), name); + } + *input_line_pointer = ch; + demand_empty_rest_of_line (); +} + +/* Handle the .abicalls pseudo-op. I believe this is equivalent to + .option pic2. It means to generate SVR4 PIC calls. */ + +static void +s_abicalls (ignore) + int ignore; +{ + mips_pic = SVR4_PIC; + if (USE_GLOBAL_POINTER_OPT) + { + if (g_switch_seen && g_switch_value != 0) + as_warn (_("-G may not be used with SVR4 PIC code")); + g_switch_value = 0; + } + bfd_set_gp_size (stdoutput, 0); + demand_empty_rest_of_line (); +} + +/* Handle the .cpload pseudo-op. This is used when generating SVR4 + PIC code. It sets the $gp register for the function based on the + function address, which is in the register named in the argument. + This uses a relocation against _gp_disp, which is handled specially + by the linker. The result is: + lui $gp,%hi(_gp_disp) + addiu $gp,$gp,%lo(_gp_disp) + addu $gp,$gp,.cpload argument + The .cpload argument is normally $25 == $t9. */ + +static void +s_cpload (ignore) + int ignore; +{ + expressionS ex; + int icnt = 0; + + /* If we are not generating SVR4 PIC code, .cpload is ignored. */ + if (mips_pic != SVR4_PIC) + { + s_ignore (0); + return; + } + + /* .cpload should be a in .set noreorder section. */ + if (mips_opts.noreorder == 0) + as_warn (_(".cpload not in noreorder section")); + + ex.X_op = O_symbol; + ex.X_add_symbol = symbol_find_or_make ("_gp_disp"); + ex.X_op_symbol = NULL; + ex.X_add_number = 0; + + /* In ELF, this symbol is implicitly an STT_OBJECT symbol. */ + symbol_get_bfdsym (ex.X_add_symbol)->flags |= BSF_OBJECT; + + macro_build_lui ((char *) NULL, &icnt, &ex, GP); + macro_build ((char *) NULL, &icnt, &ex, "addiu", "t,r,j", GP, GP, + (int) BFD_RELOC_LO16); + + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "addu", "d,v,t", + GP, GP, tc_get_register (0)); + + demand_empty_rest_of_line (); +} + +/* Handle the .cprestore pseudo-op. This stores $gp into a given + offset from $sp. The offset is remembered, and after making a PIC + call $gp is restored from that location. */ + +static void +s_cprestore (ignore) + int ignore; +{ + expressionS ex; + int icnt = 0; + + /* If we are not generating SVR4 PIC code, .cprestore is ignored. */ + if (mips_pic != SVR4_PIC) + { + s_ignore (0); + return; + } + + mips_cprestore_offset = get_absolute_expression (); + + ex.X_op = O_constant; + ex.X_add_symbol = NULL; + ex.X_op_symbol = NULL; + ex.X_add_number = mips_cprestore_offset; + + macro_build ((char *) NULL, &icnt, &ex, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "sw" : "sd"), + "t,o(b)", GP, (int) BFD_RELOC_LO16, SP); + + demand_empty_rest_of_line (); +} + +/* Handle the .gpword pseudo-op. This is used when generating PIC + code. It generates a 32 bit GP relative reloc. */ + +static void +s_gpword (ignore) + int ignore; +{ + symbolS *label; + expressionS ex; + char *p; + + /* When not generating PIC code, this is treated as .word. */ + if (mips_pic != SVR4_PIC) + { + s_cons (2); + return; + } + + label = insn_labels != NULL ? insn_labels->label : NULL; + mips_emit_delays (true); + if (auto_align) + mips_align (2, 0, label); + mips_clear_insn_labels (); + + expression (&ex); + + if (ex.X_op != O_symbol || ex.X_add_number != 0) + { + as_bad (_("Unsupported use of .gpword")); + ignore_rest_of_line (); + } + + p = frag_more (4); + md_number_to_chars (p, (valueT) 0, 4); + fix_new_exp (frag_now, p - frag_now->fr_literal, 4, &ex, 0, + BFD_RELOC_MIPS_GPREL32); + + demand_empty_rest_of_line (); +} + +/* Handle the .cpadd pseudo-op. This is used when dealing with switch + tables in SVR4 PIC code. */ + +static void +s_cpadd (ignore) + int ignore; +{ + int icnt = 0; + int reg; + + /* This is ignored when not generating SVR4 PIC code. */ + if (mips_pic != SVR4_PIC) + { + s_ignore (0); + return; + } + + /* Add $gp to the register named as an argument. */ + reg = tc_get_register (0); + macro_build ((char *) NULL, &icnt, (expressionS *) NULL, + ((bfd_arch_bits_per_address (stdoutput) == 32 + || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + ? "addu" : "daddu"), + "d,v,t", reg, reg, GP); + + demand_empty_rest_of_line (); +} + +/* Handle the .insn pseudo-op. This marks instruction labels in + mips16 mode. This permits the linker to handle them specially, + such as generating jalx instructions when needed. We also make + them odd for the duration of the assembly, in order to generate the + right sort of code. We will make them even in the adjust_symtab + routine, while leaving them marked. This is convenient for the + debugger and the disassembler. The linker knows to make them odd + again. */ + +static void +s_insn (ignore) + int ignore; +{ + if (mips_opts.mips16) + mips16_mark_labels (); + + demand_empty_rest_of_line (); +} + +/* Handle a .stabn directive. We need these in order to mark a label + as being a mips16 text label correctly. Sometimes the compiler + will emit a label, followed by a .stabn, and then switch sections. + If the label and .stabn are in mips16 mode, then the label is + really a mips16 text label. */ + +static void +s_mips_stab (type) + int type; +{ + if (type == 'n' && mips_opts.mips16) + mips16_mark_labels (); + + s_stab (type); +} + +/* Handle the .weakext pseudo-op as defined in Kane and Heinrich. + */ + +static void +s_mips_weakext (ignore) + int ignore; +{ + char *name; + int c; + symbolS *symbolP; + expressionS exp; + + name = input_line_pointer; + c = get_symbol_end (); + symbolP = symbol_find_or_make (name); + S_SET_WEAK (symbolP); + *input_line_pointer = c; + + SKIP_WHITESPACE (); + + if (! is_end_of_line[(unsigned char) *input_line_pointer]) + { + if (S_IS_DEFINED (symbolP)) + { + as_bad ("Ignoring attempt to redefine symbol `%s'.", + S_GET_NAME (symbolP)); + ignore_rest_of_line (); + return; + } + + if (*input_line_pointer == ',') + { + ++input_line_pointer; + SKIP_WHITESPACE (); + } + + expression (&exp); + if (exp.X_op != O_symbol) + { + as_bad ("bad .weakext directive"); + ignore_rest_of_line(); + return; + } + symbol_set_value_expression (symbolP, &exp); + } + + demand_empty_rest_of_line (); +} + +/* Parse a register string into a number. Called from the ECOFF code + to parse .frame. The argument is non-zero if this is the frame + register, so that we can record it in mips_frame_reg. */ + +int +tc_get_register (frame) + int frame; +{ + int reg; + + SKIP_WHITESPACE (); + if (*input_line_pointer++ != '$') + { + as_warn (_("expected `$'")); + reg = 0; + } + else if (isdigit ((unsigned char) *input_line_pointer)) + { + reg = get_absolute_expression (); + if (reg < 0 || reg >= 32) + { + as_warn (_("Bad register number")); + reg = 0; + } + } + else + { + if (strncmp (input_line_pointer, "fp", 2) == 0) + reg = FP; + else if (strncmp (input_line_pointer, "sp", 2) == 0) + reg = SP; + else if (strncmp (input_line_pointer, "gp", 2) == 0) + reg = GP; + else if (strncmp (input_line_pointer, "at", 2) == 0) + reg = AT; + else + { + as_warn (_("Unrecognized register name")); + reg = 0; + } + input_line_pointer += 2; + } + if (frame) + mips_frame_reg = reg != 0 ? reg : SP; + return reg; +} + +valueT +md_section_align (seg, addr) + asection *seg; + valueT addr; +{ + int align = bfd_get_section_alignment (stdoutput, seg); + +#ifdef OBJ_ELF + /* We don't need to align ELF sections to the full alignment. + However, Irix 5 may prefer that we align them at least to a 16 + byte boundary. We don't bother to align the sections if we are + targeted for an embedded system. */ + if (strcmp (TARGET_OS, "elf") == 0) + return addr; + if (align > 4) + align = 4; +#endif + + return ((addr + (1 << align) - 1) & (-1 << align)); +} + +/* Utility routine, called from above as well. If called while the + input file is still being read, it's only an approximation. (For + example, a symbol may later become defined which appeared to be + undefined earlier.) */ + +static int +nopic_need_relax (sym, before_relaxing) + symbolS *sym; + int before_relaxing; +{ + if (sym == 0) + return 0; + + if (USE_GLOBAL_POINTER_OPT) + { + const char *symname; + int change; + + /* Find out whether this symbol can be referenced off the GP + register. It can be if it is smaller than the -G size or if + it is in the .sdata or .sbss section. Certain symbols can + not be referenced off the GP, although it appears as though + they can. */ + symname = S_GET_NAME (sym); + if (symname != (const char *) NULL + && (strcmp (symname, "eprol") == 0 + || strcmp (symname, "etext") == 0 + || strcmp (symname, "_gp") == 0 + || strcmp (symname, "edata") == 0 + || strcmp (symname, "_fbss") == 0 + || strcmp (symname, "_fdata") == 0 + || strcmp (symname, "_ftext") == 0 + || strcmp (symname, "end") == 0 + || strcmp (symname, "_gp_disp") == 0)) + change = 1; + else if ((! S_IS_DEFINED (sym) || S_IS_COMMON (sym)) + && (0 +#ifndef NO_ECOFF_DEBUGGING + || (symbol_get_obj (sym)->ecoff_extern_size != 0 + && (symbol_get_obj (sym)->ecoff_extern_size + <= g_switch_value)) +#endif + /* We must defer this decision until after the whole + file has been read, since there might be a .extern + after the first use of this symbol. */ + || (before_relaxing +#ifndef NO_ECOFF_DEBUGGING + && symbol_get_obj (sym)->ecoff_extern_size == 0 +#endif + && S_GET_VALUE (sym) == 0) + || (S_GET_VALUE (sym) != 0 + && S_GET_VALUE (sym) <= g_switch_value))) + change = 0; + else + { + const char *segname; + + segname = segment_name (S_GET_SEGMENT (sym)); + assert (strcmp (segname, ".lit8") != 0 + && strcmp (segname, ".lit4") != 0); + change = (strcmp (segname, ".sdata") != 0 + && strcmp (segname, ".sbss") != 0 + && strncmp (segname, ".sdata.", 7) != 0 + && strncmp (segname, ".gnu.linkonce.s.", 16) != 0); + } + return change; + } + else + /* We are not optimizing for the GP register. */ + return 1; +} + +/* Given a mips16 variant frag FRAGP, return non-zero if it needs an + extended opcode. SEC is the section the frag is in. */ + +static int +mips16_extended_frag (fragp, sec, stretch) + fragS *fragp; + asection *sec; + long stretch; +{ + int type; + register const struct mips16_immed_operand *op; + offsetT val; + int mintiny, maxtiny; + segT symsec; + + if (RELAX_MIPS16_USER_SMALL (fragp->fr_subtype)) + return 0; + if (RELAX_MIPS16_USER_EXT (fragp->fr_subtype)) + return 1; + + type = RELAX_MIPS16_TYPE (fragp->fr_subtype); + op = mips16_immed_operands; + while (op->type != type) + { + ++op; + assert (op < mips16_immed_operands + MIPS16_NUM_IMMED); + } + + if (op->unsp) + { + if (type == '<' || type == '>' || type == '[' || type == ']') + { + mintiny = 1; + maxtiny = 1 << op->nbits; + } + else + { + mintiny = 0; + maxtiny = (1 << op->nbits) - 1; + } + } + else + { + mintiny = - (1 << (op->nbits - 1)); + maxtiny = (1 << (op->nbits - 1)) - 1; + } + + /* We can't always call S_GET_VALUE here, because we don't want to + lock in a particular frag address. */ + if (symbol_constant_p (fragp->fr_symbol)) + { + val = (S_GET_VALUE (fragp->fr_symbol) + + symbol_get_frag (fragp->fr_symbol)->fr_address); + symsec = S_GET_SEGMENT (fragp->fr_symbol); + } + else if (symbol_equated_p (fragp->fr_symbol) + && (symbol_constant_p + (symbol_get_value_expression (fragp->fr_symbol)->X_add_symbol))) + { + symbolS *eqsym; + + eqsym = symbol_get_value_expression (fragp->fr_symbol)->X_add_symbol; + val = (S_GET_VALUE (eqsym) + + symbol_get_frag (eqsym)->fr_address + + symbol_get_value_expression (fragp->fr_symbol)->X_add_number + + symbol_get_frag (fragp->fr_symbol)->fr_address); + symsec = S_GET_SEGMENT (eqsym); + } + else + return 1; + + if (op->pcrel) + { + addressT addr; + + /* We won't have the section when we are called from + mips_relax_frag. However, we will always have been called + from md_estimate_size_before_relax first. If this is a + branch to a different section, we mark it as such. If SEC is + NULL, and the frag is not marked, then it must be a branch to + the same section. */ + if (sec == NULL) + { + if (RELAX_MIPS16_LONG_BRANCH (fragp->fr_subtype)) + return 1; + } + else + { + if (symsec != sec) + { + fragp->fr_subtype = + RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype); + + /* FIXME: We should support this, and let the linker + catch branches and loads that are out of range. */ + as_bad_where (fragp->fr_file, fragp->fr_line, + _("unsupported PC relative reference to different section")); + + return 1; + } + } + + /* In this case, we know for sure that the symbol fragment is in + the same section. If the fr_address of the symbol fragment + is greater then the address of this fragment we want to add + in STRETCH in order to get a better estimate of the address. + This particularly matters because of the shift bits. */ + if (stretch != 0 + && (symbol_get_frag (fragp->fr_symbol)->fr_address + >= fragp->fr_address)) + { + fragS *f; + + /* Adjust stretch for any alignment frag. Note that if have + been expanding the earlier code, the symbol may be + defined in what appears to be an earlier frag. FIXME: + This doesn't handle the fr_subtype field, which specifies + a maximum number of bytes to skip when doing an + alignment. */ + for (f = fragp; + f != NULL && f != symbol_get_frag (fragp->fr_symbol); + f = f->fr_next) + { + if (f->fr_type == rs_align || f->fr_type == rs_align_code) + { + if (stretch < 0) + stretch = - ((- stretch) + & ~ ((1 << (int) f->fr_offset) - 1)); + else + stretch &= ~ ((1 << (int) f->fr_offset) - 1); + if (stretch == 0) + break; + } + } + if (f != NULL) + val += stretch; + } + + addr = fragp->fr_address + fragp->fr_fix; + + /* The base address rules are complicated. The base address of + a branch is the following instruction. The base address of a + PC relative load or add is the instruction itself, but if it + is in a delay slot (in which case it can not be extended) use + the address of the instruction whose delay slot it is in. */ + if (type == 'p' || type == 'q') + { + addr += 2; + + /* If we are currently assuming that this frag should be + extended, then, the current address is two bytes + higher. */ + if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype)) + addr += 2; + + /* Ignore the low bit in the target, since it will be set + for a text label. */ + if ((val & 1) != 0) + --val; + } + else if (RELAX_MIPS16_JAL_DSLOT (fragp->fr_subtype)) + addr -= 4; + else if (RELAX_MIPS16_DSLOT (fragp->fr_subtype)) + addr -= 2; + + val -= addr & ~ ((1 << op->shift) - 1); + + /* Branch offsets have an implicit 0 in the lowest bit. */ + if (type == 'p' || type == 'q') + val /= 2; + + /* If any of the shifted bits are set, we must use an extended + opcode. If the address depends on the size of this + instruction, this can lead to a loop, so we arrange to always + use an extended opcode. We only check this when we are in + the main relaxation loop, when SEC is NULL. */ + if ((val & ((1 << op->shift) - 1)) != 0 && sec == NULL) + { + fragp->fr_subtype = + RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype); + return 1; + } + + /* If we are about to mark a frag as extended because the value + is precisely maxtiny + 1, then there is a chance of an + infinite loop as in the following code: + la $4,foo + .skip 1020 + .align 2 + foo: + In this case when the la is extended, foo is 0x3fc bytes + away, so the la can be shrunk, but then foo is 0x400 away, so + the la must be extended. To avoid this loop, we mark the + frag as extended if it was small, and is about to become + extended with a value of maxtiny + 1. */ + if (val == ((maxtiny + 1) << op->shift) + && ! RELAX_MIPS16_EXTENDED (fragp->fr_subtype) + && sec == NULL) + { + fragp->fr_subtype = + RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype); + return 1; + } + } + else if (symsec != absolute_section && sec != NULL) + as_bad_where (fragp->fr_file, fragp->fr_line, _("unsupported relocation")); + + if ((val & ((1 << op->shift) - 1)) != 0 + || val < (mintiny << op->shift) + || val > (maxtiny << op->shift)) + return 1; + else + return 0; +} + +/* Estimate the size of a frag before relaxing. Unless this is the + mips16, we are not really relaxing here, and the final size is + encoded in the subtype information. For the mips16, we have to + decide whether we are using an extended opcode or not. */ + +/*ARGSUSED*/ +int +md_estimate_size_before_relax (fragp, segtype) + fragS *fragp; + asection *segtype; +{ + int change; + + if (RELAX_MIPS16_P (fragp->fr_subtype)) + { + if (mips16_extended_frag (fragp, segtype, 0)) + { + fragp->fr_subtype = RELAX_MIPS16_MARK_EXTENDED (fragp->fr_subtype); + return 4; + } + else + { + fragp->fr_subtype = RELAX_MIPS16_CLEAR_EXTENDED (fragp->fr_subtype); + return 2; + } + } + + if (mips_pic == NO_PIC) + { + change = nopic_need_relax (fragp->fr_symbol, 0); + } + else if (mips_pic == SVR4_PIC) + { + symbolS *sym; + asection *symsec; + + sym = fragp->fr_symbol; + + /* Handle the case of a symbol equated to another symbol. */ + while (symbol_equated_p (sym) + && (! S_IS_DEFINED (sym) || S_IS_COMMON (sym))) + { + symbolS *n; + + /* It's possible to get a loop here in a badly written + program. */ + n = symbol_get_value_expression (sym)->X_add_symbol; + if (n == sym) + break; + sym = n; + } + + symsec = S_GET_SEGMENT (sym); + + /* This must duplicate the test in adjust_reloc_syms. */ + change = (symsec != &bfd_und_section + && symsec != &bfd_abs_section + && ! bfd_is_com_section (symsec)); + } + else + abort (); + + if (change) + { + /* Record the offset to the first reloc in the fr_opcode field. + This lets md_convert_frag and tc_gen_reloc know that the code + must be expanded. */ + fragp->fr_opcode = (fragp->fr_literal + + fragp->fr_fix + - RELAX_OLD (fragp->fr_subtype) + + RELAX_RELOC1 (fragp->fr_subtype)); + /* FIXME: This really needs as_warn_where. */ + if (RELAX_WARN (fragp->fr_subtype)) + as_warn (_("AT used after \".set noat\" or macro used after \".set nomacro\"")); + } + + if (! change) + return 0; + else + return RELAX_NEW (fragp->fr_subtype) - RELAX_OLD (fragp->fr_subtype); +} + +/* This is called to see whether a reloc against a defined symbol + should be converted into a reloc against a section. Don't adjust + MIPS16 jump relocations, so we don't have to worry about the format + of the offset in the .o file. Don't adjust relocations against + mips16 symbols, so that the linker can find them if it needs to set + up a stub. */ + +int +mips_fix_adjustable (fixp) + fixS *fixp; +{ + if (fixp->fx_r_type == BFD_RELOC_MIPS16_JMP) + return 0; + if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT + || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY) + return 0; + if (fixp->fx_addsy == NULL) + return 1; +#ifdef OBJ_ELF + if (OUTPUT_FLAVOR == bfd_target_elf_flavour + && S_GET_OTHER (fixp->fx_addsy) == STO_MIPS16 + && fixp->fx_subsy == NULL) + return 0; +#endif + return 1; +} + +/* Translate internal representation of relocation info to BFD target + format. */ + +arelent ** +tc_gen_reloc (section, fixp) + asection *section; + fixS *fixp; +{ + static arelent *retval[4]; + arelent *reloc; + bfd_reloc_code_real_type code; + + reloc = retval[0] = (arelent *) xmalloc (sizeof (arelent)); + retval[1] = NULL; + + reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *)); + *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); + reloc->address = fixp->fx_frag->fr_address + fixp->fx_where; + + if (mips_pic == EMBEDDED_PIC + && SWITCH_TABLE (fixp)) + { + /* For a switch table entry we use a special reloc. The addend + is actually the difference between the reloc address and the + subtrahend. */ + reloc->addend = reloc->address - S_GET_VALUE (fixp->fx_subsy); + if (OUTPUT_FLAVOR != bfd_target_ecoff_flavour) + as_fatal (_("Double check fx_r_type in tc-mips.c:tc_gen_reloc")); + fixp->fx_r_type = BFD_RELOC_GPREL32; + } + else if (fixp->fx_pcrel == 0 || OUTPUT_FLAVOR == bfd_target_elf_flavour) + reloc->addend = fixp->fx_addnumber; + else if (fixp->fx_r_type == BFD_RELOC_PCREL_LO16) + { + /* We use a special addend for an internal RELLO reloc. */ + if (symbol_section_p (fixp->fx_addsy)) + reloc->addend = reloc->address - S_GET_VALUE (fixp->fx_subsy); + else + reloc->addend = fixp->fx_addnumber + reloc->address; + } + else if (fixp->fx_r_type == BFD_RELOC_PCREL_HI16_S) + { + assert (fixp->fx_next != NULL + && fixp->fx_next->fx_r_type == BFD_RELOC_PCREL_LO16); + /* We use a special addend for an internal RELHI reloc. The + reloc is relative to the RELLO; adjust the addend + accordingly. */ + if (symbol_section_p (fixp->fx_addsy)) + reloc->addend = (fixp->fx_next->fx_frag->fr_address + + fixp->fx_next->fx_where + - S_GET_VALUE (fixp->fx_subsy)); + else + reloc->addend = (fixp->fx_addnumber + + fixp->fx_next->fx_frag->fr_address + + fixp->fx_next->fx_where); + } + else + { + if (OUTPUT_FLAVOR != bfd_target_aout_flavour) + /* A gruesome hack which is a result of the gruesome gas reloc + handling. */ + reloc->addend = reloc->address; + else + reloc->addend = -reloc->address; + } + + /* If this is a variant frag, we may need to adjust the existing + reloc and generate a new one. */ + if (fixp->fx_frag->fr_opcode != NULL + && (fixp->fx_r_type == BFD_RELOC_MIPS_GPREL + || fixp->fx_r_type == BFD_RELOC_MIPS_GOT16 + || fixp->fx_r_type == BFD_RELOC_MIPS_CALL16 + || fixp->fx_r_type == BFD_RELOC_MIPS_GOT_HI16 + || fixp->fx_r_type == BFD_RELOC_MIPS_GOT_LO16 + || fixp->fx_r_type == BFD_RELOC_MIPS_CALL_HI16 + || fixp->fx_r_type == BFD_RELOC_MIPS_CALL_LO16)) + { + arelent *reloc2; + + assert (! RELAX_MIPS16_P (fixp->fx_frag->fr_subtype)); + + /* If this is not the last reloc in this frag, then we have two + GPREL relocs, or a GOT_HI16/GOT_LO16 pair, or a + CALL_HI16/CALL_LO16, both of which are being replaced. Let + the second one handle all of them. */ + if (fixp->fx_next != NULL + && fixp->fx_frag == fixp->fx_next->fx_frag) + { + assert ((fixp->fx_r_type == BFD_RELOC_MIPS_GPREL + && fixp->fx_next->fx_r_type == BFD_RELOC_MIPS_GPREL) + || (fixp->fx_r_type == BFD_RELOC_MIPS_GOT_HI16 + && (fixp->fx_next->fx_r_type + == BFD_RELOC_MIPS_GOT_LO16)) + || (fixp->fx_r_type == BFD_RELOC_MIPS_CALL_HI16 + && (fixp->fx_next->fx_r_type + == BFD_RELOC_MIPS_CALL_LO16))); + retval[0] = NULL; + return retval; + } + + fixp->fx_where = fixp->fx_frag->fr_opcode - fixp->fx_frag->fr_literal; + reloc->address = fixp->fx_frag->fr_address + fixp->fx_where; + reloc2 = retval[1] = (arelent *) xmalloc (sizeof (arelent)); + retval[2] = NULL; + reloc2->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *)); + *reloc2->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); + reloc2->address = (reloc->address + + (RELAX_RELOC2 (fixp->fx_frag->fr_subtype) + - RELAX_RELOC1 (fixp->fx_frag->fr_subtype))); + reloc2->addend = fixp->fx_addnumber; + reloc2->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_LO16); + assert (reloc2->howto != NULL); + + if (RELAX_RELOC3 (fixp->fx_frag->fr_subtype)) + { + arelent *reloc3; + + reloc3 = retval[2] = (arelent *) xmalloc (sizeof (arelent)); + retval[3] = NULL; + *reloc3 = *reloc2; + reloc3->address += 4; + } + + if (mips_pic == NO_PIC) + { + assert (fixp->fx_r_type == BFD_RELOC_MIPS_GPREL); + fixp->fx_r_type = BFD_RELOC_HI16_S; + } + else if (mips_pic == SVR4_PIC) + { + switch (fixp->fx_r_type) + { + default: + abort (); + case BFD_RELOC_MIPS_GOT16: + break; + case BFD_RELOC_MIPS_CALL16: + case BFD_RELOC_MIPS_GOT_LO16: + case BFD_RELOC_MIPS_CALL_LO16: + fixp->fx_r_type = BFD_RELOC_MIPS_GOT16; + break; + } + } + else + abort (); + } + + /* Since MIPS ELF uses Rel instead of Rela, encode the vtable entry + to be used in the relocation's section offset. */ + if (fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY) + { + reloc->address = reloc->addend; + reloc->addend = 0; + } + + /* Since DIFF_EXPR_OK is defined in tc-mips.h, it is possible that + fixup_segment converted a non-PC relative reloc into a PC + relative reloc. In such a case, we need to convert the reloc + code. */ + code = fixp->fx_r_type; + if (fixp->fx_pcrel) + { + switch (code) + { + case BFD_RELOC_8: + code = BFD_RELOC_8_PCREL; + break; + case BFD_RELOC_16: + code = BFD_RELOC_16_PCREL; + break; + case BFD_RELOC_32: + code = BFD_RELOC_32_PCREL; + break; + case BFD_RELOC_64: + code = BFD_RELOC_64_PCREL; + break; + case BFD_RELOC_8_PCREL: + case BFD_RELOC_16_PCREL: + case BFD_RELOC_32_PCREL: + case BFD_RELOC_64_PCREL: + case BFD_RELOC_16_PCREL_S2: + case BFD_RELOC_PCREL_HI16_S: + case BFD_RELOC_PCREL_LO16: + break; + default: + as_bad_where (fixp->fx_file, fixp->fx_line, + _("Cannot make %s relocation PC relative"), + bfd_get_reloc_code_name (code)); + } + } + + /* To support a PC relative reloc when generating embedded PIC code + for ECOFF, we use a Cygnus extension. We check for that here to + make sure that we don't let such a reloc escape normally. */ + if ((OUTPUT_FLAVOR == bfd_target_ecoff_flavour + || OUTPUT_FLAVOR == bfd_target_elf_flavour) + && code == BFD_RELOC_16_PCREL_S2 + && mips_pic != EMBEDDED_PIC) + reloc->howto = NULL; + else + reloc->howto = bfd_reloc_type_lookup (stdoutput, code); + + if (reloc->howto == NULL) + { + as_bad_where (fixp->fx_file, fixp->fx_line, + _("Can not represent %s relocation in this object file format"), + bfd_get_reloc_code_name (code)); + retval[0] = NULL; + } + + return retval; +} + +/* Relax a machine dependent frag. This returns the amount by which + the current size of the frag should change. */ + +int +mips_relax_frag (fragp, stretch) + fragS *fragp; + long stretch; +{ + if (! RELAX_MIPS16_P (fragp->fr_subtype)) + return 0; + + if (mips16_extended_frag (fragp, (asection *) NULL, stretch)) + { + if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype)) + return 0; + fragp->fr_subtype = RELAX_MIPS16_MARK_EXTENDED (fragp->fr_subtype); + return 2; + } + else + { + if (! RELAX_MIPS16_EXTENDED (fragp->fr_subtype)) + return 0; + fragp->fr_subtype = RELAX_MIPS16_CLEAR_EXTENDED (fragp->fr_subtype); + return -2; + } + + return 0; +} + +/* Convert a machine dependent frag. */ + +void +md_convert_frag (abfd, asec, fragp) + bfd *abfd; + segT asec; + fragS *fragp; +{ + int old, new; + char *fixptr; + + if (RELAX_MIPS16_P (fragp->fr_subtype)) + { + int type; + register const struct mips16_immed_operand *op; + boolean small, ext; + offsetT val; + bfd_byte *buf; + unsigned long insn; + boolean use_extend; + unsigned short extend; + + type = RELAX_MIPS16_TYPE (fragp->fr_subtype); + op = mips16_immed_operands; + while (op->type != type) + ++op; + + if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype)) + { + small = false; + ext = true; + } + else + { + small = true; + ext = false; + } + + resolve_symbol_value (fragp->fr_symbol, 1); + val = S_GET_VALUE (fragp->fr_symbol); + if (op->pcrel) + { + addressT addr; + + addr = fragp->fr_address + fragp->fr_fix; + + /* The rules for the base address of a PC relative reloc are + complicated; see mips16_extended_frag. */ + if (type == 'p' || type == 'q') + { + addr += 2; + if (ext) + addr += 2; + /* Ignore the low bit in the target, since it will be + set for a text label. */ + if ((val & 1) != 0) + --val; + } + else if (RELAX_MIPS16_JAL_DSLOT (fragp->fr_subtype)) + addr -= 4; + else if (RELAX_MIPS16_DSLOT (fragp->fr_subtype)) + addr -= 2; + + addr &= ~ (addressT) ((1 << op->shift) - 1); + val -= addr; + + /* Make sure the section winds up with the alignment we have + assumed. */ + if (op->shift > 0) + record_alignment (asec, op->shift); + } + + if (ext + && (RELAX_MIPS16_JAL_DSLOT (fragp->fr_subtype) + || RELAX_MIPS16_DSLOT (fragp->fr_subtype))) + as_warn_where (fragp->fr_file, fragp->fr_line, + _("extended instruction in delay slot")); + + buf = (bfd_byte *) (fragp->fr_literal + fragp->fr_fix); + + if (target_big_endian) + insn = bfd_getb16 (buf); + else + insn = bfd_getl16 (buf); + + mips16_immed (fragp->fr_file, fragp->fr_line, type, val, + RELAX_MIPS16_USER_EXT (fragp->fr_subtype), + small, ext, &insn, &use_extend, &extend); + + if (use_extend) + { + md_number_to_chars (buf, 0xf000 | extend, 2); + fragp->fr_fix += 2; + buf += 2; + } + + md_number_to_chars (buf, insn, 2); + fragp->fr_fix += 2; + buf += 2; + } + else + { + if (fragp->fr_opcode == NULL) + return; + + old = RELAX_OLD (fragp->fr_subtype); + new = RELAX_NEW (fragp->fr_subtype); + fixptr = fragp->fr_literal + fragp->fr_fix; + + if (new > 0) + memcpy (fixptr - old, fixptr, new); + + fragp->fr_fix += new - old; + } +} + +#ifdef OBJ_ELF + +/* This function is called after the relocs have been generated. + We've been storing mips16 text labels as odd. Here we convert them + back to even for the convenience of the debugger. */ + +void +mips_frob_file_after_relocs () +{ + asymbol **syms; + unsigned int count, i; + + if (OUTPUT_FLAVOR != bfd_target_elf_flavour) + return; + + syms = bfd_get_outsymbols (stdoutput); + count = bfd_get_symcount (stdoutput); + for (i = 0; i < count; i++, syms++) + { + if (elf_symbol (*syms)->internal_elf_sym.st_other == STO_MIPS16 + && ((*syms)->value & 1) != 0) + { + (*syms)->value &= ~1; + /* If the symbol has an odd size, it was probably computed + incorrectly, so adjust that as well. */ + if ((elf_symbol (*syms)->internal_elf_sym.st_size & 1) != 0) + ++elf_symbol (*syms)->internal_elf_sym.st_size; + } + } +} + +#endif + +/* This function is called whenever a label is defined. It is used + when handling branch delays; if a branch has a label, we assume we + can not move it. */ + +void +mips_define_label (sym) + symbolS *sym; +{ + struct insn_label_list *l; + + if (free_insn_labels == NULL) + l = (struct insn_label_list *) xmalloc (sizeof *l); + else + { + l = free_insn_labels; + free_insn_labels = l->next; + } + + l->label = sym; + l->next = insn_labels; + insn_labels = l; +} + +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) + +/* Some special processing for a MIPS ELF file. */ + +void +mips_elf_final_processing () +{ + /* Write out the register information. */ + if (! mips_64) + { + Elf32_RegInfo s; + + s.ri_gprmask = mips_gprmask; + s.ri_cprmask[0] = mips_cprmask[0]; + s.ri_cprmask[1] = mips_cprmask[1]; + s.ri_cprmask[2] = mips_cprmask[2]; + s.ri_cprmask[3] = mips_cprmask[3]; + /* The gp_value field is set by the MIPS ELF backend. */ + + bfd_mips_elf32_swap_reginfo_out (stdoutput, &s, + ((Elf32_External_RegInfo *) + mips_regmask_frag)); + } + else + { + Elf64_Internal_RegInfo s; + + s.ri_gprmask = mips_gprmask; + s.ri_pad = 0; + s.ri_cprmask[0] = mips_cprmask[0]; + s.ri_cprmask[1] = mips_cprmask[1]; + s.ri_cprmask[2] = mips_cprmask[2]; + s.ri_cprmask[3] = mips_cprmask[3]; + /* The gp_value field is set by the MIPS ELF backend. */ + + bfd_mips_elf64_swap_reginfo_out (stdoutput, &s, + ((Elf64_External_RegInfo *) + mips_regmask_frag)); + } + + /* Set the MIPS ELF flag bits. FIXME: There should probably be some + sort of BFD interface for this. */ + if (mips_any_noreorder) + elf_elfheader (stdoutput)->e_flags |= EF_MIPS_NOREORDER; + if (mips_pic != NO_PIC) + elf_elfheader (stdoutput)->e_flags |= EF_MIPS_PIC; + + /* Set the MIPS ELF ABI flags. */ + if (mips_abi_string == 0) + ; + else if (strcmp (mips_abi_string,"32") == 0) + elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_O32; + else if (strcmp (mips_abi_string,"o64") == 0) + elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_O64; + else if (strcmp (mips_abi_string,"eabi") == 0) + { + if (mips_eabi64) + elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_EABI64; + else + elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_EABI32; + } + + if (mips_32bitmode) + elf_elfheader (stdoutput)->e_flags |= EF_MIPS_32BITMODE; +} + +#endif /* OBJ_ELF || OBJ_MAYBE_ELF */ + +typedef struct proc + { + symbolS *isym; + unsigned long reg_mask; + unsigned long reg_offset; + unsigned long fpreg_mask; + unsigned long fpreg_offset; + unsigned long frame_offset; + unsigned long frame_reg; + unsigned long pc_reg; + } +procS; + +static procS cur_proc; +static procS *cur_proc_ptr; +static int numprocs; + +/* When we align code in the .text section of mips16, use the correct two + byte nop pattern of 0x6500 (move $0,$0) */ + +int +mips_do_align (n, fill, len, max) + int n; + const char *fill; + int len; + int max; +{ + if (fill == NULL + && subseg_text_p (now_seg) + && n > 1 + && mips_opts.mips16) + { + static const unsigned char be_nop[] = { 0x65, 0x00 }; + static const unsigned char le_nop[] = { 0x00, 0x65 }; + + frag_align (1, 0, 0); + + if (target_big_endian) + frag_align_pattern (n, be_nop, 2, max); + else + frag_align_pattern (n, le_nop, 2, max); + return 1; + } + + return 0; +} + +static void +md_obj_begin () +{ +} + +static void +md_obj_end () +{ + /* check for premature end, nesting errors, etc */ + if (cur_proc_ptr) + as_warn (_("missing `.end' at end of assembly")); +} + +static long +get_number () +{ + int negative = 0; + long val = 0; + + if (*input_line_pointer == '-') + { + ++input_line_pointer; + negative = 1; + } + if (!isdigit ((unsigned char) *input_line_pointer)) + as_bad (_("Expected simple number.")); + if (input_line_pointer[0] == '0') + { + if (input_line_pointer[1] == 'x') + { + input_line_pointer += 2; + while (isxdigit ((unsigned char) *input_line_pointer)) + { + val <<= 4; + val |= hex_value (*input_line_pointer++); + } + return negative ? -val : val; + } + else + { + ++input_line_pointer; + while (isdigit ((unsigned char) *input_line_pointer)) + { + val <<= 3; + val |= *input_line_pointer++ - '0'; + } + return negative ? -val : val; + } + } + if (!isdigit ((unsigned char) *input_line_pointer)) + { + printf (_(" *input_line_pointer == '%c' 0x%02x\n"), + *input_line_pointer, *input_line_pointer); + as_warn (_("Invalid number")); + return -1; + } + while (isdigit ((unsigned char) *input_line_pointer)) + { + val *= 10; + val += *input_line_pointer++ - '0'; + } + return negative ? -val : val; +} + +/* The .file directive; just like the usual .file directive, but there + is an initial number which is the ECOFF file index. */ + +static void +s_file (x) + int x; +{ + int line; + + line = get_number (); + s_app_file (0); +} + + +/* The .end directive. */ + +static void +s_mips_end (x) + int x; +{ + symbolS *p; + int maybe_text; + + if (!is_end_of_line[(unsigned char) *input_line_pointer]) + { + p = get_symbol (); + demand_empty_rest_of_line (); + } + else + p = NULL; + +#ifdef BFD_ASSEMBLER + if ((bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) != 0) + maybe_text = 1; + else + maybe_text = 0; +#else + if (now_seg != data_section && now_seg != bss_section) + maybe_text = 1; + else + maybe_text = 0; +#endif + + if (!maybe_text) + as_warn (_(".end not in text section")); + + if (!cur_proc_ptr) + { + as_warn (_(".end directive without a preceding .ent directive.")); + demand_empty_rest_of_line (); + return; + } + + if (p != NULL) + { + assert (S_GET_NAME (p)); + if (strcmp (S_GET_NAME (p), S_GET_NAME (cur_proc_ptr->isym))) + as_warn (_(".end symbol does not match .ent symbol.")); + } + else + as_warn (_(".end directive missing or unknown symbol")); + +#ifdef MIPS_STABS_ELF + { + segT saved_seg = now_seg; + subsegT saved_subseg = now_subseg; + fragS *saved_frag = frag_now; + valueT dot; + segT seg; + expressionS exp; + char *fragp; + + dot = frag_now_fix (); + +#ifdef md_flush_pending_output + md_flush_pending_output (); +#endif + + assert (pdr_seg); + subseg_set (pdr_seg, 0); + + /* Write the symbol */ + exp.X_op = O_symbol; + exp.X_add_symbol = p; + exp.X_add_number = 0; + emit_expr (&exp, 4); + + fragp = frag_more (7*4); + + md_number_to_chars (fragp, (valueT) cur_proc_ptr->reg_mask, 4); + md_number_to_chars (fragp + 4, (valueT) cur_proc_ptr->reg_offset, 4); + md_number_to_chars (fragp + 8, (valueT) cur_proc_ptr->fpreg_mask, 4); + md_number_to_chars (fragp +12, (valueT) cur_proc_ptr->fpreg_offset, 4); + md_number_to_chars (fragp +16, (valueT) cur_proc_ptr->frame_offset, 4); + md_number_to_chars (fragp +20, (valueT) cur_proc_ptr->frame_reg, 4); + md_number_to_chars (fragp +24, (valueT) cur_proc_ptr->pc_reg, 4); + + subseg_set (saved_seg, saved_subseg); + } +#endif + + cur_proc_ptr = NULL; +} + +/* The .aent and .ent directives. */ + +static void +s_mips_ent (aent) + int aent; +{ + int number = 0; + symbolS *symbolP; + int maybe_text; + + symbolP = get_symbol (); + if (*input_line_pointer == ',') + input_line_pointer++; + SKIP_WHITESPACE (); + if (isdigit ((unsigned char) *input_line_pointer) + || *input_line_pointer == '-') + number = get_number (); + +#ifdef BFD_ASSEMBLER + if ((bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) != 0) + maybe_text = 1; + else + maybe_text = 0; +#else + if (now_seg != data_section && now_seg != bss_section) + maybe_text = 1; + else + maybe_text = 0; +#endif + + if (!maybe_text) + as_warn (_(".ent or .aent not in text section.")); + + if (!aent && cur_proc_ptr) + as_warn (_("missing `.end'")); + + if (!aent) + { + cur_proc_ptr = &cur_proc; + memset (cur_proc_ptr, '\0', sizeof (procS)); + + cur_proc_ptr->isym = symbolP; + + symbol_get_bfdsym (symbolP)->flags |= BSF_FUNCTION; + + numprocs++; + } + + demand_empty_rest_of_line (); +} + +/* The .frame directive. If the mdebug section is present (IRIX 5 native) + then ecoff.c (ecoff_directive_frame) is used. For embedded targets, + s_mips_frame is used so that we can set the PDR information correctly. + We can't use the ecoff routines because they make reference to the ecoff + symbol table (in the mdebug section). */ + +static void +s_mips_frame (ignore) + int ignore; +{ +#ifdef MIPS_STABS_ELF + + long val; + + if (cur_proc_ptr == (procS *) NULL) + { + as_warn (_(".frame outside of .ent")); + demand_empty_rest_of_line (); + return; + } + + cur_proc_ptr->frame_reg = tc_get_register (1); + + SKIP_WHITESPACE (); + if (*input_line_pointer++ != ',' + || get_absolute_expression_and_terminator (&val) != ',') + { + as_warn (_("Bad .frame directive")); + --input_line_pointer; + demand_empty_rest_of_line (); + return; + } + + cur_proc_ptr->frame_offset = val; + cur_proc_ptr->pc_reg = tc_get_register (0); + + demand_empty_rest_of_line (); +#else + s_ignore (ignore); +#endif /* MIPS_STABS_ELF */ +} + +/* The .fmask and .mask directives. If the mdebug section is present + (IRIX 5 native) then ecoff.c (ecoff_directive_mask) is used. For + embedded targets, s_mips_mask is used so that we can set the PDR + information correctly. We can't use the ecoff routines because they + make reference to the ecoff symbol table (in the mdebug section). */ + +static void +s_mips_mask (reg_type) + char reg_type; +{ +#ifdef MIPS_STABS_ELF + long mask, off; + + if (cur_proc_ptr == (procS *) NULL) + { + as_warn (_(".mask/.fmask outside of .ent")); + demand_empty_rest_of_line (); + return; + } + + if (get_absolute_expression_and_terminator (&mask) != ',') + { + as_warn (_("Bad .mask/.fmask directive")); + --input_line_pointer; + demand_empty_rest_of_line (); + return; + } + + off = get_absolute_expression (); + + if (reg_type == 'F') + { + cur_proc_ptr->fpreg_mask = mask; + cur_proc_ptr->fpreg_offset = off; + } + else + { + cur_proc_ptr->reg_mask = mask; + cur_proc_ptr->reg_offset = off; + } + + demand_empty_rest_of_line (); +#else + s_ignore (reg_type); +#endif /* MIPS_STABS_ELF */ +} + +/* The .loc directive. */ + +#if 0 +static void +s_loc (x) + int x; +{ + symbolS *symbolP; + int lineno; + int addroff; + + assert (now_seg == text_section); + + lineno = get_number (); + addroff = frag_now_fix (); + + symbolP = symbol_new ("", N_SLINE, addroff, frag_now); + S_SET_TYPE (symbolP, N_SLINE); + S_SET_OTHER (symbolP, 0); + S_SET_DESC (symbolP, lineno); + symbolP->sy_segment = now_seg; +} +#endif diff --git a/contrib/binutils/gas/config/tc-mips.h b/contrib/binutils/gas/config/tc-mips.h new file mode 100644 index 000000000000..1008b75ecc11 --- /dev/null +++ b/contrib/binutils/gas/config/tc-mips.h @@ -0,0 +1,157 @@ +/* tc-mips.h -- header file for tc-mips.c. + Copyright (C) 1993, 94, 95, 96, 97, 1999, 2000 Free Software Foundation, Inc. + Contributed by the OSF and Ralph Campbell. + Written by Keith Knowles and Ralph Campbell, working independently. + Modified for ECOFF support by Ian Lance Taylor of Cygnus Support. + + This file is part of GAS. + + GAS 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. + + GAS 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 GAS; see the file COPYING. If not, write to the Free + Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifndef TC_MIPS + +#define TC_MIPS + +#ifdef ANSI_PROTOTYPES +struct frag; +struct expressionS; +#endif + +/* Default to big endian. */ +#ifndef TARGET_BYTES_BIG_ENDIAN +#define TARGET_BYTES_BIG_ENDIAN 1 +#endif + +#define TARGET_ARCH bfd_arch_mips + +#define ONLY_STANDARD_ESCAPES +#define WORKING_DOT_WORD 1 +#define OLD_FLOAT_READS +#define REPEAT_CONS_EXPRESSIONS +#define RELOC_EXPANSION_POSSIBLE +#define MAX_RELOC_EXPANSION 3 +#define LOCAL_LABELS_FB 1 + +/* Maximum symbol offset that can be encoded in a BFD_RELOC_MIPS_GPREL + relocation: */ +#define MAX_GPREL_OFFSET (0x7FF4) + +#define md_relax_frag(fragp, stretch) mips_relax_frag(fragp, stretch) +extern int mips_relax_frag PARAMS ((struct frag *, long)); + +#define md_undefined_symbol(name) (0) +#define md_operand(x) + +extern int mips_do_align PARAMS ((int, const char *, int, int)); +#define md_do_align(n,fill,len,max,l) if (mips_do_align (n,fill,len,max)) goto l + +/* We permit PC relative difference expressions when generating + embedded PIC code. */ +#define DIFF_EXPR_OK + +/* Tell assembler that we have an itbl_mips.h header file to include. */ +#define HAVE_ITBL_CPU + +/* The endianness of the target format may change based on command + line arguments. */ +#define TARGET_FORMAT mips_target_format() +extern const char *mips_target_format PARAMS ((void)); + +struct mips_cl_insn +{ + unsigned long insn_opcode; + const struct mips_opcode *insn_mo; + /* The next two fields are used when generating mips16 code. */ + boolean use_extend; + unsigned short extend; +}; + +extern int tc_get_register PARAMS ((int frame)); + +#define tc_init_after_args() mips_init_after_args() +extern void mips_init_after_args PARAMS ((void)); + +#define md_parse_long_option(arg) mips_parse_long_option (arg) +extern int mips_parse_long_option PARAMS ((const char *)); + +#define tc_frob_label(sym) mips_define_label (sym) +extern void mips_define_label PARAMS ((symbolS *)); + +#define tc_frob_file_before_adjust() mips_frob_file_before_adjust () +extern void mips_frob_file_before_adjust PARAMS ((void)); + +#define tc_frob_file() mips_frob_file () +extern void mips_frob_file PARAMS ((void)); + +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) +#define tc_frob_file_after_relocs mips_frob_file_after_relocs +extern void mips_frob_file_after_relocs PARAMS ((void)); +#endif + +#define TC_CONS_FIX_NEW cons_fix_new_mips +extern void cons_fix_new_mips + PARAMS ((struct frag *, int, unsigned int, struct expressionS *)); + +#define tc_fix_adjustable(fixp) mips_fix_adjustable (fixp) +extern int mips_fix_adjustable PARAMS ((struct fix *)); + +/* When generating embedded PIC code we must keep PC relative + relocations. */ +#define TC_FORCE_RELOCATION(fixp) mips_force_relocation (fixp) +extern int mips_force_relocation PARAMS ((struct fix *)); + +/* md_apply_fix sets fx_done correctly. */ +#define TC_HANDLE_FX_DONE 1 + +/* Register mask variables. These are set by the MIPS assembly code + and used by ECOFF and possibly other object file formats. */ +extern unsigned long mips_gprmask; +extern unsigned long mips_cprmask[4]; + +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) + +#define elf_tc_final_processing mips_elf_final_processing +extern void mips_elf_final_processing PARAMS ((void)); + +#define ELF_TC_SPECIAL_SECTIONS \ + { ".sdata", SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL }, \ + { ".sbss", SHT_NOBITS, SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL }, \ + { ".lit4", SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL }, \ + { ".lit8", SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL }, \ + { ".ucode", SHT_MIPS_UCODE, 0 }, \ + { ".mdebug", SHT_MIPS_DEBUG, 0 }, +/* Other special sections not generated by the assembler: .reginfo, + .liblist, .conflict, .gptab, .got, .dynamic, .rel.dyn. */ + +#endif + +extern void md_mips_end PARAMS ((void)); +#define md_end() md_mips_end() + +#define USE_GLOBAL_POINTER_OPT (OUTPUT_FLAVOR == bfd_target_ecoff_flavour \ + || OUTPUT_FLAVOR == bfd_target_coff_flavour \ + || OUTPUT_FLAVOR == bfd_target_elf_flavour) + +extern void mips_pop_insert PARAMS ((void)); +#define md_pop_insert() mips_pop_insert() + +extern void mips_flush_pending_output PARAMS ((void)); +#define md_flush_pending_output mips_flush_pending_output + +extern void mips_enable_auto_align PARAMS ((void)); +#define md_elf_section_change_hook() mips_enable_auto_align() + +#endif /* TC_MIPS */ diff --git a/contrib/binutils/gas/config/te-freebsd.h b/contrib/binutils/gas/config/te-freebsd.h index 27149334d7e8..4b2acde4f403 100644 --- a/contrib/binutils/gas/config/te-freebsd.h +++ b/contrib/binutils/gas/config/te-freebsd.h @@ -18,7 +18,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/binutils/gas/config/te-freebsd.h,v 1.1.6.2 2000/07/07 05:33:57 obrien Exp $ */ /* Target environment for FreeBSD. It is the same as the generic target, except that it arranges via the TE_FreeBSD define to diff --git a/contrib/binutils/gas/configure b/contrib/binutils/gas/configure index 668f68479080..b2a7d1aef9b3 100755 --- a/contrib/binutils/gas/configure +++ b/contrib/binutils/gas/configure @@ -1,6 +1,6 @@ #! /bin/sh -# $FreeBSD$ +# $FreeBSD: src/contrib/binutils/gas/configure,v 1.4.6.2 2000/07/07 05:33:42 obrien Exp $ # Guess values for system-dependent variables and create Makefiles. # Generated automatically using autoconf version 2.13 diff --git a/contrib/binutils/gas/configure.in b/contrib/binutils/gas/configure.in index 05b7b7f34e2b..ffff92a8f6d9 100644 --- a/contrib/binutils/gas/configure.in +++ b/contrib/binutils/gas/configure.in @@ -3,7 +3,7 @@ dnl dnl And be careful when changing it! If you must add tests with square dnl brackets, be sure changequote invocations surround it. dnl -dnl $FreeBSD$ +dnl $FreeBSD: src/contrib/binutils/gas/configure.in,v 1.3.6.2 2000/07/07 05:33:43 obrien Exp $ dnl dnl v2.5 needed for --bindir et al AC_PREREQ(2.13) diff --git a/contrib/binutils/gas/doc/as.1 b/contrib/binutils/gas/doc/as.1 index 5e0b820a9a9c..680354e1c06e 100644 --- a/contrib/binutils/gas/doc/as.1 +++ b/contrib/binutils/gas/doc/as.1 @@ -1,6 +1,6 @@ .\" Copyright (c) 1991, 1992, 1996, 1997, 1998 Free Software Foundation .\" See section COPYING for conditions for redistribution -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/binutils/gas/doc/as.1,v 1.2.6.2 2000/07/07 05:33:59 obrien Exp $ .TH as 1 "29 March 1996" "cygnus support" "GNU Development Tools" .SH NAME diff --git a/contrib/binutils/gas/doc/c-mips.texi b/contrib/binutils/gas/doc/c-mips.texi new file mode 100644 index 000000000000..26940deabc5f --- /dev/null +++ b/contrib/binutils/gas/doc/c-mips.texi @@ -0,0 +1,275 @@ +@c Copyright (C) 1991, 92, 93, 94, 95, 1997 Free Software Foundation, Inc. +@c This is part of the GAS manual. +@c For copying conditions, see the file as.texinfo. +@ifset GENERIC +@page +@node MIPS-Dependent +@chapter MIPS Dependent Features +@end ifset +@ifclear GENERIC +@node Machine Dependencies +@chapter MIPS Dependent Features +@end ifclear + +@cindex MIPS processor +@sc{gnu} @code{@value{AS}} for @sc{mips} architectures supports several +different @sc{mips} processors, and MIPS ISA levels I through IV. For +information about the @sc{mips} instruction set, see @cite{MIPS RISC +Architecture}, by Kane and Heindrich (Prentice-Hall). For an overview +of @sc{mips} assembly conventions, see ``Appendix D: Assembly Language +Programming'' in the same work. + +@menu +* MIPS Opts:: Assembler options +* MIPS Object:: ECOFF object code +* MIPS Stabs:: Directives for debugging information +* MIPS ISA:: Directives to override the ISA level +* MIPS autoextend:: Directives for extending MIPS 16 bit instructions +* MIPS insn:: Directive to mark data as an instruction +* MIPS option stack:: Directives to save and restore options +@end menu + +@node MIPS Opts +@section Assembler options + +The @sc{mips} configurations of @sc{gnu} @code{@value{AS}} support these +special options: + +@table @code +@cindex @code{-G} option (MIPS) +@item -G @var{num} +This option sets the largest size of an object that can be referenced +implicitly with the @code{gp} register. It is only accepted for targets +that use @sc{ecoff} format. The default value is 8. + +@cindex @code{-EB} option (MIPS) +@cindex @code{-EL} option (MIPS) +@cindex MIPS big-endian output +@cindex MIPS little-endian output +@cindex big-endian output, MIPS +@cindex little-endian output, MIPS +@item -EB +@itemx -EL +Any @sc{mips} configuration of @code{@value{AS}} can select big-endian or +little-endian output at run time (unlike the other @sc{gnu} development +tools, which must be configured for one or the other). Use @samp{-EB} +to select big-endian output, and @samp{-EL} for little-endian. + +@cindex MIPS architecture options +@item -mips1 +@itemx -mips2 +@itemx -mips3 +@itemx -mips4 +Generate code for a particular MIPS Instruction Set Architecture level. +@samp{-mips1} corresponds to the @sc{r2000} and @sc{r3000} processors, +@samp{-mips2} to the @sc{r6000} processor, @samp{-mips3} to the +@sc{r4000} processor, and @samp{-mips4} to the @sc{r8000} and +@sc{r10000} processors. You can also switch instruction sets during the +assembly; see @ref{MIPS ISA,, Directives to override the ISA level}. + +@item -mgp32 +Assume that 32-bit general purpose registers are available. This +affects synthetic instructions such as @code{move}, which will assemble +to a 32-bit or a 64-bit instruction depending on this flag. On some +MIPS variants there is a 32-bit mode flag; when this flag is set, +64-bit instructions generate a trap. Also, some 32-bit OSes only save +the 32-bit registers on a context switch, so it is essential never to +use the 64-bit registers. + +@item -mgp64 +Assume that 64-bit general purpose registers are available. This is +provided in the interests of symmetry with -gp32. + +@item -mips16 +@itemx -no-mips16 +Generate code for the MIPS 16 processor. This is equivalent to putting +@samp{.set mips16} at the start of the assembly file. @samp{-no-mips16} +turns off this option. + +@item -mfix7000 +@itemx -no-mfix7000 +Cause nops to be inserted if the read of the destination register +of an mfhi or mflo instruction occurs in the following two instructions. + +@item -m4010 +@itemx -no-m4010 +Generate code for the LSI @sc{r4010} chip. This tells the assembler to +accept the @sc{r4010} specific instructions (@samp{addciu}, @samp{ffc}, +etc.), and to not schedule @samp{nop} instructions around accesses to +the @samp{HI} and @samp{LO} registers. @samp{-no-m4010} turns off this +option. + +@item -m4650 +@itemx -no-m4650 +Generate code for the MIPS @sc{r4650} chip. This tells the assembler to accept +the @samp{mad} and @samp{madu} instruction, and to not schedule @samp{nop} +instructions around accesses to the @samp{HI} and @samp{LO} registers. +@samp{-no-m4650} turns off this option. + +@itemx -m3900 +@itemx -no-m3900 +@itemx -m4100 +@itemx -no-m4100 +For each option @samp{-m@var{nnnn}}, generate code for the MIPS +@sc{r@var{nnnn}} chip. This tells the assembler to accept instructions +specific to that chip, and to schedule for that chip's hazards. + +@item -mcpu=@var{cpu} +Generate code for a particular MIPS cpu. It is exactly equivalent to +@samp{-m@var{cpu}}, except that there are more value of @var{cpu} +understood. Valid @var{cpu} value are: + +@quotation +2000, +3000, +3900, +4000, +4010, +4100, +4111, +4300, +4400, +4600, +4650, +5000, +6000, +8000, +10000 +@end quotation + + +@cindex @code{-nocpp} ignored (MIPS) +@item -nocpp +This option is ignored. It is accepted for command-line compatibility with +other assemblers, which use it to turn off C style preprocessing. With +@sc{gnu} @code{@value{AS}}, there is no need for @samp{-nocpp}, because the +@sc{gnu} assembler itself never runs the C preprocessor. + +@item --trap +@itemx --no-break +@c FIXME! (1) reflect these options (next item too) in option summaries; +@c (2) stop teasing, say _which_ instructions expanded _how_. +@code{@value{AS}} automatically macro expands certain division and +multiplication instructions to check for overflow and division by zero. This +option causes @code{@value{AS}} to generate code to take a trap exception +rather than a break exception when an error is detected. The trap instructions +are only supported at Instruction Set Architecture level 2 and higher. + +@item --break +@itemx --no-trap +Generate code to take a break exception rather than a trap exception when an +error is detected. This is the default. +@end table + +@node MIPS Object +@section MIPS ECOFF object code + +@cindex ECOFF sections +@cindex MIPS ECOFF sections +Assembling for a @sc{mips} @sc{ecoff} target supports some additional sections +besides the usual @code{.text}, @code{.data} and @code{.bss}. The +additional sections are @code{.rdata}, used for read-only data, +@code{.sdata}, used for small data, and @code{.sbss}, used for small +common objects. + +@cindex small objects, MIPS ECOFF +@cindex @code{gp} register, MIPS +When assembling for @sc{ecoff}, the assembler uses the @code{$gp} (@code{$28}) +register to form the address of a ``small object''. Any object in the +@code{.sdata} or @code{.sbss} sections is considered ``small'' in this sense. +For external objects, or for objects in the @code{.bss} section, you can use +the @code{@value{GCC}} @samp{-G} option to control the size of objects addressed via +@code{$gp}; the default value is 8, meaning that a reference to any object +eight bytes or smaller uses @code{$gp}. Passing @samp{-G 0} to +@code{@value{AS}} prevents it from using the @code{$gp} register on the basis +of object size (but the assembler uses @code{$gp} for objects in @code{.sdata} +or @code{sbss} in any case). The size of an object in the @code{.bss} section +is set by the @code{.comm} or @code{.lcomm} directive that defines it. The +size of an external object may be set with the @code{.extern} directive. For +example, @samp{.extern sym,4} declares that the object at @code{sym} is 4 bytes +in length, whie leaving @code{sym} otherwise undefined. + +Using small @sc{ecoff} objects requires linker support, and assumes that the +@code{$gp} register is correctly initialized (normally done automatically by +the startup code). @sc{mips} @sc{ecoff} assembly code must not modify the +@code{$gp} register. + +@node MIPS Stabs +@section Directives for debugging information + +@cindex MIPS debugging directives +@sc{mips} @sc{ecoff} @code{@value{AS}} supports several directives used for +generating debugging information which are not support by traditional @sc{mips} +assemblers. These are @code{.def}, @code{.endef}, @code{.dim}, @code{.file}, +@code{.scl}, @code{.size}, @code{.tag}, @code{.type}, @code{.val}, +@code{.stabd}, @code{.stabn}, and @code{.stabs}. The debugging information +generated by the three @code{.stab} directives can only be read by @sc{gdb}, +not by traditional @sc{mips} debuggers (this enhancement is required to fully +support C++ debugging). These directives are primarily used by compilers, not +assembly language programmers! + +@node MIPS ISA +@section Directives to override the ISA level + +@cindex MIPS ISA override +@kindex @code{.set mips@var{n}} +@sc{gnu} @code{@value{AS}} supports an additional directive to change +the @sc{mips} Instruction Set Architecture level on the fly: @code{.set +mips@var{n}}. @var{n} should be a number from 0 to 4. A value from 1 +to 4 makes the assembler accept instructions for the corresponding +@sc{isa} level, from that point on in the assembly. @code{.set +mips@var{n}} affects not only which instructions are permitted, but also +how certain macros are expanded. @code{.set mips0} restores the +@sc{isa} level to its original level: either the level you selected with +command line options, or the default for your configuration. You can +use this feature to permit specific @sc{r4000} instructions while +assembling in 32 bit mode. Use this directive with care! + +The directive @samp{.set mips16} puts the assembler into MIPS 16 mode, +in which it will assemble instructions for the MIPS 16 processor. Use +@samp{.set nomips16} to return to normal 32 bit mode. + +Traditional @sc{mips} assemblers do not support this directive. + +@node MIPS autoextend +@section Directives for extending MIPS 16 bit instructions + +@kindex @code{.set autoextend} +@kindex @code{.set noautoextend} +By default, MIPS 16 instructions are automatically extended to 32 bits +when necessary. The directive @samp{.set noautoextend} will turn this +off. When @samp{.set noautoextend} is in effect, any 32 bit instruction +must be explicitly extended with the @samp{.e} modifier (e.g., +@samp{li.e $4,1000}). The directive @samp{.set autoextend} may be used +to once again automatically extend instructions when necessary. + +This directive is only meaningful when in MIPS 16 mode. Traditional +@sc{mips} assemblers do not support this directive. + +@node MIPS insn +@section Directive to mark data as an instruction + +@kindex @code{.insn} +The @code{.insn} directive tells @code{@value{AS}} that the following +data is actually instructions. This makes a difference in MIPS 16 mode: +when loading the address of a label which precedes instructions, +@code{@value{AS}} automatically adds 1 to the value, so that jumping to +the loaded address will do the right thing. + +@node MIPS option stack +@section Directives to save and restore options + +@cindex MIPS option stack +@kindex @code{.set push} +@kindex @code{.set pop} +The directives @code{.set push} and @code{.set pop} may be used to save +and restore the current settings for all the options which are +controlled by @code{.set}. The @code{.set push} directive saves the +current settings on a stack. The @code{.set pop} directive pops the +stack and restores the settings. + +These directives can be useful inside an macro which must change an +option such as the ISA level or instruction reordering but does not want +to change the state of the code which invoked the macro. + +Traditional @sc{mips} assemblers do not support these directives. diff --git a/contrib/binutils/include/coff/mips.h b/contrib/binutils/include/coff/mips.h new file mode 100644 index 000000000000..d4665b1f3964 --- /dev/null +++ b/contrib/binutils/include/coff/mips.h @@ -0,0 +1,369 @@ +/* ECOFF support on MIPS machines. + coff/ecoff.h must be included before this file. */ + +/********************** FILE HEADER **********************/ + +struct external_filehdr { + unsigned char f_magic[2]; /* magic number */ + unsigned char f_nscns[2]; /* number of sections */ + unsigned char f_timdat[4]; /* time & date stamp */ + unsigned char f_symptr[4]; /* file pointer to symtab */ + unsigned char f_nsyms[4]; /* number of symtab entries */ + unsigned char f_opthdr[2]; /* sizeof(optional hdr) */ + unsigned char f_flags[2]; /* flags */ +}; + +/* Magic numbers are defined in coff/ecoff.h. */ +#define MIPS_ECOFF_BADMAG(x) (((x).f_magic!=MIPS_MAGIC_1) && \ + ((x).f_magic!=MIPS_MAGIC_LITTLE) &&\ + ((x).f_magic!=MIPS_MAGIC_BIG) && \ + ((x).f_magic!=MIPS_MAGIC_LITTLE2) && \ + ((x).f_magic!=MIPS_MAGIC_BIG2) && \ + ((x).f_magic!=MIPS_MAGIC_LITTLE3) && \ + ((x).f_magic!=MIPS_MAGIC_BIG3)) + +#define FILHDR struct external_filehdr +#define FILHSZ 20 + +/********************** AOUT "OPTIONAL HEADER" **********************/ + + +typedef struct external_aouthdr +{ + unsigned char magic[2]; /* type of file */ + unsigned char vstamp[2]; /* version stamp */ + unsigned char tsize[4]; /* text size in bytes, padded to FW bdry*/ + unsigned char dsize[4]; /* initialized data " " */ + unsigned char bsize[4]; /* uninitialized data " " */ + unsigned char entry[4]; /* entry pt. */ + unsigned char text_start[4]; /* base of text used for this file */ + unsigned char data_start[4]; /* base of data used for this file */ + unsigned char bss_start[4]; /* base of bss used for this file */ + unsigned char gprmask[4]; /* ?? */ + unsigned char cprmask[4][4]; /* ?? */ + unsigned char gp_value[4]; /* value for gp register */ +} AOUTHDR; + +/* compute size of a header */ + +#define AOUTSZ 56 +#define AOUTHDRSZ 56 + +/********************** SECTION HEADER **********************/ + +struct external_scnhdr { + unsigned char s_name[8]; /* section name */ + unsigned char s_paddr[4]; /* physical address, aliased s_nlib */ + unsigned char s_vaddr[4]; /* virtual address */ + unsigned char s_size[4]; /* section size */ + unsigned char s_scnptr[4]; /* file ptr to raw data for section */ + unsigned char s_relptr[4]; /* file ptr to relocation */ + unsigned char s_lnnoptr[4]; /* file ptr to line numbers */ + unsigned char s_nreloc[2]; /* number of relocation entries */ + unsigned char s_nlnno[2]; /* number of line number entries*/ + unsigned char s_flags[4]; /* flags */ +}; + +#define SCNHDR struct external_scnhdr +#define SCNHSZ 40 + +/********************** RELOCATION DIRECTIVES **********************/ + +struct external_reloc { + unsigned char r_vaddr[4]; + unsigned char r_bits[4]; +}; + +#define RELOC struct external_reloc +#define RELSZ 8 + +/* MIPS ECOFF uses a packed 8 byte format for relocs. These constants + are used to unpack the r_bits field. */ + +#define RELOC_BITS0_SYMNDX_SH_LEFT_BIG 16 +#define RELOC_BITS0_SYMNDX_SH_LEFT_LITTLE 0 + +#define RELOC_BITS1_SYMNDX_SH_LEFT_BIG 8 +#define RELOC_BITS1_SYMNDX_SH_LEFT_LITTLE 8 + +#define RELOC_BITS2_SYMNDX_SH_LEFT_BIG 0 +#define RELOC_BITS2_SYMNDX_SH_LEFT_LITTLE 16 + +/* Originally, ECOFF used four bits for the reloc type and had three + reserved bits. Irix 4 added another bit for the reloc type, which + was easy because it was big endian and one of the spare bits became + the new most significant bit. To make this also work for little + endian ECOFF, we need to wrap one of the reserved bits around to + become the most significant bit of the reloc type. */ +#define RELOC_BITS3_TYPE_BIG 0x3E +#define RELOC_BITS3_TYPE_SH_BIG 1 +#define RELOC_BITS3_TYPE_LITTLE 0x78 +#define RELOC_BITS3_TYPE_SH_LITTLE 3 +#define RELOC_BITS3_TYPEHI_LITTLE 0x04 +#define RELOC_BITS3_TYPEHI_SH_LITTLE 2 + +#define RELOC_BITS3_EXTERN_BIG 0x01 +#define RELOC_BITS3_EXTERN_LITTLE 0x80 + +/* The r_type field in a reloc is one of the following values. I + don't know if any other values can appear. These seem to be all + that occur in the Ultrix 4.2 libraries. */ +#define MIPS_R_IGNORE 0 +#define MIPS_R_REFHALF 1 +#define MIPS_R_REFWORD 2 +#define MIPS_R_JMPADDR 3 +#define MIPS_R_REFHI 4 +#define MIPS_R_REFLO 5 +#define MIPS_R_GPREL 6 +#define MIPS_R_LITERAL 7 + +/* These reloc types are a Cygnus extension used when generating + position independent code for embedded systems. The numbers are + taken from Irix 4, but at least for internal relocs Irix 5 does not + give them the same meaning. For an internal reloc the symbol index + of RELHI and RELLO is modified as described below for + MIPS_R_SWITCH. */ +#define MIPS_R_PCREL16 12 +#define MIPS_R_RELHI 13 +#define MIPS_R_RELLO 14 + +/* This reloc type is a Cygnus extension used when generating position + independent code for embedded systems. It is used for an entry in + a switch table, which looks like this: + .word $L3-$LS12 + The object file will contain the correct difference, and does not + require adjustment. However, when the linker is relaxing PC + relative calls, it is possible for $L3 to move farther away. This + reloc always appears in the .text section, and is always against + the .text section. However, the symbol index is not + RELOC_SECTION_TEXT. It is, instead, the distance between this + switch table entry and $LS12. Thus, the original value of $L12 is + vaddr - symndx + and the original value of $L3 is + vaddr - symndx + addend + where addend is the value in the object file. Knowing this, the + linker can know whether the addend in the object file must be + adjusted. */ +#define MIPS_R_SWITCH 22 + +/********************** STABS **********************/ + +#define MIPS_IS_STAB ECOFF_IS_STAB +#define MIPS_MARK_STAB ECOFF_MARK_STAB +#define MIPS_UNMARK_STAB ECOFF_UNMARK_STAB + +/********************** SYMBOLIC INFORMATION **********************/ + +/* Written by John Gilmore. */ + +/* ECOFF uses COFF-like section structures, but its own symbol format. + This file defines the symbol format in fields whose size and alignment + will not vary on different host systems. */ + +/* File header as a set of bytes */ + +struct hdr_ext { + unsigned char h_magic[2]; + unsigned char h_vstamp[2]; + unsigned char h_ilineMax[4]; + unsigned char h_cbLine[4]; + unsigned char h_cbLineOffset[4]; + unsigned char h_idnMax[4]; + unsigned char h_cbDnOffset[4]; + unsigned char h_ipdMax[4]; + unsigned char h_cbPdOffset[4]; + unsigned char h_isymMax[4]; + unsigned char h_cbSymOffset[4]; + unsigned char h_ioptMax[4]; + unsigned char h_cbOptOffset[4]; + unsigned char h_iauxMax[4]; + unsigned char h_cbAuxOffset[4]; + unsigned char h_issMax[4]; + unsigned char h_cbSsOffset[4]; + unsigned char h_issExtMax[4]; + unsigned char h_cbSsExtOffset[4]; + unsigned char h_ifdMax[4]; + unsigned char h_cbFdOffset[4]; + unsigned char h_crfd[4]; + unsigned char h_cbRfdOffset[4]; + unsigned char h_iextMax[4]; + unsigned char h_cbExtOffset[4]; +}; + +/* File descriptor external record */ + +struct fdr_ext { + unsigned char f_adr[4]; + unsigned char f_rss[4]; + unsigned char f_issBase[4]; + unsigned char f_cbSs[4]; + unsigned char f_isymBase[4]; + unsigned char f_csym[4]; + unsigned char f_ilineBase[4]; + unsigned char f_cline[4]; + unsigned char f_ioptBase[4]; + unsigned char f_copt[4]; + unsigned char f_ipdFirst[2]; + unsigned char f_cpd[2]; + unsigned char f_iauxBase[4]; + unsigned char f_caux[4]; + unsigned char f_rfdBase[4]; + unsigned char f_crfd[4]; + unsigned char f_bits1[1]; + unsigned char f_bits2[3]; + unsigned char f_cbLineOffset[4]; + unsigned char f_cbLine[4]; +}; + +#define FDR_BITS1_LANG_BIG 0xF8 +#define FDR_BITS1_LANG_SH_BIG 3 +#define FDR_BITS1_LANG_LITTLE 0x1F +#define FDR_BITS1_LANG_SH_LITTLE 0 + +#define FDR_BITS1_FMERGE_BIG 0x04 +#define FDR_BITS1_FMERGE_LITTLE 0x20 + +#define FDR_BITS1_FREADIN_BIG 0x02 +#define FDR_BITS1_FREADIN_LITTLE 0x40 + +#define FDR_BITS1_FBIGENDIAN_BIG 0x01 +#define FDR_BITS1_FBIGENDIAN_LITTLE 0x80 + +#define FDR_BITS2_GLEVEL_BIG 0xC0 +#define FDR_BITS2_GLEVEL_SH_BIG 6 +#define FDR_BITS2_GLEVEL_LITTLE 0x03 +#define FDR_BITS2_GLEVEL_SH_LITTLE 0 + +/* We ignore the `reserved' field in bits2. */ + +/* Procedure descriptor external record */ + +struct pdr_ext { + unsigned char p_adr[4]; + unsigned char p_isym[4]; + unsigned char p_iline[4]; + unsigned char p_regmask[4]; + unsigned char p_regoffset[4]; + unsigned char p_iopt[4]; + unsigned char p_fregmask[4]; + unsigned char p_fregoffset[4]; + unsigned char p_frameoffset[4]; + unsigned char p_framereg[2]; + unsigned char p_pcreg[2]; + unsigned char p_lnLow[4]; + unsigned char p_lnHigh[4]; + unsigned char p_cbLineOffset[4]; +}; + +/* Runtime procedure table */ + +struct rpdr_ext { + unsigned char p_adr[4]; + unsigned char p_regmask[4]; + unsigned char p_regoffset[4]; + unsigned char p_fregmask[4]; + unsigned char p_fregoffset[4]; + unsigned char p_frameoffset[4]; + unsigned char p_framereg[2]; + unsigned char p_pcreg[2]; + unsigned char p_irpss[4]; + unsigned char p_reserved[4]; + unsigned char p_exception_info[4]; +}; + +/* Line numbers */ + +struct line_ext { + unsigned char l_line[4]; +}; + +/* Symbol external record */ + +struct sym_ext { + unsigned char s_iss[4]; + unsigned char s_value[4]; + unsigned char s_bits1[1]; + unsigned char s_bits2[1]; + unsigned char s_bits3[1]; + unsigned char s_bits4[1]; +}; + +#define SYM_BITS1_ST_BIG 0xFC +#define SYM_BITS1_ST_SH_BIG 2 +#define SYM_BITS1_ST_LITTLE 0x3F +#define SYM_BITS1_ST_SH_LITTLE 0 + +#define SYM_BITS1_SC_BIG 0x03 +#define SYM_BITS1_SC_SH_LEFT_BIG 3 +#define SYM_BITS1_SC_LITTLE 0xC0 +#define SYM_BITS1_SC_SH_LITTLE 6 + +#define SYM_BITS2_SC_BIG 0xE0 +#define SYM_BITS2_SC_SH_BIG 5 +#define SYM_BITS2_SC_LITTLE 0x07 +#define SYM_BITS2_SC_SH_LEFT_LITTLE 2 + +#define SYM_BITS2_RESERVED_BIG 0x10 +#define SYM_BITS2_RESERVED_LITTLE 0x08 + +#define SYM_BITS2_INDEX_BIG 0x0F +#define SYM_BITS2_INDEX_SH_LEFT_BIG 16 +#define SYM_BITS2_INDEX_LITTLE 0xF0 +#define SYM_BITS2_INDEX_SH_LITTLE 4 + +#define SYM_BITS3_INDEX_SH_LEFT_BIG 8 +#define SYM_BITS3_INDEX_SH_LEFT_LITTLE 4 + +#define SYM_BITS4_INDEX_SH_LEFT_BIG 0 +#define SYM_BITS4_INDEX_SH_LEFT_LITTLE 12 + +/* External symbol external record */ + +struct ext_ext { + unsigned char es_bits1[1]; + unsigned char es_bits2[1]; + unsigned char es_ifd[2]; + struct sym_ext es_asym; +}; + +#define EXT_BITS1_JMPTBL_BIG 0x80 +#define EXT_BITS1_JMPTBL_LITTLE 0x01 + +#define EXT_BITS1_COBOL_MAIN_BIG 0x40 +#define EXT_BITS1_COBOL_MAIN_LITTLE 0x02 + +#define EXT_BITS1_WEAKEXT_BIG 0x20 +#define EXT_BITS1_WEAKEXT_LITTLE 0x04 + +/* Dense numbers external record */ + +struct dnr_ext { + unsigned char d_rfd[4]; + unsigned char d_index[4]; +}; + +/* Relative file descriptor */ + +struct rfd_ext { + unsigned char rfd[4]; +}; + +/* Optimizer symbol external record */ + +struct opt_ext { + unsigned char o_bits1[1]; + unsigned char o_bits2[1]; + unsigned char o_bits3[1]; + unsigned char o_bits4[1]; + struct rndx_ext o_rndx; + unsigned char o_offset[4]; +}; + +#define OPT_BITS2_VALUE_SH_LEFT_BIG 16 +#define OPT_BITS2_VALUE_SH_LEFT_LITTLE 0 + +#define OPT_BITS3_VALUE_SH_LEFT_BIG 8 +#define OPT_BITS3_VALUE_SH_LEFT_LITTLE 8 + +#define OPT_BITS4_VALUE_SH_LEFT_BIG 0 +#define OPT_BITS4_VALUE_SH_LEFT_LITTLE 16 diff --git a/contrib/binutils/include/coff/mipspe.h b/contrib/binutils/include/coff/mipspe.h new file mode 100644 index 000000000000..1927d991d3b3 --- /dev/null +++ b/contrib/binutils/include/coff/mipspe.h @@ -0,0 +1,223 @@ +/*** coff information for Windows CE with MIPS VR4111 */ + +/********************** FILE HEADER **********************/ + +struct external_filehdr { + char f_magic[2]; /* magic number */ + char f_nscns[2]; /* number of sections */ + char f_timdat[4]; /* time & date stamp */ + char f_symptr[4]; /* file pointer to symtab */ + char f_nsyms[4]; /* number of symtab entries */ + char f_opthdr[2]; /* sizeof(optional hdr) */ + char f_flags[2]; /* flags */ +}; + + + +#define MIPS_ARCH_MAGIC_WINCE 0x0166 /* Windows CE - little endian */ +#define MIPS_PE_MAGIC 0x010b + +#define MIPSBADMAG(x) \ + ((x).f_magic!=MIPS_ARCH_MAGIC_WINCE) + +#define FILHDR struct external_filehdr +#define FILHSZ 20 + + +/********************** AOUT "OPTIONAL HEADER" **********************/ + + +typedef struct +{ + char magic[2]; /* type of file */ + char vstamp[2]; /* version stamp */ + char tsize[4]; /* text size in bytes, padded to FW bdry*/ + char dsize[4]; /* initialized data " " */ + char bsize[4]; /* uninitialized data " " */ + char entry[4]; /* entry pt. */ + char text_start[4]; /* base of text used for this file */ + char data_start[4]; /* base of data used for this file */ +} +AOUTHDR; + + +#define AOUTHDRSZ 28 +#define AOUTSZ 28 + + + + +/* define some NT default values */ +/* #define NT_IMAGE_BASE 0x400000 moved to internal.h */ +#define NT_SECTION_ALIGNMENT 0x1000 +#define NT_FILE_ALIGNMENT 0x200 +#define NT_DEF_RESERVE 0x100000 +#define NT_DEF_COMMIT 0x1000 + +/********************** SECTION HEADER **********************/ + + +struct external_scnhdr { + char s_name[8]; /* section name */ + char s_paddr[4]; /* physical address, aliased s_nlib */ + char s_vaddr[4]; /* virtual address */ + char s_size[4]; /* section size */ + char s_scnptr[4]; /* file ptr to raw data for section */ + char s_relptr[4]; /* file ptr to relocation */ + char s_lnnoptr[4]; /* file ptr to line numbers */ + char s_nreloc[2]; /* number of relocation entries */ + char s_nlnno[2]; /* number of line number entries*/ + char s_flags[4]; /* flags */ +}; + +/* + * names of "special" sections + */ +#define _TEXT ".text" +#define _DATA ".data" +#define _BSS ".bss" + + +#define SCNHDR struct external_scnhdr +#define SCNHSZ 40 + + +/********************** LINE NUMBERS **********************/ + +/* 1 line number entry for every "breakpointable" source line in a section. + * Line numbers are grouped on a per function basis; first entry in a function + * grouping will have l_lnno = 0 and in place of physical address will be the + * symbol table index of the function name. + */ +struct external_lineno { + union { + char l_symndx[4]; /* function name symbol index, iff l_lnno == 0*/ + char l_paddr[4]; /* (physical) address of line number */ + } l_addr; + char l_lnno[2]; /* line number */ +}; + +#define GET_LINENO_LNNO(abfd, ext) bfd_h_get_16(abfd, (bfd_byte *) (ext->l_lnno)); +#define PUT_LINENO_LNNO(abfd,val, ext) bfd_h_put_16(abfd,val, (bfd_byte *) (ext->l_lnno)); + +#define LINENO struct external_lineno +#define LINESZ 6 + + +/********************** SYMBOLS **********************/ + +#define E_SYMNMLEN 8 /* # characters in a symbol name */ +#define E_FILNMLEN 14 /* # characters in a file name */ +#define E_DIMNUM 4 /* # array dimensions in auxiliary entry */ + +struct external_syment +{ + union { + char e_name[E_SYMNMLEN]; + struct { + char e_zeroes[4]; + char e_offset[4]; + } e; + } e; + char e_value[4]; + char e_scnum[2]; + char e_type[2]; + char e_sclass[1]; + char e_numaux[1]; +}; + + + +#define N_BTMASK (017) +#define N_TMASK (060) +#define N_BTSHFT (4) +#define N_TSHIFT (2) + + +union external_auxent { + struct { + char x_tagndx[4]; /* str, un, or enum tag indx */ + union { + struct { + char x_lnno[2]; /* declaration line number */ + char x_size[2]; /* str/union/array size */ + } x_lnsz; + char x_fsize[4]; /* size of function */ + } x_misc; + union { + struct { /* if ISFCN, tag, or .bb */ + char x_lnnoptr[4]; /* ptr to fcn line # */ + char x_endndx[4]; /* entry ndx past block end */ + } x_fcn; + struct { /* if ISARY, up to 4 dimen. */ + char x_dimen[E_DIMNUM][2]; + } x_ary; + } x_fcnary; + char x_tvndx[2]; /* tv index */ + } x_sym; + + union { + char x_fname[E_FILNMLEN]; + struct { + char x_zeroes[4]; + char x_offset[4]; + } x_n; + } x_file; + + struct { + char x_scnlen[4]; /* section length */ + char x_nreloc[2]; /* # relocation entries */ + char x_nlinno[2]; /* # line numbers */ + char x_checksum[4]; /* section COMDAT checksum */ + char x_associated[2]; /* COMDAT associated section index */ + char x_comdat[1]; /* COMDAT selection number */ + } x_scn; + + struct { + char x_tvfill[4]; /* tv fill value */ + char x_tvlen[2]; /* length of .tv */ + char x_tvran[2][2]; /* tv range */ + } x_tv; /* info about .tv section (in auxent of symbol .tv)) */ + + +}; + +#define SYMENT struct external_syment +#define SYMESZ 18 +#define AUXENT union external_auxent +#define AUXESZ 18 + + + +/********************** RELOCATION DIRECTIVES **********************/ + +/* The external reloc has an offset field, because some of the reloc + types on the h8 don't have room in the instruction for the entire + offset - eg the strange jump and high page addressing modes */ + +struct external_reloc { + char r_vaddr[4]; + char r_symndx[4]; + char r_type[2]; +}; + + +#define RELOC struct external_reloc +#define RELSZ 10 + +/* MIPS PE relocation types. */ + +#define MIPS_R_ABSOLUTE 0 /* ignored */ +#define MIPS_R_REFHALF 1 +#define MIPS_R_REFWORD 2 +#define MIPS_R_JMPADDR 3 +#define MIPS_R_REFHI 4 /* PAIR follows */ +#define MIPS_R_REFLO 5 +#define MIPS_R_GPREL 6 +#define MIPS_R_LITERAL 7 /* same as GPREL */ +#define MIPS_R_SECTION 10 +#define MIPS_R_SECREL 11 +#define MIPS_R_SECRELLO 12 +#define MIPS_R_SECRELHI 13 /* PAIR follows */ +#define MIPS_R_RVA 34 /* 0x22 */ +#define MIPS_R_PAIR 37 /* 0x25 - symndx is really a signed 16-bit addend */ diff --git a/contrib/binutils/install.sh b/contrib/binutils/install.sh new file mode 100755 index 000000000000..4b883b386de2 --- /dev/null +++ b/contrib/binutils/install.sh @@ -0,0 +1,247 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5 (mit/util/scripts/install.sh). +# +# Copyright 1991 by the Massachusetts Institute of Technology +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of M.I.T. not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. M.I.T. makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# This script is compatible with the BSD install script, but was written +# from scratch. +# + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + chmodcmd="" + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/contrib/binutils/ld/Makefile.in b/contrib/binutils/ld/Makefile.in index 1238a4522816..18d79c6e57c2 100644 --- a/contrib/binutils/ld/Makefile.in +++ b/contrib/binutils/ld/Makefile.in @@ -10,7 +10,7 @@ # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. -# $FreeBSD$ +# $FreeBSD: src/contrib/binutils/ld/Makefile.in,v 1.3.6.2 2000/07/07 05:34:11 obrien Exp $ SHELL = @SHELL@ diff --git a/contrib/binutils/ld/configure.host b/contrib/binutils/ld/configure.host index 32c9a81dd289..46623fa0dfaa 100644 --- a/contrib/binutils/ld/configure.host +++ b/contrib/binutils/ld/configure.host @@ -1,4 +1,4 @@ -# $FreeBSD$ +# $FreeBSD: src/contrib/binutils/ld/configure.host,v 1.1.1.2.6.2 2000/07/07 05:34:11 obrien Exp $ # This is the linker host specific file. This is invoked by the # autoconf generated configure script. Putting it in a separate shell diff --git a/contrib/binutils/ld/configure.tgt b/contrib/binutils/ld/configure.tgt index ad04f1d19259..b06c7f24d817 100644 --- a/contrib/binutils/ld/configure.tgt +++ b/contrib/binutils/ld/configure.tgt @@ -1,4 +1,4 @@ -# $FreeBSD$ +# $FreeBSD: src/contrib/binutils/ld/configure.tgt,v 1.4.6.2 2000/07/07 05:34:11 obrien Exp $ # This is the linker target specific file. This is invoked by the # autoconf generated configure script. Putting it in a separate shell diff --git a/contrib/binutils/ld/emulparams/mipsbig.sh b/contrib/binutils/ld/emulparams/mipsbig.sh new file mode 100644 index 000000000000..9fe29538f452 --- /dev/null +++ b/contrib/binutils/ld/emulparams/mipsbig.sh @@ -0,0 +1,6 @@ +SCRIPT_NAME=mips +OUTPUT_FORMAT="ecoff-bigmips" +BIG_OUTPUT_FORMAT="ecoff-bigmips" +LITTLE_OUTPUT_FORMAT="ecoff-littlemips" +TARGET_PAGE_SIZE=0x1000000 +ARCH=mips diff --git a/contrib/binutils/ld/emulparams/mipsbsd.sh b/contrib/binutils/ld/emulparams/mipsbsd.sh new file mode 100644 index 000000000000..e8fb35beca68 --- /dev/null +++ b/contrib/binutils/ld/emulparams/mipsbsd.sh @@ -0,0 +1,7 @@ +SCRIPT_NAME=mipsbsd +OUTPUT_FORMAT="a.out-mips-little" +BIG_OUTPUT_FORMAT="a.out-mips-big" +LITTLE_OUTPUT_FORMAT="a.out-mips-little" +TEXT_START_ADDR=0x1020 +TARGET_PAGE_SIZE=4096 +ARCH=mips diff --git a/contrib/binutils/ld/emulparams/mipsidt.sh b/contrib/binutils/ld/emulparams/mipsidt.sh new file mode 100644 index 000000000000..63176f5fdeae --- /dev/null +++ b/contrib/binutils/ld/emulparams/mipsidt.sh @@ -0,0 +1,11 @@ +SCRIPT_NAME=mips +OUTPUT_FORMAT="ecoff-bigmips" +BIG_OUTPUT_FORMAT="ecoff-bigmips" +LITTLE_OUTPUT_FORMAT="ecoff-littlemips" +TARGET_PAGE_SIZE=0x1000000 +ARCH=mips +ENTRY=start +TEXT_START_ADDR=0xa0012000 +DATA_ADDR=. +TEMPLATE_NAME=mipsecoff +EMBEDDED=yes diff --git a/contrib/binutils/ld/emulparams/mipsidtl.sh b/contrib/binutils/ld/emulparams/mipsidtl.sh new file mode 100644 index 000000000000..02279ded6359 --- /dev/null +++ b/contrib/binutils/ld/emulparams/mipsidtl.sh @@ -0,0 +1,11 @@ +SCRIPT_NAME=mips +OUTPUT_FORMAT="ecoff-littlemips" +BIG_OUTPUT_FORMAT="ecoff-bigmips" +LITTLE_OUTPUT_FORMAT="ecoff-littlemips" +TARGET_PAGE_SIZE=0x1000000 +ARCH=mips +ENTRY=start +TEXT_START_ADDR=0xa0012000 +DATA_ADDR=. +TEMPLATE_NAME=mipsecoff +EMBEDDED=yes diff --git a/contrib/binutils/ld/emulparams/mipslit.sh b/contrib/binutils/ld/emulparams/mipslit.sh new file mode 100644 index 000000000000..acb234464be4 --- /dev/null +++ b/contrib/binutils/ld/emulparams/mipslit.sh @@ -0,0 +1,6 @@ +SCRIPT_NAME=mips +OUTPUT_FORMAT="ecoff-littlemips" +BIG_OUTPUT_FORMAT="ecoff-bigmips" +LITTLE_OUTPUT_FORMAT="ecoff-littlemips" +TARGET_PAGE_SIZE=0x1000000 +ARCH=mips diff --git a/contrib/binutils/ld/emulparams/mipspe.sh b/contrib/binutils/ld/emulparams/mipspe.sh new file mode 100644 index 000000000000..34674526c519 --- /dev/null +++ b/contrib/binutils/ld/emulparams/mipspe.sh @@ -0,0 +1,8 @@ +ARCH=mips +SCRIPT_NAME=pe +OUTPUT_FORMAT="pei-mips" +OUTPUT_ARCH="mips" +RELOCATEABLE_OUTPUT_FORMAT="ecoff-littlemips" +TEMPLATE_NAME=pe +SUBSYSTEM=PE_DEF_SUBSYSTEM +INITIAL_SYMBOL_CHAR=\"_\" diff --git a/contrib/binutils/ld/emultempl/elf32.em b/contrib/binutils/ld/emultempl/elf32.em index a53f3d77f72e..204abdadeef8 100644 --- a/contrib/binutils/ld/emultempl/elf32.em +++ b/contrib/binutils/ld/emultempl/elf32.em @@ -1,4 +1,4 @@ -# $FreeBSD$ +# $FreeBSD: src/contrib/binutils/ld/emultempl/elf32.em,v 1.2.6.2 2000/07/07 05:34:17 obrien Exp $ # This shell script emits a C file. -*- C -*- # It does some substitutions. # This file is now misnamed, because it supports both 32 bit and 64 bit diff --git a/contrib/binutils/ld/emultempl/mipsecoff.em b/contrib/binutils/ld/emultempl/mipsecoff.em new file mode 100644 index 000000000000..4c932a14fad3 --- /dev/null +++ b/contrib/binutils/ld/emultempl/mipsecoff.em @@ -0,0 +1,239 @@ +# This shell script emits a C file. -*- C -*- +# It does some substitutions. +cat >e${EMULATION_NAME}.c <<EOF +/* This file is is generated by a shell script. DO NOT EDIT! */ + +/* Handle embedded relocs for MIPS. + Copyright 1994, 95, 97, 1999 Free Software Foundation, Inc. + Written by Ian Lance Taylor <ian@cygnus.com> based on generic.em. + +This file is part of GLD, the Gnu Linker. + +This program 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 of the License, or +(at your option) any later version. + +This program 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; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#define TARGET_IS_${EMULATION_NAME} + +#include "bfd.h" +#include "sysdep.h" +#include "bfdlink.h" + +#include "ld.h" +#include "ldmain.h" +#include "ldemul.h" +#include "ldfile.h" +#include "ldmisc.h" + +static void gld${EMULATION_NAME}_before_parse PARAMS ((void)); +static void gld${EMULATION_NAME}_after_open PARAMS ((void)); +static void check_sections PARAMS ((bfd *, asection *, PTR)); +static void gld${EMULATION_NAME}_after_allocation PARAMS ((void)); +static char *gld${EMULATION_NAME}_get_script PARAMS ((int *isfile)); + +static void +gld${EMULATION_NAME}_before_parse() +{ +#ifndef TARGET_ /* I.e., if not generic. */ + ldfile_output_architecture = bfd_arch_${ARCH}; +#endif /* not TARGET_ */ +} + +/* This function is run after all the input files have been opened. + We create a .rel.sdata section for each input file with a non zero + .sdata section. The BFD backend will fill in these sections with + magic numbers which can be used to relocate the data section at run + time. This will only do the right thing if all the input files + have been compiled using -membedded-pic. */ + +static void +gld${EMULATION_NAME}_after_open () +{ + bfd *abfd; + + if (! command_line.embedded_relocs + || link_info.relocateable) + return; + + for (abfd = link_info.input_bfds; abfd != NULL; abfd = abfd->link_next) + { + asection *datasec; + + datasec = bfd_get_section_by_name (abfd, ".sdata"); + + /* Note that we assume that the reloc_count field has already + been set up. We could call bfd_get_reloc_upper_bound, but + that returns the size of a memory buffer rather than a reloc + count. We do not want to call bfd_canonicalize_reloc, + because although it would always work it would force us to + read in the relocs into BFD canonical form, which would waste + a significant amount of time and memory. */ + if (datasec != NULL && datasec->reloc_count > 0) + { + asection *relsec; + + relsec = bfd_make_section (abfd, ".rel.sdata"); + if (relsec == NULL + || ! bfd_set_section_flags (abfd, relsec, + (SEC_ALLOC + | SEC_LOAD + | SEC_HAS_CONTENTS + | SEC_IN_MEMORY)) + || ! bfd_set_section_alignment (abfd, relsec, 2) + || ! bfd_set_section_size (abfd, relsec, + datasec->reloc_count * 4)) + einfo ("%F%B: can not create .rel.sdata section: %E\n"); + } + + /* Double check that all other data sections are empty, as is + required for embedded PIC code. */ + bfd_map_over_sections (abfd, check_sections, (PTR) datasec); + } +} + +/* Check that of the data sections, only the .sdata section has + relocs. This is called via bfd_map_over_sections. */ + +static void +check_sections (abfd, sec, sdatasec) + bfd *abfd; + asection *sec; + PTR sdatasec; +{ + if ((bfd_get_section_flags (abfd, sec) & SEC_CODE) == 0 + && sec != (asection *) sdatasec + && sec->reloc_count != 0) + einfo ("%P%X: section %s has relocs; can not use --embedded-relocs\n", + abfd, bfd_get_section_name (abfd, sec)); +} + +/* This function is called after the section sizes and offsets have + been set. If we are generating embedded relocs, it calls a special + BFD backend routine to do the work. */ + +static void +gld${EMULATION_NAME}_after_allocation () +{ + bfd *abfd; + + if (! command_line.embedded_relocs + || link_info.relocateable) + return; + + for (abfd = link_info.input_bfds; abfd != NULL; abfd = abfd->link_next) + { + asection *datasec, *relsec; + char *errmsg; + + datasec = bfd_get_section_by_name (abfd, ".sdata"); + + if (datasec == NULL || datasec->reloc_count == 0) + continue; + + relsec = bfd_get_section_by_name (abfd, ".rel.sdata"); + ASSERT (relsec != NULL); + + if (! bfd_mips_ecoff_create_embedded_relocs (abfd, &link_info, + datasec, relsec, + &errmsg)) + { + if (errmsg == NULL) + einfo ("%B%X: can not create runtime reloc information: %E\n", + abfd); + else + einfo ("%X%B: can not create runtime reloc information: %s\n", + abfd, errmsg); + } + } +} + +static char * +gld${EMULATION_NAME}_get_script(isfile) + int *isfile; +EOF + +if test -n "$COMPILE_IN" +then +# Scripts compiled in. + +# sed commands to quote an ld script as a C string. +sc="-f stringify.sed" + +cat >>e${EMULATION_NAME}.c <<EOF +{ + *isfile = 0; + + if (link_info.relocateable == true && config.build_constructors == true) + return +EOF +sed $sc ldscripts/${EMULATION_NAME}.xu >> e${EMULATION_NAME}.c +echo ' ; else if (link_info.relocateable == true) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xr >> e${EMULATION_NAME}.c +echo ' ; else if (!config.text_read_only) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xbn >> e${EMULATION_NAME}.c +echo ' ; else if (!config.magic_demand_paged) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xn >> e${EMULATION_NAME}.c +echo ' ; else return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.x >> e${EMULATION_NAME}.c +echo '; }' >> e${EMULATION_NAME}.c + +else +# Scripts read from the filesystem. + +cat >>e${EMULATION_NAME}.c <<EOF +{ + *isfile = 1; + + if (link_info.relocateable == true && config.build_constructors == true) + return "ldscripts/${EMULATION_NAME}.xu"; + else if (link_info.relocateable == true) + return "ldscripts/${EMULATION_NAME}.xr"; + else if (!config.text_read_only) + return "ldscripts/${EMULATION_NAME}.xbn"; + else if (!config.magic_demand_paged) + return "ldscripts/${EMULATION_NAME}.xn"; + else + return "ldscripts/${EMULATION_NAME}.x"; +} +EOF + +fi + +cat >>e${EMULATION_NAME}.c <<EOF + +struct ld_emulation_xfer_struct ld_${EMULATION_NAME}_emulation = +{ + gld${EMULATION_NAME}_before_parse, + syslib_default, + hll_default, + after_parse_default, + gld${EMULATION_NAME}_after_open, + gld${EMULATION_NAME}_after_allocation, + set_output_arch_default, + ldemul_default_target, + before_allocation_default, + gld${EMULATION_NAME}_get_script, + "${EMULATION_NAME}", + "${OUTPUT_FORMAT}", + NULL, /* finish */ + NULL, /* create output section statements */ + NULL, /* open dynamic archive */ + NULL, /* place orphan */ + NULL, /* set symbols */ + NULL, /* parse args */ + NULL, /* unrecognized file */ + NULL, /* list options */ + NULL, /* recognized file */ + NULL /* find_potential_libraries */ +}; +EOF diff --git a/contrib/binutils/ld/ld.1 b/contrib/binutils/ld/ld.1 index 6967a6a35705..536a035dffe9 100644 --- a/contrib/binutils/ld/ld.1 +++ b/contrib/binutils/ld/ld.1 @@ -1,6 +1,6 @@ .\" Copyright (c) 1991, 92, 93, 94, 95, 96, 97, 98, 1999 Free Software Foundation .\" See section COPYING for conditions for redistribution -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/binutils/ld/ld.1,v 1.2.2.2 2000/07/07 05:34:12 obrien Exp $ .TH ld 1 "17 August 1992" "cygnus support" "GNU Development Tools" .de BP .sp diff --git a/contrib/binutils/ld/scripttempl/mips.sc b/contrib/binutils/ld/scripttempl/mips.sc new file mode 100644 index 000000000000..d60aeedfeebc --- /dev/null +++ b/contrib/binutils/ld/scripttempl/mips.sc @@ -0,0 +1,72 @@ +# Linker script for MIPS systems. +# Ian Lance Taylor <ian@cygnus.com>. +# These variables may be overridden by the emulation file. The +# defaults are appropriate for a DECstation running Ultrix. +test -z "$ENTRY" && ENTRY=__start + +if [ -z "$EMBEDDED" ]; then + test -z "$TEXT_START_ADDR" && TEXT_START_ADDR="0x400000 + SIZEOF_HEADERS" +else + test -z "$TEXT_START_ADDR" && TEXT_START_ADDR="0x400000" +fi +if test "x$LD_FLAG" = "xn" -o "x$LD_FLAG" = "xN"; then + DATA_ADDR=. +else + test -z "$DATA_ADDR" && DATA_ADDR=0x10000000 +fi +cat <<EOF +OUTPUT_FORMAT("${OUTPUT_FORMAT}", "${BIG_OUTPUT_FORMAT}", + "${LITTLE_OUTPUT_FORMAT}") +${LIB_SEARCH_DIRS} + +ENTRY(${ENTRY}) + +SECTIONS +{ + ${RELOCATING+. = ${TEXT_START_ADDR};} + .text : { + ${RELOCATING+ _ftext = . }; + *(.init) + ${RELOCATING+ eprol = .}; + *(.text) + ${RELOCATING+PROVIDE (__runtime_reloc_start = .);} + *(.rel.sdata) + ${RELOCATING+PROVIDE (__runtime_reloc_stop = .);} + *(.fini) + ${RELOCATING+ etext = .}; + ${RELOCATING+ _etext = .}; + } + ${RELOCATING+. = ${DATA_ADDR};} + .rdata : { + *(.rdata) + } + ${RELOCATING+ _fdata = ALIGN(16);} + .data : { + *(.data) + ${CONSTRUCTING+CONSTRUCTORS} + } + ${RELOCATING+ _gp = ALIGN(16) + 0x8000;} + .lit8 : { + *(.lit8) + } + .lit4 : { + *(.lit4) + } + .sdata : { + *(.sdata) + } + ${RELOCATING+ edata = .;} + ${RELOCATING+ _edata = .;} + ${RELOCATING+ _fbss = .;} + .sbss : { + *(.sbss) + *(.scommon) + } + .bss : { + *(.bss) + *(COMMON) + } + ${RELOCATING+ end = .;} + ${RELOCATING+ _end = .;} +} +EOF diff --git a/contrib/binutils/ld/scripttempl/mipsbsd.sc b/contrib/binutils/ld/scripttempl/mipsbsd.sc new file mode 100644 index 000000000000..b222b3356003 --- /dev/null +++ b/contrib/binutils/ld/scripttempl/mipsbsd.sc @@ -0,0 +1,30 @@ +cat <<EOF +OUTPUT_FORMAT("${OUTPUT_FORMAT}", "${BIG_OUTPUT_FORMAT}", + "${LITTLE_OUTPUT_FORMAT}") +OUTPUT_ARCH(${ARCH}) + +${RELOCATING+${LIB_SEARCH_DIRS}} +SECTIONS +{ + ${RELOCATING+. = ${TEXT_START_ADDR};} + .text : + { + CREATE_OBJECT_SYMBOLS + *(.text) + ${RELOCATING+etext = ${DATA_ALIGNMENT};} + } + ${RELOCATING+. = ${DATA_ALIGNMENT};} + .data : + { + *(.data) + ${CONSTRUCTING+CONSTRUCTORS} + ${RELOCATING+edata = .;} + } + .bss : + { + *(.bss) + *(COMMON) + ${RELOCATING+end = . }; + } +} +EOF diff --git a/contrib/binutils/opcodes/mips-dis.c b/contrib/binutils/opcodes/mips-dis.c new file mode 100644 index 000000000000..9ab9f98690c5 --- /dev/null +++ b/contrib/binutils/opcodes/mips-dis.c @@ -0,0 +1,1047 @@ +/* Print mips instructions for GDB, the GNU debugger, or for objdump. + Copyright (c) 1989, 91-97, 1998 Free Software Foundation, Inc. + Contributed by Nobuyuki Hikichi(hikichi@sra.co.jp). + +This file is part of GDB, GAS, and the GNU binutils. + +This program 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 of the License, or +(at your option) any later version. + +This program 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; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "sysdep.h" +#include "dis-asm.h" +#include "opcode/mips.h" +#include "opintl.h" + +/* FIXME: These are needed to figure out if the code is mips16 or + not. The low bit of the address is often a good indicator. No + symbol table is available when this code runs out in an embedded + system as when it is used for disassembler support in a monitor. */ + +#if !defined(EMBEDDED_ENV) +#define SYMTAB_AVAILABLE 1 +#include "elf-bfd.h" +#include "elf/mips.h" +#endif + +static int print_insn_mips16 PARAMS ((bfd_vma, struct disassemble_info *)); +static void print_mips16_insn_arg + PARAMS ((int, const struct mips_opcode *, int, boolean, int, bfd_vma, + struct disassemble_info *)); + +/* Mips instructions are never longer than this many bytes. */ +#define MAXLEN 4 + +static void print_insn_arg PARAMS ((const char *, unsigned long, bfd_vma, + struct disassemble_info *)); +static int _print_insn_mips PARAMS ((bfd_vma, unsigned long int, + struct disassemble_info *)); + + +/* FIXME: This should be shared with gdb somehow. */ +#define REGISTER_NAMES \ + { "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", \ + "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", \ + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", \ + "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra", \ + "sr", "lo", "hi", "bad", "cause","pc", \ + "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",\ + "fsr", "fir", "fp", "inx", "rand", "tlblo","ctxt", "tlbhi",\ + "epc", "prid"\ + } + +static CONST char * CONST reg_names[] = REGISTER_NAMES; + +/* The mips16 register names. */ +static const char * const mips16_reg_names[] = +{ + "s0", "s1", "v0", "v1", "a0", "a1", "a2", "a3" +}; + +/* subroutine */ +static void +print_insn_arg (d, l, pc, info) + const char *d; + register unsigned long int l; + bfd_vma pc; + struct disassemble_info *info; +{ + int delta; + + switch (*d) + { + case ',': + case '(': + case ')': + (*info->fprintf_func) (info->stream, "%c", *d); + break; + + case 's': + case 'b': + case 'r': + case 'v': + (*info->fprintf_func) (info->stream, "$%s", + reg_names[(l >> OP_SH_RS) & OP_MASK_RS]); + break; + + case 't': + case 'w': + (*info->fprintf_func) (info->stream, "$%s", + reg_names[(l >> OP_SH_RT) & OP_MASK_RT]); + break; + + case 'i': + case 'u': + (*info->fprintf_func) (info->stream, "0x%x", + (l >> OP_SH_IMMEDIATE) & OP_MASK_IMMEDIATE); + break; + + case 'j': /* same as i, but sign-extended */ + case 'o': + delta = (l >> OP_SH_DELTA) & OP_MASK_DELTA; + if (delta & 0x8000) + delta |= ~0xffff; + (*info->fprintf_func) (info->stream, "%d", + delta); + break; + + case 'h': + (*info->fprintf_func) (info->stream, "0x%x", + (unsigned int) ((l >> OP_SH_PREFX) + & OP_MASK_PREFX)); + break; + + case 'k': + (*info->fprintf_func) (info->stream, "0x%x", + (unsigned int) ((l >> OP_SH_CACHE) + & OP_MASK_CACHE)); + break; + + case 'a': + (*info->print_address_func) + (((pc & 0xF0000000) | (((l >> OP_SH_TARGET) & OP_MASK_TARGET) << 2)), + info); + break; + + case 'p': + /* sign extend the displacement */ + delta = (l >> OP_SH_DELTA) & OP_MASK_DELTA; + if (delta & 0x8000) + delta |= ~0xffff; + (*info->print_address_func) + ((delta << 2) + pc + 4, + info); + break; + + case 'd': + (*info->fprintf_func) (info->stream, "$%s", + reg_names[(l >> OP_SH_RD) & OP_MASK_RD]); + break; + + case 'z': + (*info->fprintf_func) (info->stream, "$%s", reg_names[0]); + break; + + case '<': + (*info->fprintf_func) (info->stream, "0x%x", + (l >> OP_SH_SHAMT) & OP_MASK_SHAMT); + break; + + case 'c': + (*info->fprintf_func) (info->stream, "0x%x", + (l >> OP_SH_CODE) & OP_MASK_CODE); + break; + + + case 'q': + (*info->fprintf_func) (info->stream, "0x%x", + (l >> OP_SH_CODE2) & OP_MASK_CODE2); + break; + + case 'C': + (*info->fprintf_func) (info->stream, "0x%x", + (l >> OP_SH_COPZ) & OP_MASK_COPZ); + break; + + case 'B': + (*info->fprintf_func) (info->stream, "0x%x", + (l >> OP_SH_SYSCALL) & OP_MASK_SYSCALL); + break; + + case 'S': + case 'V': + (*info->fprintf_func) (info->stream, "$f%d", + (l >> OP_SH_FS) & OP_MASK_FS); + break; + + + case 'T': + case 'W': + (*info->fprintf_func) (info->stream, "$f%d", + (l >> OP_SH_FT) & OP_MASK_FT); + break; + + case 'D': + (*info->fprintf_func) (info->stream, "$f%d", + (l >> OP_SH_FD) & OP_MASK_FD); + break; + + case 'R': + (*info->fprintf_func) (info->stream, "$f%d", + (l >> OP_SH_FR) & OP_MASK_FR); + break; + + case 'E': + (*info->fprintf_func) (info->stream, "$%d", + (l >> OP_SH_RT) & OP_MASK_RT); + break; + + case 'G': + (*info->fprintf_func) (info->stream, "$%d", + (l >> OP_SH_RD) & OP_MASK_RD); + break; + + case 'N': + (*info->fprintf_func) (info->stream, "$fcc%d", + (l >> OP_SH_BCC) & OP_MASK_BCC); + break; + + case 'M': + (*info->fprintf_func) (info->stream, "$fcc%d", + (l >> OP_SH_CCC) & OP_MASK_CCC); + break; + + case 'P': + (*info->fprintf_func) (info->stream, "%d", + (l >> OP_SH_PERFREG) & OP_MASK_PERFREG); + break; + + + default: + /* xgettext:c-format */ + (*info->fprintf_func) (info->stream, + _("# internal error, undefined modifier(%c)"), + *d); + break; + } +} + +#if SYMTAB_AVAILABLE + +/* Figure out the MIPS ISA and CPU based on the machine number. + FIXME: What does this have to do with SYMTAB_AVAILABLE? */ + +static void +set_mips_isa_type (mach, isa, cputype) + int mach; + int *isa; + int *cputype; +{ + int target_processor = 0; + int mips_isa = 0; + + switch (mach) + { + case bfd_mach_mips3000: + target_processor = 3000; + mips_isa = 1; + break; + case bfd_mach_mips3900: + target_processor = 3900; + mips_isa = 1; + break; + case bfd_mach_mips4000: + target_processor = 4000; + mips_isa = 3; + break; + case bfd_mach_mips4010: + target_processor = 4010; + mips_isa = 2; + break; + case bfd_mach_mips4100: + target_processor = 4100; + mips_isa = 3; + break; + case bfd_mach_mips4111: + target_processor = 4100; + mips_isa = 3; + break; + case bfd_mach_mips4300: + target_processor = 4300; + mips_isa = 3; + break; + case bfd_mach_mips4400: + target_processor = 4400; + mips_isa = 3; + break; + case bfd_mach_mips4600: + target_processor = 4600; + mips_isa = 3; + break; + case bfd_mach_mips4650: + target_processor = 4650; + mips_isa = 3; + break; + case bfd_mach_mips5000: + target_processor = 5000; + mips_isa = 4; + break; + case bfd_mach_mips6000: + target_processor = 6000; + mips_isa = 2; + break; + case bfd_mach_mips8000: + target_processor = 8000; + mips_isa = 4; + break; + case bfd_mach_mips10000: + target_processor = 10000; + mips_isa = 4; + break; + case bfd_mach_mips16: + target_processor = 16; + mips_isa = 3; + break; + default: + target_processor = 3000; + mips_isa = 3; + break; + + } + + *isa = mips_isa; + *cputype = target_processor; +} + +#endif /* SYMTAB_AVAILABLE */ + +/* Print the mips instruction at address MEMADDR in debugged memory, + on using INFO. Returns length of the instruction, in bytes, which is + always 4. BIGENDIAN must be 1 if this is big-endian code, 0 if + this is little-endian code. */ + +static int +_print_insn_mips (memaddr, word, info) + bfd_vma memaddr; + unsigned long int word; + struct disassemble_info *info; +{ + register const struct mips_opcode *op; + int target_processor, mips_isa; + static boolean init = 0; + static const struct mips_opcode *mips_hash[OP_MASK_OP + 1]; + + /* Build a hash table to shorten the search time. */ + if (! init) + { + unsigned int i; + + for (i = 0; i <= OP_MASK_OP; i++) + { + for (op = mips_opcodes; op < &mips_opcodes[NUMOPCODES]; op++) + { + if (op->pinfo == INSN_MACRO) + continue; + if (i == ((op->match >> OP_SH_OP) & OP_MASK_OP)) + { + mips_hash[i] = op; + break; + } + } + } + + init = 1; + } + +#if ! SYMTAB_AVAILABLE + /* This is running out on a target machine, not in a host tool. + FIXME: Where does mips_target_info come from? */ + target_processor = mips_target_info.processor; + mips_isa = mips_target_info.isa; +#else + set_mips_isa_type (info->mach, &mips_isa, &target_processor); +#endif + + info->bytes_per_chunk = 4; + info->display_endian = info->endian; + + op = mips_hash[(word >> OP_SH_OP) & OP_MASK_OP]; + if (op != NULL) + { + for (; op < &mips_opcodes[NUMOPCODES]; op++) + { + if (op->pinfo != INSN_MACRO && (word & op->mask) == op->match) + { + register const char *d; + + if (! OPCODE_IS_MEMBER (op, mips_isa, target_processor, 0)) + continue; + + (*info->fprintf_func) (info->stream, "%s", op->name); + + d = op->args; + if (d != NULL && *d != '\0') + { + (*info->fprintf_func) (info->stream, "\t"); + for (; *d != '\0'; d++) + print_insn_arg (d, word, memaddr, info); + } + + return 4; + } + } + } + + /* Handle undefined instructions. */ + (*info->fprintf_func) (info->stream, "0x%x", word); + return 4; +} + + +/* In an environment where we do not know the symbol type of the + instruction we are forced to assume that the low order bit of the + instructions' address may mark it as a mips16 instruction. If we + are single stepping, or the pc is within the disassembled function, + this works. Otherwise, we need a clue. Sometimes. */ + +int +print_insn_big_mips (memaddr, info) + bfd_vma memaddr; + struct disassemble_info *info; +{ + bfd_byte buffer[4]; + int status; + +#if 1 + /* FIXME: If odd address, this is CLEARLY a mips 16 instruction. */ + /* Only a few tools will work this way. */ + if (memaddr & 0x01) + return print_insn_mips16 (memaddr, info); +#endif + +#if SYMTAB_AVAILABLE + if (info->mach == 16 + || (info->flavour == bfd_target_elf_flavour + && info->symbols != NULL + && ((*(elf_symbol_type **) info->symbols)->internal_elf_sym.st_other + == STO_MIPS16))) + return print_insn_mips16 (memaddr, info); +#endif + + status = (*info->read_memory_func) (memaddr, buffer, 4, info); + if (status == 0) + return _print_insn_mips (memaddr, (unsigned long) bfd_getb32 (buffer), + info); + else + { + (*info->memory_error_func) (status, memaddr, info); + return -1; + } +} + +int +print_insn_little_mips (memaddr, info) + bfd_vma memaddr; + struct disassemble_info *info; +{ + bfd_byte buffer[4]; + int status; + + +#if 1 + if (memaddr & 0x01) + return print_insn_mips16 (memaddr, info); +#endif + +#if SYMTAB_AVAILABLE + if (info->mach == 16 + || (info->flavour == bfd_target_elf_flavour + && info->symbols != NULL + && ((*(elf_symbol_type **) info->symbols)->internal_elf_sym.st_other + == STO_MIPS16))) + return print_insn_mips16 (memaddr, info); +#endif + + status = (*info->read_memory_func) (memaddr, buffer, 4, info); + if (status == 0) + return _print_insn_mips (memaddr, (unsigned long) bfd_getl32 (buffer), + info); + else + { + (*info->memory_error_func) (status, memaddr, info); + return -1; + } +} + +/* Disassemble mips16 instructions. */ + +static int +print_insn_mips16 (memaddr, info) + bfd_vma memaddr; + struct disassemble_info *info; +{ + int status; + bfd_byte buffer[2]; + int length; + int insn; + boolean use_extend; + int extend = 0; + const struct mips_opcode *op, *opend; + + info->bytes_per_chunk = 2; + info->display_endian = info->endian; + + info->insn_info_valid = 1; + info->branch_delay_insns = 0; + info->data_size = 0; + info->insn_type = dis_nonbranch; + info->target = 0; + info->target2 = 0; + + status = (*info->read_memory_func) (memaddr, buffer, 2, info); + if (status != 0) + { + (*info->memory_error_func) (status, memaddr, info); + return -1; + } + + length = 2; + + if (info->endian == BFD_ENDIAN_BIG) + insn = bfd_getb16 (buffer); + else + insn = bfd_getl16 (buffer); + + /* Handle the extend opcode specially. */ + use_extend = false; + if ((insn & 0xf800) == 0xf000) + { + use_extend = true; + extend = insn & 0x7ff; + + memaddr += 2; + + status = (*info->read_memory_func) (memaddr, buffer, 2, info); + if (status != 0) + { + (*info->fprintf_func) (info->stream, "extend 0x%x", + (unsigned int) extend); + (*info->memory_error_func) (status, memaddr, info); + return -1; + } + + if (info->endian == BFD_ENDIAN_BIG) + insn = bfd_getb16 (buffer); + else + insn = bfd_getl16 (buffer); + + /* Check for an extend opcode followed by an extend opcode. */ + if ((insn & 0xf800) == 0xf000) + { + (*info->fprintf_func) (info->stream, "extend 0x%x", + (unsigned int) extend); + info->insn_type = dis_noninsn; + return length; + } + + length += 2; + } + + /* FIXME: Should probably use a hash table on the major opcode here. */ + + opend = mips16_opcodes + bfd_mips16_num_opcodes; + for (op = mips16_opcodes; op < opend; op++) + { + if (op->pinfo != INSN_MACRO && (insn & op->mask) == op->match) + { + const char *s; + + if (strchr (op->args, 'a') != NULL) + { + if (use_extend) + { + (*info->fprintf_func) (info->stream, "extend 0x%x", + (unsigned int) extend); + info->insn_type = dis_noninsn; + return length - 2; + } + + use_extend = false; + + memaddr += 2; + + status = (*info->read_memory_func) (memaddr, buffer, 2, + info); + if (status == 0) + { + use_extend = true; + if (info->endian == BFD_ENDIAN_BIG) + extend = bfd_getb16 (buffer); + else + extend = bfd_getl16 (buffer); + length += 2; + } + } + + (*info->fprintf_func) (info->stream, "%s", op->name); + if (op->args[0] != '\0') + (*info->fprintf_func) (info->stream, "\t"); + + for (s = op->args; *s != '\0'; s++) + { + if (*s == ',' + && s[1] == 'w' + && (((insn >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX) + == ((insn >> MIPS16OP_SH_RY) & MIPS16OP_MASK_RY))) + { + /* Skip the register and the comma. */ + ++s; + continue; + } + if (*s == ',' + && s[1] == 'v' + && (((insn >> MIPS16OP_SH_RZ) & MIPS16OP_MASK_RZ) + == ((insn >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX))) + { + /* Skip the register and the comma. */ + ++s; + continue; + } + print_mips16_insn_arg (*s, op, insn, use_extend, extend, memaddr, + info); + } + + if ((op->pinfo & INSN_UNCOND_BRANCH_DELAY) != 0) + { + info->branch_delay_insns = 1; + if (info->insn_type != dis_jsr) + info->insn_type = dis_branch; + } + + return length; + } + } + + if (use_extend) + (*info->fprintf_func) (info->stream, "0x%x", extend | 0xf000); + (*info->fprintf_func) (info->stream, "0x%x", insn); + info->insn_type = dis_noninsn; + + return length; +} + +/* Disassemble an operand for a mips16 instruction. */ + +static void +print_mips16_insn_arg (type, op, l, use_extend, extend, memaddr, info) + int type; + const struct mips_opcode *op; + int l; + boolean use_extend; + int extend; + bfd_vma memaddr; + struct disassemble_info *info; +{ + switch (type) + { + case ',': + case '(': + case ')': + (*info->fprintf_func) (info->stream, "%c", type); + break; + + case 'y': + case 'w': + (*info->fprintf_func) (info->stream, "$%s", + mips16_reg_names[((l >> MIPS16OP_SH_RY) + & MIPS16OP_MASK_RY)]); + break; + + case 'x': + case 'v': + (*info->fprintf_func) (info->stream, "$%s", + mips16_reg_names[((l >> MIPS16OP_SH_RX) + & MIPS16OP_MASK_RX)]); + break; + + case 'z': + (*info->fprintf_func) (info->stream, "$%s", + mips16_reg_names[((l >> MIPS16OP_SH_RZ) + & MIPS16OP_MASK_RZ)]); + break; + + case 'Z': + (*info->fprintf_func) (info->stream, "$%s", + mips16_reg_names[((l >> MIPS16OP_SH_MOVE32Z) + & MIPS16OP_MASK_MOVE32Z)]); + break; + + case '0': + (*info->fprintf_func) (info->stream, "$%s", reg_names[0]); + break; + + case 'S': + (*info->fprintf_func) (info->stream, "$%s", reg_names[29]); + break; + + case 'P': + (*info->fprintf_func) (info->stream, "$pc"); + break; + + case 'R': + (*info->fprintf_func) (info->stream, "$%s", reg_names[31]); + break; + + case 'X': + (*info->fprintf_func) (info->stream, "$%s", + reg_names[((l >> MIPS16OP_SH_REGR32) + & MIPS16OP_MASK_REGR32)]); + break; + + case 'Y': + (*info->fprintf_func) (info->stream, "$%s", + reg_names[MIPS16OP_EXTRACT_REG32R (l)]); + break; + + case '<': + case '>': + case '[': + case ']': + case '4': + case '5': + case 'H': + case 'W': + case 'D': + case 'j': + case '6': + case '8': + case 'V': + case 'C': + case 'U': + case 'k': + case 'K': + case 'p': + case 'q': + case 'A': + case 'B': + case 'E': + { + int immed, nbits, shift, signedp, extbits, pcrel, extu, branch; + + shift = 0; + signedp = 0; + extbits = 16; + pcrel = 0; + extu = 0; + branch = 0; + switch (type) + { + case '<': + nbits = 3; + immed = (l >> MIPS16OP_SH_RZ) & MIPS16OP_MASK_RZ; + extbits = 5; + extu = 1; + break; + case '>': + nbits = 3; + immed = (l >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX; + extbits = 5; + extu = 1; + break; + case '[': + nbits = 3; + immed = (l >> MIPS16OP_SH_RZ) & MIPS16OP_MASK_RZ; + extbits = 6; + extu = 1; + break; + case ']': + nbits = 3; + immed = (l >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX; + extbits = 6; + extu = 1; + break; + case '4': + nbits = 4; + immed = (l >> MIPS16OP_SH_IMM4) & MIPS16OP_MASK_IMM4; + signedp = 1; + extbits = 15; + break; + case '5': + nbits = 5; + immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5; + info->insn_type = dis_dref; + info->data_size = 1; + break; + case 'H': + nbits = 5; + shift = 1; + immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5; + info->insn_type = dis_dref; + info->data_size = 2; + break; + case 'W': + nbits = 5; + shift = 2; + immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5; + if ((op->pinfo & MIPS16_INSN_READ_PC) == 0 + && (op->pinfo & MIPS16_INSN_READ_SP) == 0) + { + info->insn_type = dis_dref; + info->data_size = 4; + } + break; + case 'D': + nbits = 5; + shift = 3; + immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5; + info->insn_type = dis_dref; + info->data_size = 8; + break; + case 'j': + nbits = 5; + immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5; + signedp = 1; + break; + case '6': + nbits = 6; + immed = (l >> MIPS16OP_SH_IMM6) & MIPS16OP_MASK_IMM6; + break; + case '8': + nbits = 8; + immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8; + break; + case 'V': + nbits = 8; + shift = 2; + immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8; + /* FIXME: This might be lw, or it might be addiu to $sp or + $pc. We assume it's load. */ + info->insn_type = dis_dref; + info->data_size = 4; + break; + case 'C': + nbits = 8; + shift = 3; + immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8; + info->insn_type = dis_dref; + info->data_size = 8; + break; + case 'U': + nbits = 8; + immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8; + extu = 1; + break; + case 'k': + nbits = 8; + immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8; + signedp = 1; + break; + case 'K': + nbits = 8; + shift = 3; + immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8; + signedp = 1; + break; + case 'p': + nbits = 8; + immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8; + signedp = 1; + pcrel = 1; + branch = 1; + info->insn_type = dis_condbranch; + break; + case 'q': + nbits = 11; + immed = (l >> MIPS16OP_SH_IMM11) & MIPS16OP_MASK_IMM11; + signedp = 1; + pcrel = 1; + branch = 1; + info->insn_type = dis_branch; + break; + case 'A': + nbits = 8; + shift = 2; + immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8; + pcrel = 1; + /* FIXME: This can be lw or la. We assume it is lw. */ + info->insn_type = dis_dref; + info->data_size = 4; + break; + case 'B': + nbits = 5; + shift = 3; + immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5; + pcrel = 1; + info->insn_type = dis_dref; + info->data_size = 8; + break; + case 'E': + nbits = 5; + shift = 2; + immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5; + pcrel = 1; + break; + default: + abort (); + } + + if (! use_extend) + { + if (signedp && immed >= (1 << (nbits - 1))) + immed -= 1 << nbits; + immed <<= shift; + if ((type == '<' || type == '>' || type == '[' || type == ']') + && immed == 0) + immed = 8; + } + else + { + if (extbits == 16) + immed |= ((extend & 0x1f) << 11) | (extend & 0x7e0); + else if (extbits == 15) + immed |= ((extend & 0xf) << 11) | (extend & 0x7f0); + else + immed = ((extend >> 6) & 0x1f) | (extend & 0x20); + immed &= (1 << extbits) - 1; + if (! extu && immed >= (1 << (extbits - 1))) + immed -= 1 << extbits; + } + + if (! pcrel) + (*info->fprintf_func) (info->stream, "%d", immed); + else + { + bfd_vma baseaddr; + bfd_vma val; + + if (branch) + { + immed *= 2; + baseaddr = memaddr + 2; + } + else if (use_extend) + baseaddr = memaddr - 2; + else + { + int status; + bfd_byte buffer[2]; + + baseaddr = memaddr; + + /* If this instruction is in the delay slot of a jr + instruction, the base address is the address of the + jr instruction. If it is in the delay slot of jalr + instruction, the base address is the address of the + jalr instruction. This test is unreliable: we have + no way of knowing whether the previous word is + instruction or data. */ + status = (*info->read_memory_func) (memaddr - 4, buffer, 2, + info); + if (status == 0 + && (((info->endian == BFD_ENDIAN_BIG + ? bfd_getb16 (buffer) + : bfd_getl16 (buffer)) + & 0xf800) == 0x1800)) + baseaddr = memaddr - 4; + else + { + status = (*info->read_memory_func) (memaddr - 2, buffer, + 2, info); + if (status == 0 + && (((info->endian == BFD_ENDIAN_BIG + ? bfd_getb16 (buffer) + : bfd_getl16 (buffer)) + & 0xf81f) == 0xe800)) + baseaddr = memaddr - 2; + } + } + val = (baseaddr & ~ ((1 << shift) - 1)) + immed; + (*info->print_address_func) (val, info); + info->target = val; + } + } + break; + + case 'a': + if (! use_extend) + extend = 0; + l = ((l & 0x1f) << 23) | ((l & 0x3e0) << 13) | (extend << 2); + (*info->print_address_func) ((memaddr & 0xf0000000) | l, info); + info->insn_type = dis_jsr; + info->target = (memaddr & 0xf0000000) | l; + info->branch_delay_insns = 1; + break; + + case 'l': + case 'L': + { + int need_comma, amask, smask; + + need_comma = 0; + + l = (l >> MIPS16OP_SH_IMM6) & MIPS16OP_MASK_IMM6; + + amask = (l >> 3) & 7; + + if (amask > 0 && amask < 5) + { + (*info->fprintf_func) (info->stream, "$%s", reg_names[4]); + if (amask > 1) + (*info->fprintf_func) (info->stream, "-$%s", + reg_names[amask + 3]); + need_comma = 1; + } + + smask = (l >> 1) & 3; + if (smask == 3) + { + (*info->fprintf_func) (info->stream, "%s??", + need_comma ? "," : ""); + need_comma = 1; + } + else if (smask > 0) + { + (*info->fprintf_func) (info->stream, "%s$%s", + need_comma ? "," : "", + reg_names[16]); + if (smask > 1) + (*info->fprintf_func) (info->stream, "-$%s", + reg_names[smask + 15]); + need_comma = 1; + } + + if (l & 1) + { + (*info->fprintf_func) (info->stream, "%s$%s", + need_comma ? "," : "", + reg_names[31]); + need_comma = 1; + } + + if (amask == 5 || amask == 6) + { + (*info->fprintf_func) (info->stream, "%s$f0", + need_comma ? "," : ""); + if (amask == 6) + (*info->fprintf_func) (info->stream, "-$f1"); + } + } + break; + + default: + abort (); + } +} diff --git a/contrib/binutils/opcodes/mips-opc.c b/contrib/binutils/opcodes/mips-opc.c new file mode 100644 index 000000000000..b1000db069c8 --- /dev/null +++ b/contrib/binutils/opcodes/mips-opc.c @@ -0,0 +1,836 @@ +/* mips.h. Mips opcode list for GDB, the GNU debugger. + Copyright 1993, 1994, 1995, 1996, 1997, 1998 Free Software Foundation, Inc. + Contributed by Ralph Campbell and OSF + Commented and modified by Ian Lance Taylor, Cygnus Support + +This file is part of GDB, GAS, and the GNU binutils. + +GDB, GAS, and the GNU binutils are free software; you can redistribute +them and/or modify them under the terms of the GNU General Public +License as published by the Free Software Foundation; either version +1, or (at your option) any later version. + +GDB, GAS, and the GNU binutils are distributed in the hope that they +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 file; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <stdio.h> +#include "sysdep.h" +#include "opcode/mips.h" + +/* Short hand so the lines aren't too long. */ + +#define LDD INSN_LOAD_MEMORY_DELAY +#define LCD INSN_LOAD_COPROC_DELAY +#define UBD INSN_UNCOND_BRANCH_DELAY +#define CBD INSN_COND_BRANCH_DELAY +#define COD INSN_COPROC_MOVE_DELAY +#define CLD INSN_COPROC_MEMORY_DELAY +#define CBL INSN_COND_BRANCH_LIKELY +#define TRAP INSN_TRAP +#define SM INSN_STORE_MEMORY + +#define WR_d INSN_WRITE_GPR_D +#define WR_t INSN_WRITE_GPR_T +#define WR_31 INSN_WRITE_GPR_31 +#define WR_D INSN_WRITE_FPR_D +#define WR_T INSN_WRITE_FPR_T +#define WR_S INSN_WRITE_FPR_S +#define RD_s INSN_READ_GPR_S +#define RD_b INSN_READ_GPR_S +#define RD_t INSN_READ_GPR_T +#define RD_S INSN_READ_FPR_S +#define RD_T INSN_READ_FPR_T +#define RD_R INSN_READ_FPR_R +#define WR_CC INSN_WRITE_COND_CODE +#define RD_CC INSN_READ_COND_CODE +#define RD_C0 INSN_COP +#define RD_C1 INSN_COP +#define RD_C2 INSN_COP +#define RD_C3 INSN_COP +#define WR_C0 INSN_COP +#define WR_C1 INSN_COP +#define WR_C2 INSN_COP +#define WR_C3 INSN_COP + +#define WR_HI INSN_WRITE_HI +#define RD_HI INSN_READ_HI +#define MOD_HI WR_HI|RD_HI + +#define WR_LO INSN_WRITE_LO +#define RD_LO INSN_READ_LO +#define MOD_LO WR_LO|RD_LO + +#define WR_HILO WR_HI|WR_LO +#define RD_HILO RD_HI|RD_LO +#define MOD_HILO WR_HILO|RD_HILO + +#define IS_M INSN_MULT + +#define I1 INSN_ISA1 +#define I2 INSN_ISA2 +#define I3 INSN_ISA3 +#define I4 INSN_ISA4 +#define I5 INSN_ISA5 +#define P3 INSN_4650 +#define L1 INSN_4010 +#define V1 INSN_4100 +#define T3 INSN_3900 + +#define G1 (T3 \ + ) + +#define G2 (T3 \ + ) + +#define G3 (I4 \ + ) + +#define G6 INSN_GP32 + +#define M1 0 +#define M2 0 + +/* The order of overloaded instructions matters. Label arguments and + register arguments look the same. Instructions that can have either + for arguments must apear in the correct order in this table for the + assembler to pick the right one. In other words, entries with + immediate operands must apear after the same instruction with + registers. + + Many instructions are short hand for other instructions (i.e., The + jal <register> instruction is short for jalr <register>). */ + +const struct mips_opcode mips_builtin_opcodes[] = { +/* These instructions appear first so that the disassembler will find + them first. The assemblers uses a hash table based on the + instruction name anyhow. */ +/* name, args, mask, match, pinfo */ +{"nop", "", 0x00000000, 0xffffffff, 0, I1 }, +{"li", "t,j", 0x24000000, 0xffe00000, WR_t, I1 }, /* addiu */ +{"li", "t,i", 0x34000000, 0xffe00000, WR_t, I1 }, /* ori */ +{"li", "t,I", 0, (int) M_LI, INSN_MACRO, I1 }, +{"move", "d,s", 0x00000025, 0xfc1f07ff, WR_d|RD_s, I1|G6 },/* or */ +{"move", "d,s", 0x0000002d, 0xfc1f07ff, WR_d|RD_s, I3 },/* daddu */ +{"move", "d,s", 0x00000021, 0xfc1f07ff, WR_d|RD_s, I1 },/* addu */ +{"move", "d,s", 0x00000025, 0xfc1f07ff, WR_d|RD_s, I1 },/* or */ +{"b", "p", 0x10000000, 0xffff0000, UBD, I1 },/* beq 0,0 */ +{"b", "p", 0x04010000, 0xffff0000, UBD, I1 },/* bgez 0 */ +{"bal", "p", 0x04110000, 0xffff0000, UBD|WR_31, I1 },/* bgezal 0*/ + +{"abs", "d,v", 0, (int) M_ABS, INSN_MACRO, I1 }, +{"abs.s", "D,V", 0x46000005, 0xffff003f, WR_D|RD_S|FP_S, I1 }, +{"abs.d", "D,V", 0x46200005, 0xffff003f, WR_D|RD_S|FP_D, I1 }, +{"abs.ps", "D,V", 0x46c00005, 0xffff003f, WR_D|RD_S|FP_D, I5 }, +{"add", "d,v,t", 0x00000020, 0xfc0007ff, WR_d|RD_s|RD_t, I1 }, +{"add", "t,r,I", 0, (int) M_ADD_I, INSN_MACRO, I1 }, +{"add.s", "D,V,T", 0x46000000, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, I1}, +{"add.d", "D,V,T", 0x46200000, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I1}, +{"add.ps", "D,V,T", 0x46c00000, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5}, +{"addi", "t,r,j", 0x20000000, 0xfc000000, WR_t|RD_s, I1 }, +{"addiu", "t,r,j", 0x24000000, 0xfc000000, WR_t|RD_s, I1 }, +{"addu", "d,v,t", 0x00000021, 0xfc0007ff, WR_d|RD_s|RD_t, I1 }, +{"addu", "t,r,I", 0, (int) M_ADDU_I, INSN_MACRO, I1 }, +{"alnv.ps", "D,V,T,s", 0x4c00001e, 0xfc00003f, WR_D|RD_S|RD_T|FP_D, I5}, +{"and", "d,v,t", 0x00000024, 0xfc0007ff, WR_d|RD_s|RD_t, I1 }, +{"and", "t,r,I", 0, (int) M_AND_I, INSN_MACRO, I1 }, +{"andi", "t,r,i", 0x30000000, 0xfc000000, WR_t|RD_s, I1 }, +/* b is at the top of the table. */ +/* bal is at the top of the table. */ +{"bc0f", "p", 0x41000000, 0xffff0000, CBD|RD_CC, I1 }, +{"bc0fl", "p", 0x41020000, 0xffff0000, CBL|RD_CC, I2|T3 }, +{"bc1f", "p", 0x45000000, 0xffff0000, CBD|RD_CC|FP_S, I1|M1 }, +{"bc1f", "N,p", 0x45000000, 0xffe30000, CBD|RD_CC|FP_S, I4|M1 }, +{"bc1fl", "p", 0x45020000, 0xffff0000, CBL|RD_CC|FP_S, I2|T3|M1}, +{"bc1fl", "N,p", 0x45020000, 0xffe30000, CBL|RD_CC|FP_S, I4|M1 }, +{"bc2f", "p", 0x49000000, 0xffff0000, CBD|RD_CC, I1 }, +{"bc2fl", "p", 0x49020000, 0xffff0000, CBL|RD_CC, I2|T3 }, +{"bc3f", "p", 0x4d000000, 0xffff0000, CBD|RD_CC, I1 }, +{"bc3fl", "p", 0x4d020000, 0xffff0000, CBL|RD_CC, I2|T3 }, +{"bc0t", "p", 0x41010000, 0xffff0000, CBD|RD_CC, I1 }, +{"bc0tl", "p", 0x41030000, 0xffff0000, CBL|RD_CC, I2|T3 }, +{"bc1t", "p", 0x45010000, 0xffff0000, CBD|RD_CC|FP_S, I1 }, +{"bc1t", "N,p", 0x45010000, 0xffe30000, CBD|RD_CC|FP_S, I4 }, +{"bc1tl", "p", 0x45030000, 0xffff0000, CBL|RD_CC|FP_S, I2|T3 }, +{"bc1tl", "N,p", 0x45030000, 0xffe30000, CBL|RD_CC|FP_S, I4 }, +{"bc2t", "p", 0x49010000, 0xffff0000, CBD|RD_CC, I1 }, +{"bc2tl", "p", 0x49030000, 0xffff0000, CBL|RD_CC, I2|T3 }, +{"bc3t", "p", 0x4d010000, 0xffff0000, CBD|RD_CC, I1 }, +{"bc3tl", "p", 0x4d030000, 0xffff0000, CBL|RD_CC, I2|T3 }, +{"beqz", "s,p", 0x10000000, 0xfc1f0000, CBD|RD_s, I1 }, +{"beqzl", "s,p", 0x50000000, 0xfc1f0000, CBL|RD_s, I2|T3 }, +{"beq", "s,t,p", 0x10000000, 0xfc000000, CBD|RD_s|RD_t, I1 }, +{"beq", "s,I,p", 0, (int) M_BEQ_I, INSN_MACRO, I1 }, +{"beql", "s,t,p", 0x50000000, 0xfc000000, CBL|RD_s|RD_t, I2|T3 }, +{"beql", "s,I,p", 0, (int) M_BEQL_I, INSN_MACRO, I2 }, +{"bge", "s,t,p", 0, (int) M_BGE, INSN_MACRO, I1 }, +{"bge", "s,I,p", 0, (int) M_BGE_I, INSN_MACRO, I1 }, +{"bgel", "s,t,p", 0, (int) M_BGEL, INSN_MACRO, I2 }, +{"bgel", "s,I,p", 0, (int) M_BGEL_I, INSN_MACRO, I2 }, +{"bgeu", "s,t,p", 0, (int) M_BGEU, INSN_MACRO, I1 }, +{"bgeu", "s,I,p", 0, (int) M_BGEU_I, INSN_MACRO, I1 }, +{"bgeul", "s,t,p", 0, (int) M_BGEUL, INSN_MACRO, I2 }, +{"bgeul", "s,I,p", 0, (int) M_BGEUL_I, INSN_MACRO, I2 }, +{"bgez", "s,p", 0x04010000, 0xfc1f0000, CBD|RD_s, I1 }, +{"bgezl", "s,p", 0x04030000, 0xfc1f0000, CBL|RD_s, I2|T3 }, +{"bgezal", "s,p", 0x04110000, 0xfc1f0000, CBD|RD_s|WR_31, I1 }, +{"bgezall", "s,p", 0x04130000, 0xfc1f0000, CBL|RD_s, I2|T3 }, +{"bgt", "s,t,p", 0, (int) M_BGT, INSN_MACRO, I1 }, +{"bgt", "s,I,p", 0, (int) M_BGT_I, INSN_MACRO, I1 }, +{"bgtl", "s,t,p", 0, (int) M_BGTL, INSN_MACRO, I2 }, +{"bgtl", "s,I,p", 0, (int) M_BGTL_I, INSN_MACRO, I2 }, +{"bgtu", "s,t,p", 0, (int) M_BGTU, INSN_MACRO, I1 }, +{"bgtu", "s,I,p", 0, (int) M_BGTU_I, INSN_MACRO, I1 }, +{"bgtul", "s,t,p", 0, (int) M_BGTUL, INSN_MACRO, I2 }, +{"bgtul", "s,I,p", 0, (int) M_BGTUL_I, INSN_MACRO, I2 }, +{"bgtz", "s,p", 0x1c000000, 0xfc1f0000, CBD|RD_s, I1 }, +{"bgtzl", "s,p", 0x5c000000, 0xfc1f0000, CBL|RD_s, I2|T3 }, +{"ble", "s,t,p", 0, (int) M_BLE, INSN_MACRO, I1 }, +{"ble", "s,I,p", 0, (int) M_BLE_I, INSN_MACRO, I1 }, +{"blel", "s,t,p", 0, (int) M_BLEL, INSN_MACRO, I2 }, +{"blel", "s,I,p", 0, (int) M_BLEL_I, INSN_MACRO, I2 }, +{"bleu", "s,t,p", 0, (int) M_BLEU, INSN_MACRO, I1 }, +{"bleu", "s,I,p", 0, (int) M_BLEU_I, INSN_MACRO, I1 }, +{"bleul", "s,t,p", 0, (int) M_BLEUL, INSN_MACRO, I2 }, +{"bleul", "s,I,p", 0, (int) M_BLEUL_I, INSN_MACRO, I2 }, +{"blez", "s,p", 0x18000000, 0xfc1f0000, CBD|RD_s, I1 }, +{"blezl", "s,p", 0x58000000, 0xfc1f0000, CBL|RD_s, I2|T3 }, +{"blt", "s,t,p", 0, (int) M_BLT, INSN_MACRO, I1 }, +{"blt", "s,I,p", 0, (int) M_BLT_I, INSN_MACRO, I1 }, +{"bltl", "s,t,p", 0, (int) M_BLTL, INSN_MACRO, I2 }, +{"bltl", "s,I,p", 0, (int) M_BLTL_I, INSN_MACRO, I2 }, +{"bltu", "s,t,p", 0, (int) M_BLTU, INSN_MACRO, I1 }, +{"bltu", "s,I,p", 0, (int) M_BLTU_I, INSN_MACRO, I1 }, +{"bltul", "s,t,p", 0, (int) M_BLTUL, INSN_MACRO, I2 }, +{"bltul", "s,I,p", 0, (int) M_BLTUL_I, INSN_MACRO, I2 }, +{"bltz", "s,p", 0x04000000, 0xfc1f0000, CBD|RD_s, I1 }, +{"bltzl", "s,p", 0x04020000, 0xfc1f0000, CBL|RD_s, I2|T3 }, +{"bltzal", "s,p", 0x04100000, 0xfc1f0000, CBD|RD_s|WR_31, I1 }, +{"bltzall", "s,p", 0x04120000, 0xfc1f0000, CBL|RD_s, I2|T3 }, +{"bnez", "s,p", 0x14000000, 0xfc1f0000, CBD|RD_s, I1 }, +{"bnezl", "s,p", 0x54000000, 0xfc1f0000, CBL|RD_s, I2|T3 }, +{"bne", "s,t,p", 0x14000000, 0xfc000000, CBD|RD_s|RD_t, I1 }, +{"bne", "s,I,p", 0, (int) M_BNE_I, INSN_MACRO, I1 }, +{"bnel", "s,t,p", 0x54000000, 0xfc000000, CBL|RD_s|RD_t, I2|T3 }, +{"bnel", "s,I,p", 0, (int) M_BNEL_I, INSN_MACRO, I2 }, +{"break", "", 0x0000000d, 0xffffffff, TRAP, I1 }, +{"break", "c", 0x0000000d, 0xfc00ffff, TRAP, I1 }, +{"break", "c,q", 0x0000000d, 0xfc00003f, TRAP, I1 }, +{"c.f.d", "S,T", 0x46200030, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.f.d", "M,S,T", 0x46200030, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4 }, +{"c.f.s", "S,T", 0x46000030, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.f.s", "M,S,T", 0x46000030, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4 }, +{"c.f.ps", "S,T", 0x46c00030, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.f.ps", "M,S,T", 0x46c00030, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.un.d", "S,T", 0x46200031, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.un.d", "M,S,T", 0x46200031, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4 }, +{"c.un.s", "S,T", 0x46000031, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.un.s", "M,S,T", 0x46000031, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4 }, +{"c.un.ps", "S,T", 0x46c00031, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.un.ps", "M,S,T", 0x46c00031, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.eq.d", "S,T", 0x46200032, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.eq.d", "M,S,T", 0x46200032, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4 }, +{"c.eq.s", "S,T", 0x46000032, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.eq.s", "M,S,T", 0x46000032, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4 }, +{"c.eq.ps", "S,T", 0x46c00032, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.eq.ps", "M,S,T", 0x46c00032, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.ueq.d", "S,T", 0x46200033, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.ueq.d", "M,S,T", 0x46200033, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4 }, +{"c.ueq.s", "S,T", 0x46000033, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.ueq.s", "M,S,T", 0x46000033, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4 }, +{"c.ueq.ps","S,T", 0x46c00033, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.ueq.ps","M,S,T", 0x46c00033, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.lt.s", "S,T", 0x4600003c, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.lt.s", "M,S,T", 0x4600003c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4 }, +{"c.olt.d", "S,T", 0x46200034, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.olt.d", "M,S,T", 0x46200034, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4 }, +{"c.olt.s", "S,T", 0x46000034, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.olt.s", "M,S,T", 0x46000034, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4 }, +{"c.olt.ps","S,T", 0x46c00034, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.olt.ps","M,S,T", 0x46c00034, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.ult.d", "S,T", 0x46200035, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.ult.d", "M,S,T", 0x46200035, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4 }, +{"c.ult.s", "S,T", 0x46000035, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.ult.s", "M,S,T", 0x46000035, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4 }, +{"c.ult.ps","S,T", 0x46c00035, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.ult.ps","M,S,T", 0x46c00035, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.le.s", "S,T", 0x4600003e, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.le.s", "M,S,T", 0x4600003e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4 }, +{"c.ole.d", "S,T", 0x46200036, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.ole.d", "M,S,T", 0x46200036, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4 }, +{"c.ole.s", "S,T", 0x46000036, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.ole.s", "M,S,T", 0x46000036, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4 }, +{"c.ole.ps","S,T", 0x46c00036, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.ole.ps","M,S,T", 0x46c00036, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.ule.d", "S,T", 0x46200037, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.ule.d", "M,S,T", 0x46200037, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4 }, +{"c.ule.s", "S,T", 0x46000037, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.ule.s", "M,S,T", 0x46000037, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4 }, +{"c.ule.ps","S,T", 0x46c00037, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.ule.ps","M,S,T", 0x46c00037, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.sf.d", "S,T", 0x46200038, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.sf.d", "M,S,T", 0x46200038, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4 }, +{"c.sf.s", "S,T", 0x46000038, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.sf.s", "M,S,T", 0x46000038, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4 }, +{"c.sf.ps", "S,T", 0x46c00038, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.sf.ps", "M,S,T", 0x46c00038, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.ngle.d","S,T", 0x46200039, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.ngle.d","M,S,T", 0x46200039, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4 }, +{"c.ngle.s","S,T", 0x46000039, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.ngle.s","M,S,T", 0x46000039, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4 }, +{"c.ngle.ps","S,T", 0x46c00039, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.ngle.ps","M,S,T", 0x46c00039, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.seq.d", "S,T", 0x4620003a, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.seq.d", "M,S,T", 0x4620003a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4 }, +{"c.seq.s", "S,T", 0x4600003a, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.seq.s", "M,S,T", 0x4600003a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4 }, +{"c.seq.ps","S,T", 0x46c0003a, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.seq.ps","M,S,T", 0x46c0003a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.ngl.d", "S,T", 0x4620003b, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.ngl.d", "M,S,T", 0x4620003b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4 }, +{"c.ngl.s", "S,T", 0x4600003b, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.ngl.s", "M,S,T", 0x4600003b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4 }, +{"c.ngl.ps","S,T", 0x46c0003b, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.ngl.ps","M,S,T", 0x46c0003b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.lt.d", "S,T", 0x4620003c, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.lt.d", "M,S,T", 0x4620003c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4 }, +{"c.lt.ps", "S,T", 0x46c0003c, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.lt.ps", "M,S,T", 0x46c0003c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.nge.d", "S,T", 0x4620003d, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.nge.d", "M,S,T", 0x4620003d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4 }, +{"c.nge.s", "S,T", 0x4600003d, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.nge.s", "M,S,T", 0x4600003d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4 }, +{"c.nge.ps","S,T", 0x46c0003d, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.nge.ps","M,S,T", 0x46c0003d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.le.d", "S,T", 0x4620003e, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.le.d", "M,S,T", 0x4620003e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4 }, +{"c.le.ps", "S,T", 0x46c0003e, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.le.ps", "M,S,T", 0x46c0003e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.ngt.d", "S,T", 0x4620003f, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.ngt.d", "M,S,T", 0x4620003f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4 }, +{"c.ngt.s", "S,T", 0x4600003f, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.ngt.s", "M,S,T", 0x4600003f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4 }, +{"c.ngt.ps","S,T", 0x46c0003f, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.ngt.ps","M,S,T", 0x46c0003f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"cache", "k,o(b)", 0xbc000000, 0xfc000000, RD_b, I3|T3|M1 }, +{"ceil.l.d", "D,S", 0x4620000a, 0xffff003f, WR_D|RD_S|FP_D, I3 }, +{"ceil.l.s", "D,S", 0x4600000a, 0xffff003f, WR_D|RD_S|FP_S, I3 }, +{"ceil.w.d", "D,S", 0x4620000e, 0xffff003f, WR_D|RD_S|FP_D, I2 }, +{"ceil.w.s", "D,S", 0x4600000e, 0xffff003f, WR_D|RD_S|FP_S, I2 }, +{"cfc0", "t,G", 0x40400000, 0xffe007ff, LCD|WR_t|RD_C0, I1 }, +{"cfc1", "t,G", 0x44400000, 0xffe007ff, LCD|WR_t|RD_C1|FP_S, I1 }, +{"cfc1", "t,S", 0x44400000, 0xffe007ff, LCD|WR_t|RD_C1|FP_S, I1 }, +{"cfc2", "t,G", 0x48400000, 0xffe007ff, LCD|WR_t|RD_C2, I1 }, +{"cfc3", "t,G", 0x4c400000, 0xffe007ff, LCD|WR_t|RD_C3, I1 }, +{"ctc0", "t,G", 0x40c00000, 0xffe007ff, COD|RD_t|WR_CC, I1 }, +{"ctc1", "t,G", 0x44c00000, 0xffe007ff, COD|RD_t|WR_CC|FP_S, I1 }, +{"ctc1", "t,S", 0x44c00000, 0xffe007ff, COD|RD_t|WR_CC|FP_S, I1 }, +{"ctc2", "t,G", 0x48c00000, 0xffe007ff, COD|RD_t|WR_CC, I1 }, +{"ctc3", "t,G", 0x4cc00000, 0xffe007ff, COD|RD_t|WR_CC, I1 }, +{"cvt.d.l", "D,S", 0x46a00021, 0xffff003f, WR_D|RD_S|FP_D, I3 }, +{"cvt.d.s", "D,S", 0x46000021, 0xffff003f, WR_D|RD_S|FP_D|FP_S, I1 }, +{"cvt.d.w", "D,S", 0x46800021, 0xffff003f, WR_D|RD_S|FP_D, I1 }, +{"cvt.l.d", "D,S", 0x46200025, 0xffff003f, WR_D|RD_S|FP_D, I3 }, +{"cvt.l.s", "D,S", 0x46000025, 0xffff003f, WR_D|RD_S|FP_S, I3 }, +{"cvt.s.l", "D,S", 0x46a00020, 0xffff003f, WR_D|RD_S|FP_S, I3 }, +{"cvt.s.d", "D,S", 0x46200020, 0xffff003f, WR_D|RD_S|FP_S|FP_D, I1 }, +{"cvt.s.w", "D,S", 0x46800020, 0xffff003f, WR_D|RD_S|FP_S, I1 }, +{"cvt.s.pl","D,S", 0x46c00028, 0xffff003f, WR_D|RD_S|FP_S|FP_D, I5 }, +{"cvt.s.pu","D,S", 0x46c00020, 0xffff003f, WR_D|RD_S|FP_S|FP_D, I5 }, +{"cvt.w.d", "D,S", 0x46200024, 0xffff003f, WR_D|RD_S|FP_D, I1 }, +{"cvt.w.s", "D,S", 0x46000024, 0xffff003f, WR_D|RD_S|FP_S, I1 }, +{"cvt.ps.s","D,V,T", 0x46000026, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5}, +{"dabs", "d,v", 0, (int) M_DABS, INSN_MACRO, I3 }, +{"dadd", "d,v,t", 0x0000002c, 0xfc0007ff, WR_d|RD_s|RD_t, I3 }, +{"dadd", "t,r,I", 0, (int) M_DADD_I, INSN_MACRO, I3 }, +{"daddi", "t,r,j", 0x60000000, 0xfc000000, WR_t|RD_s, I3 }, +{"daddiu", "t,r,j", 0x64000000, 0xfc000000, WR_t|RD_s, I3 }, +{"daddu", "d,v,t", 0x0000002d, 0xfc0007ff, WR_d|RD_s|RD_t, I3 }, +{"daddu", "t,r,I", 0, (int) M_DADDU_I, INSN_MACRO, I3 }, +/* dctr and dctw are used on the r5000. */ +{"dctr", "o(b)", 0xbc050000, 0xfc1f0000, RD_b, I3 }, +{"dctw", "o(b)", 0xbc090000, 0xfc1f0000, RD_b, I3 }, +{"deret", "", 0x4200001f, 0xffffffff, 0, G2|M1 }, +/* For ddiv, see the comments about div. */ +{"ddiv", "z,s,t", 0x0000001e, 0xfc00ffff, RD_s|RD_t|WR_HI|WR_LO, I3 }, +{"ddiv", "d,v,t", 0, (int) M_DDIV_3, INSN_MACRO, I3 }, +{"ddiv", "d,v,I", 0, (int) M_DDIV_3I, INSN_MACRO, I3 }, +/* For ddivu, see the comments about div. */ +{"ddivu", "z,s,t", 0x0000001f, 0xfc00ffff, RD_s|RD_t|WR_HI|WR_LO, I3 }, +{"ddivu", "d,v,t", 0, (int) M_DDIVU_3, INSN_MACRO, I3 }, +{"ddivu", "d,v,I", 0, (int) M_DDIVU_3I, INSN_MACRO, I3 }, +/* The MIPS assembler treats the div opcode with two operands as + though the first operand appeared twice (the first operand is both + a source and a destination). To get the div machine instruction, + you must use an explicit destination of $0. */ +{"div", "z,s,t", 0x0000001a, 0xfc00ffff, RD_s|RD_t|WR_HI|WR_LO, I1 }, +{"div", "z,t", 0x0000001a, 0xffe0ffff, RD_s|RD_t|WR_HI|WR_LO, I1 }, +{"div", "d,v,t", 0, (int) M_DIV_3, INSN_MACRO, I1 }, +{"div", "d,v,I", 0, (int) M_DIV_3I, INSN_MACRO, I1 }, +{"div.d", "D,V,T", 0x46200003, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I1 }, +{"div.s", "D,V,T", 0x46000003, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, I1 }, +/* For divu, see the comments about div. */ +{"divu", "z,s,t", 0x0000001b, 0xfc00ffff, RD_s|RD_t|WR_HI|WR_LO, I1 }, +{"divu", "z,t", 0x0000001b, 0xffe0ffff, RD_s|RD_t|WR_HI|WR_LO, I1 }, +{"divu", "d,v,t", 0, (int) M_DIVU_3, INSN_MACRO, I1 }, +{"divu", "d,v,I", 0, (int) M_DIVU_3I, INSN_MACRO, I1 }, +{"dla", "t,o(b)", 0x64000000, 0xfc000000, WR_t|RD_s, I3 }, /* daddiu */ +{"dla", "t,A(b)", 0, (int) M_DLA_AB, INSN_MACRO, I3 }, +{"dli", "t,j", 0x24000000, 0xffe00000, WR_t, I3 }, /* addiu */ +{"dli", "t,i", 0x34000000, 0xffe00000, WR_t, I3 }, /* ori */ +{"dli", "t,I", 0, (int) M_DLI, INSN_MACRO, I3 }, + +{"dmadd16", "s,t", 0x00000029, 0xfc00ffff, RD_s|RD_t|WR_LO|RD_LO, V1 }, +{"dmfc0", "t,G", 0x40200000, 0xffe007ff, LCD|WR_t|RD_C0, I3 }, +{"dmtc0", "t,G", 0x40a00000, 0xffe007ff, COD|RD_t|WR_C0|WR_CC, I3 }, +{"dmfc1", "t,S", 0x44200000, 0xffe007ff, LCD|WR_t|RD_S|FP_S, I3 }, +{"dmtc1", "t,S", 0x44a00000, 0xffe007ff, COD|RD_t|WR_S|FP_S, I3 }, +{"dmfc2", "t,S", 0x48200000, 0xffe007ff, LCD|WR_t|RD_S|FP_S, I3 }, +{"dmtc2", "t,S", 0x48a00000, 0xffe007ff, COD|RD_t|WR_S|FP_S, I3 }, +{"dmul", "d,v,t", 0, (int) M_DMUL, INSN_MACRO, I3 }, +{"dmul", "d,v,I", 0, (int) M_DMUL_I, INSN_MACRO, I3 }, +{"dmulo", "d,v,t", 0, (int) M_DMULO, INSN_MACRO, I3 }, +{"dmulo", "d,v,I", 0, (int) M_DMULO_I, INSN_MACRO, I3 }, +{"dmulou", "d,v,t", 0, (int) M_DMULOU, INSN_MACRO, I3 }, +{"dmulou", "d,v,I", 0, (int) M_DMULOU_I, INSN_MACRO, I3 }, +{"dmult", "s,t", 0x0000001c, 0xfc00ffff, RD_s|RD_t|WR_HI|WR_LO, I3}, +{"dmultu", "s,t", 0x0000001d, 0xfc00ffff, RD_s|RD_t|WR_HI|WR_LO, I3}, +{"dneg", "d,w", 0x0000002e, 0xffe007ff, WR_d|RD_t, I3 }, /* dsub 0 */ +{"dnegu", "d,w", 0x0000002f, 0xffe007ff, WR_d|RD_t, I3 }, /* dsubu 0*/ +{"drem", "z,s,t", 0x0000001e, 0xfc00ffff, RD_s|RD_t|WR_HI|WR_LO, I3 }, +{"drem", "d,v,t", 3, (int) M_DREM_3, INSN_MACRO, I3 }, +{"drem", "d,v,I", 3, (int) M_DREM_3I, INSN_MACRO, I3 }, +{"dremu", "z,s,t", 0x0000001f, 0xfc00ffff, RD_s|RD_t|WR_HI|WR_LO, I3 }, +{"dremu", "d,v,t", 3, (int) M_DREMU_3, INSN_MACRO, I3 }, +{"dremu", "d,v,I", 3, (int) M_DREMU_3I, INSN_MACRO, I3 }, +{"dsllv", "d,t,s", 0x00000014, 0xfc0007ff, WR_d|RD_t|RD_s, I3 }, +{"dsll32", "d,w,<", 0x0000003c, 0xffe0003f, WR_d|RD_t, I3 }, +{"dsll", "d,w,s", 0x00000014, 0xfc0007ff, WR_d|RD_t|RD_s, I3 }, /* dsllv */ +{"dsll", "d,w,>", 0x0000003c, 0xffe0003f, WR_d|RD_t, I3 }, /* dsll32 */ +{"dsll", "d,w,<", 0x00000038, 0xffe0003f, WR_d|RD_t, I3 }, +{"dsrav", "d,t,s", 0x00000017, 0xfc0007ff, WR_d|RD_t|RD_s, I3 }, +{"dsra32", "d,w,<", 0x0000003f, 0xffe0003f, WR_d|RD_t, I3 }, +{"dsra", "d,w,s", 0x00000017, 0xfc0007ff, WR_d|RD_t|RD_s, I3 }, /* dsrav */ +{"dsra", "d,w,>", 0x0000003f, 0xffe0003f, WR_d|RD_t, I3 }, /* dsra32 */ +{"dsra", "d,w,<", 0x0000003b, 0xffe0003f, WR_d|RD_t, I3 }, +{"dsrlv", "d,t,s", 0x00000016, 0xfc0007ff, WR_d|RD_t|RD_s, I3 }, +{"dsrl32", "d,w,<", 0x0000003e, 0xffe0003f, WR_d|RD_t, I3 }, +{"dsrl", "d,w,s", 0x00000016, 0xfc0007ff, WR_d|RD_t|RD_s, I3 }, /* dsrlv */ +{"dsrl", "d,w,>", 0x0000003e, 0xffe0003f, WR_d|RD_t, I3 }, /* dsrl32 */ +{"dsrl", "d,w,<", 0x0000003a, 0xffe0003f, WR_d|RD_t, I3 }, +{"dsub", "d,v,t", 0x0000002e, 0xfc0007ff, WR_d|RD_s|RD_t, I3 }, +{"dsub", "d,v,I", 0, (int) M_DSUB_I, INSN_MACRO, I3 }, +{"dsubu", "d,v,t", 0x0000002f, 0xfc0007ff, WR_d|RD_s|RD_t, I3 }, +{"dsubu", "d,v,I", 0, (int) M_DSUBU_I, INSN_MACRO, I3 }, +{"eret", "", 0x42000018, 0xffffffff, 0, I3|M1 }, +{"floor.l.d", "D,S", 0x4620000b, 0xffff003f, WR_D|RD_S|FP_D, I3 }, +{"floor.l.s", "D,S", 0x4600000b, 0xffff003f, WR_D|RD_S|FP_S, I3 }, +{"floor.w.d", "D,S", 0x4620000f, 0xffff003f, WR_D|RD_S|FP_D, I2 }, +{"floor.w.s", "D,S", 0x4600000f, 0xffff003f, WR_D|RD_S|FP_S, I2 }, +{"flushi", "", 0xbc010000, 0xffffffff, 0, L1 }, +{"flushd", "", 0xbc020000, 0xffffffff, 0, L1 }, +{"flushid", "", 0xbc030000, 0xffffffff, 0, L1 }, +{"hibernate","", 0x42000023, 0xffffffff, 0, V1 }, +{"jr", "s", 0x00000008, 0xfc1fffff, UBD|RD_s, I1 }, +{"j", "s", 0x00000008, 0xfc1fffff, UBD|RD_s, I1 }, /* jr */ +/* SVR4 PIC code requires special handling for j, so it must be a + macro. */ +{"j", "a", 0, (int) M_J_A, INSN_MACRO, I1 }, +/* This form of j is used by the disassembler and internally by the + assembler, but will never match user input (because the line above + will match first). */ +{"j", "a", 0x08000000, 0xfc000000, UBD, I1 }, +{"jalr", "s", 0x0000f809, 0xfc1fffff, UBD|RD_s|WR_d, I1 }, +{"jalr", "d,s", 0x00000009, 0xfc1f07ff, UBD|RD_s|WR_d, I1 }, +/* SVR4 PIC code requires special handling for jal, so it must be a + macro. */ +{"jal", "d,s", 0, (int) M_JAL_2, INSN_MACRO, I1 }, +{"jal", "s", 0, (int) M_JAL_1, INSN_MACRO, I1 }, +{"jal", "a", 0, (int) M_JAL_A, INSN_MACRO, I1 }, +/* This form of jal is used by the disassembler and internally by the + assembler, but will never match user input (because the line above + will match first). */ +{"jal", "a", 0x0c000000, 0xfc000000, UBD|WR_31, I1 }, + /* jalx really should only be avaliable if mips16 is available, + but for now make it I1. */ +{"jalx", "a", 0x74000000, 0xfc000000, UBD|WR_31, I1 }, +{"la", "t,o(b)", 0x24000000, 0xfc000000, WR_t|RD_s, I1 }, /* addiu */ +{"la", "t,A(b)", 0, (int) M_LA_AB, INSN_MACRO, I1 }, +{"lb", "t,o(b)", 0x80000000, 0xfc000000, LDD|RD_b|WR_t, I1 }, +{"lb", "t,A(b)", 0, (int) M_LB_AB, INSN_MACRO, I1 }, +{"lbu", "t,o(b)", 0x90000000, 0xfc000000, LDD|RD_b|WR_t, I1 }, +{"lbu", "t,A(b)", 0, (int) M_LBU_AB, INSN_MACRO, I1 }, +{"ld", "t,o(b)", 0xdc000000, 0xfc000000, WR_t|RD_b, I3 }, +{"ld", "t,o(b)", 0, (int) M_LD_OB, INSN_MACRO, I1 }, +{"ld", "t,A(b)", 0, (int) M_LD_AB, INSN_MACRO, I1 }, +{"ldc1", "T,o(b)", 0xd4000000, 0xfc000000, CLD|RD_b|WR_T|FP_D, I2 }, +{"ldc1", "E,o(b)", 0xd4000000, 0xfc000000, CLD|RD_b|WR_T|FP_D, I2 }, +{"ldc1", "T,A(b)", 0, (int) M_LDC1_AB, INSN_MACRO, I2 }, +{"ldc1", "E,A(b)", 0, (int) M_LDC1_AB, INSN_MACRO, I2 }, +{"l.d", "T,o(b)", 0xd4000000, 0xfc000000, CLD|RD_b|WR_T|FP_D, I2 }, /* ldc1 */ +{"l.d", "T,o(b)", 0, (int) M_L_DOB, INSN_MACRO, I1 }, +{"l.d", "T,A(b)", 0, (int) M_L_DAB, INSN_MACRO, I1 }, +{"ldc2", "E,o(b)", 0xd8000000, 0xfc000000, CLD|RD_b|WR_CC, I2 }, +{"ldc2", "E,A(b)", 0, (int) M_LDC2_AB, INSN_MACRO, I2 }, +{"ldc3", "E,o(b)", 0xdc000000, 0xfc000000, CLD|RD_b|WR_CC, I2 }, +{"ldc3", "E,A(b)", 0, (int) M_LDC3_AB, INSN_MACRO, I2 }, +{"ldl", "t,o(b)", 0x68000000, 0xfc000000, LDD|WR_t|RD_b, I3 }, +{"ldl", "t,A(b)", 0, (int) M_LDL_AB, INSN_MACRO, I3 }, +{"ldr", "t,o(b)", 0x6c000000, 0xfc000000, LDD|WR_t|RD_b, I3 }, +{"ldr", "t,A(b)", 0, (int) M_LDR_AB, INSN_MACRO, I3 }, +{"ldxc1", "D,t(b)", 0x4c000001, 0xfc00f83f, LDD|WR_D|RD_t|RD_b, I4 }, +{"lh", "t,o(b)", 0x84000000, 0xfc000000, LDD|RD_b|WR_t, I1 }, +{"lh", "t,A(b)", 0, (int) M_LH_AB, INSN_MACRO, I1 }, +{"lhu", "t,o(b)", 0x94000000, 0xfc000000, LDD|RD_b|WR_t, I1 }, +{"lhu", "t,A(b)", 0, (int) M_LHU_AB, INSN_MACRO, I1 }, +/* li is at the start of the table. */ +{"li.d", "t,F", 0, (int) M_LI_D, INSN_MACRO, I1 }, +{"li.d", "T,L", 0, (int) M_LI_DD, INSN_MACRO, I1 }, +{"li.s", "t,f", 0, (int) M_LI_S, INSN_MACRO, I1 }, +{"li.s", "T,l", 0, (int) M_LI_SS, INSN_MACRO, I1 }, +{"ll", "t,o(b)", 0xc0000000, 0xfc000000, LDD|RD_b|WR_t, I2 }, +{"ll", "t,A(b)", 0, (int) M_LL_AB, INSN_MACRO, I2 }, +{"lld", "t,o(b)", 0xd0000000, 0xfc000000, LDD|RD_b|WR_t, I3 }, +{"lld", "t,A(b)", 0, (int) M_LLD_AB, INSN_MACRO, I3 }, +{"lui", "t,u", 0x3c000000, 0xffe00000, WR_t, I1 }, +{"luxc1", "D,t(b)", 0x4c000005, 0xfc00f83f, LDD|WR_D|RD_t|RD_b, I5 }, +{"lw", "t,o(b)", 0x8c000000, 0xfc000000, LDD|RD_b|WR_t, I1 }, +{"lw", "t,A(b)", 0, (int) M_LW_AB, INSN_MACRO, I1 }, +{"lwc0", "E,o(b)", 0xc0000000, 0xfc000000, CLD|RD_b|WR_CC, I1 }, +{"lwc0", "E,A(b)", 0, (int) M_LWC0_AB, INSN_MACRO, I1 }, +{"lwc1", "T,o(b)", 0xc4000000, 0xfc000000, CLD|RD_b|WR_T|FP_S, I1 }, +{"lwc1", "E,o(b)", 0xc4000000, 0xfc000000, CLD|RD_b|WR_T|FP_S, I1 }, +{"lwc1", "T,A(b)", 0, (int) M_LWC1_AB, INSN_MACRO, I1 }, +{"lwc1", "E,A(b)", 0, (int) M_LWC1_AB, INSN_MACRO, I1 }, +{"l.s", "T,o(b)", 0xc4000000, 0xfc000000, CLD|RD_b|WR_T|FP_S, I1 }, /* lwc1 */ +{"l.s", "T,A(b)", 0, (int) M_LWC1_AB, INSN_MACRO, I1 }, +{"lwc2", "E,o(b)", 0xc8000000, 0xfc000000, CLD|RD_b|WR_CC, I1 }, +{"lwc2", "E,A(b)", 0, (int) M_LWC2_AB, INSN_MACRO, I1 }, +{"lwc3", "E,o(b)", 0xcc000000, 0xfc000000, CLD|RD_b|WR_CC, I1 }, +{"lwc3", "E,A(b)", 0, (int) M_LWC3_AB, INSN_MACRO, I1 }, +{"lwl", "t,o(b)", 0x88000000, 0xfc000000, LDD|RD_b|WR_t, I1 }, +{"lwl", "t,A(b)", 0, (int) M_LWL_AB, INSN_MACRO, I1 }, +{"lcache", "t,o(b)", 0x88000000, 0xfc000000, LDD|RD_b|WR_t, I2 }, /* same */ +{"lcache", "t,A(b)", 0, (int) M_LWL_AB, INSN_MACRO, I2 }, /* as lwl */ +{"lwr", "t,o(b)", 0x98000000, 0xfc000000, LDD|RD_b|WR_t, I1 }, +{"lwr", "t,A(b)", 0, (int) M_LWR_AB, INSN_MACRO, I1 }, +{"flush", "t,o(b)", 0x98000000, 0xfc000000, LDD|RD_b|WR_t, I2 }, /* same */ +{"flush", "t,A(b)", 0, (int) M_LWR_AB, INSN_MACRO, I2 }, /* as lwr */ +{"lwu", "t,o(b)", 0x9c000000, 0xfc000000, LDD|RD_b|WR_t, I3 }, +{"lwu", "t,A(b)", 0, (int) M_LWU_AB, INSN_MACRO, I3 }, +{"lwxc1", "D,t(b)", 0x4c000000, 0xfc00f83f, LDD|WR_D|RD_t|RD_b, I4 }, + + +{"mad", "s,t", 0x70000000, 0xfc00ffff, RD_s|RD_t|WR_HI|WR_LO|RD_HI|RD_LO, P3 }, +{"madu", "s,t", 0x70000001, 0xfc00ffff, RD_s|RD_t|WR_HI|WR_LO|RD_HI|RD_LO, P3 }, +{"madd.d", "D,R,S,T", 0x4c000021, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I4 }, +{"madd.s", "D,R,S,T", 0x4c000020, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_S, I4 }, +{"madd.ps", "D,R,S,T", 0x4c000026, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I5 }, +{"madd", "s,t", 0x0000001c, 0xfc00ffff, RD_s|RD_t|WR_HI|WR_LO, L1 }, +{"madd", "s,t", 0x70000000, 0xfc00ffff, RD_s|RD_t|WR_HI|WR_LO|IS_M, G1|M1 }, +{"madd", "d,s,t", 0x70000000, 0xfc0007ff, RD_s|RD_t|WR_HI|WR_LO|WR_d|IS_M, G1 }, +{"maddu", "s,t", 0x0000001d, 0xfc00ffff, RD_s|RD_t|WR_HI|WR_LO, L1 }, +{"maddu", "s,t", 0x70000001, 0xfc00ffff, RD_s|RD_t|WR_HI|WR_LO|IS_M, G1|M1}, +{"maddu", "d,s,t", 0x70000001, 0xfc0007ff, RD_s|RD_t|WR_HI|WR_LO|WR_d|IS_M, G1}, +{"madd16", "s,t", 0x00000028, 0xfc00ffff, RD_s|RD_t|WR_HI|WR_LO|RD_HI|RD_LO, V1 }, +{"mfc0", "t,G", 0x40000000, 0xffe007ff, LCD|WR_t|RD_C0, I1 }, +{"mfc1", "t,S", 0x44000000, 0xffe007ff, LCD|WR_t|RD_S|FP_S, I1 }, +{"mfc1", "t,G", 0x44000000, 0xffe007ff, LCD|WR_t|RD_S|FP_S, I1 }, +{"mfc2", "t,G", 0x48000000, 0xffe007ff, LCD|WR_t|RD_C2, I1 }, +{"mfc3", "t,G", 0x4c000000, 0xffe007ff, LCD|WR_t|RD_C3, I1 }, +{"mfhi", "d", 0x00000010, 0xffff07ff, WR_d|RD_HI, I1 }, +{"mflo", "d", 0x00000012, 0xffff07ff, WR_d|RD_LO, I1 }, +{"mov.d", "D,S", 0x46200006, 0xffff003f, WR_D|RD_S|FP_D, I1 }, +{"mov.s", "D,S", 0x46000006, 0xffff003f, WR_D|RD_S|FP_S, I1 }, +{"mov.ps", "D,S", 0x46c00006, 0xffff003f, WR_D|RD_S|FP_D, I5 }, +{"movf", "d,s,N", 0x00000001, 0xfc0307ff, WR_d|RD_s|RD_CC|FP_D|FP_S, I4|M1}, +{"movf.d", "D,S,N", 0x46200011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, I4|M1 }, +{"movf.s", "D,S,N", 0x46000011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_S, I4|M1 }, +{"movf.ps", "D,S,N", 0x46c00011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, I5 }, +{"movn", "d,v,t", 0x0000000b, 0xfc0007ff, WR_d|RD_s|RD_t, I4|M1 }, +{"ffc", "d,v", 0x0000000b, 0xfc1f07ff, WR_d|RD_s,L1 }, +{"movn.d", "D,S,t", 0x46200013, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, I4|M1 }, +{"movn.s", "D,S,t", 0x46000013, 0xffe0003f, WR_D|RD_S|RD_t|FP_S, I4|M1 }, +{"movt", "d,s,N", 0x00010001, 0xfc0307ff, WR_d|RD_s|RD_CC, I4|M1 }, +{"movt.d", "D,S,N", 0x46210011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, I4|M1 }, +{"movt.s", "D,S,N", 0x46010011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_S, I4|M1 }, +{"movt.ps", "D,S,N", 0x46c10011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, I5}, +{"movz", "d,v,t", 0x0000000a, 0xfc0007ff, WR_d|RD_s|RD_t, I4|M1 }, +{"ffs", "d,v", 0x0000000a, 0xfc1f07ff, WR_d|RD_s,L1 }, +{"movz.d", "D,S,t", 0x46200012, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, I4|M1 }, +{"movz.s", "D,S,t", 0x46000012, 0xffe0003f, WR_D|RD_S|RD_t|FP_S, I4|M1 }, +/* move is at the top of the table. */ +{"msub.d", "D,R,S,T", 0x4c000029, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I4 }, +{"msub.s", "D,R,S,T", 0x4c000028, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_S, I4 }, +{"msub.ps", "D,R,S,T", 0x4c00002e, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I5 }, +{"msub", "s,t", 0x0000001e, 0xfc00ffff, RD_s|RD_t|WR_HI|WR_LO,L1 }, +{"msubu", "s,t", 0x0000001f, 0xfc00ffff, RD_s|RD_t|WR_HI|WR_LO,L1 }, +{"mtc0", "t,G", 0x40800000, 0xffe007ff, COD|RD_t|WR_C0|WR_CC, I1 }, +{"mtc1", "t,S", 0x44800000, 0xffe007ff, COD|RD_t|WR_S|FP_S, I1 }, +{"mtc1", "t,G", 0x44800000, 0xffe007ff, COD|RD_t|WR_S|FP_S, I1 }, +{"mtc2", "t,G", 0x48800000, 0xffe007ff, COD|RD_t|WR_C2|WR_CC, I1 }, +{"mtc3", "t,G", 0x4c800000, 0xffe007ff, COD|RD_t|WR_C3|WR_CC, I1 }, +{"mthi", "s", 0x00000011, 0xfc1fffff, RD_s|WR_HI, I1 }, +{"mtlo", "s", 0x00000013, 0xfc1fffff, RD_s|WR_LO, I1 }, +{"mul.d", "D,V,T", 0x46200002, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I1 }, +{"mul.s", "D,V,T", 0x46000002, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, I1 }, +{"mul.ps", "D,V,T", 0x46c00002, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5 }, +{"mul", "d,v,t", 0x70000002, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HI|WR_LO,P3}, +{"mul", "d,v,t", 0, (int) M_MUL, INSN_MACRO, I1 }, +{"mul", "d,v,I", 0, (int) M_MUL_I, INSN_MACRO, I1 }, +{"mulo", "d,v,t", 0, (int) M_MULO, INSN_MACRO, I1 }, +{"mulo", "d,v,I", 0, (int) M_MULO_I, INSN_MACRO, I1 }, +{"mulou", "d,v,t", 0, (int) M_MULOU, INSN_MACRO, I1 }, +{"mulou", "d,v,I", 0, (int) M_MULOU_I, INSN_MACRO, I1 }, +{"mult", "s,t", 0x00000018, 0xfc00ffff, RD_s|RD_t|WR_HI|WR_LO|IS_M, I1}, +{"mult", "d,s,t", 0x00000018, 0xfc0007ff, RD_s|RD_t|WR_HI|WR_LO|WR_d|IS_M, G1}, +{"multu", "s,t", 0x00000019, 0xfc00ffff, RD_s|RD_t|WR_HI|WR_LO|IS_M, I1}, +{"multu", "d,s,t", 0x00000019, 0xfc0007ff, RD_s|RD_t|WR_HI|WR_LO|WR_d|IS_M, G1}, +{"neg", "d,w", 0x00000022, 0xffe007ff, WR_d|RD_t, I1 }, /* sub 0 */ +{"negu", "d,w", 0x00000023, 0xffe007ff, WR_d|RD_t, I1 }, /* subu 0 */ +{"neg.d", "D,V", 0x46200007, 0xffff003f, WR_D|RD_S|FP_D, I1 }, +{"neg.s", "D,V", 0x46000007, 0xffff003f, WR_D|RD_S|FP_S, I1 }, +{"neg.ps", "D,V", 0x46c00007, 0xffff003f, WR_D|RD_S|FP_D,I5 }, +{"nmadd.d", "D,R,S,T", 0x4c000031, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I4 }, +{"nmadd.s", "D,R,S,T", 0x4c000030, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_S, I4 }, +{"nmadd.ps","D,R,S,T", 0x4c000036, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I5 }, +{"nmsub.d", "D,R,S,T", 0x4c000039, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I4 }, +{"nmsub.s", "D,R,S,T", 0x4c000038, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_S, I4 }, +{"nmsub.ps","D,R,S,T", 0x4c00003e, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I5 }, +/* nop is at the start of the table. */ +{"nor", "d,v,t", 0x00000027, 0xfc0007ff, WR_d|RD_s|RD_t, I1 }, +{"nor", "t,r,I", 0, (int) M_NOR_I, INSN_MACRO, I1 }, +{"not", "d,v", 0x00000027, 0xfc1f07ff, WR_d|RD_s|RD_t, I1 },/*nor d,s,0*/ +{"or", "d,v,t", 0x00000025, 0xfc0007ff, WR_d|RD_s|RD_t, I1 }, +{"or", "t,r,I", 0, (int) M_OR_I, INSN_MACRO, I1 }, +{"ori", "t,r,i", 0x34000000, 0xfc000000, WR_t|RD_s, I1 }, + +{"pll.ps", "D,V,T", 0x46c0002c, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5}, +{"plu.ps", "D,V,T", 0x46c0002d, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5}, + +{"pref", "k,o(b)", 0xcc000000, 0xfc000000, RD_b, G3|M1 }, +{"prefx", "h,t(b)", 0x4c00000f, 0xfc0007ff, RD_b|RD_t, I4 }, + +{"pul.ps", "D,V,T", 0x46c0002e, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5}, +{"puu.ps", "D,V,T", 0x46c0002f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5}, + +{"recip.d", "D,S", 0x46200015, 0xffff003f, WR_D|RD_S|FP_D, I4 }, +{"recip.s", "D,S", 0x46000015, 0xffff003f, WR_D|RD_S|FP_S, I4 }, +{"rem", "z,s,t", 0x0000001a, 0xfc00ffff, RD_s|RD_t|WR_HI|WR_LO, I1 }, +{"rem", "d,v,t", 0, (int) M_REM_3, INSN_MACRO, I1 }, +{"rem", "d,v,I", 0, (int) M_REM_3I, INSN_MACRO, I1 }, +{"remu", "z,s,t", 0x0000001b, 0xfc00ffff, RD_s|RD_t|WR_HI|WR_LO, I1 }, +{"remu", "d,v,t", 0, (int) M_REMU_3, INSN_MACRO, I1 }, +{"remu", "d,v,I", 0, (int) M_REMU_3I, INSN_MACRO, I1 }, +{"rfe", "", 0x42000010, 0xffffffff, 0, I1|T3 }, +{"rol", "d,v,t", 0, (int) M_ROL, INSN_MACRO, I1 }, +{"rol", "d,v,I", 0, (int) M_ROL_I, INSN_MACRO, I1 }, +{"ror", "d,v,t", 0, (int) M_ROR, INSN_MACRO, I1 }, +{"ror", "d,v,I", 0, (int) M_ROR_I, INSN_MACRO, I1 }, +{"round.l.d", "D,S", 0x46200008, 0xffff003f, WR_D|RD_S|FP_D, I3 }, +{"round.l.s", "D,S", 0x46000008, 0xffff003f, WR_D|RD_S|FP_S, I3 }, +{"round.w.d", "D,S", 0x4620000c, 0xffff003f, WR_D|RD_S|FP_D, I2 }, +{"round.w.s", "D,S", 0x4600000c, 0xffff003f, WR_D|RD_S|FP_S, I2 }, +{"rsqrt.d", "D,S", 0x46200016, 0xffff003f, WR_D|RD_S|FP_D, I4 }, +{"rsqrt.s", "D,S", 0x46000016, 0xffff003f, WR_D|RD_S|FP_S, I4 }, +{"sb", "t,o(b)", 0xa0000000, 0xfc000000, SM|RD_t|RD_b, I1 }, +{"sb", "t,A(b)", 0, (int) M_SB_AB, INSN_MACRO, I1 }, +{"sc", "t,o(b)", 0xe0000000, 0xfc000000, SM|RD_t|WR_t|RD_b, I2 }, +{"sc", "t,A(b)", 0, (int) M_SC_AB, INSN_MACRO, I2 }, +{"scd", "t,o(b)", 0xf0000000, 0xfc000000, SM|RD_t|WR_t|RD_b, I3 }, +{"scd", "t,A(b)", 0, (int) M_SCD_AB, INSN_MACRO, I3 }, +{"sd", "t,o(b)", 0xfc000000, 0xfc000000, SM|RD_t|RD_b, I3 }, +{"sd", "t,o(b)", 0, (int) M_SD_OB, INSN_MACRO, I1 }, +{"sd", "t,A(b)", 0, (int) M_SD_AB, INSN_MACRO, I1 }, +{"sdbbp", "", 0x0000000e, 0xffffffff, TRAP, G2|M1 }, +{"sdbbp", "c", 0x0000000e, 0xfc00ffff, TRAP, G2|M1 }, +{"sdbbp", "c,q", 0x0000000e, 0xfc00003f, TRAP, G2|M1 }, +{"sdc1", "T,o(b)", 0xf4000000, 0xfc000000, SM|RD_T|RD_b|FP_D, I2 }, +{"sdc1", "E,o(b)", 0xf4000000, 0xfc000000, SM|RD_T|RD_b|FP_D, I2 }, +{"sdc1", "T,A(b)", 0, (int) M_SDC1_AB, INSN_MACRO, I2 }, +{"sdc1", "E,A(b)", 0, (int) M_SDC1_AB, INSN_MACRO, I2 }, +{"sdc2", "E,o(b)", 0xf8000000, 0xfc000000, SM|RD_C2|RD_b, I2 }, +{"sdc2", "E,A(b)", 0, (int) M_SDC2_AB, INSN_MACRO, I2 }, +{"sdc3", "E,o(b)", 0xfc000000, 0xfc000000, SM|RD_C3|RD_b, I2 }, +{"sdc3", "E,A(b)", 0, (int) M_SDC3_AB, INSN_MACRO, I2 }, +{"s.d", "T,o(b)", 0xf4000000, 0xfc000000, SM|RD_T|RD_b|FP_D, I2 }, +{"s.d", "T,o(b)", 0, (int) M_S_DOB, INSN_MACRO, I1 }, +{"s.d", "T,A(b)", 0, (int) M_S_DAB, INSN_MACRO, I1 }, +{"sdl", "t,o(b)", 0xb0000000, 0xfc000000, SM|RD_t|RD_b, I3 }, +{"sdl", "t,A(b)", 0, (int) M_SDL_AB, INSN_MACRO, I3 }, +{"sdr", "t,o(b)", 0xb4000000, 0xfc000000, SM|RD_t|RD_b, I3 }, +{"sdr", "t,A(b)", 0, (int) M_SDR_AB, INSN_MACRO, I3 }, +{"sdxc1", "S,t(b)", 0x4c000009, 0xfc0007ff, SM|RD_S|RD_t|RD_b, I4 }, +{"selsl", "d,v,t", 0x00000005, 0xfc0007ff, WR_d|RD_s|RD_t,L1 }, +{"selsr", "d,v,t", 0x00000001, 0xfc0007ff, WR_d|RD_s|RD_t,L1 }, +{"seq", "d,v,t", 0, (int) M_SEQ, INSN_MACRO, I1 }, +{"seq", "d,v,I", 0, (int) M_SEQ_I, INSN_MACRO, I1 }, +{"sge", "d,v,t", 0, (int) M_SGE, INSN_MACRO, I1 }, +{"sge", "d,v,I", 0, (int) M_SGE_I, INSN_MACRO, I1 }, +{"sgeu", "d,v,t", 0, (int) M_SGEU, INSN_MACRO, I1 }, +{"sgeu", "d,v,I", 0, (int) M_SGEU_I, INSN_MACRO, I1 }, +{"sgt", "d,v,t", 0, (int) M_SGT, INSN_MACRO, I1 }, +{"sgt", "d,v,I", 0, (int) M_SGT_I, INSN_MACRO, I1 }, +{"sgtu", "d,v,t", 0, (int) M_SGTU, INSN_MACRO, I1 }, +{"sgtu", "d,v,I", 0, (int) M_SGTU_I, INSN_MACRO, I1 }, +{"sh", "t,o(b)", 0xa4000000, 0xfc000000, SM|RD_t|RD_b, I1 }, +{"sh", "t,A(b)", 0, (int) M_SH_AB, INSN_MACRO, I1 }, +{"sle", "d,v,t", 0, (int) M_SLE, INSN_MACRO, I1 }, +{"sle", "d,v,I", 0, (int) M_SLE_I, INSN_MACRO, I1 }, +{"sleu", "d,v,t", 0, (int) M_SLEU, INSN_MACRO, I1 }, +{"sleu", "d,v,I", 0, (int) M_SLEU_I, INSN_MACRO, I1 }, +{"sllv", "d,t,s", 0x00000004, 0xfc0007ff, WR_d|RD_t|RD_s, I1 }, +{"sll", "d,w,s", 0x00000004, 0xfc0007ff, WR_d|RD_t|RD_s, I1 }, /* sllv */ +{"sll", "d,w,<", 0x00000000, 0xffe0003f, WR_d|RD_t, I1 }, +{"slt", "d,v,t", 0x0000002a, 0xfc0007ff, WR_d|RD_s|RD_t, I1 }, +{"slt", "d,v,I", 0, (int) M_SLT_I, INSN_MACRO, I1 }, +{"slti", "t,r,j", 0x28000000, 0xfc000000, WR_t|RD_s, I1 }, +{"sltiu", "t,r,j", 0x2c000000, 0xfc000000, WR_t|RD_s, I1 }, +{"sltu", "d,v,t", 0x0000002b, 0xfc0007ff, WR_d|RD_s|RD_t, I1 }, +{"sltu", "d,v,I", 0, (int) M_SLTU_I, INSN_MACRO, I1 }, +{"sne", "d,v,t", 0, (int) M_SNE, INSN_MACRO, I1 }, +{"sne", "d,v,I", 0, (int) M_SNE_I, INSN_MACRO, I1 }, +{"sqrt.d", "D,S", 0x46200004, 0xffff003f, WR_D|RD_S|FP_D, I2 }, +{"sqrt.s", "D,S", 0x46000004, 0xffff003f, WR_D|RD_S|FP_S, I2 }, +{"srav", "d,t,s", 0x00000007, 0xfc0007ff, WR_d|RD_t|RD_s, I1 }, +{"sra", "d,w,s", 0x00000007, 0xfc0007ff, WR_d|RD_t|RD_s, I1 }, /* srav */ +{"sra", "d,w,<", 0x00000003, 0xffe0003f, WR_d|RD_t, I1 }, +{"srlv", "d,t,s", 0x00000006, 0xfc0007ff, WR_d|RD_t|RD_s, I1 }, +{"srl", "d,w,s", 0x00000006, 0xfc0007ff, WR_d|RD_t|RD_s, I1 }, /* srlv */ +{"srl", "d,w,<", 0x00000002, 0xffe0003f, WR_d|RD_t, I1 }, +{"ssnop", "", 0x00000040, 0xffffffff, 0, M1 }, +{"standby", "", 0x42000021, 0xffffffff, 0, V1 }, +{"sub", "d,v,t", 0x00000022, 0xfc0007ff, WR_d|RD_s|RD_t, I1 }, +{"sub", "d,v,I", 0, (int) M_SUB_I, INSN_MACRO, I1 }, +{"sub.d", "D,V,T", 0x46200001, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I1 }, +{"sub.s", "D,V,T", 0x46000001, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, I1 }, +{"sub.ps", "D,V,T", 0x46c00001, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5 }, +{"subu", "d,v,t", 0x00000023, 0xfc0007ff, WR_d|RD_s|RD_t, I1 }, +{"subu", "d,v,I", 0, (int) M_SUBU_I, INSN_MACRO, I1 }, +{"suspend", "", 0x42000022, 0xffffffff, 0, V1 }, +{"suxc1", "S,t(b)", 0x4c00000d, 0xfc0007ff, SM|RD_S|RD_t|RD_b, I5 }, +{"sw", "t,o(b)", 0xac000000, 0xfc000000, SM|RD_t|RD_b, I1 }, +{"sw", "t,A(b)", 0, (int) M_SW_AB, INSN_MACRO, I1 }, +{"swc0", "E,o(b)", 0xe0000000, 0xfc000000, SM|RD_C0|RD_b, I1 }, +{"swc0", "E,A(b)", 0, (int) M_SWC0_AB, INSN_MACRO, I1 }, +{"swc1", "T,o(b)", 0xe4000000, 0xfc000000, SM|RD_T|RD_b|FP_S, I1 }, +{"swc1", "E,o(b)", 0xe4000000, 0xfc000000, SM|RD_T|RD_b|FP_S, I1 }, +{"swc1", "T,A(b)", 0, (int) M_SWC1_AB, INSN_MACRO, I1 }, +{"swc1", "E,A(b)", 0, (int) M_SWC1_AB, INSN_MACRO, I1 }, +{"s.s", "T,o(b)", 0xe4000000, 0xfc000000, SM|RD_T|RD_b|FP_S, I1 }, /* swc1 */ +{"s.s", "T,A(b)", 0, (int) M_SWC1_AB, INSN_MACRO, I1 }, +{"swc2", "E,o(b)", 0xe8000000, 0xfc000000, SM|RD_C2|RD_b, I1 }, +{"swc2", "E,A(b)", 0, (int) M_SWC2_AB, INSN_MACRO, I1 }, +{"swc3", "E,o(b)", 0xec000000, 0xfc000000, SM|RD_C3|RD_b, I1 }, +{"swc3", "E,A(b)", 0, (int) M_SWC3_AB, INSN_MACRO, I1 }, +{"swl", "t,o(b)", 0xa8000000, 0xfc000000, SM|RD_t|RD_b, I1 }, +{"swl", "t,A(b)", 0, (int) M_SWL_AB, INSN_MACRO, I1 }, +{"scache", "t,o(b)", 0xa8000000, 0xfc000000, RD_t|RD_b, I2 }, /* same */ +{"scache", "t,A(b)", 0, (int) M_SWL_AB, INSN_MACRO, I2 }, /* as swl */ +{"swr", "t,o(b)", 0xb8000000, 0xfc000000, SM|RD_t|RD_b, I1 }, +{"swr", "t,A(b)", 0, (int) M_SWR_AB, INSN_MACRO, I1 }, +{"invalidate", "t,o(b)",0xb8000000, 0xfc000000, RD_t|RD_b, I2 }, /* same */ +{"invalidate", "t,A(b)",0, (int) M_SWR_AB, INSN_MACRO, I2 }, /* as swr */ +{"swxc1", "S,t(b)", 0x4c000008, 0xfc0007ff, SM|RD_S|RD_t|RD_b, I4 }, +{"sync", "", 0x0000000f, 0xffffffff, INSN_SYNC, I2|G1 }, +{"sync.p", "", 0x0000040f, 0xffffffff, INSN_SYNC, I2 }, +{"sync.l", "", 0x0000000f, 0xffffffff, INSN_SYNC, I2 }, +{"syscall", "", 0x0000000c, 0xffffffff, TRAP, I1 }, +{"syscall", "B", 0x0000000c, 0xfc00003f, TRAP, I1 }, +{"teqi", "s,j", 0x040c0000, 0xfc1f0000, RD_s|TRAP, I2 }, +{"teq", "s,t", 0x00000034, 0xfc00ffff, RD_s|RD_t|TRAP, I2 }, +{"teq", "s,t,q", 0x00000034, 0xfc00003f, RD_s|RD_t|TRAP, I2 }, +{"teq", "s,j", 0x040c0000, 0xfc1f0000, RD_s|TRAP, I2 }, /* teqi */ +{"teq", "s,I", 0, (int) M_TEQ_I, INSN_MACRO, I2 }, +{"tgei", "s,j", 0x04080000, 0xfc1f0000, RD_s|TRAP, I2 }, +{"tge", "s,t", 0x00000030, 0xfc00ffff, RD_s|RD_t|TRAP, I2 }, +{"tge", "s,t,q", 0x00000030, 0xfc00003f, RD_s|RD_t|TRAP, I2 }, +{"tge", "s,j", 0x04080000, 0xfc1f0000, RD_s|TRAP, I2 }, /* tgei */ +{"tge", "s,I", 0, (int) M_TGE_I, INSN_MACRO, I2 }, +{"tgeiu", "s,j", 0x04090000, 0xfc1f0000, RD_s|TRAP, I2 }, +{"tgeu", "s,t", 0x00000031, 0xfc00ffff, RD_s|RD_t|TRAP, I2 }, +{"tgeu", "s,t,q", 0x00000031, 0xfc00003f, RD_s|RD_t|TRAP, I2 }, +{"tgeu", "s,j", 0x04090000, 0xfc1f0000, RD_s|TRAP, I2 }, /* tgeiu */ +{"tgeu", "s,I", 0, (int) M_TGEU_I, INSN_MACRO, I2 }, +{"tlbp", "", 0x42000008, 0xffffffff, INSN_TLB, I1|M1 }, +{"tlbr", "", 0x42000001, 0xffffffff, INSN_TLB, I1|M1 }, +{"tlbwi", "", 0x42000002, 0xffffffff, INSN_TLB, I1|M1 }, +{"tlbwr", "", 0x42000006, 0xffffffff, INSN_TLB, I1|M1 }, +{"tlti", "s,j", 0x040a0000, 0xfc1f0000, RD_s|TRAP, I2 }, +{"tlt", "s,t", 0x00000032, 0xfc00ffff, RD_s|RD_t|TRAP, I2 }, +{"tlt", "s,t,q", 0x00000032, 0xfc00003f, RD_s|RD_t|TRAP, I2 }, +{"tlt", "s,j", 0x040a0000, 0xfc1f0000, RD_s|TRAP, I2 }, /* tlti */ +{"tlt", "s,I", 0, (int) M_TLT_I, INSN_MACRO, I2 }, +{"tltiu", "s,j", 0x040b0000, 0xfc1f0000, RD_s|TRAP, I2 }, +{"tltu", "s,t", 0x00000033, 0xfc00ffff, RD_s|RD_t|TRAP, I2 }, +{"tltu", "s,t,q", 0x00000033, 0xfc00003f, RD_s|RD_t|TRAP, I2 }, +{"tltu", "s,j", 0x040b0000, 0xfc1f0000, RD_s|TRAP, I2 }, /* tltiu */ +{"tltu", "s,I", 0, (int) M_TLTU_I, INSN_MACRO, I2 }, +{"tnei", "s,j", 0x040e0000, 0xfc1f0000, RD_s|TRAP, I2 }, +{"tne", "s,t", 0x00000036, 0xfc00ffff, RD_s|RD_t|TRAP, I2 }, +{"tne", "s,t,q", 0x00000036, 0xfc00003f, RD_s|RD_t|TRAP, I2 }, +{"tne", "s,j", 0x040e0000, 0xfc1f0000, RD_s|TRAP, I2 }, /* tnei */ +{"tne", "s,I", 0, (int) M_TNE_I, INSN_MACRO, I2 }, +{"trunc.l.d", "D,S", 0x46200009, 0xffff003f, WR_D|RD_S|FP_D, I3 }, +{"trunc.l.s", "D,S", 0x46000009, 0xffff003f, WR_D|RD_S|FP_S, I3 }, +{"trunc.w.d", "D,S", 0x4620000d, 0xffff003f, WR_D|RD_S|FP_D, I2 }, +{"trunc.w.d", "D,S,x", 0x4620000d, 0xffff003f, WR_D|RD_S|FP_D, I2 }, +{"trunc.w.d", "D,S,t", 0, (int) M_TRUNCWD, INSN_MACRO, I1 }, +{"trunc.w.s", "D,S", 0x4600000d, 0xffff003f, WR_D|RD_S|FP_S, I2 }, +{"trunc.w.s", "D,S,x", 0x4600000d, 0xffff003f, WR_D|RD_S|FP_S, I2 }, +{"trunc.w.s", "D,S,t", 0, (int) M_TRUNCWS, INSN_MACRO, I1 }, +{"uld", "t,o(b)", 0, (int) M_ULD, INSN_MACRO, I3 }, +{"uld", "t,A(b)", 0, (int) M_ULD_A, INSN_MACRO, I3 }, +{"ulh", "t,o(b)", 0, (int) M_ULH, INSN_MACRO, I1 }, +{"ulh", "t,A(b)", 0, (int) M_ULH_A, INSN_MACRO, I1 }, +{"ulhu", "t,o(b)", 0, (int) M_ULHU, INSN_MACRO, I1 }, +{"ulhu", "t,A(b)", 0, (int) M_ULHU_A, INSN_MACRO, I1 }, +{"ulw", "t,o(b)", 0, (int) M_ULW, INSN_MACRO, I1 }, +{"ulw", "t,A(b)", 0, (int) M_ULW_A, INSN_MACRO, I1 }, +{"usd", "t,o(b)", 0, (int) M_USD, INSN_MACRO, I3 }, +{"usd", "t,A(b)", 0, (int) M_USD_A, INSN_MACRO, I3 }, +{"ush", "t,o(b)", 0, (int) M_USH, INSN_MACRO, I1 }, +{"ush", "t,A(b)", 0, (int) M_USH_A, INSN_MACRO, I1 }, +{"usw", "t,o(b)", 0, (int) M_USW, INSN_MACRO, I1 }, +{"usw", "t,A(b)", 0, (int) M_USW_A, INSN_MACRO, I1 }, +{"xor", "d,v,t", 0x00000026, 0xfc0007ff, WR_d|RD_s|RD_t, I1 }, +{"xor", "t,r,I", 0, (int) M_XOR_I, INSN_MACRO, I1 }, +{"xori", "t,r,i", 0x38000000, 0xfc000000, WR_t|RD_s, I1 }, +{"wait", "", 0x42000020, 0xffffffff, TRAP, I3|M1 }, +{"waiti", "", 0x42000020, 0xffffffff, TRAP, L1 }, +{"wb", "o(b)", 0xbc040000, 0xfc1f0000, SM|RD_b, L1 }, +/* No hazard protection on coprocessor instructions--they shouldn't + change the state of the processor and if they do it's up to the + user to put in nops as necessary. These are at the end so that the + disasembler recognizes more specific versions first. */ +{"c0", "C", 0x42000000, 0xfe000000, 0, I1 }, +{"c1", "C", 0x46000000, 0xfe000000, 0, I1 }, +{"c2", "C", 0x4a000000, 0xfe000000, 0, I1 }, +{"c3", "C", 0x4e000000, 0xfe000000, 0, I1 }, +{"cop0", "C", 0, (int) M_COP0, INSN_MACRO, I1 }, +{"cop1", "C", 0, (int) M_COP1, INSN_MACRO, I1 }, +{"cop2", "C", 0, (int) M_COP2, INSN_MACRO, I1 }, +{"cop3", "C", 0, (int) M_COP3, INSN_MACRO, I1 }, + + /* Conflicts with the 4650's "mul" instruction. Nobody's using the + 4010 any more, so move this insn out of the way. If the object + format gave us more info, we could do this right. */ +{"addciu", "t,r,j", 0x70000000, 0xfc000000, WR_t|RD_s,L1 }, +}; + +#define MIPS_NUM_OPCODES \ + ((sizeof mips_builtin_opcodes) / (sizeof (mips_builtin_opcodes[0]))) +const int bfd_mips_num_builtin_opcodes = MIPS_NUM_OPCODES; + +/* const removed from the following to allow for dynamic extensions to the + * built-in instruction set. */ +struct mips_opcode *mips_opcodes = + (struct mips_opcode *) mips_builtin_opcodes; +int bfd_mips_num_opcodes = MIPS_NUM_OPCODES; +#undef MIPS_NUM_OPCODES + diff --git a/contrib/binutils/opcodes/mips16-opc.c b/contrib/binutils/opcodes/mips16-opc.c new file mode 100644 index 000000000000..ab2d7c0795ac --- /dev/null +++ b/contrib/binutils/opcodes/mips16-opc.c @@ -0,0 +1,226 @@ +/* mips16-opc.c. Mips16 opcode table. + Copyright 1996, 1997 Free Software Foundation, Inc. + Contributed by Ian Lance Taylor, Cygnus Support + +This file is part of GDB, GAS, and the GNU binutils. + +GDB, GAS, and the GNU binutils are free software; you can redistribute +them and/or modify them under the terms of the GNU General Public +License as published by the Free Software Foundation; either version +1, or (at your option) any later version. + +GDB, GAS, and the GNU binutils are distributed in the hope that they +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 file; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA +02111-1307, USA. */ + +#include <stdio.h> +#include "sysdep.h" +#include "opcode/mips.h" + +/* This is the opcodes table for the mips16 processor. The format of + this table is intentionally identical to the one in mips-opc.c. + However, the special letters that appear in the argument string are + different, and the table uses some different flags. */ + +/* Use some short hand macros to keep down the length of the lines in + the opcodes table. */ + +#define UBD INSN_UNCOND_BRANCH_DELAY +#define BR MIPS16_INSN_BRANCH + +#define WR_x MIPS16_INSN_WRITE_X +#define WR_y MIPS16_INSN_WRITE_Y +#define WR_z MIPS16_INSN_WRITE_Z +#define WR_T MIPS16_INSN_WRITE_T +#define WR_SP MIPS16_INSN_WRITE_SP +#define WR_31 MIPS16_INSN_WRITE_31 +#define WR_Y MIPS16_INSN_WRITE_GPR_Y + +#define RD_x MIPS16_INSN_READ_X +#define RD_y MIPS16_INSN_READ_Y +#define RD_Z MIPS16_INSN_READ_Z +#define RD_T MIPS16_INSN_READ_T +#define RD_SP MIPS16_INSN_READ_SP +#define RD_31 MIPS16_INSN_READ_31 +#define RD_PC MIPS16_INSN_READ_PC +#define RD_X MIPS16_INSN_READ_GPR_X + +#define WR_HI INSN_WRITE_HI +#define WR_LO INSN_WRITE_LO +#define RD_HI INSN_READ_HI +#define RD_LO INSN_READ_LO + +#define TRAP INSN_TRAP + +#define I3 INSN_ISA3 + +#define T3 INSN_3900 + +const struct mips_opcode mips16_opcodes[] = { +{"nop", "", 0x6500, 0xffff, RD_Z }, /* move $0,$Z */ +{"la", "x,A", 0x0800, 0xf800, WR_x|RD_PC }, +{"abs", "x,w", 0, (int) M_ABS, INSN_MACRO }, +{"addiu", "y,x,4", 0x4000, 0xf810, WR_y|RD_x }, +{"addiu", "x,k", 0x4800, 0xf800, WR_x|RD_x }, +{"addiu", "S,K", 0x6300, 0xff00, WR_SP|RD_SP }, +{"addiu", "S,S,K", 0x6300, 0xff00, WR_SP|RD_SP }, +{"addiu", "x,P,V", 0x0800, 0xf800, WR_x|RD_PC }, +{"addiu", "x,S,V", 0x0000, 0xf800, WR_x|RD_SP }, +{"addu", "z,v,y", 0xe001, 0xf803, WR_z|RD_x|RD_y }, +{"addu", "y,x,4", 0x4000, 0xf810, WR_y|RD_x }, +{"addu", "x,k", 0x4800, 0xf800, WR_x|RD_x }, +{"addu", "S,K", 0x6300, 0xff00, WR_SP|RD_SP }, +{"addu", "S,S,K", 0x6300, 0xff00, WR_SP|RD_SP }, +{"addu", "x,P,V", 0x0800, 0xf800, WR_x|RD_PC }, +{"addu", "x,S,V", 0x0000, 0xf800, WR_x|RD_SP }, +{"and", "x,y", 0xe80c, 0xf81f, WR_x|RD_x|RD_y }, +{"b", "q", 0x1000, 0xf800, BR}, +{"beq", "x,y,p", 0, (int) M_BEQ, INSN_MACRO }, +{"beq", "x,U,p", 0, (int) M_BEQ_I, INSN_MACRO }, +{"beqz", "x,p", 0x2000, 0xf800, BR|RD_x }, +{"bge", "x,y,p", 0, (int) M_BGE, INSN_MACRO }, +{"bge", "x,8,p", 0, (int) M_BGE_I, INSN_MACRO }, +{"bgeu", "x,y,p", 0, (int) M_BGEU, INSN_MACRO }, +{"bgeu", "x,8,p", 0, (int) M_BGEU_I, INSN_MACRO }, +{"bgt", "x,y,p", 0, (int) M_BGT, INSN_MACRO }, +{"bgt", "x,8,p", 0, (int) M_BGT_I, INSN_MACRO }, +{"bgtu", "x,y,p", 0, (int) M_BGTU, INSN_MACRO }, +{"bgtu", "x,8,p", 0, (int) M_BGTU_I, INSN_MACRO }, +{"ble", "x,y,p", 0, (int) M_BLE, INSN_MACRO }, +{"ble", "x,8,p", 0, (int) M_BLE_I, INSN_MACRO }, +{"bleu", "x,y,p", 0, (int) M_BLEU, INSN_MACRO }, +{"bleu", "x,8,p", 0, (int) M_BLEU_I, INSN_MACRO }, +{"blt", "x,y,p", 0, (int) M_BLT, INSN_MACRO }, +{"blt", "x,8,p", 0, (int) M_BLT_I, INSN_MACRO }, +{"bltu", "x,y,p", 0, (int) M_BLTU, INSN_MACRO }, +{"bltu", "x,8,p", 0, (int) M_BLTU_I, INSN_MACRO }, +{"bne", "x,y,p", 0, (int) M_BNE, INSN_MACRO }, +{"bne", "x,U,p", 0, (int) M_BNE_I, INSN_MACRO }, +{"bnez", "x,p", 0x2800, 0xf800, BR|RD_x }, +{"break", "6", 0xe805, 0xf81f, TRAP }, +{"bteqz", "p", 0x6000, 0xff00, BR|RD_T }, +{"btnez", "p", 0x6100, 0xff00, BR|RD_T }, +{"cmpi", "x,U", 0x7000, 0xf800, WR_T|RD_x }, +{"cmp", "x,y", 0xe80a, 0xf81f, WR_T|RD_x|RD_y }, +{"cmp", "x,U", 0x7000, 0xf800, WR_T|RD_x }, +{"dla", "y,E", 0xfe00, 0xff00, WR_y|RD_PC, I3 }, +{"daddiu", "y,x,4", 0x4010, 0xf810, WR_y|RD_x, I3 }, +{"daddiu", "y,j", 0xfd00, 0xff00, WR_y|RD_y, I3 }, +{"daddiu", "S,K", 0xfb00, 0xff00, WR_SP|RD_SP, I3 }, +{"daddiu", "S,S,K", 0xfb00, 0xff00, WR_SP|RD_SP, I3 }, +{"daddiu", "y,P,W", 0xfe00, 0xff00, WR_y|RD_PC, I3 }, +{"daddiu", "y,S,W", 0xff00, 0xff00, WR_y|RD_SP, I3 }, +{"daddu", "z,v,y", 0xe000, 0xf803, WR_z|RD_x|RD_y, I3 }, +{"daddu", "y,x,4", 0x4010, 0xf810, WR_y|RD_x, I3 }, +{"daddu", "y,j", 0xfd00, 0xff00, WR_y|RD_y, I3 }, +{"daddu", "S,K", 0xfb00, 0xff00, WR_SP|RD_SP, I3 }, +{"daddu", "S,S,K", 0xfb00, 0xff00, WR_SP|RD_SP, I3 }, +{"daddu", "y,P,W", 0xfe00, 0xff00, WR_y|RD_PC, I3 }, +{"daddu", "y,S,W", 0xff00, 0xff00, WR_y|RD_SP, I3 }, +{"ddiv", "0,x,y", 0xe81e, 0xf81f, RD_x|RD_y|WR_HI|WR_LO, I3 }, +{"ddiv", "z,v,y", 0, (int) M_DDIV_3, INSN_MACRO }, +{"ddivu", "0,x,y", 0xe81f, 0xf81f, RD_x|RD_y|WR_HI|WR_LO, I3 }, +{"ddivu", "z,v,y", 0, (int) M_DDIVU_3, INSN_MACRO }, +{"div", "0,x,y", 0xe81a, 0xf81f, RD_x|RD_y|WR_HI|WR_LO }, +{"div", "z,v,y", 0, (int) M_DIV_3, INSN_MACRO }, +{"divu", "0,x,y", 0xe81b, 0xf81f, RD_x|RD_y|WR_HI|WR_LO }, +{"divu", "z,v,y", 0, (int) M_DIVU_3, INSN_MACRO }, +{"dmul", "z,v,y", 0, (int) M_DMUL, INSN_MACRO, I3 }, +{"dmult", "x,y", 0xe81c, 0xf81f, RD_x|RD_y|WR_HI|WR_LO, I3 }, +{"dmultu", "x,y", 0xe81d, 0xf81f, RD_x|RD_y|WR_HI|WR_LO, I3 }, +{"drem", "0,x,y", 0xe81e, 0xf81f, RD_x|RD_y|WR_HI|WR_LO, I3 }, +{"drem", "z,v,y", 0, (int) M_DREM_3, INSN_MACRO }, +{"dremu", "0,x,y", 0xe81f, 0xf81f, RD_x|RD_y|WR_HI|WR_LO, I3 }, +{"dremu", "z,v,y", 0, (int) M_DREMU_3, INSN_MACRO }, +{"dsllv", "y,x", 0xe814, 0xf81f, WR_y|RD_y|RD_x, I3 }, +{"dsll", "x,w,[", 0x3001, 0xf803, WR_x|RD_y, I3 }, +{"dsll", "y,x", 0xe814, 0xf81f, WR_y|RD_y|RD_x, I3 }, +{"dsrav", "y,x", 0xe817, 0xf81f, WR_y|RD_y|RD_x, I3 }, +{"dsra", "y,]", 0xe813, 0xf81f, WR_y|RD_y, I3 }, +{"dsra", "y,x", 0xe817, 0xf81f, WR_y|RD_y|RD_x, I3 }, +{"dsrlv", "y,x", 0xe816, 0xf81f, WR_y|RD_y|RD_x, I3 }, +{"dsrl", "y,]", 0xe808, 0xf81f, WR_y|RD_y, I3 }, +{"dsrl", "y,x", 0xe816, 0xf81f, WR_y|RD_y|RD_x, I3 }, +{"dsubu", "z,v,y", 0xe002, 0xf803, WR_z|RD_x|RD_y, I3 }, +{"dsubu", "y,x,4", 0, (int) M_DSUBU_I, INSN_MACRO }, +{"dsubu", "y,j", 0, (int) M_DSUBU_I_2, INSN_MACRO }, +{"exit", "L", 0xed09, 0xff1f, TRAP }, +{"exit", "L", 0xee09, 0xff1f, TRAP }, +{"exit", "L", 0xef09, 0xff1f, TRAP }, +{"entry", "l", 0xe809, 0xf81f, TRAP }, +{"extend", "e", 0xf000, 0xf800, 0 }, +{"jalr", "x", 0xe840, 0xf8ff, UBD|WR_31|RD_x }, +{"jalr", "R,x", 0xe840, 0xf8ff, UBD|WR_31|RD_x }, +{"jal", "x", 0xe840, 0xf8ff, UBD|WR_31|RD_x }, +{"jal", "R,x", 0xe840, 0xf8ff, UBD|WR_31|RD_x }, +{"jal", "a", 0x1800, 0xfc00, UBD|WR_31 }, +{"jalx", "a", 0x1c00, 0xfc00, UBD|WR_31 }, +{"jr", "x", 0xe800, 0xf8ff, UBD|RD_x }, +{"jr", "R", 0xe820, 0xffff, UBD|RD_31 }, +{"j", "x", 0xe800, 0xf8ff, UBD|RD_x }, +{"j", "R", 0xe820, 0xffff, UBD|RD_31 }, +{"lb", "y,5(x)", 0x8000, 0xf800, WR_y|RD_x }, +{"lbu", "y,5(x)", 0xa000, 0xf800, WR_y|RD_x }, +{"ld", "y,D(x)", 0x3800, 0xf800, WR_y|RD_x, I3 }, +{"ld", "y,B", 0xfc00, 0xff00, WR_y|RD_PC, I3 }, +{"ld", "y,D(P)", 0xfc00, 0xff00, WR_y|RD_PC, I3 }, +{"ld", "y,D(S)", 0xf800, 0xff00, WR_y|RD_SP, I3 }, +{"lh", "y,H(x)", 0x8800, 0xf800, WR_y|RD_x }, +{"lhu", "y,H(x)", 0xa800, 0xf800, WR_y|RD_x }, +{"li", "x,U", 0x6800, 0xf800, WR_x }, +{"lw", "y,W(x)", 0x9800, 0xf800, WR_y|RD_x }, +{"lw", "x,A", 0xb000, 0xf800, WR_x|RD_PC }, +{"lw", "x,V(P)", 0xb000, 0xf800, WR_x|RD_PC }, +{"lw", "x,V(S)", 0x9000, 0xf800, WR_x|RD_SP }, +{"lwu", "y,W(x)", 0xb800, 0xf800, WR_y|RD_x, I3 }, +{"mfhi", "x", 0xe810, 0xf8ff, WR_x|RD_HI }, +{"mflo", "x", 0xe812, 0xf8ff, WR_x|RD_LO }, +{"move", "y,X", 0x6700, 0xff00, WR_y|RD_X }, +{"move", "Y,Z", 0x6500, 0xff00, WR_Y|RD_Z }, +{"mul", "z,v,y", 0, (int) M_MUL, INSN_MACRO }, +{"mult", "x,y", 0xe818, 0xf81f, RD_x|RD_y|WR_HI|WR_LO }, +{"multu", "x,y", 0xe819, 0xf81f, RD_x|RD_y|WR_HI|WR_LO }, +{"neg", "x,w", 0xe80b, 0xf81f, WR_x|RD_y }, +{"not", "x,w", 0xe80f, 0xf81f, WR_x|RD_y }, +{"or", "x,y", 0xe80d, 0xf81f, WR_x|RD_x|RD_y }, +{"rem", "0,x,y", 0xe81a, 0xf81f, RD_x|RD_y|WR_HI|WR_LO }, +{"rem", "z,v,y", 0, (int) M_REM_3, INSN_MACRO }, +{"remu", "0,x,y", 0xe81b, 0xf81f, RD_x|RD_y|WR_HI|WR_LO }, +{"remu", "z,v,y", 0, (int) M_REMU_3, INSN_MACRO }, +{"sb", "y,5(x)", 0xc000, 0xf800, RD_y|RD_x }, +{"sd", "y,D(x)", 0x7800, 0xf800, RD_y|RD_x, I3 }, +{"sd", "y,D(S)", 0xf900, 0xff00, RD_y|RD_PC, I3 }, +{"sd", "R,C(S)", 0xfa00, 0xff00, RD_31|RD_PC }, +{"sh", "y,H(x)", 0xc800, 0xf800, RD_y|RD_x }, +{"sllv", "y,x", 0xe804, 0xf81f, WR_y|RD_y|RD_x }, +{"sll", "x,w,<", 0x3000, 0xf803, WR_x|RD_y }, +{"sll", "y,x", 0xe804, 0xf81f, WR_y|RD_y|RD_x }, +{"slti", "x,8", 0x5000, 0xf800, WR_T|RD_x }, +{"slt", "x,y", 0xe802, 0xf81f, WR_T|RD_x|RD_y }, +{"slt", "x,8", 0x5000, 0xf800, WR_T|RD_x }, +{"sltiu", "x,8", 0x5800, 0xf800, WR_T|RD_x }, +{"sltu", "x,y", 0xe803, 0xf81f, WR_T|RD_x|RD_y }, +{"sltu", "x,8", 0x5800, 0xf800, WR_T|RD_x }, +{"srav", "y,x", 0xe807, 0xf81f, WR_y|RD_y|RD_x }, +{"sra", "x,w,<", 0x3003, 0xf803, WR_x|RD_y }, +{"sra", "y,x", 0xe807, 0xf81f, WR_y|RD_y|RD_x }, +{"srlv", "y,x", 0xe806, 0xf81f, WR_y|RD_y|RD_x }, +{"srl", "x,w,<", 0x3002, 0xf803, WR_x|RD_y }, +{"srl", "y,x", 0xe806, 0xf81f, WR_y|RD_y|RD_x }, +{"subu", "z,v,y", 0xe003, 0xf803, WR_z|RD_x|RD_y }, +{"subu", "y,x,4", 0, (int) M_SUBU_I, INSN_MACRO }, +{"subu", "x,k", 0, (int) M_SUBU_I_2, INSN_MACRO }, +{"sw", "y,W(x)", 0xd800, 0xf800, RD_y|RD_x }, +{"sw", "x,V(S)", 0xd000, 0xf800, RD_x|RD_SP }, +{"sw", "R,V(S)", 0x6200, 0xff00, RD_31|RD_SP }, +{"xor", "x,y", 0xe80e, 0xf81f, WR_x|RD_x|RD_y }, +}; + +const int bfd_mips16_num_opcodes = + ((sizeof mips16_opcodes) / (sizeof (mips16_opcodes[0]))); diff --git a/contrib/com_err/com_err.3 b/contrib/com_err/com_err.3 index e6eeea13b852..7f6685e4a63c 100644 --- a/contrib/com_err/com_err.3 +++ b/contrib/com_err/com_err.3 @@ -1,7 +1,7 @@ .\" Copyright (c) 1988 Massachusetts Institute of Technology, .\" Student Information Processing Board. All rights reserved. .\" -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/com_err/com_err.3,v 1.1 1999/09/04 09:48:58 markm Exp $ .\" .TH COM_ERR 3 "22 Nov 1988" SIPB .SH NAME diff --git a/contrib/com_err/com_err.h b/contrib/com_err/com_err.h index 19cf034d6840..b19a12385b38 100644 --- a/contrib/com_err/com_err.h +++ b/contrib/com_err/com_err.h @@ -37,7 +37,7 @@ */ /* $Id: com_err.h,v 1.3 1998/05/02 20:13:28 assar Exp $ */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/com_err/com_err.h,v 1.2 2000/01/14 18:28:23 bde Exp $ */ /* MIT compatible com_err library */ diff --git a/contrib/com_err/com_right.h b/contrib/com_err/com_right.h index c329bced9785..7084ec1be6a1 100644 --- a/contrib/com_err/com_right.h +++ b/contrib/com_err/com_right.h @@ -37,7 +37,7 @@ */ /* $Id: com_right.h,v 1.8 1998/02/17 21:19:43 bg Exp $ */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/com_err/com_right.h,v 1.2 2000/01/14 18:51:30 bde Exp $ */ #ifndef __COM_RIGHT_H__ #define __COM_RIGHT_H__ diff --git a/contrib/com_err/compile_et.1 b/contrib/com_err/compile_et.1 index 091038079535..31607f866141 100644 --- a/contrib/com_err/compile_et.1 +++ b/contrib/com_err/compile_et.1 @@ -1,7 +1,7 @@ .\" Copyright (c) 1988 Massachusetts Institute of Technology, .\" Student Information Processing Board. All rights reserved. .\" -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/com_err/compile_et.1,v 1.1 1999/09/04 09:48:59 markm Exp $ .\" .Dd November 22, 1988 .Os diff --git a/contrib/cpio/FREEBSD-upgrade b/contrib/cpio/FREEBSD-upgrade index 8e2bf3600b97..c3964828f485 100644 --- a/contrib/cpio/FREEBSD-upgrade +++ b/contrib/cpio/FREEBSD-upgrade @@ -1,4 +1,4 @@ -$FreeBSD$ +$FreeBSD: src/contrib/cpio/FREEBSD-upgrade,v 1.3 1999/09/08 11:16:36 obrien Exp $ GNU cpio 2.4.2 originals can be found at: ftp://prep.ai.mit.edu/pub/gnu diff --git a/contrib/cpio/main.c b/contrib/cpio/main.c index 20829ef50e1a..6309973999e4 100644 --- a/contrib/cpio/main.c +++ b/contrib/cpio/main.c @@ -19,7 +19,7 @@ David MacKenzie <djm@gnu.ai.mit.edu>, and John Oleynick <juo@klinzhai.rutgers.edu>. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/cpio/main.c,v 1.3 1999/09/15 01:47:13 peter Exp $ */ #include <stdio.h> #include <getopt.h> diff --git a/contrib/cvs/FREEBSD-upgrade b/contrib/cvs/FREEBSD-upgrade index c5ff053c2956..1bcc7445fd18 100644 --- a/contrib/cvs/FREEBSD-upgrade +++ b/contrib/cvs/FREEBSD-upgrade @@ -1,4 +1,4 @@ -$FreeBSD$ +$FreeBSD: src/contrib/cvs/FREEBSD-upgrade,v 1.7.2.1 2000/10/31 09:37:43 obrien Exp $ MAINTAINER= peter@FreeBSD.org diff --git a/contrib/cvs/contrib/log.pl b/contrib/cvs/contrib/log.pl index d67d58f507c6..0423d5eae014 100644 --- a/contrib/cvs/contrib/log.pl +++ b/contrib/cvs/contrib/log.pl @@ -5,7 +5,7 @@ # # XXX -- I HATE Perl! This *will* be re-written in shell/awk/sed soon! # -# $FreeBSD$ +# $FreeBSD: src/contrib/cvs/contrib/log.pl,v 1.4.2.1 2000/10/31 09:37:44 obrien Exp $ # Usage: log.pl [-u user] [[-m mailto] ...] [-s] -f logfile 'dirname file ...' # diff --git a/contrib/cvs/diff/diff3.c b/contrib/cvs/diff/diff3.c index ab68eaf25ee9..9f66eae0cabe 100644 --- a/contrib/cvs/diff/diff3.c +++ b/contrib/cvs/diff/diff3.c @@ -13,7 +13,7 @@ */ /* - * $FreeBSD$ + * $FreeBSD: src/contrib/cvs/diff/diff3.c,v 1.2.2.1 2000/10/31 09:37:45 obrien Exp $ */ /* Written by Randy Smith */ diff --git a/contrib/cvs/lib/md5.h b/contrib/cvs/lib/md5.h index 3b5ba05891f2..f9d886b61a8c 100644 --- a/contrib/cvs/lib/md5.h +++ b/contrib/cvs/lib/md5.h @@ -1,7 +1,7 @@ /* See md5.c for explanation and copyright information. */ /* - * $FreeBSD$ + * $FreeBSD: src/contrib/cvs/lib/md5.h,v 1.2 1999/12/11 15:10:02 peter Exp $ */ #ifndef MD5_H diff --git a/contrib/cvs/man/cvs.1 b/contrib/cvs/man/cvs.1 index 395da71ce0f1..87015908947c 100644 --- a/contrib/cvs/man/cvs.1 +++ b/contrib/cvs/man/cvs.1 @@ -1,4 +1,4 @@ -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/cvs/man/cvs.1,v 1.15.2.1 2000/10/31 09:37:50 obrien Exp $ .de Id .ds Rv \\$3 .ds Dt \\$4 diff --git a/contrib/cvs/man/cvsbug.8 b/contrib/cvs/man/cvsbug.8 index 460ca4640d95..4fb88e1ad963 100644 --- a/contrib/cvs/man/cvsbug.8 +++ b/contrib/cvs/man/cvsbug.8 @@ -17,7 +17,7 @@ .\" General Public License for more details. .\" .\" --------------------------------------------------------------------------- -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/cvs/man/cvsbug.8,v 1.2.6.1 2000/10/31 09:37:50 obrien Exp $ .nh .TH CVSBUG 8 xVERSIONx "February 1993" .SH NAME diff --git a/contrib/cvs/src/client.c b/contrib/cvs/src/client.c index 31196137835d..5c890363f98b 100644 --- a/contrib/cvs/src/client.c +++ b/contrib/cvs/src/client.c @@ -13,7 +13,7 @@ GNU General Public License for more details. */ /* - * $FreeBSD$ + * $FreeBSD: src/contrib/cvs/src/client.c,v 1.2.2.1 2000/10/31 09:37:51 obrien Exp $ */ #ifdef HAVE_CONFIG_H diff --git a/contrib/cvs/src/commit.c b/contrib/cvs/src/commit.c index 3d46e25adcfd..0213254ebd2c 100644 --- a/contrib/cvs/src/commit.c +++ b/contrib/cvs/src/commit.c @@ -12,7 +12,7 @@ * * The call is: cvs commit [options] files... * - * $FreeBSD$ + * $FreeBSD: src/contrib/cvs/src/commit.c,v 1.8.2.1 2000/10/31 09:37:52 obrien Exp $ */ #include <assert.h> diff --git a/contrib/cvs/src/cvs.h b/contrib/cvs/src/cvs.h index 6489f07af804..eec082d7fb0f 100644 --- a/contrib/cvs/src/cvs.h +++ b/contrib/cvs/src/cvs.h @@ -9,7 +9,7 @@ /* * basic information used in all source files * - * $FreeBSD$ + * $FreeBSD: src/contrib/cvs/src/cvs.h,v 1.11.2.1 2000/10/31 09:37:52 obrien Exp $ */ diff --git a/contrib/cvs/src/diff.c b/contrib/cvs/src/diff.c index 15353e63f61c..df060570f12a 100644 --- a/contrib/cvs/src/diff.c +++ b/contrib/cvs/src/diff.c @@ -13,7 +13,7 @@ * Without any file arguments, runs diff against all the currently modified * files. * - * $FreeBSD$ + * $FreeBSD: src/contrib/cvs/src/diff.c,v 1.14 1999/12/11 12:50:08 peter Exp $ */ #include "cvs.h" diff --git a/contrib/cvs/src/filesubr.c b/contrib/cvs/src/filesubr.c index 2cccbcdb1fa9..7ad8d4d42965 100644 --- a/contrib/cvs/src/filesubr.c +++ b/contrib/cvs/src/filesubr.c @@ -18,7 +18,7 @@ file system semantics. */ /* - * $FreeBSD$ + * $FreeBSD: src/contrib/cvs/src/filesubr.c,v 1.6.2.1 2000/10/31 09:37:52 obrien Exp $ */ #include "cvs.h" diff --git a/contrib/cvs/src/import.c b/contrib/cvs/src/import.c index 3f77dc90906f..a6b197b0d3e1 100644 --- a/contrib/cvs/src/import.c +++ b/contrib/cvs/src/import.c @@ -15,7 +15,7 @@ * * Additional arguments specify more Vendor Release Tags. * - * $FreeBSD$ + * $FreeBSD: src/contrib/cvs/src/import.c,v 1.7.2.1 2000/10/31 09:37:53 obrien Exp $ */ #include "cvs.h" diff --git a/contrib/cvs/src/lock.c b/contrib/cvs/src/lock.c index 30c72f90f472..df631d803fc7 100644 --- a/contrib/cvs/src/lock.c +++ b/contrib/cvs/src/lock.c @@ -9,7 +9,7 @@ * * Lock file support for CVS. * - * $FreeBSD$ + * $FreeBSD: src/contrib/cvs/src/lock.c,v 1.7.2.1 2000/10/31 09:37:53 obrien Exp $ */ /* The node Concurrency in doc/cvs.texinfo has a brief introduction to diff --git a/contrib/cvs/src/login.c b/contrib/cvs/src/login.c index cc564c066837..944273f77683 100644 --- a/contrib/cvs/src/login.c +++ b/contrib/cvs/src/login.c @@ -6,7 +6,7 @@ * * Allow user to log in for an authenticating server. * - * $FreeBSD$ + * $FreeBSD: src/contrib/cvs/src/login.c,v 1.3.2.1 2000/10/31 09:37:53 obrien Exp $ */ #include "cvs.h" diff --git a/contrib/cvs/src/logmsg.c b/contrib/cvs/src/logmsg.c index a5e16b74c4d9..c1b709362d79 100644 --- a/contrib/cvs/src/logmsg.c +++ b/contrib/cvs/src/logmsg.c @@ -5,7 +5,7 @@ * You may distribute under the terms of the GNU General Public License as * specified in the README file that comes with the CVS source distribution. * - * $FreeBSD$ + * $FreeBSD: src/contrib/cvs/src/logmsg.c,v 1.6.2.1 2000/10/31 09:37:53 obrien Exp $ */ #include "cvs.h" diff --git a/contrib/cvs/src/main.c b/contrib/cvs/src/main.c index cad210bb5d0d..a2c2c69f5e31 100644 --- a/contrib/cvs/src/main.c +++ b/contrib/cvs/src/main.c @@ -10,7 +10,7 @@ * Credit to Dick Grune, Vrije Universiteit, Amsterdam, for writing * the shell-script CVS system that this is based on. * - * $FreeBSD$ + * $FreeBSD: src/contrib/cvs/src/main.c,v 1.18.2.1 2000/10/31 09:37:53 obrien Exp $ */ #include <assert.h> diff --git a/contrib/cvs/src/mkmodules.c b/contrib/cvs/src/mkmodules.c index 49cda18cf885..686839732ccd 100644 --- a/contrib/cvs/src/mkmodules.c +++ b/contrib/cvs/src/mkmodules.c @@ -5,7 +5,7 @@ * You may distribute under the terms of the GNU General Public License as * specified in the README file that comes with the CVS kit. * - * $FreeBSD$ + * $FreeBSD: src/contrib/cvs/src/mkmodules.c,v 1.9.2.1 2000/10/31 09:37:53 obrien Exp $ */ #include "cvs.h" diff --git a/contrib/cvs/src/prepend_args.c b/contrib/cvs/src/prepend_args.c index 12322ce48bdf..7be868a25815 100644 --- a/contrib/cvs/src/prepend_args.c +++ b/contrib/cvs/src/prepend_args.c @@ -16,7 +16,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/cvs/src/prepend_args.c,v 1.2 1999/12/04 08:44:05 obrien Exp $ */ #ifdef HAVE_CONFIG_H diff --git a/contrib/cvs/src/prepend_args.h b/contrib/cvs/src/prepend_args.h index 6708442ec124..feabbe6b7058 100644 --- a/contrib/cvs/src/prepend_args.h +++ b/contrib/cvs/src/prepend_args.h @@ -16,7 +16,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/cvs/src/prepend_args.h,v 1.1 1999/12/04 01:23:26 obrien Exp $ */ /* This code, taken from GNU Grep, originally used the "PARAM" macro, as the current GNU coding standards requires. Older GNU code used the "PROTO" diff --git a/contrib/cvs/src/rcs.c b/contrib/cvs/src/rcs.c index 1e9edce9638b..9557b88e26a0 100644 --- a/contrib/cvs/src/rcs.c +++ b/contrib/cvs/src/rcs.c @@ -7,7 +7,7 @@ * The routines contained in this file do all the rcs file parsing and * manipulation * - * $FreeBSD$ + * $FreeBSD: src/contrib/cvs/src/rcs.c,v 1.19.2.1 2000/10/31 09:37:53 obrien Exp $ */ #include <assert.h> diff --git a/contrib/cvs/src/rcs.h b/contrib/cvs/src/rcs.h index 397488ea89de..4316cf09a75d 100644 --- a/contrib/cvs/src/rcs.h +++ b/contrib/cvs/src/rcs.h @@ -7,7 +7,7 @@ * * RCS source control definitions needed by rcs.c and friends * - * $FreeBSD$ + * $FreeBSD: src/contrib/cvs/src/rcs.h,v 1.7.2.1 2000/10/31 09:37:54 obrien Exp $ */ /* Strings which indicate a conflict if they occur at the start of a line. */ diff --git a/contrib/cvs/src/rcscmds.c b/contrib/cvs/src/rcscmds.c index bcdffd5ad312..a6b742237a5a 100644 --- a/contrib/cvs/src/rcscmds.c +++ b/contrib/cvs/src/rcscmds.c @@ -8,7 +8,7 @@ * The functions in this file provide an interface for performing * operations directly on RCS files. * - * $FreeBSD$ + * $FreeBSD: src/contrib/cvs/src/rcscmds.c,v 1.7 1999/12/11 12:50:09 peter Exp $ */ #include "cvs.h" diff --git a/contrib/cvs/src/recurse.c b/contrib/cvs/src/recurse.c index 09e1cad00a11..51ba4eccfd61 100644 --- a/contrib/cvs/src/recurse.c +++ b/contrib/cvs/src/recurse.c @@ -6,7 +6,7 @@ * * General recursion handler * - * $FreeBSD$ + * $FreeBSD: src/contrib/cvs/src/recurse.c,v 1.6.2.1 2000/10/31 09:37:54 obrien Exp $ */ #include "cvs.h" diff --git a/contrib/cvs/src/server.c b/contrib/cvs/src/server.c index a53dc797ee5a..8a0cefb9e07b 100644 --- a/contrib/cvs/src/server.c +++ b/contrib/cvs/src/server.c @@ -9,7 +9,7 @@ GNU General Public License for more details. */ /* - * $FreeBSD$ + * $FreeBSD: src/contrib/cvs/src/server.c,v 1.13.2.1 2000/10/31 09:37:57 obrien Exp $ */ #include <assert.h> diff --git a/contrib/cvs/src/update.c b/contrib/cvs/src/update.c index bec23971de1f..6c6cbaecc400 100644 --- a/contrib/cvs/src/update.c +++ b/contrib/cvs/src/update.c @@ -32,7 +32,7 @@ * directories added to the repository are automatically created and updated * as well. * - * $FreeBSD$ + * $FreeBSD: src/contrib/cvs/src/update.c,v 1.6.2.1 2000/10/31 09:37:58 obrien Exp $ */ #include "cvs.h" diff --git a/contrib/diff/diff.c b/contrib/diff/diff.c index 2b2eec0236f2..2e8f5c660b08 100644 --- a/contrib/diff/diff.c +++ b/contrib/diff/diff.c @@ -20,7 +20,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* GNU DIFF was written by Mike Haertel, David Hayes, Richard Stallman, Len Tower, and Paul Eggert. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/diff/diff.c,v 1.3 1999/11/26 02:51:44 obrien Exp $ */ #define GDIFF_MAIN #include "diff.h" diff --git a/contrib/diff/prepend_args.c b/contrib/diff/prepend_args.c index 27f6da47403f..5bd7dfe92669 100644 --- a/contrib/diff/prepend_args.c +++ b/contrib/diff/prepend_args.c @@ -16,7 +16,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/diff/prepend_args.c,v 1.1 1999/11/26 02:51:44 obrien Exp $ */ #ifdef HAVE_CONFIG_H diff --git a/contrib/diff/prepend_args.h b/contrib/diff/prepend_args.h index 3f72cc2fd112..a46de62ff1eb 100644 --- a/contrib/diff/prepend_args.h +++ b/contrib/diff/prepend_args.h @@ -16,6 +16,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/diff/prepend_args.h,v 1.1 1999/11/26 02:51:44 obrien Exp $ */ void prepend_default_options PARAMS ((char const *, int *, char ***)); diff --git a/contrib/diff/util.c b/contrib/diff/util.c index a1e46dc95729..e169f932374c 100644 --- a/contrib/diff/util.c +++ b/contrib/diff/util.c @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with GNU DIFF; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/diff/util.c,v 1.2.6.2 2000/09/20 02:24:32 jkh Exp $ */ #include "diff.h" diff --git a/contrib/gcc/INSTALL b/contrib/gcc/INSTALL new file mode 100644 index 000000000000..a7c63d57de89 --- /dev/null +++ b/contrib/gcc/INSTALL @@ -0,0 +1,2188 @@ +This is Info file INSTALL, produced by Makeinfo version 1.68 from the +input file install1.texi. + + This file documents the installation of the GNU compiler. Copyright +(C) 1988, 1989, 1992, 1994, 1995 Free Software Foundation, Inc. You +may copy, distribute, and modify it freely as long as you preserve this +copyright notice and permission notice. + + +File: INSTALL, Node: Installation, Up: (dir) + +Installing GNU CC +***************** + + Note most of this information is out of date and superceded by the +EGCS install procedures. It is provided for historical reference only. + +* Menu: + +* Configurations:: Configurations Supported by GNU CC. +* Other Dir:: Compiling in a separate directory (not where the source is). +* Cross-Compiler:: Building and installing a cross-compiler. +* Sun Install:: See below for installation on the Sun. +* VMS Install:: See below for installation on VMS. +* Collect2:: How `collect2' works; how it finds `ld'. +* Header Dirs:: Understanding the standard header file directories. + + Here is the procedure for installing GNU CC on a Unix system. See +*Note VMS Install::, for VMS systems. In this section we assume you +compile in the same directory that contains the source files; see *Note +Other Dir::, to find out how to compile in a separate directory on Unix +systems. + + You cannot install GNU C by itself on MSDOS; it will not compile +under any MSDOS compiler except itself. You need to get the complete +compilation package DJGPP, which includes binaries as well as sources, +and includes all the necessary compilation tools and libraries. + + 1. If you have built GNU CC previously in the same directory for a + different target machine, do `make distclean' to delete all files + that might be invalid. One of the files this deletes is + `Makefile'; if `make distclean' complains that `Makefile' does not + exist, it probably means that the directory is already suitably + clean. + + 2. On a System V release 4 system, make sure `/usr/bin' precedes + `/usr/ucb' in `PATH'. The `cc' command in `/usr/ucb' uses + libraries which have bugs. + + 3. Specify the host, build and target machine configurations. You do + this by running the file `configure'. + + The "build" machine is the system which you are using, the "host" + machine is the system where you want to run the resulting compiler + (normally the build machine), and the "target" machine is the + system for which you want the compiler to generate code. + + If you are building a compiler to produce code for the machine it + runs on (a native compiler), you normally do not need to specify + any operands to `configure'; it will try to guess the type of + machine you are on and use that as the build, host and target + machines. So you don't need to specify a configuration when + building a native compiler unless `configure' cannot figure out + what your configuration is or guesses wrong. + + In those cases, specify the build machine's "configuration name" + with the `--host' option; the host and target will default to be + the same as the host machine. (If you are building a + cross-compiler, see *Note Cross-Compiler::.) + + Here is an example: + + ./configure --host=sparc-sun-sunos4.1 + + A configuration name may be canonical or it may be more or less + abbreviated. + + A canonical configuration name has three parts, separated by + dashes. It looks like this: `CPU-COMPANY-SYSTEM'. (The three + parts may themselves contain dashes; `configure' can figure out + which dashes serve which purpose.) For example, + `m68k-sun-sunos4.1' specifies a Sun 3. + + You can also replace parts of the configuration by nicknames or + aliases. For example, `sun3' stands for `m68k-sun', so + `sun3-sunos4.1' is another way to specify a Sun 3. You can also + use simply `sun3-sunos', since the version of SunOS is assumed by + default to be version 4. + + You can specify a version number after any of the system types, + and some of the CPU types. In most cases, the version is + irrelevant, and will be ignored. So you might as well specify the + version if you know it. + + See *Note Configurations::, for a list of supported configuration + names and notes on many of the configurations. You should check + the notes in that section before proceeding any further with the + installation of GNU CC. + + There are four additional options you can specify independently to + describe variant hardware and software configurations. These are + `--with-gnu-as', `--with-gnu-ld', `--with-stabs' and `--nfp'. + + `--with-gnu-as' + If you will use GNU CC with the GNU assembler (GAS), you + should declare this by using the `--with-gnu-as' option when + you run `configure'. + + Using this option does not install GAS. It only modifies the + output of GNU CC to work with GAS. Building and installing + GAS is up to you. + + Conversely, if you *do not* wish to use GAS and do not specify + `--with-gnu-as' when building GNU CC, it is up to you to make + sure that GAS is not installed. GNU CC searches for a + program named `as' in various directories; if the program it + finds is GAS, then it runs GAS. If you are not sure where + GNU CC finds the assembler it is using, try specifying `-v' + when you run it. + + The systems where it makes a difference whether you use GAS + are + `hppa1.0-ANY-ANY', `hppa1.1-ANY-ANY', `i386-ANY-sysv', + `i386-ANY-isc', + `i860-ANY-bsd', `m68k-bull-sysv', + `m68k-hp-hpux', `m68k-sony-bsd', + `m68k-altos-sysv', `m68000-hp-hpux', + `m68000-att-sysv', `ANY-lynx-lynxos', and `mips-ANY'). On + any other system, `--with-gnu-as' has no effect. + + On the systems listed above (except for the HP-PA, for ISC on + the 386, and for `mips-sgi-irix5.*'), if you use GAS, you + should also use the GNU linker (and specify `--with-gnu-ld'). + + `--with-gnu-ld' + Specify the option `--with-gnu-ld' if you plan to use the GNU + linker with GNU CC. + + This option does not cause the GNU linker to be installed; it + just modifies the behavior of GNU CC to work with the GNU + linker. + + `--with-stabs' + On MIPS based systems and on Alphas, you must specify whether + you want GNU CC to create the normal ECOFF debugging format, + or to use BSD-style stabs passed through the ECOFF symbol + table. The normal ECOFF debug format cannot fully handle + languages other than C. BSD stabs format can handle other + languages, but it only works with the GNU debugger GDB. + + Normally, GNU CC uses the ECOFF debugging format by default; + if you prefer BSD stabs, specify `--with-stabs' when you + configure GNU CC. + + No matter which default you choose when you configure GNU CC, + the user can use the `-gcoff' and `-gstabs+' options to + specify explicitly the debug format for a particular + compilation. + + `--with-stabs' is meaningful on the ISC system on the 386, + also, if `--with-gas' is used. It selects use of stabs + debugging information embedded in COFF output. This kind of + debugging information supports C++ well; ordinary COFF + debugging information does not. + + `--with-stabs' is also meaningful on 386 systems running + SVR4. It selects use of stabs debugging information embedded + in ELF output. The C++ compiler currently (2.6.0) does not + support the DWARF debugging information normally used on 386 + SVR4 platforms; stabs provide a workable alternative. This + requires gas and gdb, as the normal SVR4 tools can not + generate or interpret stabs. + + `--nfp' + On certain systems, you must specify whether the machine has + a floating point unit. These systems include + `m68k-sun-sunosN' and `m68k-isi-bsd'. On any other system, + `--nfp' currently has no effect, though perhaps there are + other systems where it could usefully make a difference. + + `--enable-haifa' + `--disable-haifa' + Use `--enable-haifa' to enable use of an experimental + instruction scheduler (from IBM Haifa). This may or may not + produce better code. Some targets on which it is known to be + a win enable it by default; use `--disable-haifa' to disable + it in these cases. `configure' will print out whether the + Haifa scheduler is enabled when it is run. + + `--enable-threads=TYPE' + Certain systems, notably Linux-based GNU systems, can't be + relied on to supply a threads facility for the Objective C + runtime and so will default to single-threaded runtime. They + may, however, have a library threads implementation + available, in which case threads can be enabled with this + option by supplying a suitable TYPE, probably `posix'. The + possibilities for TYPE are `single', `posix', `win32', + `solaris', `irix' and `mach'. + + `--enable-checking' + When you specify this option, the compiler is built to + perform checking of tree node types when referencing fields + of that node. This does not change the generated code, but + adds error checking within the compiler. This will slow down + the compiler and may only work properly if you are building + the compiler with GNU C. + + The `configure' script searches subdirectories of the source + directory for other compilers that are to be integrated into GNU + CC. The GNU compiler for C++, called G++ is in a subdirectory + named `cp'. `configure' inserts rules into `Makefile' to build + all of those compilers. + + Here we spell out what files will be set up by `configure'. + Normally you need not be concerned with these files. + + * A file named `config.h' is created that contains a `#include' + of the top-level config file for the machine you will run the + compiler on (*note The Configuration File: + (gcc.info)Config.). This file is responsible for defining + information about the host machine. It includes `tm.h'. + + The top-level config file is located in the subdirectory + `config'. Its name is always `xm-SOMETHING.h'; usually + `xm-MACHINE.h', but there are some exceptions. + + If your system does not support symbolic links, you might + want to set up `config.h' to contain a `#include' command + which refers to the appropriate file. + + * A file named `tconfig.h' is created which includes the + top-level config file for your target machine. This is used + for compiling certain programs to run on that machine. + + * A file named `tm.h' is created which includes the + machine-description macro file for your target machine. It + should be in the subdirectory `config' and its name is often + `MACHINE.h'. + + * The command file `configure' also constructs the file + `Makefile' by adding some text to the template file + `Makefile.in'. The additional text comes from files in the + `config' directory, named `t-TARGET' and `x-HOST'. If these + files do not exist, it means nothing needs to be added for a + given target or host. + + 4. The standard directory for installing GNU CC is `/usr/local/lib'. + If you want to install its files somewhere else, specify + `--prefix=DIR' when you run `configure'. Here DIR is a directory + name to use instead of `/usr/local' for all purposes with one + exception: the directory `/usr/local/include' is searched for + header files no matter where you install the compiler. To override + this name, use the `--with-local-prefix' option below. The + directory you specify need not exist, but its parent directory + must exist. + + 5. Specify `--with-local-prefix=DIR' if you want the compiler to + search directory `DIR/include' for locally installed header files + *instead* of `/usr/local/include'. + + You should specify `--with-local-prefix' *only* if your site has a + different convention (not `/usr/local') for where to put + site-specific files. + + The default value for `--with-local-prefix' is `/usr/local' + regardless of the value of `--prefix'. Specifying `--prefix' has + no effect on which directory GNU CC searches for local header + files. This may seem counterintuitive, but actually it is logical. + + The purpose of `--prefix' is to specify where to *install GNU CC*. + The local header files in `/usr/local/include'--if you put any in + that directory--are not part of GNU CC. They are part of other + programs--perhaps many others. (GNU CC installs its own header + files in another directory which is based on the `--prefix' value.) + + *Do not* specify `/usr' as the `--with-local-prefix'! The + directory you use for `--with-local-prefix' *must not* contain any + of the system's standard header files. If it did contain them, + certain programs would be miscompiled (including GNU Emacs, on + certain targets), because this would override and nullify the + header file corrections made by the `fixincludes' script. + + Indications are that people who use this option use it based on + mistaken ideas of what it is for. People use it as if it specified + where to install part of GNU CC. Perhaps they make this assumption + because installing GNU CC creates the directory. + + 6. Make sure the Bison parser generator is installed. (This is + unnecessary if the Bison output files `c-parse.c' and `cexp.c' are + more recent than `c-parse.y' and `cexp.y' and you do not plan to + change the `.y' files.) + + Bison versions older than Sept 8, 1988 will produce incorrect + output for `c-parse.c'. + + 7. If you have chosen a configuration for GNU CC which requires other + GNU tools (such as GAS or the GNU linker) instead of the standard + system tools, install the required tools in the build directory + under the names `as', `ld' or whatever is appropriate. This will + enable the compiler to find the proper tools for compilation of + the program `enquire'. + + Alternatively, you can do subsequent compilation using a value of + the `PATH' environment variable such that the necessary GNU tools + come before the standard system tools. + + 8. Build the compiler. Just type `make LANGUAGES=c' in the compiler + directory. + + `LANGUAGES=c' specifies that only the C compiler should be + compiled. The makefile normally builds compilers for all the + supported languages; currently, C, C++ and Objective C. However, + C is the only language that is sure to work when you build with + other non-GNU C compilers. In addition, building anything but C + at this stage is a waste of time. + + In general, you can specify the languages to build by typing the + argument `LANGUAGES="LIST"', where LIST is one or more words from + the list `c', `c++', and `objective-c'. If you have any + additional GNU compilers as subdirectories of the GNU CC source + directory, you may also specify their names in this list. + + Ignore any warnings you may see about "statement not reached" in + `insn-emit.c'; they are normal. Also, warnings about "unknown + escape sequence" are normal in `genopinit.c' and perhaps some + other files. Likewise, you should ignore warnings about "constant + is so large that it is unsigned" in `insn-emit.c' and + `insn-recog.c', a warning about a comparison always being zero in + `enquire.o', and warnings about shift counts exceeding type widths + in `cexp.y'. Any other compilation errors may represent bugs in + the port to your machine or operating system, and should be + investigated and reported. + + Some commercial compilers fail to compile GNU CC because they have + bugs or limitations. For example, the Microsoft compiler is said + to run out of macro space. Some Ultrix compilers run out of + expression space; then you need to break up the statement where + the problem happens. + + 9. If you are building a cross-compiler, stop here. *Note + Cross-Compiler::. + + 10. Move the first-stage object files and executables into a + subdirectory with this command: + + make stage1 + + The files are moved into a subdirectory named `stage1'. Once + installation is complete, you may wish to delete these files with + `rm -r stage1'. + + 11. If you have chosen a configuration for GNU CC which requires other + GNU tools (such as GAS or the GNU linker) instead of the standard + system tools, install the required tools in the `stage1' + subdirectory under the names `as', `ld' or whatever is + appropriate. This will enable the stage 1 compiler to find the + proper tools in the following stage. + + Alternatively, you can do subsequent compilation using a value of + the `PATH' environment variable such that the necessary GNU tools + come before the standard system tools. + + 12. Recompile the compiler with itself, with this command: + + make CC="stage1/xgcc -Bstage1/" CFLAGS="-g -O2" + + This is called making the stage 2 compiler. + + The command shown above builds compilers for all the supported + languages. If you don't want them all, you can specify the + languages to build by typing the argument `LANGUAGES="LIST"'. LIST + should contain one or more words from the list `c', `c++', + `objective-c', and `proto'. Separate the words with spaces. + `proto' stands for the programs `protoize' and `unprotoize'; they + are not a separate language, but you use `LANGUAGES' to enable or + disable their installation. + + If you are going to build the stage 3 compiler, then you might + want to build only the C language in stage 2. + + Once you have built the stage 2 compiler, if you are short of disk + space, you can delete the subdirectory `stage1'. + + On a 68000 or 68020 system lacking floating point hardware, unless + you have selected a `tm.h' file that expects by default that there + is no such hardware, do this instead: + + make CC="stage1/xgcc -Bstage1/" CFLAGS="-g -O2 -msoft-float" + + 13. If you wish to test the compiler by compiling it with itself one + more time, install any other necessary GNU tools (such as GAS or + the GNU linker) in the `stage2' subdirectory as you did in the + `stage1' subdirectory, then do this: + + make stage2 + make CC="stage2/xgcc -Bstage2/" CFLAGS="-g -O2" + + This is called making the stage 3 compiler. Aside from the `-B' + option, the compiler options should be the same as when you made + the stage 2 compiler. But the `LANGUAGES' option need not be the + same. The command shown above builds compilers for all the + supported languages; if you don't want them all, you can specify + the languages to build by typing the argument `LANGUAGES="LIST"', + as described above. + + If you do not have to install any additional GNU tools, you may + use the command + + make bootstrap LANGUAGES=LANGUAGE-LIST BOOT_CFLAGS=OPTION-LIST + + instead of making `stage1', `stage2', and performing the two + compiler builds. + + 14. Then compare the latest object files with the stage 2 object + files--they ought to be identical, aside from time stamps (if any). + + On some systems, meaningful comparison of object files is + impossible; they always appear "different." This is currently + true on Solaris and some systems that use ELF object file format. + On some versions of Irix on SGI machines and DEC Unix (OSF/1) on + Alpha systems, you will not be able to compare the files without + specifying `-save-temps'; see the description of individual + systems above to see if you get comparison failures. You may have + similar problems on other systems. + + Use this command to compare the files: + + make compare + + This will mention any object files that differ between stage 2 and + stage 3. Any difference, no matter how innocuous, indicates that + the stage 2 compiler has compiled GNU CC incorrectly, and is + therefore a potentially serious bug which you should investigate + and report. + + If your system does not put time stamps in the object files, then + this is a faster way to compare them (using the Bourne shell): + + for file in *.o; do + cmp $file stage2/$file + done + + If you have built the compiler with the `-mno-mips-tfile' option on + MIPS machines, you will not be able to compare the files. + + 15. Install the compiler driver, the compiler's passes and run-time + support with `make install'. Use the same value for `CC', + `CFLAGS' and `LANGUAGES' that you used when compiling the files + that are being installed. One reason this is necessary is that + some versions of Make have bugs and recompile files gratuitously + when you do this step. If you use the same variable values, those + files will be recompiled properly. + + For example, if you have built the stage 2 compiler, you can use + the following command: + + make install CC="stage2/xgcc -Bstage2/" CFLAGS="-g -O" LANGUAGES="LIST" + + This copies the files `cc1', `cpp' and `libgcc.a' to files `cc1', + `cpp' and `libgcc.a' in the directory + `/usr/local/lib/gcc-lib/TARGET/VERSION', which is where the + compiler driver program looks for them. Here TARGET is the + canonicalized form of target machine type specified when you ran + `configure', and VERSION is the version number of GNU CC. This + naming scheme permits various versions and/or cross-compilers to + coexist. It also copies the executables for compilers for other + languages (e.g., `cc1plus' for C++) to the same directory. + + This also copies the driver program `xgcc' into + `/usr/local/bin/gcc', so that it appears in typical execution + search paths. It also copies `gcc.1' into `/usr/local/man/man1' + and info pages into `/usr/local/info'. + + On some systems, this command causes recompilation of some files. + This is usually due to bugs in `make'. You should either ignore + this problem, or use GNU Make. + + *Warning: there is a bug in `alloca' in the Sun library. To avoid + this bug, be sure to install the executables of GNU CC that were + compiled by GNU CC. (That is, the executables from stage 2 or 3, + not stage 1.) They use `alloca' as a built-in function and never + the one in the library.* + + (It is usually better to install GNU CC executables from stage 2 + or 3, since they usually run faster than the ones compiled with + some other compiler.) + + 16. If you're going to use C++, it's likely that you need to also + install a C++ runtime library. Just as GNU C does not distribute + a C runtime library, it also does not include a C++ runtime + library. All I/O functionality, special class libraries, etc., are + provided by the C++ runtime library. + + The standard C++ runtime library for GNU CC is called `libstdc++'. + An obsolescent library `libg++' may also be available, but it's + necessary only for older software that hasn't been converted yet; + if you don't know whether you need `libg++' then you probably don't + need it. + + Here's one way to build and install `libstdc++' for GNU CC: + + * Build and install GNU CC, so that invoking `gcc' obtains the + GNU CC that was just built. + + * Obtain a copy of a compatible `libstdc++' distribution. For + example, the `libstdc++-2.8.0.tar.gz' distribution should be + compatible with GCC 2.8.0. GCC distributors normally + distribute `libstdc++' as well. + + * Set the `CXX' environment variable to `gcc' while running the + `libstdc++' distribution's `configure' command. Use the same + `configure' options that you used when you invoked GCC's + `configure' command. + + * Invoke `make' to build the C++ runtime. + + * Invoke `make install' to install the C++ runtime. + + To summarize, after building and installing GNU CC, invoke the + following shell commands in the topmost directory of the C++ + library distribution. For CONFIGURE-OPTIONS, use the same options + that you used to configure GNU CC. + + $ CXX=gcc ./configure CONFIGURE-OPTIONS + $ make + $ make install + + 17. GNU CC includes a runtime library for Objective-C because it is an + integral part of the language. You can find the files associated + with the library in the subdirectory `objc'. The GNU Objective-C + Runtime Library requires header files for the target's C library in + order to be compiled,and also requires the header files for the + target's thread library if you want thread support. *Note + Cross-Compilers and Header Files: Cross Headers, for discussion + about header files issues for cross-compilation. + + When you run `configure', it picks the appropriate Objective-C + thread implementation file for the target platform. In some + situations, you may wish to choose a different back-end as some + platforms support multiple thread implementations or you may wish + to disable thread support completely. You do this by specifying a + value for the OBJC_THREAD_FILE makefile variable on the command + line when you run make, for example: + + make CC="stage2/xgcc -Bstage2/" CFLAGS="-g -O2" OBJC_THREAD_FILE=thr-single + + Below is a list of the currently available back-ends. + + * thr-single Disable thread support, should work for all + platforms. + + * thr-decosf1 DEC OSF/1 thread support. + + * thr-irix SGI IRIX thread support. + + * thr-mach Generic MACH thread support, known to work on + NEXTSTEP. + + * thr-os2 IBM OS/2 thread support. + + * thr-posix Generix POSIX thread support. + + * thr-pthreads PCThreads on Linux-based GNU systems. + + * thr-solaris SUN Solaris thread support. + + * thr-win32 Microsoft Win32 API thread support. + + +File: INSTALL, Node: Configurations, Next: Other Dir, Up: Installation + +Configurations Supported by GNU CC +================================== + + Here are the possible CPU types: + + 1750a, a29k, alpha, arm, cN, clipper, dsp16xx, elxsi, h8300, + hppa1.0, hppa1.1, i370, i386, i486, i586, i860, i960, m32r, + m68000, m68k, m88k, mips, mipsel, mips64, mips64el, ns32k, + powerpc, powerpcle, pyramid, romp, rs6000, sh, sparc, sparclite, + sparc64, vax, we32k. + + Here are the recognized company names. As you can see, customary +abbreviations are used rather than the longer official names. + + acorn, alliant, altos, apollo, apple, att, bull, cbm, convergent, + convex, crds, dec, dg, dolphin, elxsi, encore, harris, hitachi, + hp, ibm, intergraph, isi, mips, motorola, ncr, next, ns, omron, + plexus, sequent, sgi, sony, sun, tti, unicom, wrs. + + The company name is meaningful only to disambiguate when the rest of +the information supplied is insufficient. You can omit it, writing +just `CPU-SYSTEM', if it is not needed. For example, `vax-ultrix4.2' +is equivalent to `vax-dec-ultrix4.2'. + + Here is a list of system types: + + 386bsd, aix, acis, amigaos, aos, aout, aux, bosx, bsd, clix, coff, + ctix, cxux, dgux, dynix, ebmon, ecoff, elf, esix, freebsd, hms, + genix, gnu, linux-gnu, hiux, hpux, iris, irix, isc, luna, lynxos, + mach, minix, msdos, mvs, netbsd, newsos, nindy, ns, osf, osfrose, + ptx, riscix, riscos, rtu, sco, sim, solaris, sunos, sym, sysv, + udi, ultrix, unicos, uniplus, unos, vms, vsta, vxworks, winnt, + xenix. + +You can omit the system type; then `configure' guesses the operating +system from the CPU and company. + + You can add a version number to the system type; this may or may not +make a difference. For example, you can write `bsd4.3' or `bsd4.4' to +distinguish versions of BSD. In practice, the version number is most +needed for `sysv3' and `sysv4', which are often treated differently. + + If you specify an impossible combination such as `i860-dg-vms', then +you may get an error message from `configure', or it may ignore part of +the information and do the best it can with the rest. `configure' +always prints the canonical name for the alternative that it used. GNU +CC does not support all possible alternatives. + + Often a particular model of machine has a name. Many machine names +are recognized as aliases for CPU/company combinations. Thus, the +machine name `sun3', mentioned above, is an alias for `m68k-sun'. +Sometimes we accept a company name as a machine name, when the name is +popularly used for a particular machine. Here is a table of the known +machine names: + + 3300, 3b1, 3bN, 7300, altos3068, altos, apollo68, att-7300, + balance, convex-cN, crds, decstation-3100, decstation, delta, + encore, fx2800, gmicro, hp7NN, hp8NN, hp9k2NN, hp9k3NN, hp9k7NN, + hp9k8NN, iris4d, iris, isi68, m3230, magnum, merlin, miniframe, + mmax, news-3600, news800, news, next, pbd, pc532, pmax, powerpc, + powerpcle, ps2, risc-news, rtpc, sun2, sun386i, sun386, sun3, + sun4, symmetry, tower-32, tower. + +Remember that a machine name specifies both the cpu type and the company +name. If you want to install your own homemade configuration files, +you can use `local' as the company name to access them. If you use +configuration `CPU-local', the configuration name without the cpu prefix +is used to form the configuration file names. + + Thus, if you specify `m68k-local', configuration uses files +`m68k.md', `local.h', `m68k.c', `xm-local.h', `t-local', and `x-local', +all in the directory `config/m68k'. + + Here is a list of configurations that have special treatment or +special things you must know: + +`1750a-*-*' + MIL-STD-1750A processors. + + The MIL-STD-1750A cross configuration produces output for + `as1750', an assembler/linker available under the GNU Public + License for the 1750A. `as1750' can be obtained at + *ftp://ftp.fta-berlin.de/pub/crossgcc/1750gals/*. A similarly + licensed simulator for the 1750A is available from same address. + + You should ignore a fatal error during the building of libgcc + (libgcc is not yet implemented for the 1750A.) + + The `as1750' assembler requires the file `ms1750.inc', which is + found in the directory `config/1750a'. + + GNU CC produced the same sections as the Fairchild F9450 C + Compiler, namely: + + `Normal' + The program code section. + + `Static' + The read/write (RAM) data section. + + `Konst' + The read-only (ROM) constants section. + + `Init' + Initialization section (code to copy KREL to SREL). + + The smallest addressable unit is 16 bits (BITS_PER_UNIT is 16). + This means that type `char' is represented with a 16-bit word per + character. The 1750A's "Load/Store Upper/Lower Byte" instructions + are not used by GNU CC. + +`alpha-*-osf1' + Systems using processors that implement the DEC Alpha architecture + and are running the DEC Unix (OSF/1) operating system, for example + the DEC Alpha AXP systems.CC.) + + GNU CC writes a `.verstamp' directive to the assembler output file + unless it is built as a cross-compiler. It gets the version to + use from the system header file `/usr/include/stamp.h'. If you + install a new version of DEC Unix, you should rebuild GCC to pick + up the new version stamp. + + Note that since the Alpha is a 64-bit architecture, + cross-compilers from 32-bit machines will not generate code as + efficient as that generated when the compiler is running on a + 64-bit machine because many optimizations that depend on being + able to represent a word on the target in an integral value on the + host cannot be performed. Building cross-compilers on the Alpha + for 32-bit machines has only been tested in a few cases and may + not work properly. + + `make compare' may fail on old versions of DEC Unix unless you add + `-save-temps' to `CFLAGS'. On these systems, the name of the + assembler input file is stored in the object file, and that makes + comparison fail if it differs between the `stage1' and `stage2' + compilations. The option `-save-temps' forces a fixed name to be + used for the assembler input file, instead of a randomly chosen + name in `/tmp'. Do not add `-save-temps' unless the comparisons + fail without that option. If you add `-save-temps', you will have + to manually delete the `.i' and `.s' files after each series of + compilations. + + GNU CC now supports both the native (ECOFF) debugging format used + by DBX and GDB and an encapsulated STABS format for use only with + GDB. See the discussion of the `--with-stabs' option of + `configure' above for more information on these formats and how to + select them. + + There is a bug in DEC's assembler that produces incorrect line + numbers for ECOFF format when the `.align' directive is used. To + work around this problem, GNU CC will not emit such alignment + directives while writing ECOFF format debugging information even + if optimization is being performed. Unfortunately, this has the + very undesirable side-effect that code addresses when `-O' is + specified are different depending on whether or not `-g' is also + specified. + + To avoid this behavior, specify `-gstabs+' and use GDB instead of + DBX. DEC is now aware of this problem with the assembler and + hopes to provide a fix shortly. + +`arc-*-elf' + Argonaut ARC processor. This configuration is intended for + embedded systems. + +`arm-*-aout' + Advanced RISC Machines ARM-family processors. These are often + used in embedded applications. There are no standard Unix + configurations. This configuration corresponds to the basic + instruction sequences and will produce `a.out' format object + modules. + + You may need to make a variant of the file `arm.h' for your + particular configuration. + +`arm-*-linuxaout' + Any of the ARM family processors running the Linux-based GNU + system with the `a.out' binary format (ELF is not yet supported). + You must use version 2.8.1.0.7 or later of the GNU/Linux binutils, + which you can download from `sunsite.unc.edu:/pub/Linux/GCC' and + other mirror sites for Linux-based GNU systems. + +`arm-*-riscix' + The ARM2 or ARM3 processor running RISC iX, Acorn's port of BSD + Unix. If you are running a version of RISC iX prior to 1.2 then + you must specify the version number during configuration. Note + that the assembler shipped with RISC iX does not support stabs + debugging information; a new version of the assembler, with stabs + support included, is now available from Acorn and via ftp + `ftp.acorn.com:/pub/riscix/as+xterm.tar.Z'. To enable stabs + debugging, pass `--with-gnu-as' to configure. + + You will need to install GNU `sed' before you can run configure. + +`a29k' + AMD Am29k-family processors. These are normally used in embedded + applications. There are no standard Unix configurations. This + configuration corresponds to AMD's standard calling sequence and + binary interface and is compatible with other 29k tools. + + You may need to make a variant of the file `a29k.h' for your + particular configuration. + +`a29k-*-bsd' + AMD Am29050 used in a system running a variant of BSD Unix. + +`decstation-*' + MIPS-based DECstations can support three different personalities: + Ultrix, DEC OSF/1, and OSF/rose. (Alpha-based DECstation products + have a configuration name beginning with `alpha-dec'.) To + configure GCC for these platforms use the following configurations: + + `decstation-ultrix' + Ultrix configuration. + + `decstation-osf1' + Dec's version of OSF/1. + + `decstation-osfrose' + Open Software Foundation reference port of OSF/1 which uses + the OSF/rose object file format instead of ECOFF. Normally, + you would not select this configuration. + + The MIPS C compiler needs to be told to increase its table size + for switch statements with the `-Wf,-XNg1500' option in order to + compile `cp/parse.c'. If you use the `-O2' optimization option, + you also need to use `-Olimit 3000'. Both of these options are + automatically generated in the `Makefile' that the shell script + `configure' builds. If you override the `CC' make variable and + use the MIPS compilers, you may need to add `-Wf,-XNg1500 -Olimit + 3000'. + +`elxsi-elxsi-bsd' + The Elxsi's C compiler has known limitations that prevent it from + compiling GNU C. Please contact `mrs@cygnus.com' for more details. + +`dsp16xx' + A port to the AT&T DSP1610 family of processors. + +`h8300-*-*' + Hitachi H8/300 series of processors. + + The calling convention and structure layout has changed in release + 2.6. All code must be recompiled. The calling convention now + passes the first three arguments in function calls in registers. + Structures are no longer a multiple of 2 bytes. + +`hppa*-*-*' + There are several variants of the HP-PA processor which run a + variety of operating systems. GNU CC must be configured to use + the correct processor type and operating system, or GNU CC will + not function correctly. The easiest way to handle this problem is + to *not* specify a target when configuring GNU CC, the `configure' + script will try to automatically determine the right processor + type and operating system. + + `-g' does not work on HP-UX, since that system uses a peculiar + debugging format which GNU CC does not know about. However, `-g' + will work if you also use GAS and GDB in conjunction with GCC. We + highly recommend using GAS for all HP-PA configurations. + + You should be using GAS-2.6 (or later) along with GDB-4.16 (or + later). These can be retrieved from all the traditional GNU ftp + archive sites. + + On some versions of HP-UX, you will need to install GNU `sed'. + + You will need to be install GAS into a directory before `/bin', + `/usr/bin', and `/usr/ccs/bin' in your search path. You should + install GAS before you build GNU CC. + + To enable debugging, you must configure GNU CC with the + `--with-gnu-as' option before building. + +`i370-*-*' + This port is very preliminary and has many known bugs. We hope to + have a higher-quality port for this machine soon. + +`i386-*-linux-gnuoldld' + Use this configuration to generate `a.out' binaries on Linux-based + GNU systems if you do not have gas/binutils version 2.5.2 or later + installed. This is an obsolete configuration. + +`i386-*-linux-gnuaout' + Use this configuration to generate `a.out' binaries on Linux-based + GNU systems. This configuration is being superseded. You must use + gas/binutils version 2.5.2 or later. + +`i386-*-linux-gnu' + Use this configuration to generate ELF binaries on Linux-based GNU + systems. You must use gas/binutils version 2.5.2 or later. + +`i386-*-sco' + Compilation with RCC is recommended. Also, it may be a good idea + to link with GNU malloc instead of the malloc that comes with the + system. + +`i386-*-sco3.2v4' + Use this configuration for SCO release 3.2 version 4. + +`i386-*-sco3.2v5*' + Use this for the SCO OpenServer Release family including 5.0.0, + 5.0.2, 5.0.4, 5.0.5, Internet FastStart 1.0, and Internet + FastStart 1.1. + + GNU CC can generate COFF binaries if you specify `-mcoff' or ELF + binaries, the default. A full `make bootstrap' is recommended + so that an ELF compiler that builds ELF is generated. + + You must have TLS597 from `ftp://ftp.sco.com/TLS' installed for ELF + C++ binaries to work correctly on releases before 5.0.4. + + The native SCO assembler that is provided with the OS at no charge + is normally required. If, however, you must be able to use the GNU + assembler (perhaps you have complex asms) you must configure this + package `--with-gnu-as'. To do this, install (cp or symlink) + gcc/as to your copy of the GNU assembler. You must use a recent + version of GNU binutils; version 2.9.1 seems to work well. If you + select this option, you will be unable to build COFF images. + Trying to do so will result in non-obvious failures. In general, + the "-with-gnu-as" option isn't as well tested as the native + assembler. + + *NOTE:* If you are building C++, you must follow the instructions + about invoking `make bootstrap' because the native OpenServer + compiler may build a `cc1plus' that will not correctly parse many + valid C++ programs. You must do a `make bootstrap' if you are + building with the native compiler. + +`i386-*-isc' + It may be a good idea to link with GNU malloc instead of the + malloc that comes with the system. + + In ISC version 4.1, `sed' core dumps when building `deduced.h'. + Use the version of `sed' from version 4.0. + +`i386-*-esix' + It may be good idea to link with GNU malloc instead of the malloc + that comes with the system. + +`i386-ibm-aix' + You need to use GAS version 2.1 or later, and LD from GNU binutils + version 2.2 or later. + +`i386-sequent-bsd' + Go to the Berkeley universe before compiling. + +`i386-sequent-ptx1*' +`i386-sequent-ptx2*' + You must install GNU `sed' before running `configure'. + +`i386-sun-sunos4' + You may find that you need another version of GNU CC to begin + bootstrapping with, since the current version when built with the + system's own compiler seems to get an infinite loop compiling part + of `libgcc2.c'. GNU CC version 2 compiled with GNU CC (any + version) seems not to have this problem. + + See *Note Sun Install::, for information on installing GNU CC on + Sun systems. + +`i[345]86-*-winnt3.5' + This version requires a GAS that has not yet been released. Until + it is, you can get a prebuilt binary version via anonymous ftp from + `cs.washington.edu:pub/gnat' or `cs.nyu.edu:pub/gnat'. You must + also use the Microsoft header files from the Windows NT 3.5 SDK. + Find these on the CDROM in the `/mstools/h' directory dated + 9/4/94. You must use a fixed version of Microsoft linker made + especially for NT 3.5, which is also is available on the NT 3.5 + SDK CDROM. If you do not have this linker, can you also use the + linker from Visual C/C++ 1.0 or 2.0. + + Installing GNU CC for NT builds a wrapper linker, called `ld.exe', + which mimics the behaviour of Unix `ld' in the specification of + libraries (`-L' and `-l'). `ld.exe' looks for both Unix and + Microsoft named libraries. For example, if you specify `-lfoo', + `ld.exe' will look first for `libfoo.a' and then for `foo.lib'. + + You may install GNU CC for Windows NT in one of two ways, + depending on whether or not you have a Unix-like shell and various + Unix-like utilities. + + 1. If you do not have a Unix-like shell and few Unix-like + utilities, you will use a DOS style batch script called + `configure.bat'. Invoke it as `configure winnt' from an + MSDOS console window or from the program manager dialog box. + `configure.bat' assumes you have already installed and have + in your path a Unix-like `sed' program which is used to + create a working `Makefile' from `Makefile.in'. + + `Makefile' uses the Microsoft Nmake program maintenance + utility and the Visual C/C++ V8.00 compiler to build GNU CC. + You need only have the utilities `sed' and `touch' to use + this installation method, which only automatically builds the + compiler itself. You must then examine what `fixinc.winnt' + does, edit the header files by hand and build `libgcc.a' + manually. + + 2. The second type of installation assumes you are running a + Unix-like shell, have a complete suite of Unix-like utilities + in your path, and have a previous version of GNU CC already + installed, either through building it via the above + installation method or acquiring a pre-built binary. In this + case, use the `configure' script in the normal fashion. + +`i860-intel-osf1' + This is the Paragon. If you have version 1.0 of the operating + system, you need to take special steps to build GNU CC due to + peculiarities of the system. Newer system versions have no + problem. See the section `Installation Problems' in the GNU CC + Manual. + +`*-lynx-lynxos' + LynxOS 2.2 and earlier comes with GNU CC 1.x already installed as + `/bin/gcc'. You should compile with this instead of `/bin/cc'. + You can tell GNU CC to use the GNU assembler and linker, by + specifying `--with-gnu-as --with-gnu-ld' when configuring. These + will produce COFF format object files and executables; otherwise + GNU CC will use the installed tools, which produce `a.out' format + executables. + +`m32r-*-elf' + Mitsubishi M32R processor. This configuration is intended for + embedded systems. + +`m68000-hp-bsd' + HP 9000 series 200 running BSD. Note that the C compiler that + comes with this system cannot compile GNU CC; contact + `law@cygnus.com' to get binaries of GNU CC for bootstrapping. + +`m68k-altos' + Altos 3068. You must use the GNU assembler, linker and debugger. + Also, you must fix a kernel bug. Details in the file + `README.ALTOS'. + +`m68k-apple-aux' + Apple Macintosh running A/UX. You may configure GCC to use + either the system assembler and linker or the GNU assembler and + linker. You should use the GNU configuration if you can, + especially if you also want to use GNU C++. You enabled that + configuration with + the `--with-gnu-as' and `--with-gnu-ld' + options to `configure'. + + Note the C compiler that comes with this system cannot compile GNU + CC. You can find binaries of GNU CC for bootstrapping on + `jagubox.gsfc.nasa.gov'. You will also a patched version of + `/bin/ld' there that raises some of the arbitrary limits found in + the original. + +`m68k-att-sysv' + AT&T 3b1, a.k.a. 7300 PC. Special procedures are needed to + compile GNU CC with this machine's standard C compiler, due to + bugs in that compiler. You can bootstrap it more easily with + previous versions of GNU CC if you have them. + + Installing GNU CC on the 3b1 is difficult if you do not already + have GNU CC running, due to bugs in the installed C compiler. + However, the following procedure might work. We are unable to + test it. + + 1. Comment out the `#include "config.h"' line near the start of + `cccp.c' and do `make cpp'. This makes a preliminary version + of GNU cpp. + + 2. Save the old `/lib/cpp' and copy the preliminary GNU cpp to + that file name. + + 3. Undo your change in `cccp.c', or reinstall the original + version, and do `make cpp' again. + + 4. Copy this final version of GNU cpp into `/lib/cpp'. + + 5. Replace every occurrence of `obstack_free' in the file + `tree.c' with `_obstack_free'. + + 6. Run `make' to get the first-stage GNU CC. + + 7. Reinstall the original version of `/lib/cpp'. + + 8. Now you can compile GNU CC with itself and install it in the + normal fashion. + +`m68k-bull-sysv' + Bull DPX/2 series 200 and 300 with BOS-2.00.45 up to BOS-2.01. GNU + CC works either with native assembler or GNU assembler. You can use + GNU assembler with native coff generation by providing + `--with-gnu-as' to the configure script or use GNU assembler with + dbx-in-coff encapsulation by providing `--with-gnu-as --stabs'. + For any problem with native assembler or for availability of the + DPX/2 port of GAS, contact `F.Pierresteguy@frcl.bull.fr'. + +`m68k-crds-unox' + Use `configure unos' for building on Unos. + + The Unos assembler is named `casm' instead of `as'. For some + strange reason linking `/bin/as' to `/bin/casm' changes the + behavior, and does not work. So, when installing GNU CC, you + should install the following script as `as' in the subdirectory + where the passes of GCC are installed: + + #!/bin/sh + casm $* + + The default Unos library is named `libunos.a' instead of `libc.a'. + To allow GNU CC to function, either change all references to + `-lc' in `gcc.c' to `-lunos' or link `/lib/libc.a' to + `/lib/libunos.a'. + + When compiling GNU CC with the standard compiler, to overcome bugs + in the support of `alloca', do not use `-O' when making stage 2. + Then use the stage 2 compiler with `-O' to make the stage 3 + compiler. This compiler will have the same characteristics as the + usual stage 2 compiler on other systems. Use it to make a stage 4 + compiler and compare that with stage 3 to verify proper + compilation. + + (Perhaps simply defining `ALLOCA' in `x-crds' as described in the + comments there will make the above paragraph superfluous. Please + inform us of whether this works.) + + Unos uses memory segmentation instead of demand paging, so you + will need a lot of memory. 5 Mb is barely enough if no other + tasks are running. If linking `cc1' fails, try putting the object + files into a library and linking from that library. + +`m68k-hp-hpux' + HP 9000 series 300 or 400 running HP-UX. HP-UX version 8.0 has a + bug in the assembler that prevents compilation of GNU CC. To fix + it, get patch PHCO_4484 from HP. + + In addition, if you wish to use gas `--with-gnu-as' you must use + gas version 2.1 or later, and you must use the GNU linker version + 2.1 or later. Earlier versions of gas relied upon a program which + converted the gas output into the native HP-UX format, but that + program has not been kept up to date. gdb does not understand + that native HP-UX format, so you must use gas if you wish to use + gdb. + +`m68k-sun' + Sun 3. We do not provide a configuration file to use the Sun FPA + by default, because programs that establish signal handlers for + floating point traps inherently cannot work with the FPA. + + See *Note Sun Install::, for information on installing GNU CC on + Sun systems. + +`m88k-*-svr3' + Motorola m88k running the AT&T/Unisoft/Motorola V.3 reference port. + These systems tend to use the Green Hills C, revision 1.8.5, as the + standard C compiler. There are apparently bugs in this compiler + that result in object files differences between stage 2 and stage + 3. If this happens, make the stage 4 compiler and compare it to + the stage 3 compiler. If the stage 3 and stage 4 object files are + identical, this suggests you encountered a problem with the + standard C compiler; the stage 3 and 4 compilers may be usable. + + It is best, however, to use an older version of GNU CC for + bootstrapping if you have one. + +`m88k-*-dgux' + Motorola m88k running DG/UX. To build 88open BCS native or cross + compilers on DG/UX, specify the configuration name as + `m88k-*-dguxbcs' and build in the 88open BCS software development + environment. To build ELF native or cross compilers on DG/UX, + specify `m88k-*-dgux' and build in the DG/UX ELF development + environment. You set the software development environment by + issuing `sde-target' command and specifying either `m88kbcs' or + `m88kdguxelf' as the operand. + + If you do not specify a configuration name, `configure' guesses the + configuration based on the current software development + environment. + +`m88k-tektronix-sysv3' + Tektronix XD88 running UTekV 3.2e. Do not turn on optimization + while building stage1 if you bootstrap with the buggy Green Hills + compiler. Also, The bundled LAI System V NFS is buggy so if you + build in an NFS mounted directory, start from a fresh reboot, or + avoid NFS all together. Otherwise you may have trouble getting + clean comparisons between stages. + +`mips-mips-bsd' + MIPS machines running the MIPS operating system in BSD mode. It's + possible that some old versions of the system lack the functions + `memcpy', `memcmp', and `memset'. If your system lacks these, you + must remove or undo the definition of `TARGET_MEM_FUNCTIONS' in + `mips-bsd.h'. + + The MIPS C compiler needs to be told to increase its table size + for switch statements with the `-Wf,-XNg1500' option in order to + compile `cp/parse.c'. If you use the `-O2' optimization option, + you also need to use `-Olimit 3000'. Both of these options are + automatically generated in the `Makefile' that the shell script + `configure' builds. If you override the `CC' make variable and + use the MIPS compilers, you may need to add `-Wf,-XNg1500 -Olimit + 3000'. + +`mips-mips-riscos*' + The MIPS C compiler needs to be told to increase its table size + for switch statements with the `-Wf,-XNg1500' option in order to + compile `cp/parse.c'. If you use the `-O2' optimization option, + you also need to use `-Olimit 3000'. Both of these options are + automatically generated in the `Makefile' that the shell script + `configure' builds. If you override the `CC' make variable and + use the MIPS compilers, you may need to add `-Wf,-XNg1500 -Olimit + 3000'. + + MIPS computers running RISC-OS can support four different + personalities: default, BSD 4.3, System V.3, and System V.4 (older + versions of RISC-OS don't support V.4). To configure GCC for + these platforms use the following configurations: + + `mips-mips-riscos`rev'' + Default configuration for RISC-OS, revision `rev'. + + `mips-mips-riscos`rev'bsd' + BSD 4.3 configuration for RISC-OS, revision `rev'. + + `mips-mips-riscos`rev'sysv4' + System V.4 configuration for RISC-OS, revision `rev'. + + `mips-mips-riscos`rev'sysv' + System V.3 configuration for RISC-OS, revision `rev'. + + The revision `rev' mentioned above is the revision of RISC-OS to + use. You must reconfigure GCC when going from a RISC-OS revision + 4 to RISC-OS revision 5. This has the effect of avoiding a linker + bug. + +`mips-sgi-*' + In order to compile GCC on an SGI running IRIX 4, the "c.hdr.lib" + option must be installed from the CD-ROM supplied from Silicon + Graphics. This is found on the 2nd CD in release 4.0.1. + + In order to compile GCC on an SGI running IRIX 5, the + "compiler_dev.hdr" subsystem must be installed from the IDO CD-ROM + supplied by Silicon Graphics. + + `make compare' may fail on version 5 of IRIX unless you add + `-save-temps' to `CFLAGS'. On these systems, the name of the + assembler input file is stored in the object file, and that makes + comparison fail if it differs between the `stage1' and `stage2' + compilations. The option `-save-temps' forces a fixed name to be + used for the assembler input file, instead of a randomly chosen + name in `/tmp'. Do not add `-save-temps' unless the comparisons + fail without that option. If you do you `-save-temps', you will + have to manually delete the `.i' and `.s' files after each series + of compilations. + + The MIPS C compiler needs to be told to increase its table size + for switch statements with the `-Wf,-XNg1500' option in order to + compile `cp/parse.c'. If you use the `-O2' optimization option, + you also need to use `-Olimit 3000'. Both of these options are + automatically generated in the `Makefile' that the shell script + `configure' builds. If you override the `CC' make variable and + use the MIPS compilers, you may need to add `-Wf,-XNg1500 -Olimit + 3000'. + + On Irix version 4.0.5F, and perhaps on some other versions as well, + there is an assembler bug that reorders instructions incorrectly. + To work around it, specify the target configuration + `mips-sgi-irix4loser'. This configuration inhibits assembler + optimization. + + In a compiler configured with target `mips-sgi-irix4', you can turn + off assembler optimization by using the `-noasmopt' option. This + compiler option passes the option `-O0' to the assembler, to + inhibit reordering. + + The `-noasmopt' option can be useful for testing whether a problem + is due to erroneous assembler reordering. Even if a problem does + not go away with `-noasmopt', it may still be due to assembler + reordering--perhaps GNU CC itself was miscompiled as a result. + + To enable debugging under Irix 5, you must use GNU as 2.5 or later, + and use the `--with-gnu-as' configure option when configuring gcc. + GNU as is distributed as part of the binutils package. + +`mips-sony-sysv' + Sony MIPS NEWS. This works in NEWSOS 5.0.1, but not in 5.0.2 + (which uses ELF instead of COFF). Support for 5.0.2 will probably + be provided soon by volunteers. In particular, the linker does + not like the code generated by GCC when shared libraries are + linked in. + +`ns32k-encore' + Encore ns32000 system. Encore systems are supported only under + BSD. + +`ns32k-*-genix' + National Semiconductor ns32000 system. Genix has bugs in `alloca' + and `malloc'; you must get the compiled versions of these from GNU + Emacs. + +`ns32k-sequent' + Go to the Berkeley universe before compiling. + +`ns32k-utek' + UTEK ns32000 system ("merlin"). The C compiler that comes with + this system cannot compile GNU CC; contact `tektronix!reed!mason' + to get binaries of GNU CC for bootstrapping. + +`romp-*-aos' +`romp-*-mach' + The only operating systems supported for the IBM RT PC are AOS and + MACH. GNU CC does not support AIX running on the RT. We + recommend you compile GNU CC with an earlier version of itself; if + you compile GNU CC with `hc', the Metaware compiler, it will work, + but you will get mismatches between the stage 2 and stage 3 + compilers in various files. These errors are minor differences in + some floating-point constants and can be safely ignored; the stage + 3 compiler is correct. + +`rs6000-*-aix' +`powerpc-*-aix' + Various early versions of each release of the IBM XLC compiler + will not bootstrap GNU CC. Symptoms include differences between + the stage2 and stage3 object files, and errors when compiling + `libgcc.a' or `enquire'. Known problematic releases include: + xlc-1.2.1.8, xlc-1.3.0.0 (distributed with AIX 3.2.5), and + xlc-1.3.0.19. Both xlc-1.2.1.28 and xlc-1.3.0.24 (PTF 432238) are + known to produce working versions of GNU CC, but most other recent + releases correctly bootstrap GNU CC. + + Release 4.3.0 of AIX and ones prior to AIX 3.2.4 include a version + of the IBM assembler which does not accept debugging directives: + assembler updates are available as PTFs. Also, if you are using + AIX 3.2.5 or greater and the GNU assembler, you must have a + version modified after October 16th, 1995 in order for the GNU C + compiler to build. See the file `README.RS6000' for more details + on any of these problems. + + GNU CC does not yet support the 64-bit PowerPC instructions. + + Objective C does not work on this architecture because it makes + assumptions that are incompatible with the calling conventions. + + AIX on the RS/6000 provides support (NLS) for environments outside + of the United States. Compilers and assemblers use NLS to support + locale-specific representations of various objects including + floating-point numbers ("." vs "," for separating decimal + fractions). There have been problems reported where the library + linked with GNU CC does not produce the same floating-point + formats that the assembler accepts. If you have this problem, set + the LANG environment variable to "C" or "En_US". + + Due to changes in the way that GNU CC invokes the binder (linker) + for AIX 4.1, you may now receive warnings of duplicate symbols + from the link step that were not reported before. The assembly + files generated by GNU CC for AIX have always included multiple + symbol definitions for certain global variable and function + declarations in the original program. The warnings should not + prevent the linker from producing a correct library or runnable + executable. + + By default, AIX 4.1 produces code that can be used on either Power + or PowerPC processors. + + You can specify a default version for the `-mcpu='CPU_TYPE switch + by using the configure option `--with-cpu-'CPU_TYPE. + +`powerpc-*-elf' +`powerpc-*-sysv4' + PowerPC system in big endian mode, running System V.4. + + You can specify a default version for the `-mcpu='CPU_TYPE switch + by using the configure option `--with-cpu-'CPU_TYPE. + +`powerpc-*-linux-gnu' + PowerPC system in big endian mode, running the Linux-based GNU + system. + + You can specify a default version for the `-mcpu='CPU_TYPE switch + by using the configure option `--with-cpu-'CPU_TYPE. + +`powerpc-*-eabiaix' + Embedded PowerPC system in big endian mode with -mcall-aix + selected as the default. + + You can specify a default version for the `-mcpu='CPU_TYPE switch + by using the configure option `--with-cpu-'CPU_TYPE. + +`powerpc-*-eabisim' + Embedded PowerPC system in big endian mode for use in running + under the PSIM simulator. + + You can specify a default version for the `-mcpu='CPU_TYPE switch + by using the configure option `--with-cpu-'CPU_TYPE. + +`powerpc-*-eabi' + Embedded PowerPC system in big endian mode. + + You can specify a default version for the `-mcpu='CPU_TYPE switch + by using the configure option `--with-cpu-'CPU_TYPE. + +`powerpcle-*-elf' +`powerpcle-*-sysv4' + PowerPC system in little endian mode, running System V.4. + + You can specify a default version for the `-mcpu='CPU_TYPE switch + by using the configure option `--with-cpu-'CPU_TYPE. + +`powerpcle-*-solaris2*' + PowerPC system in little endian mode, running Solaris 2.5.1 or + higher. + + You can specify a default version for the `-mcpu='CPU_TYPE switch + by using the configure option `--with-cpu-'CPU_TYPE. Beta + versions of the Sun 4.0 compiler do not seem to be able to build + GNU CC correctly. There are also problems with the host assembler + and linker that are fixed by using the GNU versions of these tools. + +`powerpcle-*-eabisim' + Embedded PowerPC system in little endian mode for use in running + under the PSIM simulator. + +`powerpcle-*-eabi' + Embedded PowerPC system in little endian mode. + + You can specify a default version for the `-mcpu='CPU_TYPE switch + by using the configure option `--with-cpu-'CPU_TYPE. + +`powerpcle-*-winnt' +`powerpcle-*-pe' + PowerPC system in little endian mode running Windows NT. + + You can specify a default version for the `-mcpu='CPU_TYPE switch + by using the configure option `--with-cpu-'CPU_TYPE. + +`vax-dec-ultrix' + Don't try compiling with Vax C (`vcc'). It produces incorrect code + in some cases (for example, when `alloca' is used). + + Meanwhile, compiling `cp/parse.c' with pcc does not work because of + an internal table size limitation in that compiler. To avoid this + problem, compile just the GNU C compiler first, and use it to + recompile building all the languages that you want to run. + +`sparc-sun-*' + See *Note Sun Install::, for information on installing GNU CC on + Sun systems. + +`vax-dec-vms' + See *Note VMS Install::, for details on how to install GNU CC on + VMS. + +`we32k-*-*' + These computers are also known as the 3b2, 3b5, 3b20 and other + similar names. (However, the 3b1 is actually a 68000; see *Note + Configurations::.) + + Don't use `-g' when compiling with the system's compiler. The + system's linker seems to be unable to handle such a large program + with debugging information. + + The system's compiler runs out of capacity when compiling `stmt.c' + in GNU CC. You can work around this by building `cpp' in GNU CC + first, then use that instead of the system's preprocessor with the + system's C compiler to compile `stmt.c'. Here is how: + + mv /lib/cpp /lib/cpp.att + cp cpp /lib/cpp.gnu + echo '/lib/cpp.gnu -traditional ${1+"$@"}' > /lib/cpp + chmod +x /lib/cpp + + The system's compiler produces bad code for some of the GNU CC + optimization files. So you must build the stage 2 compiler without + optimization. Then build a stage 3 compiler with optimization. + That executable should work. Here are the necessary commands: + + make LANGUAGES=c CC=stage1/xgcc CFLAGS="-Bstage1/ -g" + make stage2 + make CC=stage2/xgcc CFLAGS="-Bstage2/ -g -O" + + You may need to raise the ULIMIT setting to build a C++ compiler, + as the file `cc1plus' is larger than one megabyte. + + +File: INSTALL, Node: Other Dir, Next: Cross-Compiler, Prev: Configurations, Up: Installation + +Compilation in a Separate Directory +=================================== + + If you wish to build the object files and executables in a directory +other than the one containing the source files, here is what you must +do differently: + + 1. Make sure you have a version of Make that supports the `VPATH' + feature. (GNU Make supports it, as do Make versions on most BSD + systems.) + + 2. If you have ever run `configure' in the source directory, you must + undo the configuration. Do this by running: + + make distclean + + 3. Go to the directory in which you want to build the compiler before + running `configure': + + mkdir gcc-sun3 + cd gcc-sun3 + + On systems that do not support symbolic links, this directory must + be on the same file system as the source code directory. + + 4. Specify where to find `configure' when you run it: + + ../gcc/configure ... + + This also tells `configure' where to find the compiler sources; + `configure' takes the directory from the file name that was used to + invoke it. But if you want to be sure, you can specify the source + directory with the `--srcdir' option, like this: + + ../gcc/configure --srcdir=../gcc OTHER OPTIONS + + The directory you specify with `--srcdir' need not be the same as + the one that `configure' is found in. + + Now, you can run `make' in that directory. You need not repeat the +configuration steps shown above, when ordinary source files change. You +must, however, run `configure' again when the configuration files +change, if your system does not support symbolic links. + + +File: INSTALL, Node: Cross-Compiler, Next: Sun Install, Prev: Other Dir, Up: Installation + +Building and Installing a Cross-Compiler +======================================== + + GNU CC can function as a cross-compiler for many machines, but not +all. + + * Cross-compilers for the Mips as target using the Mips assembler + currently do not work, because the auxiliary programs + `mips-tdump.c' and `mips-tfile.c' can't be compiled on anything + but a Mips. It does work to cross compile for a Mips if you use + the GNU assembler and linker. + + * Cross-compilers between machines with different floating point + formats have not all been made to work. GNU CC now has a floating + point emulator with which these can work, but each target machine + description needs to be updated to take advantage of it. + + * Cross-compilation between machines of different word sizes is + somewhat problematic and sometimes does not work. + + Since GNU CC generates assembler code, you probably need a +cross-assembler that GNU CC can run, in order to produce object files. +If you want to link on other than the target machine, you need a +cross-linker as well. You also need header files and libraries suitable +for the target machine that you can install on the host machine. + +* Menu: + +* Steps of Cross:: Using a cross-compiler involves several steps + that may be carried out on different machines. +* Configure Cross:: Configuring a cross-compiler. +* Tools and Libraries:: Where to put the linker and assembler, and the C library. +* Cross Headers:: Finding and installing header files + for a cross-compiler. +* Cross Runtime:: Supplying arithmetic runtime routines (`libgcc1.a'). +* Build Cross:: Actually compiling the cross-compiler. + + +File: INSTALL, Node: Steps of Cross, Next: Configure Cross, Up: Cross-Compiler + +Steps of Cross-Compilation +-------------------------- + + To compile and run a program using a cross-compiler involves several +steps: + + * Run the cross-compiler on the host machine to produce assembler + files for the target machine. This requires header files for the + target machine. + + * Assemble the files produced by the cross-compiler. You can do this + either with an assembler on the target machine, or with a + cross-assembler on the host machine. + + * Link those files to make an executable. You can do this either + with a linker on the target machine, or with a cross-linker on the + host machine. Whichever machine you use, you need libraries and + certain startup files (typically `crt....o') for the target + machine. + + It is most convenient to do all of these steps on the same host +machine, since then you can do it all with a single invocation of GNU +CC. This requires a suitable cross-assembler and cross-linker. For +some targets, the GNU assembler and linker are available. + + +File: INSTALL, Node: Configure Cross, Next: Tools and Libraries, Prev: Steps of Cross, Up: Cross-Compiler + +Configuring a Cross-Compiler +---------------------------- + + To build GNU CC as a cross-compiler, you start out by running +`configure'. Use the `--target=TARGET' to specify the target type. If +`configure' was unable to correctly identify the system you are running +on, also specify the `--build=BUILD' option. For example, here is how +to configure for a cross-compiler that produces code for an HP 68030 +system running BSD on a system that `configure' can correctly identify: + + ./configure --target=m68k-hp-bsd4.3 + + +File: INSTALL, Node: Tools and Libraries, Next: Cross Headers, Prev: Configure Cross, Up: Cross-Compiler + +Tools and Libraries for a Cross-Compiler +---------------------------------------- + + If you have a cross-assembler and cross-linker available, you should +install them now. Put them in the directory `/usr/local/TARGET/bin'. +Here is a table of the tools you should put in this directory: + +`as' + This should be the cross-assembler. + +`ld' + This should be the cross-linker. + +`ar' + This should be the cross-archiver: a program which can manipulate + archive files (linker libraries) in the target machine's format. + +`ranlib' + This should be a program to construct a symbol table in an archive + file. + + The installation of GNU CC will find these programs in that +directory, and copy or link them to the proper place to for the +cross-compiler to find them when run later. + + The easiest way to provide these files is to build the Binutils +package and GAS. Configure them with the same `--host' and `--target' +options that you use for configuring GNU CC, then build and install +them. They install their executables automatically into the proper +directory. Alas, they do not support all the targets that GNU CC +supports. + + If you want to install libraries to use with the cross-compiler, +such as a standard C library, put them in the directory +`/usr/local/TARGET/lib'; installation of GNU CC copies all the files in +that subdirectory into the proper place for GNU CC to find them and +link with them. Here's an example of copying some libraries from a +target machine: + + ftp TARGET-MACHINE + lcd /usr/local/TARGET/lib + cd /lib + get libc.a + cd /usr/lib + get libg.a + get libm.a + quit + +The precise set of libraries you'll need, and their locations on the +target machine, vary depending on its operating system. + + Many targets require "start files" such as `crt0.o' and `crtn.o' +which are linked into each executable; these too should be placed in +`/usr/local/TARGET/lib'. There may be several alternatives for +`crt0.o', for use with profiling or other compilation options. Check +your target's definition of `STARTFILE_SPEC' to find out what start +files it uses. Here's an example of copying these files from a target +machine: + + ftp TARGET-MACHINE + lcd /usr/local/TARGET/lib + prompt + cd /lib + mget *crt*.o + cd /usr/lib + mget *crt*.o + quit + + +File: INSTALL, Node: Cross Runtime, Next: Build Cross, Prev: Cross Headers, Up: Cross-Compiler + +`libgcc.a' and Cross-Compilers +------------------------------ + + Code compiled by GNU CC uses certain runtime support functions +implicitly. Some of these functions can be compiled successfully with +GNU CC itself, but a few cannot be. These problem functions are in the +source file `libgcc1.c'; the library made from them is called +`libgcc1.a'. + + When you build a native compiler, these functions are compiled with +some other compiler-the one that you use for bootstrapping GNU CC. +Presumably it knows how to open code these operations, or else knows how +to call the run-time emulation facilities that the machine comes with. +But this approach doesn't work for building a cross-compiler. The +compiler that you use for building knows about the host system, not the +target system. + + So, when you build a cross-compiler you have to supply a suitable +library `libgcc1.a' that does the job it is expected to do. + + To compile `libgcc1.c' with the cross-compiler itself does not work. +The functions in this file are supposed to implement arithmetic +operations that GNU CC does not know how to open code for your target +machine. If these functions are compiled with GNU CC itself, they will +compile into infinite recursion. + + On any given target, most of these functions are not needed. If GNU +CC can open code an arithmetic operation, it will not call these +functions to perform the operation. It is possible that on your target +machine, none of these functions is needed. If so, you can supply an +empty library as `libgcc1.a'. + + Many targets need library support only for multiplication and +division. If you are linking with a library that contains functions for +multiplication and division, you can tell GNU CC to call them directly +by defining the macros `MULSI3_LIBCALL', and the like. These macros +need to be defined in the target description macro file. For some +targets, they are defined already. This may be sufficient to avoid the +need for libgcc1.a; if so, you can supply an empty library. + + Some targets do not have floating point instructions; they need other +functions in `libgcc1.a', which do floating arithmetic. Recent +versions of GNU CC have a file which emulates floating point. With a +certain amount of work, you should be able to construct a floating +point emulator that can be used as `libgcc1.a'. Perhaps future +versions will contain code to do this automatically and conveniently. +That depends on whether someone wants to implement it. + + Some embedded targets come with all the necessary `libgcc1.a' +routines written in C or assembler. These targets build `libgcc1.a' +automatically and you do not need to do anything special for them. +Other embedded targets do not need any `libgcc1.a' routines since all +the necessary operations are supported by the hardware. + + If your target system has another C compiler, you can configure GNU +CC as a native compiler on that machine, build just `libgcc1.a' with +`make libgcc1.a' on that machine, and use the resulting file with the +cross-compiler. To do this, execute the following on the target +machine: + + cd TARGET-BUILD-DIR + ./configure --host=sparc --target=sun3 + make libgcc1.a + +And then this on the host machine: + + ftp TARGET-MACHINE + binary + cd TARGET-BUILD-DIR + get libgcc1.a + quit + + Another way to provide the functions you need in `libgcc1.a' is to +define the appropriate `perform_...' macros for those functions. If +these definitions do not use the C arithmetic operators that they are +meant to implement, you should be able to compile them with the +cross-compiler you are building. (If these definitions already exist +for your target file, then you are all set.) + + To build `libgcc1.a' using the perform macros, use +`LIBGCC1=libgcc1.a OLDCC=./xgcc' when building the compiler. +Otherwise, you should place your replacement library under the name +`libgcc1.a' in the directory in which you will build the +cross-compiler, before you run `make'. + + +File: INSTALL, Node: Cross Headers, Next: Cross Runtime, Prev: Tools and Libraries, Up: Cross-Compiler + +Cross-Compilers and Header Files +-------------------------------- + + If you are cross-compiling a standalone program or a program for an +embedded system, then you may not need any header files except the few +that are part of GNU CC (and those of your program). However, if you +intend to link your program with a standard C library such as `libc.a', +then you probably need to compile with the header files that go with +the library you use. + + The GNU C compiler does not come with these files, because (1) they +are system-specific, and (2) they belong in a C library, not in a +compiler. + + If the GNU C library supports your target machine, then you can get +the header files from there (assuming you actually use the GNU library +when you link your program). + + If your target machine comes with a C compiler, it probably comes +with suitable header files also. If you make these files accessible +from the host machine, the cross-compiler can use them also. + + Otherwise, you're on your own in finding header files to use when +cross-compiling. + + When you have found suitable header files, put them in the directory +`/usr/local/TARGET/include', before building the cross compiler. Then +installation will run fixincludes properly and install the corrected +versions of the header files where the compiler will use them. + + Provide the header files before you build the cross-compiler, because +the build stage actually runs the cross-compiler to produce parts of +`libgcc.a'. (These are the parts that *can* be compiled with GNU CC.) +Some of them need suitable header files. + + Here's an example showing how to copy the header files from a target +machine. On the target machine, do this: + + (cd /usr/include; tar cf - .) > tarfile + + Then, on the host machine, do this: + + ftp TARGET-MACHINE + lcd /usr/local/TARGET/include + get tarfile + quit + tar xf tarfile + + +File: INSTALL, Node: Build Cross, Prev: Cross Runtime, Up: Cross-Compiler + +Actually Building the Cross-Compiler +------------------------------------ + + Now you can proceed just as for compiling a single-machine compiler +through the step of building stage 1. If you have not provided some +sort of `libgcc1.a', then compilation will give up at the point where +it needs that file, printing a suitable error message. If you do +provide `libgcc1.a', then building the compiler will automatically +compile and link a test program called `libgcc1-test'; if you get +errors in the linking, it means that not all of the necessary routines +in `libgcc1.a' are available. + + You must provide the header file `float.h'. One way to do this is +to compile `enquire' and run it on your target machine. The job of +`enquire' is to run on the target machine and figure out by experiment +the nature of its floating point representation. `enquire' records its +findings in the header file `float.h'. If you can't produce this file +by running `enquire' on the target machine, then you will need to come +up with a suitable `float.h' in some other way (or else, avoid using it +in your programs). + + Do not try to build stage 2 for a cross-compiler. It doesn't work to +rebuild GNU CC as a cross-compiler using the cross-compiler, because +that would produce a program that runs on the target machine, not on the +host. For example, if you compile a 386-to-68030 cross-compiler with +itself, the result will not be right either for the 386 (because it was +compiled into 68030 code) or for the 68030 (because it was configured +for a 386 as the host). If you want to compile GNU CC into 68030 code, +whether you compile it on a 68030 or with a cross-compiler on a 386, you +must specify a 68030 as the host when you configure it. + + To install the cross-compiler, use `make install', as usual. + + +File: INSTALL, Node: Sun Install, Next: VMS Install, Prev: Cross-Compiler, Up: Installation + +Installing GNU CC on the Sun +============================ + + On Solaris, do not use the linker or other tools in `/usr/ucb' to +build GNU CC. Use `/usr/ccs/bin'. + + If the assembler reports `Error: misaligned data' when bootstrapping, +you are probably using an obsolete version of the GNU assembler. +Upgrade to the latest version of GNU `binutils', or use the Solaris +assembler. + + Make sure the environment variable `FLOAT_OPTION' is not set when +you compile `libgcc.a'. If this option were set to `f68881' when +`libgcc.a' is compiled, the resulting code would demand to be linked +with a special startup file and would not link properly without special +pains. + + There is a bug in `alloca' in certain versions of the Sun library. +To avoid this bug, install the binaries of GNU CC that were compiled by +GNU CC. They use `alloca' as a built-in function and never the one in +the library. + + Some versions of the Sun compiler crash when compiling GNU CC. The +problem is a segmentation fault in cpp. This problem seems to be due to +the bulk of data in the environment variables. You may be able to avoid +it by using the following command to compile GNU CC with Sun CC: + + make CC="TERMCAP=x OBJS=x LIBFUNCS=x STAGESTUFF=x cc" + + SunOS 4.1.3 and 4.1.3_U1 have bugs that can cause intermittent core +dumps when compiling GNU CC. A common symptom is an internal compiler +error which does not recur if you run it again. To fix the problem, +install Sun recommended patch 100726 (for SunOS 4.1.3) or 101508 (for +SunOS 4.1.3_U1), or upgrade to a later SunOS release. + + +File: INSTALL, Node: VMS Install, Next: Collect2, Prev: Sun Install, Up: Installation + +Installing GNU CC on VMS +======================== + + The VMS version of GNU CC is distributed in a backup saveset +containing both source code and precompiled binaries. + + To install the `gcc' command so you can use the compiler easily, in +the same manner as you use the VMS C compiler, you must install the VMS +CLD file for GNU CC as follows: + + 1. Define the VMS logical names `GNU_CC' and `GNU_CC_INCLUDE' to + point to the directories where the GNU CC executables + (`gcc-cpp.exe', `gcc-cc1.exe', etc.) and the C include files are + kept respectively. This should be done with the commands: + + $ assign /system /translation=concealed - + disk:[gcc.] gnu_cc + $ assign /system /translation=concealed - + disk:[gcc.include.] gnu_cc_include + + with the appropriate disk and directory names. These commands can + be placed in your system startup file so they will be executed + whenever the machine is rebooted. You may, if you choose, do this + via the `GCC_INSTALL.COM' script in the `[GCC]' directory. + + 2. Install the `GCC' command with the command line: + + $ set command /table=sys$common:[syslib]dcltables - + /output=sys$common:[syslib]dcltables gnu_cc:[000000]gcc + $ install replace sys$common:[syslib]dcltables + + 3. To install the help file, do the following: + + $ library/help sys$library:helplib.hlb gcc.hlp + + Now you can invoke the compiler with a command like `gcc /verbose + file.c', which is equivalent to the command `gcc -v -c file.c' in + Unix. + + If you wish to use GNU C++ you must first install GNU CC, and then +perform the following steps: + + 1. Define the VMS logical name `GNU_GXX_INCLUDE' to point to the + directory where the preprocessor will search for the C++ header + files. This can be done with the command: + + $ assign /system /translation=concealed - + disk:[gcc.gxx_include.] gnu_gxx_include + + with the appropriate disk and directory name. If you are going to + be using a C++ runtime library, this is where its install + procedure will install its header files. + + 2. Obtain the file `gcc-cc1plus.exe', and place this in the same + directory that `gcc-cc1.exe' is kept. + + The GNU C++ compiler can be invoked with a command like `gcc /plus + /verbose file.cc', which is equivalent to the command `g++ -v -c + file.cc' in Unix. + + We try to put corresponding binaries and sources on the VMS +distribution tape. But sometimes the binaries will be from an older +version than the sources, because we don't always have time to update +them. (Use the `/version' option to determine the version number of +the binaries and compare it with the source file `version.c' to tell +whether this is so.) In this case, you should use the binaries you get +to recompile the sources. If you must recompile, here is how: + + 1. Execute the command procedure `vmsconfig.com' to set up the files + `tm.h', `config.h', `aux-output.c', and `md.', and to create files + `tconfig.h' and `hconfig.h'. This procedure also creates several + linker option files used by `make-cc1.com' and a data file used by + `make-l2.com'. + + $ @vmsconfig.com + + 2. Setup the logical names and command tables as defined above. In + addition, define the VMS logical name `GNU_BISON' to point at the + to the directories where the Bison executable is kept. This + should be done with the command: + + $ assign /system /translation=concealed - + disk:[bison.] gnu_bison + + You may, if you choose, use the `INSTALL_BISON.COM' script in the + `[BISON]' directory. + + 3. Install the `BISON' command with the command line: + + $ set command /table=sys$common:[syslib]dcltables - + /output=sys$common:[syslib]dcltables - + gnu_bison:[000000]bison + $ install replace sys$common:[syslib]dcltables + + 4. Type `@make-gcc' to recompile everything (alternatively, submit + the file `make-gcc.com' to a batch queue). If you wish to build + the GNU C++ compiler as well as the GNU CC compiler, you must + first edit `make-gcc.com' and follow the instructions that appear + in the comments. + + 5. In order to use GCC, you need a library of functions which GCC + compiled code will call to perform certain tasks, and these + functions are defined in the file `libgcc2.c'. To compile this + you should use the command procedure `make-l2.com', which will + generate the library `libgcc2.olb'. `libgcc2.olb' should be built + using the compiler built from the same distribution that + `libgcc2.c' came from, and `make-gcc.com' will automatically do + all of this for you. + + To install the library, use the following commands: + + $ library gnu_cc:[000000]gcclib/delete=(new,eprintf) + $ library gnu_cc:[000000]gcclib/delete=L_* + $ library libgcc2/extract=*/output=libgcc2.obj + $ library gnu_cc:[000000]gcclib libgcc2.obj + + The first command simply removes old modules that will be replaced + with modules from `libgcc2' under different module names. The + modules `new' and `eprintf' may not actually be present in your + `gcclib.olb'--if the VMS librarian complains about those modules + not being present, simply ignore the message and continue on with + the next command. The second command removes the modules that + came from the previous version of the library `libgcc2.c'. + + Whenever you update the compiler on your system, you should also + update the library with the above procedure. + + 6. You may wish to build GCC in such a way that no files are written + to the directory where the source files reside. An example would + be the when the source files are on a read-only disk. In these + cases, execute the following DCL commands (substituting your + actual path names): + + $ assign dua0:[gcc.build_dir.]/translation=concealed, - + dua1:[gcc.source_dir.]/translation=concealed gcc_build + $ set default gcc_build:[000000] + + where the directory `dua1:[gcc.source_dir]' contains the source + code, and the directory `dua0:[gcc.build_dir]' is meant to contain + all of the generated object files and executables. Once you have + done this, you can proceed building GCC as described above. (Keep + in mind that `gcc_build' is a rooted logical name, and thus the + device names in each element of the search list must be an actual + physical device name rather than another rooted logical name). + + 7. *If you are building GNU CC with a previous version of GNU CC, you + also should check to see that you have the newest version of the + assembler*. In particular, GNU CC version 2 treats global constant + variables slightly differently from GNU CC version 1, and GAS + version 1.38.1 does not have the patches required to work with GCC + version 2. If you use GAS 1.38.1, then `extern const' variables + will not have the read-only bit set, and the linker will generate + warning messages about mismatched psect attributes for these + variables. These warning messages are merely a nuisance, and can + safely be ignored. + + If you are compiling with a version of GNU CC older than 1.33, + specify `/DEFINE=("inline=")' as an option in all the + compilations. This requires editing all the `gcc' commands in + `make-cc1.com'. (The older versions had problems supporting + `inline'.) Once you have a working 1.33 or newer GNU CC, you can + change this file back. + + 8. If you want to build GNU CC with the VAX C compiler, you will need + to make minor changes in `make-cccp.com' and `make-cc1.com' to + choose alternate definitions of `CC', `CFLAGS', and `LIBS'. See + comments in those files. However, you must also have a working + version of the GNU assembler (GNU as, aka GAS) as it is used as + the back-end for GNU CC to produce binary object modules and is + not included in the GNU CC sources. GAS is also needed to compile + `libgcc2' in order to build `gcclib' (see above); `make-l2.com' + expects to be able to find it operational in + `gnu_cc:[000000]gnu-as.exe'. + + To use GNU CC on VMS, you need the VMS driver programs `gcc.exe', + `gcc.com', and `gcc.cld'. They are distributed with the VMS + binaries (`gcc-vms') rather than the GNU CC sources. GAS is also + included in `gcc-vms', as is Bison. + + Once you have successfully built GNU CC with VAX C, you should use + the resulting compiler to rebuild itself. Before doing this, be + sure to restore the `CC', `CFLAGS', and `LIBS' definitions in + `make-cccp.com' and `make-cc1.com'. The second generation + compiler will be able to take advantage of many optimizations that + must be suppressed when building with other compilers. + + Under previous versions of GNU CC, the generated code would +occasionally give strange results when linked with the sharable +`VAXCRTL' library. Now this should work. + + Even with this version, however, GNU CC itself should not be linked +with the sharable `VAXCRTL'. The version of `qsort' in `VAXCRTL' has a +bug (known to be present in VMS versions V4.6 through V5.5) which +causes the compiler to fail. + + The executables are generated by `make-cc1.com' and `make-cccp.com' +use the object library version of `VAXCRTL' in order to make use of the +`qsort' routine in `gcclib.olb'. If you wish to link the compiler +executables with the shareable image version of `VAXCRTL', you should +edit the file `tm.h' (created by `vmsconfig.com') to define the macro +`QSORT_WORKAROUND'. + + `QSORT_WORKAROUND' is always defined when GNU CC is compiled with +VAX C, to avoid a problem in case `gcclib.olb' is not yet available. + + +File: INSTALL, Node: Collect2, Next: Header Dirs, Prev: VMS Install, Up: Installation + +`collect2' +========== + + GNU CC uses a utility called `collect2' on nearly all systems to +arrange to call various initialization functions at start time. + + The program `collect2' works by linking the program once and looking +through the linker output file for symbols with particular names +indicating they are constructor functions. If it finds any, it creates +a new temporary `.c' file containing a table of them, compiles it, and +links the program a second time including that file. + + The actual calls to the constructors are carried out by a subroutine +called `__main', which is called (automatically) at the beginning of +the body of `main' (provided `main' was compiled with GNU CC). Calling +`__main' is necessary, even when compiling C code, to allow linking C +and C++ object code together. (If you use `-nostdlib', you get an +unresolved reference to `__main', since it's defined in the standard +GCC library. Include `-lgcc' at the end of your compiler command line +to resolve this reference.) + + The program `collect2' is installed as `ld' in the directory where +the passes of the compiler are installed. When `collect2' needs to +find the *real* `ld', it tries the following file names: + + * `real-ld' in the directories listed in the compiler's search + directories. + + * `real-ld' in the directories listed in the environment variable + `PATH'. + + * The file specified in the `REAL_LD_FILE_NAME' configuration macro, + if specified. + + * `ld' in the compiler's search directories, except that `collect2' + will not execute itself recursively. + + * `ld' in `PATH'. + + "The compiler's search directories" means all the directories where +`gcc' searches for passes of the compiler. This includes directories +that you specify with `-B'. + + Cross-compilers search a little differently: + + * `real-ld' in the compiler's search directories. + + * `TARGET-real-ld' in `PATH'. + + * The file specified in the `REAL_LD_FILE_NAME' configuration macro, + if specified. + + * `ld' in the compiler's search directories. + + * `TARGET-ld' in `PATH'. + + `collect2' explicitly avoids running `ld' using the file name under +which `collect2' itself was invoked. In fact, it remembers up a list +of such names--in case one copy of `collect2' finds another copy (or +version) of `collect2' installed as `ld' in a second place in the +search path. + + `collect2' searches for the utilities `nm' and `strip' using the +same algorithm as above for `ld'. + + +File: INSTALL, Node: Header Dirs, Prev: Collect2, Up: Installation + +Standard Header File Directories +================================ + + `GCC_INCLUDE_DIR' means the same thing for native and cross. It is +where GNU CC stores its private include files, and also where GNU CC +stores the fixed include files. A cross compiled GNU CC runs +`fixincludes' on the header files in `$(tooldir)/include'. (If the +cross compilation header files need to be fixed, they must be installed +before GNU CC is built. If the cross compilation header files are +already suitable for ANSI C and GNU CC, nothing special need be done). + + `GPLUS_INCLUDE_DIR' means the same thing for native and cross. It +is where `g++' looks first for header files. The C++ library installs +only target independent header files in that directory. + + `LOCAL_INCLUDE_DIR' is used only for a native compiler. It is +normally `/usr/local/include'. GNU CC searches this directory so that +users can install header files in `/usr/local/include'. + + `CROSS_INCLUDE_DIR' is used only for a cross compiler. GNU CC +doesn't install anything there. + + `TOOL_INCLUDE_DIR' is used for both native and cross compilers. It +is the place for other packages to install header files that GNU CC will +use. For a cross-compiler, this is the equivalent of `/usr/include'. +When you build a cross-compiler, `fixincludes' processes any header +files in this directory. + + + +Tag Table: +Node: Installation351 +Node: Configurations26618 +Node: Other Dir65739 +Node: Cross-Compiler67454 +Node: Steps of Cross69284 +Node: Configure Cross70401 +Node: Tools and Libraries71037 +Node: Cross Runtime73475 +Node: Cross Headers77555 +Node: Build Cross79553 +Node: Sun Install81428 +Node: VMS Install83099 +Node: Collect293028 +Node: Header Dirs95592 + +End Tag Table diff --git a/contrib/gcc/Makefile.in b/contrib/gcc/Makefile.in index b1f7b0808c75..1a140c4ec2bd 100644 --- a/contrib/gcc/Makefile.in +++ b/contrib/gcc/Makefile.in @@ -18,7 +18,7 @@ #the Free Software Foundation, 59 Temple Place - Suite 330, #Boston MA 02111-1307, USA. -# $FreeBSD$ +# $FreeBSD: src/contrib/gcc/Makefile.in,v 1.4 1999/10/16 08:21:54 obrien Exp $ # The targets for external use include: # all, doc, proto, install, install-cross, install-cross-rest, diff --git a/contrib/gcc/c-common.c b/contrib/gcc/c-common.c index aeb08fd742c3..6396c2da7f06 100644 --- a/contrib/gcc/c-common.c +++ b/contrib/gcc/c-common.c @@ -18,7 +18,7 @@ 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. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/gcc/c-common.c,v 1.8.2.2 2000/04/18 21:09:03 obrien Exp $ */ #include "config.h" #include "system.h" diff --git a/contrib/gcc/c-tree.h b/contrib/gcc/c-tree.h index 40c2f2dbac5b..d8cec4751657 100644 --- a/contrib/gcc/c-tree.h +++ b/contrib/gcc/c-tree.h @@ -18,7 +18,7 @@ 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. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/gcc/c-tree.h,v 1.4 1999/10/16 08:34:33 obrien Exp $ */ #ifndef _C_TREE_H #define _C_TREE_H diff --git a/contrib/gcc/cccp.1 b/contrib/gcc/cccp.1 index 442f0ab748e9..a056a7e10f64 100644 --- a/contrib/gcc/cccp.1 +++ b/contrib/gcc/cccp.1 @@ -1,4 +1,4 @@ -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/gcc/cccp.1,v 1.3 1999/09/19 08:18:18 obrien Exp $ .\" Copyright (c) 1991, 1992, 1993 Free Software Foundation \-*-Text-*- .\" See section COPYING for conditions for redistribution .TH cpp 1 "April 30, 1993" "FreeBSD" "GNU Tools" diff --git a/contrib/gcc/cccp.c b/contrib/gcc/cccp.c index 69cd93d3e88b..a26e6bcdb1be 100644 --- a/contrib/gcc/cccp.c +++ b/contrib/gcc/cccp.c @@ -18,7 +18,7 @@ along with this program; if not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/gcc/cccp.c,v 1.6 2000/03/09 10:11:07 obrien Exp $ */ #include "config.h" diff --git a/contrib/gcc/choose-temp.c b/contrib/gcc/choose-temp.c index 018f7d92a89f..22678f19cce1 100644 --- a/contrib/gcc/choose-temp.c +++ b/contrib/gcc/choose-temp.c @@ -22,7 +22,7 @@ Boston, MA 02111-1307, USA. */ /* This file lives in at least two places: libiberty and gcc. Don't change one without the other. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/gcc/choose-temp.c,v 1.3 1999/11/04 10:23:25 obrien Exp $ */ #ifdef HAVE_CONFIG_H #include "config.h" diff --git a/contrib/gcc/config/alpha/elf.h b/contrib/gcc/config/alpha/elf.h index 24af9d3d0313..a226a6a460f5 100644 --- a/contrib/gcc/config/alpha/elf.h +++ b/contrib/gcc/config/alpha/elf.h @@ -19,7 +19,7 @@ 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. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/gcc/config/alpha/elf.h,v 1.3 1999/10/16 08:13:35 obrien Exp $ */ #undef OBJECT_FORMAT_COFF #undef EXTENDED_COFF diff --git a/contrib/gcc/config/alpha/freebsd.h b/contrib/gcc/config/alpha/freebsd.h index f635ff9ef7fe..ccf1f0ae499f 100644 --- a/contrib/gcc/config/alpha/freebsd.h +++ b/contrib/gcc/config/alpha/freebsd.h @@ -22,7 +22,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ This was taken from the NetBSD configuration, and modified for FreeBSD/Alpha by Hidetoshi Shimokawa <simokawa@FreeBSD.ORG> */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/gcc/config/alpha/freebsd.h,v 1.9.2.2 2000/10/30 20:32:25 obrien Exp $ */ /* Names to predefine in the preprocessor for this target machine. diff --git a/contrib/gcc/config/freebsd.h b/contrib/gcc/config/freebsd.h index f9f454acd201..ba25897f2630 100644 --- a/contrib/gcc/config/freebsd.h +++ b/contrib/gcc/config/freebsd.h @@ -25,7 +25,7 @@ Boston, MA 02111-1307, USA. */ /usr/src/contrib/gcc/config/svr4.h & egcs/gcc/config/i386/freebsd-elf.h version by David O'Brien */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/gcc/config/freebsd.h,v 1.25.2.4 2000/10/31 11:52:02 obrien Exp $ */ /* Cpp, assembler, linker, library, and startfile spec's. */ diff --git a/contrib/gcc/config/i386/freebsd.h b/contrib/gcc/config/i386/freebsd.h index d4a03586532f..b538465e8326 100644 --- a/contrib/gcc/config/i386/freebsd.h +++ b/contrib/gcc/config/i386/freebsd.h @@ -23,7 +23,7 @@ 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. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/gcc/config/i386/freebsd.h,v 1.34.2.1 2000/07/04 05:51:05 obrien Exp $ */ #undef CPP_PREDEFINES #define CPP_PREDEFINES \ diff --git a/contrib/gcc/config/i386/freebsd.h.fixed b/contrib/gcc/config/i386/freebsd.h.fixed deleted file mode 100644 index d4a03586532f..000000000000 --- a/contrib/gcc/config/i386/freebsd.h.fixed +++ /dev/null @@ -1,692 +0,0 @@ -/* Definitions for Intel 386 running FreeBSD with either a.out or ELF format - Copyright (C) 1996-2000 Free Software Foundation, Inc. - Contributed by Eric Youngdale. - Modified for stabs-in-ELF by H.J. Lu. - Adapted from GNU/Linux version by John Polstra. - Added support for generating "old a.out gas" on the fly by Peter Wemm. - Continued development by David O'Brien <obrien@freebsd.org> - -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. */ - -/* $FreeBSD$ */ - -#undef CPP_PREDEFINES -#define CPP_PREDEFINES \ - "-Di386 -Acpu(i386) -Amachine(i386)" \ - FBSD_CPP_PREDEFINES - -#undef CC1_SPEC -#define CC1_SPEC "\ - %{gline:%{!g:%{!g0:%{!g1:%{!g2: -g1}}}}} \ - %{maout: %{!mno-underscores: %{!munderscores: -munderscores }}}" - -#undef ASM_SPEC -#define ASM_SPEC "%{v*: -v} %{maout: %{fpic:-k} %{fPIC:-k}}" - -#undef ASM_FINAL_SPEC -#define ASM_FINAL_SPEC "%|" - -/* Provide a LINK_SPEC appropriate for FreeBSD. 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. */ - -#undef LINK_SPEC -#define LINK_SPEC "\ - %{p:%e`-p' not supported; use `-pg' and gprof(1)} \ - %{maout: %{shared:-Bshareable} \ - %{!shared:%{!nostdlib:%{!r:%{!e*:-e start}}} -dc -dp %{static:-Bstatic} \ - %{pg:-Bstatic} %{Z}} \ - %{assert*} %{R*}} \ - %{!maout: \ - -m elf_i386 \ - %{Wl,*:%*} \ - %{assert*} %{R*} %{rpath*} %{defsym*} \ - %{shared:-Bshareable %{h*} %{soname*}} \ - %{symbolic:-Bsymbolic} \ - %{!shared: \ - %{!static: \ - %{rdynamic: -export-dynamic} \ - %{!dynamic-linker: -dynamic-linker /usr/libexec/ld-elf.so.1}} \ - %{static:-Bstatic}}}" - -#undef STARTFILE_SPEC -#define STARTFILE_SPEC "\ - %{maout: %{shared:c++rt0.o%s} \ - %{!shared: \ - %{pg:gcrt0.o%s}%{!pg: \ - %{static:scrt0.o%s} \ - %{!static:crt0.o%s}}}} \ - %{!maout: \ - %{!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 an ENDFILE_SPEC appropriate for FreeBSD/i386. Here we tack on our - own magical crtend.o file (compare w/crtstuff.c) which provides part of the - support for getting C++ file-scope static object constructed before - entering `main', followed by the normal "finalizer" file, `crtn.o'. */ - -#undef ENDFILE_SPEC -#define ENDFILE_SPEC "\ - %{!maout: \ - %{!shared:crtend.o%s} \ - %{shared:crtendS.o%s} crtn.o%s}" - - -/************************[ Target stuff ]***********************************/ - -/* Define the actual types of some ANSI-mandated types. - Needs to agree with <machine/ansi.h>. GCC defaults come from c-decl.c, - c-common.c, and config/<arch>/<arch>.h. */ - -#undef SIZE_TYPE -#define SIZE_TYPE "unsigned int" - -#undef PTRDIFF_TYPE -#define PTRDIFF_TYPE "int" - -/* This is the pseudo-op used to generate a 32-bit word of data with a - specific value in some section. */ - -#undef INT_ASM_OP -#define INT_ASM_OP ".long" - -/* Biggest alignment supported by the object file format of this - machine. Use this macro to limit the alignment which can be - specified using the `__attribute__ ((aligned (N)))' construct. If - not defined, the default value is `BIGGEST_ALIGNMENT'. */ - -#define MAX_OFILE_ALIGNMENT (32768*8) - -#undef TARGET_VERSION -#define TARGET_VERSION fprintf (stderr, " (i386 FreeBSD/ELF)"); - -#define MASK_PROFILER_EPILOGUE 010000000000 -#define MASK_AOUT 004000000000 /* a.out not elf */ -#define MASK_UNDERSCORES 002000000000 /* use leading _ */ - -#define TARGET_PROFILER_EPILOGUE (target_flags & MASK_PROFILER_EPILOGUE) -#define TARGET_AOUT (target_flags & MASK_AOUT) -#define TARGET_ELF ((target_flags & MASK_AOUT) == 0) -#define TARGET_UNDERSCORES ((target_flags & MASK_UNDERSCORES) != 0) - -#undef SUBTARGET_SWITCHES -#define SUBTARGET_SWITCHES \ - { "profiler-epilogue", MASK_PROFILER_EPILOGUE}, \ - { "no-profiler-epilogue", -MASK_PROFILER_EPILOGUE}, \ - { "aout", MASK_AOUT}, \ - { "no-aout", -MASK_AOUT}, \ - { "underscores", MASK_UNDERSCORES}, \ - { "no-underscores", -MASK_UNDERSCORES}, - -/* This goes away when the math emulator is fixed. */ -#undef TARGET_DEFAULT -#define TARGET_DEFAULT \ - (MASK_80387 | MASK_IEEE_FP | MASK_FLOAT_RETURNS | MASK_NO_FANCY_MATH_387) - -/* Prefix for internally generated assembler labels. If we aren't using - underscores, we are using prefix `.'s to identify labels that should - be ignored, as in `i386/gas.h' --karl@cs.umb.edu */ -#undef LPREFIX -#define LPREFIX ((TARGET_UNDERSCORES) ? "L" : ".L") - -/* The a.out tools do not support "linkonce" sections. */ -#undef SUPPORTS_ONE_ONLY -#define SUPPORTS_ONE_ONLY TARGET_ELF - -/* Enable alias attribute support. */ -#undef SET_ASM_OP -#define SET_ASM_OP ".set" - -/* The a.out tools do not support "Lscope" .stabs symbols. */ -#undef NO_DBX_FUNCTION_END -#define NO_DBX_FUNCTION_END TARGET_AOUT - -/* In ELF, the function stabs come first, before the relative offsets. */ -#undef DBX_FUNCTION_FIRST -#define DBX_CHECK_FUNCTION_FIRST TARGET_ELF - -/* supply our own hook for calling __main() from main() */ -#undef INVOKE__main -#define INVOKE__main -#undef GEN_CALL__MAIN -#define GEN_CALL__MAIN \ - do { \ - if (!(TARGET_ELF)) \ - emit_library_call (gen_rtx (SYMBOL_REF, Pmode, NAME__MAIN), 0, \ - VOIDmode, 0); \ - } while (0) - -/* Indicate that jump tables go in the text section. This is - necessary when compiling PIC code. */ -#undef JUMP_TABLES_IN_TEXT_SECTION -#define JUMP_TABLES_IN_TEXT_SECTION (flag_pic) - -/* override the exception table positioning */ -#undef EXCEPTION_SECTION -#define EXCEPTION_SECTION() \ - do { \ - if (TARGET_ELF) \ - { \ - named_section (NULL_TREE, ".gcc_except_table", 0); \ - } \ - else \ - { \ - if (flag_pic) \ - data_section (); \ - else \ - readonly_data_section (); \ - } \ - } while (0); - -/* Tell final.c that we don't need a label passed to mcount. */ -#undef NO_PROFILE_DATA -#define NO_PROFILE_DATA - -/* Output assembler code to FILE to begin profiling of the current function. - LABELNO is an optional label. */ - -#undef FUNCTION_PROFILER -#define FUNCTION_PROFILER(FILE, LABELNO) \ - do { \ - char *_name = TARGET_AOUT ? "mcount" : ".mcount"; \ - if (flag_pic) \ - fprintf ((FILE), "\tcall *%s@GOT(%%ebx)\n", _name); \ - else \ - fprintf ((FILE), "\tcall %s\n", _name); \ - } while (0) - -/* Output assembler code to FILE to end profiling of the current function. */ - -#undef FUNCTION_PROFILER_EPILOGUE -#define FUNCTION_PROFILER_EPILOGUE(FILE, DO_RTL) \ - do { \ - if (TARGET_PROFILER_EPILOGUE) \ - { \ - if (DO_RTL) \ - { \ - /* ".mexitcount" is specially handled in \ - ASM_HACK_SYMBOLREF () so that we don't need to handle \ - flag_pic or TARGET_AOUT here. */ \ - rtx xop; \ - xop = gen_rtx_MEM (FUNCTION_MODE, \ - gen_rtx_SYMBOL_REF (Pmode, ".mexitcount")); \ - emit_call_insn (gen_rtx (CALL, VOIDmode, xop, const0_rtx)); \ - } \ - else \ - { \ - /* XXX this !DO_RTL case is broken but not actually used. */ \ - char *_name = TARGET_AOUT ? "mcount" : ".mcount"; \ - if (flag_pic) \ - fprintf (FILE, "\tcall *%s@GOT(%%ebx)\n", _name); \ - else \ - fprintf (FILE, "\tcall %s\n", _name); \ - } \ - } \ - } while (0) - - -/************************[ Assembler stuff ]********************************/ - -#undef ASM_APP_ON -#define ASM_APP_ON "#APP\n" - -#undef ASM_APP_OFF -#define ASM_APP_OFF "#NO_APP\n" - -/* This is how to begin an assembly language file. - The .file command should always begin the output. - ELF also needs a .version. */ - -#undef ASM_FILE_START -#define ASM_FILE_START(FILE) \ - do { \ - output_file_directive ((FILE), main_input_filename); \ - if (TARGET_ELF) \ - fprintf ((FILE), "\t.version\t\"01.01\"\n"); \ - } while (0) - -/* This is how to store into the string BUF - 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(BUF, PREFIX, NUMBER) \ - sprintf ((BUF), "*%s%s%d", (TARGET_UNDERSCORES) ? "" : ".", \ - (PREFIX), (NUMBER)) - -/* This is how to output an internal numbered label where - PREFIX is the class of label and NUM is the number within the class. - For most svr4/ELF systems, the convention is that any symbol which begins - with a period is not put into the linker symbol table by the assembler. */ -#undef ASM_OUTPUT_INTERNAL_LABEL -#define ASM_OUTPUT_INTERNAL_LABEL(FILE,PREFIX,NUM) \ - fprintf ((FILE), "%s%s%d:\n", (TARGET_UNDERSCORES) ? "" : ".", \ - (PREFIX), (NUM)) - -/* This is how to output a reference to a user-level label named NAME. */ -#undef ASM_OUTPUT_LABELREF -#define ASM_OUTPUT_LABELREF(FILE, NAME) \ - do { \ - char *_name = (NAME); \ - /* Hack to avoid writing lots of rtl in \ - FUNCTION_PROFILER_EPILOGUE (). */ \ - if (*_name == '.' && strcmp(_name + 1, "mexitcount") == 0) \ - { \ - if (TARGET_AOUT) \ - _name++; \ - if (flag_pic) \ - fprintf ((FILE), "*%s@GOT(%%ebx)", _name); \ - else \ - fprintf ((FILE), "%s", _name); \ - } \ - else \ - fprintf (FILE, "%s%s", TARGET_UNDERSCORES ? "_" : "", _name); \ -} while (0) - -/* This is how to hack on the symbol code of certain relcalcitrant - symbols to modify their output in output_pic_addr_const (). */ - -#undef ASM_HACK_SYMBOLREF_CODE -#define ASM_HACK_SYMBOLREF_CODE(NAME, CODE) \ - do { \ - /* Part of hack to avoid writing lots of rtl in \ - FUNCTION_PROFILER_EPILOGUE (). */ \ - char *_name = (NAME); \ - if (*_name == '.' && strcmp(_name + 1, "mexitcount") == 0) \ - (CODE) = 'X'; \ - } while (0) - -/* This is how to output an element of a case-vector that is relative. - This is only used for PIC code. See comments by the `casesi' insn in - i386.md for an explanation of the expression this outputs. */ -#undef ASM_OUTPUT_ADDR_DIFF_ELT -#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \ - fprintf ((FILE), "\t.long _GLOBAL_OFFSET_TABLE_+[.-%s%d]\n", LPREFIX, (VALUE)) - -#undef ASM_OUTPUT_ALIGN -#define ASM_OUTPUT_ALIGN(FILE, LOG) \ - if ((LOG)!=0) { \ - if (in_text_section()) \ - fprintf ((FILE), "\t.p2align %d,0x90\n", (LOG)); \ - else \ - fprintf ((FILE), "\t.p2align %d\n", (LOG)); \ - } - -#undef ASM_OUTPUT_SOURCE_LINE -#define ASM_OUTPUT_SOURCE_LINE(FILE, LINE) \ - do { \ - static int sym_lineno = 1; \ - if (TARGET_ELF) \ - { \ - fprintf ((FILE), ".stabn 68,0,%d,.LM%d-", (LINE), sym_lineno); \ - assemble_name ((FILE), \ - XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0)); \ - fprintf ((FILE), "\n.LM%d:\n", sym_lineno); \ - sym_lineno += 1; \ - } \ - else \ - { \ - fprintf ((FILE), "\t%s %d,0,%d\n", ASM_STABD_OP, N_SLINE, \ - lineno); \ - } \ - } while (0) - -/* These macros generate the special .type and .size directives which - are used to set the corresponding fields of the linker symbol table - entries in an ELF object file under SVR4. These macros also output - the starting labels for the relevant functions/objects. */ - -/* Write the extra assembler code needed to declare a function properly. - Some svr4 assemblers need to also have something extra said about the - function's return value. We allow for that here. */ - -#undef ASM_DECLARE_FUNCTION_NAME -#define ASM_DECLARE_FUNCTION_NAME(FILE, NAME, DECL) \ - do { \ - fprintf (FILE, "\t%s\t ", TYPE_ASM_OP); \ - assemble_name (FILE, NAME); \ - putc (',', FILE); \ - fprintf (FILE, TYPE_OPERAND_FMT, "function"); \ - putc ('\n', FILE); \ - ASM_DECLARE_RESULT (FILE, DECL_RESULT (DECL)); \ - ASM_OUTPUT_LABEL(FILE, NAME); \ - } while (0) - -/* This is how to declare the size of a function. */ - -#undef ASM_DECLARE_FUNCTION_SIZE -#define ASM_DECLARE_FUNCTION_SIZE(FILE, FNAME, DECL) \ - do { \ - if (!flag_inhibit_size_directive) \ - { \ - char label[256]; \ - static int labelno; \ - labelno++; \ - ASM_GENERATE_INTERNAL_LABEL (label, "Lfe", labelno); \ - ASM_OUTPUT_INTERNAL_LABEL (FILE, "Lfe", labelno); \ - fprintf (FILE, "\t%s\t ", SIZE_ASM_OP); \ - assemble_name (FILE, (FNAME)); \ - fprintf (FILE, ","); \ - assemble_name (FILE, label); \ - fprintf (FILE, "-"); \ - assemble_name (FILE, (FNAME)); \ - putc ('\n', FILE); \ - } \ - } while (0) - - -/* The routine used to output NUL terminated strings. We use a special - version of this for most svr4 targets because doing so makes the - generated assembly code more compact (and thus faster to assemble) - as well as more readable, especially for targets like the i386 - (where the only alternative is to output character sequences as - comma separated lists of numbers). */ - -#undef ASM_OUTPUT_LIMITED_STRING -#define ASM_OUTPUT_LIMITED_STRING(FILE, STR) \ - do { \ - register unsigned char *_limited_str = (unsigned char *) (STR); \ - register unsigned ch; \ - fprintf ((FILE), "\t%s\t\"", STRING_ASM_OP); \ - for (; (ch = *_limited_str); _limited_str++) \ - { \ - register int escape; \ - switch (escape = ESCAPES[ch]) \ - { \ - case 0: \ - putc (ch, (FILE)); \ - break; \ - case 1: \ - fprintf ((FILE), "\\%03o", ch); \ - break; \ - default: \ - putc ('\\', (FILE)); \ - putc (escape, (FILE)); \ - break; \ - } \ - } \ - fprintf ((FILE), "\"\n"); \ - } while (0) - -/* Switch into a generic section. - - We make the section read-only and executable for a function decl, - read-only for a const data decl, and writable for a non-const data decl. - - If the section has already been defined, we must not - emit the attributes here. The SVR4 assembler does not - recognize section redefinitions. - If DECL is NULL, no attributes are emitted. */ - -#undef ASM_OUTPUT_SECTION_NAME -#define ASM_OUTPUT_SECTION_NAME(FILE, DECL, NAME, RELOC) \ - do { \ - static struct section_info \ - { \ - struct section_info *next; \ - char *name; \ - enum sect_enum {SECT_RW, SECT_RO, SECT_EXEC} type; \ - } *sections; \ - struct section_info *s; \ - char *mode; \ - enum sect_enum type; \ - \ - for (s = sections; s; s = s->next) \ - if (!strcmp (NAME, s->name)) \ - break; \ - \ - if (DECL && TREE_CODE (DECL) == FUNCTION_DECL) \ - type = SECT_EXEC, mode = "ax"; \ - else if (DECL && DECL_READONLY_SECTION (DECL, RELOC)) \ - type = SECT_RO, mode = "a"; \ - else \ - type = SECT_RW, mode = "aw"; \ - \ - if (s == 0) \ - { \ - s = (struct section_info *) xmalloc (sizeof (struct section_info)); \ - s->name = xmalloc ((strlen (NAME) + 1) * sizeof (*NAME)); \ - strcpy (s->name, NAME); \ - s->type = type; \ - s->next = sections; \ - sections = s; \ - fprintf (FILE, ".section\t%s,\"%s\",@progbits\n", NAME, mode); \ - } \ - else \ - { \ - if (DECL && s->type != type) \ - error_with_decl (DECL, "%s causes a section type conflict"); \ - \ - fprintf (FILE, ".section\t%s\n", NAME); \ - } \ - } while (0) - -#undef MAKE_DECL_ONE_ONLY -#define MAKE_DECL_ONE_ONLY(DECL) (DECL_WEAK (DECL) = 1) -#undef UNIQUE_SECTION_P -#define UNIQUE_SECTION_P(DECL) (DECL_ONE_ONLY (DECL)) -#undef UNIQUE_SECTION -#define UNIQUE_SECTION(DECL,RELOC) \ - do { \ - int len; \ - char *name, *string, *prefix; \ - \ - name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (DECL)); \ - \ - if (! DECL_ONE_ONLY (DECL)) \ - { \ - prefix = "."; \ - if (TREE_CODE (DECL) == FUNCTION_DECL) \ - prefix = ".text."; \ - else if (DECL_READONLY_SECTION (DECL, RELOC)) \ - prefix = ".rodata."; \ - else \ - prefix = ".data."; \ - } \ - else if (TREE_CODE (DECL) == FUNCTION_DECL) \ - prefix = ".gnu.linkonce.t."; \ - else if (DECL_READONLY_SECTION (DECL, RELOC)) \ - prefix = ".gnu.linkonce.r."; \ - else \ - prefix = ".gnu.linkonce.d."; \ - \ - len = strlen (name) + strlen (prefix); \ - string = alloca (len + 1); \ - sprintf (string, "%s%s", prefix, name); \ - \ - DECL_SECTION_NAME (DECL) = build_string (len, string); \ - } while (0) - -/* A C statement or statements to switch to the appropriate - section for output of DECL. DECL is either a `VAR_DECL' node - or a constant of some sort. RELOC indicates whether forming - the initial value of DECL requires link-time relocations. */ - -#undef SELECT_SECTION -#define SELECT_SECTION(DECL,RELOC) \ - { \ - if (flag_pic && RELOC) \ - data_section (); \ - else if (TREE_CODE (DECL) == STRING_CST) \ - { \ - if (! flag_writable_strings) \ - const_section (); \ - else \ - data_section (); \ - } \ - else if (TREE_CODE (DECL) == VAR_DECL) \ - { \ - if (! DECL_READONLY_SECTION (DECL, RELOC)) \ - data_section (); \ - else \ - const_section (); \ - } \ - else \ - const_section (); \ - } - -/* Define macro used to output shift-double opcodes when the shift - count is in %cl. Some assemblers require %cl as an argument; - some don't. - - *OLD* GAS requires the %cl argument, so override i386/unix.h. */ - -#undef AS3_SHIFT_DOUBLE -#define AS3_SHIFT_DOUBLE(a,b,c,d) AS3 (a,b,c,d) - - -/************************[ Debugger stuff ]*********************************/ - -/* Copy this from the svr4 specifications... */ -/* Define the register numbers to be used in Dwarf debugging information. - The SVR4 reference port C compiler uses the following register numbers - in its Dwarf output code: - 0 for %eax (gnu regno = 0) - 1 for %ecx (gnu regno = 2) - 2 for %edx (gnu regno = 1) - 3 for %ebx (gnu regno = 3) - 4 for %esp (gnu regno = 7) - 5 for %ebp (gnu regno = 6) - 6 for %esi (gnu regno = 4) - 7 for %edi (gnu regno = 5) - The following three DWARF register numbers are never generated by - the SVR4 C compiler or by the GNU compilers, but SDB on x86/svr4 - believes these numbers have these meanings. - 8 for %eip (no gnu equivalent) - 9 for %eflags (no gnu equivalent) - 10 for %trapno (no gnu equivalent) - It is not at all clear how we should number the FP stack registers - for the x86 architecture. If the version of SDB on x86/svr4 were - a bit less brain dead with respect to floating-point then we would - have a precedent to follow with respect to DWARF register numbers - for x86 FP registers, but the SDB on x86/svr4 is so completely - broken with respect to FP registers that it is hardly worth thinking - of it as something to strive for compatibility with. - The version of x86/svr4 SDB I have at the moment does (partially) - seem to believe that DWARF register number 11 is associated with - the x86 register %st(0), but that's about all. Higher DWARF - register numbers don't seem to be associated with anything in - particular, and even for DWARF regno 11, SDB only seems to under- - stand that it should say that a variable lives in %st(0) (when - asked via an `=' command) if we said it was in DWARF regno 11, - but SDB still prints garbage when asked for the value of the - variable in question (via a `/' command). - (Also note that the labels SDB prints for various FP stack regs - when doing an `x' command are all wrong.) - Note that these problems generally don't affect the native SVR4 - C compiler because it doesn't allow the use of -O with -g and - because when it is *not* optimizing, it allocates a memory - location for each floating-point variable, and the memory - location is what gets described in the DWARF AT_location - attribute for the variable in question. - Regardless of the severe mental illness of the x86/svr4 SDB, we - do something sensible here and we use the following DWARF - register numbers. Note that these are all stack-top-relative - numbers. - 11 for %st(0) (gnu regno = 8) - 12 for %st(1) (gnu regno = 9) - 13 for %st(2) (gnu regno = 10) - 14 for %st(3) (gnu regno = 11) - 15 for %st(4) (gnu regno = 12) - 16 for %st(5) (gnu regno = 13) - 17 for %st(6) (gnu regno = 14) - 18 for %st(7) (gnu regno = 15) -*/ -#undef DWARF_DBX_REGISTER_NUMBER -#define DWARF_DBX_REGISTER_NUMBER(n) \ -((n) == 0 ? 0 \ - : (n) == 1 ? 2 \ - : (n) == 2 ? 1 \ - : (n) == 3 ? 3 \ - : (n) == 4 ? 6 \ - : (n) == 5 ? 7 \ - : (n) == 6 ? 5 \ - : (n) == 7 ? 4 \ - : ((n) >= FIRST_STACK_REG && (n) <= LAST_STACK_REG) ? (n)+3 \ - : (-1)) - -/* Now what stabs expects in the register. */ -#undef STABS_DBX_REGISTER_NUMBER -#define STABS_DBX_REGISTER_NUMBER(n) \ -((n) == 0 ? 0 : \ - (n) == 1 ? 2 : \ - (n) == 2 ? 1 : \ - (n) == 3 ? 3 : \ - (n) == 4 ? 6 : \ - (n) == 5 ? 7 : \ - (n) == 6 ? 4 : \ - (n) == 7 ? 5 : \ - (n) + 4) - -#undef DBX_REGISTER_NUMBER -#define DBX_REGISTER_NUMBER(n) ((write_symbols == DWARF_DEBUG) \ - ? DWARF_DBX_REGISTER_NUMBER(n) \ - : STABS_DBX_REGISTER_NUMBER(n)) - -/* tag end of file in elf mode */ -#undef DBX_OUTPUT_MAIN_SOURCE_FILE_END -#define DBX_OUTPUT_MAIN_SOURCE_FILE_END(FILE, FILENAME) \ - do { \ - if (TARGET_ELF) { \ - fprintf ((FILE), "\t.text\n\t.stabs \"\",%d,0,0,.Letext\n.Letext:\n", \ - N_SO); \ - } \ - } while (0) - -/* stabs-in-elf has offsets relative to function beginning */ -#undef DBX_OUTPUT_LBRAC -#define DBX_OUTPUT_LBRAC(FILE, NAME) \ - do { \ - fprintf (asmfile, "%s %d,0,0,", ASM_STABN_OP, N_LBRAC); \ - assemble_name (asmfile, buf); \ - if (TARGET_ELF) \ - { \ - fputc ('-', asmfile); \ - assemble_name (asmfile, \ - XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0)); \ - } \ - fprintf (asmfile, "\n"); \ - } while (0) - -#undef DBX_OUTPUT_RBRAC -#define DBX_OUTPUT_RBRAC(FILE, NAME) \ - do { \ - fprintf (asmfile, "%s %d,0,0,", ASM_STABN_OP, N_RBRAC); \ - assemble_name (asmfile, buf); \ - if (TARGET_ELF) \ - { \ - fputc ('-', asmfile); \ - assemble_name (asmfile, \ - XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0)); \ - } \ - fprintf (asmfile, "\n"); \ - } while (0) diff --git a/contrib/gcc/config/i386/i386.c b/contrib/gcc/config/i386/i386.c index 9eb9582553c4..458e1760a4a0 100644 --- a/contrib/gcc/config/i386/i386.c +++ b/contrib/gcc/config/i386/i386.c @@ -18,7 +18,7 @@ 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. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/gcc/config/i386/i386.c,v 1.7 2000/01/29 13:06:33 obrien Exp $ */ #include <setjmp.h> #include "config.h" diff --git a/contrib/gcc/config/i386/i386.h b/contrib/gcc/config/i386/i386.h index e492e04101ee..92f2f92aacec 100644 --- a/contrib/gcc/config/i386/i386.h +++ b/contrib/gcc/config/i386/i386.h @@ -34,7 +34,7 @@ Boston, MA 02111-1307, USA. */ PUT_OP_SIZE, USE_STAR, ADDR_BEG, ADDR_END, PRINT_IREG, PRINT_SCALE, PRINT_B_I_S, and many that start with ASM_ or end in ASM_OP. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/gcc/config/i386/i386.h,v 1.5 1999/10/16 08:10:36 obrien Exp $ */ /* Names to predefine in the preprocessor for this target machine. */ diff --git a/contrib/gcc/config/i386/i386.md b/contrib/gcc/config/i386/i386.md index 27df88774449..3c7b0eae5b18 100644 --- a/contrib/gcc/config/i386/i386.md +++ b/contrib/gcc/config/i386/i386.md @@ -71,7 +71,7 @@ ;; This shadows the processor_type enumeration, so changes must be made ;; to i386.h at the same time. -;; $FreeBSD$ +;; $FreeBSD: src/contrib/gcc/config/i386/i386.md,v 1.7.2.1 2000/08/07 10:06:43 obrien Exp $ (define_attr "type" "integer,binary,memory,test,compare,fcompare,idiv,imul,lea,fld,fpop,fpdiv,fpmul" diff --git a/contrib/gcc/config/mips/abi64.h b/contrib/gcc/config/mips/abi64.h new file mode 100644 index 000000000000..ce1e5fe3831f --- /dev/null +++ b/contrib/gcc/config/mips/abi64.h @@ -0,0 +1,250 @@ +/* Definitions of target machine for GNU compiler. 64 bit ABI support. + Copyright (C) 1994, 1995, 1996, 1998, 1999 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. */ + +/* Macros to implement the 64 bit ABI. This file is meant to be included + after mips.h. */ + +#undef SUBTARGET_TARGET_OPTIONS +#define SUBTARGET_TARGET_OPTIONS \ + { "abi=", &mips_abi_string, \ + "Speciy ABI to use"}, + +#undef STACK_BOUNDARY +#define STACK_BOUNDARY \ + ((mips_abi == ABI_32 || mips_abi == ABI_O64 || mips_abi == ABI_EABI) \ + ? 64 : 128) + +#undef MIPS_STACK_ALIGN +#define MIPS_STACK_ALIGN(LOC) \ + ((mips_abi == ABI_32 || mips_abi == ABI_O64 || mips_abi == ABI_EABI) \ + ? ((LOC) + 7) & ~7 \ + : ((LOC) + 15) & ~15) + +#undef GP_ARG_LAST +#define GP_ARG_LAST ((mips_abi == ABI_32 || mips_abi == ABI_O64) \ + ? GP_REG_FIRST + 7 : GP_REG_FIRST + 11) +#undef FP_ARG_LAST +#define FP_ARG_LAST ((mips_abi == ABI_32 || mips_abi == ABI_O64) \ + ? FP_REG_FIRST + 15 : FP_REG_FIRST + 19) + +#undef SUBTARGET_CONDITIONAL_REGISTER_USAGE +#define SUBTARGET_CONDITIONAL_REGISTER_USAGE \ +{ \ + /* fp20-23 are now caller saved. */ \ + if (mips_abi == ABI_64) \ + { \ + int regno; \ + for (regno = FP_REG_FIRST + 20; regno < FP_REG_FIRST + 24; regno++) \ + call_used_regs[regno] = 1; \ + } \ + /* odd registers from fp21 to fp31 are now caller saved. */ \ + if (mips_abi == ABI_N32) \ + { \ + int regno; \ + for (regno = FP_REG_FIRST + 21; regno <= FP_REG_FIRST + 31; regno+=2) \ + call_used_regs[regno] = 1; \ + } \ +} + +#undef MAX_ARGS_IN_REGISTERS +#define MAX_ARGS_IN_REGISTERS ((mips_abi == ABI_32 || mips_abi == ABI_O64) \ + ? 4 : 8) + +#undef REG_PARM_STACK_SPACE +#define REG_PARM_STACK_SPACE(FNDECL) \ + ((mips_abi == ABI_32 || mips_abi == ABI_O64) \ + ? (MAX_ARGS_IN_REGISTERS*UNITS_PER_WORD) - FIRST_PARM_OFFSET (FNDECL) \ + : 0) + +#define FUNCTION_ARG_PADDING(MODE, TYPE) \ + (! 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 \ + && (mips_abi == ABI_32 \ + || mips_abi == ABI_O64 \ + || mips_abi == ABI_EABI \ + || GET_MODE_CLASS (MODE) == MODE_INT))) \ + ? downward : upward)) + +#undef RETURN_IN_MEMORY +#define RETURN_IN_MEMORY(TYPE) \ + ((mips_abi == ABI_32 || mips_abi == ABI_O64) \ + ? TYPE_MODE (TYPE) == BLKmode \ + : (int_size_in_bytes (TYPE) \ + > (mips_abi == ABI_EABI ? 2 * UNITS_PER_WORD : 16))) + +extern struct rtx_def *mips_function_value (); +#undef FUNCTION_VALUE +#define FUNCTION_VALUE(VALTYPE, FUNC) mips_function_value (VALTYPE, FUNC) + +/* For varargs, we must save the current argument, because it is the fake + argument va_alist, and will need to be converted to the real argument. + For stdarg, we do not need to save the current argument, because it + is a real argument. */ +#define SETUP_INCOMING_VARARGS(CUM,MODE,TYPE,PRETEND_SIZE,NO_RTL) \ +{ int mips_off = (! current_function_varargs) && (! (CUM).last_arg_fp); \ + int mips_fp_off = (! current_function_varargs) && ((CUM).last_arg_fp); \ + if (((mips_abi != ABI_32 && mips_abi != ABI_O64) \ + && (CUM).arg_words < MAX_ARGS_IN_REGISTERS - mips_off) \ + || (mips_abi == ABI_EABI \ + && ! TARGET_SOFT_FLOAT \ + && (CUM).fp_arg_words < MAX_ARGS_IN_REGISTERS - mips_fp_off)) \ + { \ + int mips_save_gp_regs = \ + MAX_ARGS_IN_REGISTERS - (CUM).arg_words - mips_off; \ + int mips_save_fp_regs = \ + (mips_abi != ABI_EABI ? 0 \ + : MAX_ARGS_IN_REGISTERS - (CUM).fp_arg_words - mips_fp_off); \ + \ + if (mips_save_gp_regs < 0) \ + mips_save_gp_regs = 0; \ + if (mips_save_fp_regs < 0) \ + mips_save_fp_regs = 0; \ + PRETEND_SIZE = ((mips_save_gp_regs * UNITS_PER_WORD) \ + + (mips_save_fp_regs * UNITS_PER_FPREG)); \ + \ + if (! (NO_RTL)) \ + { \ + if ((CUM).arg_words < MAX_ARGS_IN_REGISTERS - mips_off) \ + { \ + rtx ptr, mem; \ + if (mips_abi != ABI_EABI) \ + ptr = virtual_incoming_args_rtx; \ + else \ + ptr = plus_constant (virtual_incoming_args_rtx, \ + - (mips_save_gp_regs \ + * UNITS_PER_WORD)); \ + mem = gen_rtx (MEM, BLKmode, ptr); \ + /* va_arg is an array access in this case, which causes \ + it to get MEM_IN_STRUCT_P set. We must set it here \ + so that the insn scheduler won't assume that these \ + stores can't possibly overlap with the va_arg loads. */ \ + if (mips_abi != ABI_EABI && BYTES_BIG_ENDIAN) \ + MEM_SET_IN_STRUCT_P (mem, 1); \ + move_block_from_reg \ + ((CUM).arg_words + GP_ARG_FIRST + mips_off, \ + mem, \ + mips_save_gp_regs, \ + mips_save_gp_regs * UNITS_PER_WORD); \ + } \ + if (mips_abi == ABI_EABI \ + && ! TARGET_SOFT_FLOAT \ + && (CUM).fp_arg_words < MAX_ARGS_IN_REGISTERS - mips_fp_off) \ + { \ + enum machine_mode mode = TARGET_SINGLE_FLOAT ? SFmode : DFmode; \ + int size = GET_MODE_SIZE (mode); \ + int off; \ + int i; \ + /* We can't use move_block_from_reg, because it will use \ + the wrong mode. */ \ + off = - (mips_save_gp_regs * UNITS_PER_WORD); \ + if (! TARGET_SINGLE_FLOAT) \ + off &= ~ 7; \ + if (! TARGET_FLOAT64 || TARGET_SINGLE_FLOAT) \ + off -= (mips_save_fp_regs / 2) * size; \ + else \ + off -= mips_save_fp_regs * size; \ + for (i = 0; i < mips_save_fp_regs; i++) \ + { \ + rtx tem = \ + gen_rtx (MEM, mode, \ + plus_constant (virtual_incoming_args_rtx, \ + off)); \ + emit_move_insn (tem, \ + gen_rtx (REG, mode, \ + ((CUM).fp_arg_words \ + + FP_ARG_FIRST \ + + i \ + + mips_fp_off))); \ + off += size; \ + if (! TARGET_FLOAT64 || TARGET_SINGLE_FLOAT) \ + ++i; \ + } \ + } \ + } \ + } \ +} + +#define STRICT_ARGUMENT_NAMING (mips_abi != ABI_32 && mips_abi != ABI_O64) + +/* 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. */ +#define FUNCTION_ARG_PASS_BY_REFERENCE(CUM, MODE, TYPE, NAMED) \ + (mips_abi == ABI_EABI \ + && function_arg_pass_by_reference (&CUM, MODE, TYPE, NAMED)) + +/* A C expression that indicates when it is the called function's + responsibility to make a copy of arguments passed by invisible + reference. Normally, the caller makes a copy and passes the + address of the copy to the routine being called. When + FUNCTION_ARG_CALLEE_COPIES is defined and is nonzero, the caller + does not make a copy. Instead, it passes a pointer to the "live" + value. The called function must not modify this value. If it can + be determined that the value won't be modified, it need not make a + copy; otherwise a copy must be made. + + ??? The MIPS EABI says that the caller should copy in ``K&R mode.'' + I don't know how to detect that here, since flag_traditional is not + a back end flag. */ +#define FUNCTION_ARG_CALLEE_COPIES(CUM, MODE, TYPE, NAMED) \ + (mips_abi == ABI_EABI && (NAMED) \ + && FUNCTION_ARG_PASS_BY_REFERENCE (CUM, MODE, TYPE, NAMED)) + +/* Define LONG_MAX correctly for all users. We need to handle 32 bit EABI, + 64 bit EABI, N32, and N64 as possible defaults. The checks performed here + are the same as the checks in override_options in mips.c that determines + whether MASK_LONG64 will be set. + + This does not handle inappropriate options or ununusal option + combinations. */ + +#undef LONG_MAX_SPEC +#if ((MIPS_ABI_DEFAULT == ABI_64) || ((MIPS_ABI_DEFAULT == ABI_EABI) && ((TARGET_DEFAULT | TARGET_CPU_DEFAULT) & MASK_64BIT))) +#define LONG_MAX_SPEC \ + "%{!mabi=32:%{!mabi=n32:%{!mlong32:%{!mgp32:%{!mips1:%{!mips2:-D__LONG_MAX__=9223372036854775807L}}}}}}" +#else +#define LONG_MAX_SPEC \ + "%{mabi=64:-D__LONG_MAX__=9223372036854775807L} \ + %{mlong64:-D__LONG_MAX__=9223372036854775807L} \ + %{mgp64:-D__LONG_MAX__=9223372036854775807L}" +#endif + +/* ??? Unimplemented stuff follows. */ + +/* ??? Add support for 16 byte/128 bit long doubles here when + mips_abi != ABI32. */ + +/* ??? Make main return zero if user did not specify return value. */ + +/* ??? Add support for .interfaces section, so as to get linker warnings + when stdarg functions called without prototype in scope? */ + +/* ??? Could optimize structure passing by putting the right register rtx + into the field decl, so that if we use the field, we can take the value from + a register instead of from memory. */ + + + diff --git a/contrib/gcc/config/mips/bsd-4.h b/contrib/gcc/config/mips/bsd-4.h new file mode 100644 index 000000000000..c2aee83df7b5 --- /dev/null +++ b/contrib/gcc/config/mips/bsd-4.h @@ -0,0 +1,46 @@ +/* Definitions of target machine for GNU compiler. MIPS RISC-OS BSD version. + Copyright (C) 1991 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 MIPS_BSD43 + +#define CPP_PREDEFINES "\ +-Dmips -Dunix -Dhost_mips -DMIPSEB -DR3000 -DSYSTYPE_BSD43 \ +-D_mips -D_unix -D_host_mips -D_MIPSEB -D_R3000 -D_SYSTYPE_BSD43 \ +-Asystem(unix) -Asystem(bsd) -Acpu(mips) -Amachine(mips)" + +#define STANDARD_INCLUDE_DIR "/bsd43/usr/include" + +#define LINK_SPEC "\ +%{G*} %{EB} %{EL} %{mips1} %{mips2} %{mips3} \ +%{bestGnum} %{shared} %{non_shared} \ +-systype /bsd43/" + +#define LIB_SPEC "%{p:-lprof1} %{pg:-lprof1} -lc" + +#define STARTFILE_SPEC "%{pg:gcrt0.o%s}%{!pg:%{p:mcrt0.o%s}%{!p:crt1.o%s crtn.o%s}}" + +#define MACHINE_TYPE "RISC-OS BSD Mips" + +/* Generate calls to memcpy, etc., not bcopy, etc. */ +#define TARGET_MEM_FUNCTIONS + +/* Override defaults for finding the MIPS tools. */ +#define MD_STARTFILE_PREFIX "/bsd43/usr/lib/cmplrs/cc/" +#define MD_EXEC_PREFIX "/bsd43/usr/lib/cmplrs/cc/" diff --git a/contrib/gcc/config/mips/bsd-5.h b/contrib/gcc/config/mips/bsd-5.h new file mode 100644 index 000000000000..f97af5e1f80d --- /dev/null +++ b/contrib/gcc/config/mips/bsd-5.h @@ -0,0 +1,67 @@ +/* Definitions of target machine for GNU compiler. + MIPS RISC-OS, 5.0 BSD version. + Copyright (C) 1991 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 MIPS_BSD43 + +#define CPP_PREDEFINES "\ +-Dmips -Dunix -Dhost_mips -DMIPSEB -DR3000 -DSYSTYPE_BSD43 \ +-D_mips -D_unix -D_host_mips -D_MIPSEB -D_R3000 -D_SYSTYPE_BSD43 \ +-Asystem(unix) -Asystem(bsd) -Acpu(mips) -Amachine(mips)" + +#define STANDARD_INCLUDE_DIR "/bsd43/usr/include" + +#define LINK_SPEC "\ +%{G*} %{EB} %{EL} %{mips1} %{mips2} %{mips3} \ +%{bestGnum} %{shared} %{non_shared} \ +%{call_shared} %{no_archive} %{exact_version} \ +%{!shared: %{!non_shared: %{!call_shared: -non_shared}}} \ +-systype /bsd43/" + +#define LIB_SPEC "%{p:-lprof1} %{pg:-lprof1} -lc" + +#define STARTFILE_SPEC "%{pg:gcrt0.o%s}%{!pg:%{p:mcrt0.o%s}%{!p:crt1.o%s crtn.o%s}}" + +#define MACHINE_TYPE "RISC-OS BSD Mips" + +/* Generate calls to memcpy, etc., not bcopy, etc. */ +#define TARGET_MEM_FUNCTIONS + +/* Override defaults for finding the MIPS tools. */ +#define MD_STARTFILE_PREFIX "/bsd43/usr/lib/cmplrs/cc/" +#define MD_EXEC_PREFIX "/bsd43/usr/lib/cmplrs/cc/" + +#include "mips/mips.h" + +/* Some assemblers have a bug that causes backslash escaped chars in .ascii + to be misassembled, so we just completely avoid it. */ +#undef ASM_OUTPUT_ASCII +#define ASM_OUTPUT_ASCII(FILE,PTR,LEN) \ +do { \ + unsigned char *s; \ + int i; \ + for (i = 0, s = (unsigned char *)(PTR); i < (LEN); s++, i++) \ + { \ + if ((i % 8) == 0) \ + fputs ("\n\t.byte\t", (FILE)); \ + fprintf ((FILE), "%s0x%x", (i%8?",":""), (unsigned)*s); \ + } \ + fputs ("\n", (FILE)); \ +} while (0) diff --git a/contrib/gcc/config/mips/cross64.h b/contrib/gcc/config/mips/cross64.h new file mode 100644 index 000000000000..4462e5ecc55c --- /dev/null +++ b/contrib/gcc/config/mips/cross64.h @@ -0,0 +1,34 @@ +/* Configuration for an Irix 5 host and Irix 6 target using SGI's cross64 + package. */ + +#define STANDARD_INCLUDE_DIR "/usr/cross64/usr/include" +#undef MD_EXEC_PREFIX +#define MD_EXEC_PREFIX "/usr/cross64/usr/bin/" +#undef MD_STARTFILE_PREFIX +#define MD_STARTFILE_PREFIX "/usr/cross64/usr/lib/lib64/" + +/* Must add TOOLROOT to the environment, or else the assembler will not + work. */ +#define INIT_ENVIRONMENT \ + "TOOLROOT=/usr/cross64" + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC \ + "%{mips1:%{pg:gcrt1.o%s}%{!pg:%{p:mcrt1.o%s libprof1.a%s}%{!p:crt1.o%s}}} \ + %{mips2:%{pg:gcrt1.o%s}%{!pg:%{p:mcrt1.o%s libprof1.a%s}%{!p:crt1.o%s}}} \ + %{!mips1:%{!mips2:%{pg:/usr/cross64/usr/lib64/mips4/gcrt1.o} \ + %{!pg:%{p:/usr/cross64/usr/lib64/mips4/mcrt1.o \ + /usr/cross64/usr/lib64/mips4/libprof1.a} \ + %{!p:/usr/cross64/usr/lib64/mips4/crt1.o}}}}" + +#undef ENDFILE_SPEC +#define ENDFILE_SPEC \ + "%{mips1:crtn.o%s}%{mips2:crtn.o%s}%{!mips1:%{!mips2:/usr/cross64/usr/lib64/mips4/crtn.o}}" + +#undef LINK_SPEC +#define LINK_SPEC "\ +-64 -_SYSTYPE_SVR4 %{G*} %{EB} %{EL} %{mips1} %{mips2} %{mips3} %{mips4} \ +%{bestGnum} %{shared} %{non_shared} \ +%{call_shared} %{no_archive} %{exact_version} \ +%{!shared: %{!non_shared: %{!call_shared: -call_shared -no_unresolved}}} \ +%{!mips1:%{!mips2:-L/usr/cross64/usr/lib64/mips4 -L/usr/cross64/usr/lib64}}" diff --git a/contrib/gcc/config/mips/dec-bsd.h b/contrib/gcc/config/mips/dec-bsd.h new file mode 100644 index 000000000000..126353b25dd7 --- /dev/null +++ b/contrib/gcc/config/mips/dec-bsd.h @@ -0,0 +1,53 @@ +/* Definitions for DECstation running BSD as target machine for GNU compiler. + 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. */ + +#define DECSTATION + +#ifndef CPP_PREDEFINES +#define CPP_PREDEFINES "-D__ANSI_COMPAT \ +-DMIPSEL -DR3000 -DSYSTYPE_BSD -D_SYSTYPE_BSD -Dbsd4_4 -Dhost_mips -Dmips \ +-Dunix -D_mips -D_unix -D_host_mips -D_MIPSEL -D_R3000 \ +-Asystem(unix) -Asystem(bsd) -Amachine(mips)" +#endif + +/* Always uses GNU ld. */ +#ifndef LINK_SPEC +#define LINK_SPEC "%{G*} %{EB} %{EL} %{mips1} %{mips2} %{mips3}" +#endif + +#define LIB_SPEC "" +#define STARTFILE_SPEC "" + +#ifndef MACHINE_TYPE +#define MACHINE_TYPE "DECstation running BSD 4.4" +#endif + +#define TARGET_DEFAULT MASK_GAS +#define PREFERRED_DEBUGGING_TYPE DBX_DEBUG + +#include "mips/mips.h" + +/* Since gas and gld are standard on 4.4 BSD, we don't need these */ +#undef MD_EXEC_PREFIX +#undef MD_STARTFILE_PREFIX +#undef ASM_FINAL_SPEC +#undef LIB_SPEC +#undef STARTFILE_SPEC + diff --git a/contrib/gcc/config/mips/dec-osf1.h b/contrib/gcc/config/mips/dec-osf1.h new file mode 100644 index 000000000000..ee7e787353c4 --- /dev/null +++ b/contrib/gcc/config/mips/dec-osf1.h @@ -0,0 +1,55 @@ +/* Definitions of target machine for GNU compiler. DECstation (OSF/1) version. + Copyright (C) 1992, 1996, 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. */ + +#define DEC_OSF1 + +#define CPP_PREDEFINES "\ +-D__ANSI_COMPAT -DMIPSEL -DR3000 -DSYSTYPE_BSD -D_SYSTYPE_BSD \ +-Dbsd4_2 -Dhost_mips -Dmips -Dosf -Dunix \ +-Asystem(unix) -Asystem(xpg4) -Acpu(mips) -Amachine(mips)" + +#define LINK_SPEC "\ +%{G*} %{EB} %{EL} %{mips1} %{mips2} %{mips3} \ +%{bestGnum} %{shared} %{non_shared} \ +%{call_shared} %{no_archive} %{exact_version} \ +%{!shared: %{!non_shared: %{!call_shared: -non_shared}}}" + +#include "mips/ultrix.h" +#include "mips/mips.h" + +/* Specify size_t and wchar_t types. */ +#undef SIZE_TYPE +#undef WCHAR_TYPE +#undef WCHAR_TYPE_SIZE + +#define SIZE_TYPE "long unsigned int" +#define WCHAR_TYPE "short unsigned int" +#define WCHAR_TYPE_SIZE SHORT_TYPE_SIZE + +#undef SUBTARGET_CPP_SIZE_SPEC +#define SUBTARGET_CPP_SIZE_SPEC "\ +%{mlong64:-D__PTRDIFF_TYPE__=long\\ int} \ +%{!mlong64:-D__PTRDIFF_TYPE__=int}" + +/* turn off collect2 COFF support, since ldfcn now has elf declaration */ +#undef OBJECT_FORMAT_COFF + +#undef MACHINE_TYPE +#define MACHINE_TYPE "DECstation running DEC OSF/1" diff --git a/contrib/gcc/config/mips/ecoff.h b/contrib/gcc/config/mips/ecoff.h new file mode 100644 index 000000000000..dcc00a4b4b84 --- /dev/null +++ b/contrib/gcc/config/mips/ecoff.h @@ -0,0 +1,35 @@ +/* Definitions of target machine for GNU compiler. MIPS version with + GOFAST floating point library. + Copyright (C) 1994, 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. */ + +/* US Software GOFAST library support. */ +#define INIT_SUBTARGET_OPTABS INIT_GOFAST_OPTABS + +#include "mips/mips.h" + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dmips -DMIPSEB -DR3000 -D_mips -D_MIPSEB -D_R3000" + +/* Use memcpy, et. al., rather than bcopy. */ +#define TARGET_MEM_FUNCTIONS + +/* Don't assume anything about startfiles. The linker script will load the + appropriate startfiles. */ +#define STARTFILE_SPEC "" diff --git a/contrib/gcc/config/mips/ecoffl.h b/contrib/gcc/config/mips/ecoffl.h new file mode 100644 index 000000000000..9fe90e848480 --- /dev/null +++ b/contrib/gcc/config/mips/ecoffl.h @@ -0,0 +1,30 @@ +/* Definitions of target machine for GNU compiler. Little endian MIPS + version with GOFAST floating point library. + Copyright (C) 1994, 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. */ + +/* This is a little endian version of ecoff.h. */ + +#define TARGET_ENDIAN_DEFAULT 0 + +#include "gofast.h" +#include "mips/ecoff.h" + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dmips -DMIPSEL -DR3000 -D_mips -D_MIPSEL -D_R3000" diff --git a/contrib/gcc/config/mips/elf.h b/contrib/gcc/config/mips/elf.h new file mode 100644 index 000000000000..53af04690760 --- /dev/null +++ b/contrib/gcc/config/mips/elf.h @@ -0,0 +1,329 @@ +/* Definitions of target machine for GNU compiler. MIPS R3000 version with + GOFAST floating point library. + Copyright (C) 1994, 1997, 1999 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. */ + +/* Use ELF. */ +#define OBJECT_FORMAT_ELF + +/* Until we figure out what MIPS ELF targets normally use, just do + stabs in ELF. */ +#ifndef PREFERRED_DEBUGGING_TYPE +#define PREFERRED_DEBUGGING_TYPE DBX_DEBUG +#endif + +/* Mostly like ECOFF. */ +#include "gofast.h" +#include "mips/ecoff.h" + +/* We need to use .esize and .etype instead of .size and .type to + avoid conflicting with ELF directives. */ +#undef PUT_SDB_SIZE +#define PUT_SDB_SIZE(a) \ +do { \ + extern FILE *asm_out_text_file; \ + fprintf (asm_out_text_file, "\t.esize\t%d;", (a)); \ +} while (0) + +#undef PUT_SDB_TYPE +#define PUT_SDB_TYPE(a) \ +do { \ + extern FILE *asm_out_text_file; \ + fprintf (asm_out_text_file, "\t.etype\t0x%x;", (a)); \ +} while (0) + +/* Biggest alignment supported by the object file format of this + machine. Use this macro to limit the alignment which can be + specified using the `__attribute__ ((aligned (N)))' construct. If + not defined, the default value is `BIGGEST_ALIGNMENT'. */ + +#define MAX_OFILE_ALIGNMENT (32768*8) + +/* 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. */ + +#define ASM_OUTPUT_SECTION_NAME(F, DECL, NAME, RELOC) \ +do { \ + extern FILE *asm_out_text_file; \ + if ((DECL) && TREE_CODE (DECL) == FUNCTION_DECL) \ + fprintf (asm_out_text_file, "\t.section %s,\"ax\",@progbits\n", (NAME)); \ + else if ((DECL) && DECL_READONLY_SECTION (DECL, RELOC)) \ + fprintf (F, "\t.section %s,\"a\",@progbits\n", (NAME)); \ + else \ + fprintf (F, "\t.section %s,\"aw\",@progbits\n", (NAME)); \ +} while (0) + +/* The following macro defines the format used to output the second + operand of the .type assembler directive. Different svr4 assemblers + expect various different forms for this operand. The one given here + is just a default. You may need to override it in your machine- + specific tm.h file (depending upon the particulars of your assembler). */ + +#define TYPE_OPERAND_FMT "@%s" + +/* Define the strings used for the special svr4 .type and .size directives. + These strings generally do not vary from one system running svr4 to + another, but if a given system (e.g. m88k running svr) needs to use + different pseudo-op names for these, they may be overridden in the + file which includes this one. */ + +#undef TYPE_ASM_OP +#undef SIZE_ASM_OP +#define TYPE_ASM_OP ".type" +#define SIZE_ASM_OP ".size" + +/* These macros generate the special .type and .size directives which + are used to set the corresponding fields of the linker symbol table + entries in an ELF object file under SVR4. These macros also output + the starting labels for the relevant functions/objects. */ + +/* Write the extra assembler code needed to declare an object properly. */ + +#undef ASM_DECLARE_OBJECT_NAME +#define ASM_DECLARE_OBJECT_NAME(FILE, NAME, DECL) \ + do { \ + fprintf (FILE, "\t%s\t ", TYPE_ASM_OP); \ + assemble_name (FILE, NAME); \ + putc (',', FILE); \ + fprintf (FILE, TYPE_OPERAND_FMT, "object"); \ + putc ('\n', FILE); \ + size_directive_output = 0; \ + if (!flag_inhibit_size_directive && DECL_SIZE (DECL)) \ + { \ + size_directive_output = 1; \ + fprintf (FILE, "\t%s\t ", SIZE_ASM_OP); \ + assemble_name (FILE, NAME); \ + fprintf (FILE, ",%d\n", int_size_in_bytes (TREE_TYPE (DECL))); \ + } \ + mips_declare_object (FILE, NAME, "", ":\n", 0); \ + } while (0) + +/* Output the size directive for a decl in rest_of_decl_compilation + in the case where we did not do so before the initializer. + Once we find the error_mark_node, we know that the value of + size_directive_output was set + by ASM_DECLARE_OBJECT_NAME when it was run for the same decl. */ + +#undef ASM_FINISH_DECLARE_OBJECT +#define ASM_FINISH_DECLARE_OBJECT(FILE, DECL, TOP_LEVEL, AT_END) \ +do { \ + char *name = XSTR (XEXP (DECL_RTL (DECL), 0), 0); \ + if (!flag_inhibit_size_directive && DECL_SIZE (DECL) \ + && ! AT_END && TOP_LEVEL \ + && DECL_INITIAL (DECL) == error_mark_node \ + && !size_directive_output) \ + { \ + size_directive_output = 1; \ + fprintf (FILE, "\t%s\t ", SIZE_ASM_OP); \ + assemble_name (FILE, name); \ + fprintf (FILE, ",%d\n", int_size_in_bytes (TREE_TYPE (DECL))); \ + } \ + } while (0) + +#define ASM_OUTPUT_DEF(FILE,LABEL1,LABEL2) \ + do { fputc ( '\t', FILE); \ + assemble_name (FILE, LABEL1); \ + fputs ( " = ", FILE); \ + assemble_name (FILE, LABEL2); \ + fputc ( '\n', FILE); \ + } while (0) + +/* Note about .weak vs. .weakext + The mips native assemblers support .weakext, but not .weak. + mips-elf gas supports .weak, but not .weakext. + mips-elf gas has been changed to support both .weak and .weakext, + but until that support is generally available, the 'if' below + should serve. */ + +#define ASM_WEAKEN_LABEL(FILE,NAME) ASM_OUTPUT_WEAK_ALIAS(FILE,NAME,0) +#define ASM_OUTPUT_WEAK_ALIAS(FILE,NAME,VALUE) \ + do { \ + if (TARGET_GAS) \ + fputs ("\t.weak\t", FILE); \ + else \ + fputs ("\t.weakext\t", FILE); \ + assemble_name (FILE, NAME); \ + if (VALUE) \ + { \ + fputc (' ', FILE); \ + assemble_name (FILE, VALUE); \ + } \ + fputc ('\n', FILE); \ + } while (0) + +#define MAKE_DECL_ONE_ONLY(DECL) (DECL_WEAK (DECL) = 1) +#undef UNIQUE_SECTION_P +#define UNIQUE_SECTION_P(DECL) (DECL_ONE_ONLY (DECL)) +#define UNIQUE_SECTION(DECL,RELOC) \ +do { \ + int len, size, sec; \ + char *name, *string, *prefix; \ + static char *prefixes[4][2] = { \ + { ".text.", ".gnu.linkonce.t." }, \ + { ".rodata.", ".gnu.linkonce.r." }, \ + { ".data.", ".gnu.linkonce.d." }, \ + { ".sdata.", ".gnu.linkonce.s." } \ + }; \ + \ + name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (DECL)); \ + size = int_size_in_bytes (TREE_TYPE (decl)); \ + \ + /* Determine the base section we are interested in: \ + 0=text, 1=rodata, 2=data, 3=sdata. */ \ + if (TREE_CODE (DECL) == FUNCTION_DECL) \ + sec = 0; \ + else if ((TARGET_EMBEDDED_PIC || TARGET_MIPS16) \ + && TREE_CODE (decl) == STRING_CST \ + && !flag_writable_strings) \ + { \ + /* For embedded position independent code, put constant strings \ + in the text section, because the data section is limited to \ + 64K in size. For mips16 code, put strings in the text \ + section so that a PC relative load instruction can be used to \ + get their address. */ \ + sec = 0; \ + } \ + else if (TARGET_EMBEDDED_DATA) \ + { \ + /* For embedded applications, always put an object in read-only data \ + if possible, in order to reduce RAM usage. */ \ + \ + if (DECL_READONLY_SECTION (DECL, RELOC)) \ + sec = 1; \ + else if (size > 0 && size <= mips_section_threshold) \ + sec = 3; \ + else \ + sec = 2; \ + } \ + else \ + { \ + /* For hosted applications, always put an object in small data if \ + possible, as this gives the best performance. */ \ + \ + if (size > 0 && size <= mips_section_threshold) \ + sec = 3; \ + else if (DECL_READONLY_SECTION (DECL, RELOC)) \ + sec = 1; \ + else \ + sec = 2; \ + } \ + \ + prefix = prefixes[sec][DECL_ONE_ONLY (DECL)]; \ + len = strlen (name) + strlen (prefix); \ + string = alloca (len + 1); \ + sprintf (string, "%s%s", prefix, name); \ + \ + DECL_SECTION_NAME (DECL) = build_string (len, string); \ +} while (0) + +/* Support the ctors/dtors and other sections. */ + +/* 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.) */ + +#define CTORS_SECTION_ASM_OP "\t.section\t.ctors,\"aw\"" +#define DTORS_SECTION_ASM_OP "\t.section\t.dtors,\"aw\"" + +/* A list of other sections which the compiler might be "in" at any + given time. */ +#undef EXTRA_SECTIONS +#define EXTRA_SECTIONS in_sdata, in_rdata, in_ctors, in_dtors + +#define INVOKE__main +#define NAME__MAIN "__gccmain" +#define SYMBOL__MAIN __gccmain + +#undef EXTRA_SECTION_FUNCTIONS +#define EXTRA_SECTION_FUNCTIONS \ + SECTION_FUNCTION_TEMPLATE(sdata_section, in_sdata, SDATA_SECTION_ASM_OP) \ + SECTION_FUNCTION_TEMPLATE(rdata_section, in_rdata, RDATA_SECTION_ASM_OP) \ + SECTION_FUNCTION_TEMPLATE(ctors_section, in_ctors, CTORS_SECTION_ASM_OP) \ + SECTION_FUNCTION_TEMPLATE(dtors_section, in_dtors, DTORS_SECTION_ASM_OP) + +#define SECTION_FUNCTION_TEMPLATE(FN, ENUM, OP) \ +void FN () \ +{ \ + if (in_section != ENUM) \ + { \ + fprintf (asm_out_file, "%s\n", OP); \ + in_section = ENUM; \ + } \ +} + + +/* A C statement (sans semicolon) to output an element in the table of + global constructors. */ +#define ASM_OUTPUT_CONSTRUCTOR(FILE,NAME) \ + do { \ + ctors_section (); \ + fprintf (FILE, "\t%s\t", TARGET_LONG64 ? ".dword" : ".word"); \ + assemble_name (FILE, NAME); \ + fprintf (FILE, "\n"); \ + } while (0) + + +/* A C statement (sans semicolon) to output an element in the table of + global destructors. */ +#define ASM_OUTPUT_DESTRUCTOR(FILE,NAME) \ + do { \ + dtors_section (); \ + fprintf (FILE, "\t%s\t", TARGET_LONG64 ? ".dword" : ".word"); \ + assemble_name (FILE, NAME); \ + fprintf (FILE, "\n"); \ + } while (0) + +#define CTOR_LIST_BEGIN \ +asm (CTORS_SECTION_ASM_OP); \ +func_ptr __CTOR_LIST__[1] = { (func_ptr) (-1) } + +#define CTOR_LIST_END \ +asm (CTORS_SECTION_ASM_OP); \ +func_ptr __CTOR_END__[1] = { (func_ptr) 0 }; + +#define DTOR_LIST_BEGIN \ +asm (DTORS_SECTION_ASM_OP); \ +func_ptr __DTOR_LIST__[1] = { (func_ptr) (-1) } + +#define DTOR_LIST_END \ +asm (DTORS_SECTION_ASM_OP); \ +func_ptr __DTOR_END__[1] = { (func_ptr) 0 }; + +/* Don't set the target flags, this is done by the linker script */ +#undef LIB_SPEC +#define LIB_SPEC "" + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC "crtbegin%O%s crt0%O%s" + +#undef ENDFILE_SPEC +#define ENDFILE_SPEC "crtend%O%s" diff --git a/contrib/gcc/config/mips/elf64.h b/contrib/gcc/config/mips/elf64.h new file mode 100644 index 000000000000..91c83103b975 --- /dev/null +++ b/contrib/gcc/config/mips/elf64.h @@ -0,0 +1,355 @@ +/* Definitions of target machine for GNU compiler. MIPS R4000 version with + GOFAST floating point library. + Copyright (C) 1994, 1995, 1996, 1997, 1999 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 OBJECT_FORMAT_ELF + +/* Default to -mips3. */ +#define TARGET_DEFAULT MASK_FLOAT64|MASK_64BIT +#define MIPS_ISA_DEFAULT 3 + +/* Until we figure out what MIPS ELF targets normally use, just do + stabs in ELF. */ +#ifndef PREFERRED_DEBUGGING_TYPE +#define PREFERRED_DEBUGGING_TYPE DBX_DEBUG +#endif + +/* US Software GOFAST library support. */ +#include "gofast.h" +#define INIT_SUBTARGET_OPTABS INIT_GOFAST_OPTABS + +#include "mips/mips.h" + +/* This must be done after mips.h, because mips.h defines + TARGET_ENDIAN_DEFAULT. */ +#undef MULTILIB_DEFAULTS +#if TARGET_ENDIAN_DEFAULT == 0 +#define MULTILIB_DEFAULTS { "EL", "mips3" } +#else +#define MULTILIB_DEFAULTS { "EB", "mips3" } +#endif + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dmips -DMIPSEB -DR4000 -D_mips -D_MIPSEB -D_R4000" + +/* I would rather put this in CPP_PREDEFINES, but the gcc driver + doesn't handle -U options in CPP_PREDEFINES. */ +#undef SUBTARGET_CPP_SPEC +#define SUBTARGET_CPP_SPEC "\ +%{!mips1:%{!mips2:-U__mips -D__mips=3 -D__mips64}}" + +/* Use memcpy, et. al., rather than bcopy. */ +#define TARGET_MEM_FUNCTIONS + +/* Biggest alignment supported by the object file format of this + machine. Use this macro to limit the alignment which can be + specified using the `__attribute__ ((aligned (N)))' construct. If + not defined, the default value is `BIGGEST_ALIGNMENT'. */ + +#define MAX_OFILE_ALIGNMENT (32768*8) + +/* We need to use .esize and .etype instead of .size and .type to + avoid conflicting with ELF directives. */ +#undef PUT_SDB_SIZE +#define PUT_SDB_SIZE(a) \ +do { \ + extern FILE *asm_out_text_file; \ + fprintf (asm_out_text_file, "\t.esize\t%d;", (a)); \ +} while (0) + +#undef PUT_SDB_TYPE +#define PUT_SDB_TYPE(a) \ +do { \ + extern FILE *asm_out_text_file; \ + fprintf (asm_out_text_file, "\t.etype\t0x%x;", (a)); \ +} while (0) + +/* 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. */ + +#define ASM_OUTPUT_SECTION_NAME(F, DECL, NAME, RELOC) \ +do { \ + extern FILE *asm_out_text_file; \ + if ((DECL) && TREE_CODE (DECL) == FUNCTION_DECL) \ + fprintf (asm_out_text_file, "\t.section %s,\"ax\",@progbits\n", (NAME)); \ + else if ((DECL) && DECL_READONLY_SECTION (DECL, RELOC)) \ + fprintf (F, "\t.section %s,\"a\",@progbits\n", (NAME)); \ + else \ + fprintf (F, "\t.section %s,\"aw\",@progbits\n", (NAME)); \ +} while (0) + +/* The following macro defines the format used to output the second + operand of the .type assembler directive. Different svr4 assemblers + expect various different forms for this operand. The one given here + is just a default. You may need to override it in your machine- + specific tm.h file (depending upon the particulars of your assembler). */ + +#define TYPE_OPERAND_FMT "@%s" + +/* Define the strings used for the special svr4 .type and .size directives. + These strings generally do not vary from one system running svr4 to + another, but if a given system (e.g. m88k running svr) needs to use + different pseudo-op names for these, they may be overridden in the + file which includes this one. */ + +#undef TYPE_ASM_OP +#undef SIZE_ASM_OP +#define TYPE_ASM_OP ".type" +#define SIZE_ASM_OP ".size" + +/* These macros generate the special .type and .size directives which + are used to set the corresponding fields of the linker symbol table + entries in an ELF object file under SVR4. These macros also output + the starting labels for the relevant functions/objects. */ + +/* Write the extra assembler code needed to declare an object properly. */ + +#undef ASM_DECLARE_OBJECT_NAME +#define ASM_DECLARE_OBJECT_NAME(FILE, NAME, DECL) \ + do { \ + fprintf (FILE, "\t%s\t ", TYPE_ASM_OP); \ + assemble_name (FILE, NAME); \ + putc (',', FILE); \ + fprintf (FILE, TYPE_OPERAND_FMT, "object"); \ + putc ('\n', FILE); \ + size_directive_output = 0; \ + if (!flag_inhibit_size_directive && DECL_SIZE (DECL)) \ + { \ + size_directive_output = 1; \ + fprintf (FILE, "\t%s\t ", SIZE_ASM_OP); \ + assemble_name (FILE, NAME); \ + fprintf (FILE, ",%d\n", int_size_in_bytes (TREE_TYPE (DECL))); \ + } \ + mips_declare_object (FILE, NAME, "", ":\n", 0); \ + } while (0) + +/* Output the size directive for a decl in rest_of_decl_compilation + in the case where we did not do so before the initializer. + Once we find the error_mark_node, we know that the value of + size_directive_output was set + by ASM_DECLARE_OBJECT_NAME when it was run for the same decl. */ + +#undef ASM_FINISH_DECLARE_OBJECT +#define ASM_FINISH_DECLARE_OBJECT(FILE, DECL, TOP_LEVEL, AT_END) \ +do { \ + char *name = XSTR (XEXP (DECL_RTL (DECL), 0), 0); \ + if (!flag_inhibit_size_directive && DECL_SIZE (DECL) \ + && ! AT_END && TOP_LEVEL \ + && DECL_INITIAL (DECL) == error_mark_node \ + && !size_directive_output) \ + { \ + size_directive_output = 1; \ + fprintf (FILE, "\t%s\t ", SIZE_ASM_OP); \ + assemble_name (FILE, name); \ + fprintf (FILE, ",%d\n", int_size_in_bytes (TREE_TYPE (DECL))); \ + } \ + } while (0) + +#define ASM_OUTPUT_DEF(FILE,LABEL1,LABEL2) \ + do { fputc ( '\t', FILE); \ + assemble_name (FILE, LABEL1); \ + fputs ( " = ", FILE); \ + assemble_name (FILE, LABEL2); \ + fputc ( '\n', FILE); \ + } while (0) + +/* Note about .weak vs. .weakext + The mips native assemblers support .weakext, but not .weak. + mips-elf gas supports .weak, but not .weakext. + mips-elf gas has been changed to support both .weak and .weakext, + but until that support is generally available, the 'if' below + should serve. */ + +#define ASM_WEAKEN_LABEL(FILE,NAME) ASM_OUTPUT_WEAK_ALIAS(FILE,NAME,0) +#define ASM_OUTPUT_WEAK_ALIAS(FILE,NAME,VALUE) \ + do { \ + if (TARGET_GAS) \ + fputs ("\t.weak\t", FILE); \ + else \ + fputs ("\t.weakext\t", FILE); \ + assemble_name (FILE, NAME); \ + if (VALUE) \ + { \ + fputc (' ', FILE); \ + assemble_name (FILE, VALUE); \ + } \ + fputc ('\n', FILE); \ + } while (0) + +#define MAKE_DECL_ONE_ONLY(DECL) (DECL_WEAK (DECL) = 1) +#undef UNIQUE_SECTION_P +#define UNIQUE_SECTION_P(DECL) (DECL_ONE_ONLY (DECL)) +#define UNIQUE_SECTION(DECL,RELOC) \ +do { \ + int len, size, sec; \ + char *name, *string, *prefix; \ + static char *prefixes[4][2] = { \ + { ".text.", ".gnu.linkonce.t." }, \ + { ".rodata.", ".gnu.linkonce.r." }, \ + { ".data.", ".gnu.linkonce.d." }, \ + { ".sdata.", ".gnu.linkonce.s." } \ + }; \ + \ + name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (DECL)); \ + size = int_size_in_bytes (TREE_TYPE (decl)); \ + \ + /* Determine the base section we are interested in: \ + 0=text, 1=rodata, 2=data, 3=sdata. */ \ + if (TREE_CODE (DECL) == FUNCTION_DECL) \ + sec = 0; \ + else if ((TARGET_EMBEDDED_PIC || TARGET_MIPS16) \ + && TREE_CODE (decl) == STRING_CST \ + && !flag_writable_strings) \ + { \ + /* For embedded position independent code, put constant strings \ + in the text section, because the data section is limited to \ + 64K in size. For mips16 code, put strings in the text \ + section so that a PC relative load instruction can be used to \ + get their address. */ \ + sec = 0; \ + } \ + else if (TARGET_EMBEDDED_DATA) \ + { \ + /* For embedded applications, always put an object in read-only data \ + if possible, in order to reduce RAM usage. */ \ + \ + if (DECL_READONLY_SECTION (DECL, RELOC)) \ + sec = 1; \ + else if (size > 0 && size <= mips_section_threshold) \ + sec = 3; \ + else \ + sec = 2; \ + } \ + else \ + { \ + /* For hosted applications, always put an object in small data if \ + possible, as this gives the best performance. */ \ + \ + if (size > 0 && size <= mips_section_threshold) \ + sec = 3; \ + else if (DECL_READONLY_SECTION (DECL, RELOC)) \ + sec = 1; \ + else \ + sec = 2; \ + } \ + \ + prefix = prefixes[sec][DECL_ONE_ONLY (DECL)]; \ + len = strlen (name) + strlen (prefix); \ + string = alloca (len + 1); \ + sprintf (string, "%s%s", prefix, name); \ + \ + DECL_SECTION_NAME (DECL) = build_string (len, string); \ +} while (0) + +/* Support the ctors/dtors and other sections. */ + +/* 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.) */ + +#define CTORS_SECTION_ASM_OP "\t.section\t.ctors,\"aw\"" +#define DTORS_SECTION_ASM_OP "\t.section\t.dtors,\"aw\"" + +/* A list of other sections which the compiler might be "in" at any + given time. */ +#undef EXTRA_SECTIONS +#define EXTRA_SECTIONS in_sdata, in_rdata, in_ctors, in_dtors + +#define INVOKE__main +#define NAME__MAIN "__gccmain" +#define SYMBOL__MAIN __gccmain + +#undef EXTRA_SECTION_FUNCTIONS +#define EXTRA_SECTION_FUNCTIONS \ + SECTION_FUNCTION_TEMPLATE(sdata_section, in_sdata, SDATA_SECTION_ASM_OP) \ + SECTION_FUNCTION_TEMPLATE(rdata_section, in_rdata, RDATA_SECTION_ASM_OP) \ + SECTION_FUNCTION_TEMPLATE(ctors_section, in_ctors, CTORS_SECTION_ASM_OP) \ + SECTION_FUNCTION_TEMPLATE(dtors_section, in_dtors, DTORS_SECTION_ASM_OP) + +#define SECTION_FUNCTION_TEMPLATE(FN, ENUM, OP) \ +void FN () \ +{ \ + if (in_section != ENUM) \ + { \ + fprintf (asm_out_file, "%s\n", OP); \ + in_section = ENUM; \ + } \ +} + + +/* A C statement (sans semicolon) to output an element in the table of + global constructors. */ +#define ASM_OUTPUT_CONSTRUCTOR(FILE,NAME) \ + do { \ + ctors_section (); \ + fprintf (FILE, "\t%s\t", TARGET_LONG64 ? ".dword" : ".word"); \ + assemble_name (FILE, NAME); \ + fprintf (FILE, "\n"); \ + } while (0) + + +/* A C statement (sans semicolon) to output an element in the table of + global destructors. */ +#define ASM_OUTPUT_DESTRUCTOR(FILE,NAME) \ + do { \ + dtors_section (); \ + fprintf (FILE, "\t%s\t", TARGET_LONG64 ? ".dword" : ".word"); \ + assemble_name (FILE, NAME); \ + fprintf (FILE, "\n"); \ + } while (0) + +#define CTOR_LIST_BEGIN \ +asm (CTORS_SECTION_ASM_OP); \ +func_ptr __CTOR_LIST__[1] = { (func_ptr) (-1) } + +#define CTOR_LIST_END \ +asm (CTORS_SECTION_ASM_OP); \ +func_ptr __CTOR_END__[1] = { (func_ptr) 0 }; + +#define DTOR_LIST_BEGIN \ +asm (DTORS_SECTION_ASM_OP); \ +func_ptr __DTOR_LIST__[1] = { (func_ptr) (-1) } + +#define DTOR_LIST_END \ +asm (DTORS_SECTION_ASM_OP); \ +func_ptr __DTOR_END__[1] = { (func_ptr) 0 }; + +/* Don't set the target flags, this is done by the linker script */ +#undef LIB_SPEC +#define LIB_SPEC "" + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC "crtbegin%O%s crt0%O%s" + +#undef ENDFILE_SPEC +#define ENDFILE_SPEC "crtend%O%s" diff --git a/contrib/gcc/config/mips/elfl.h b/contrib/gcc/config/mips/elfl.h new file mode 100644 index 000000000000..7575e3da5d8d --- /dev/null +++ b/contrib/gcc/config/mips/elfl.h @@ -0,0 +1,29 @@ +/* Definitions of target machine for GNU compiler. Little endian MIPS + R3000 version with GOFAST floating point library. + 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. */ + +/* This is a little endian version of elf.h. */ + +#define TARGET_ENDIAN_DEFAULT 0 + +#include "mips/elf.h" + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dmips -DMIPSEL -DR3000 -D_mips -D_MIPSEL -D_R3000" diff --git a/contrib/gcc/config/mips/elfl64.h b/contrib/gcc/config/mips/elfl64.h new file mode 100644 index 000000000000..5e18c0932086 --- /dev/null +++ b/contrib/gcc/config/mips/elfl64.h @@ -0,0 +1,29 @@ +/* Definitions of target machine for GNU compiler. Little endian MIPS + R4000 version with GOFAST floating point library. + 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. */ + +/* This is a little endian version of elf64.h. */ + +#define TARGET_ENDIAN_DEFAULT 0 + +#include "mips/elf64.h" + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dmips -DMIPSEL -DR4000 -D_mips -D_MIPSEL -D_R4000" diff --git a/contrib/gcc/config/mips/elflorion.h b/contrib/gcc/config/mips/elflorion.h new file mode 100644 index 000000000000..4b7f111873ff --- /dev/null +++ b/contrib/gcc/config/mips/elflorion.h @@ -0,0 +1,24 @@ +/* Definitions of target machine for GNU compiler. MIPS ORION version with + GOFAST floating point library. + 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 MIPS_CPU_STRING_DEFAULT "orion" + +#include "mips/elfl64.h" diff --git a/contrib/gcc/config/mips/elforion.h b/contrib/gcc/config/mips/elforion.h new file mode 100644 index 000000000000..aa1a058b50d3 --- /dev/null +++ b/contrib/gcc/config/mips/elforion.h @@ -0,0 +1,22 @@ +/* Definitions of target machine for GNU compiler. MIPS ORION version with + GOFAST floating point library. + Copyright (C) 1994, 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. */ + +#define MIPS_CPU_STRING_DEFAULT "orion" diff --git a/contrib/gcc/config/mips/gnu.h b/contrib/gcc/config/mips/gnu.h new file mode 100644 index 000000000000..734548b52115 --- /dev/null +++ b/contrib/gcc/config/mips/gnu.h @@ -0,0 +1,130 @@ +/* Definitions of target machine for GNU compiler. MIPS GNU Hurd version. + Copyright (C) 1995, 1996, 1999 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 TARGET_DEFAULT MASK_GAS + +#include <mips/mips.h> + +#undef SWITCH_TAKES_ARG +#undef ASM_FILE_END +#undef ASM_OUTPUT_IDENT +#undef ASM_OUTPUT_SOURCE_LINE +#undef READONLY_DATA_SECTION +#undef SELECT_SECTION +#undef ASM_DECLARE_FUNCTION_NAME +#undef ASM_DECLARE_OBJECT_NAME +/* #undef PREFERRED_DEBUGGING_TYPE */ + +#include <svr4.h> + +#undef MD_EXEC_PREFIX +#undef MD_STARTFILE_PREFIX +#undef TARGET_VERSION +#define TARGET_VERSION fprintf (stderr, " (MIPS GNU/ELF)"); + +/* Output at beginning of assembler file. */ +/* The .file command should always begin the output. */ +#undef ASM_FILE_START +#define ASM_FILE_START(FILE) \ + do { \ + mips_asm_file_start (FILE); \ + fprintf (FILE, "\t.version\t\"01.01\"\n"); \ + } while (0) + +#undef ASM_FILE_END +#define ASM_FILE_END(FILE) \ + do { \ + mips_asm_file_end(FILE); \ + if (!flag_no_ident) \ + fprintf ((FILE), "\t%s\t\"GCC: (GNU) %s\"\n", \ + IDENT_ASM_OP, version_string); \ + } while (0) + +#undef ASM_OUTPUT_SOURCE_LINE +#define ASM_OUTPUT_SOURCE_LINE(FILE, LINE) \ + do { \ + ++sym_lineno; \ + fprintf ((FILE), ".LM%d:\n\t%s %d,0,%d,.LM%d\n", \ + sym_lineno, ASM_STABN_OP, N_SLINE, (LINE), sym_lineno); \ + } while (0) + +#undef ASM_DECLARE_FUNCTION_NAME +#define ASM_DECLARE_FUNCTION_NAME(STREAM, NAME, DECL) \ + do { \ + extern FILE *asm_out_text_file; \ + \ + if (TARGET_GP_OPT) \ + STREAM = asm_out_text_file; \ + fprintf (STREAM, "\t%s\t ", TYPE_ASM_OP); \ + assemble_name (STREAM, NAME); \ + putc (',', STREAM); \ + fprintf (STREAM, TYPE_OPERAND_FMT, "function"); \ + putc ('\n', STREAM); \ + ASM_DECLARE_RESULT (STREAM, DECL_RESULT (DECL)); \ + HALF_PIC_DECLARE (NAME); \ + } while (0) + +/* Switch Recognition by gcc.c. Add -G xx support */ +#undef SWITCH_TAKES_ARG +#define SWITCH_TAKES_ARG(CHAR) \ + (DEFAULT_SWITCH_TAKES_ARG(CHAR) || (CHAR) == 'G') + +#undef DEFAULT_PCC_STRUCT_RETURN +#define DEFAULT_PCC_STRUCT_RETURN 1 + +#undef DBX_REGISTER_NUMBER +#define DBX_REGISTER_NUMBER(REGNO) mips_dbx_regno[ (REGNO) ] + +#define MIPS_GNU + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dmips -Acpu(mips) -Amachine(mips) \ +-Dunix -Asystem(unix) -DMACH -Asystem(mach) -D__GNU__ -Asystem(gnu) \ +-DMIPSEB -DR3000 -D_MIPSEB -D_R3000 \ +-D_MIPS_SZINT=32 -D_MIPS_SZLONG=32 -D_MIPS_SZPTR=32" + +#undef LINK_SPEC +#define LINK_SPEC "\ +%{G*} %{EB} %{EL} %{mips1} %{mips2} %{mips3} \ +%{bestGnum} %{shared} %{non_shared} \ +%{call_shared} %{no_archive} %{exact_version} \ +%{!shared: %{!non_shared: %{!call_shared: -non_shared}}} \ +-systype /gnu/ " + +#undef LIB_SPEC +#define LIB_SPEC "%{p:-lprof1} %{pg:-lprof1} -lc crtn.o%s" + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC "%{pg:gcrt0.o%s} %{!pg:%{p:gcrt0.o%s} %{!p:crt0.o%s}} %{static:-static}" + +#undef MACHINE_TYPE +#define MACHINE_TYPE "GNU MIPS/ELF" + +#undef YES_UNDERSCORE + +#undef SDB_DEBUGGING_INFO +#undef DBX_DEBUGGING_INFO +#undef MIPS_DEBUGGING_INFO +#define DWARF_DEBUGGING_INFO + +#define NO_MIPS_SELECT_SECTION + +/* Get machine-independent configuration parameters for the GNU system. */ +#include <gnu.h> diff --git a/contrib/gcc/config/mips/iris3.h b/contrib/gcc/config/mips/iris3.h new file mode 100644 index 000000000000..1f690ffa466b --- /dev/null +++ b/contrib/gcc/config/mips/iris3.h @@ -0,0 +1,72 @@ +/* Definitions of target machine for GNU compiler. Iris version. + Copyright (C) 1991, 1993, 1995, 1996, 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. */ + +#define SGI_TARGET 1 /* inform other mips files this is SGI */ + +/* Names to predefine in the preprocessor for this target machine. */ + +#define CPP_PREDEFINES "\ +-Dunix -Dmips -Dsgi -DSVR3 -Dhost_mips -DMIPSEB -DSYSTYPE_SYSV \ +-Asystem(unix) -Asystem(svr3) -Acpu(mips) -Amachine(mips)" + +#define STARTFILE_SPEC "%{pg:gcrt1.o%s}%{!pg:%{p:mcrt1.o%s}%{!p:crt1.o%s}}" + +#define SUBTARGET_CPP_SPEC "\ +%{!ansi:-D__EXTENSIONS__} -D_MIPSEB -D_SYSTYPE_SYSV" + +#define LIB_SPEC \ + "%{!p:%{!pg:%{!static:%{!g*:-lc_s}} -lc}}%{p:-lc_p}%{pg:-lc_p} crtn.o%s" + +#define MACHINE_TYPE "Silicon Graphics Mips" + +/* Always use 1 for .file number. I [meissner@osf.org] wonder why + IRIS needs this. */ + +#define SET_FILE_NUMBER() num_source_filenames = 1 + +/* Put out a label after a .loc. I [meissner@osf.org] wonder why + IRIS needs this. */ + +#define LABEL_AFTER_LOC(STREAM) fprintf (STREAM, "LM%d:\n", ++sym_lineno) + +#define STACK_ARGS_ADJUST(SIZE) \ +{ \ + SIZE.constant += 4; \ + if (SIZE.constant < 32) \ + SIZE.constant = 32; \ +} + +/* Do not allow `$' in identifiers. */ + +#define DOLLARS_IN_IDENTIFIERS 0 + +/* Tell G++ not to create constructors or destructors with $'s in them. */ + +#define NO_DOLLAR_IN_LABEL 1 + +/* Specify wchar_t type. */ +#define WCHAR_TYPE "unsigned char" +#define WCHAR_TYPE_SIZE BITS_PER_UNIT + +/* Generate calls to memcpy, etc., not bcopy, etc. */ +#define TARGET_MEM_FUNCTIONS + +/* Plain char is unsigned in the SGI compiler. */ +#define DEFAULT_SIGNED_CHAR 0 diff --git a/contrib/gcc/config/mips/iris4.h b/contrib/gcc/config/mips/iris4.h new file mode 100644 index 000000000000..7ca0459c6254 --- /dev/null +++ b/contrib/gcc/config/mips/iris4.h @@ -0,0 +1,48 @@ +/* Definitions of target machine for GNU compiler. Iris version 4. + Copyright (C) 1991, 1993 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. */ + +/* Use atexit for static constructors/destructors, instead of defining + our own exit function. */ +#define HAVE_ATEXIT + +/* Profiling is supported via libprof1.a not -lc_p as in Irix 3. */ +#undef STARTFILE_SPEC +#define STARTFILE_SPEC \ + "%{pg:gcrt1.o%s}%{!pg:%{p:mcrt1.o%s libprof1.a%s}%{!p:crt1.o%s}}" + +#undef LIB_SPEC +#define LIB_SPEC \ + "%{!p:%{!pg:%{!static:%{!g*:-lc_s}}}}%{p:libprof1.a%s}%{pg:libprof1.a%s} -lc crtn.o%s" + +/* Some assemblers have a bug that causes backslash escaped chars in .ascii + to be misassembled, so we just completely avoid it. */ +#undef ASM_OUTPUT_ASCII +#define ASM_OUTPUT_ASCII(FILE,PTR,LEN) \ +do { \ + unsigned char *s; \ + int i; \ + for (i = 0, s = (unsigned char *)(PTR); i < (LEN); s++, i++) \ + { \ + if ((i % 8) == 0) \ + fputs ("\n\t.byte\t", (FILE)); \ + fprintf ((FILE), "%s0x%x", (i%8?",":""), (unsigned)*s); \ + } \ + fputs ("\n", (FILE)); \ +} while (0) diff --git a/contrib/gcc/config/mips/iris4loser.h b/contrib/gcc/config/mips/iris4loser.h new file mode 100644 index 000000000000..426c822b68a5 --- /dev/null +++ b/contrib/gcc/config/mips/iris4loser.h @@ -0,0 +1,5 @@ +/* Like iris4.h, but always inhibits assembler optimization for MIPS as. + Use this via mips-sgi-iris4loser if you need it. */ + +#define SUBTARGET_MIPS_AS_ASM_SPEC "-O0 %{v}" +#define SUBTARGET_ASM_OPTIMIZING_SPEC "" diff --git a/contrib/gcc/config/mips/iris5.h b/contrib/gcc/config/mips/iris5.h new file mode 100644 index 000000000000..7910eef88d6d --- /dev/null +++ b/contrib/gcc/config/mips/iris5.h @@ -0,0 +1,164 @@ +/* Definitions of target machine for GNU compiler. Iris version 5. + Copyright (C) 1993, 1995, 1996, 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. */ + +#ifndef TARGET_DEFAULT +#define TARGET_DEFAULT MASK_ABICALLS +#endif +#define ABICALLS_ASM_OP ".option pic2" + +#include "mips/iris3.h" +#include "mips/mips.h" +#include "mips/iris4.h" + +/* Irix 5 doesn't use COFF, so disable special COFF handling in collect2.c. */ +#undef OBJECT_FORMAT_COFF + +/* ??? This is correct, but not very useful, because there is no file that + uses this macro. */ +/* ??? The best way to handle global constructors under ELF is to use .init + and .fini sections. Unfortunately, there is apparently no way to get + the Irix 5.x (x <= 2) assembler to create these sections. So we instead + use collect. The linker can create these sections via -init and -fini + options, but using this would require modifying how crtstuff works, and + I will leave that for another time (or someone else). */ +#define OBJECT_FORMAT_ELF +#define HAS_INIT_SECTION +#define LD_INIT_SWITCH "-init" +#define LD_FINI_SWITCH "-fini" + +/* Specify wchar_t types. */ +#undef WCHAR_TYPE +#undef WCHAR_TYPE_SIZE +#undef MAX_WCHAR_TYPE_SIZE + +#define WCHAR_TYPE "long int" +#define WCHAR_TYPE_SIZE LONG_TYPE_SIZE +#define MAX_WCHAR_TYPE_SIZE MAX_LONG_TYPE_SIZE + +#define WORD_SWITCH_TAKES_ARG(STR) \ + (DEFAULT_WORD_SWITCH_TAKES_ARG (STR) \ + || !strcmp (STR, "rpath")) + +#undef SUBTARGET_CC1_SPEC +#define SUBTARGET_CC1_SPEC "%{static: -mno-abicalls}" + +/* ??? _MIPS_SIM and _MIPS_SZPTR should eventually depend on options when + options for them exist. */ + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES \ + "-Dunix -Dmips -Dsgi -Dhost_mips -DMIPSEB -D_MIPSEB -DSYSTYPE_SVR4 \ + -D_SVR4_SOURCE -D_MODERN_C -D__DSO__ \ + -D_MIPS_SIM=_MIPS_SIM_ABI32 -D_MIPS_SZPTR=32 \ + -Asystem(unix) -Asystem(svr4) -Acpu(mips) -Amachine(sgi)" + +#undef SUBTARGET_CPP_SPEC +#define SUBTARGET_CPP_SPEC "\ +%{!ansi:-D__EXTENSIONS__ -D_SGI_SOURCE -D_LONGLONG} \ +%{!mfp64: -D_MIPS_FPSET=16}%{mfp64: -D_MIPS_FPSET=32} \ +%{mips1: -D_MIPS_ISA=_MIPS_ISA_MIPS1} \ +%{mips2: -D_MIPS_ISA=_MIPS_ISA_MIPS2} \ +%{mips3: -D_MIPS_ISA=_MIPS_ISA_MIPS3} \ +%{!mips1: %{!mips2: %{!mips3: -D_MIPS_ISA=_MIPS_ISA_MIPS1}}} \ +%{!mint64: -D_MIPS_SZINT=32}%{mint64: -D_MIPS_SZINT=64} \ +%{!mlong64: -D_MIPS_SZLONG=32}%{mlong64: -D_MIPS_SZLONG=64}" + +#undef LINK_SPEC +#define LINK_SPEC "\ +%{G*} %{EB} %{EL} %{mips1} %{mips2} %{mips3} \ +%{bestGnum} %{shared} %{non_shared} \ +%{call_shared} %{no_archive} %{exact_version} \ +%{static: -non_shared} \ +%{!static: \ + %{!shared:%{!non_shared:%{!call_shared: -call_shared -no_unresolved}}}} \ +%{rpath} \ +-_SYSTYPE_SVR4" + +/* We now support shared libraries. */ +#undef STARTFILE_SPEC +#define STARTFILE_SPEC "\ +%{!static: \ + %{!shared:%{pg:gcrt1.o%s}%{!pg:%{p:mcrt1.o%s libprof1.a%s}%{!p:crt1.o%s}}}} \ +%{static: \ + %{pg:gcrt1.o%s} \ + %{!pg:%{p:/usr/lib/nonshared/mcrt1.o%s libprof1.a%s} \ + %{!p:/usr/lib/nonshared/crt1.o%s}}}" + +#undef LIB_SPEC +#define LIB_SPEC "%{!shared:%{p:-lprof1} %{pg:-lprof1} -lc}" + +#undef ENDFILE_SPEC +#define ENDFILE_SPEC "%{!shared:crtn.o%s}" + +/* We do not want to run mips-tfile! */ +#undef ASM_FINAL_SPEC + +/* The system header files are C++ aware. */ +/* ??? Unfortunately, most but not all of the headers are C++ aware. + Specifically, curses.h is not, and as a consequence, defining this + used to prevent libg++ building. This is no longer the case so + define it again to prevent other problems, e.g. with getopt in + unistd.h. We still need some way to fix just those files that need + fixing. */ +#define NO_IMPLICIT_EXTERN_C 1 + +/* We don't support debugging info for now. */ +#undef DBX_DEBUGGING_INFO +#undef SDB_DEBUGGING_INFO +#undef MIPS_DEBUGGING_INFO +#undef PREFERRED_DEBUGGING_TYPE + +/* Likewise, the assembler doesn't handle DWARF2 directives. */ +#define DWARF2_UNWIND_INFO 0 + +#undef MACHINE_TYPE +#define MACHINE_TYPE "SGI running IRIX 5.x" + + /* Dollar signs are OK in Irix5 but not in Irix3. */ +#undef DOLLARS_IN_IDENTIFIERS +#undef NO_DOLLAR_IN_LABEL + +/* -G is incompatible with -KPIC which is the default, so only allow objects + in the small data section if the user explicitly asks for it. */ +#undef MIPS_DEFAULT_GVALUE +#define MIPS_DEFAULT_GVALUE 0 + +/* In Irix 5, we must output a `.global name .text' directive for every used + but undefined function. If we don't, the linker may perform an optimization + (skipping over the insns that set $gp) when it is unsafe. This is used + indirectly by ASM_OUTPUT_EXTERNAL. */ +#define ASM_OUTPUT_UNDEF_FUNCTION(FILE, NAME) \ +do { \ + fputs ("\t.globl ", FILE); \ + assemble_name (FILE, NAME); \ + fputs (" .text\n", FILE); \ +} while (0) + +/* Also do this for libcalls. */ +#define ASM_OUTPUT_EXTERNAL_LIBCALL(FILE, FUN) \ + mips_output_external_libcall (FILE, XSTR (FUN, 0)) + +/* This does for functions what ASM_DECLARE_OBJECT_NAME does for variables. + This is used indirectly by ASM_OUTPUT_EXTERNAL. */ +#define ASM_DECLARE_FUNCTION_SIZE(STREAM, NAME, DECL) \ +do { \ + tree name_tree = get_identifier (NAME); \ + TREE_ASM_WRITTEN (name_tree) = 1; \ +} while (0) diff --git a/contrib/gcc/config/mips/iris5gas.h b/contrib/gcc/config/mips/iris5gas.h new file mode 100644 index 000000000000..477a55fb647c --- /dev/null +++ b/contrib/gcc/config/mips/iris5gas.h @@ -0,0 +1,34 @@ +/* Definitions of target machine for GNU compiler. Irix version 5 with gas. */ + +/* Enable debugging. */ +#define DBX_DEBUGGING_INFO +#define SDB_DEBUGGING_INFO +#define MIPS_DEBUGGING_INFO +#define PREFERRED_DEBUGGING_TYPE SDB_DEBUG + +/* GNU as does handle DWARF2 directives. */ +#undef DWARF2_UNWIND_INFO +#define DWARF2_UNWIND_INFO 1 + +/* Irix 5 does not have some strange restrictions that Irix 3 had. */ +#undef SET_FILE_NUMBER +#define SET_FILE_NUMBER() ++num_source_filenames +#undef LABEL_AFTER_LOC +#define LABEL_AFTER_LOC(STREAM) + +/* We need to use .esize and .etype instead of .size and .type to + avoid conflicting with ELF directives. These are only recognized + by gas, anyhow, not the native assembler. */ +#undef PUT_SDB_SIZE +#define PUT_SDB_SIZE(a) \ +do { \ + extern FILE *asm_out_text_file; \ + fprintf (asm_out_text_file, "\t.esize\t%d;", (a)); \ +} while (0) + +#undef PUT_SDB_TYPE +#define PUT_SDB_TYPE(a) \ +do { \ + extern FILE *asm_out_text_file; \ + fprintf (asm_out_text_file, "\t.etype\t0x%x;", (a)); \ +} while (0) diff --git a/contrib/gcc/config/mips/iris6.h b/contrib/gcc/config/mips/iris6.h new file mode 100644 index 000000000000..9df7732e8b07 --- /dev/null +++ b/contrib/gcc/config/mips/iris6.h @@ -0,0 +1,567 @@ +/* Definitions of target machine for GNU compiler. Iris version 6. + Copyright (C) 1994, 1995, 1996, 1997, 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. */ + +/* Default to -mabi=n32 and -mips3. */ +#define MIPS_ISA_DEFAULT 3 +#define MIPS_ABI_DEFAULT ABI_N32 +#define MULTILIB_DEFAULTS { "mabi=n32" } + +#ifndef TARGET_DEFAULT +#define TARGET_DEFAULT (MASK_ABICALLS|MASK_FLOAT64|MASK_64BIT) +#endif + +#include "mips/iris5.h" +#include "mips/abi64.h" + +/* Irix6 assembler does handle DWARF2 directives. Override setting in + irix5.h file. */ +#undef DWARF2_UNWIND_INFO + +/* For Irix 6, -mabi=64 implies TARGET_LONG64. */ +/* This is handled in override_options. */ + +#undef SUBTARGET_CC1_SPEC +#define SUBTARGET_CC1_SPEC "%{static: -mno-abicalls}" + +/* We must pass -D_LONGLONG always, even when -ansi is used, because irix6 + system header files require it. This is OK, because gcc never warns + when long long is used in system header files. Alternatively, we can + add support for the SGI builtin type __long_long. */ +#undef CPP_PREDEFINES +#define CPP_PREDEFINES \ + "-Dunix -Dmips -Dsgi -Dhost_mips -DMIPSEB -D_MIPSEB -DSYSTYPE_SVR4 \ + -D_LONGLONG -D_SVR4_SOURCE -D_MODERN_C -D__DSO__ \ + -Asystem(unix) -Asystem(svr4) -Acpu(mips) -Amachine(sgi)" + +#undef SUBTARGET_CPP_SIZE_SPEC +#define SUBTARGET_CPP_SIZE_SPEC "\ +%{mabi=32: -D__SIZE_TYPE__=unsigned\\ int -D__PTRDIFF_TYPE__=int} \ +%{mabi=n32: -D__SIZE_TYPE__=unsigned\\ int -D__PTRDIFF_TYPE__=int} \ +%{mabi=64: -D__SIZE_TYPE__=long\\ unsigned\\ int -D__PTRDIFF_TYPE__=long\\ int} \ +%{!mabi*: -D__SIZE_TYPE__=unsigned\\ int -D__PTRDIFF_TYPE__=int}" + +/* We must make -mips3 do what -mlong64 used to do. */ +/* ??? If no mipsX option given, but a mabi=X option is, then should set + _MIPS_ISA based on the mabi=X option. */ +/* ??? If no mabi=X option give, but a mipsX option is, then should set + _MIPS_SIM based on the mipsX option. */ +/* ??? Same for _MIPS_SZINT. */ +/* ??? Same for _MIPS_SZPTR. */ +/* ??? Same for __SIZE_TYPE and __PTRDIFF_TYPE. */ +#undef SUBTARGET_CPP_SPEC +#define SUBTARGET_CPP_SPEC "\ +%{!ansi:-D__EXTENSIONS__ -D_SGI_SOURCE} \ +%{mfp32: -D_MIPS_FPSET=16}%{!mfp32: -D_MIPS_FPSET=32} \ +%{mips1: -D_MIPS_ISA=_MIPS_ISA_MIPS1} \ +%{mips2: -D_MIPS_ISA=_MIPS_ISA_MIPS2} \ +%{mips3: -D_MIPS_ISA=_MIPS_ISA_MIPS3} \ +%{mips4: -D_MIPS_ISA=_MIPS_ISA_MIPS4} \ +%{!mips*: -D_MIPS_ISA=_MIPS_ISA_MIPS3} \ +%{mabi=32: -D_MIPS_SIM=_MIPS_SIM_ABI32} \ +%{mabi=n32: -D_ABIN32=2 -D_MIPS_SIM=_ABIN32} \ +%{mabi=64: -D_ABI64=3 -D_MIPS_SIM=_ABI64} \ +%{!mabi*: -D_ABIN32=2 -D_MIPS_SIM=_ABIN32} \ +%{!mint64: -D_MIPS_SZINT=32}%{mint64: -D_MIPS_SZINT=64} \ +%{mabi=32: -D_MIPS_SZLONG=32} \ +%{mabi=n32: -D_MIPS_SZLONG=32} \ +%{mabi=64: -D_MIPS_SZLONG=64} \ +%{!mabi*: -D_MIPS_SZLONG=32} \ +%{mabi=32: -D_MIPS_SZPTR=32} \ +%{mabi=n32: -D_MIPS_SZPTR=32} \ +%{mabi=64: -D_MIPS_SZPTR=64} \ +%{!mabi*: -D_MIPS_SZPTR=32} \ +%{!mips1:%{!mips2: -D_COMPILER_VERSION=601}} \ +%{!mips*: -U__mips -D__mips=3} \ +%{mabi=32: -U__mips64} \ +%{mabi=n32: -D__mips64} \ +%{mabi=64: -D__mips64} \ +%{!mabi*: -D__mips64}" + +/* Irix 6 uses DWARF-2. */ +#define DWARF2_DEBUGGING_INFO +#define MIPS_DEBUGGING_INFO +#undef PREFERRED_DEBUGGING_TYPE +#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG + +/* Force the generation of dwarf .debug_frame sections even if not + compiling -g. This guarantees that we can unwind the stack. */ +#define DWARF2_FRAME_INFO 1 +/* The size in bytes of a DWARF field indicating an offset or length + relative to a debug info section, specified to be 4 bytes in the DWARF-2 + specification. The SGI/MIPS ABI defines it to be the same as PTR_SIZE. */ +#define DWARF_OFFSET_SIZE PTR_SIZE + +/* There is no GNU as port for Irix6 yet, so we set MD_EXEC_PREFIX so that + gcc will automatically find SGI as instead of searching the user's path. + The latter can fail when building a cross compiler if the user has . in + the path before /usr/bin, since then gcc will find and try to use the link + to the cross assembler which can't possibly work. */ + +#undef MD_EXEC_PREFIX +#define MD_EXEC_PREFIX "/usr/bin/" + +/* We have no need for MD_STARTFILE_PREFIX. */ +#undef MD_STARTFILE_PREFIX + +#undef MACHINE_TYPE +#define MACHINE_TYPE "SGI running IRIX 6.x" + +/* The Irix 6.0.1 assembler doesn't like labels in the text section, so + just avoid emitting them. */ +#define ASM_IDENTIFY_GCC(x) ((void)0) +#define ASM_IDENTIFY_LANGUAGE(x) ((void)0) + +/* Irix 5 stuff that we don't need for Irix 6. */ +/* ??? We do need this for the -mabi=32 switch though. */ +#undef ASM_OUTPUT_UNDEF_FUNCTION +#undef ASM_OUTPUT_EXTERNAL_LIBCALL +#undef ASM_DECLARE_FUNCTION_SIZE + +/* Stuff we need for Irix 6 that isn't in Irix 5. */ + +/* The SGI assembler doesn't like labels before the .ent, so we must output + the .ent and function name here, which is the normal place for it. */ + +#undef ASM_DECLARE_FUNCTION_NAME +#define ASM_DECLARE_FUNCTION_NAME(STREAM, NAME, DECL) \ + do { \ + fputs ("\t.ent\t", STREAM); \ + assemble_name (STREAM, NAME); \ + fputs ("\n", STREAM); \ + assemble_name (STREAM, NAME); \ + fputs (":\n", STREAM); \ + } while (0) + +/* Likewise, the SGI assembler doesn't like labels after the .end, so we + must output the .end here. */ +#define ASM_DECLARE_FUNCTION_SIZE(STREAM, NAME, DECL) \ + do { \ + fputs ("\t.end\t", STREAM); \ + assemble_name (STREAM, NAME); \ + fputs ("\n", STREAM); \ + } while (0) + +/* Tell function_prologue in mips.c that we have already output the .ent/.end + pseudo-ops. */ +#define FUNCTION_NAME_ALREADY_DECLARED + +#undef SET_ASM_OP /* Has no equivalent. See ASM_OUTPUT_DEF below. */ + +#if 0 +/* This is *NOT* how to equate one symbol to another symbol. The assembler + '=' syntax just equates a name to a constant expression. + See ASM_OUTPUT_WEAK_ALIAS. */ + +#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) +#endif + +/* Define the strings used for the special svr4 .type and .size directives. */ + +#define TYPE_ASM_OP ".type" +#define SIZE_ASM_OP ".size" + +/* This is how we tell the assembler that a symbol is weak. */ + +#define ASM_OUTPUT_WEAK_ALIAS(FILE,NAME,VALUE) \ + do { \ + ASM_GLOBALIZE_LABEL (FILE, NAME); \ + fputs ("\t.weakext\t", FILE); \ + assemble_name (FILE, NAME); \ + if (VALUE) \ + { \ + fputc (' ', FILE); \ + assemble_name (FILE, VALUE); \ + } \ + fputc ('\n', FILE); \ + } while (0) + +#define ASM_WEAKEN_LABEL(FILE,NAME) ASM_OUTPUT_WEAK_ALIAS(FILE,NAME,0) + +#define POPSECTION_ASM_OP ".popsection" + +#define DEBUG_INFO_SECTION ".debug_info,0x7000001e,0,0,1" +#define DEBUG_LINE_SECTION ".debug_line,0x7000001e,0,0,1" +#define SFNAMES_SECTION ".debug_sfnames,0x7000001e,0,0,1" +#define SRCINFO_SECTION ".debug_srcinfo,0x7000001e,0,0,1" +#define MACINFO_SECTION ".debug_macinfo,0x7000001e,0,0,1" +#define PUBNAMES_SECTION ".debug_pubnames,0x7000001e,0,0,1" +#define ARANGES_SECTION ".debug_aranges,0x7000001e,0,0,1" +#define FRAME_SECTION ".debug_frame,0x7000001e,0x08000000,0,1" +#define ABBREV_SECTION ".debug_abbrev,0x7000001e,0,0,1" + +/* ??? If no mabi=X option give, but a mipsX option is, then should depend + on the mipsX option. */ +#undef SUBTARGET_ASM_SPEC +#define SUBTARGET_ASM_SPEC "%{!mabi*:-n32}" + +/* Must pass -g0 to the assembler, otherwise it may overwrite our + debug info with its own debug info. */ +/* Must pass -show instead of -v. */ +/* Must pass -G 0 to the assembler, otherwise we may get warnings about + GOT overflow. */ +/* ??? We pass -w to disable all assembler warnings. The `label should be + inside .ent/.end block' warning that we get for DWARF II debug info labels + is particularly annoying. */ +#undef SUBTARGET_MIPS_AS_ASM_SPEC +#define SUBTARGET_MIPS_AS_ASM_SPEC "%{v:-show} -G 0 -w" + +#undef SUBTARGET_ASM_DEBUGGING_SPEC +#define SUBTARGET_ASM_DEBUGGING_SPEC "-g0" + +/* Stuff for constructors. Start here. */ + +/* The assembler now accepts .section pseudo-ops, but it does not allow + one to change the section in the middle of a function, so we can't use + the INIT_SECTION_ASM_OP code in crtstuff. But we can build up the ctor + and dtor lists this way, so we use -init and -fini to invoke the + do_global_* functions instead of running collect2. */ + +#define BSS_SECTION_ASM_OP ".section\t.bss" +#define CONST_SECTION_ASM_OP_32 "\t.rdata" +#define CONST_SECTION_ASM_OP_64 ".section\t.rodata" + +/* The IRIX 6 assembler .section directive takes four additional args: + section type, flags, entry size, and alignment. The alignment of the + .ctors and .dtors sections needs to be the same as the size of a pointer + so that the linker doesn't add padding between elements. */ +#if defined (CRT_BEGIN) || defined (CRT_END) + +/* If we are included from crtstuff.c, these need to be plain strings. + _MIPS_SZPTR is defined in SUBTARGET_CPP_SPEC above. */ +#if _MIPS_SZPTR == 64 +#define CTORS_SECTION_ASM_OP ".section\t.ctors,1,2,0,8" +#define DTORS_SECTION_ASM_OP ".section\t.dtors,1,2,0,8" +#else /* _MIPS_SZPTR != 64 */ +#define CTORS_SECTION_ASM_OP ".section\t.ctors,1,2,0,4" +#define DTORS_SECTION_ASM_OP ".section\t.dtors,1,2,0,4" +#endif /* _MIPS_SZPTR == 64 */ + +#else /* ! (defined (CRT_BEGIN) || defined (CRT_END)) */ + +/* If we are included from varasm.c, these need to depend on -mabi. */ +#define CTORS_SECTION_ASM_OP \ + (Pmode == DImode ? ".section\t.ctors,1,2,0,8" : ".section\t.ctors,1,2,0,4") +#define DTORS_SECTION_ASM_OP \ + (Pmode == DImode ? ".section\t.dtors,1,2,0,8" : ".section\t.dtors,1,2,0,4") +#endif /* defined (CRT_BEGIN) || defined (CRT_END) */ + +/* dwarf2out will handle padding this data properly. We definitely don't + want it 8-byte aligned on n32. */ +#define EH_FRAME_SECTION_ASM_OP ".section\t.eh_frame,1,2,0,1" + +/* A default list of other sections which we might be "in" at any given + time. For targets that use additional sections (e.g. .tdesc) you + should override this definition in the target-specific file which + includes this file. */ + +#undef EXTRA_SECTIONS +#define EXTRA_SECTIONS in_sdata, in_rdata, in_const, in_ctors, in_dtors + +/* A default list of extra section function definitions. For targets + that use additional sections (e.g. .tdesc) you should override this + definition in the target-specific file which includes this file. */ + +/* ??? rdata_section is now same as svr4 const_section. */ + +#undef EXTRA_SECTION_FUNCTIONS +#define EXTRA_SECTION_FUNCTIONS \ +void \ +sdata_section () \ +{ \ + if (in_section != in_sdata) \ + { \ + fprintf (asm_out_file, "%s\n", SDATA_SECTION_ASM_OP); \ + in_section = in_sdata; \ + } \ +} \ + \ +void \ +rdata_section () \ +{ \ + if (in_section != in_rdata) \ + { \ + if (mips_abi != ABI_32 && mips_abi != ABI_O64) \ + fprintf (asm_out_file, "%s\n", CONST_SECTION_ASM_OP_64); \ + else \ + fprintf (asm_out_file, "%s\n", CONST_SECTION_ASM_OP_32); \ + in_section = in_rdata; \ + } \ +} \ + 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; \ + } \ +} + +/* A C statement (sans semicolon) to output an element in the table of + global constructors. */ +#define ASM_OUTPUT_CONSTRUCTOR(FILE,NAME) \ + do { \ + ctors_section (); \ + fprintf (FILE, "\t%s\t ", \ + (Pmode == DImode) ? ".dword" : ".word"); \ + assemble_name (FILE, NAME); \ + fprintf (FILE, "\n"); \ + } while (0) + +/* A C statement (sans semicolon) to output an element in the table of + global destructors. */ +#define ASM_OUTPUT_DESTRUCTOR(FILE,NAME) \ + do { \ + dtors_section (); \ + fprintf (FILE, "\t%s\t ", \ + (Pmode == DImode) ? ".dword" : ".word"); \ + assemble_name (FILE, NAME); \ + fprintf (FILE, "\n"); \ + } while (0) + +/* 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. */ + +#define ASM_OUTPUT_SECTION_NAME(F, DECL, NAME, RELOC) \ +do { \ + extern FILE *asm_out_text_file; \ + if ((DECL) && TREE_CODE (DECL) == FUNCTION_DECL) \ + fprintf (asm_out_text_file, "\t.section %s,1,6,4,4\n", (NAME)); \ + else if ((DECL) && DECL_READONLY_SECTION (DECL, RELOC)) \ + fprintf (F, "\t.section %s,1,2,0,8\n", (NAME)); \ + else \ + fprintf (F, "\t.section %s,1,3,0,8\n", (NAME)); \ +} while (0) + +/* Stuff for constructors. End here. */ + +/* ??? Perhaps just include svr4.h in this file? */ + +/* ??? SGI assembler may core dump when compiling with -g. + Sometimes as succeeds, but then we get a linker error. (cmds.c in 072.sc) + Getting rid of .file solves both problems. */ +#undef ASM_OUTPUT_FILENAME +#define ASM_OUTPUT_FILENAME(STREAM, NUM_SOURCE_FILENAMES, NAME) \ +do \ + { \ + fprintf (STREAM, "\t#.file\t%d ", NUM_SOURCE_FILENAMES); \ + output_quoted_string (STREAM, NAME); \ + fputs ("\n", STREAM); \ + } \ +while (0) + +/* ??? SGI assembler gives warning whenever .lcomm is used. */ +#undef ASM_OUTPUT_LOCAL +#define ASM_OUTPUT_ALIGNED_LOCAL(STREAM, NAME, SIZE, ALIGN) \ +do \ + { \ + if (mips_abi != ABI_32 && mips_abi != ABI_O64) \ + { \ + fprintf (STREAM, "%s\n", BSS_SECTION_ASM_OP); \ + mips_declare_object (STREAM, NAME, "", ":\n", 0); \ + ASM_OUTPUT_ALIGN (STREAM, floor_log2 (ALIGN / BITS_PER_UNIT)); \ + ASM_OUTPUT_SKIP (STREAM, SIZE); \ + fprintf (STREAM, "\t%s\n", POPSECTION_ASM_OP); \ + } \ + else \ + mips_declare_object (STREAM, NAME, "\n\t.lcomm\t", ",%u\n", (SIZE)); \ + } \ +while (0) + +/* 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) \ + asm_output_aligned_bss (FILE, DECL, NAME, SIZE, ALIGN) + +/* Write the extra assembler code needed to declare an object properly. */ + +#undef ASM_DECLARE_OBJECT_NAME +#define ASM_DECLARE_OBJECT_NAME(STREAM, NAME, DECL) \ +do \ + { \ + size_directive_output = 0; \ + if (!flag_inhibit_size_directive && DECL_SIZE (DECL)) \ + { \ + size_directive_output = 1; \ + fprintf (STREAM, "\t%s\t ", SIZE_ASM_OP); \ + assemble_name (STREAM, NAME); \ + fprintf (STREAM, ",%d\n", int_size_in_bytes (TREE_TYPE (DECL))); \ + } \ + mips_declare_object (STREAM, NAME, "", ":\n", 0); \ + } \ +while (0) + +/* Output the size directive for a decl in rest_of_decl_compilation + in the case where we did not do so before the initializer. + Once we find the error_mark_node, we know that the value of + size_directive_output was set + by ASM_DECLARE_OBJECT_NAME when it was run for the same decl. */ + +#define ASM_FINISH_DECLARE_OBJECT(FILE, DECL, TOP_LEVEL, AT_END) \ +do { \ + char *name = XSTR (XEXP (DECL_RTL (DECL), 0), 0); \ + if (!flag_inhibit_size_directive && DECL_SIZE (DECL) \ + && ! AT_END && TOP_LEVEL \ + && DECL_INITIAL (DECL) == error_mark_node \ + && !size_directive_output) \ + { \ + size_directive_output = 1; \ + fprintf (FILE, "\t%s\t ", SIZE_ASM_OP); \ + assemble_name (FILE, name); \ + fprintf (FILE, ",%d\n", int_size_in_bytes (TREE_TYPE (DECL))); \ + } \ + } while (0) + +#undef LOCAL_LABEL_PREFIX +#define LOCAL_LABEL_PREFIX ((mips_abi == ABI_32 || mips_abi == ABI_O64) \ + ? "$" : ".") + +/* Profiling is supported via libprof1.a not -lc_p as in Irix 3. */ +/* ??? If no mabi=X option give, but a mipsX option is, then should depend + on the mipsX option. */ +#undef STARTFILE_SPEC +#define STARTFILE_SPEC \ + "%{!static:%{!shared: \ + %{mabi=32:%{pg:gcrt1.o%s} \ + %{!pg:%{p:mcrt1.o%s libprof1.a%s}%{!p:crt1.o%s}}} \ + %{mabi=n32: \ + %{mips4:%{pg:/usr/lib32/mips4/gcrt1.o%s} \ + %{!pg:%{p:/usr/lib32/mips4/mcrt1.o%s /usr/lib32/mips4/libprof1.a%s} \ + %{!p:/usr/lib32/mips4/crt1.o%s}}} \ + %{!mips4:%{pg:/usr/lib32/mips3/gcrt1.o%s} \ + %{!pg:%{p:/usr/lib32/mips3/mcrt1.o%s /usr/lib32/mips3/libprof1.a%s} \ + %{!p:/usr/lib32/mips3/crt1.o%s}}}} \ + %{mabi=64: \ + %{mips4:%{pg:/usr/lib64/mips4/gcrt1.o} \ + %{!pg:%{p:/usr/lib64/mips4/mcrt1.o /usr/lib64/mips4/libprof1.a} \ + %{!p:/usr/lib64/mips4/crt1.o}}} \ + %{!mips4:%{pg:/usr/lib64/mips3/gcrt1.o} \ + %{!pg:%{p:/usr/lib64/mips3/mcrt1.o /usr/lib64/mips3/libprof1.a} \ + %{!p:/usr/lib64/mips3/crt1.o}}}} \ + %{!mabi*: \ + %{mips4:%{pg:/usr/lib32/mips4/gcrt1.o%s} \ + %{!pg:%{p:/usr/lib32/mips4/mcrt1.o%s /usr/lib32/mips4/libprof1.a%s} \ + %{!p:/usr/lib32/mips4/crt1.o%s}}} \ + %{!mips4:%{pg:/usr/lib32/mips3/gcrt1.o%s} \ + %{!pg:%{p:/usr/lib32/mips3/mcrt1.o%s /usr/lib32/mips3/libprof1.a%s} \ + %{!p:/usr/lib32/mips3/crt1.o%s}}}}}} \ + %{static: \ + %{mabi=32:%{pg:/usr/lib/nonshared/gcrt1.o%s} \ + %{!pg:%{p:/usr/lib/nonshared/mcrt1.o%s /usr/lib/nonshared/libprof1.a%s} \ + %{!p:/usr/lib/nonshared/crt1.o%s}}} \ + %{mabi=n32: \ + %{mips4:%{pg:/usr/lib32/mips4/nonshared/gcrt1.o%s} \ + %{!pg:%{p:/usr/lib32/mips4/nonshared/mcrt1.o%s \ + /usr/lib32/mips4/nonshared/libprof1.a%s} \ + %{!p:/usr/lib32/mips4/nonshared/crt1.o%s}}} \ + %{!mips4:%{pg:/usr/lib32/mips3/nonshared/gcrt1.o%s} \ + %{!pg:%{p:/usr/lib32/mips3/nonshared/mcrt1.o%s \ + /usr/lib32/mips3/nonshared/libprof1.a%s} \ + %{!p:/usr/lib32/mips3/nonshared/crt1.o%s}}}} \ + %{mabi=64: \ + %{mips4:%{pg:/usr/lib64/mips4/nonshared/gcrt1.o} \ + %{!pg:%{p:/usr/lib64/mips4/nonshared/mcrt1.o \ + /usr/lib64/mips4/nonshared/libprof1.a} \ + %{!p:/usr/lib64/mips4/nonshared/crt1.o}}} \ + %{!mips4:%{pg:/usr/lib64/mips3/nonshared/gcrt1.o} \ + %{!pg:%{p:/usr/lib64/mips3/nonshared/mcrt1.o \ + /usr/lib64/mips3/nonshared/libprof1.a} \ + %{!p:/usr/lib64/mips3/nonshared/crt1.o}}}} \ + %{!mabi*: \ + %{mips4:%{pg:/usr/lib32/mips4/nonshared/gcrt1.o%s} \ + %{!pg:%{p:/usr/lib32/mips4/nonshared/mcrt1.o%s \ + /usr/lib32/mips4/nonshared/libprof1.a%s} \ + %{!p:/usr/lib32/mips4/nonshared/crt1.o%s}}} \ + %{!mips4:%{pg:/usr/lib32/mips3/nonshared/gcrt1.o%s} \ + %{!pg:%{p:/usr/lib32/mips3/nonshared/mcrt1.o%s \ + /usr/lib32/mips3/nonshared/libprof1.a%s} \ + %{!pg:%{p:/usr/lib32/mips3/nonshared/mcrt1.o%s \ + /usr/lib32/mips3/nonshared/libprof1.a%s} \ + %{!p:/usr/lib32/mips3/nonshared/crt1.o%s}}}}}} \ + crtbegin.o%s" + +#undef LIB_SPEC +#define LIB_SPEC \ + "%{mabi=n32: %{mips4:-L/usr/lib32/mips4} %{!mips4:-L/usr/lib32/mips3} \ + -L/usr/lib32} \ + %{mabi=64: %{mips4:-L/usr/lib64/mips4} %{!mips4:-L/usr/lib64/mips3} \ + -L/usr/lib64} \ + %{!mabi*: %{mips4:-L/usr/lib32/mips4} %{!mips4:-L/usr/lib32/mips3} \ + -L/usr/lib32} \ + %{!shared: \ + -dont_warn_unused %{p:libprof1.a%s}%{pg:libprof1.a%s} -lc -warn_unused}" + +/* Avoid getting two warnings for libgcc.a everytime we link. */ +#undef LIBGCC_SPEC +#define LIBGCC_SPEC "-dont_warn_unused -lgcc -warn_unused" + +/* ??? If no mabi=X option give, but a mipsX option is, then should depend + on the mipsX option. */ +#undef ENDFILE_SPEC +#define ENDFILE_SPEC \ + "crtend.o%s \ + %{!shared: \ + %{mabi=32:crtn.o%s}\ + %{mabi=n32:%{mips4:/usr/lib32/mips4/crtn.o%s}\ + %{!mips4:/usr/lib32/mips3/crtn.o%s}}\ + %{mabi=64:%{mips4:/usr/lib64/mips4/crtn.o%s}\ + %{!mips4:/usr/lib64/mips3/crtn.o%s}}\ + %{!mabi*:%{mips4:/usr/lib32/mips4/crtn.o%s}\ + %{!mips4:/usr/lib32/mips3/crtn.o%s}}}" + +/* ??? If no mabi=X option give, but a mipsX option is, then should depend + on the mipsX option. */ +#undef LINK_SPEC +#define LINK_SPEC "\ +%{G*} %{EB} %{EL} %{mips1} %{mips2} %{mips3} %{mips4} \ +%{bestGnum} %{shared} %{non_shared} \ +%{call_shared} %{no_archive} %{exact_version} %{w} \ +%{static: -non_shared} \ +%{!static: \ + %{!shared: %{!non_shared: %{!call_shared: -call_shared -no_unresolved}}}} \ +%{rpath} -init __do_global_ctors -fini __do_global_dtors \ +%{shared:-hidden_symbol __do_global_ctors,__do_global_dtors,__EH_FRAME_BEGIN__,__frame_dummy} \ +-_SYSTYPE_SVR4 -woff 131 \ +%{mabi=32: -32}%{mabi=n32: -n32}%{mabi=64: -64}%{!mabi*: -n32}" diff --git a/contrib/gcc/config/mips/linux.h b/contrib/gcc/config/mips/linux.h new file mode 100644 index 000000000000..f4ed424a6056 --- /dev/null +++ b/contrib/gcc/config/mips/linux.h @@ -0,0 +1,99 @@ +/* Definitions for MIPS running Linux-based GNU systems with ELF format. + 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. */ + +/* Required to keep collect2.c happy */ +#undef OBJECT_FORMAT_COFF + +#define HAVE_ATEXIT + +/* If we don't set MASK_ABICALLS, we can't default to PIC. */ +#undef TARGET_DEFAULT +#define TARGET_DEFAULT (MASK_ABICALLS|MASK_GAS) + + +/* 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" + +/* From iris5.h */ +/* -G is incompatible with -KPIC which is the default, so only allow objects + in the small data section if the user explicitly asks for it. */ +#undef MIPS_DEFAULT_GVALUE +#define MIPS_DEFAULT_GVALUE 0 + +/* Borrowed from sparc/linux.h */ +#undef LINK_SPEC +#define LINK_SPEC "-Y P,/usr/lib %{shared:-shared} \ + %{!shared: \ + %{!ibcs: \ + %{!static: \ + %{rdynamic:-export-dynamic} \ + %{!dynamic-linker:-dynamic-linker /lib/ld.so.1}} \ + %{static:-static}}}" + + +#undef SUBTARGET_ASM_SPEC +#define SUBTARGET_ASM_SPEC "-KPIC" + +/* Undefine the following which were defined in elf.h. This will cause the linux + port to continue to use collect2 for constructors/destructors. These may be removed + when .ctor/.dtor section support is desired. */ + +#undef CTORS_SECTION_ASM_OP +#undef DTORS_SECTION_ASM_OP + +#undef EXTRA_SECTIONS +#define EXTRA_SECTIONS in_sdata, in_rdata + +#undef INVOKE__main +#undef NAME__MAIN +#undef SYMBOL__MAIN + +#undef EXTRA_SECTION_FUNCTIONS +#define EXTRA_SECTION_FUNCTIONS \ + SECTION_FUNCTION_TEMPLATE(sdata_section, in_sdata, SDATA_SECTION_ASM_OP) \ + SECTION_FUNCTION_TEMPLATE(rdata_section, in_rdata, RDATA_SECTION_ASM_OP) + +#undef ASM_OUTPUT_CONSTRUCTOR +#undef ASM_OUTPUT_DESTRUCTOR + +#undef CTOR_LIST_BEGIN +#undef CTOR_LIST_END +#undef DTOR_LIST_BEGIN +#undef DTOR_LIST_END + +/* End of undefines to turn off .ctor/.dtor section support */ diff --git a/contrib/gcc/config/mips/mips-5.h b/contrib/gcc/config/mips/mips-5.h new file mode 100644 index 000000000000..f8b0941b0351 --- /dev/null +++ b/contrib/gcc/config/mips/mips-5.h @@ -0,0 +1,46 @@ +/* Definitions of target machine for GNU compiler. MIPS RISC-OS 5.0 + default version. + Copyright (C) 1992 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 LINK_SPEC "\ +%{G*} %{EB} %{EL} %{mips1} %{mips2} %{mips3} \ +%{bestGnum} %{shared} %{non_shared} \ +%{call_shared} %{no_archive} %{exact_version} \ +%{!shared: %{!non_shared: %{!call_shared: -non_shared}}}" + +#define STARTFILE_SPEC "%{pg:gcrt0.o%s}%{!pg:%{p:mcrt0.o%s}%{!p:crt1.o%s crtn.o%s}}" + +#include "mips/mips.h" + +/* Some assemblers have a bug that causes backslash escaped chars in .ascii + to be misassembled, so we just completely avoid it. */ +#undef ASM_OUTPUT_ASCII +#define ASM_OUTPUT_ASCII(FILE,PTR,LEN) \ +do { \ + unsigned char *s; \ + int i; \ + for (i = 0, s = (unsigned char *)(PTR); i < (LEN); s++, i++) \ + { \ + if ((i % 8) == 0) \ + fputs ("\n\t.byte\t", (FILE)); \ + fprintf ((FILE), "%s0x%x", (i%8?",":""), (unsigned)*s); \ + } \ + fputs ("\n", (FILE)); \ +} while (0) diff --git a/contrib/gcc/config/mips/mips.c b/contrib/gcc/config/mips/mips.c new file mode 100644 index 000000000000..20caaa441790 --- /dev/null +++ b/contrib/gcc/config/mips/mips.c @@ -0,0 +1,8522 @@ +/* Subroutines for insn-output.c for MIPS + Copyright (C) 1989, 90, 91, 93-98, 1999 Free Software Foundation, Inc. + Contributed by A. Lichnewsky, lich@inria.inria.fr. + Changes by Michael Meissner, meissner@osf.org. + 64 bit r4000 support by Ian Lance Taylor, ian@cygnus.com, and + Brendan Eich, brendan@microunity.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. */ + +/* ??? The TARGET_FP_CALL_32 macros are intended to simulate a 32 bit + calling convention in 64 bit mode. It doesn't work though, and should + be replaced with something better designed. */ + +#include "config.h" +#include "system.h" +#include <signal.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 "insn-attr.h" +#include "insn-codes.h" +#include "recog.h" +#include "toplev.h" + +#undef MAX /* sys/param.h may also define these */ +#undef MIN + +#include "tree.h" +#include "expr.h" +#include "flags.h" +#include "reload.h" +#include "output.h" + +#if defined(USG) || !defined(HAVE_STAB_H) +#include "gstab.h" /* If doing DBX on sysV, use our own stab.h. */ +#else +#include <stab.h> /* On BSD, use the system's stab.h. */ +#endif /* not USG */ + +#ifdef __GNU_STAB__ +#define STAB_CODE_TYPE enum __stab_debug_code +#else +#define STAB_CODE_TYPE int +#endif + +extern char *mktemp (); +extern tree lookup_name (); + +/* Enumeration for all of the relational tests, so that we can build + arrays indexed by the test type, and not worry about the order + of EQ, NE, etc. */ + +enum internal_test { + ITEST_EQ, + ITEST_NE, + ITEST_GT, + ITEST_GE, + ITEST_LT, + ITEST_LE, + ITEST_GTU, + ITEST_GEU, + ITEST_LTU, + ITEST_LEU, + ITEST_MAX + }; + + +struct constant; +static enum internal_test map_test_to_internal_test PROTO ((enum rtx_code)); +static int mips16_simple_memory_operand PROTO ((rtx, rtx, + enum machine_mode)); +static int m16_check_op PROTO ((rtx, int, int, int)); +static void block_move_loop PROTO ((rtx, rtx, int, int, + rtx, rtx)); +static void block_move_call PROTO ((rtx, rtx, rtx)); +static FILE *make_temp_file PROTO ((void)); +static void save_restore_insns PROTO ((int, rtx, + long, FILE *)); +static void mips16_output_gp_offset PROTO ((FILE *, rtx)); +static void mips16_fp_args PROTO ((FILE *, int, int)); +static void build_mips16_function_stub PROTO ((FILE *)); +static void mips16_optimize_gp PROTO ((rtx)); +static rtx add_constant PROTO ((struct constant **, + rtx, + enum machine_mode)); +static void dump_constants PROTO ((struct constant *, + rtx)); +static rtx mips_find_symbol PROTO ((rtx)); +static void abort_with_insn PROTO ((rtx, const char *)) + ATTRIBUTE_NORETURN; + + +/* Global variables for machine-dependent things. */ + +/* Threshold for data being put into the small data/bss area, instead + of the normal data area (references to the small data/bss area take + 1 instruction, and use the global pointer, references to the normal + data area takes 2 instructions). */ +int mips_section_threshold = -1; + +/* Count the number of .file directives, so that .loc is up to date. */ +int num_source_filenames = 0; + +/* Count the number of sdb related labels are generated (to find block + start and end boundaries). */ +int sdb_label_count = 0; + +/* Next label # for each statement for Silicon Graphics IRIS systems. */ +int sym_lineno = 0; + +/* Non-zero if inside of a function, because the stupid MIPS asm can't + handle .files inside of functions. */ +int inside_function = 0; + +/* Files to separate the text and the data output, so that all of the data + can be emitted before the text, which will mean that the assembler will + generate smaller code, based on the global pointer. */ +FILE *asm_out_data_file; +FILE *asm_out_text_file; + +/* Linked list of all externals that are to be emitted when optimizing + for the global pointer if they haven't been declared by the end of + the program with an appropriate .comm or initialization. */ + +struct extern_list +{ + struct extern_list *next; /* next external */ + char *name; /* name of the external */ + int size; /* size in bytes */ +} *extern_head = 0; + +/* Name of the file containing the current function. */ +const char *current_function_file = ""; + +/* Warning given that Mips ECOFF can't support changing files + within a function. */ +int file_in_function_warning = FALSE; + +/* Whether to suppress issuing .loc's because the user attempted + to change the filename within a function. */ +int ignore_line_number = FALSE; + +/* Number of nested .set noreorder, noat, nomacro, and volatile requests. */ +int set_noreorder; +int set_noat; +int set_nomacro; +int set_volatile; + +/* The next branch instruction is a branch likely, not branch normal. */ +int mips_branch_likely; + +/* Count of delay slots and how many are filled. */ +int dslots_load_total; +int dslots_load_filled; +int dslots_jump_total; +int dslots_jump_filled; + +/* # of nops needed by previous insn */ +int dslots_number_nops; + +/* Number of 1/2/3 word references to data items (ie, not jal's). */ +int num_refs[3]; + +/* registers to check for load delay */ +rtx mips_load_reg, mips_load_reg2, mips_load_reg3, mips_load_reg4; + +/* Cached operands, and operator to compare for use in set/branch on + condition codes. */ +rtx branch_cmp[2]; + +/* what type of branch to use */ +enum cmp_type branch_type; + +/* Number of previously seen half-pic pointers and references. */ +static int prev_half_pic_ptrs = 0; +static int prev_half_pic_refs = 0; + +/* which cpu are we scheduling for */ +enum processor_type mips_cpu; + +/* which instruction set architecture to use. */ +int mips_isa; + +#ifdef MIPS_ABI_DEFAULT +/* Which ABI to use. This is defined to a constant in mips.h if the target + doesn't support multiple ABIs. */ +int mips_abi; +#endif + +/* Strings to hold which cpu and instruction set architecture to use. */ +const char *mips_cpu_string; /* for -mcpu=<xxx> */ +const char *mips_isa_string; /* for -mips{1,2,3,4} */ +const char *mips_abi_string; /* for -mabi={32,n32,64,eabi} */ + +/* Whether we are generating mips16 code. This is a synonym for + TARGET_MIPS16, and exists for use as an attribute. */ +int mips16; + +/* This variable is set by -mno-mips16. We only care whether + -mno-mips16 appears or not, and using a string in this fashion is + just a way to avoid using up another bit in target_flags. */ +const char *mips_no_mips16_string; + +/* This is only used to determine if an type size setting option was + explicitly specified (-mlong64, -mint64, -mlong32). The specs + set this option if such an option is used. */ +const char *mips_explicit_type_size_string; + +/* Whether we are generating mips16 hard float code. In mips16 mode + we always set TARGET_SOFT_FLOAT; this variable is nonzero if + -msoft-float was not specified by the user, which means that we + should arrange to call mips32 hard floating point code. */ +int mips16_hard_float; + +/* This variable is set by -mentry. We only care whether -mentry + appears or not, and using a string in this fashion is just a way to + avoid using up another bit in target_flags. */ +const char *mips_entry_string; + +/* Whether we should entry and exit pseudo-ops in mips16 mode. */ +int mips_entry; + +/* If TRUE, we split addresses into their high and low parts in the RTL. */ +int mips_split_addresses; + +/* Generating calls to position independent functions? */ +enum mips_abicalls_type mips_abicalls; + +/* High and low marks for floating point values which we will accept + as legitimate constants for LEGITIMATE_CONSTANT_P. These are + initialized in override_options. */ +REAL_VALUE_TYPE dfhigh, dflow, sfhigh, sflow; + +/* Mode used for saving/restoring general purpose registers. */ +static enum machine_mode gpr_mode; + +/* Array giving truth value on whether or not a given hard register + can support a given mode. */ +char mips_hard_regno_mode_ok[(int)MAX_MACHINE_MODE][FIRST_PSEUDO_REGISTER]; + +/* Current frame information calculated by compute_frame_size. */ +struct mips_frame_info current_frame_info; + +/* Zero structure to initialize current_frame_info. */ +struct mips_frame_info zero_frame_info; + +/* Temporary filename used to buffer .text until end of program + for -mgpopt. */ +static char *temp_filename; + +/* Pseudo-reg holding the address of the current function when + generating embedded PIC code. Created by LEGITIMIZE_ADDRESS, used + by mips_finalize_pic if it was created. */ +rtx embedded_pic_fnaddr_rtx; + +/* The length of all strings seen when compiling for the mips16. This + is used to tell how many strings are in the constant pool, so that + we can see if we may have an overflow. This is reset each time the + constant pool is output. */ +int mips_string_length; + +/* Pseudo-reg holding the value of $28 in a mips16 function which + refers to GP relative global variables. */ +rtx mips16_gp_pseudo_rtx; + +/* In mips16 mode, we build a list of all the string constants we see + in a particular function. */ + +struct string_constant +{ + struct string_constant *next; + char *label; +}; + +static struct string_constant *string_constants; + +/* List of all MIPS punctuation characters used by print_operand. */ +char mips_print_operand_punct[256]; + +/* Map GCC register number to debugger register number. */ +int mips_dbx_regno[FIRST_PSEUDO_REGISTER]; + +/* Buffer to use to enclose a load/store operation with %{ %} to + turn on .set volatile. */ +static char volatile_buffer[60]; + +/* Hardware names for the registers. If -mrnames is used, this + will be overwritten with mips_sw_reg_names. */ + +char mips_reg_names[][8] = +{ + "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7", + "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", + "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23", + "$24", "$25", "$26", "$27", "$28", "$sp", "$fp", "$31", + "$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", + "hi", "lo", "accum","$fcc0","$fcc1","$fcc2","$fcc3","$fcc4", + "$fcc5","$fcc6","$fcc7","$rap" +}; + +/* Mips software names for the registers, used to overwrite the + mips_reg_names array. */ + +char mips_sw_reg_names[][8] = +{ + "$zero","$at", "$v0", "$v1", "$a0", "$a1", "$a2", "$a3", + "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7", + "$s0", "$s1", "$s2", "$s3", "$s4", "$s5", "$s6", "$s7", + "$t8", "$t9", "$k0", "$k1", "$gp", "$sp", "$fp", "$ra", + "$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", + "hi", "lo", "accum","$fcc0","$fcc1","$fcc2","$fcc3","$fcc4", + "$fcc5","$fcc6","$fcc7","$rap" +}; + +/* Map hard register number to register class */ +enum reg_class mips_regno_to_class[] = +{ + GR_REGS, GR_REGS, M16_NA_REGS, M16_NA_REGS, + M16_REGS, M16_REGS, M16_REGS, M16_REGS, + GR_REGS, GR_REGS, GR_REGS, GR_REGS, + GR_REGS, GR_REGS, GR_REGS, GR_REGS, + M16_NA_REGS, M16_NA_REGS, GR_REGS, GR_REGS, + GR_REGS, GR_REGS, GR_REGS, GR_REGS, + T_REG, GR_REGS, GR_REGS, GR_REGS, + GR_REGS, GR_REGS, GR_REGS, GR_REGS, + FP_REGS, FP_REGS, FP_REGS, FP_REGS, + FP_REGS, FP_REGS, FP_REGS, FP_REGS, + FP_REGS, FP_REGS, FP_REGS, FP_REGS, + FP_REGS, FP_REGS, FP_REGS, FP_REGS, + FP_REGS, FP_REGS, FP_REGS, FP_REGS, + FP_REGS, FP_REGS, FP_REGS, FP_REGS, + FP_REGS, FP_REGS, FP_REGS, FP_REGS, + FP_REGS, FP_REGS, FP_REGS, FP_REGS, + HI_REG, LO_REG, HILO_REG, ST_REGS, + ST_REGS, ST_REGS, ST_REGS, ST_REGS, + ST_REGS, ST_REGS, ST_REGS, GR_REGS +}; + +/* Map register constraint character to register class. */ +enum reg_class mips_char_to_class[256] = +{ + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, + NO_REGS, NO_REGS, NO_REGS, NO_REGS, +}; + + +/* Return truth value of whether OP can be used as an operands + where a register or 16 bit unsigned integer is needed. */ + +int +uns_arith_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (GET_CODE (op) == CONST_INT && SMALL_INT_UNSIGNED (op)) + return 1; + + return register_operand (op, mode); +} + +/* Return truth value of whether OP can be used as an operands + where a 16 bit integer is needed */ + +int +arith_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (GET_CODE (op) == CONST_INT && SMALL_INT (op)) + return 1; + + /* On the mips16, a GP relative value is a signed 16 bit offset. */ + if (TARGET_MIPS16 && GET_CODE (op) == CONST && mips16_gp_offset_p (op)) + return 1; + + return register_operand (op, mode); +} + +/* Return truth value of whether OP can be used as an operand in a two + address arithmetic insn (such as set 123456,%o4) of mode MODE. */ + +int +arith32_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (GET_CODE (op) == CONST_INT) + return 1; + + return register_operand (op, mode); +} + +/* Return truth value of whether OP is a integer which fits in 16 bits */ + +int +small_int (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return (GET_CODE (op) == CONST_INT && SMALL_INT (op)); +} + +/* Return truth value of whether OP is a 32 bit integer which is too big to + be loaded with one instruction. */ + +int +large_int (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + HOST_WIDE_INT value; + + if (GET_CODE (op) != CONST_INT) + return 0; + + value = INTVAL (op); + + /* ior reg,$r0,value */ + if ((value & ~ ((HOST_WIDE_INT) 0x0000ffff)) == 0) + return 0; + + /* subu reg,$r0,value */ + if (((unsigned HOST_WIDE_INT) (value + 32768)) <= 32767) + return 0; + + /* lui reg,value>>16 */ + if ((value & 0x0000ffff) == 0) + return 0; + + return 1; +} + +/* Return truth value of whether OP is a register or the constant 0. + In mips16 mode, we only accept a register, since the mips16 does + not have $0. */ + +int +reg_or_0_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + switch (GET_CODE (op)) + { + case CONST_INT: + if (TARGET_MIPS16) + return 0; + return INTVAL (op) == 0; + + case CONST_DOUBLE: + if (TARGET_MIPS16) + return 0; + return op == CONST0_RTX (mode); + + case REG: + case SUBREG: + return register_operand (op, mode); + + default: + break; + } + + return 0; +} + +/* Return truth value of whether OP is a register or the constant 0, + even in mips16 mode. */ + +int +true_reg_or_0_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + switch (GET_CODE (op)) + { + case CONST_INT: + return INTVAL (op) == 0; + + case CONST_DOUBLE: + return op == CONST0_RTX (mode); + + case REG: + case SUBREG: + return register_operand (op, mode); + + default: + break; + } + + return 0; +} + +/* Return truth value if a CONST_DOUBLE is ok to be a legitimate constant. */ + +int +mips_const_double_ok (op, mode) + rtx op; + enum machine_mode mode; +{ + REAL_VALUE_TYPE d; + + if (GET_CODE (op) != CONST_DOUBLE) + return 0; + + if (mode == VOIDmode) + return 1; + + if (mode != SFmode && mode != DFmode) + return 0; + + if (op == CONST0_RTX (mode)) + return 1; + + /* ??? li.s does not work right with SGI's Irix 6 assembler. */ + if (mips_abi != ABI_32 && mips_abi != ABI_O64 && mips_abi != ABI_EABI) + return 0; + + REAL_VALUE_FROM_CONST_DOUBLE (d, op); + + if (REAL_VALUE_ISNAN (d)) + return FALSE; + + if (REAL_VALUE_NEGATIVE (d)) + d = REAL_VALUE_NEGATE (d); + + if (mode == DFmode) + { + if (REAL_VALUES_LESS (d, dfhigh) + && REAL_VALUES_LESS (dflow, d)) + return 1; + } + else + { + if (REAL_VALUES_LESS (d, sfhigh) + && REAL_VALUES_LESS (sflow, d)) + return 1; + } + + return 0; +} + +/* Accept the floating point constant 1 in the appropriate mode. */ + +int +const_float_1_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + REAL_VALUE_TYPE d; + static REAL_VALUE_TYPE onedf; + static REAL_VALUE_TYPE onesf; + static int one_initialized; + + if (GET_CODE (op) != CONST_DOUBLE + || mode != GET_MODE (op) + || (mode != DFmode && mode != SFmode)) + return 0; + + REAL_VALUE_FROM_CONST_DOUBLE (d, op); + + /* We only initialize these values if we need them, since we will + never get called unless mips_isa >= 4. */ + if (! one_initialized) + { + onedf = REAL_VALUE_ATOF ("1.0", DFmode); + onesf = REAL_VALUE_ATOF ("1.0", SFmode); + one_initialized = 1; + } + + if (mode == DFmode) + return REAL_VALUES_EQUAL (d, onedf); + else + return REAL_VALUES_EQUAL (d, onesf); +} + +/* Return true if a memory load or store of REG plus OFFSET in MODE + can be represented in a single word on the mips16. */ + +static int +mips16_simple_memory_operand (reg, offset, mode) + rtx reg; + rtx offset; + enum machine_mode mode; +{ + int size, off; + + if (mode == BLKmode) + { + /* We can't tell, because we don't know how the value will + eventually be accessed. Returning 0 here does no great + harm; it just prevents some possible instruction scheduling. */ + return 0; + } + + size = GET_MODE_SIZE (mode); + + if (INTVAL (offset) % size != 0) + return 0; + if (REGNO (reg) == STACK_POINTER_REGNUM && GET_MODE_SIZE (mode) == 4) + off = 0x100; + else + off = 0x20; + if (INTVAL (offset) >= 0 && INTVAL (offset) < off * size) + return 1; + return 0; +} + +/* Return truth value if a memory operand fits in a single instruction + (ie, register + small offset). */ + +int +simple_memory_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + rtx addr, plus0, plus1; + + /* Eliminate non-memory operations */ + if (GET_CODE (op) != MEM) + return 0; + + /* dword operations really put out 2 instructions, so eliminate them. */ + /* ??? This isn't strictly correct. It is OK to accept multiword modes + here, since the length attributes are being set correctly, but only + if the address is offsettable. LO_SUM is not offsettable. */ + if (GET_MODE_SIZE (GET_MODE (op)) > UNITS_PER_WORD) + return 0; + + /* Decode the address now. */ + addr = XEXP (op, 0); + switch (GET_CODE (addr)) + { + case REG: + case LO_SUM: + return 1; + + case CONST_INT: + if (TARGET_MIPS16) + return 0; + return SMALL_INT (op); + + case PLUS: + plus0 = XEXP (addr, 0); + plus1 = XEXP (addr, 1); + if (GET_CODE (plus0) == REG + && GET_CODE (plus1) == CONST_INT && SMALL_INT (plus1) + && (! TARGET_MIPS16 + || mips16_simple_memory_operand (plus0, plus1, mode))) + return 1; + + else if (GET_CODE (plus1) == REG + && GET_CODE (plus0) == CONST_INT && SMALL_INT (plus0) + && (! TARGET_MIPS16 + || mips16_simple_memory_operand (plus1, plus0, mode))) + return 1; + + else + return 0; + +#if 0 + /* We used to allow small symbol refs here (ie, stuff in .sdata + or .sbss), but this causes some bugs in G++. Also, it won't + interfere if the MIPS linker rewrites the store instruction + because the function is PIC. */ + + case LABEL_REF: /* never gp relative */ + break; + + case CONST: + /* If -G 0, we can never have a GP relative memory operation. + Also, save some time if not optimizing. */ + if (!TARGET_GP_OPT) + return 0; + + { + rtx offset = const0_rtx; + addr = eliminate_constant_term (XEXP (addr, 0), &offset); + if (GET_CODE (op) != SYMBOL_REF) + return 0; + + /* let's be paranoid.... */ + if (! SMALL_INT (offset)) + return 0; + } + + /* fall through */ + + case SYMBOL_REF: + return SYMBOL_REF_FLAG (addr); +#endif + + /* This SYMBOL_REF case is for the mips16. If the above case is + reenabled, this one should be merged in. */ + case SYMBOL_REF: + /* References to the constant pool on the mips16 use a small + offset if the function is small. The only time we care about + getting this right is during delayed branch scheduling, so + don't need to check until then. The machine_dependent_reorg + function will set the total length of the instructions used + in the function in current_frame_info. If that is small + enough, we know for sure that this is a small offset. It + would be better if we could take into account the location of + the instruction within the function, but we can't, because we + don't know where we are. */ + if (TARGET_MIPS16 + && CONSTANT_POOL_ADDRESS_P (addr) + && current_frame_info.insns_len > 0) + { + long size; + + size = current_frame_info.insns_len + get_pool_size (); + if (GET_MODE_SIZE (mode) == 4) + return size < 4 * 0x100; + else if (GET_MODE_SIZE (mode) == 8) + return size < 8 * 0x20; + else + return 0; + } + + return 0; + + default: + break; + } + + return 0; +} + +/* Return nonzero for a memory address that can be used to load or store + a doubleword. */ + +int +double_memory_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + rtx addr; + + if (GET_CODE (op) != MEM + || ! memory_operand (op, mode)) + { + /* During reload, we accept a pseudo register if it has an + appropriate memory address. If we don't do this, we will + wind up reloading into a register, and then reloading that + register from memory, when we could just reload directly from + memory. */ + if (reload_in_progress + && GET_CODE (op) == REG + && REGNO (op) >= FIRST_PSEUDO_REGISTER + && reg_renumber[REGNO (op)] < 0 + && reg_equiv_mem[REGNO (op)] != 0 + && double_memory_operand (reg_equiv_mem[REGNO (op)], mode)) + return 1; + + /* All reloaded addresses are valid in TARGET_64BIT mode. This is + the same test performed for 'm' in find_reloads. */ + + if (reload_in_progress + && TARGET_64BIT + && (GET_CODE (op) == MEM + || (GET_CODE (op) == REG + && REGNO (op) >= FIRST_PSEUDO_REGISTER + && reg_renumber[REGNO (op)] < 0))) + return 1; + + if (reload_in_progress + && TARGET_MIPS16 + && GET_CODE (op) == MEM) + { + rtx addr; + + addr = XEXP (op, 0); + + /* During reload on the mips16, we accept a large offset + from the frame pointer or the stack pointer. This large + address will get reloaded anyhow. */ + if (GET_CODE (addr) == PLUS + && GET_CODE (XEXP (addr, 0)) == REG + && (REGNO (XEXP (addr, 0)) == HARD_FRAME_POINTER_REGNUM + || REGNO (XEXP (addr, 0)) == STACK_POINTER_REGNUM) + && ((GET_CODE (XEXP (addr, 1)) == CONST_INT + && ! SMALL_INT (XEXP (addr, 1))) + || (GET_CODE (XEXP (addr, 1)) == SYMBOL_REF + && CONSTANT_POOL_ADDRESS_P (XEXP (addr, 1))))) + return 1; + + /* Similarly, we accept a case where the memory address is + itself on the stack, and will be reloaded. */ + if (GET_CODE (addr) == MEM) + { + rtx maddr; + + maddr = XEXP (addr, 0); + if (GET_CODE (maddr) == PLUS + && GET_CODE (XEXP (maddr, 0)) == REG + && (REGNO (XEXP (maddr, 0)) == HARD_FRAME_POINTER_REGNUM + || REGNO (XEXP (maddr, 0)) == STACK_POINTER_REGNUM) + && ((GET_CODE (XEXP (maddr, 1)) == CONST_INT + && ! SMALL_INT (XEXP (maddr, 1))) + || (GET_CODE (XEXP (maddr, 1)) == SYMBOL_REF + && CONSTANT_POOL_ADDRESS_P (XEXP (maddr, 1))))) + return 1; + } + + /* We also accept the same case when we have a 16 bit signed + offset mixed in as well. The large address will get + reloaded, and the 16 bit offset will be OK. */ + if (GET_CODE (addr) == PLUS + && GET_CODE (XEXP (addr, 0)) == MEM + && GET_CODE (XEXP (addr, 1)) == CONST_INT + && SMALL_INT (XEXP (addr, 1))) + { + addr = XEXP (XEXP (addr, 0), 0); + if (GET_CODE (addr) == PLUS + && GET_CODE (XEXP (addr, 0)) == REG + && (REGNO (XEXP (addr, 0)) == HARD_FRAME_POINTER_REGNUM + || REGNO (XEXP (addr, 0)) == STACK_POINTER_REGNUM) + && ((GET_CODE (XEXP (addr, 1)) == CONST_INT + && ! SMALL_INT (XEXP (addr, 1))) + || (GET_CODE (XEXP (addr, 1)) == SYMBOL_REF + && CONSTANT_POOL_ADDRESS_P (XEXP (addr, 1))))) + return 1; + } + } + + return 0; + } + + if (TARGET_64BIT) + { + /* In this case we can use an instruction like sd. */ + return 1; + } + + /* Make sure that 4 added to the address is a valid memory address. + This essentially just checks for overflow in an added constant. */ + + addr = XEXP (op, 0); + + if (CONSTANT_ADDRESS_P (addr)) + return 1; + + return memory_address_p ((GET_MODE_CLASS (mode) == MODE_INT + ? SImode + : SFmode), + plus_constant_for_output (addr, 4)); +} + +/* Return nonzero if the code of this rtx pattern is EQ or NE. */ + +int +equality_op (op, mode) + rtx op; + enum machine_mode mode; +{ + if (mode != GET_MODE (op)) + return 0; + + return GET_CODE (op) == EQ || GET_CODE (op) == NE; +} + +/* Return nonzero if the code is a relational operations (EQ, LE, etc.) */ + +int +cmp_op (op, mode) + rtx op; + enum machine_mode mode; +{ + if (mode != GET_MODE (op)) + return 0; + + return GET_RTX_CLASS (GET_CODE (op)) == '<'; +} + +/* Return nonzero if the operand is either the PC or a label_ref. */ + +int +pc_or_label_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + if (op == pc_rtx) + return 1; + + if (GET_CODE (op) == LABEL_REF) + return 1; + + return 0; +} + +/* Test for a valid operand for a call instruction. + Don't allow the arg pointer register or virtual regs + since they may change into reg + const, which the patterns + can't handle yet. */ + +int +call_insn_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return (CONSTANT_ADDRESS_P (op) + || (GET_CODE (op) == REG && op != arg_pointer_rtx + && ! (REGNO (op) >= FIRST_PSEUDO_REGISTER + && REGNO (op) <= LAST_VIRTUAL_REGISTER))); +} + +/* Return nonzero if OPERAND is valid as a source operand for a move + instruction. */ + +int +move_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + /* Accept any general operand after reload has started; doing so + avoids losing if reload does an in-place replacement of a register + with a SYMBOL_REF or CONST. */ + return (general_operand (op, mode) + && (! (mips_split_addresses && mips_check_split (op, mode)) + || reload_in_progress || reload_completed) + && ! (TARGET_MIPS16 + && GET_CODE (op) == SYMBOL_REF + && ! mips16_constant (op, mode, 1, 0))); +} + +/* Return nonzero if OPERAND is valid as a source operand for movdi. + This accepts not only general_operand, but also sign extended + constants and registers. We need to accept sign extended constants + in case a sign extended register which is used in an expression, + and is equivalent to a constant, is spilled. */ + +int +movdi_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (TARGET_64BIT + && mode == DImode + && GET_CODE (op) == SIGN_EXTEND + && GET_MODE (op) == DImode + && (GET_MODE (XEXP (op, 0)) == SImode + || (GET_CODE (XEXP (op, 0)) == CONST_INT + && GET_MODE (XEXP (op, 0)) == VOIDmode)) + && (register_operand (XEXP (op, 0), SImode) + || immediate_operand (XEXP (op, 0), SImode))) + return 1; + + return (general_operand (op, mode) + && ! (TARGET_MIPS16 + && GET_CODE (op) == SYMBOL_REF + && ! mips16_constant (op, mode, 1, 0))); +} + +/* Like register_operand, but when in 64 bit mode also accept a sign + extend of a 32 bit register, since the value is known to be already + sign extended. */ + +int +se_register_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (TARGET_64BIT + && mode == DImode + && GET_CODE (op) == SIGN_EXTEND + && GET_MODE (op) == DImode + && GET_MODE (XEXP (op, 0)) == SImode + && register_operand (XEXP (op, 0), SImode)) + return 1; + + return register_operand (op, mode); +} + +/* Like reg_or_0_operand, but when in 64 bit mode also accept a sign + extend of a 32 bit register, since the value is known to be already + sign extended. */ + +int +se_reg_or_0_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (TARGET_64BIT + && mode == DImode + && GET_CODE (op) == SIGN_EXTEND + && GET_MODE (op) == DImode + && GET_MODE (XEXP (op, 0)) == SImode + && register_operand (XEXP (op, 0), SImode)) + return 1; + + return reg_or_0_operand (op, mode); +} + +/* Like uns_arith_operand, but when in 64 bit mode also accept a sign + extend of a 32 bit register, since the value is known to be already + sign extended. */ + +int +se_uns_arith_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (TARGET_64BIT + && mode == DImode + && GET_CODE (op) == SIGN_EXTEND + && GET_MODE (op) == DImode + && GET_MODE (XEXP (op, 0)) == SImode + && register_operand (XEXP (op, 0), SImode)) + return 1; + + return uns_arith_operand (op, mode); +} + +/* Like arith_operand, but when in 64 bit mode also accept a sign + extend of a 32 bit register, since the value is known to be already + sign extended. */ + +int +se_arith_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (TARGET_64BIT + && mode == DImode + && GET_CODE (op) == SIGN_EXTEND + && GET_MODE (op) == DImode + && GET_MODE (XEXP (op, 0)) == SImode + && register_operand (XEXP (op, 0), SImode)) + return 1; + + return arith_operand (op, mode); +} + +/* Like nonmemory_operand, but when in 64 bit mode also accept a sign + extend of a 32 bit register, since the value is known to be already + sign extended. */ + +int +se_nonmemory_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (TARGET_64BIT + && mode == DImode + && GET_CODE (op) == SIGN_EXTEND + && GET_MODE (op) == DImode + && GET_MODE (XEXP (op, 0)) == SImode + && register_operand (XEXP (op, 0), SImode)) + return 1; + + return nonmemory_operand (op, mode); +} + +/* Like nonimmediate_operand, but when in 64 bit mode also accept a + sign extend of a 32 bit register, since the value is known to be + already sign extended. */ + +int +se_nonimmediate_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (TARGET_64BIT + && mode == DImode + && GET_CODE (op) == SIGN_EXTEND + && GET_MODE (op) == DImode + && GET_MODE (XEXP (op, 0)) == SImode + && register_operand (XEXP (op, 0), SImode)) + return 1; + + return nonimmediate_operand (op, mode); +} + +/* Accept any operand that can appear in a mips16 constant table + instruction. We can't use any of the standard operand functions + because for these instructions we accept values that are not + accepted by LEGITIMATE_CONSTANT, such as arbitrary SYMBOL_REFs. */ + +int +consttable_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return CONSTANT_P (op); +} + +/* Return nonzero if we split the address into high and low parts. */ + +/* ??? We should also handle reg+array somewhere. We get four + instructions currently, lui %hi/addui %lo/addui reg/lw. Better is + lui %hi/addui reg/lw %lo. Fixing GO_IF_LEGITIMATE_ADDRESS to accept + (plus (reg) (symbol_ref)) doesn't work because the SYMBOL_REF is broken + out of the address, then we have 4 instructions to combine. Perhaps + add a 3->2 define_split for combine. */ + +/* ??? We could also split a CONST_INT here if it is a large_int(). + However, it doesn't seem to be very useful to have %hi(constant). + We would be better off by doing the masking ourselves and then putting + the explicit high part of the constant in the RTL. This will give better + optimization. Also, %hi(constant) needs assembler changes to work. + There is already a define_split that does this. */ + +int +mips_check_split (address, mode) + rtx address; + enum machine_mode mode; +{ + /* ??? This is the same check used in simple_memory_operand. + We use it here because LO_SUM is not offsettable. */ + if (GET_MODE_SIZE (mode) > UNITS_PER_WORD) + return 0; + + if ((GET_CODE (address) == SYMBOL_REF && ! SYMBOL_REF_FLAG (address)) + || (GET_CODE (address) == CONST + && GET_CODE (XEXP (XEXP (address, 0), 0)) == SYMBOL_REF + && ! SYMBOL_REF_FLAG (XEXP (XEXP (address, 0), 0))) + || GET_CODE (address) == LABEL_REF) + return 1; + + return 0; +} + +/* We need a lot of little routines to check constant values on the + mips16. These are used to figure out how long the instruction will + be. It would be much better to do this using constraints, but + there aren't nearly enough letters available. */ + +static int +m16_check_op (op, low, high, mask) + rtx op; + int low; + int high; + int mask; +{ + return (GET_CODE (op) == CONST_INT + && INTVAL (op) >= low + && INTVAL (op) <= high + && (INTVAL (op) & mask) == 0); +} + +int +m16_uimm3_b (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return m16_check_op (op, 0x1, 0x8, 0); +} + +int +m16_simm4_1 (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return m16_check_op (op, - 0x8, 0x7, 0); +} + +int +m16_nsimm4_1 (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return m16_check_op (op, - 0x7, 0x8, 0); +} + +int +m16_simm5_1 (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return m16_check_op (op, - 0x10, 0xf, 0); +} + +int +m16_nsimm5_1 (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return m16_check_op (op, - 0xf, 0x10, 0); +} + +int +m16_uimm5_4 (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return m16_check_op (op, (- 0x10) << 2, 0xf << 2, 3); +} + +int +m16_nuimm5_4 (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return m16_check_op (op, (- 0xf) << 2, 0x10 << 2, 3); +} + +int +m16_simm8_1 (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return m16_check_op (op, - 0x80, 0x7f, 0); +} + +int +m16_nsimm8_1 (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return m16_check_op (op, - 0x7f, 0x80, 0); +} + +int +m16_uimm8_1 (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return m16_check_op (op, 0x0, 0xff, 0); +} + +int +m16_nuimm8_1 (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return m16_check_op (op, - 0xff, 0x0, 0); +} + +int +m16_uimm8_m1_1 (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return m16_check_op (op, - 0x1, 0xfe, 0); +} + +int +m16_uimm8_4 (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return m16_check_op (op, 0x0, 0xff << 2, 3); +} + +int +m16_nuimm8_4 (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return m16_check_op (op, (- 0xff) << 2, 0x0, 3); +} + +int +m16_simm8_8 (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return m16_check_op (op, (- 0x80) << 3, 0x7f << 3, 7); +} + +int +m16_nsimm8_8 (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return m16_check_op (op, (- 0x7f) << 3, 0x80 << 3, 7); +} + +/* References to the string table on the mips16 only use a small + offset if the function is small. See the comment in the SYMBOL_REF + case in simple_memory_operand. We can't check for LABEL_REF here, + because the offset is always large if the label is before the + referencing instruction. */ + +int +m16_usym8_4 (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + if (GET_CODE (op) == SYMBOL_REF + && SYMBOL_REF_FLAG (op) + && current_frame_info.insns_len > 0 + && XSTR (op, 0)[0] == '*' + && strncmp (XSTR (op, 0) + 1, LOCAL_LABEL_PREFIX, + sizeof LOCAL_LABEL_PREFIX - 1) == 0 + && (current_frame_info.insns_len + get_pool_size () + mips_string_length + < 4 * 0x100)) + { + struct string_constant *l; + + /* Make sure this symbol is on thelist of string constants to be + output for this function. It is possible that it has already + been output, in which case this requires a large offset. */ + for (l = string_constants; l != NULL; l = l->next) + if (strcmp (l->label, XSTR (op, 0)) == 0) + return 1; + } + + return 0; +} + +int +m16_usym5_4 (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + if (GET_CODE (op) == SYMBOL_REF + && SYMBOL_REF_FLAG (op) + && current_frame_info.insns_len > 0 + && XSTR (op, 0)[0] == '*' + && strncmp (XSTR (op, 0) + 1, LOCAL_LABEL_PREFIX, + sizeof LOCAL_LABEL_PREFIX - 1) == 0 + && (current_frame_info.insns_len + get_pool_size () + mips_string_length + < 4 * 0x20)) + { + struct string_constant *l; + + /* Make sure this symbol is on thelist of string constants to be + output for this function. It is possible that it has already + been output, in which case this requires a large offset. */ + for (l = string_constants; l != NULL; l = l->next) + if (strcmp (l->label, XSTR (op, 0)) == 0) + return 1; + } + + return 0; +} + +/* Returns an operand string for the given instruction's delay slot, + after updating filled delay slot statistics. + + We assume that operands[0] is the target register that is set. + + In order to check the next insn, most of this functionality is moved + to FINAL_PRESCAN_INSN, and we just set the global variables that + it needs. */ + +/* ??? This function no longer does anything useful, because final_prescan_insn + now will never emit a nop. */ + +char * +mips_fill_delay_slot (ret, type, operands, cur_insn) + char *ret; /* normal string to return */ + enum delay_type type; /* type of delay */ + rtx operands[]; /* operands to use */ + rtx cur_insn; /* current insn */ +{ + register rtx set_reg; + register enum machine_mode mode; + register rtx next_insn = cur_insn ? NEXT_INSN (cur_insn) : NULL_RTX; + register int num_nops; + + if (type == DELAY_LOAD || type == DELAY_FCMP) + num_nops = 1; + + else if (type == DELAY_HILO) + num_nops = 2; + + else + num_nops = 0; + + /* Make sure that we don't put nop's after labels. */ + next_insn = NEXT_INSN (cur_insn); + while (next_insn != 0 && GET_CODE (next_insn) == NOTE) + next_insn = NEXT_INSN (next_insn); + + dslots_load_total += num_nops; + if (TARGET_DEBUG_F_MODE + || !optimize + || type == DELAY_NONE + || operands == 0 + || cur_insn == 0 + || next_insn == 0 + || GET_CODE (next_insn) == CODE_LABEL + || (set_reg = operands[0]) == 0) + { + dslots_number_nops = 0; + mips_load_reg = 0; + mips_load_reg2 = 0; + mips_load_reg3 = 0; + mips_load_reg4 = 0; + return ret; + } + + set_reg = operands[0]; + if (set_reg == 0) + return ret; + + while (GET_CODE (set_reg) == SUBREG) + set_reg = SUBREG_REG (set_reg); + + mode = GET_MODE (set_reg); + dslots_number_nops = num_nops; + mips_load_reg = set_reg; + if (GET_MODE_SIZE (mode) + > (FP_REG_P (REGNO (set_reg)) ? UNITS_PER_FPREG : UNITS_PER_WORD)) + mips_load_reg2 = gen_rtx (REG, SImode, REGNO (set_reg) + 1); + else + mips_load_reg2 = 0; + + if (type == DELAY_HILO) + { + mips_load_reg3 = gen_rtx (REG, SImode, MD_REG_FIRST); + mips_load_reg4 = gen_rtx (REG, SImode, MD_REG_FIRST+1); + } + else + { + mips_load_reg3 = 0; + mips_load_reg4 = 0; + } + + return ret; +} + + +/* Determine whether a memory reference takes one (based off of the GP + pointer), two (normal), or three (label + reg) instructions, and bump the + appropriate counter for -mstats. */ + +void +mips_count_memory_refs (op, num) + rtx op; + int num; +{ + int additional = 0; + int n_words = 0; + rtx addr, plus0, plus1; + enum rtx_code code0, code1; + int looping; + + if (TARGET_DEBUG_B_MODE) + { + fprintf (stderr, "\n========== mips_count_memory_refs:\n"); + debug_rtx (op); + } + + /* Skip MEM if passed, otherwise handle movsi of address. */ + addr = (GET_CODE (op) != MEM) ? op : XEXP (op, 0); + + /* Loop, going through the address RTL. */ + do + { + looping = FALSE; + switch (GET_CODE (addr)) + { + case REG: + case CONST_INT: + case LO_SUM: + break; + + case PLUS: + plus0 = XEXP (addr, 0); + plus1 = XEXP (addr, 1); + code0 = GET_CODE (plus0); + code1 = GET_CODE (plus1); + + if (code0 == REG) + { + additional++; + addr = plus1; + looping = 1; + continue; + } + + if (code0 == CONST_INT) + { + addr = plus1; + looping = 1; + continue; + } + + if (code1 == REG) + { + additional++; + addr = plus0; + looping = 1; + continue; + } + + if (code1 == CONST_INT) + { + addr = plus0; + looping = 1; + continue; + } + + if (code0 == SYMBOL_REF || code0 == LABEL_REF || code0 == CONST) + { + addr = plus0; + looping = 1; + continue; + } + + if (code1 == SYMBOL_REF || code1 == LABEL_REF || code1 == CONST) + { + addr = plus1; + looping = 1; + continue; + } + + break; + + case LABEL_REF: + n_words = 2; /* always 2 words */ + break; + + case CONST: + addr = XEXP (addr, 0); + looping = 1; + continue; + + case SYMBOL_REF: + n_words = SYMBOL_REF_FLAG (addr) ? 1 : 2; + break; + + default: + break; + } + } + while (looping); + + if (n_words == 0) + return; + + n_words += additional; + if (n_words > 3) + n_words = 3; + + num_refs[n_words-1] += num; +} + + +/* Return RTL for the offset from the current function to the argument. + + ??? Which argument is this? */ + +rtx +embedded_pic_offset (x) + rtx x; +{ + if (embedded_pic_fnaddr_rtx == NULL) + { + rtx seq; + + embedded_pic_fnaddr_rtx = gen_reg_rtx (Pmode); + + /* Output code at function start to initialize the pseudo-reg. */ + /* ??? We used to do this in FINALIZE_PIC, but that does not work for + inline functions, because it is called after RTL for the function + has been copied. The pseudo-reg in embedded_pic_fnaddr_rtx however + does not get copied, and ends up not matching the rest of the RTL. + This solution works, but means that we get unnecessary code to + initialize this value every time a function is inlined into another + function. */ + start_sequence (); + emit_insn (gen_get_fnaddr (embedded_pic_fnaddr_rtx, + XEXP (DECL_RTL (current_function_decl), 0))); + seq = gen_sequence (); + end_sequence (); + push_topmost_sequence (); + emit_insn_after (seq, get_insns ()); + pop_topmost_sequence (); + } + + return gen_rtx (CONST, Pmode, + gen_rtx (MINUS, Pmode, x, + XEXP (DECL_RTL (current_function_decl), 0))); +} + +/* Return the appropriate instructions to move one operand to another. */ + +const char * +mips_move_1word (operands, insn, unsignedp) + rtx operands[]; + rtx insn; + int unsignedp; +{ + const char *ret = 0; + rtx op0 = operands[0]; + rtx op1 = operands[1]; + enum rtx_code code0 = GET_CODE (op0); + enum rtx_code code1 = GET_CODE (op1); + enum machine_mode mode = GET_MODE (op0); + int subreg_word0 = 0; + int subreg_word1 = 0; + enum delay_type delay = DELAY_NONE; + + while (code0 == SUBREG) + { + subreg_word0 += SUBREG_WORD (op0); + op0 = SUBREG_REG (op0); + code0 = GET_CODE (op0); + } + + while (code1 == SUBREG) + { + subreg_word1 += SUBREG_WORD (op1); + op1 = SUBREG_REG (op1); + code1 = GET_CODE (op1); + } + + /* For our purposes, a condition code mode is the same as SImode. */ + if (mode == CCmode) + mode = SImode; + + if (code0 == REG) + { + int regno0 = REGNO (op0) + subreg_word0; + + if (code1 == REG) + { + int regno1 = REGNO (op1) + subreg_word1; + + /* Just in case, don't do anything for assigning a register + to itself, unless we are filling a delay slot. */ + if (regno0 == regno1 && set_nomacro == 0) + ret = ""; + + else if (GP_REG_P (regno0)) + { + if (GP_REG_P (regno1)) + ret = "move\t%0,%1"; + + else if (MD_REG_P (regno1)) + { + delay = DELAY_HILO; + if (regno1 != HILO_REGNUM) + ret = "mf%1\t%0"; + else + ret = "mflo\t%0"; + } + + else if (ST_REG_P (regno1) && mips_isa >= 4) + ret = "li\t%0,1\n\tmovf\t%0,%.,%1"; + + else + { + delay = DELAY_LOAD; + if (FP_REG_P (regno1)) + ret = "mfc1\t%0,%1"; + + else if (regno1 == FPSW_REGNUM && mips_isa < 4) + ret = "cfc1\t%0,$31"; + } + } + + else if (FP_REG_P (regno0)) + { + if (GP_REG_P (regno1)) + { + delay = DELAY_LOAD; + ret = "mtc1\t%1,%0"; + } + + if (FP_REG_P (regno1)) + ret = "mov.s\t%0,%1"; + } + + else if (MD_REG_P (regno0)) + { + if (GP_REG_P (regno1)) + { + delay = DELAY_HILO; + if (regno0 != HILO_REGNUM && ! TARGET_MIPS16) + ret = "mt%0\t%1"; + } + } + + else if (regno0 == FPSW_REGNUM && mips_isa < 4) + { + if (GP_REG_P (regno1)) + { + delay = DELAY_LOAD; + ret = "ctc1\t%0,$31"; + } + } + } + + else if (code1 == MEM) + { + delay = DELAY_LOAD; + + if (TARGET_STATS) + mips_count_memory_refs (op1, 1); + + if (GP_REG_P (regno0)) + { + /* For loads, use the mode of the memory item, instead of the + target, so zero/sign extend can use this code as well. */ + switch (GET_MODE (op1)) + { + default: + break; + case SFmode: + ret = "lw\t%0,%1"; + break; + case SImode: + case CCmode: + ret = ((unsignedp && TARGET_64BIT) + ? "lwu\t%0,%1" + : "lw\t%0,%1"); + break; + case HImode: + ret = (unsignedp) ? "lhu\t%0,%1" : "lh\t%0,%1"; + break; + case QImode: + ret = (unsignedp) ? "lbu\t%0,%1" : "lb\t%0,%1"; + break; + } + } + + else if (FP_REG_P (regno0) && (mode == SImode || mode == SFmode)) + ret = "l.s\t%0,%1"; + + if (ret != (char *)0 && MEM_VOLATILE_P (op1)) + { + size_t i = strlen (ret); + if (i > sizeof (volatile_buffer) - sizeof ("%{%}")) + abort (); + + sprintf (volatile_buffer, "%%{%s%%}", ret); + ret = volatile_buffer; + } + } + + else if (code1 == CONST_INT + || (code1 == CONST_DOUBLE + && GET_MODE (op1) == VOIDmode)) + { + if (code1 == CONST_DOUBLE) + { + /* This can happen when storing constants into long long + bitfields. Just store the least significant word of + the value. */ + operands[1] = op1 = GEN_INT (CONST_DOUBLE_LOW (op1)); + } + + if (INTVAL (op1) == 0 && ! TARGET_MIPS16) + { + if (GP_REG_P (regno0)) + ret = "move\t%0,%z1"; + + else if (FP_REG_P (regno0)) + { + delay = DELAY_LOAD; + ret = "mtc1\t%z1,%0"; + } + + else if (MD_REG_P (regno0)) + { + delay = DELAY_HILO; + ret = "mt%0\t%."; + } + } + + else if (GP_REG_P (regno0)) + { + /* Don't use X format, because that will give out of + range numbers for 64 bit host and 32 bit target. */ + if (! TARGET_MIPS16) + ret = "li\t%0,%1\t\t\t# %X1"; + else + { + if (INTVAL (op1) >= 0 && INTVAL (op1) <= 0xffff) + ret = "li\t%0,%1"; + else if (INTVAL (op1) < 0 && INTVAL (op1) >= -0xffff) + ret = "li\t%0,%n1\n\tneg\t%0"; + } + } + } + + else if (code1 == CONST_DOUBLE && mode == SFmode) + { + if (op1 == CONST0_RTX (SFmode)) + { + if (GP_REG_P (regno0)) + ret = "move\t%0,%."; + + else if (FP_REG_P (regno0)) + { + delay = DELAY_LOAD; + ret = "mtc1\t%.,%0"; + } + } + + else + { + delay = DELAY_LOAD; + ret = "li.s\t%0,%1"; + } + } + + else if (code1 == LABEL_REF) + { + if (TARGET_STATS) + mips_count_memory_refs (op1, 1); + + ret = "la\t%0,%a1"; + } + + else if (code1 == SYMBOL_REF || code1 == CONST) + { + if (HALF_PIC_P () && CONSTANT_P (op1) && HALF_PIC_ADDRESS_P (op1)) + { + rtx offset = const0_rtx; + + if (GET_CODE (op1) == CONST) + op1 = eliminate_constant_term (XEXP (op1, 0), &offset); + + if (GET_CODE (op1) == SYMBOL_REF) + { + operands[2] = HALF_PIC_PTR (op1); + + if (TARGET_STATS) + mips_count_memory_refs (operands[2], 1); + + if (INTVAL (offset) == 0) + { + delay = DELAY_LOAD; + ret = (unsignedp && TARGET_64BIT + ? "lwu\t%0,%2" + : "lw\t%0,%2"); + } + else + { + dslots_load_total++; + operands[3] = offset; + if (unsignedp && TARGET_64BIT) + ret = (SMALL_INT (offset) + ? "lwu\t%0,%2%#\n\tadd\t%0,%0,%3" + : "lwu\t%0,%2%#\n\t%[li\t%@,%3\n\tadd\t%0,%0,%@%]"); + else + ret = (SMALL_INT (offset) + ? "lw\t%0,%2%#\n\tadd\t%0,%0,%3" + : "lw\t%0,%2%#\n\t%[li\t%@,%3\n\tadd\t%0,%0,%@%]"); + } + } + } + else if (TARGET_MIPS16 + && code1 == CONST + && GET_CODE (XEXP (op1, 0)) == REG + && REGNO (XEXP (op1, 0)) == GP_REG_FIRST + 28) + { + /* This case arises on the mips16; see + mips16_gp_pseudo_reg. */ + ret = "move\t%0,%+"; + } + else if (TARGET_MIPS16 + && code1 == SYMBOL_REF + && SYMBOL_REF_FLAG (op1) + && (XSTR (op1, 0)[0] != '*' + || strncmp (XSTR (op1, 0) + 1, + LOCAL_LABEL_PREFIX, + sizeof LOCAL_LABEL_PREFIX - 1) != 0)) + { + /* This can occur when reloading the address of a GP + relative symbol on the mips16. */ + ret = "move\t%0,%+\n\taddu\t%0,%%gprel(%a1)"; + } + else + { + if (TARGET_STATS) + mips_count_memory_refs (op1, 1); + + ret = "la\t%0,%a1"; + } + } + + else if (code1 == PLUS) + { + rtx add_op0 = XEXP (op1, 0); + rtx add_op1 = XEXP (op1, 1); + + if (GET_CODE (XEXP (op1, 1)) == REG + && GET_CODE (XEXP (op1, 0)) == CONST_INT) + add_op0 = XEXP (op1, 1), add_op1 = XEXP (op1, 0); + + operands[2] = add_op0; + operands[3] = add_op1; + ret = "add%:\t%0,%2,%3"; + } + + else if (code1 == HIGH) + { + operands[1] = XEXP (op1, 0); + ret = "lui\t%0,%%hi(%1)"; + } + } + + else if (code0 == MEM) + { + if (TARGET_STATS) + mips_count_memory_refs (op0, 1); + + if (code1 == REG) + { + int regno1 = REGNO (op1) + subreg_word1; + + if (GP_REG_P (regno1)) + { + switch (mode) + { + case SFmode: ret = "sw\t%1,%0"; break; + case SImode: ret = "sw\t%1,%0"; break; + case HImode: ret = "sh\t%1,%0"; break; + case QImode: ret = "sb\t%1,%0"; break; + default: break; + } + } + + else if (FP_REG_P (regno1) && (mode == SImode || mode == SFmode)) + ret = "s.s\t%1,%0"; + } + + else if (code1 == CONST_INT && INTVAL (op1) == 0) + { + switch (mode) + { + case SFmode: ret = "sw\t%z1,%0"; break; + case SImode: ret = "sw\t%z1,%0"; break; + case HImode: ret = "sh\t%z1,%0"; break; + case QImode: ret = "sb\t%z1,%0"; break; + default: break; + } + } + + else if (code1 == CONST_DOUBLE && op1 == CONST0_RTX (mode)) + { + switch (mode) + { + case SFmode: ret = "sw\t%.,%0"; break; + case SImode: ret = "sw\t%.,%0"; break; + case HImode: ret = "sh\t%.,%0"; break; + case QImode: ret = "sb\t%.,%0"; break; + default: break; + } + } + + if (ret != 0 && MEM_VOLATILE_P (op0)) + { + size_t i = strlen (ret); + + if (i > sizeof (volatile_buffer) - sizeof ("%{%}")) + abort (); + + sprintf (volatile_buffer, "%%{%s%%}", ret); + ret = volatile_buffer; + } + } + + if (ret == 0) + { + abort_with_insn (insn, "Bad move"); + return 0; + } + + if (delay != DELAY_NONE) + return mips_fill_delay_slot (ret, delay, operands, insn); + + return ret; +} + + +/* Return the appropriate instructions to move 2 words */ + +const char * +mips_move_2words (operands, insn) + rtx operands[]; + rtx insn; +{ + const char *ret = 0; + rtx op0 = operands[0]; + rtx op1 = operands[1]; + enum rtx_code code0 = GET_CODE (operands[0]); + enum rtx_code code1 = GET_CODE (operands[1]); + int subreg_word0 = 0; + int subreg_word1 = 0; + enum delay_type delay = DELAY_NONE; + + while (code0 == SUBREG) + { + subreg_word0 += SUBREG_WORD (op0); + op0 = SUBREG_REG (op0); + code0 = GET_CODE (op0); + } + + if (code1 == SIGN_EXTEND) + { + op1 = XEXP (op1, 0); + code1 = GET_CODE (op1); + } + + while (code1 == SUBREG) + { + subreg_word1 += SUBREG_WORD (op1); + op1 = SUBREG_REG (op1); + code1 = GET_CODE (op1); + } + + /* Sanity check. */ + if (GET_CODE (operands[1]) == SIGN_EXTEND + && code1 != REG + && code1 != CONST_INT + /* The following three can happen as the result of a questionable + cast. */ + && code1 != LABEL_REF + && code1 != SYMBOL_REF + && code1 != CONST) + abort (); + + if (code0 == REG) + { + int regno0 = REGNO (op0) + subreg_word0; + + if (code1 == REG) + { + int regno1 = REGNO (op1) + subreg_word1; + + /* Just in case, don't do anything for assigning a register + to itself, unless we are filling a delay slot. */ + if (regno0 == regno1 && set_nomacro == 0) + ret = ""; + + else if (FP_REG_P (regno0)) + { + if (FP_REG_P (regno1)) + ret = "mov.d\t%0,%1"; + + else + { + delay = DELAY_LOAD; + if (TARGET_FLOAT64) + { + if (!TARGET_64BIT) + abort_with_insn (insn, "Bad move"); + +#ifdef TARGET_FP_CALL_32 + if (FP_CALL_GP_REG_P (regno1)) + ret = "dsll\t%1,32\n\tor\t%1,%D1\n\tdmtc1\t%1,%0"; + else +#endif + ret = "dmtc1\t%1,%0"; + } + else + ret = "mtc1\t%L1,%0\n\tmtc1\t%M1,%D0"; + } + } + + else if (FP_REG_P (regno1)) + { + delay = DELAY_LOAD; + if (TARGET_FLOAT64) + { + if (!TARGET_64BIT) + abort_with_insn (insn, "Bad move"); + +#ifdef TARGET_FP_CALL_32 + if (FP_CALL_GP_REG_P (regno0)) + ret = "dmfc1\t%0,%1\n\tmfc1\t%D0,%1\n\tdsrl\t%0,32"; + else +#endif + ret = "dmfc1\t%0,%1"; + } + else + ret = "mfc1\t%L0,%1\n\tmfc1\t%M0,%D1"; + } + + else if (MD_REG_P (regno0) && GP_REG_P (regno1) && !TARGET_MIPS16) + { + delay = DELAY_HILO; + if (TARGET_64BIT) + { + if (regno0 != HILO_REGNUM) + ret = "mt%0\t%1"; + else if (regno1 == 0) + ret = "mtlo\t%.\n\tmthi\t%."; + } + else + ret = "mthi\t%M1\n\tmtlo\t%L1"; + } + + else if (GP_REG_P (regno0) && MD_REG_P (regno1)) + { + delay = DELAY_HILO; + if (TARGET_64BIT) + { + if (regno1 != HILO_REGNUM) + ret = "mf%1\t%0"; + } + else + ret = "mfhi\t%M0\n\tmflo\t%L0"; + } + + else if (TARGET_64BIT) + ret = "move\t%0,%1"; + + else if (regno0 != (regno1+1)) + ret = "move\t%0,%1\n\tmove\t%D0,%D1"; + + else + ret = "move\t%D0,%D1\n\tmove\t%0,%1"; + } + + else if (code1 == CONST_DOUBLE) + { + /* Move zero from $0 unless !TARGET_64BIT and recipient + is 64-bit fp reg, in which case generate a constant. */ + if (op1 != CONST0_RTX (GET_MODE (op1)) + || (TARGET_FLOAT64 && !TARGET_64BIT && FP_REG_P (regno0))) + { + if (GET_MODE (op1) == DFmode) + { + delay = DELAY_LOAD; + +#ifdef TARGET_FP_CALL_32 + if (FP_CALL_GP_REG_P (regno0)) + { + if (TARGET_FLOAT64 && !TARGET_64BIT) + { + split_double (op1, operands + 2, operands + 3); + ret = "li\t%0,%2\n\tli\t%D0,%3"; + } + else + ret = "li.d\t%0,%1\n\tdsll\t%D0,%0,32\n\tdsrl\t%D0,32\n\tdsrl\t%0,32"; + } + else +#endif + ret = "li.d\t%0,%1"; + } + + else if (TARGET_64BIT) + { + if (! TARGET_MIPS16) + ret = "dli\t%0,%1"; + } + + else + { + split_double (op1, operands + 2, operands + 3); + ret = "li\t%0,%2\n\tli\t%D0,%3"; + } + } + + else + { + if (GP_REG_P (regno0)) + ret = (TARGET_64BIT +#ifdef TARGET_FP_CALL_32 + && ! FP_CALL_GP_REG_P (regno0) +#endif + ? "move\t%0,%." + : "move\t%0,%.\n\tmove\t%D0,%."); + + else if (FP_REG_P (regno0)) + { + delay = DELAY_LOAD; + ret = (TARGET_64BIT + ? "dmtc1\t%.,%0" + : "mtc1\t%.,%0\n\tmtc1\t%.,%D0"); + } + } + } + + else if (code1 == CONST_INT && INTVAL (op1) == 0 && ! TARGET_MIPS16) + { + if (GP_REG_P (regno0)) + ret = (TARGET_64BIT + ? "move\t%0,%." + : "move\t%0,%.\n\tmove\t%D0,%."); + + else if (FP_REG_P (regno0)) + { + delay = DELAY_LOAD; + ret = (TARGET_64BIT + ? "dmtc1\t%.,%0" + : (TARGET_FLOAT64 + ? "li.d\t%0,%1" + : "mtc1\t%.,%0\n\tmtc1\t%.,%D0")); + } + else if (MD_REG_P (regno0)) + { + delay = DELAY_HILO; + ret = (regno0 == HILO_REGNUM + ? "mtlo\t%.\n\tmthi\t%." + : "mt%0\t%.\n"); + } + } + + else if (code1 == CONST_INT && GET_MODE (op0) == DImode + && GP_REG_P (regno0)) + { + if (TARGET_64BIT) + { + if (TARGET_MIPS16) + { + if (INTVAL (op1) >= 0 && INTVAL (op1) <= 0xffff) + ret = "li\t%0,%1"; + else if (INTVAL (op1) < 0 && INTVAL (op1) >= -0xffff) + ret = "li\t%0,%n1\n\tneg\t%0"; + } + else if (GET_CODE (operands[1]) == SIGN_EXTEND) + ret = "li\t%0,%1\t\t# %X1"; + else if (HOST_BITS_PER_WIDE_INT < 64) + /* We can't use 'X' for negative numbers, because then we won't + get the right value for the upper 32 bits. */ + ret = (INTVAL (op1) < 0 + ? "dli\t%0,%1\t\t\t# %X1" + : "dli\t%0,%X1\t\t# %1"); + else + /* We must use 'X', because otherwise LONG_MIN will print as + a number that the assembler won't accept. */ + ret = "dli\t%0,%X1\t\t# %1"; + } + else if (HOST_BITS_PER_WIDE_INT < 64) + { + operands[2] = GEN_INT (INTVAL (operands[1]) >= 0 ? 0 : -1); + if (TARGET_MIPS16) + { + if (INTVAL (op1) >= 0 && INTVAL (op1) <= 0xffff) + ret = "li\t%M0,%2\n\tli\t%L0,%1"; + else if (INTVAL (op1) < 0 && INTVAL (op1) >= -0xffff) + { + operands[2] = GEN_INT (1); + ret = "li\t%M0,%2\n\tneg\t%M0\n\tli\t%L0,%n1\n\tneg\t%L0"; + } + } + else + ret = "li\t%M0,%2\n\tli\t%L0,%1"; + } + else + { + /* We use multiple shifts here, to avoid warnings about out + of range shifts on 32 bit hosts. */ + operands[2] = GEN_INT (INTVAL (operands[1]) >> 16 >> 16); + operands[1] + = GEN_INT (INTVAL (operands[1]) << 16 << 16 >> 16 >> 16); + ret = "li\t%M0,%2\n\tli\t%L0,%1"; + } + } + + else if (code1 == MEM) + { + delay = DELAY_LOAD; + + if (TARGET_STATS) + mips_count_memory_refs (op1, 2); + + if (FP_REG_P (regno0)) + ret = "l.d\t%0,%1"; + + else if (TARGET_64BIT) + { + +#ifdef TARGET_FP_CALL_32 + if (FP_CALL_GP_REG_P (regno0)) + ret = (double_memory_operand (op1, GET_MODE (op1)) + ? "lwu\t%0,%1\n\tlwu\t%D0,4+%1" + : "ld\t%0,%1\n\tdsll\t%D0,%0,32\n\tdsrl\t%D0,32\n\tdsrl\t%0,32"); + else +#endif + ret = "ld\t%0,%1"; + } + + else if (double_memory_operand (op1, GET_MODE (op1))) + { + operands[2] = adj_offsettable_operand (op1, 4); + ret = (reg_mentioned_p (op0, op1) + ? "lw\t%D0,%2\n\tlw\t%0,%1" + : "lw\t%0,%1\n\tlw\t%D0,%2"); + } + + if (ret != 0 && MEM_VOLATILE_P (op1)) + { + size_t i = strlen (ret); + + if (i > sizeof (volatile_buffer) - sizeof ("%{%}")) + abort (); + + sprintf (volatile_buffer, "%%{%s%%}", ret); + ret = volatile_buffer; + } + } + + else if (code1 == LABEL_REF) + { + if (TARGET_STATS) + mips_count_memory_refs (op1, 2); + + if (GET_CODE (operands[1]) == SIGN_EXTEND) + /* We deliberately remove the 'a' from '%1', so that we don't + have to add SIGN_EXTEND support to print_operand_address. + print_operand will just call print_operand_address in this + case, so there is no problem. */ + ret = "la\t%0,%1"; + else + ret = "dla\t%0,%a1"; + } + else if (code1 == SYMBOL_REF || code1 == CONST) + { + if (TARGET_MIPS16 + && code1 == CONST + && GET_CODE (XEXP (op1, 0)) == REG + && REGNO (XEXP (op1, 0)) == GP_REG_FIRST + 28) + { + /* This case arises on the mips16; see + mips16_gp_pseudo_reg. */ + ret = "move\t%0,%+"; + } + else if (TARGET_MIPS16 + && code1 == SYMBOL_REF + && SYMBOL_REF_FLAG (op1) + && (XSTR (op1, 0)[0] != '*' + || strncmp (XSTR (op1, 0) + 1, + LOCAL_LABEL_PREFIX, + sizeof LOCAL_LABEL_PREFIX - 1) != 0)) + { + /* This can occur when reloading the address of a GP + relative symbol on the mips16. */ + ret = "move\t%0,%+\n\taddu\t%0,%%gprel(%a1)"; + } + else + { + if (TARGET_STATS) + mips_count_memory_refs (op1, 2); + + if (GET_CODE (operands[1]) == SIGN_EXTEND) + /* We deliberately remove the 'a' from '%1', so that we don't + have to add SIGN_EXTEND support to print_operand_address. + print_operand will just call print_operand_address in this + case, so there is no problem. */ + ret = "la\t%0,%1"; + else + ret = "dla\t%0,%a1"; + } + } + } + + else if (code0 == MEM) + { + if (code1 == REG) + { + int regno1 = REGNO (op1) + subreg_word1; + + if (FP_REG_P (regno1)) + ret = "s.d\t%1,%0"; + + else if (TARGET_64BIT) + { + +#ifdef TARGET_FP_CALL_32 + if (FP_CALL_GP_REG_P (regno1)) + ret = "dsll\t%1,32\n\tor\t%1,%D1\n\tsd\t%1,%0"; + else +#endif + ret = "sd\t%1,%0"; + } + + else if (double_memory_operand (op0, GET_MODE (op0))) + { + operands[2] = adj_offsettable_operand (op0, 4); + ret = "sw\t%1,%0\n\tsw\t%D1,%2"; + } + } + + else if (((code1 == CONST_INT && INTVAL (op1) == 0) + || (code1 == CONST_DOUBLE + && op1 == CONST0_RTX (GET_MODE (op1)))) + && (TARGET_64BIT + || double_memory_operand (op0, GET_MODE (op0)))) + { + if (TARGET_64BIT) + ret = "sd\t%.,%0"; + else + { + operands[2] = adj_offsettable_operand (op0, 4); + ret = "sw\t%.,%0\n\tsw\t%.,%2"; + } + } + + if (TARGET_STATS) + mips_count_memory_refs (op0, 2); + + if (ret != 0 && MEM_VOLATILE_P (op0)) + { + size_t i = strlen (ret); + + if (i > sizeof (volatile_buffer) - sizeof ("%{%}")) + abort (); + + sprintf (volatile_buffer, "%%{%s%%}", ret); + ret = volatile_buffer; + } + } + + if (ret == 0) + { + abort_with_insn (insn, "Bad move"); + return 0; + } + + if (delay != DELAY_NONE) + return mips_fill_delay_slot (ret, delay, operands, insn); + + return ret; +} + +/* Provide the costs of an addressing mode that contains ADDR. + If ADDR is not a valid address, its cost is irrelevant. */ + +int +mips_address_cost (addr) + rtx addr; +{ + switch (GET_CODE (addr)) + { + case LO_SUM: + return 1; + + case LABEL_REF: + return 2; + + case CONST: + { + rtx offset = const0_rtx; + addr = eliminate_constant_term (XEXP (addr, 0), &offset); + if (GET_CODE (addr) == LABEL_REF) + return 2; + + if (GET_CODE (addr) != SYMBOL_REF) + return 4; + + if (! SMALL_INT (offset)) + return 2; + } + + /* ... fall through ... */ + + case SYMBOL_REF: + return SYMBOL_REF_FLAG (addr) ? 1 : 2; + + case PLUS: + { + register rtx plus0 = XEXP (addr, 0); + register rtx plus1 = XEXP (addr, 1); + + if (GET_CODE (plus0) != REG && GET_CODE (plus1) == REG) + plus0 = XEXP (addr, 1), plus1 = XEXP (addr, 0); + + if (GET_CODE (plus0) != REG) + break; + + switch (GET_CODE (plus1)) + { + case CONST_INT: + return SMALL_INT (plus1) ? 1 : 2; + + case CONST: + case SYMBOL_REF: + case LABEL_REF: + case HIGH: + case LO_SUM: + return mips_address_cost (plus1) + 1; + + default: + break; + } + } + + default: + break; + } + + return 4; +} + +/* Return nonzero 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; +} + +/* Make normal rtx_code into something we can index from an array */ + +static enum internal_test +map_test_to_internal_test (test_code) + enum rtx_code test_code; +{ + enum internal_test test = ITEST_MAX; + + switch (test_code) + { + case EQ: test = ITEST_EQ; break; + case NE: test = ITEST_NE; break; + case GT: test = ITEST_GT; break; + case GE: test = ITEST_GE; break; + case LT: test = ITEST_LT; break; + case LE: test = ITEST_LE; break; + case GTU: test = ITEST_GTU; break; + case GEU: test = ITEST_GEU; break; + case LTU: test = ITEST_LTU; break; + case LEU: test = ITEST_LEU; break; + default: break; + } + + return test; +} + + +/* Generate the code to compare two integer values. The return value is: + (reg:SI xx) The pseudo register the comparison is in + 0 No register, generate a simple branch. + + ??? This is called with result nonzero by the Scond patterns in + mips.md. These patterns are called with a target in the mode of + the Scond instruction pattern. Since this must be a constant, we + must use SImode. This means that if RESULT is non-zero, it will + always be an SImode register, even if TARGET_64BIT is true. We + cope with this by calling convert_move rather than emit_move_insn. + This will sometimes lead to an unnecessary extension of the result; + for example: + + long long + foo (long long i) + { + return i < 5; + } + + */ + +rtx +gen_int_relational (test_code, result, cmp0, cmp1, p_invert) + enum rtx_code test_code; /* relational test (EQ, etc) */ + rtx result; /* result to store comp. or 0 if branch */ + rtx cmp0; /* first operand to compare */ + rtx cmp1; /* second operand to compare */ + int *p_invert; /* NULL or ptr to hold whether branch needs */ + /* to reverse its test */ +{ + struct cmp_info + { + enum rtx_code test_code; /* code to use in instruction (LT vs. LTU) */ + int const_low; /* low bound of constant we can accept */ + int const_high; /* high bound of constant we can accept */ + int const_add; /* constant to add (convert LE -> LT) */ + int reverse_regs; /* reverse registers in test */ + int invert_const; /* != 0 if invert value if cmp1 is constant */ + int invert_reg; /* != 0 if invert value if cmp1 is register */ + int unsignedp; /* != 0 for unsigned comparisons. */ + }; + + static struct cmp_info info[ (int)ITEST_MAX ] = { + + { XOR, 0, 65535, 0, 0, 0, 0, 0 }, /* EQ */ + { XOR, 0, 65535, 0, 0, 1, 1, 0 }, /* NE */ + { LT, -32769, 32766, 1, 1, 1, 0, 0 }, /* GT */ + { LT, -32768, 32767, 0, 0, 1, 1, 0 }, /* GE */ + { LT, -32768, 32767, 0, 0, 0, 0, 0 }, /* LT */ + { LT, -32769, 32766, 1, 1, 0, 1, 0 }, /* LE */ + { LTU, -32769, 32766, 1, 1, 1, 0, 1 }, /* GTU */ + { LTU, -32768, 32767, 0, 0, 1, 1, 1 }, /* GEU */ + { LTU, -32768, 32767, 0, 0, 0, 0, 1 }, /* LTU */ + { LTU, -32769, 32766, 1, 1, 0, 1, 1 }, /* LEU */ + }; + + enum internal_test test; + enum machine_mode mode; + struct cmp_info *p_info; + int branch_p; + int eqne_p; + int invert; + rtx reg; + rtx reg2; + + test = map_test_to_internal_test (test_code); + if (test == ITEST_MAX) + abort (); + + p_info = &info[(int) test]; + eqne_p = (p_info->test_code == XOR); + + mode = GET_MODE (cmp0); + if (mode == VOIDmode) + mode = GET_MODE (cmp1); + + /* Eliminate simple branches */ + branch_p = (result == 0); + if (branch_p) + { + if (GET_CODE (cmp0) == REG || GET_CODE (cmp0) == SUBREG) + { + /* Comparisons against zero are simple branches */ + if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0 + && (! TARGET_MIPS16 || eqne_p)) + return 0; + + /* Test for beq/bne. */ + if (eqne_p && ! TARGET_MIPS16) + return 0; + } + + /* allocate a pseudo to calculate the value in. */ + result = gen_reg_rtx (mode); + } + + /* Make sure we can handle any constants given to us. */ + if (GET_CODE (cmp0) == CONST_INT) + cmp0 = force_reg (mode, cmp0); + + if (GET_CODE (cmp1) == CONST_INT) + { + HOST_WIDE_INT value = INTVAL (cmp1); + + if (value < p_info->const_low + || value > p_info->const_high + /* ??? Why? And why wasn't the similar code below modified too? */ + || (TARGET_64BIT + && HOST_BITS_PER_WIDE_INT < 64 + && p_info->const_add != 0 + && ((p_info->unsignedp + ? ((unsigned HOST_WIDE_INT) (value + p_info->const_add) + > (unsigned HOST_WIDE_INT) INTVAL (cmp1)) + : (value + p_info->const_add) > INTVAL (cmp1)) + != (p_info->const_add > 0)))) + cmp1 = force_reg (mode, cmp1); + } + + /* See if we need to invert the result. */ + invert = (GET_CODE (cmp1) == CONST_INT + ? p_info->invert_const : p_info->invert_reg); + + if (p_invert != (int *)0) + { + *p_invert = invert; + invert = 0; + } + + /* Comparison to constants, may involve adding 1 to change a LT into LE. + Comparison between two registers, may involve switching operands. */ + if (GET_CODE (cmp1) == CONST_INT) + { + if (p_info->const_add != 0) + { + HOST_WIDE_INT new = INTVAL (cmp1) + p_info->const_add; + + /* If modification of cmp1 caused overflow, + we would get the wrong answer if we follow the usual path; + thus, x > 0xffffffffU would turn into x > 0U. */ + if ((p_info->unsignedp + ? (unsigned HOST_WIDE_INT) new > + (unsigned HOST_WIDE_INT) INTVAL (cmp1) + : new > INTVAL (cmp1)) + != (p_info->const_add > 0)) + { + /* This test is always true, but if INVERT is true then + the result of the test needs to be inverted so 0 should + be returned instead. */ + emit_move_insn (result, invert ? const0_rtx : const_true_rtx); + return result; + } + else + cmp1 = GEN_INT (new); + } + } + + else if (p_info->reverse_regs) + { + rtx temp = cmp0; + cmp0 = cmp1; + cmp1 = temp; + } + + if (test == ITEST_NE && GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0) + reg = cmp0; + else + { + reg = (invert || eqne_p) ? gen_reg_rtx (mode) : result; + convert_move (reg, gen_rtx (p_info->test_code, mode, cmp0, cmp1), 0); + } + + if (test == ITEST_NE) + { + if (! TARGET_MIPS16) + { + convert_move (result, gen_rtx (GTU, mode, reg, const0_rtx), 0); + invert = 0; + } + else + { + reg2 = invert ? gen_reg_rtx (mode) : result; + convert_move (reg2, gen_rtx (LTU, mode, reg, const1_rtx), 0); + reg = reg2; + } + } + + else if (test == ITEST_EQ) + { + reg2 = invert ? gen_reg_rtx (mode) : result; + convert_move (reg2, gen_rtx (LTU, mode, reg, const1_rtx), 0); + reg = reg2; + } + + if (invert) + { + rtx one; + + if (! TARGET_MIPS16) + one = const1_rtx; + else + { + /* The value is in $24. Copy it to another register, so + that reload doesn't think it needs to store the $24 and + the input to the XOR in the same location. */ + reg2 = gen_reg_rtx (mode); + emit_move_insn (reg2, reg); + reg = reg2; + one = force_reg (mode, const1_rtx); + } + convert_move (result, gen_rtx (XOR, mode, reg, one), 0); + } + + return result; +} + +/* Emit the common code for doing conditional branches. + operand[0] is the label to jump to. + The comparison operands are saved away by cmp{si,di,sf,df}. */ + +void +gen_conditional_branch (operands, test_code) + rtx operands[]; + enum rtx_code test_code; +{ + enum cmp_type type = branch_type; + rtx cmp0 = branch_cmp[0]; + rtx cmp1 = branch_cmp[1]; + enum machine_mode mode; + rtx reg; + int invert; + rtx label1, label2; + + switch (type) + { + case CMP_SI: + case CMP_DI: + mode = type == CMP_SI ? SImode : DImode; + invert = 0; + reg = gen_int_relational (test_code, NULL_RTX, cmp0, cmp1, &invert); + + if (reg) + { + cmp0 = reg; + cmp1 = const0_rtx; + test_code = NE; + } + else if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) != 0) + /* We don't want to build a comparison against a non-zero + constant. */ + cmp1 = force_reg (mode, cmp1); + + break; + + case CMP_SF: + case CMP_DF: + if (mips_isa < 4) + reg = gen_rtx (REG, CCmode, FPSW_REGNUM); + else + reg = gen_reg_rtx (CCmode); + + /* For cmp0 != cmp1, build cmp0 == cmp1, and test for result == + 0 in the instruction built below. The MIPS FPU handles + inequality testing by testing for equality and looking for a + false result. */ + emit_insn (gen_rtx (SET, VOIDmode, reg, + gen_rtx (test_code == NE ? EQ : test_code, + CCmode, cmp0, cmp1))); + + test_code = test_code == NE ? EQ : NE; + mode = CCmode; + cmp0 = reg; + cmp1 = const0_rtx; + invert = 0; + break; + + default: + abort_with_insn (gen_rtx (test_code, VOIDmode, cmp0, cmp1), "bad test"); + } + + /* Generate the branch. */ + + label1 = gen_rtx (LABEL_REF, VOIDmode, operands[0]); + label2 = pc_rtx; + + if (invert) + { + label2 = label1; + label1 = pc_rtx; + } + + emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, + gen_rtx (IF_THEN_ELSE, VOIDmode, + gen_rtx (test_code, mode, cmp0, cmp1), + label1, label2))); +} + +/* Emit the common code for conditional moves. OPERANDS is the array + of operands passed to the conditional move defined_expand. */ + +void +gen_conditional_move (operands) + rtx *operands; +{ + rtx op0 = branch_cmp[0]; + rtx op1 = branch_cmp[1]; + enum machine_mode mode = GET_MODE (branch_cmp[0]); + enum rtx_code cmp_code = GET_CODE (operands[1]); + enum rtx_code move_code = NE; + enum machine_mode op_mode = GET_MODE (operands[0]); + enum machine_mode cmp_mode; + rtx cmp_reg; + + if (GET_MODE_CLASS (mode) != MODE_FLOAT) + { + switch (cmp_code) + { + case EQ: + cmp_code = XOR; + move_code = EQ; + break; + case NE: + cmp_code = XOR; + break; + case LT: + break; + case GE: + cmp_code = LT; + move_code = EQ; + break; + case GT: + cmp_code = LT; + op0 = force_reg (mode, branch_cmp[1]); + op1 = branch_cmp[0]; + break; + case LE: + cmp_code = LT; + op0 = force_reg (mode, branch_cmp[1]); + op1 = branch_cmp[0]; + move_code = EQ; + break; + case LTU: + break; + case GEU: + cmp_code = LTU; + move_code = EQ; + break; + case GTU: + cmp_code = LTU; + op0 = force_reg (mode, branch_cmp[1]); + op1 = branch_cmp[0]; + break; + case LEU: + cmp_code = LTU; + op0 = force_reg (mode, branch_cmp[1]); + op1 = branch_cmp[0]; + move_code = EQ; + break; + default: + abort (); + } + } + else if (cmp_code == NE) + cmp_code = EQ, move_code = EQ; + + if (mode == SImode || mode == DImode) + cmp_mode = mode; + else if (mode == SFmode || mode == DFmode) + cmp_mode = CCmode; + else + abort (); + + cmp_reg = gen_reg_rtx (cmp_mode); + emit_insn (gen_rtx (SET, cmp_mode, cmp_reg, + gen_rtx (cmp_code, cmp_mode, op0, op1))); + + emit_insn (gen_rtx (SET, op_mode, operands[0], + gen_rtx (IF_THEN_ELSE, op_mode, + gen_rtx (move_code, VOIDmode, + cmp_reg, CONST0_RTX (SImode)), + operands[2], operands[3]))); +} + +/* Write a loop to move a constant number of bytes. + Generate load/stores as follows: + + do { + temp1 = src[0]; + temp2 = src[1]; + ... + temp<last> = src[MAX_MOVE_REGS-1]; + dest[0] = temp1; + dest[1] = temp2; + ... + dest[MAX_MOVE_REGS-1] = temp<last>; + src += MAX_MOVE_REGS; + dest += MAX_MOVE_REGS; + } while (src != final); + + This way, no NOP's are needed, and only MAX_MOVE_REGS+3 temp + registers are needed. + + Aligned moves move MAX_MOVE_REGS*4 bytes every (2*MAX_MOVE_REGS)+3 + cycles, unaligned moves move MAX_MOVE_REGS*4 bytes every + (4*MAX_MOVE_REGS)+3 cycles, assuming no cache misses. */ + +#define MAX_MOVE_REGS 4 +#define MAX_MOVE_BYTES (MAX_MOVE_REGS * UNITS_PER_WORD) + +static void +block_move_loop (dest_reg, src_reg, bytes, align, orig_dest, orig_src) + rtx dest_reg; /* register holding destination address */ + rtx src_reg; /* register holding source address */ + int bytes; /* # bytes to move */ + int align; /* alignment */ + rtx orig_dest; /* original dest for change_address */ + rtx orig_src; /* original source for making a reg note */ +{ + rtx dest_mem = change_address (orig_dest, BLKmode, dest_reg); + rtx src_mem = change_address (orig_src, BLKmode, src_reg); + rtx align_rtx = GEN_INT (align); + rtx label; + rtx final_src; + rtx bytes_rtx; + int leftover; + + if (bytes < 2 * MAX_MOVE_BYTES) + abort (); + + leftover = bytes % MAX_MOVE_BYTES; + bytes -= leftover; + + label = gen_label_rtx (); + final_src = gen_reg_rtx (Pmode); + bytes_rtx = GEN_INT (bytes); + + if (bytes > 0x7fff) + { + if (Pmode == DImode) + { + emit_insn (gen_movdi (final_src, bytes_rtx)); + emit_insn (gen_adddi3 (final_src, final_src, src_reg)); + } + else + { + emit_insn (gen_movsi (final_src, bytes_rtx)); + emit_insn (gen_addsi3 (final_src, final_src, src_reg)); + } + } + else + { + if (Pmode == DImode) + emit_insn (gen_adddi3 (final_src, src_reg, bytes_rtx)); + else + emit_insn (gen_addsi3 (final_src, src_reg, bytes_rtx)); + } + + emit_label (label); + + bytes_rtx = GEN_INT (MAX_MOVE_BYTES); + emit_insn (gen_movstrsi_internal (dest_mem, src_mem, bytes_rtx, align_rtx)); + + if (Pmode == DImode) + { + emit_insn (gen_adddi3 (src_reg, src_reg, bytes_rtx)); + emit_insn (gen_adddi3 (dest_reg, dest_reg, bytes_rtx)); + emit_insn (gen_cmpdi (src_reg, final_src)); + } + else + { + emit_insn (gen_addsi3 (src_reg, src_reg, bytes_rtx)); + emit_insn (gen_addsi3 (dest_reg, dest_reg, bytes_rtx)); + emit_insn (gen_cmpsi (src_reg, final_src)); + } + + emit_jump_insn (gen_bne (label)); + + if (leftover) + emit_insn (gen_movstrsi_internal (dest_mem, src_mem, GEN_INT (leftover), + align_rtx)); +} + +/* Use a library function to move some bytes. */ + +static void +block_move_call (dest_reg, src_reg, bytes_rtx) + rtx dest_reg; + rtx src_reg; + rtx bytes_rtx; +{ + /* We want to pass the size as Pmode, which will normally be SImode + but will be DImode if we are using 64 bit longs and pointers. */ + if (GET_MODE (bytes_rtx) != VOIDmode + && GET_MODE (bytes_rtx) != Pmode) + bytes_rtx = convert_to_mode (Pmode, bytes_rtx, 1); + +#ifdef TARGET_MEM_FUNCTIONS + emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "memcpy"), 0, + VOIDmode, 3, dest_reg, Pmode, src_reg, Pmode, + convert_to_mode (TYPE_MODE (sizetype), bytes_rtx, + TREE_UNSIGNED (sizetype)), + TYPE_MODE (sizetype)); +#else + emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "bcopy"), 0, + VOIDmode, 3, src_reg, Pmode, dest_reg, Pmode, + convert_to_mode (TYPE_MODE (integer_type_node), bytes_rtx, + TREE_UNSIGNED (integer_type_node)), + TYPE_MODE (integer_type_node)); +#endif +} + +/* Expand string/block move operations. + + operands[0] is the pointer to the destination. + operands[1] is the pointer to the source. + operands[2] is the number of bytes to move. + operands[3] is the alignment. */ + +void +expand_block_move (operands) + rtx operands[]; +{ + rtx bytes_rtx = operands[2]; + rtx align_rtx = operands[3]; + int constp = GET_CODE (bytes_rtx) == CONST_INT; + HOST_WIDE_INT bytes = constp ? INTVAL (bytes_rtx) : 0; + int align = INTVAL (align_rtx); + rtx orig_src = operands[1]; + rtx orig_dest = operands[0]; + rtx src_reg; + rtx dest_reg; + + if (constp && bytes <= 0) + return; + + if (align > UNITS_PER_WORD) + align = UNITS_PER_WORD; + + /* Move the address into scratch registers. */ + dest_reg = copy_addr_to_reg (XEXP (orig_dest, 0)); + src_reg = copy_addr_to_reg (XEXP (orig_src, 0)); + + if (TARGET_MEMCPY) + block_move_call (dest_reg, src_reg, bytes_rtx); + + else if (constp && bytes <= 2 * MAX_MOVE_BYTES + && align == UNITS_PER_WORD) + move_by_pieces (orig_dest, orig_src, bytes, align); + + else if (constp && bytes <= 2 * MAX_MOVE_BYTES) + emit_insn (gen_movstrsi_internal (change_address (orig_dest, BLKmode, + dest_reg), + change_address (orig_src, BLKmode, + src_reg), + bytes_rtx, align_rtx)); + + else if (constp && align >= UNITS_PER_WORD && optimize) + block_move_loop (dest_reg, src_reg, bytes, align, orig_dest, orig_src); + + else if (constp && optimize) + { + /* If the alignment is not word aligned, generate a test at + runtime, to see whether things wound up aligned, and we + can use the faster lw/sw instead ulw/usw. */ + + rtx temp = gen_reg_rtx (Pmode); + rtx aligned_label = gen_label_rtx (); + rtx join_label = gen_label_rtx (); + int leftover = bytes % MAX_MOVE_BYTES; + + bytes -= leftover; + + if (Pmode == DImode) + { + emit_insn (gen_iordi3 (temp, src_reg, dest_reg)); + emit_insn (gen_anddi3 (temp, temp, GEN_INT (UNITS_PER_WORD - 1))); + emit_insn (gen_cmpdi (temp, const0_rtx)); + } + else + { + emit_insn (gen_iorsi3 (temp, src_reg, dest_reg)); + emit_insn (gen_andsi3 (temp, temp, GEN_INT (UNITS_PER_WORD - 1))); + emit_insn (gen_cmpsi (temp, const0_rtx)); + } + + emit_jump_insn (gen_beq (aligned_label)); + + /* Unaligned loop. */ + block_move_loop (dest_reg, src_reg, bytes, 1, orig_dest, orig_src); + emit_jump_insn (gen_jump (join_label)); + emit_barrier (); + + /* Aligned loop. */ + emit_label (aligned_label); + block_move_loop (dest_reg, src_reg, bytes, UNITS_PER_WORD, orig_dest, + orig_src); + emit_label (join_label); + + /* Bytes at the end of the loop. */ + if (leftover) + emit_insn (gen_movstrsi_internal (change_address (orig_dest, BLKmode, + dest_reg), + change_address (orig_src, BLKmode, + src_reg), + GEN_INT (leftover), + GEN_INT (align))); + } + + else + block_move_call (dest_reg, src_reg, bytes_rtx); +} + +/* Emit load/stores for a small constant block_move. + + operands[0] is the memory address of the destination. + operands[1] is the memory address of the source. + operands[2] is the number of bytes to move. + operands[3] is the alignment. + operands[4] is a temp register. + operands[5] is a temp register. + ... + operands[3+num_regs] is the last temp register. + + The block move type can be one of the following: + BLOCK_MOVE_NORMAL Do all of the block move. + BLOCK_MOVE_NOT_LAST Do all but the last store. + BLOCK_MOVE_LAST Do just the last store. */ + +const char * +output_block_move (insn, operands, num_regs, move_type) + rtx insn; + rtx operands[]; + int num_regs; + enum block_move_type move_type; +{ + rtx dest_reg = XEXP (operands[0], 0); + rtx src_reg = XEXP (operands[1], 0); + HOST_WIDE_INT bytes = INTVAL (operands[2]); + int align = INTVAL (operands[3]); + int num = 0; + int offset = 0; + int use_lwl_lwr = 0; + int last_operand = num_regs + 4; + int safe_regs = 4; + int i; + rtx xoperands[10]; + + struct { + const char *load; /* load insn without nop */ + const char *load_nop; /* load insn with trailing nop */ + const char *store; /* store insn */ + const char *final; /* if last_store used: NULL or swr */ + const char *last_store; /* last store instruction */ + int offset; /* current offset */ + enum machine_mode mode; /* mode to use on (MEM) */ + } load_store[4]; + + /* ??? Detect a bug in GCC, where it can give us a register + the same as one of the addressing registers and reduce + the number of registers available. */ + for (i = 4; + i < last_operand + && safe_regs < (int)(sizeof(xoperands) / sizeof(xoperands[0])); + i++) + if (! reg_mentioned_p (operands[i], operands[0]) + && ! reg_mentioned_p (operands[i], operands[1])) + xoperands[safe_regs++] = operands[i]; + + if (safe_regs < last_operand) + { + xoperands[0] = operands[0]; + xoperands[1] = operands[1]; + xoperands[2] = operands[2]; + xoperands[3] = operands[3]; + return output_block_move (insn, xoperands, safe_regs - 4, move_type); + } + + /* If we are given global or static addresses, and we would be + emitting a few instructions, try to save time by using a + temporary register for the pointer. */ + /* ??? The SGI Irix6 assembler fails when a SYMBOL_REF is used in + an ldl/ldr instruction pair. We play it safe, and always move + constant addresses into registers when generating N32/N64 code, just + in case we might emit an unaligned load instruction. */ + if (num_regs > 2 && (bytes > 2 * align || move_type != BLOCK_MOVE_NORMAL + || mips_abi == ABI_N32 || mips_abi == ABI_64)) + { + if (CONSTANT_P (src_reg)) + { + if (TARGET_STATS) + mips_count_memory_refs (operands[1], 1); + + src_reg = operands[3 + num_regs--]; + if (move_type != BLOCK_MOVE_LAST) + { + xoperands[1] = operands[1]; + xoperands[0] = src_reg; + if (Pmode == DImode) + output_asm_insn ("dla\t%0,%1", xoperands); + else + output_asm_insn ("la\t%0,%1", xoperands); + } + } + + if (CONSTANT_P (dest_reg)) + { + if (TARGET_STATS) + mips_count_memory_refs (operands[0], 1); + + dest_reg = operands[3 + num_regs--]; + if (move_type != BLOCK_MOVE_LAST) + { + xoperands[1] = operands[0]; + xoperands[0] = dest_reg; + if (Pmode == DImode) + output_asm_insn ("dla\t%0,%1", xoperands); + else + output_asm_insn ("la\t%0,%1", xoperands); + } + } + } + + /* ??? We really shouldn't get any LO_SUM addresses here, because they + are not offsettable, however, offsettable_address_p says they are + offsettable. I think this is a bug in offsettable_address_p. + For expediency, we fix this by just loading the address into a register + if we happen to get one. */ + + if (GET_CODE (src_reg) == LO_SUM) + { + src_reg = operands[3 + num_regs--]; + if (move_type != BLOCK_MOVE_LAST) + { + xoperands[2] = XEXP (XEXP (operands[1], 0), 1); + xoperands[1] = XEXP (XEXP (operands[1], 0), 0); + xoperands[0] = src_reg; + if (Pmode == DImode) + output_asm_insn ("daddiu\t%0,%1,%%lo(%2)", xoperands); + else + output_asm_insn ("addiu\t%0,%1,%%lo(%2)", xoperands); + } + } + + if (GET_CODE (dest_reg) == LO_SUM) + { + dest_reg = operands[3 + num_regs--]; + if (move_type != BLOCK_MOVE_LAST) + { + xoperands[2] = XEXP (XEXP (operands[0], 0), 1); + xoperands[1] = XEXP (XEXP (operands[0], 0), 0); + xoperands[0] = dest_reg; + if (Pmode == DImode) + output_asm_insn ("daddiu\t%0,%1,%%lo(%2)", xoperands); + else + output_asm_insn ("addiu\t%0,%1,%%lo(%2)", xoperands); + } + } + + if (num_regs > (int)(sizeof (load_store) / sizeof (load_store[0]))) + num_regs = sizeof (load_store) / sizeof (load_store[0]); + + else if (num_regs < 1) + abort_with_insn (insn, + "Cannot do block move, not enough scratch registers"); + + while (bytes > 0) + { + load_store[num].offset = offset; + + if (TARGET_64BIT && bytes >= 8 && align >= 8) + { + load_store[num].load = "ld\t%0,%1"; + load_store[num].load_nop = "ld\t%0,%1%#"; + load_store[num].store = "sd\t%0,%1"; + load_store[num].last_store = "sd\t%0,%1"; + load_store[num].final = 0; + load_store[num].mode = DImode; + offset += 8; + bytes -= 8; + } + + /* ??? Fails because of a MIPS assembler bug? */ + else if (TARGET_64BIT && bytes >= 8 && ! TARGET_MIPS16) + { + if (BYTES_BIG_ENDIAN) + { + load_store[num].load = "ldl\t%0,%1\n\tldr\t%0,%2"; + load_store[num].load_nop = "ldl\t%0,%1\n\tldr\t%0,%2%#"; + load_store[num].store = "sdl\t%0,%1\n\tsdr\t%0,%2"; + load_store[num].last_store = "sdr\t%0,%2"; + load_store[num].final = "sdl\t%0,%1"; + } + else + { + load_store[num].load = "ldl\t%0,%2\n\tldr\t%0,%1"; + load_store[num].load_nop = "ldl\t%0,%2\n\tldr\t%0,%1%#"; + load_store[num].store = "sdl\t%0,%2\n\tsdr\t%0,%1"; + load_store[num].last_store = "sdr\t%0,%1"; + load_store[num].final = "sdl\t%0,%2"; + } + + load_store[num].mode = DImode; + offset += 8; + bytes -= 8; + use_lwl_lwr = 1; + } + + else if (bytes >= 4 && align >= 4) + { + load_store[num].load = "lw\t%0,%1"; + load_store[num].load_nop = "lw\t%0,%1%#"; + load_store[num].store = "sw\t%0,%1"; + load_store[num].last_store = "sw\t%0,%1"; + load_store[num].final = 0; + load_store[num].mode = SImode; + offset += 4; + bytes -= 4; + } + + else if (bytes >= 4 && ! TARGET_MIPS16) + { + if (BYTES_BIG_ENDIAN) + { + load_store[num].load = "lwl\t%0,%1\n\tlwr\t%0,%2"; + load_store[num].load_nop = "lwl\t%0,%1\n\tlwr\t%0,%2%#"; + load_store[num].store = "swl\t%0,%1\n\tswr\t%0,%2"; + load_store[num].last_store = "swr\t%0,%2"; + load_store[num].final = "swl\t%0,%1"; + } + else + { + load_store[num].load = "lwl\t%0,%2\n\tlwr\t%0,%1"; + load_store[num].load_nop = "lwl\t%0,%2\n\tlwr\t%0,%1%#"; + load_store[num].store = "swl\t%0,%2\n\tswr\t%0,%1"; + load_store[num].last_store = "swr\t%0,%1"; + load_store[num].final = "swl\t%0,%2"; + } + + load_store[num].mode = SImode; + offset += 4; + bytes -= 4; + use_lwl_lwr = 1; + } + + else if (bytes >= 2 && align >= 2) + { + load_store[num].load = "lh\t%0,%1"; + load_store[num].load_nop = "lh\t%0,%1%#"; + load_store[num].store = "sh\t%0,%1"; + load_store[num].last_store = "sh\t%0,%1"; + load_store[num].final = 0; + load_store[num].mode = HImode; + offset += 2; + bytes -= 2; + } + else + { + load_store[num].load = "lb\t%0,%1"; + load_store[num].load_nop = "lb\t%0,%1%#"; + load_store[num].store = "sb\t%0,%1"; + load_store[num].last_store = "sb\t%0,%1"; + load_store[num].final = 0; + load_store[num].mode = QImode; + offset++; + bytes--; + } + + if (TARGET_STATS && move_type != BLOCK_MOVE_LAST) + { + dslots_load_total++; + dslots_load_filled++; + + if (CONSTANT_P (src_reg)) + mips_count_memory_refs (src_reg, 1); + + if (CONSTANT_P (dest_reg)) + mips_count_memory_refs (dest_reg, 1); + } + + /* Emit load/stores now if we have run out of registers or are + at the end of the move. */ + + if (++num == num_regs || bytes == 0) + { + /* If only load/store, we need a NOP after the load. */ + if (num == 1) + { + load_store[0].load = load_store[0].load_nop; + if (TARGET_STATS && move_type != BLOCK_MOVE_LAST) + dslots_load_filled--; + } + + if (move_type != BLOCK_MOVE_LAST) + { + for (i = 0; i < num; i++) + { + int offset; + + if (!operands[i + 4]) + abort (); + + if (GET_MODE (operands[i + 4]) != load_store[i].mode) + operands[i + 4] = gen_rtx (REG, load_store[i].mode, + REGNO (operands[i + 4])); + + offset = load_store[i].offset; + xoperands[0] = operands[i + 4]; + xoperands[1] = gen_rtx (MEM, load_store[i].mode, + plus_constant (src_reg, offset)); + + if (use_lwl_lwr) + { + int extra_offset + = GET_MODE_SIZE (load_store[i].mode) - 1; + + xoperands[2] = gen_rtx (MEM, load_store[i].mode, + plus_constant (src_reg, + extra_offset + + offset)); + } + + output_asm_insn (load_store[i].load, xoperands); + } + } + + for (i = 0; i < num; i++) + { + int last_p = (i == num-1 && bytes == 0); + int offset = load_store[i].offset; + + xoperands[0] = operands[i + 4]; + xoperands[1] = gen_rtx (MEM, load_store[i].mode, + plus_constant (dest_reg, offset)); + + + if (use_lwl_lwr) + { + int extra_offset = GET_MODE_SIZE (load_store[i].mode) - 1; + xoperands[2] = gen_rtx (MEM, load_store[i].mode, + plus_constant (dest_reg, + extra_offset + + offset)); + } + + if (move_type == BLOCK_MOVE_NORMAL) + output_asm_insn (load_store[i].store, xoperands); + + else if (move_type == BLOCK_MOVE_NOT_LAST) + { + if (!last_p) + output_asm_insn (load_store[i].store, xoperands); + + else if (load_store[i].final != 0) + output_asm_insn (load_store[i].final, xoperands); + } + + else if (last_p) + output_asm_insn (load_store[i].last_store, xoperands); + } + + num = 0; /* reset load_store */ + use_lwl_lwr = 0; + } + } + + return ""; +} + +/* Argument support functions. */ + +/* Initialize CUMULATIVE_ARGS for a function. */ + +void +init_cumulative_args (cum, fntype, libname) + CUMULATIVE_ARGS *cum; /* argument info to initialize */ + tree fntype; /* tree ptr for function decl */ + rtx libname ATTRIBUTE_UNUSED; /* SYMBOL_REF of library name or 0 */ +{ + static CUMULATIVE_ARGS zero_cum; + tree param, next_param; + + if (TARGET_DEBUG_E_MODE) + { + fprintf (stderr, + "\ninit_cumulative_args, fntype = 0x%.8lx", (long)fntype); + + if (!fntype) + fputc ('\n', stderr); + + else + { + tree ret_type = TREE_TYPE (fntype); + fprintf (stderr, ", fntype code = %s, ret code = %s\n", + tree_code_name[(int)TREE_CODE (fntype)], + tree_code_name[(int)TREE_CODE (ret_type)]); + } + } + + *cum = zero_cum; + + /* Determine if this function has variable arguments. This is + indicated by the last argument being 'void_type_mode' if there + are no variable arguments. The standard MIPS calling sequence + passes all arguments in the general purpose registers in this case. */ + + for (param = fntype ? TYPE_ARG_TYPES (fntype) : 0; + param != 0; param = next_param) + { + next_param = TREE_CHAIN (param); + if (next_param == 0 && TREE_VALUE (param) != void_type_node) + cum->gp_reg_found = 1; + } +} + +/* Advance the argument to the next argument position. */ + +void +function_arg_advance (cum, mode, type, named) + CUMULATIVE_ARGS *cum; /* current arg information */ + enum machine_mode mode; /* current arg mode */ + tree type; /* type of the argument or 0 if lib support */ + int named; /* whether or not the argument was named */ +{ + if (TARGET_DEBUG_E_MODE) + { + fprintf (stderr, + "function_adv({gp reg found = %d, arg # = %2d, words = %2d}, %4s, ", + cum->gp_reg_found, cum->arg_number, cum->arg_words, + GET_MODE_NAME (mode)); + fprintf (stderr, HOST_PTR_PRINTF, type); + fprintf (stderr, ", %d )\n\n", named); + } + + cum->arg_number++; + switch (mode) + { + case VOIDmode: + break; + + default: + if (GET_MODE_CLASS (mode) != MODE_COMPLEX_INT + && GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT) + abort (); + + cum->gp_reg_found = 1; + cum->arg_words += ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) + / UNITS_PER_WORD); + break; + + case BLKmode: + cum->gp_reg_found = 1; + cum->arg_words += ((int_size_in_bytes (type) + UNITS_PER_WORD - 1) + / UNITS_PER_WORD); + break; + + case SFmode: + if (mips_abi == ABI_EABI && ! TARGET_SOFT_FLOAT) + cum->fp_arg_words++; + else + cum->arg_words++; + if (! cum->gp_reg_found && cum->arg_number <= 2) + cum->fp_code += 1 << ((cum->arg_number - 1) * 2); + break; + + case DFmode: + if (mips_abi == ABI_EABI && ! TARGET_SOFT_FLOAT && ! TARGET_SINGLE_FLOAT) + cum->fp_arg_words += (TARGET_64BIT ? 1 : 2); + else + cum->arg_words += (TARGET_64BIT ? 1 : 2); + if (! cum->gp_reg_found && ! TARGET_SINGLE_FLOAT && cum->arg_number <= 2) + cum->fp_code += 2 << ((cum->arg_number - 1) * 2); + break; + + case DImode: + cum->gp_reg_found = 1; + cum->arg_words += (TARGET_64BIT ? 1 : 2); + break; + + case QImode: + case HImode: + case SImode: + cum->gp_reg_found = 1; + cum->arg_words++; + break; + } +} + +/* Return an RTL expression containing the register for the given mode, + or 0 if the argument is to be passed on the stack. */ + +struct rtx_def * +function_arg (cum, mode, type, named) + CUMULATIVE_ARGS *cum; /* current arg information */ + enum machine_mode mode; /* current arg mode */ + tree type; /* type of the argument or 0 if lib support */ + int named; /* != 0 for normal args, == 0 for ... args */ +{ + rtx ret; + int regbase = -1; + int bias = 0; + int *arg_words = &cum->arg_words; + int struct_p = (type != 0 + && (TREE_CODE (type) == RECORD_TYPE + || TREE_CODE (type) == UNION_TYPE + || TREE_CODE (type) == QUAL_UNION_TYPE)); + + if (TARGET_DEBUG_E_MODE) + { + fprintf (stderr, + "function_arg( {gp reg found = %d, arg # = %2d, words = %2d}, %4s, ", + cum->gp_reg_found, cum->arg_number, cum->arg_words, + GET_MODE_NAME (mode)); + fprintf (stderr, HOST_PTR_PRINTF, type); + fprintf (stderr, ", %d ) = ", named); + } + + + cum->last_arg_fp = 0; + switch (mode) + { + case SFmode: + if (mips_abi == ABI_32 || mips_abi == ABI_O64) + { + if (cum->gp_reg_found || cum->arg_number >= 2 || TARGET_SOFT_FLOAT) + regbase = GP_ARG_FIRST; + else + { + regbase = FP_ARG_FIRST; + + /* If the first arg was a float in a floating point register, + then set bias to align this float arg properly. */ + if (cum->arg_words == 1) + bias = 1; + } + } + else if (mips_abi == ABI_EABI && ! TARGET_SOFT_FLOAT) + { + if (! TARGET_64BIT) + cum->fp_arg_words += cum->fp_arg_words & 1; + cum->last_arg_fp = 1; + arg_words = &cum->fp_arg_words; + regbase = FP_ARG_FIRST; + } + else + regbase = (TARGET_SOFT_FLOAT || ! named ? GP_ARG_FIRST : FP_ARG_FIRST); + break; + + case DFmode: + if (! TARGET_64BIT) + { + if (mips_abi == ABI_EABI + && ! TARGET_SOFT_FLOAT && ! TARGET_SINGLE_FLOAT) + cum->fp_arg_words += cum->fp_arg_words & 1; + else + cum->arg_words += cum->arg_words & 1; + } + + if (mips_abi == ABI_32 || mips_abi == ABI_O64) + regbase = ((cum->gp_reg_found + || TARGET_SOFT_FLOAT || TARGET_SINGLE_FLOAT + || cum->arg_number >= 2) + ? GP_ARG_FIRST : FP_ARG_FIRST); + else if (mips_abi == ABI_EABI + && ! TARGET_SOFT_FLOAT && ! TARGET_SINGLE_FLOAT) + { + cum->last_arg_fp = 1; + arg_words = &cum->fp_arg_words; + regbase = FP_ARG_FIRST; + } + else + regbase = (TARGET_SOFT_FLOAT || TARGET_SINGLE_FLOAT || ! named + ? GP_ARG_FIRST : FP_ARG_FIRST); + break; + + default: + if (GET_MODE_CLASS (mode) != MODE_COMPLEX_INT + && GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT) + abort (); + + /* Drops through. */ + case BLKmode: + if (type != (tree)0 && TYPE_ALIGN (type) > (unsigned) BITS_PER_WORD + && ! TARGET_64BIT && mips_abi != ABI_EABI) + cum->arg_words += (cum->arg_words & 1); + regbase = GP_ARG_FIRST; + break; + + case VOIDmode: + case QImode: + case HImode: + case SImode: + regbase = GP_ARG_FIRST; + break; + + case DImode: + if (! TARGET_64BIT) + cum->arg_words += (cum->arg_words & 1); + regbase = GP_ARG_FIRST; + } + + if (*arg_words >= MAX_ARGS_IN_REGISTERS) + { + if (TARGET_DEBUG_E_MODE) + fprintf (stderr, "<stack>%s\n", struct_p ? ", [struct]" : ""); + + ret = 0; + } + else + { + if (regbase == -1) + abort (); + + if (! type || TREE_CODE (type) != RECORD_TYPE || mips_abi == ABI_32 + || mips_abi == ABI_EABI || mips_abi == ABI_O64 || ! named) + ret = gen_rtx (REG, mode, regbase + *arg_words + bias); + else + { + /* The Irix 6 n32/n64 ABIs say that if any 64 bit chunk of the + structure contains a double in its entirety, then that 64 bit + chunk is passed in a floating point register. */ + tree field; + + /* First check to see if there is any such field. */ + for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field)) + if (TREE_CODE (field) == FIELD_DECL + && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE + && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD + && (TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field)) + % BITS_PER_WORD == 0)) + break; + + /* If the whole struct fits a DFmode register, + we don't need the PARALLEL. */ + if (! field || mode == DFmode) + ret = gen_rtx (REG, mode, regbase + *arg_words + bias); + else + { + /* Now handle the special case by returning a PARALLEL + indicating where each 64 bit chunk goes. */ + int chunks; + int bitpos; + int regno; + int i; + + /* ??? If this is a packed structure, then the last hunk won't + be 64 bits. */ + + chunks = TREE_INT_CST_LOW (TYPE_SIZE (type)) / BITS_PER_WORD; + if (chunks + *arg_words + bias > MAX_ARGS_IN_REGISTERS) + chunks = MAX_ARGS_IN_REGISTERS - *arg_words - bias; + + /* assign_parms checks the mode of ENTRY_PARM, so we must + use the actual mode here. */ + ret = gen_rtx (PARALLEL, mode, rtvec_alloc (chunks)); + + bitpos = 0; + regno = regbase + *arg_words + bias; + field = TYPE_FIELDS (type); + for (i = 0; i < chunks; i++) + { + rtx reg; + + for (; field; field = TREE_CHAIN (field)) + if (TREE_CODE (field) == FIELD_DECL + && (TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field)) + >= bitpos)) + break; + + if (field + && TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field)) == bitpos + && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE + && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD) + reg = gen_rtx (REG, DFmode, + regno + FP_ARG_FIRST - GP_ARG_FIRST); + else + reg = gen_rtx (REG, word_mode, regno); + + XVECEXP (ret, 0, i) + = gen_rtx (EXPR_LIST, VOIDmode, reg, + GEN_INT (bitpos / BITS_PER_UNIT)); + + bitpos += 64; + regno++; + } + } + } + + if (TARGET_DEBUG_E_MODE) + fprintf (stderr, "%s%s\n", reg_names[regbase + *arg_words + bias], + struct_p ? ", [struct]" : ""); + + /* The following is a hack in order to pass 1 byte structures + the same way that the MIPS compiler does (namely by passing + the structure in the high byte or half word of the register). + This also makes varargs work. If we have such a structure, + we save the adjustment RTL, and the call define expands will + emit them. For the VOIDmode argument (argument after the + last real argument), pass back a parallel vector holding each + of the adjustments. */ + + /* ??? function_arg can be called more than once for each argument. + As a result, we compute more adjustments than we need here. + See the CUMULATIVE_ARGS definition in mips.h. */ + + /* ??? This scheme requires everything smaller than the word size to + shifted to the left, but when TARGET_64BIT and ! TARGET_INT64, + that would mean every int needs to be shifted left, which is very + inefficient. Let's not carry this compatibility to the 64 bit + calling convention for now. */ + + if (struct_p && int_size_in_bytes (type) < UNITS_PER_WORD + && ! TARGET_64BIT && mips_abi != ABI_EABI) + { + rtx amount = GEN_INT (BITS_PER_WORD + - int_size_in_bytes (type) * BITS_PER_UNIT); + rtx reg = gen_rtx (REG, word_mode, regbase + *arg_words + bias); + + if (TARGET_64BIT) + cum->adjust[cum->num_adjusts++] = gen_ashldi3 (reg, reg, amount); + else + cum->adjust[cum->num_adjusts++] = gen_ashlsi3 (reg, reg, amount); + } + } + + /* We will be called with a mode of VOIDmode after the last argument + has been seen. Whatever we return will be passed to the call + insn. If we need any shifts for small structures, return them in + a PARALLEL; in that case, stuff the mips16 fp_code in as the + mode. Otherwise, if we have need a mips16 fp_code, return a REG + with the code stored as the mode. */ + if (mode == VOIDmode) + { + if (cum->num_adjusts > 0) + ret = gen_rtx (PARALLEL, (enum machine_mode) cum->fp_code, + gen_rtvec_v (cum->num_adjusts, cum->adjust)); + else if (TARGET_MIPS16 && cum->fp_code != 0) + ret = gen_rtx (REG, (enum machine_mode) cum->fp_code, 0); + } + + return ret; +} + +int +function_arg_partial_nregs (cum, mode, type, named) + CUMULATIVE_ARGS *cum; /* current arg information */ + enum machine_mode mode; /* current arg mode */ + tree type; /* type of the argument or 0 if lib support */ + int named ATTRIBUTE_UNUSED;/* != 0 for normal args, == 0 for ... args */ +{ + if ((mode == BLKmode + || GET_MODE_CLASS (mode) != MODE_COMPLEX_INT + || GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT) + && cum->arg_words < MAX_ARGS_IN_REGISTERS + && mips_abi != ABI_EABI) + { + int words; + if (mode == BLKmode) + words = ((int_size_in_bytes (type) + UNITS_PER_WORD - 1) + / UNITS_PER_WORD); + else + words = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD; + + if (words + cum->arg_words <= MAX_ARGS_IN_REGISTERS) + return 0; /* structure fits in registers */ + + if (TARGET_DEBUG_E_MODE) + fprintf (stderr, "function_arg_partial_nregs = %d\n", + MAX_ARGS_IN_REGISTERS - cum->arg_words); + + return MAX_ARGS_IN_REGISTERS - cum->arg_words; + } + + else if (mode == DImode && cum->arg_words == MAX_ARGS_IN_REGISTERS-1 + && ! TARGET_64BIT && mips_abi != ABI_EABI) + { + if (TARGET_DEBUG_E_MODE) + fprintf (stderr, "function_arg_partial_nregs = 1\n"); + + return 1; + } + + return 0; +} + +/* Abort after printing out a specific insn. */ + +static void +abort_with_insn (insn, reason) + rtx insn; + const char *reason; +{ + error (reason); + debug_rtx (insn); + abort (); +} + +/* Write a message to stderr (for use in macros expanded in files that do not + include stdio.h). */ + +void +trace (s, s1, s2) + char *s, *s1, *s2; +{ + fprintf (stderr, s, s1, s2); +} + +/* Set up the threshold for data to go into the small data area, instead + of the normal data area, and detect any conflicts in the switches. */ + +void +override_options () +{ + register int i, start; + register int regno; + register enum machine_mode mode; + + mips_section_threshold = g_switch_set ? g_switch_value : MIPS_DEFAULT_GVALUE; + + if (mips_section_threshold <= 0) + target_flags &= ~MASK_GPOPT; + else if (optimize) + target_flags |= MASK_GPOPT; + +#ifndef MIPS_ISA_DEFAULT +#define MIPS_ISA_DEFAULT 1 +#endif + + /* If both single-float and soft-float are set, then clear the one that + was set by TARGET_DEFAULT, leaving the one that was set by the + user. We assume here that the specs prevent both being set by the + user. */ +#ifdef TARGET_DEFAULT + if (TARGET_SINGLE_FLOAT && TARGET_SOFT_FLOAT) + target_flags &= ~(TARGET_DEFAULT&(MASK_SOFT_FLOAT|MASK_SINGLE_FLOAT)); +#endif + + /* Get the architectural level. */ + if (mips_isa_string == 0) + mips_isa = MIPS_ISA_DEFAULT; + + else if (ISDIGIT (*mips_isa_string)) + { + mips_isa = atoi (mips_isa_string); + if (mips_isa == 16) + { + /* -mno-mips16 overrides -mips16. */ + if (mips_no_mips16_string == NULL) + { + target_flags |= MASK_MIPS16; + if (TARGET_64BIT) + mips_isa = 3; + else + mips_isa = MIPS_ISA_DEFAULT; + } + else + { + mips_isa = MIPS_ISA_DEFAULT; + } + } + else if (mips_isa < 1 || mips_isa > 4) + { + error ("-mips%d not supported", mips_isa); + mips_isa = 1; + } + } + + else + { + error ("bad value (%s) for -mips switch", mips_isa_string); + mips_isa = 1; + } + +#ifdef MIPS_ABI_DEFAULT + /* Get the ABI to use. */ + if (mips_abi_string == (char *) 0) + mips_abi = MIPS_ABI_DEFAULT; + else if (! strcmp (mips_abi_string, "32")) + mips_abi = ABI_32; + else if (! strcmp (mips_abi_string, "o64")) + mips_abi = ABI_O64; + else if (! strcmp (mips_abi_string, "n32")) + mips_abi = ABI_N32; + else if (! strcmp (mips_abi_string, "64")) + mips_abi = ABI_64; + else if (! strcmp (mips_abi_string, "eabi")) + mips_abi = ABI_EABI; + else + error ("bad value (%s) for -mabi= switch", mips_abi_string); + + /* A specified ISA defaults the ABI if it was not specified. */ + if (mips_abi_string == 0 && mips_isa_string + && mips_abi != ABI_EABI && mips_abi != ABI_O64) + { + if (mips_isa <= 2) + mips_abi = ABI_32; + else + mips_abi = ABI_64; + } + + /* A specified ABI defaults the ISA if it was not specified. */ + else if (mips_isa_string == 0 && mips_abi_string + && mips_abi != ABI_EABI && mips_abi != ABI_O64) + { + if (mips_abi == ABI_32) + mips_isa = 1; + else if (mips_abi == ABI_N32) + mips_isa = 3; + else + mips_isa = 4; + } + + /* If both ABI and ISA were specified, check for conflicts. */ + else if (mips_isa_string && mips_abi_string) + { + if ((mips_isa <= 2 && (mips_abi == ABI_N32 || mips_abi == ABI_64 + || mips_abi == ABI_O64)) + || (mips_isa >= 3 && mips_abi == ABI_32)) + error ("-mabi=%s does not support -mips%d", mips_abi_string, mips_isa); + } + + /* Override TARGET_DEFAULT if necessary. */ + if (mips_abi == ABI_32) + target_flags &= ~ (MASK_FLOAT64|MASK_64BIT); + + /* If no type size setting options (-mlong64,-mint64,-mlong32) were used + then set the type sizes. In the EABI in 64 bit mode, longs and + pointers are 64 bits. Likewise for the SGI Irix6 N64 ABI. */ + if (mips_explicit_type_size_string == NULL + && ((mips_abi == ABI_EABI && TARGET_64BIT) + || mips_abi == ABI_64)) + target_flags |= MASK_LONG64; + + /* ??? This doesn't work yet, so don't let people try to use it. */ + if (mips_abi == ABI_32) + error ("The -mabi=32 support does not work yet."); + +#else + if (mips_abi_string) + error ("This target does not support the -mabi switch."); +#endif + +#ifdef MIPS_CPU_STRING_DEFAULT + /* ??? There is a minor inconsistency here. If the user specifies an ISA + greater than that supported by the default processor, then the user gets + an error. Normally, the compiler will just default to the base level cpu + for the indicated isa. */ + if (mips_cpu_string == 0) + mips_cpu_string = MIPS_CPU_STRING_DEFAULT; +#endif + + /* Identify the processor type */ + if (mips_cpu_string == 0 + || !strcmp (mips_cpu_string, "default") + || !strcmp (mips_cpu_string, "DEFAULT")) + { + switch (mips_isa) + { + default: + mips_cpu_string = "3000"; + mips_cpu = PROCESSOR_R3000; + break; + case 2: + mips_cpu_string = "6000"; + mips_cpu = PROCESSOR_R6000; + break; + case 3: + mips_cpu_string = "4000"; + mips_cpu = PROCESSOR_R4000; + break; + case 4: + mips_cpu_string = "8000"; + mips_cpu = PROCESSOR_R8000; + break; + } + } + + else + { + const char *p = mips_cpu_string; + int seen_v = 0; + + /* We need to cope with the various "vr" prefixes for the NEC 4300 + and 4100 processors. */ + if (*p == 'v' || *p == 'V') + seen_v = 1, p++; + + if (*p == 'r' || *p == 'R') + p++; + + /* Since there is no difference between a R2000 and R3000 in + terms of the scheduler, we collapse them into just an R3000. */ + + mips_cpu = PROCESSOR_DEFAULT; + switch (*p) + { + case '2': + if (!strcmp (p, "2000") || !strcmp (p, "2k") || !strcmp (p, "2K")) + mips_cpu = PROCESSOR_R3000; + break; + + case '3': + if (!strcmp (p, "3000") || !strcmp (p, "3k") || !strcmp (p, "3K")) + mips_cpu = PROCESSOR_R3000; + else if (!strcmp (p, "3900")) + mips_cpu = PROCESSOR_R3900; + break; + + case '4': + if (!strcmp (p, "4000") || !strcmp (p, "4k") || !strcmp (p, "4K")) + mips_cpu = PROCESSOR_R4000; + /* The vr4100 is a non-FP ISA III processor with some extra + instructions. */ + else if (!strcmp (p, "4100")) + { + mips_cpu = PROCESSOR_R4100; + target_flags |= MASK_SOFT_FLOAT ; + } + /* The vr4300 is a standard ISA III processor, but with a different + pipeline. */ + else if (!strcmp (p, "4300")) + mips_cpu = PROCESSOR_R4300; + /* The r4400 is exactly the same as the r4000 from the compiler's + viewpoint. */ + else if (!strcmp (p, "4400")) + mips_cpu = PROCESSOR_R4000; + else if (!strcmp (p, "4600")) + mips_cpu = PROCESSOR_R4600; + else if (!strcmp (p, "4650")) + mips_cpu = PROCESSOR_R4650; + break; + + case '5': + if (!strcmp (p, "5000") || !strcmp (p, "5k") || !strcmp (p, "5K")) + mips_cpu = PROCESSOR_R5000; + break; + + case '6': + if (!strcmp (p, "6000") || !strcmp (p, "6k") || !strcmp (p, "6K")) + mips_cpu = PROCESSOR_R6000; + break; + + case '8': + if (!strcmp (p, "8000")) + mips_cpu = PROCESSOR_R8000; + break; + + case 'o': + if (!strcmp (p, "orion")) + mips_cpu = PROCESSOR_R4600; + break; + } + + if (seen_v + && mips_cpu != PROCESSOR_R4300 + && mips_cpu != PROCESSOR_R4100 + && mips_cpu != PROCESSOR_R5000) + mips_cpu = PROCESSOR_DEFAULT; + + if (mips_cpu == PROCESSOR_DEFAULT) + { + error ("bad value (%s) for -mcpu= switch", mips_cpu_string); + mips_cpu_string = "default"; + } + } + + if ((mips_cpu == PROCESSOR_R3000 && mips_isa > 1) + || (mips_cpu == PROCESSOR_R6000 && mips_isa > 2) + || ((mips_cpu == PROCESSOR_R4000 + || mips_cpu == PROCESSOR_R4100 + || mips_cpu == PROCESSOR_R4300 + || mips_cpu == PROCESSOR_R4600 + || mips_cpu == PROCESSOR_R4650) + && mips_isa > 3)) + error ("-mcpu=%s does not support -mips%d", mips_cpu_string, mips_isa); + + /* make sure sizes of ints/longs/etc. are ok */ + if (mips_isa < 3) + { + if (TARGET_FLOAT64) + fatal ("Only MIPS-III or MIPS-IV CPUs can support 64 bit fp registers"); + + else if (TARGET_64BIT) + fatal ("Only MIPS-III or MIPS-IV CPUs can support 64 bit gp registers"); + } + + if (mips_abi != ABI_32 && mips_abi != ABI_O64) + flag_pcc_struct_return = 0; + + /* Tell halfpic.c that we have half-pic code if we do. */ + if (TARGET_HALF_PIC) + HALF_PIC_INIT (); + + /* -fpic (-KPIC) is the default when TARGET_ABICALLS is defined. We need + to set flag_pic so that the LEGITIMATE_PIC_OPERAND_P macro will work. */ + /* ??? -non_shared turns off pic code generation, but this is not + implemented. */ + if (TARGET_ABICALLS) + { + mips_abicalls = MIPS_ABICALLS_YES; + flag_pic = 1; + if (mips_section_threshold > 0) + warning ("-G is incompatible with PIC code which is the default"); + } + else + mips_abicalls = MIPS_ABICALLS_NO; + + /* -membedded-pic is a form of PIC code suitable for embedded + systems. All calls are made using PC relative addressing, and + all data is addressed using the $gp register. This requires gas, + which does most of the work, and GNU ld, which automatically + expands PC relative calls which are out of range into a longer + instruction sequence. All gcc really does differently is + generate a different sequence for a switch. */ + if (TARGET_EMBEDDED_PIC) + { + flag_pic = 1; + if (TARGET_ABICALLS) + warning ("-membedded-pic and -mabicalls are incompatible"); + + if (g_switch_set) + warning ("-G and -membedded-pic are incompatible"); + + /* Setting mips_section_threshold is not required, because gas + will force everything to be GP addressable anyhow, but + setting it will cause gcc to make better estimates of the + number of instructions required to access a particular data + item. */ + mips_section_threshold = 0x7fffffff; + } + + /* This optimization requires a linker that can support a R_MIPS_LO16 + relocation which is not immediately preceded by a R_MIPS_HI16 relocation. + GNU ld has this support, but not all other MIPS linkers do, so we enable + this optimization only if the user requests it, or if GNU ld is the + standard linker for this configuration. */ + /* ??? This does not work when target addresses are DImode. + This is because we are missing DImode high/lo_sum patterns. */ + if (TARGET_GAS && ! TARGET_MIPS16 && TARGET_SPLIT_ADDRESSES && optimize && ! flag_pic + && Pmode == SImode) + mips_split_addresses = 1; + else + mips_split_addresses = 0; + + /* -mrnames says to use the MIPS software convention for register + names instead of the hardware names (ie, $a0 instead of $4). + We do this by switching the names in mips_reg_names, which the + reg_names points into via the REGISTER_NAMES macro. */ + + if (TARGET_NAME_REGS) + bcopy ((char *) mips_sw_reg_names, (char *) mips_reg_names, + sizeof (mips_reg_names)); + + /* When compiling for the mips16, we can not use floating point. We + record the original hard float value in mips16_hard_float. */ + if (TARGET_MIPS16) + { + if (TARGET_SOFT_FLOAT) + mips16_hard_float = 0; + else + mips16_hard_float = 1; + target_flags |= MASK_SOFT_FLOAT; + + /* Don't run the scheduler before reload, since it tends to + increase register pressure. */ + flag_schedule_insns = 0; + } + + /* We put -mentry in TARGET_OPTIONS rather than TARGET_SWITCHES only + to avoid using up another bit in target_flags. */ + if (mips_entry_string != NULL) + { + if (*mips_entry_string != '\0') + error ("Invalid option `entry%s'", mips_entry_string); + + if (! TARGET_MIPS16) + warning ("-mentry is only meaningful with -mips-16"); + else + mips_entry = 1; + } + + /* We copy TARGET_MIPS16 into the mips16 global variable, so that + attributes can access it. */ + if (TARGET_MIPS16) + mips16 = 1; + else + mips16 = 0; + + /* Initialize the high and low values for legitimate floating point + constants. Rather than trying to get the accuracy down to the + last bit, just use approximate ranges. */ + dfhigh = REAL_VALUE_ATOF ("1.0e300", DFmode); + dflow = REAL_VALUE_ATOF ("1.0e-300", DFmode); + sfhigh = REAL_VALUE_ATOF ("1.0e38", SFmode); + sflow = REAL_VALUE_ATOF ("1.0e-38", SFmode); + + mips_print_operand_punct['?'] = 1; + mips_print_operand_punct['#'] = 1; + mips_print_operand_punct['&'] = 1; + mips_print_operand_punct['!'] = 1; + mips_print_operand_punct['*'] = 1; + mips_print_operand_punct['@'] = 1; + mips_print_operand_punct['.'] = 1; + mips_print_operand_punct['('] = 1; + mips_print_operand_punct[')'] = 1; + mips_print_operand_punct['['] = 1; + mips_print_operand_punct[']'] = 1; + mips_print_operand_punct['<'] = 1; + mips_print_operand_punct['>'] = 1; + mips_print_operand_punct['{'] = 1; + mips_print_operand_punct['}'] = 1; + mips_print_operand_punct['^'] = 1; + mips_print_operand_punct['$'] = 1; + mips_print_operand_punct['+'] = 1; + + mips_char_to_class['d'] = TARGET_MIPS16 ? M16_REGS : GR_REGS; + mips_char_to_class['e'] = M16_NA_REGS; + mips_char_to_class['t'] = T_REG; + mips_char_to_class['f'] = (TARGET_HARD_FLOAT ? FP_REGS : NO_REGS); + mips_char_to_class['h'] = HI_REG; + mips_char_to_class['l'] = LO_REG; + mips_char_to_class['a'] = HILO_REG; + mips_char_to_class['x'] = MD_REGS; + mips_char_to_class['b'] = ALL_REGS; + mips_char_to_class['y'] = GR_REGS; + mips_char_to_class['z'] = ST_REGS; + + /* Set up array to map GCC register number to debug register number. + Ignore the special purpose register numbers. */ + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + mips_dbx_regno[i] = -1; + + start = GP_DBX_FIRST - GP_REG_FIRST; + for (i = GP_REG_FIRST; i <= GP_REG_LAST; i++) + mips_dbx_regno[i] = i + start; + + start = FP_DBX_FIRST - FP_REG_FIRST; + for (i = FP_REG_FIRST; i <= FP_REG_LAST; i++) + mips_dbx_regno[i] = i + start; + + /* Set up array giving whether a given register can hold a given mode. + At present, restrict ints from being in FP registers, because reload + is a little enthusiastic about storing extra values in FP registers, + and this is not good for things like OS kernels. Also, due to the + mandatory delay, it is as fast to load from cached memory as to move + from the FP register. */ + + for (mode = VOIDmode; + mode != MAX_MACHINE_MODE; + mode = (enum machine_mode) ((int)mode + 1)) + { + register int size = GET_MODE_SIZE (mode); + register enum mode_class class = GET_MODE_CLASS (mode); + + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + { + register int temp; + + if (mode == CCmode) + { + if (mips_isa < 4) + temp = (regno == FPSW_REGNUM); + else + temp = (ST_REG_P (regno) || GP_REG_P (regno) + || FP_REG_P (regno)); + } + + else if (GP_REG_P (regno)) + temp = ((regno & 1) == 0 || size <= UNITS_PER_WORD); + + else if (FP_REG_P (regno)) + temp = ((TARGET_FLOAT64 || ((regno & 1) == 0)) + && (class == MODE_FLOAT + || class == MODE_COMPLEX_FLOAT + || (TARGET_DEBUG_H_MODE && class == MODE_INT)) + && (! TARGET_SINGLE_FLOAT || size <= 4)); + + else if (MD_REG_P (regno)) + temp = (class == MODE_INT + && (size <= UNITS_PER_WORD + || (regno == MD_REG_FIRST + && size == 2 * UNITS_PER_WORD))); + + else + temp = 0; + + mips_hard_regno_mode_ok[(int)mode][regno] = temp; + } + } + + /* Save GPR registers in word_mode sized hunks. word_mode hasn't been + initialized yet, so we can't use that here. */ + gpr_mode = TARGET_64BIT ? DImode : SImode; +} + +/* On the mips16, we want to allocate $24 (T_REG) before other + registers for instructions for which it is possible. This helps + avoid shuffling registers around in order to set up for an xor, + encouraging the compiler to use a cmp instead. */ + +void +mips_order_regs_for_local_alloc () +{ + register int i; + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + reg_alloc_order[i] = i; + + if (TARGET_MIPS16) + { + /* It really doesn't matter where we put register 0, since it is + a fixed register anyhow. */ + reg_alloc_order[0] = 24; + reg_alloc_order[24] = 0; + } +} + + +/* The MIPS debug format wants all automatic variables and arguments + to be in terms of the virtual frame pointer (stack pointer before + any adjustment in the function), while the MIPS 3.0 linker wants + the frame pointer to be the stack pointer after the initial + adjustment. So, we do the adjustment here. The arg pointer (which + is eliminated) points to the virtual frame pointer, while the frame + pointer (which may be eliminated) points to the stack pointer after + the initial adjustments. */ + +HOST_WIDE_INT +mips_debugger_offset (addr, offset) + rtx addr; + HOST_WIDE_INT offset; +{ + rtx offset2 = const0_rtx; + rtx reg = eliminate_constant_term (addr, &offset2); + + if (offset == 0) + offset = INTVAL (offset2); + + if (reg == stack_pointer_rtx || reg == frame_pointer_rtx + || reg == hard_frame_pointer_rtx) + { + HOST_WIDE_INT frame_size = (!current_frame_info.initialized) + ? compute_frame_size (get_frame_size ()) + : current_frame_info.total_size; + + /* MIPS16 frame is smaller */ + if (frame_pointer_needed && TARGET_MIPS16) + frame_size -= current_function_outgoing_args_size; + + offset = offset - frame_size; + } + + /* sdbout_parms does not want this to crash for unrecognized cases. */ +#if 0 + else if (reg != arg_pointer_rtx) + abort_with_insn (addr, "mips_debugger_offset called with non stack/frame/arg pointer."); +#endif + + return offset; +} + +/* A C compound statement to output to stdio stream STREAM the + assembler syntax for an instruction operand X. X is an RTL + expression. + + CODE is a value that can be used to specify one of several ways + of printing the operand. It is used when identical operands + must be printed differently depending on the context. CODE + comes from the `%' specification that was used to request + printing of the operand. If the specification was just `%DIGIT' + then CODE is 0; if the specification was `%LTR DIGIT' then CODE + is the ASCII code for LTR. + + If X is a register, this macro should print the register's name. + The names can be found in an array `reg_names' whose type is + `char *[]'. `reg_names' is initialized from `REGISTER_NAMES'. + + When the machine description has a specification `%PUNCT' (a `%' + followed by a punctuation character), this macro is called with + a null pointer for X and the punctuation character for CODE. + + The MIPS specific codes are: + + 'X' X is CONST_INT, prints 32 bits in hexadecimal format = "0x%08x", + 'x' X is CONST_INT, prints 16 bits in hexadecimal format = "0x%04x", + 'd' output integer constant in decimal, + 'z' if the operand is 0, use $0 instead of normal operand. + 'D' print second register of double-word register operand. + 'L' print low-order register of double-word register operand. + 'M' print high-order register of double-word register operand. + 'C' print part of opcode for a branch condition. + 'N' print part of opcode for a branch condition, inverted. + 'S' X is CODE_LABEL, print with prefix of "LS" (for embedded switch). + 'B' print 'z' for EQ, 'n' for NE + 'b' print 'n' for EQ, 'z' for NE + 'T' print 'f' for EQ, 't' for NE + 't' print 't' for EQ, 'f' for NE + 'Z' print register and a comma, but print nothing for $fcc0 + '(' Turn on .set noreorder + ')' Turn on .set reorder + '[' Turn on .set noat + ']' Turn on .set at + '<' Turn on .set nomacro + '>' Turn on .set macro + '{' Turn on .set volatile (not GAS) + '}' Turn on .set novolatile (not GAS) + '&' Turn on .set noreorder if filling delay slots + '*' Turn on both .set noreorder and .set nomacro if filling delay slots + '!' Turn on .set nomacro if filling delay slots + '#' Print nop if in a .set noreorder section. + '?' Print 'l' if we are to use a branch likely instead of normal branch. + '@' Print the name of the assembler temporary register (at or $1). + '.' Print the name of the register with a hard-wired zero (zero or $0). + '^' Print the name of the pic call-through register (t9 or $25). + '$' Print the name of the stack pointer register (sp or $29). + '+' Print the name of the gp register (gp or $28). */ + +void +print_operand (file, op, letter) + FILE *file; /* file to write to */ + rtx op; /* operand to print */ + int letter; /* %<letter> or 0 */ +{ + register enum rtx_code code; + + if (PRINT_OPERAND_PUNCT_VALID_P (letter)) + { + switch (letter) + { + case '?': + if (mips_branch_likely) + putc ('l', file); + break; + + case '@': + fputs (reg_names [GP_REG_FIRST + 1], file); + break; + + case '^': + fputs (reg_names [PIC_FUNCTION_ADDR_REGNUM], file); + break; + + case '.': + fputs (reg_names [GP_REG_FIRST + 0], file); + break; + + case '$': + fputs (reg_names[STACK_POINTER_REGNUM], file); + break; + + case '+': + fputs (reg_names[GP_REG_FIRST + 28], file); + break; + + case '&': + if (final_sequence != 0 && set_noreorder++ == 0) + fputs (".set\tnoreorder\n\t", file); + break; + + case '*': + if (final_sequence != 0) + { + if (set_noreorder++ == 0) + fputs (".set\tnoreorder\n\t", file); + + if (set_nomacro++ == 0) + fputs (".set\tnomacro\n\t", file); + } + break; + + case '!': + if (final_sequence != 0 && set_nomacro++ == 0) + fputs ("\n\t.set\tnomacro", file); + break; + + case '#': + if (set_noreorder != 0) + fputs ("\n\tnop", file); + else if (TARGET_STATS) + fputs ("\n\t#nop", file); + + break; + + case '(': + if (set_noreorder++ == 0) + fputs (".set\tnoreorder\n\t", file); + break; + + case ')': + if (set_noreorder == 0) + error ("internal error: %%) found without a %%( in assembler pattern"); + + else if (--set_noreorder == 0) + fputs ("\n\t.set\treorder", file); + + break; + + case '[': + if (set_noat++ == 0) + fputs (".set\tnoat\n\t", file); + break; + + case ']': + if (set_noat == 0) + error ("internal error: %%] found without a %%[ in assembler pattern"); + else if (--set_noat == 0) + fputs ("\n\t.set\tat", file); + + break; + + case '<': + if (set_nomacro++ == 0) + fputs (".set\tnomacro\n\t", file); + break; + + case '>': + if (set_nomacro == 0) + error ("internal error: %%> found without a %%< in assembler pattern"); + else if (--set_nomacro == 0) + fputs ("\n\t.set\tmacro", file); + + break; + + case '{': + if (set_volatile++ == 0) + fprintf (file, "%s.set\tvolatile\n\t", TARGET_MIPS_AS ? "" : "#"); + break; + + case '}': + if (set_volatile == 0) + error ("internal error: %%} found without a %%{ in assembler pattern"); + else if (--set_volatile == 0) + fprintf (file, "\n\t%s.set\tnovolatile", (TARGET_MIPS_AS) ? "" : "#"); + + break; + + default: + error ("PRINT_OPERAND: Unknown punctuation '%c'", letter); + break; + } + + return; + } + + if (! op) + { + error ("PRINT_OPERAND null pointer"); + return; + } + + code = GET_CODE (op); + + if (code == SIGN_EXTEND) + op = XEXP (op, 0), code = GET_CODE (op); + + if (letter == 'C') + switch (code) + { + case EQ: fputs ("eq", file); break; + case NE: fputs ("ne", file); break; + case GT: fputs ("gt", file); break; + case GE: fputs ("ge", file); break; + case LT: fputs ("lt", file); break; + case LE: fputs ("le", file); break; + case GTU: fputs ("gtu", file); break; + case GEU: fputs ("geu", file); break; + case LTU: fputs ("ltu", file); break; + case LEU: fputs ("leu", file); break; + default: + abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%C"); + } + + else if (letter == 'N') + switch (code) + { + case EQ: fputs ("ne", file); break; + case NE: fputs ("eq", file); break; + case GT: fputs ("le", file); break; + case GE: fputs ("lt", file); break; + case LT: fputs ("ge", file); break; + case LE: fputs ("gt", file); break; + case GTU: fputs ("leu", file); break; + case GEU: fputs ("ltu", file); break; + case LTU: fputs ("geu", file); break; + case LEU: fputs ("gtu", file); break; + default: + abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%N"); + } + + else if (letter == 'S') + { + char buffer[100]; + + ASM_GENERATE_INTERNAL_LABEL (buffer, "LS", CODE_LABEL_NUMBER (op)); + assemble_name (file, buffer); + } + + else if (letter == 'Z') + { + register int regnum; + + if (code != REG) + abort (); + + regnum = REGNO (op); + if (! ST_REG_P (regnum)) + abort (); + + if (regnum != ST_REG_FIRST) + fprintf (file, "%s,", reg_names[regnum]); + } + + else if (code == REG || code == SUBREG) + { + register int regnum; + + if (code == REG) + regnum = REGNO (op); + else + regnum = true_regnum (op); + + if ((letter == 'M' && ! WORDS_BIG_ENDIAN) + || (letter == 'L' && WORDS_BIG_ENDIAN) + || letter == 'D') + regnum++; + + fprintf (file, "%s", reg_names[regnum]); + } + + else if (code == MEM) + output_address (XEXP (op, 0)); + + else if (code == CONST_DOUBLE + && GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT) + { + REAL_VALUE_TYPE d; + char s[30]; + + REAL_VALUE_FROM_CONST_DOUBLE (d, op); + REAL_VALUE_TO_DECIMAL (d, "%.20e", s); + fprintf (file, s); + } + + else if (letter == 'x' && GET_CODE (op) == CONST_INT) + fprintf (file, HOST_WIDE_INT_PRINT_HEX, 0xffff & INTVAL(op)); + + else if (letter == 'X' && GET_CODE(op) == CONST_INT) + fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op)); + + else if (letter == 'd' && GET_CODE(op) == CONST_INT) + fprintf (file, HOST_WIDE_INT_PRINT_DEC, (INTVAL(op))); + + else if (letter == 'z' && GET_CODE (op) == CONST_INT && INTVAL (op) == 0) + fputs (reg_names[GP_REG_FIRST], file); + + else if (letter == 'd' || letter == 'x' || letter == 'X') + fatal ("PRINT_OPERAND: letter %c was found & insn was not CONST_INT", + letter); + + else if (letter == 'B') + fputs (code == EQ ? "z" : "n", file); + else if (letter == 'b') + fputs (code == EQ ? "n" : "z", file); + else if (letter == 'T') + fputs (code == EQ ? "f" : "t", file); + else if (letter == 't') + fputs (code == EQ ? "t" : "f", file); + + else if (code == CONST && GET_CODE (XEXP (op, 0)) == REG) + { + /* This case arises on the mips16; see mips16_gp_pseudo_reg. */ + print_operand (file, XEXP (op, 0), letter); + } + + else if (TARGET_MIPS16 && code == CONST && mips16_gp_offset_p (op)) + { + fputs ("%gprel(", file); + mips16_output_gp_offset (file, op); + fputs (")", file); + } + + else + output_addr_const (file, op); +} + +/* A C compound statement to output to stdio stream STREAM the + assembler syntax for an instruction operand that is a memory + reference whose address is ADDR. ADDR is an RTL expression. + + On some machines, the syntax for a symbolic address depends on + the section that the address refers to. On these machines, + define the macro `ENCODE_SECTION_INFO' to store the information + into the `symbol_ref', and then check for it here. */ + +void +print_operand_address (file, addr) + FILE *file; + rtx addr; +{ + if (!addr) + error ("PRINT_OPERAND_ADDRESS, null pointer"); + + else + switch (GET_CODE (addr)) + { + case REG: + if (! TARGET_MIPS16 && REGNO (addr) == ARG_POINTER_REGNUM) + abort_with_insn (addr, "Arg pointer not eliminated."); + + fprintf (file, "0(%s)", reg_names [REGNO (addr)]); + break; + + case LO_SUM: + { + register rtx arg0 = XEXP (addr, 0); + register rtx arg1 = XEXP (addr, 1); + + if (! mips_split_addresses) + abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, Spurious LO_SUM."); + + if (GET_CODE (arg0) != REG) + abort_with_insn (addr, + "PRINT_OPERAND_ADDRESS, LO_SUM with #1 not REG."); + + fprintf (file, "%%lo("); + print_operand_address (file, arg1); + fprintf (file, ")(%s)", reg_names [REGNO (arg0)]); + } + break; + + case PLUS: + { + register rtx reg = 0; + register rtx offset = 0; + register rtx arg0 = XEXP (addr, 0); + register rtx arg1 = XEXP (addr, 1); + + if (GET_CODE (arg0) == REG) + { + reg = arg0; + offset = arg1; + if (GET_CODE (offset) == REG) + abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, 2 regs"); + } + + else if (GET_CODE (arg1) == REG) + reg = arg1, offset = arg0; + else if (CONSTANT_P (arg0) && CONSTANT_P (arg1)) + { + output_addr_const (file, addr); + break; + } + else + abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, no regs"); + + if (! CONSTANT_P (offset)) + abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, invalid insn #2"); + + if (REGNO (reg) == ARG_POINTER_REGNUM) + abort_with_insn (addr, "Arg pointer not eliminated."); + + if (TARGET_MIPS16 + && GET_CODE (offset) == CONST + && mips16_gp_offset_p (offset)) + { + fputs ("%gprel(", file); + mips16_output_gp_offset (file, offset); + fputs (")", file); + } + else + output_addr_const (file, offset); + fprintf (file, "(%s)", reg_names [REGNO (reg)]); + } + break; + + case LABEL_REF: + case SYMBOL_REF: + case CONST_INT: + case CONST: + output_addr_const (file, addr); + break; + + default: + abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, invalid insn #1"); + break; + } +} + + +/* If optimizing for the global pointer, keep track of all of the externs, so + that at the end of the file, we can emit the appropriate .extern + declaration for them, before writing out the text section. We assume all + names passed to us are in the permanent obstack, so they will be valid at + the end of the compilation. + + If we have -G 0, or the extern size is unknown, or the object is in a user + specified section that is not .sbss/.sdata, don't bother emitting the + .externs. In the case of user specified sections this behaviour is + required as otherwise GAS will think the object lives in .sbss/.sdata. */ + +int +mips_output_external (file, decl, name) + FILE *file ATTRIBUTE_UNUSED; + tree decl; + char *name; +{ + register struct extern_list *p; + int len; + tree section_name; + + if (TARGET_GP_OPT + && TREE_CODE (decl) != FUNCTION_DECL + && (len = int_size_in_bytes (TREE_TYPE (decl))) > 0 + && ((section_name = DECL_SECTION_NAME (decl)) == NULL + || strcmp (TREE_STRING_POINTER (section_name), ".sbss") == 0 + || strcmp (TREE_STRING_POINTER (section_name), ".sdata") == 0)) + { + p = (struct extern_list *) permalloc (sizeof (struct extern_list)); + p->next = extern_head; + p->name = name; + p->size = len; + extern_head = p; + } + +#ifdef ASM_OUTPUT_UNDEF_FUNCTION + if (TREE_CODE (decl) == FUNCTION_DECL + /* ??? Don't include alloca, since gcc will always expand it + inline. If we don't do this, the C++ library fails to build. */ + && strcmp (name, "alloca") + /* ??? Don't include __builtin_next_arg, because then gcc will not + bootstrap under Irix 5.1. */ + && strcmp (name, "__builtin_next_arg")) + { + p = (struct extern_list *) permalloc (sizeof (struct extern_list)); + p->next = extern_head; + p->name = name; + p->size = -1; + extern_head = p; + } +#endif + + return 0; +} + +#ifdef ASM_OUTPUT_UNDEF_FUNCTION +int +mips_output_external_libcall (file, name) + FILE *file; + char *name; +{ + register struct extern_list *p; + + p = (struct extern_list *) permalloc (sizeof (struct extern_list)); + p->next = extern_head; + p->name = name; + p->size = -1; + extern_head = p; + + return 0; +} +#endif + +/* Compute a string to use as a temporary file name. */ + +/* On MSDOS, write temp files in current dir + because there's no place else we can expect to use. */ +#if __MSDOS__ +#ifndef P_tmpdir +#define P_tmpdir "./" +#endif +#endif + +static FILE * +make_temp_file () +{ + FILE *stream; + const char *base = getenv ("TMPDIR"); + int len; + + if (base == 0) + { +#ifdef P_tmpdir + if (access (P_tmpdir, R_OK | W_OK) == 0) + base = P_tmpdir; + else +#endif + if (access ("/usr/tmp", R_OK | W_OK) == 0) + base = "/usr/tmp/"; + else + base = "/tmp/"; + } + + len = strlen (base); + /* temp_filename is global, so we must use malloc, not alloca. */ + temp_filename = (char *) xmalloc (len + sizeof("/ctXXXXXX")); + strcpy (temp_filename, base); + if (len > 0 && temp_filename[len-1] != '/') + temp_filename[len++] = '/'; + + strcpy (temp_filename + len, "ctXXXXXX"); + mktemp (temp_filename); + + stream = fopen (temp_filename, "w+"); + if (!stream) + pfatal_with_name (temp_filename); + +#ifndef __MSDOS__ + /* In MSDOS, we cannot unlink the temporary file until we are finished using + it. Otherwise, we delete it now, so that it will be gone even if the + compiler happens to crash. */ + unlink (temp_filename); +#endif + return stream; +} + +/* Emit a new filename to a stream. If this is MIPS ECOFF, watch out + for .file's that start within a function. If we are smuggling stabs, try to + put out a MIPS ECOFF file and a stab. */ + +void +mips_output_filename (stream, name) + FILE *stream; + char *name; +{ + static int first_time = 1; + char ltext_label_name[100]; + + if (first_time) + { + first_time = 0; + SET_FILE_NUMBER (); + current_function_file = name; + ASM_OUTPUT_FILENAME (stream, num_source_filenames, name); + /* This tells mips-tfile that stabs will follow. */ + if (!TARGET_GAS && write_symbols == DBX_DEBUG) + fprintf (stream, "\t#@stabs\n"); + } + + else if (write_symbols == DBX_DEBUG) + { + ASM_GENERATE_INTERNAL_LABEL (ltext_label_name, "Ltext", 0); + fprintf (stream, "%s ", ASM_STABS_OP); + output_quoted_string (stream, name); + fprintf (stream, ",%d,0,0,%s\n", N_SOL, <ext_label_name[1]); + } + + else if (name != current_function_file + && strcmp (name, current_function_file) != 0) + { + if (inside_function && !TARGET_GAS) + { + if (!file_in_function_warning) + { + file_in_function_warning = 1; + ignore_line_number = 1; + warning ("MIPS ECOFF format does not allow changing filenames within functions with #line"); + } + } + else + { + SET_FILE_NUMBER (); + current_function_file = name; + ASM_OUTPUT_FILENAME (stream, num_source_filenames, name); + } + } +} + +/* Emit a linenumber. For encapsulated stabs, we need to put out a stab + as well as a .loc, since it is possible that MIPS ECOFF might not be + able to represent the location for inlines that come from a different + file. */ + +void +mips_output_lineno (stream, line) + FILE *stream; + int line; +{ + if (write_symbols == DBX_DEBUG) + { + ++sym_lineno; + fprintf (stream, "%sLM%d:\n\t%s %d,0,%d,%sLM%d\n", + LOCAL_LABEL_PREFIX, sym_lineno, ASM_STABN_OP, N_SLINE, line, + LOCAL_LABEL_PREFIX, sym_lineno); + } + + else + { + fprintf (stream, "\n\t%s.loc\t%d %d\n", + (ignore_line_number) ? "#" : "", + num_source_filenames, line); + + LABEL_AFTER_LOC (stream); + } +} + +/* If defined, a C statement to be executed just prior to the output of + assembler code for INSN, to modify the extracted operands so they will be + output differently. + + Here the argument OPVEC is the vector containing the operands extracted + from INSN, and NOPERANDS is the number of elements of the vector which + contain meaningful data for this insn. The contents of this vector are + what will be used to convert the insn template into assembler code, so you + can change the assembler output by changing the contents of the vector. + + We use it to check if the current insn needs a nop in front of it because + of load delays, and also to update the delay slot statistics. */ + +/* ??? There is no real need for this function, because it never actually + emits a NOP anymore. */ + +void +final_prescan_insn (insn, opvec, noperands) + rtx insn; + rtx opvec[] ATTRIBUTE_UNUSED; + int noperands ATTRIBUTE_UNUSED; +{ + if (dslots_number_nops > 0) + { + rtx pattern = PATTERN (insn); + int length = get_attr_length (insn); + + /* Do we need to emit a NOP? */ + if (length == 0 + || (mips_load_reg != 0 && reg_mentioned_p (mips_load_reg, pattern)) + || (mips_load_reg2 != 0 && reg_mentioned_p (mips_load_reg2, pattern)) + || (mips_load_reg3 != 0 && reg_mentioned_p (mips_load_reg3, pattern)) + || (mips_load_reg4 != 0 + && reg_mentioned_p (mips_load_reg4, pattern))) + fputs ("\t#nop\n", asm_out_file); + + else + dslots_load_filled++; + + while (--dslots_number_nops > 0) + fputs ("\t#nop\n", asm_out_file); + + mips_load_reg = 0; + mips_load_reg2 = 0; + mips_load_reg3 = 0; + mips_load_reg4 = 0; + } + + if (TARGET_STATS + && (GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == CALL_INSN)) + dslots_jump_total++; +} + +/* Output at beginning of assembler file. + + If we are optimizing to use the global pointer, create a temporary file to + hold all of the text stuff, and write it out to the end. This is needed + because the MIPS assembler is evidently one pass, and if it hasn't seen the + relevant .comm/.lcomm/.extern/.sdata declaration when the code is + processed, it generates a two instruction sequence. */ + +void +mips_asm_file_start (stream) + FILE *stream; +{ + ASM_OUTPUT_SOURCE_FILENAME (stream, main_input_filename); + + /* Versions of the MIPS assembler before 2.20 generate errors if a branch + inside of a .set noreorder section jumps to a label outside of the .set + noreorder section. Revision 2.20 just set nobopt silently rather than + fixing the bug. */ + + if (TARGET_MIPS_AS && optimize && flag_delayed_branch) + fprintf (stream, "\t.set\tnobopt\n"); + + /* Generate the pseudo ops that System V.4 wants. */ +#ifndef ABICALLS_ASM_OP +#define ABICALLS_ASM_OP ".abicalls" +#endif + if (TARGET_ABICALLS) + /* ??? but do not want this (or want pic0) if -non-shared? */ + fprintf (stream, "\t%s\n", ABICALLS_ASM_OP); + + if (TARGET_MIPS16) + fprintf (stream, "\t.set\tmips16\n"); + + /* Start a section, so that the first .popsection directive is guaranteed + to have a previously defined section to pop back to. */ + if (mips_abi != ABI_32 && mips_abi != ABI_O64 && mips_abi != ABI_EABI) + fprintf (stream, "\t.section\t.text\n"); + + /* This code exists so that we can put all externs before all symbol + references. This is necessary for the MIPS assembler's global pointer + optimizations to work. */ + if (TARGET_FILE_SWITCHING && ! TARGET_MIPS16) + { + asm_out_data_file = stream; + asm_out_text_file = make_temp_file (); + } + + else + asm_out_data_file = asm_out_text_file = stream; + + if (flag_verbose_asm) + fprintf (stream, "\n%s -G value = %d, Cpu = %s, ISA = %d\n", + ASM_COMMENT_START, + mips_section_threshold, mips_cpu_string, mips_isa); +} + +/* If we are optimizing the global pointer, emit the text section now and any + small externs which did not have .comm, etc that are needed. Also, give a + warning if the data area is more than 32K and -pic because 3 instructions + are needed to reference the data pointers. */ + +void +mips_asm_file_end (file) + FILE *file; +{ + char buffer[8192]; + tree name_tree; + struct extern_list *p; + int len; + + if (HALF_PIC_P ()) + { + HALF_PIC_FINISH (file); + } + + if (extern_head) + { + fputs ("\n", file); + + for (p = extern_head; p != 0; p = p->next) + { + name_tree = get_identifier (p->name); + + /* Positively ensure only one .extern for any given symbol. */ + if (! TREE_ASM_WRITTEN (name_tree)) + { + TREE_ASM_WRITTEN (name_tree) = 1; +#ifdef ASM_OUTPUT_UNDEF_FUNCTION + if (p->size == -1) + ASM_OUTPUT_UNDEF_FUNCTION (file, p->name); + else +#endif + { + fputs ("\t.extern\t", file); + assemble_name (file, p->name); + fprintf (file, ", %d\n", p->size); + } + } + } + } + + if (TARGET_FILE_SWITCHING && ! TARGET_MIPS16) + { + fprintf (file, "\n\t.text\n"); + rewind (asm_out_text_file); + if (ferror (asm_out_text_file)) + fatal_io_error (temp_filename); + + while ((len = fread (buffer, 1, sizeof (buffer), asm_out_text_file)) > 0) + if ((int) fwrite (buffer, 1, len, file) != len) + pfatal_with_name (asm_file_name); + + if (len < 0) + pfatal_with_name (temp_filename); + + if (fclose (asm_out_text_file) != 0) + pfatal_with_name (temp_filename); + +#ifdef __MSDOS__ + unlink (temp_filename); +#endif + } +} + +/* Emit either a label, .comm, or .lcomm directive, and mark that the symbol + is used, so that we don't emit an .extern for it in mips_asm_file_end. */ + +void +mips_declare_object (stream, name, init_string, final_string, size) + FILE *stream; + char *name; + char *init_string; + char *final_string; + int size; +{ + fputs (init_string, stream); /* "", "\t.comm\t", or "\t.lcomm\t" */ + assemble_name (stream, name); + fprintf (stream, final_string, size); /* ":\n", ",%u\n", ",%u\n" */ + + if (TARGET_GP_OPT) + { + tree name_tree = get_identifier (name); + TREE_ASM_WRITTEN (name_tree) = 1; + } +} + +/* Output a double precision value to the assembler. If both the + host and target are IEEE, emit the values in hex. */ + +void +mips_output_double (stream, value) + FILE *stream; + REAL_VALUE_TYPE value; +{ +#ifdef REAL_VALUE_TO_TARGET_DOUBLE + long value_long[2]; + REAL_VALUE_TO_TARGET_DOUBLE (value, value_long); + + fprintf (stream, "\t.word\t0x%08lx\t\t# %.20g\n\t.word\t0x%08lx\n", + value_long[0], value, value_long[1]); +#else + fprintf (stream, "\t.double\t%.20g\n", value); +#endif +} + +/* Output a single precision value to the assembler. If both the + host and target are IEEE, emit the values in hex. */ + +void +mips_output_float (stream, value) + FILE *stream; + REAL_VALUE_TYPE value; +{ +#ifdef REAL_VALUE_TO_TARGET_SINGLE + long value_long; + REAL_VALUE_TO_TARGET_SINGLE (value, value_long); + + fprintf (stream, "\t.word\t0x%08lx\t\t# %.12g (float)\n", value_long, value); +#else + fprintf (stream, "\t.float\t%.12g\n", value); +#endif +} + +/* Return the bytes needed to compute the frame pointer from the current + stack pointer. + + Mips stack frames look like: + + Before call After call + +-----------------------+ +-----------------------+ + high | | | | + mem. | | | | + | caller's temps. | | caller's temps. | + | | | | + +-----------------------+ +-----------------------+ + | | | | + | arguments on stack. | | arguments on stack. | + | | | | + +-----------------------+ +-----------------------+ + | 4 words to save | | 4 words to save | + | arguments passed | | arguments passed | + | in registers, even | | in registers, even | + SP->| if not passed. | VFP->| if not passed. | + +-----------------------+ +-----------------------+ + | | + | fp register save | + | | + +-----------------------+ + | | + | gp register save | + | | + +-----------------------+ + | | + | local variables | + | | + +-----------------------+ + | | + | alloca allocations | + | | + +-----------------------+ + | | + | GP save for V.4 abi | + | | + +-----------------------+ + | | + | arguments on stack | + | | + +-----------------------+ + | 4 words to save | + | arguments passed | + | in registers, even | + low SP->| if not passed. | + memory +-----------------------+ + +*/ + +HOST_WIDE_INT +compute_frame_size (size) + HOST_WIDE_INT size; /* # of var. bytes allocated */ +{ + int regno; + HOST_WIDE_INT total_size; /* # bytes that the entire frame takes up */ + HOST_WIDE_INT var_size; /* # bytes that variables take up */ + HOST_WIDE_INT args_size; /* # bytes that outgoing arguments take up */ + HOST_WIDE_INT extra_size; /* # extra bytes */ + HOST_WIDE_INT gp_reg_rounded; /* # bytes needed to store gp after rounding */ + HOST_WIDE_INT gp_reg_size; /* # bytes needed to store gp regs */ + HOST_WIDE_INT fp_reg_size; /* # bytes needed to store fp regs */ + long mask; /* mask of saved gp registers */ + long fmask; /* mask of saved fp registers */ + int fp_inc; /* 1 or 2 depending on the size of fp regs */ + long fp_bits; /* bitmask to use for each fp register */ + + gp_reg_size = 0; + fp_reg_size = 0; + mask = 0; + fmask = 0; + extra_size = MIPS_STACK_ALIGN (((TARGET_ABICALLS) ? UNITS_PER_WORD : 0)); + var_size = MIPS_STACK_ALIGN (size); + args_size = MIPS_STACK_ALIGN (current_function_outgoing_args_size); + + /* The MIPS 3.0 linker does not like functions that dynamically + allocate the stack and have 0 for STACK_DYNAMIC_OFFSET, since it + looks like we are trying to create a second frame pointer to the + function, so allocate some stack space to make it happy. */ + + if (args_size == 0 && current_function_calls_alloca) + args_size = 4 * UNITS_PER_WORD; + + total_size = var_size + args_size + extra_size; + + /* Calculate space needed for gp registers. */ + for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++) + { + /* $18 is a special case on the mips16. It may be used to call + a function which returns a floating point value, but it is + marked in call_used_regs. $31 is also a special case. When + not using -mentry, it will be used to copy a return value + into the floating point registers if the return value is + floating point. */ + if (MUST_SAVE_REGISTER (regno) + || (TARGET_MIPS16 + && regno == GP_REG_FIRST + 18 + && regs_ever_live[regno]) + || (TARGET_MIPS16 + && regno == GP_REG_FIRST + 31 + && mips16_hard_float + && ! mips_entry + && ! aggregate_value_p (DECL_RESULT (current_function_decl)) + && (GET_MODE_CLASS (DECL_MODE (DECL_RESULT (current_function_decl))) + == MODE_FLOAT) + && (! TARGET_SINGLE_FLOAT + || (GET_MODE_SIZE (DECL_MODE (DECL_RESULT (current_function_decl))) + <= 4)))) + { + gp_reg_size += GET_MODE_SIZE (gpr_mode); + mask |= 1L << (regno - GP_REG_FIRST); + + /* The entry and exit pseudo instructions can not save $17 + without also saving $16. */ + if (mips_entry + && regno == GP_REG_FIRST + 17 + && ! MUST_SAVE_REGISTER (GP_REG_FIRST + 16)) + { + gp_reg_size += UNITS_PER_WORD; + mask |= 1L << 16; + } + } + } + + /* Calculate space needed for fp registers. */ + if (TARGET_FLOAT64 || TARGET_SINGLE_FLOAT) + { + fp_inc = 1; + fp_bits = 1; + } + else + { + fp_inc = 2; + fp_bits = 3; + } + + /* This loop must iterate over the same space as its companion in + save_restore_regs. */ + for (regno = (FP_REG_LAST - fp_inc + 1); + regno >= FP_REG_FIRST; + regno -= fp_inc) + { + if (regs_ever_live[regno] && !call_used_regs[regno]) + { + fp_reg_size += fp_inc * UNITS_PER_FPREG; + fmask |= fp_bits << (regno - FP_REG_FIRST); + } + } + + gp_reg_rounded = MIPS_STACK_ALIGN (gp_reg_size); + total_size += gp_reg_rounded + MIPS_STACK_ALIGN (fp_reg_size); + + /* The gp reg is caller saved in the 32 bit ABI, so there is no need + for leaf routines (total_size == extra_size) to save the gp reg. + The gp reg is callee saved in the 64 bit ABI, so all routines must + save the gp reg. This is not a leaf routine if -p, because of the + call to mcount. */ + if (total_size == extra_size + && (mips_abi == ABI_32 || mips_abi == ABI_O64 || mips_abi == ABI_EABI) + && ! profile_flag) + total_size = extra_size = 0; + else if (TARGET_ABICALLS) + { + /* Add the context-pointer to the saved registers. */ + gp_reg_size += UNITS_PER_WORD; + mask |= 1L << (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST); + total_size -= gp_reg_rounded; + gp_reg_rounded = MIPS_STACK_ALIGN (gp_reg_size); + total_size += gp_reg_rounded; + } + + /* Add in space reserved on the stack by the callee for storing arguments + passed in registers. */ + if (mips_abi != ABI_32 && mips_abi != ABI_O64) + total_size += MIPS_STACK_ALIGN (current_function_pretend_args_size); + + /* The entry pseudo instruction will allocate 32 bytes on the stack. */ + if (mips_entry && total_size > 0 && total_size < 32) + total_size = 32; + + /* 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.mask = mask; + current_frame_info.fmask = fmask; + current_frame_info.initialized = reload_completed; + current_frame_info.num_gp = gp_reg_size / UNITS_PER_WORD; + current_frame_info.num_fp = fp_reg_size / (fp_inc * UNITS_PER_FPREG); + + if (mask) + { + unsigned long offset; + + /* When using mips_entry, the registers are always saved at the + top of the stack. */ + if (! mips_entry) + offset = (args_size + extra_size + var_size + + gp_reg_size - GET_MODE_SIZE (gpr_mode)); + else + offset = total_size - GET_MODE_SIZE (gpr_mode); + + current_frame_info.gp_sp_offset = offset; + current_frame_info.gp_save_offset = offset - total_size; + } + else + { + current_frame_info.gp_sp_offset = 0; + current_frame_info.gp_save_offset = 0; + } + + if (fmask) + { + unsigned long offset = (args_size + extra_size + var_size + + gp_reg_rounded + fp_reg_size + - fp_inc * UNITS_PER_FPREG); + current_frame_info.fp_sp_offset = offset; + current_frame_info.fp_save_offset = offset - total_size; + } + else + { + current_frame_info.fp_sp_offset = 0; + current_frame_info.fp_save_offset = 0; + } + + /* Ok, we're done. */ + return total_size; +} + +/* Common code to emit the insns (or to write the instructions to a file) + to save/restore registers. + + Other parts of the code assume that MIPS_TEMP1_REGNUM (aka large_reg) + is not modified within save_restore_insns. */ + +#define BITSET_P(VALUE,BIT) (((VALUE) & (1L << (BIT))) != 0) + +static void +save_restore_insns (store_p, large_reg, large_offset, file) + int store_p; /* true if this is prologue */ + rtx large_reg; /* register holding large offset constant or NULL */ + long large_offset; /* large constant offset value */ + FILE *file; /* file to write instructions instead of making RTL */ +{ + long mask = current_frame_info.mask; + long fmask = current_frame_info.fmask; + int regno; + rtx base_reg_rtx; + HOST_WIDE_INT base_offset; + HOST_WIDE_INT gp_offset; + HOST_WIDE_INT fp_offset; + HOST_WIDE_INT end_offset; + rtx insn; + + if (frame_pointer_needed + && ! BITSET_P (mask, HARD_FRAME_POINTER_REGNUM - GP_REG_FIRST)) + abort (); + + if (mask == 0 && fmask == 0) + return; + + /* Save registers starting from high to low. The debuggers prefer at least + the return register be stored at func+4, and also it allows us not to + need a nop in the epilog if at least one register is reloaded in + addition to return address. */ + + /* Save GP registers if needed. */ + if (mask) + { + /* Pick which pointer to use as a base register. For small frames, just + use the stack pointer. Otherwise, use a temporary register. Save 2 + cycles if the save area is near the end of a large frame, by reusing + the constant created in the prologue/epilogue to adjust the stack + frame. */ + + gp_offset = current_frame_info.gp_sp_offset; + end_offset + = gp_offset - (current_frame_info.gp_reg_size + - GET_MODE_SIZE (gpr_mode)); + + if (gp_offset < 0 || end_offset < 0) + fatal ("gp_offset (%ld) or end_offset (%ld) is less than zero.", + (long) gp_offset, (long) end_offset); + + /* If we see a large frame in mips16 mode, we save the registers + before adjusting the stack pointer, and load them afterward. */ + else if (TARGET_MIPS16 && large_offset > 32767) + base_reg_rtx = stack_pointer_rtx, base_offset = large_offset; + + else if (gp_offset < 32768) + base_reg_rtx = stack_pointer_rtx, base_offset = 0; + + else if (large_reg != 0 + && (unsigned HOST_WIDE_INT) (large_offset - gp_offset) < 32768 + && (unsigned HOST_WIDE_INT) (large_offset - end_offset) < 32768) + { + base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM); + base_offset = large_offset; + if (file == 0) + { + if (Pmode == DImode) + insn = emit_insn (gen_adddi3 (base_reg_rtx, large_reg, + stack_pointer_rtx)); + else + insn = emit_insn (gen_addsi3 (base_reg_rtx, large_reg, + stack_pointer_rtx)); + if (store_p) + RTX_FRAME_RELATED_P (insn) = 1; + } + else + fprintf (file, "\t%s\t%s,%s,%s\n", + Pmode == DImode ? "daddu" : "addu", + reg_names[MIPS_TEMP2_REGNUM], + reg_names[REGNO (large_reg)], + reg_names[STACK_POINTER_REGNUM]); + } + + else + { + base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM); + base_offset = gp_offset; + if (file == 0) + { + rtx gp_offset_rtx = GEN_INT (gp_offset); + + /* Instruction splitting doesn't preserve the RTX_FRAME_RELATED_P + bit, so make sure that we don't emit anything that can be + split. */ + /* ??? There is no DImode ori immediate pattern, so we can only + do this for 32 bit code. */ + if (large_int (gp_offset_rtx) + && GET_MODE (base_reg_rtx) == SImode) + { + insn = emit_move_insn (base_reg_rtx, + GEN_INT (gp_offset & 0xffff0000)); + if (store_p) + RTX_FRAME_RELATED_P (insn) = 1; + insn + = emit_insn (gen_iorsi3 (base_reg_rtx, base_reg_rtx, + GEN_INT (gp_offset & 0x0000ffff))); + if (store_p) + RTX_FRAME_RELATED_P (insn) = 1; + } + else + { + insn = emit_move_insn (base_reg_rtx, gp_offset_rtx); + if (store_p) + RTX_FRAME_RELATED_P (insn) = 1; + } + + if (Pmode == DImode) + insn = emit_insn (gen_adddi3 (base_reg_rtx, base_reg_rtx, + stack_pointer_rtx)); + else + insn = emit_insn (gen_addsi3 (base_reg_rtx, base_reg_rtx, + stack_pointer_rtx)); + if (store_p) + RTX_FRAME_RELATED_P (insn) = 1; + } + else + { + fprintf (file, "\tli\t%s,0x%.08lx\t# ", + reg_names[MIPS_TEMP2_REGNUM], (long) base_offset); + fprintf (file, HOST_WIDE_INT_PRINT_DEC, base_offset); + fprintf (file, "\n\t%s\t%s,%s,%s\n", + Pmode == DImode ? "daddu" : "addu", + reg_names[MIPS_TEMP2_REGNUM], + reg_names[MIPS_TEMP2_REGNUM], + reg_names[STACK_POINTER_REGNUM]); + } + } + + /* When we restore the registers in MIPS16 mode, then if we are + using a frame pointer, and this is not a large frame, the + current stack pointer will be offset by + current_function_outgoing_args_size. Doing it this way lets + us avoid offsetting the frame pointer before copying it into + the stack pointer; there is no instruction to set the stack + pointer to the sum of a register and a constant. */ + if (TARGET_MIPS16 + && ! store_p + && frame_pointer_needed + && large_offset <= 32767) + base_offset += current_function_outgoing_args_size; + + for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--) + if (BITSET_P (mask, regno - GP_REG_FIRST)) + { + if (file == 0) + { + rtx reg_rtx; + rtx mem_rtx + = gen_rtx (MEM, gpr_mode, + gen_rtx (PLUS, Pmode, base_reg_rtx, + GEN_INT (gp_offset - base_offset))); + + RTX_UNCHANGING_P (mem_rtx) = 1; + + /* The mips16 does not have an instruction to load + $31, so we load $7 instead, and work things out + in the caller. */ + if (TARGET_MIPS16 && ! store_p && regno == GP_REG_FIRST + 31) + reg_rtx = gen_rtx (REG, gpr_mode, GP_REG_FIRST + 7); + /* The mips16 sometimes needs to save $18. */ + else if (TARGET_MIPS16 + && regno != GP_REG_FIRST + 31 + && ! M16_REG_P (regno)) + { + if (! store_p) + reg_rtx = gen_rtx (REG, gpr_mode, 6); + else + { + reg_rtx = gen_rtx (REG, gpr_mode, 3); + emit_move_insn (reg_rtx, + gen_rtx (REG, gpr_mode, regno)); + } + } + else + reg_rtx = gen_rtx (REG, gpr_mode, regno); + + if (store_p) + { + insn = emit_move_insn (mem_rtx, reg_rtx); + RTX_FRAME_RELATED_P (insn) = 1; + } + else if (!TARGET_ABICALLS + || (mips_abi != ABI_32 && mips_abi != ABI_O64) + || regno != (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST)) + { + emit_move_insn (reg_rtx, mem_rtx); + if (TARGET_MIPS16 + && regno != GP_REG_FIRST + 31 + && ! M16_REG_P (regno)) + emit_move_insn (gen_rtx (REG, gpr_mode, regno), + reg_rtx); + } + } + else + { + if (store_p || !TARGET_ABICALLS + || (mips_abi != ABI_32 && mips_abi != ABI_O64) + || regno != (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST)) + { + int r = regno; + + /* The mips16 does not have an instruction to + load $31, so we load $7 instead, and work + things out in the caller. */ + if (TARGET_MIPS16 && ! store_p && r == GP_REG_FIRST + 31) + r = GP_REG_FIRST + 7; + /* The mips16 sometimes needs to save $18. */ + if (TARGET_MIPS16 + && regno != GP_REG_FIRST + 31 + && ! M16_REG_P (regno)) + { + if (! store_p) + r = GP_REG_FIRST + 6; + else + { + r = GP_REG_FIRST + 3; + fprintf (file, "\tmove\t%s,%s\n", + reg_names[r], reg_names[regno]); + } + } + fprintf (file, "\t%s\t%s,", + (TARGET_64BIT + ? (store_p) ? "sd" : "ld" + : (store_p) ? "sw" : "lw"), + reg_names[r]); + fprintf (file, HOST_WIDE_INT_PRINT_DEC, + gp_offset - base_offset); + fprintf (file, "(%s)\n", reg_names[REGNO(base_reg_rtx)]); + if (! store_p + && TARGET_MIPS16 + && regno != GP_REG_FIRST + 31 + && ! M16_REG_P (regno)) + fprintf (file, "\tmove\t%s,%s\n", + reg_names[regno], reg_names[r]); + } + + } + gp_offset -= GET_MODE_SIZE (gpr_mode); + } + } + else + base_reg_rtx = 0, base_offset = 0; + + /* Save floating point registers if needed. */ + if (fmask) + { + int fp_inc = (TARGET_FLOAT64 || TARGET_SINGLE_FLOAT) ? 1 : 2; + int fp_size = fp_inc * UNITS_PER_FPREG; + + /* Pick which pointer to use as a base register. */ + fp_offset = current_frame_info.fp_sp_offset; + end_offset = fp_offset - (current_frame_info.fp_reg_size - fp_size); + + if (fp_offset < 0 || end_offset < 0) + fatal ("fp_offset (%ld) or end_offset (%ld) is less than zero.", + (long) fp_offset, (long) end_offset); + + else if (fp_offset < 32768) + base_reg_rtx = stack_pointer_rtx, base_offset = 0; + + else if (base_reg_rtx != 0 + && (unsigned HOST_WIDE_INT) (base_offset - fp_offset) < 32768 + && (unsigned HOST_WIDE_INT) (base_offset - end_offset) < 32768) + ; /* already set up for gp registers above */ + + else if (large_reg != 0 + && (unsigned HOST_WIDE_INT) (large_offset - fp_offset) < 32768 + && (unsigned HOST_WIDE_INT) (large_offset - end_offset) < 32768) + { + base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM); + base_offset = large_offset; + if (file == 0) + { + if (Pmode == DImode) + insn = emit_insn (gen_adddi3 (base_reg_rtx, large_reg, + stack_pointer_rtx)); + else + insn = emit_insn (gen_addsi3 (base_reg_rtx, large_reg, + stack_pointer_rtx)); + if (store_p) + RTX_FRAME_RELATED_P (insn) = 1; + } + + else + fprintf (file, "\t%s\t%s,%s,%s\n", + Pmode == DImode ? "daddu" : "addu", + reg_names[MIPS_TEMP2_REGNUM], + reg_names[REGNO (large_reg)], + reg_names[STACK_POINTER_REGNUM]); + } + + else + { + base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM); + base_offset = fp_offset; + if (file == 0) + { + rtx fp_offset_rtx = GEN_INT (fp_offset); + + /* Instruction splitting doesn't preserve the RTX_FRAME_RELATED_P + bit, so make sure that we don't emit anything that can be + split. */ + /* ??? There is no DImode ori immediate pattern, so we can only + do this for 32 bit code. */ + if (large_int (fp_offset_rtx) + && GET_MODE (base_reg_rtx) == SImode) + { + insn = emit_move_insn (base_reg_rtx, + GEN_INT (fp_offset & 0xffff0000)); + if (store_p) + RTX_FRAME_RELATED_P (insn) = 1; + insn = emit_insn (gen_iorsi3 (base_reg_rtx, base_reg_rtx, + GEN_INT (fp_offset & 0x0000ffff))); + if (store_p) + RTX_FRAME_RELATED_P (insn) = 1; + } + else + { + insn = emit_move_insn (base_reg_rtx, fp_offset_rtx); + if (store_p) + RTX_FRAME_RELATED_P (insn) = 1; + } + + if (store_p) + RTX_FRAME_RELATED_P (insn) = 1; + if (Pmode == DImode) + insn = emit_insn (gen_adddi3 (base_reg_rtx, base_reg_rtx, + stack_pointer_rtx)); + else + insn = emit_insn (gen_addsi3 (base_reg_rtx, base_reg_rtx, + stack_pointer_rtx)); + if (store_p) + RTX_FRAME_RELATED_P (insn) = 1; + } + else + { + fprintf (file, "\tli\t%s,0x%.08lx\t# ", + reg_names[MIPS_TEMP2_REGNUM], (long) base_offset); + fprintf (file, HOST_WIDE_INT_PRINT_DEC, base_offset); + fprintf (file, "\n\t%s\t%s,%s,%s\n", + Pmode == DImode ? "daddu" : "addu", + reg_names[MIPS_TEMP2_REGNUM], + reg_names[MIPS_TEMP2_REGNUM], + reg_names[STACK_POINTER_REGNUM]); + } + } + + /* This loop must iterate over the same space as its companion in + compute_frame_size. */ + for (regno = (FP_REG_LAST - fp_inc + 1); + regno >= FP_REG_FIRST; + regno -= fp_inc) + if (BITSET_P (fmask, regno - FP_REG_FIRST)) + { + if (file == 0) + { + enum machine_mode sz + = TARGET_SINGLE_FLOAT ? SFmode : DFmode; + rtx reg_rtx = gen_rtx (REG, sz, regno); + rtx mem_rtx = gen_rtx (MEM, sz, + gen_rtx (PLUS, Pmode, base_reg_rtx, + GEN_INT (fp_offset + - base_offset))); + RTX_UNCHANGING_P (mem_rtx) = 1; + + if (store_p) + { + insn = emit_move_insn (mem_rtx, reg_rtx); + RTX_FRAME_RELATED_P (insn) = 1; + } + else + emit_move_insn (reg_rtx, mem_rtx); + } + else + { + fprintf (file, "\t%s\t%s,", + (TARGET_SINGLE_FLOAT + ? (store_p ? "s.s" : "l.s") + : (store_p ? "s.d" : "l.d")), + reg_names[regno]); + fprintf (file, HOST_WIDE_INT_PRINT_DEC, + fp_offset - base_offset); + fprintf (file, "(%s)\n", reg_names[REGNO(base_reg_rtx)]); + } + + fp_offset -= fp_size; + } + } +} + +/* Set up the stack and frame (if desired) for the function. */ + +void +function_prologue (file, size) + FILE *file; + int size ATTRIBUTE_UNUSED; +{ +#ifndef FUNCTION_NAME_ALREADY_DECLARED + char *fnname; +#endif + long tsize = current_frame_info.total_size; + + ASM_OUTPUT_SOURCE_FILENAME (file, DECL_SOURCE_FILE (current_function_decl)); + +#ifdef SDB_DEBUGGING_INFO + if (debug_info_level != DINFO_LEVEL_TERSE && write_symbols == SDB_DEBUG) + ASM_OUTPUT_SOURCE_LINE (file, DECL_SOURCE_LINE (current_function_decl)); +#endif + + /* In mips16 mode, we may need to generate a 32 bit to handle + floating point arguments. The linker will arrange for any 32 bit + functions to call this stub, which will then jump to the 16 bit + function proper. */ + if (TARGET_MIPS16 && !TARGET_SOFT_FLOAT + && current_function_args_info.fp_code != 0) + build_mips16_function_stub (file); + + inside_function = 1; + +#ifndef FUNCTION_NAME_ALREADY_DECLARED + /* Get the function name the same way that toplev.c does before calling + assemble_start_function. This is needed so that the name used here + exactly matches the name used in ASM_DECLARE_FUNCTION_NAME. */ + fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0); + + if (!flag_inhibit_size_directive) + { + fputs ("\t.ent\t", file); + assemble_name (file, fnname); + fputs ("\n", file); + } + + assemble_name (file, fnname); + fputs (":\n", file); +#endif + + if (!flag_inhibit_size_directive) + { + /* .frame FRAMEREG, FRAMESIZE, RETREG */ + fprintf (file, + "\t.frame\t%s,%ld,%s\t\t# vars= %ld, regs= %d/%d, args= %d, extra= %ld\n", + (reg_names[(frame_pointer_needed) + ? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM]), + ((frame_pointer_needed && TARGET_MIPS16) + ? (tsize - current_function_outgoing_args_size) + : tsize), + reg_names[31 + GP_REG_FIRST], + current_frame_info.var_size, + current_frame_info.num_gp, + current_frame_info.num_fp, + current_function_outgoing_args_size, + current_frame_info.extra_size); + + /* .mask MASK, GPOFFSET; .fmask FPOFFSET */ + fprintf (file, "\t.mask\t0x%08lx,%ld\n\t.fmask\t0x%08lx,%ld\n", + current_frame_info.mask, + current_frame_info.gp_save_offset, + current_frame_info.fmask, + current_frame_info.fp_save_offset); + + /* Require: + OLD_SP == *FRAMEREG + FRAMESIZE => can find old_sp from nominated FP reg. + HIGHEST_GP_SAVED == *FRAMEREG + FRAMESIZE + GPOFFSET => can find saved regs. */ + } + + if (mips_entry && ! mips_can_use_return_insn ()) + { + int save16 = BITSET_P (current_frame_info.mask, 16); + int save17 = BITSET_P (current_frame_info.mask, 17); + int save31 = BITSET_P (current_frame_info.mask, 31); + int savearg = 0; + rtx insn; + + /* Look through the initial insns to see if any of them store + the function parameters into the incoming parameter storage + area. If they do, we delete the insn, and save the register + using the entry pseudo-instruction instead. We don't try to + look past a label, jump, or call. */ + for (insn = get_insns (); insn != NULL_RTX; insn = NEXT_INSN (insn)) + { + rtx note, set, src, dest, base, offset; + int hireg; + + if (GET_CODE (insn) == CODE_LABEL + || GET_CODE (insn) == JUMP_INSN + || GET_CODE (insn) == CALL_INSN) + break; + if (GET_CODE (insn) != INSN) + continue; + set = PATTERN (insn); + if (GET_CODE (set) != SET) + continue; + + /* An insn storing a function parameter will still have a + REG_EQUIV note on it mentioning the argument pointer. */ + note = find_reg_note (insn, REG_EQUIV, NULL_RTX); + if (note == NULL_RTX) + continue; + if (! reg_mentioned_p (arg_pointer_rtx, XEXP (note, 0))) + continue; + + src = SET_SRC (set); + if (GET_CODE (src) != REG + || REGNO (src) < GP_REG_FIRST + 4 + || REGNO (src) > GP_REG_FIRST + 7) + continue; + + dest = SET_DEST (set); + if (GET_CODE (dest) != MEM) + continue; + if (GET_MODE_SIZE (GET_MODE (dest)) == UNITS_PER_WORD) + ; + else if (GET_MODE_SIZE (GET_MODE (dest)) == 2 * UNITS_PER_WORD + && REGNO (src) < GP_REG_FIRST + 7) + ; + else + continue; + offset = const0_rtx; + base = eliminate_constant_term (XEXP (dest, 0), &offset); + if (GET_CODE (base) != REG + || GET_CODE (offset) != CONST_INT) + continue; + if (REGNO (base) == STACK_POINTER_REGNUM + && INTVAL (offset) == tsize + (REGNO (src) - 4) * UNITS_PER_WORD) + ; + else if (REGNO (base) == HARD_FRAME_POINTER_REGNUM + && (INTVAL (offset) + == (tsize + + (REGNO (src) - 4) * UNITS_PER_WORD + - current_function_outgoing_args_size))) + ; + else + continue; + + /* This insn stores a parameter onto the stack, in the same + location where the entry pseudo-instruction will put it. + Delete the insn, and arrange to tell the entry + instruction to save the register. */ + PUT_CODE (insn, NOTE); + NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (insn) = 0; + + hireg = (REGNO (src) + + HARD_REGNO_NREGS (REGNO (src), GET_MODE (dest)) + - 1); + if (hireg > savearg) + savearg = hireg; + } + + /* If this is a varargs function, we need to save all the + registers onto the stack anyhow. */ + if (current_function_stdarg || current_function_varargs) + savearg = GP_REG_FIRST + 7; + + fprintf (file, "\tentry\t"); + if (savearg > 0) + { + if (savearg == GP_REG_FIRST + 4) + fprintf (file, "%s", reg_names[savearg]); + else + fprintf (file, "%s-%s", reg_names[GP_REG_FIRST + 4], + reg_names[savearg]); + } + if (save16 || save17) + { + if (savearg > 0) + fprintf (file, ","); + fprintf (file, "%s", reg_names[GP_REG_FIRST + 16]); + if (save17) + fprintf (file, "-%s", reg_names[GP_REG_FIRST + 17]); + } + if (save31) + { + if (savearg > 0 || save16 || save17) + fprintf (file, ","); + fprintf (file, "%s", reg_names[GP_REG_FIRST + 31]); + } + fprintf (file, "\n"); + } + + if (TARGET_ABICALLS && (mips_abi == ABI_32 || mips_abi == ABI_O64)) + { + char *sp_str = reg_names[STACK_POINTER_REGNUM]; + + fprintf (file, "\t.set\tnoreorder\n\t.cpload\t%s\n\t.set\treorder\n", + reg_names[PIC_FUNCTION_ADDR_REGNUM]); + if (tsize > 0) + { + fprintf (file, "\t%s\t%s,%s,%ld\n", + (Pmode == DImode ? "dsubu" : "subu"), + sp_str, sp_str, tsize); + fprintf (file, "\t.cprestore %ld\n", current_frame_info.args_size); + } + + if (dwarf2out_do_frame ()) + dwarf2out_def_cfa ("", STACK_POINTER_REGNUM, tsize); + } +} + +/* Expand the prologue into a bunch of separate insns. */ + +void +mips_expand_prologue () +{ + int regno; + HOST_WIDE_INT tsize; + rtx tmp_rtx = 0; + char *arg_name = 0; + tree fndecl = current_function_decl; + tree fntype = TREE_TYPE (fndecl); + tree fnargs = DECL_ARGUMENTS (fndecl); + rtx next_arg_reg; + int i; + tree next_arg; + tree cur_arg; + CUMULATIVE_ARGS args_so_far; + rtx reg_18_save = NULL_RTX; + + /* If struct value address is treated as the first argument, make it so. */ + if (aggregate_value_p (DECL_RESULT (fndecl)) + && ! current_function_returns_pcc_struct + && struct_value_incoming_rtx == 0) + { + tree type = build_pointer_type (fntype); + tree function_result_decl = build_decl (PARM_DECL, NULL_TREE, type); + + DECL_ARG_TYPE (function_result_decl) = type; + TREE_CHAIN (function_result_decl) = fnargs; + fnargs = function_result_decl; + } + + /* Determine the last argument, and get its name. */ + + INIT_CUMULATIVE_ARGS (args_so_far, fntype, NULL_RTX, 0); + regno = GP_ARG_FIRST; + + for (cur_arg = fnargs; cur_arg != 0; cur_arg = next_arg) + { + tree passed_type = DECL_ARG_TYPE (cur_arg); + enum machine_mode passed_mode = TYPE_MODE (passed_type); + rtx entry_parm; + + if (TREE_ADDRESSABLE (passed_type)) + { + passed_type = build_pointer_type (passed_type); + passed_mode = Pmode; + } + + entry_parm = FUNCTION_ARG (args_so_far, passed_mode, passed_type, 1); + + if (entry_parm) + { + int words; + + /* passed in a register, so will get homed automatically */ + if (GET_MODE (entry_parm) == BLKmode) + words = (int_size_in_bytes (passed_type) + 3) / 4; + else + words = (GET_MODE_SIZE (GET_MODE (entry_parm)) + 3) / 4; + + regno = REGNO (entry_parm) + words - 1; + } + else + { + regno = GP_ARG_LAST+1; + break; + } + + FUNCTION_ARG_ADVANCE (args_so_far, passed_mode, passed_type, 1); + + next_arg = TREE_CHAIN (cur_arg); + if (next_arg == 0) + { + if (DECL_NAME (cur_arg)) + arg_name = IDENTIFIER_POINTER (DECL_NAME (cur_arg)); + + break; + } + } + + /* In order to pass small structures by value in registers compatibly with + the MIPS compiler, we need to shift the value into the high part of the + register. Function_arg has encoded a PARALLEL rtx, holding a vector of + adjustments to be made as the next_arg_reg variable, so we split up the + insns, and emit them separately. */ + + next_arg_reg = FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1); + if (next_arg_reg != 0 && GET_CODE (next_arg_reg) == PARALLEL) + { + rtvec adjust = XVEC (next_arg_reg, 0); + int num = GET_NUM_ELEM (adjust); + + for (i = 0; i < num; i++) + { + rtx pattern = RTVEC_ELT (adjust, i); + if (GET_CODE (pattern) != SET + || GET_CODE (SET_SRC (pattern)) != ASHIFT) + abort_with_insn (pattern, "Insn is not a shift"); + + PUT_CODE (SET_SRC (pattern), ASHIFTRT); + emit_insn (pattern); + } + } + + tsize = compute_frame_size (get_frame_size ()); + + /* If this function is a varargs function, store any registers that + would normally hold arguments ($4 - $7) on the stack. */ + if ((mips_abi == ABI_32 || mips_abi == ABI_O64) + && (! mips_entry || mips_can_use_return_insn ()) + && ((TYPE_ARG_TYPES (fntype) != 0 + && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) + != void_type_node)) + || (arg_name != 0 + && ((arg_name[0] == '_' + && strcmp (arg_name, "__builtin_va_alist") == 0) + || (arg_name[0] == 'v' + && strcmp (arg_name, "va_alist") == 0))))) + { + int offset = (regno - GP_ARG_FIRST) * UNITS_PER_WORD; + rtx ptr = stack_pointer_rtx; + + /* If we are doing svr4-abi, sp has already been decremented by tsize. */ + if (TARGET_ABICALLS) + offset += tsize; + + for (; regno <= GP_ARG_LAST; regno++) + { + if (offset != 0) + ptr = gen_rtx (PLUS, Pmode, stack_pointer_rtx, GEN_INT (offset)); + emit_move_insn (gen_rtx (MEM, gpr_mode, ptr), + gen_rtx (REG, gpr_mode, regno)); + + offset += GET_MODE_SIZE (gpr_mode); + } + } + + /* If we are using the entry pseudo instruction, it will + automatically subtract 32 from the stack pointer, so we don't + need to. The entry pseudo instruction is emitted by + function_prologue. */ + if (mips_entry && ! mips_can_use_return_insn ()) + { + if (tsize > 0 && tsize <= 32 && frame_pointer_needed) + { + rtx insn; + + /* If we are using a frame pointer with a small stack frame, + we need to initialize it here since it won't be done + below. */ + if (TARGET_MIPS16 && current_function_outgoing_args_size != 0) + { + rtx incr = GEN_INT (current_function_outgoing_args_size); + if (Pmode == DImode) + insn = emit_insn (gen_adddi3 (hard_frame_pointer_rtx, + stack_pointer_rtx, + incr)); + else + insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx, + stack_pointer_rtx, + incr)); + } + else if (Pmode == DImode) + insn = emit_insn (gen_movdi (hard_frame_pointer_rtx, + stack_pointer_rtx)); + else + insn = emit_insn (gen_movsi (hard_frame_pointer_rtx, + stack_pointer_rtx)); + + RTX_FRAME_RELATED_P (insn) = 1; + } + + /* We may need to save $18, if it is used to call a function + which may return a floating point value. Set up a sequence + of instructions to do so. Later on we emit them at the right + moment. */ + if (TARGET_MIPS16 && BITSET_P (current_frame_info.mask, 18)) + { + rtx reg_rtx = gen_rtx (REG, gpr_mode, GP_REG_FIRST + 3); + long gp_offset, base_offset; + + gp_offset = current_frame_info.gp_sp_offset; + if (BITSET_P (current_frame_info.mask, 16)) + gp_offset -= UNITS_PER_WORD; + if (BITSET_P (current_frame_info.mask, 17)) + gp_offset -= UNITS_PER_WORD; + if (BITSET_P (current_frame_info.mask, 31)) + gp_offset -= UNITS_PER_WORD; + if (tsize > 32767) + base_offset = tsize; + else + base_offset = 0; + start_sequence (); + emit_move_insn (reg_rtx, + gen_rtx (REG, gpr_mode, GP_REG_FIRST + 18)); + emit_move_insn (gen_rtx (MEM, gpr_mode, + gen_rtx (PLUS, Pmode, stack_pointer_rtx, + GEN_INT (gp_offset + - base_offset))), + reg_rtx); + reg_18_save = gen_sequence (); + end_sequence (); + } + + if (tsize > 32) + tsize -= 32; + else + { + tsize = 0; + if (reg_18_save != NULL_RTX) + emit_insn (reg_18_save); + } + } + + if (tsize > 0) + { + rtx tsize_rtx = GEN_INT (tsize); + + /* If we are doing svr4-abi, sp move is done by + function_prologue. In mips16 mode with a large frame, we + save the registers before adjusting the stack. */ + if ((!TARGET_ABICALLS || (mips_abi != ABI_32 && mips_abi != ABI_O64)) + && (!TARGET_MIPS16 || tsize <= 32767)) + { + rtx insn; + + if (tsize > 32767) + { + tmp_rtx = gen_rtx (REG, Pmode, MIPS_TEMP1_REGNUM); + + /* Instruction splitting doesn't preserve the RTX_FRAME_RELATED_P + bit, so make sure that we don't emit anything that can be + split. */ + /* ??? There is no DImode ori immediate pattern, so we can only + do this for 32 bit code. */ + if (large_int (tsize_rtx) && GET_MODE (tmp_rtx) == SImode) + { + insn = emit_move_insn (tmp_rtx, + GEN_INT (tsize & 0xffff0000)); + RTX_FRAME_RELATED_P (insn) = 1; + insn = emit_insn (gen_iorsi3 (tmp_rtx, tmp_rtx, + GEN_INT (tsize & 0x0000ffff))); + RTX_FRAME_RELATED_P (insn) = 1; + } + else + { + insn = emit_move_insn (tmp_rtx, tsize_rtx); + RTX_FRAME_RELATED_P (insn) = 1; + } + + tsize_rtx = tmp_rtx; + } + + if (Pmode == DImode) + insn = emit_insn (gen_subdi3 (stack_pointer_rtx, stack_pointer_rtx, + tsize_rtx)); + else + insn = emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx, + tsize_rtx)); + + RTX_FRAME_RELATED_P (insn) = 1; + } + + if (! mips_entry) + save_restore_insns (1, tmp_rtx, tsize, (FILE *)0); + else if (reg_18_save != NULL_RTX) + emit_insn (reg_18_save); + + if ((!TARGET_ABICALLS || (mips_abi != ABI_32 && mips_abi != ABI_O64)) + && TARGET_MIPS16 + && tsize > 32767) + { + rtx reg_rtx; + + if (!frame_pointer_needed) + abort (); + + reg_rtx = gen_rtx (REG, Pmode, 3); + emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx); + emit_move_insn (reg_rtx, tsize_rtx); + if (Pmode == DImode) + emit_insn (gen_subdi3 (hard_frame_pointer_rtx, + hard_frame_pointer_rtx, + reg_rtx)); + else + emit_insn (gen_subsi3 (hard_frame_pointer_rtx, + hard_frame_pointer_rtx, + reg_rtx)); + emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx); + } + + if (frame_pointer_needed) + { + rtx insn = 0; + + /* On the mips16, we encourage the use of unextended + instructions when using the frame pointer by pointing the + frame pointer ahead of the argument space allocated on + the stack. */ + if ((! TARGET_ABICALLS || (mips_abi != ABI_32 && mips_abi != ABI_O64)) + && TARGET_MIPS16 + && tsize > 32767) + { + /* In this case, we have already copied the stack + pointer into the frame pointer, above. We need only + adjust for the outgoing argument size. */ + if (current_function_outgoing_args_size != 0) + { + rtx incr = GEN_INT (current_function_outgoing_args_size); + if (Pmode == DImode) + insn = emit_insn (gen_adddi3 (hard_frame_pointer_rtx, + hard_frame_pointer_rtx, + incr)); + else + insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx, + hard_frame_pointer_rtx, + incr)); + } + } + else if (TARGET_MIPS16 && current_function_outgoing_args_size != 0) + { + rtx incr = GEN_INT (current_function_outgoing_args_size); + if (Pmode == DImode) + insn = emit_insn (gen_adddi3 (hard_frame_pointer_rtx, + stack_pointer_rtx, + incr)); + else + insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx, + stack_pointer_rtx, + incr)); + } + else if (Pmode == DImode) + insn = emit_insn (gen_movdi (hard_frame_pointer_rtx, + stack_pointer_rtx)); + else + insn = emit_insn (gen_movsi (hard_frame_pointer_rtx, + stack_pointer_rtx)); + + if (insn) + RTX_FRAME_RELATED_P (insn) = 1; + } + + if (TARGET_ABICALLS && (mips_abi != ABI_32 && mips_abi != ABI_O64)) + emit_insn (gen_loadgp (XEXP (DECL_RTL (current_function_decl), 0), + gen_rtx (REG, DImode, 25))); + } + + /* If we are profiling, make sure no instructions are scheduled before + the call to mcount. */ + + if (profile_flag || profile_block_flag) + emit_insn (gen_blockage ()); +} + +/* Do any necessary cleanup after a function to restore stack, frame, + and regs. */ + +#define RA_MASK ((long) 0x80000000) /* 1 << 31 */ +#define PIC_OFFSET_TABLE_MASK (1 << (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST)) + +void +function_epilogue (file, size) + FILE *file ATTRIBUTE_UNUSED; + HOST_WIDE_INT size ATTRIBUTE_UNUSED; +{ + char *fnname; + +#ifndef FUNCTION_NAME_ALREADY_DECLARED + /* Get the function name the same way that toplev.c does before calling + assemble_start_function. This is needed so that the name used here + exactly matches the name used in ASM_DECLARE_FUNCTION_NAME. */ + fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0); + + if (!flag_inhibit_size_directive) + { + fputs ("\t.end\t", file); + assemble_name (file, fnname); + fputs ("\n", file); + } +#endif + + if (TARGET_STATS) + { + int num_gp_regs = current_frame_info.gp_reg_size / 4; + int num_fp_regs = current_frame_info.fp_reg_size / 8; + int num_regs = num_gp_regs + num_fp_regs; + char *name = fnname; + + if (name[0] == '*') + name++; + + dslots_load_total += num_regs; + + fprintf (stderr, + "%-20s fp=%c leaf=%c alloca=%c setjmp=%c stack=%4ld arg=%3d reg=%2d/%d delay=%3d/%3dL %3d/%3dJ refs=%3d/%3d/%3d", + name, frame_pointer_needed ? 'y' : 'n', + (current_frame_info.mask & RA_MASK) != 0 ? 'n' : 'y', + current_function_calls_alloca ? 'y' : 'n', + current_function_calls_setjmp ? 'y' : 'n', + current_frame_info.total_size, + current_function_outgoing_args_size, num_gp_regs, num_fp_regs, + dslots_load_total, dslots_load_filled, + dslots_jump_total, dslots_jump_filled, + num_refs[0], num_refs[1], num_refs[2]); + + if (HALF_PIC_NUMBER_PTRS > prev_half_pic_ptrs) + { + fprintf (stderr, + " half-pic=%3d", HALF_PIC_NUMBER_PTRS - prev_half_pic_ptrs); + prev_half_pic_ptrs = HALF_PIC_NUMBER_PTRS; + } + + if (HALF_PIC_NUMBER_REFS > prev_half_pic_refs) + { + fprintf (stderr, + " pic-ref=%3d", HALF_PIC_NUMBER_REFS - prev_half_pic_refs); + prev_half_pic_refs = HALF_PIC_NUMBER_REFS; + } + + fputc ('\n', stderr); + } + + /* Reset state info for each function. */ + inside_function = 0; + ignore_line_number = 0; + dslots_load_total = 0; + dslots_jump_total = 0; + dslots_load_filled = 0; + dslots_jump_filled = 0; + num_refs[0] = 0; + num_refs[1] = 0; + num_refs[2] = 0; + mips_load_reg = 0; + mips_load_reg2 = 0; + current_frame_info = zero_frame_info; + + while (string_constants != NULL) + { + struct string_constant *next; + + next = string_constants->next; + free (string_constants); + string_constants = next; + } + + /* Restore the output file if optimizing the GP (optimizing the GP causes + the text to be diverted to a tempfile, so that data decls come before + references to the data). */ + + if (TARGET_GP_OPT && ! TARGET_MIPS16 && ! TARGET_GAS) + asm_out_file = asm_out_data_file; +} + +/* Expand the epilogue into a bunch of separate insns. */ + +void +mips_expand_epilogue () +{ + HOST_WIDE_INT tsize = current_frame_info.total_size; + rtx tsize_rtx = GEN_INT (tsize); + rtx tmp_rtx = (rtx)0; + + if (mips_can_use_return_insn ()) + { + emit_insn (gen_return ()); + return; + } + + if (mips_entry && ! mips_can_use_return_insn ()) + tsize -= 32; + + if (tsize > 32767 && ! TARGET_MIPS16) + { + tmp_rtx = gen_rtx (REG, Pmode, MIPS_TEMP1_REGNUM); + emit_move_insn (tmp_rtx, tsize_rtx); + tsize_rtx = tmp_rtx; + } + + if (tsize > 0) + { + long orig_tsize = tsize; + + if (frame_pointer_needed) + { + emit_insn (gen_blockage ()); + + /* On the mips16, the frame pointer is offset from the stack + pointer by current_function_outgoing_args_size. We + account for that by changing tsize. Note that this can + actually make tsize negative. */ + if (TARGET_MIPS16) + { + tsize -= current_function_outgoing_args_size; + + /* If we have a large frame, it's easier to add to $17 + than to $sp, since the mips16 has no instruction to + add a register to $sp. */ + if (orig_tsize > 32767) + { + rtx g6_rtx = gen_rtx (REG, Pmode, GP_REG_FIRST + 6); + + emit_move_insn (g6_rtx, GEN_INT (tsize)); + if (Pmode == DImode) + emit_insn (gen_adddi3 (hard_frame_pointer_rtx, + hard_frame_pointer_rtx, + g6_rtx)); + else + emit_insn (gen_addsi3 (hard_frame_pointer_rtx, + hard_frame_pointer_rtx, + g6_rtx)); + tsize = 0; + } + + if (tsize && tsize != orig_tsize) + tsize_rtx = GEN_INT (tsize); + } + + if (Pmode == DImode) + emit_insn (gen_movdi (stack_pointer_rtx, hard_frame_pointer_rtx)); + else + emit_insn (gen_movsi (stack_pointer_rtx, hard_frame_pointer_rtx)); + } + + /* The GP/PIC register is implicitly used by all SYMBOL_REFs, so if we + are going to restore it, then we must emit a blockage insn to + prevent the scheduler from moving the restore out of the epilogue. */ + else if (TARGET_ABICALLS && mips_abi != ABI_32 && mips_abi != ABI_O64 + && (current_frame_info.mask + & (1L << (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST)))) + emit_insn (gen_blockage ()); + + save_restore_insns (0, tmp_rtx, orig_tsize, (FILE *)0); + + /* In mips16 mode with a large frame, we adjust the stack + pointer before restoring the registers. In this case, we + should always be using a frame pointer, so everything should + have been handled above. */ + if (tsize > 32767 && TARGET_MIPS16) + abort (); + + emit_insn (gen_blockage ()); + if (Pmode == DImode && tsize != 0) + emit_insn (gen_adddi3 (stack_pointer_rtx, stack_pointer_rtx, + tsize_rtx)); + else if (tsize != 0) + emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, + tsize_rtx)); + } + + /* The mips16 loads the return address into $7, not $31. */ + if (TARGET_MIPS16 && (current_frame_info.mask & RA_MASK) != 0) + emit_jump_insn (gen_return_internal (gen_rtx (REG, Pmode, + GP_REG_FIRST + 7))); + else + emit_jump_insn (gen_return_internal (gen_rtx (REG, Pmode, + GP_REG_FIRST + 31))); +} + +/* Return nonzero if this function is known to have a null epilogue. + This allows the optimizer to omit jumps to jumps if no stack + was created. */ + +int +mips_can_use_return_insn () +{ + if (! reload_completed) + return 0; + + if (regs_ever_live[31] || profile_flag) + return 0; + + /* In mips16 mode, a function which returns a floating point value + needs to arrange to copy the return value into the floating point + registers. */ + if (TARGET_MIPS16 + && mips16_hard_float + && ! aggregate_value_p (DECL_RESULT (current_function_decl)) + && (GET_MODE_CLASS (DECL_MODE (DECL_RESULT (current_function_decl))) + == MODE_FLOAT) + && (! TARGET_SINGLE_FLOAT + || (GET_MODE_SIZE (DECL_MODE (DECL_RESULT (current_function_decl))) + <= 4))) + return 0; + + if (current_frame_info.initialized) + return current_frame_info.total_size == 0; + + return compute_frame_size (get_frame_size ()) == 0; +} + +/* Choose the section to use for the constant rtx expression X that has + mode MODE. */ + +void +mips_select_rtx_section (mode, x) + enum machine_mode mode; + rtx x ATTRIBUTE_UNUSED; +{ + if (TARGET_MIPS16) + { + /* In mips16 mode, the constant table always goes in the same section + as the function, so that constants can be loaded using PC relative + addressing. */ + function_section (current_function_decl); + } + else if (TARGET_EMBEDDED_DATA) + { + /* For embedded applications, always put constants in read-only data, + in order to reduce RAM usage. */ + READONLY_DATA_SECTION (); + } + else + { + /* For hosted applications, always put constants in small data if + possible, as this gives the best performance. */ + + if (GET_MODE_SIZE (mode) <= mips_section_threshold + && mips_section_threshold > 0) + SMALL_DATA_SECTION (); + else + READONLY_DATA_SECTION (); + } +} + +/* Choose the section to use for DECL. RELOC is true if its value contains + any relocatable expression. + + Some of the logic used here needs to be replicated in + ENCODE_SECTION_INFO in mips.h so that references to these symbols + are done correctly. Specifically, at least all symbols assigned + here to rom (.text and/or .rodata) must not be referenced via + ENCODE_SECTION_INFO with %gprel, as the rom might be too far away. + + If you need to make a change here, you probably should check + ENCODE_SECTION_INFO to see if it needs a similar change. */ + +void +mips_select_section (decl, reloc) + tree decl; + int reloc; +{ + int size = int_size_in_bytes (TREE_TYPE (decl)); + + if ((TARGET_EMBEDDED_PIC || TARGET_MIPS16) + && TREE_CODE (decl) == STRING_CST + && !flag_writable_strings) + /* For embedded position independent code, put constant strings in the + text section, because the data section is limited to 64K in size. + For mips16 code, put strings in the text section so that a PC + relative load instruction can be used to get their address. */ + text_section (); + else if (TARGET_EMBEDDED_DATA) + { + /* For embedded applications, always put an object in read-only data + if possible, in order to reduce RAM usage. */ + + if (((TREE_CODE (decl) == VAR_DECL + && TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl) + && DECL_INITIAL (decl) + && (DECL_INITIAL (decl) == error_mark_node + || TREE_CONSTANT (DECL_INITIAL (decl)))) + /* Deal with calls from output_constant_def_contents. */ + || (TREE_CODE (decl) != VAR_DECL + && (TREE_CODE (decl) != STRING_CST + || !flag_writable_strings))) + && ! (flag_pic && reloc)) + READONLY_DATA_SECTION (); + else if (size > 0 && size <= mips_section_threshold) + SMALL_DATA_SECTION (); + else + data_section (); + } + else + { + /* For hosted applications, always put an object in small data if + possible, as this gives the best performance. */ + + if (size > 0 && size <= mips_section_threshold) + SMALL_DATA_SECTION (); + else if (((TREE_CODE (decl) == VAR_DECL + && TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl) + && DECL_INITIAL (decl) + && (DECL_INITIAL (decl) == error_mark_node + || TREE_CONSTANT (DECL_INITIAL (decl)))) + /* Deal with calls from output_constant_def_contents. */ + || (TREE_CODE (decl) != VAR_DECL + && (TREE_CODE (decl) != STRING_CST + || !flag_writable_strings))) + && ! (flag_pic && reloc)) + READONLY_DATA_SECTION (); + else + data_section (); + } +} + +#ifdef MIPS_ABI_DEFAULT + +/* Support functions for the 64 bit ABI. */ + +/* Return register to use for a function return value with VALTYPE for function + FUNC. */ + +rtx +mips_function_value (valtype, func) + tree valtype; + tree func ATTRIBUTE_UNUSED; +{ + int reg = GP_RETURN; + enum machine_mode mode = TYPE_MODE (valtype); + enum mode_class mclass = GET_MODE_CLASS (mode); + int unsignedp = TREE_UNSIGNED (valtype); + + /* Since we define PROMOTE_FUNCTION_RETURN, we must promote the mode + just as PROMOTE_MODE does. */ + mode = promote_mode (valtype, mode, &unsignedp, 1); + + /* ??? How should we return complex float? */ + if (mclass == MODE_FLOAT || mclass == MODE_COMPLEX_FLOAT) + { + if (TARGET_SINGLE_FLOAT + && (mclass == MODE_FLOAT + ? GET_MODE_SIZE (mode) > 4 : GET_MODE_SIZE (mode) / 2 > 4)) + reg = GP_RETURN; + else + reg = FP_RETURN; + } + + else if (TREE_CODE (valtype) == RECORD_TYPE + && mips_abi != ABI_32 + && mips_abi != ABI_O64 + && mips_abi != ABI_EABI) + { + /* A struct with only one or two floating point fields is returned in + the floating point registers. */ + tree field, fields[2]; + int i; + + for (i = 0, field = TYPE_FIELDS (valtype); field; + field = TREE_CHAIN (field)) + { + if (TREE_CODE (field) != FIELD_DECL) + continue; + + if (TREE_CODE (TREE_TYPE (field)) != REAL_TYPE || i >= 2) + break; + + fields[i++] = field; + } + + /* Must check i, so that we reject structures with no elements. */ + if (! field) + { + if (i == 1) + { + /* The structure has DImode, but we don't allow DImode values + in FP registers, so we use a PARALLEL even though it isn't + strictly necessary. */ + enum machine_mode field_mode = TYPE_MODE (TREE_TYPE (fields[0])); + + return gen_rtx (PARALLEL, mode, + gen_rtvec (1, + gen_rtx (EXPR_LIST, VOIDmode, + gen_rtx (REG, field_mode, + FP_RETURN), + const0_rtx))); + } + + else if (i == 2) + { + enum machine_mode first_mode + = TYPE_MODE (TREE_TYPE (fields[0])); + enum machine_mode second_mode + = TYPE_MODE (TREE_TYPE (fields[1])); + int first_offset + = TREE_INT_CST_LOW (DECL_FIELD_BITPOS (fields[0])); + int second_offset + = TREE_INT_CST_LOW (DECL_FIELD_BITPOS (fields[1])); + + return gen_rtx (PARALLEL, mode, + gen_rtvec (2, + gen_rtx (EXPR_LIST, VOIDmode, + gen_rtx (REG, first_mode, + FP_RETURN), + GEN_INT (first_offset + / BITS_PER_UNIT)), + gen_rtx (EXPR_LIST, VOIDmode, + gen_rtx (REG, second_mode, + FP_RETURN + 2), + GEN_INT (second_offset + / BITS_PER_UNIT)))); + } + } + } + + return gen_rtx (REG, mode, reg); +} + +/* The implementation of FUNCTION_ARG_PASS_BY_REFERENCE. Return + nonzero when an argument must be passed by reference. */ + +int +function_arg_pass_by_reference (cum, mode, type, named) + CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED; + enum machine_mode mode; + tree type; + int named ATTRIBUTE_UNUSED; +{ + int size; + + if (mips_abi != ABI_EABI) + return 0; + + /* ??? How should SCmode be handled? */ + if (type == NULL_TREE || mode == DImode || mode == DFmode) + return 0; + + size = int_size_in_bytes (type); + return size == -1 || size > UNITS_PER_WORD; +} +#endif + +/* This function returns the register class required for a secondary + register when copying between one of the registers in CLASS, and X, + using MODE. If IN_P is nonzero, the copy is going from X to the + register, otherwise the register is the source. A return value of + NO_REGS means that no secondary register is required. */ + +enum reg_class +mips_secondary_reload_class (class, mode, x, in_p) + enum reg_class class; + enum machine_mode mode; + rtx x; + int in_p; +{ + enum reg_class gr_regs = TARGET_MIPS16 ? M16_REGS : GR_REGS; + int regno = -1; + int gp_reg_p; + + if (GET_CODE (x) == SIGN_EXTEND) + { + int off = 0; + + x = XEXP (x, 0); + + /* We may be called with reg_renumber NULL from regclass. + ??? This is probably a bug. */ + if (reg_renumber) + regno = true_regnum (x); + else + { + while (GET_CODE (x) == SUBREG) + { + off += SUBREG_WORD (x); + x = SUBREG_REG (x); + } + + if (GET_CODE (x) == REG) + regno = REGNO (x) + off; + } + } + + else if (GET_CODE (x) == REG || GET_CODE (x) == SUBREG) + regno = true_regnum (x); + + gp_reg_p = TARGET_MIPS16 ? M16_REG_P (regno) : GP_REG_P (regno); + + /* We always require a general register when copying anything to + HILO_REGNUM, except when copying an SImode value from HILO_REGNUM + to a general register, or when copying from register 0. */ + if (class == HILO_REG && regno != GP_REG_FIRST + 0) + return ((! in_p + && gp_reg_p + && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (SImode)) + ? NO_REGS : gr_regs); + else if (regno == HILO_REGNUM) + return ((in_p + && class == gr_regs + && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (SImode)) + ? NO_REGS : gr_regs); + + /* Copying from HI or LO to anywhere other than a general register + requires a general register. */ + if (class == HI_REG || class == LO_REG || class == MD_REGS) + { + if (TARGET_MIPS16 && in_p) + { + /* We can't really copy to HI or LO at all in mips16 mode. */ + return M16_REGS; + } + return gp_reg_p ? NO_REGS : gr_regs; + } + if (MD_REG_P (regno)) + { + if (TARGET_MIPS16 && ! in_p) + { + /* We can't really copy to HI or LO at all in mips16 mode. */ + return M16_REGS; + } + return class == gr_regs ? NO_REGS : gr_regs; + } + + /* We can only copy a value to a condition code register from a + floating point register, and even then we require a scratch + floating point register. We can only copy a value out of a + condition code register into a general register. */ + if (class == ST_REGS) + { + if (in_p) + return FP_REGS; + return GP_REG_P (regno) ? NO_REGS : GR_REGS; + } + if (ST_REG_P (regno)) + { + if (! in_p) + return FP_REGS; + return class == GR_REGS ? NO_REGS : GR_REGS; + } + + /* In mips16 mode, going between memory and anything but M16_REGS + requires an M16_REG. */ + if (TARGET_MIPS16) + { + if (class != M16_REGS && class != M16_NA_REGS) + { + if (gp_reg_p) + return NO_REGS; + return M16_REGS; + } + if (! gp_reg_p) + { + if (class == M16_REGS || class == M16_NA_REGS) + return NO_REGS; + return M16_REGS; + } + } + + return NO_REGS; +} + +/* For each mips16 function which refers to GP relative symbols, we + use a pseudo register, initialized at the start of the function, to + hold the $gp value. */ + +rtx +mips16_gp_pseudo_reg () +{ + if (mips16_gp_pseudo_rtx == NULL_RTX) + { + rtx const_gp; + rtx insn, scan; + + mips16_gp_pseudo_rtx = gen_reg_rtx (Pmode); + RTX_UNCHANGING_P (mips16_gp_pseudo_rtx) = 1; + + /* We want to initialize this to a value which gcc will believe + is constant. */ + const_gp = gen_rtx (CONST, Pmode, + gen_rtx (REG, Pmode, GP_REG_FIRST + 28)); + + start_sequence (); + emit_move_insn (mips16_gp_pseudo_rtx, const_gp); + insn = gen_sequence (); + end_sequence (); + + push_topmost_sequence (); + /* We need to emit the initialization after the FUNCTION_BEG + note, so that it will be integrated. */ + for (scan = get_insns (); scan != NULL_RTX; scan = NEXT_INSN (scan)) + if (GET_CODE (scan) == NOTE + && NOTE_LINE_NUMBER (scan) == NOTE_INSN_FUNCTION_BEG) + break; + if (scan == NULL_RTX) + scan = get_insns (); + insn = emit_insn_after (insn, scan); + pop_topmost_sequence (); + } + + return mips16_gp_pseudo_rtx; +} + +/* Return an RTX which represents the signed 16 bit offset from the + $gp register for the given symbol. This is only used on the + mips16. */ + +rtx +mips16_gp_offset (sym) + rtx sym; +{ + tree gp; + + if (GET_CODE (sym) != SYMBOL_REF + || ! SYMBOL_REF_FLAG (sym)) + abort (); + + /* We use a special identifier to represent the value of the gp + register. */ + gp = get_identifier ("__mips16_gp_value"); + + return gen_rtx (CONST, Pmode, + gen_rtx (MINUS, Pmode, sym, + gen_rtx (SYMBOL_REF, Pmode, + IDENTIFIER_POINTER (gp)))); +} + +/* Return nonzero if the given RTX represents a signed 16 bit offset + from the $gp register. */ + +int +mips16_gp_offset_p (x) + rtx x; +{ + if (GET_CODE (x) == CONST) + x = XEXP (x, 0); + + /* It's OK to add a small integer value to a gp offset. */ + if (GET_CODE (x) == PLUS) + { + if (GET_CODE (XEXP (x, 1)) == CONST_INT + && SMALL_INT (XEXP (x, 1))) + return mips16_gp_offset_p (XEXP (x, 0)); + if (GET_CODE (XEXP (x, 0)) == CONST_INT + && SMALL_INT (XEXP (x, 0))) + return mips16_gp_offset_p (XEXP (x, 1)); + return 0; + } + + /* Make sure it is in the form SYM - __mips16_gp_value. */ + return (GET_CODE (x) == MINUS + && GET_CODE (XEXP (x, 0)) == SYMBOL_REF + && SYMBOL_REF_FLAG (XEXP (x, 0)) + && GET_CODE (XEXP (x, 1)) == SYMBOL_REF + && strcmp (XSTR (XEXP (x, 1), 0), "__mips16_gp_value") == 0); +} + +/* Output a GP offset. We don't want to print the subtraction of + __mips16_gp_value; it is implicitly represented by the %gprel which + should have been printed by the caller. */ + +static void +mips16_output_gp_offset (file, x) + FILE *file; + rtx x; +{ + if (GET_CODE (x) == CONST) + x = XEXP (x, 0); + + if (GET_CODE (x) == PLUS) + { + mips16_output_gp_offset (file, XEXP (x, 0)); + fputs ("+", file); + mips16_output_gp_offset (file, XEXP (x, 1)); + return; + } + + if (GET_CODE (x) == MINUS + && GET_CODE (XEXP (x, 1)) == SYMBOL_REF + && strcmp (XSTR (XEXP (x, 1), 0), "__mips16_gp_value") == 0) + { + mips16_output_gp_offset (file, XEXP (x, 0)); + return; + } + + output_addr_const (file, x); +} + +/* Return nonzero if a constant should not be output until after the + function. This is true of most string constants, so that we can + use a more efficient PC relative reference. However, a static + inline function may never call assemble_function_end to write out + the constant pool, so don't try to postpone the constant in that + case. + + ??? It's really a bug that a static inline function can put stuff + in the constant pool even if the function itself is not output. + + We record which string constants we've seen, so that we know which + ones might use the more efficient reference. */ + +int +mips16_constant_after_function_p (x) + tree x; +{ + if (TREE_CODE (x) == STRING_CST + && ! flag_writable_strings + && current_function_decl != 0 + && ! DECL_DEFER_OUTPUT (current_function_decl) + && ! (DECL_INLINE (current_function_decl) + && ((! TREE_PUBLIC (current_function_decl) + && ! TREE_ADDRESSABLE (current_function_decl) + && ! flag_keep_inline_functions) + || DECL_EXTERNAL (current_function_decl)))) + { + struct string_constant *n; + + n = (struct string_constant *) xmalloc (sizeof *n); + n->label = XSTR (XEXP (TREE_CST_RTL (x), 0), 0); + n->next = string_constants; + string_constants = n; + + return 1; + } + + return 0; +} + +/* Validate a constant for the mips16. This rejects general symbolic + addresses, which must be loaded from memory. If ADDR is nonzero, + this should reject anything which is not a legal address. If + ADDEND is nonzero, this is being added to something else. */ + +int +mips16_constant (x, mode, addr, addend) + rtx x; + enum machine_mode mode; + int addr; + int addend; +{ + while (GET_CODE (x) == CONST) + x = XEXP (x, 0); + + switch (GET_CODE (x)) + { + default: + return 0; + + case PLUS: + return (mips16_constant (XEXP (x, 0), mode, addr, 1) + && mips16_constant (XEXP (x, 1), mode, addr, 1)); + + case SYMBOL_REF: + if (addr && GET_MODE_SIZE (mode) != 4 && GET_MODE_SIZE (mode) != 8) + return 0; + if (CONSTANT_POOL_ADDRESS_P (x)) + return 1; + + /* If we aren't looking for a memory address, we can accept a GP + relative symbol, which will have SYMBOL_REF_FLAG set; movsi + knows how to handle this. We can always accept a string + constant, which is the other case in which SYMBOL_REF_FLAG + will be set. */ + if (! addr && ! addend && SYMBOL_REF_FLAG (x) && mode == Pmode) + return 1; + + /* We can accept a string constant, which will have + SYMBOL_REF_FLAG set but must be recognized by name to + distinguish from a GP accessible symbol. The name of a + string constant will have been generated by + ASM_GENERATE_INTERNAL_LABEL as called by output_constant_def. */ + if (SYMBOL_REF_FLAG (x)) + { + char *name = XSTR (x, 0); + + return (name[0] == '*' + && strncmp (name + 1, LOCAL_LABEL_PREFIX, + sizeof LOCAL_LABEL_PREFIX - 1) == 0); + } + + return 0; + + case LABEL_REF: + if (addr && GET_MODE_SIZE (mode) != 4 && GET_MODE_SIZE (mode) != 8) + return 0; + return 1; + + case CONST_INT: + if (addr && ! addend) + return 0; + return INTVAL (x) > - 0x10000 && INTVAL (x) <= 0xffff; + + case REG: + /* We need to treat $gp as a legitimate constant, because + mips16_gp_pseudo_reg assumes that. */ + return REGNO (x) == GP_REG_FIRST + 28; + } +} + +/* Write out code to move floating point arguments in or out of + general registers. Output the instructions to FILE. FP_CODE is + the code describing which arguments are present (see the comment at + the definition of CUMULATIVE_ARGS in mips.h). FROM_FP_P is non-zero if + we are copying from the floating point registers. */ + +static void +mips16_fp_args (file, fp_code, from_fp_p) + FILE *file; + int fp_code; + int from_fp_p; +{ + const char *s; + int gparg, fparg; + unsigned int f; + + /* This code only works for the original 32 bit ABI and the O64 ABI. */ + if (mips_abi != ABI_32 && mips_abi != ABI_O64) + abort (); + + if (from_fp_p) + s = "mfc1"; + else + s = "mtc1"; + gparg = GP_ARG_FIRST; + fparg = FP_ARG_FIRST; + for (f = (unsigned int) fp_code; f != 0; f >>= 2) + { + if ((f & 3) == 1) + { + if ((fparg & 1) != 0) + ++fparg; + fprintf (file, "\t%s\t%s,%s\n", s, + reg_names[gparg], reg_names[fparg]); + } + else if ((f & 3) == 2) + { + if (TARGET_64BIT) + fprintf (file, "\td%s\t%s,%s\n", s, + reg_names[gparg], reg_names[fparg]); + else + { + if ((fparg & 1) != 0) + ++fparg; + if (TARGET_BIG_ENDIAN) + fprintf (file, "\t%s\t%s,%s\n\t%s\t%s,%s\n", s, + reg_names[gparg], reg_names[fparg + 1], s, + reg_names[gparg + 1], reg_names[fparg]); + else + fprintf (file, "\t%s\t%s,%s\n\t%s\t%s,%s\n", s, + reg_names[gparg], reg_names[fparg], s, + reg_names[gparg + 1], reg_names[fparg + 1]); + ++gparg; + ++fparg; + } + } + else + abort (); + + ++gparg; + ++fparg; + } +} + +/* Build a mips16 function stub. This is used for functions which + take aruments in the floating point registers. It is 32 bit code + that moves the floating point args into the general registers, and + then jumps to the 16 bit code. */ + +static void +build_mips16_function_stub (file) + FILE *file; +{ + char *fnname; + char *secname, *stubname; + tree stubid, stubdecl; + int need_comma; + unsigned int f; + + fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0); + secname = (char *) alloca (strlen (fnname) + 20); + sprintf (secname, ".mips16.fn.%s", fnname); + stubname = (char *) alloca (strlen (fnname) + 20); + sprintf (stubname, "__fn_stub_%s", fnname); + stubid = get_identifier (stubname); + stubdecl = build_decl (FUNCTION_DECL, stubid, + build_function_type (void_type_node, NULL_TREE)); + DECL_SECTION_NAME (stubdecl) = build_string (strlen (secname), secname); + + fprintf (file, "\t# Stub function for %s (", current_function_name); + need_comma = 0; + for (f = (unsigned int) current_function_args_info.fp_code; f != 0; f >>= 2) + { + fprintf (file, "%s%s", + need_comma ? ", " : "", + (f & 3) == 1 ? "float" : "double"); + need_comma = 1; + } + fprintf (file, ")\n"); + + fprintf (file, "\t.set\tnomips16\n"); + function_section (stubdecl); + ASM_OUTPUT_ALIGN (file, floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT)); + + /* ??? If FUNCTION_NAME_ALREADY_DECLARED is defined, then we are + within a .ent, and we can not emit another .ent. */ +#ifndef FUNCTION_NAME_ALREADY_DECLARED + fputs ("\t.ent\t", file); + assemble_name (file, stubname); + fputs ("\n", file); +#endif + + assemble_name (file, stubname); + fputs (":\n", file); + + /* We don't want the assembler to insert any nops here. */ + fprintf (file, "\t.set\tnoreorder\n"); + + mips16_fp_args (file, current_function_args_info.fp_code, 1); + + fprintf (asm_out_file, "\t.set\tnoat\n"); + fprintf (asm_out_file, "\tla\t%s,", reg_names[GP_REG_FIRST + 1]); + assemble_name (file, fnname); + fprintf (file, "\n"); + fprintf (asm_out_file, "\tjr\t%s\n", reg_names[GP_REG_FIRST + 1]); + fprintf (asm_out_file, "\t.set\tat\n"); + + /* Unfortunately, we can't fill the jump delay slot. We can't fill + with one of the mfc1 instructions, because the result is not + available for one instruction, so if the very first instruction + in the function refers to the register, it will see the wrong + value. */ + fprintf (file, "\tnop\n"); + + fprintf (file, "\t.set\treorder\n"); + +#ifndef FUNCTION_NAME_ALREADY_DECLARED + fputs ("\t.end\t", file); + assemble_name (file, stubname); + fputs ("\n", file); +#endif + + fprintf (file, "\t.set\tmips16\n"); + + function_section (current_function_decl); +} + +/* We keep a list of functions for which we have already built stubs + in build_mips16_call_stub. */ + +struct mips16_stub +{ + struct mips16_stub *next; + char *name; + int fpret; +}; + +static struct mips16_stub *mips16_stubs; + +/* Build a call stub for a mips16 call. A stub is needed if we are + passing any floating point values which should go into the floating + point registers. If we are, and the call turns out to be to a 32 + bit function, the stub will be used to move the values into the + floating point registers before calling the 32 bit function. The + linker will magically adjust the function call to either the 16 bit + function or the 32 bit stub, depending upon where the function call + is actually defined. + + Similarly, we need a stub if the return value might come back in a + floating point register. + + RETVAL, FNMEM, and ARG_SIZE are the values passed to the call insn + (RETVAL is NULL if this is call rather than call_value). FP_CODE + is the code built by function_arg. This function returns a nonzero + value if it builds the call instruction itself. */ + +int +build_mips16_call_stub (retval, fnmem, arg_size, fp_code) + rtx retval; + rtx fnmem; + rtx arg_size; + int fp_code; +{ + int fpret; + rtx fn; + char *fnname, *secname, *stubname; + struct mips16_stub *l; + tree stubid, stubdecl; + int need_comma; + unsigned int f; + + /* We don't need to do anything if we aren't in mips16 mode, or if + we were invoked with the -msoft-float option. */ + if (! TARGET_MIPS16 || ! mips16_hard_float) + return 0; + + /* Figure out whether the value might come back in a floating point + register. */ + fpret = (retval != 0 + && GET_MODE_CLASS (GET_MODE (retval)) == MODE_FLOAT + && (! TARGET_SINGLE_FLOAT + || GET_MODE_SIZE (GET_MODE (retval)) <= 4)); + + /* We don't need to do anything if there were no floating point + arguments and the value will not be returned in a floating point + register. */ + if (fp_code == 0 && ! fpret) + return 0; + + if (GET_CODE (fnmem) != MEM) + abort (); + fn = XEXP (fnmem, 0); + + /* We don't need to do anything if this is a call to a special + mips16 support function. */ + if (GET_CODE (fn) == SYMBOL_REF + && strncmp (XSTR (fn, 0), "__mips16_", 9) == 0) + return 0; + + /* This code will only work for o32 and o64 abis. The other ABI's + require more sophisticated support. */ + if (mips_abi != ABI_32 && mips_abi != ABI_O64) + abort (); + + /* We can only handle SFmode and DFmode floating point return + values. */ + if (fpret && GET_MODE (retval) != SFmode && GET_MODE (retval) != DFmode) + abort (); + + /* If we're calling via a function pointer, then we must always call + via a stub. There are magic stubs provided in libgcc.a for each + of the required cases. Each of them expects the function address + to arrive in register $2. */ + + if (GET_CODE (fn) != SYMBOL_REF) + { + char buf[30]; + tree id; + rtx stub_fn, stub_mem, insn; + + /* ??? If this code is modified to support other ABI's, we need + to handle PARALLEL return values here. */ + + sprintf (buf, "__mips16_call_stub_%s%d", + (fpret + ? (GET_MODE (retval) == SFmode ? "sf_" : "df_") + : ""), + fp_code); + id = get_identifier (buf); + stub_fn = gen_rtx (SYMBOL_REF, Pmode, IDENTIFIER_POINTER (id)); + stub_mem = gen_rtx (MEM, Pmode, stub_fn); + + emit_move_insn (gen_rtx (REG, Pmode, 2), fn); + + if (retval == NULL_RTX) + insn = gen_call_internal0 (stub_mem, arg_size, + gen_rtx (REG, SImode, + GP_REG_FIRST + 31)); + else + insn = gen_call_value_internal0 (retval, stub_mem, arg_size, + gen_rtx (REG, SImode, + GP_REG_FIRST + 31)); + insn = emit_call_insn (insn); + + /* Put the register usage information on the CALL. */ + if (GET_CODE (insn) != CALL_INSN) + abort (); + CALL_INSN_FUNCTION_USAGE (insn) = + gen_rtx (EXPR_LIST, VOIDmode, + gen_rtx (USE, VOIDmode, gen_rtx (REG, Pmode, 2)), + CALL_INSN_FUNCTION_USAGE (insn)); + + /* If we are handling a floating point return value, we need to + save $18 in the function prologue. Putting a note on the + call will mean that regs_ever_live[$18] will be true if the + call is not eliminated, and we can check that in the prologue + code. */ + if (fpret) + CALL_INSN_FUNCTION_USAGE (insn) = + gen_rtx (EXPR_LIST, VOIDmode, + gen_rtx (USE, VOIDmode, gen_rtx (REG, word_mode, 18)), + CALL_INSN_FUNCTION_USAGE (insn)); + + /* Return 1 to tell the caller that we've generated the call + insn. */ + return 1; + } + + /* We know the function we are going to call. If we have already + built a stub, we don't need to do anything further. */ + + fnname = XSTR (fn, 0); + for (l = mips16_stubs; l != NULL; l = l->next) + if (strcmp (l->name, fnname) == 0) + break; + + if (l == NULL) + { + /* Build a special purpose stub. When the linker sees a + function call in mips16 code, it will check where the target + is defined. If the target is a 32 bit call, the linker will + search for the section defined here. It can tell which + symbol this section is associated with by looking at the + relocation information (the name is unreliable, since this + might be a static function). If such a section is found, the + linker will redirect the call to the start of the magic + section. + + If the function does not return a floating point value, the + special stub section is named + .mips16.call.FNNAME + + If the function does return a floating point value, the stub + section is named + .mips16.call.fp.FNNAME + */ + + secname = (char *) alloca (strlen (fnname) + 40); + sprintf (secname, ".mips16.call.%s%s", + fpret ? "fp." : "", + fnname); + stubname = (char *) alloca (strlen (fnname) + 20); + sprintf (stubname, "__call_stub_%s%s", + fpret ? "fp_" : "", + fnname); + stubid = get_identifier (stubname); + stubdecl = build_decl (FUNCTION_DECL, stubid, + build_function_type (void_type_node, NULL_TREE)); + DECL_SECTION_NAME (stubdecl) = build_string (strlen (secname), secname); + + fprintf (asm_out_file, "\t# Stub function to call %s%s (", + (fpret + ? (GET_MODE (retval) == SFmode ? "float " : "double ") + : ""), + fnname); + need_comma = 0; + for (f = (unsigned int) fp_code; f != 0; f >>= 2) + { + fprintf (asm_out_file, "%s%s", + need_comma ? ", " : "", + (f & 3) == 1 ? "float" : "double"); + need_comma = 1; + } + fprintf (asm_out_file, ")\n"); + + fprintf (asm_out_file, "\t.set\tnomips16\n"); + assemble_start_function (stubdecl, stubname); + +#ifndef FUNCTION_NAME_ALREADY_DECLARED + fputs ("\t.ent\t", asm_out_file); + assemble_name (asm_out_file, stubname); + fputs ("\n", asm_out_file); + + assemble_name (asm_out_file, stubname); + fputs (":\n", asm_out_file); +#endif + + /* We build the stub code by hand. That's the only way we can + do it, since we can't generate 32 bit code during a 16 bit + compilation. */ + + /* We don't want the assembler to insert any nops here. */ + fprintf (asm_out_file, "\t.set\tnoreorder\n"); + + mips16_fp_args (asm_out_file, fp_code, 0); + + if (! fpret) + { + fprintf (asm_out_file, "\t.set\tnoat\n"); + fprintf (asm_out_file, "\tla\t%s,%s\n", reg_names[GP_REG_FIRST + 1], + fnname); + fprintf (asm_out_file, "\tjr\t%s\n", reg_names[GP_REG_FIRST + 1]); + fprintf (asm_out_file, "\t.set\tat\n"); + /* Unfortunately, we can't fill the jump delay slot. We + can't fill with one of the mtc1 instructions, because the + result is not available for one instruction, so if the + very first instruction in the function refers to the + register, it will see the wrong value. */ + fprintf (asm_out_file, "\tnop\n"); + } + else + { + fprintf (asm_out_file, "\tmove\t%s,%s\n", + reg_names[GP_REG_FIRST + 18], reg_names[GP_REG_FIRST + 31]); + fprintf (asm_out_file, "\tjal\t%s\n", fnname); + /* As above, we can't fill the delay slot. */ + fprintf (asm_out_file, "\tnop\n"); + if (GET_MODE (retval) == SFmode) + fprintf (asm_out_file, "\tmfc1\t%s,%s\n", + reg_names[GP_REG_FIRST + 2], reg_names[FP_REG_FIRST + 0]); + else + { + if (TARGET_BIG_ENDIAN) + { + fprintf (asm_out_file, "\tmfc1\t%s,%s\n", + reg_names[GP_REG_FIRST + 2], + reg_names[FP_REG_FIRST + 1]); + fprintf (asm_out_file, "\tmfc1\t%s,%s\n", + reg_names[GP_REG_FIRST + 3], + reg_names[FP_REG_FIRST + 0]); + } + else + { + fprintf (asm_out_file, "\tmfc1\t%s,%s\n", + reg_names[GP_REG_FIRST + 2], + reg_names[FP_REG_FIRST + 0]); + fprintf (asm_out_file, "\tmfc1\t%s,%s\n", + reg_names[GP_REG_FIRST + 3], + reg_names[FP_REG_FIRST + 1]); + } + } + fprintf (asm_out_file, "\tj\t%s\n", reg_names[GP_REG_FIRST + 18]); + /* As above, we can't fill the delay slot. */ + fprintf (asm_out_file, "\tnop\n"); + } + + fprintf (asm_out_file, "\t.set\treorder\n"); + +#ifdef ASM_DECLARE_FUNCTION_SIZE + ASM_DECLARE_FUNCTION_SIZE (asm_out_file, stubname, stubdecl); +#endif + +#ifndef FUNCTION_NAME_ALREADY_DECLARED + fputs ("\t.end\t", asm_out_file); + assemble_name (asm_out_file, stubname); + fputs ("\n", asm_out_file); +#endif + + fprintf (asm_out_file, "\t.set\tmips16\n"); + + /* Record this stub. */ + l = (struct mips16_stub *) xmalloc (sizeof *l); + l->name = (char *) xmalloc (strlen (fnname) + 1); + strcpy (l->name, fnname); + l->fpret = fpret; + l->next = mips16_stubs; + mips16_stubs = l; + } + + /* If we expect a floating point return value, but we've built a + stub which does not expect one, then we're in trouble. We can't + use the existing stub, because it won't handle the floating point + value. We can't build a new stub, because the linker won't know + which stub to use for the various calls in this object file. + Fortunately, this case is illegal, since it means that a function + was declared in two different ways in a single compilation. */ + if (fpret && ! l->fpret) + error ("can not handle inconsistent calls to `%s'", fnname); + + /* If we are calling a stub which handles a floating point return + value, we need to arrange to save $18 in the prologue. We do + this by marking the function call as using the register. The + prologue will later see that it is used, and emit code to save + it. */ + + if (l->fpret) + { + rtx insn; + + if (retval == NULL_RTX) + insn = gen_call_internal0 (fnmem, arg_size, + gen_rtx (REG, SImode, + GP_REG_FIRST + 31)); + else + insn = gen_call_value_internal0 (retval, fnmem, arg_size, + gen_rtx (REG, SImode, + GP_REG_FIRST + 31)); + insn = emit_call_insn (insn); + + if (GET_CODE (insn) != CALL_INSN) + abort (); + + CALL_INSN_FUNCTION_USAGE (insn) = + gen_rtx (EXPR_LIST, VOIDmode, + gen_rtx (USE, VOIDmode, gen_rtx (REG, word_mode, 18)), + CALL_INSN_FUNCTION_USAGE (insn)); + + /* Return 1 to tell the caller that we've generated the call + insn. */ + return 1; + } + + /* Return 0 to let the caller generate the call insn. */ + return 0; +} + +/* This function looks through the code for a function, and tries to + optimize the usage of the $gp register. We arrange to copy $gp + into a pseudo-register, and then let gcc's normal reload handling + deal with the pseudo-register. Unfortunately, if reload choose to + put the pseudo-register into a call-clobbered register, it will + emit saves and restores for that register around any function + calls. We don't need the saves, and it's faster to copy $gp than + to do an actual restore. ??? This still means that we waste a + stack slot. + + This is an optimization, and the code which gcc has actually + generated is correct, so we do not need to catch all cases. */ + +static void +mips16_optimize_gp (first) + rtx first; +{ + rtx gpcopy, slot, insn; + + /* Look through the instructions. Set GPCOPY to the register which + holds a copy of $gp. Set SLOT to the stack slot where it is + saved. If we find an instruction which sets GPCOPY to anything + other than $gp or SLOT, then we can't use it. If we find an + instruction which sets SLOT to anything other than GPCOPY, we + can't use it. */ + + gpcopy = NULL_RTX; + slot = NULL_RTX; + for (insn = first; insn != NULL_RTX; insn = next_active_insn (insn)) + { + rtx set; + + if (GET_RTX_CLASS (GET_CODE (insn)) != 'i') + continue; + + set = PATTERN (insn); + + /* We know that all references to memory will be inside a SET, + because there is no other way to access memory on the mips16. + We don't have to worry about a PARALLEL here, because the + mips.md file will never generate them for memory references. */ + if (GET_CODE (set) != SET) + continue; + + if (gpcopy == NULL_RTX + && GET_CODE (SET_SRC (set)) == CONST + && GET_CODE (XEXP (SET_SRC (set), 0)) == REG + && REGNO (XEXP (SET_SRC (set), 0)) == GP_REG_FIRST + 28 + && GET_CODE (SET_DEST (set)) == REG + && GET_MODE (SET_DEST (set)) == Pmode) + gpcopy = SET_DEST (set); + else if (slot == NULL_RTX + && gpcopy != NULL_RTX + && GET_CODE (SET_DEST (set)) == MEM + && GET_CODE (SET_SRC (set)) == REG + && REGNO (SET_SRC (set)) == REGNO (gpcopy) + && GET_MODE (SET_DEST (set)) == Pmode) + { + rtx base, offset; + + offset = const0_rtx; + base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset); + if (GET_CODE (base) == REG + && (REGNO (base) == STACK_POINTER_REGNUM + || REGNO (base) == FRAME_POINTER_REGNUM)) + slot = SET_DEST (set); + } + else if (gpcopy != NULL_RTX + && (GET_CODE (SET_DEST (set)) == REG + || GET_CODE (SET_DEST (set)) == SUBREG) + && reg_overlap_mentioned_p (SET_DEST (set), gpcopy) + && (GET_CODE (SET_DEST (set)) != REG + || REGNO (SET_DEST (set)) != REGNO (gpcopy) + || GET_MODE (SET_DEST (set)) != Pmode + || ((GET_CODE (SET_SRC (set)) != CONST + || GET_CODE (XEXP (SET_SRC (set), 0)) != REG + || (REGNO (XEXP (SET_SRC (set), 0)) + != GP_REG_FIRST + 28)) + && ! rtx_equal_p (SET_SRC (set), slot)))) + break; + else if (slot != NULL_RTX + && GET_CODE (SET_DEST (set)) == MEM + && rtx_equal_p (SET_DEST (set), slot) + && (GET_CODE (SET_SRC (set)) != REG + || REGNO (SET_SRC (set)) != REGNO (gpcopy))) + break; + } + + /* If we couldn't find a unique value for GPCOPY or SLOT, then try a + different optimization. Any time we find a copy of $28 into a + register, followed by an add of a symbol_ref to that register, we + convert it to load the value from the constant table instead. + The copy and add will take six bytes, just as the load and + constant table entry will take six bytes. However, it is + possible that the constant table entry will be shared. + + This could be a peephole optimization, but I don't know if the + peephole code can call force_const_mem. + + Using the same register for the copy of $28 and the add of the + symbol_ref is actually pretty likely, since the add instruction + requires the destination and the first addend to be the same + register. */ + + if (insn != NULL_RTX || gpcopy == NULL_RTX || slot == NULL_RTX) + { + rtx next; + + /* This optimization is only reasonable if the constant table + entries are only 4 bytes. */ + if (Pmode != SImode) + return; + + for (insn = first; insn != NULL_RTX; insn = next) + { + rtx set1, set2; + + next = insn; + do + { + next = NEXT_INSN (next); + } + while (next != NULL_RTX + && (GET_CODE (next) == NOTE + || (GET_CODE (next) == INSN + && (GET_CODE (PATTERN (next)) == USE + || GET_CODE (PATTERN (next)) == CLOBBER)))); + + if (next == NULL_RTX) + break; + + if (GET_RTX_CLASS (GET_CODE (insn)) != 'i') + continue; + + if (GET_RTX_CLASS (GET_CODE (next)) != 'i') + continue; + + set1 = PATTERN (insn); + if (GET_CODE (set1) != SET) + continue; + set2 = PATTERN (next); + if (GET_CODE (set2) != SET) + continue; + + if (GET_CODE (SET_DEST (set1)) == REG + && GET_CODE (SET_SRC (set1)) == CONST + && GET_CODE (XEXP (SET_SRC (set1), 0)) == REG + && REGNO (XEXP (SET_SRC (set1), 0)) == GP_REG_FIRST + 28 + && rtx_equal_p (SET_DEST (set1), SET_DEST (set2)) + && GET_CODE (SET_SRC (set2)) == PLUS + && rtx_equal_p (SET_DEST (set1), XEXP (SET_SRC (set2), 0)) + && mips16_gp_offset_p (XEXP (SET_SRC (set2), 1)) + && GET_CODE (XEXP (XEXP (SET_SRC (set2), 1), 0)) == MINUS) + { + rtx sym; + + /* We've found a case we can change to load from the + constant table. */ + + sym = XEXP (XEXP (XEXP (SET_SRC (set2), 1), 0), 0); + if (GET_CODE (sym) != SYMBOL_REF) + abort (); + emit_insn_after (gen_rtx (SET, VOIDmode, SET_DEST (set1), + force_const_mem (Pmode, sym)), + next); + + PUT_CODE (insn, NOTE); + NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (insn) = 0; + + PUT_CODE (next, NOTE); + NOTE_LINE_NUMBER (next) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (next) = 0; + } + } + + return; + } + + /* We can safely remove all assignments to SLOT from GPCOPY, and + replace all assignments from SLOT to GPCOPY with assignments from + $28. */ + + for (insn = first; insn != NULL_RTX; insn = next_active_insn (insn)) + { + rtx set; + + if (GET_RTX_CLASS (GET_CODE (insn)) != 'i') + continue; + + set = PATTERN (insn); + if (GET_CODE (set) != SET + || GET_MODE (SET_DEST (set)) != Pmode) + continue; + + if (GET_CODE (SET_DEST (set)) == MEM + && rtx_equal_p (SET_DEST (set), slot) + && GET_CODE (SET_SRC (set)) == REG + && REGNO (SET_SRC (set)) == REGNO (gpcopy)) + { + PUT_CODE (insn, NOTE); + NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (insn) = 0; + } + else if (GET_CODE (SET_DEST (set)) == REG + && REGNO (SET_DEST (set)) == REGNO (gpcopy) + && GET_CODE (SET_SRC (set)) == MEM + && rtx_equal_p (SET_SRC (set), slot)) + { + emit_insn_after (gen_rtx (SET, Pmode, SET_DEST (set), + gen_rtx (CONST, Pmode, + gen_rtx (REG, Pmode, + GP_REG_FIRST + 28))), + insn); + PUT_CODE (insn, NOTE); + NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (insn) = 0; + } + } +} + +/* We keep a list of constants we which we have to add to internal + constant tables in the middle of large functions. */ + +struct constant +{ + struct constant *next; + rtx value; + rtx label; + enum machine_mode mode; +}; + +/* Add a constant to the list in *PCONSTANTS. */ + +static rtx +add_constant (pconstants, val, mode) + struct constant **pconstants; + rtx val; + enum machine_mode mode; +{ + struct constant *c; + + for (c = *pconstants; c != NULL; c = c->next) + if (mode == c->mode && rtx_equal_p (val, c->value)) + return c->label; + + c = (struct constant *) xmalloc (sizeof *c); + c->value = val; + c->mode = mode; + c->label = gen_label_rtx (); + c->next = *pconstants; + *pconstants = c; + return c->label; +} + +/* Dump out the constants in CONSTANTS after INSN. */ + +static void +dump_constants (constants, insn) + struct constant *constants; + rtx insn; +{ + struct constant *c; + int align; + + c = constants; + align = 0; + while (c != NULL) + { + rtx r; + struct constant *next; + + switch (GET_MODE_SIZE (c->mode)) + { + case 1: + align = 0; + break; + case 2: + if (align < 1) + insn = emit_insn_after (gen_align_2 (), insn); + align = 1; + break; + case 4: + if (align < 2) + insn = emit_insn_after (gen_align_4 (), insn); + align = 2; + break; + default: + if (align < 3) + insn = emit_insn_after (gen_align_8 (), insn); + align = 3; + break; + } + + insn = emit_label_after (c->label, insn); + + switch (c->mode) + { + case QImode: + r = gen_consttable_qi (c->value); + break; + case HImode: + r = gen_consttable_hi (c->value); + break; + case SImode: + r = gen_consttable_si (c->value); + break; + case SFmode: + r = gen_consttable_sf (c->value); + break; + case DImode: + r = gen_consttable_di (c->value); + break; + case DFmode: + r = gen_consttable_df (c->value); + break; + default: + abort (); + } + + insn = emit_insn_after (r, insn); + + next = c->next; + free (c); + c = next; + } + + emit_barrier_after (insn); +} + +/* Find the symbol in an address expression. */ + +static rtx +mips_find_symbol (addr) + rtx addr; +{ + if (GET_CODE (addr) == MEM) + addr = XEXP (addr, 0); + while (GET_CODE (addr) == CONST) + addr = XEXP (addr, 0); + if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF) + return addr; + if (GET_CODE (addr) == PLUS) + { + rtx l1, l2; + + l1 = mips_find_symbol (XEXP (addr, 0)); + l2 = mips_find_symbol (XEXP (addr, 1)); + if (l1 != NULL_RTX && l2 == NULL_RTX) + return l1; + else if (l1 == NULL_RTX && l2 != NULL_RTX) + return l2; + } + return NULL_RTX; +} + +/* Exported to toplev.c. + + Do a final pass over the function, just before delayed branch + scheduling. */ + +void +machine_dependent_reorg (first) + rtx first; +{ + int insns_len, max_internal_pool_size, pool_size, addr; + rtx insn; + struct constant *constants; + + if (! TARGET_MIPS16) + return; + + /* If $gp is used, try to remove stores, and replace loads with + copies from $gp. */ + if (optimize) + mips16_optimize_gp (first); + + /* Scan the function looking for PC relative loads which may be out + of range. All such loads will either be from the constant table, + or be getting the address of a constant string. If the size of + the function plus the size of the constant table is less than + 0x8000, then all loads are in range. */ + + insns_len = 0; + for (insn = first; insn; insn = NEXT_INSN (insn)) + { + insns_len += get_attr_length (insn) * 2; + + /* ??? We put switch tables in .text, but we don't define + JUMP_TABLES_IN_TEXT_SECTION, so get_attr_length will not + compute their lengths correctly. */ + if (GET_CODE (insn) == JUMP_INSN) + { + rtx body; + + body = PATTERN (insn); + if (GET_CODE (body) == ADDR_VEC || GET_CODE (body) == ADDR_DIFF_VEC) + insns_len += (XVECLEN (body, GET_CODE (body) == ADDR_DIFF_VEC) + * GET_MODE_SIZE (GET_MODE (body))); + insns_len += GET_MODE_SIZE (GET_MODE (body)) - 1; + } + } + + /* Store the original value of insns_len in current_frame_info, so + that simple_memory_operand can look at it. */ + current_frame_info.insns_len = insns_len; + + pool_size = get_pool_size (); + if (insns_len + pool_size + mips_string_length < 0x8000) + return; + + /* Loop over the insns and figure out what the maximum internal pool + size could be. */ + max_internal_pool_size = 0; + for (insn = first; insn; insn = NEXT_INSN (insn)) + { + if (GET_CODE (insn) == INSN + && GET_CODE (PATTERN (insn)) == SET) + { + rtx src; + + src = mips_find_symbol (SET_SRC (PATTERN (insn))); + if (src == NULL_RTX) + continue; + if (CONSTANT_POOL_ADDRESS_P (src)) + max_internal_pool_size += GET_MODE_SIZE (get_pool_mode (src)); + else if (SYMBOL_REF_FLAG (src)) + max_internal_pool_size += GET_MODE_SIZE (Pmode); + } + } + + constants = NULL; + addr = 0; + + for (insn = first; insn; insn = NEXT_INSN (insn)) + { + if (GET_CODE (insn) == INSN + && GET_CODE (PATTERN (insn)) == SET) + { + rtx val, src; + enum machine_mode mode; + + val = NULL_RTX; + src = mips_find_symbol (SET_SRC (PATTERN (insn))); + if (src != NULL_RTX && CONSTANT_POOL_ADDRESS_P (src)) + { + /* ??? This is very conservative, which means that we + will generate too many copies of the constant table. + The only solution would seem to be some form of + relaxing. */ + if (((insns_len - addr) + + max_internal_pool_size + + get_pool_offset (src)) + >= 0x8000) + { + val = get_pool_constant (src); + mode = get_pool_mode (src); + } + max_internal_pool_size -= GET_MODE_SIZE (get_pool_mode (src)); + } + else if (src != NULL_RTX && SYMBOL_REF_FLAG (src)) + { + /* Including all of mips_string_length is conservative, + and so is including all of max_internal_pool_size. */ + if (((insns_len - addr) + + max_internal_pool_size + + pool_size + + mips_string_length) + >= 0x8000) + val = src; + mode = Pmode; + max_internal_pool_size -= Pmode; + } + + if (val != NULL_RTX) + { + rtx lab, newsrc; + + /* This PC relative load is out of range. ??? In the + case of a string constant, we are only guessing that + it is range, since we don't know the offset of a + particular string constant. */ + + lab = add_constant (&constants, val, mode); + newsrc = gen_rtx (MEM, mode, + gen_rtx (LABEL_REF, VOIDmode, lab)); + RTX_UNCHANGING_P (newsrc) = 1; + PATTERN (insn) = gen_rtx (SET, VOIDmode, + SET_DEST (PATTERN (insn)), + newsrc); + INSN_CODE (insn) = -1; + } + } + + addr += get_attr_length (insn) * 2; + + /* ??? We put switch tables in .text, but we don't define + JUMP_TABLES_IN_TEXT_SECTION, so get_attr_length will not + compute their lengths correctly. */ + if (GET_CODE (insn) == JUMP_INSN) + { + rtx body; + + body = PATTERN (insn); + if (GET_CODE (body) == ADDR_VEC || GET_CODE (body) == ADDR_DIFF_VEC) + addr += (XVECLEN (body, GET_CODE (body) == ADDR_DIFF_VEC) + * GET_MODE_SIZE (GET_MODE (body))); + addr += GET_MODE_SIZE (GET_MODE (body)) - 1; + } + + if (GET_CODE (insn) == BARRIER) + { + /* Output any constants we have accumulated. Note that we + don't need to change ADDR, since its only use is + subtraction from INSNS_LEN, and both would be changed by + the same amount. + ??? If the instructions up to the next barrier reuse a + constant, it would often be better to continue + accumulating. */ + if (constants != NULL) + dump_constants (constants, insn); + constants = NULL; + } + + /* ??? If we don't find a barrier within 0x8000 bytes of + instructions and constants in CONSTANTS, we need to invent + one. This seems sufficiently unlikely that I am not going to + worry about it. */ + } + + if (constants != NULL) + { + rtx label, jump, barrier; + + label = gen_label_rtx (); + jump = emit_jump_insn_after (gen_jump (label), get_last_insn ()); + JUMP_LABEL (jump) = label; + LABEL_NUSES (label) = 1; + barrier = emit_barrier_after (jump); + emit_label_after (label, barrier); + dump_constants (constants, barrier); + constants = NULL; + } + + /* ??? If we output all references to a constant in internal + constants table, we don't need to output the constant in the real + constant table, but we have no way to prevent that. */ +} + +/* Return nonzero if X is a SIGN or ZERO extend operator. */ +int +extend_operator (x, mode) + rtx x; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + enum rtx_code code = GET_CODE (x); + return code == SIGN_EXTEND || code == ZERO_EXTEND; +} + +/* Accept any operator that can be used to shift the high half of the + input value to the lower half, suitable for truncation. The + remainder (the lower half of the input, and the upper half of the + output) will be discarded. */ +int +highpart_shift_operator (x, mode) + rtx x; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + enum rtx_code code = GET_CODE (x); + return (code == LSHIFTRT + || code == ASHIFTRT + || code == ROTATERT + || code == ROTATE); +} diff --git a/contrib/gcc/config/mips/mips.h b/contrib/gcc/config/mips/mips.h new file mode 100644 index 000000000000..7360e1b5d64a --- /dev/null +++ b/contrib/gcc/config/mips/mips.h @@ -0,0 +1,4720 @@ +/* Definitions of target machine for GNU compiler. MIPS version. + Copyright (C) 1989, 90-98, 1999 Free Software Foundation, Inc. + Contributed by A. Lichnewsky (lich@inria.inria.fr). + Changed by Michael Meissner (meissner@osf.org). + 64 bit r4000 support by Ian Lance Taylor (ian@cygnus.com) and + Brendan Eich (brendan@microunity.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. */ + + +/* Standard GCC variables that we reference. */ + +extern char *asm_file_name; +extern char call_used_regs[]; +extern int current_function_calls_alloca; +extern char *language_string; +extern int may_call_alloca; +extern char **save_argv; +extern int target_flags; +extern char *version_string; + +/* MIPS external variables defined in mips.c. */ + +/* comparison type */ +enum cmp_type { + CMP_SI, /* compare four byte integers */ + CMP_DI, /* compare eight byte integers */ + CMP_SF, /* compare single precision floats */ + CMP_DF, /* compare double precision floats */ + CMP_MAX /* max comparison type */ +}; + +/* types of delay slot */ +enum delay_type { + DELAY_NONE, /* no delay slot */ + DELAY_LOAD, /* load from memory delay */ + DELAY_HILO, /* move from/to hi/lo registers */ + DELAY_FCMP /* delay after doing c.<xx>.{d,s} */ +}; + +/* Which processor to schedule for. Since there is no difference between + a R2000 and R3000 in terms of the scheduler, we collapse them into + just an R3000. The elements of the enumeration must match exactly + the cpu attribute in the mips.md machine description. */ + +enum processor_type { + PROCESSOR_DEFAULT, + PROCESSOR_R3000, + PROCESSOR_R3900, + PROCESSOR_R6000, + PROCESSOR_R4000, + PROCESSOR_R4100, + PROCESSOR_R4300, + PROCESSOR_R4600, + PROCESSOR_R4650, + PROCESSOR_R5000, + PROCESSOR_R8000 +}; + +/* Recast the cpu class to be the cpu attribute. */ +#define mips_cpu_attr ((enum attr_cpu)mips_cpu) + +/* Which ABI to use. These are constants because abi64.h must check their + value at preprocessing time. + + ABI_32 (original 32, or o32), ABI_N32 (n32), ABI_64 (n64) are all + defined by SGI. ABI_O64 is o32 extended to work on a 64 bit machine. */ + +#define ABI_32 0 +#define ABI_N32 1 +#define ABI_64 2 +#define ABI_EABI 3 +#define ABI_O64 4 + +#ifndef MIPS_ABI_DEFAULT +/* We define this away so that there is no extra runtime cost if the target + doesn't support multiple ABIs. */ +#define mips_abi ABI_32 +#else +extern int mips_abi; +#endif + +/* Whether to emit abicalls code sequences or not. */ + +enum mips_abicalls_type { + MIPS_ABICALLS_NO, + MIPS_ABICALLS_YES +}; + +/* Recast the abicalls class to be the abicalls attribute. */ +#define mips_abicalls_attr ((enum attr_abicalls)mips_abicalls) + +/* Which type of block move to do (whether or not the last store is + split out so it can fill a branch delay slot). */ + +enum block_move_type { + BLOCK_MOVE_NORMAL, /* generate complete block move */ + BLOCK_MOVE_NOT_LAST, /* generate all but last store */ + BLOCK_MOVE_LAST /* generate just the last store */ +}; + +extern char mips_reg_names[][8]; /* register names (a0 vs. $4). */ +extern char mips_print_operand_punct[]; /* print_operand punctuation chars */ +extern const char *current_function_file; /* filename current function is in */ +extern int num_source_filenames; /* current .file # */ +extern int inside_function; /* != 0 if inside of a function */ +extern int ignore_line_number; /* != 0 if we are to ignore next .loc */ +extern int file_in_function_warning; /* warning given about .file in func */ +extern int sdb_label_count; /* block start/end next label # */ +extern int sdb_begin_function_line; /* Starting Line of current function */ +extern int mips_section_threshold; /* # bytes of data/sdata cutoff */ +extern int g_switch_value; /* value of the -G xx switch */ +extern int g_switch_set; /* whether -G xx was passed. */ +extern int sym_lineno; /* sgi next label # for each stmt */ +extern int set_noreorder; /* # of nested .set noreorder's */ +extern int set_nomacro; /* # of nested .set nomacro's */ +extern int set_noat; /* # of nested .set noat's */ +extern int set_volatile; /* # of nested .set volatile's */ +extern int mips_branch_likely; /* emit 'l' after br (branch likely) */ +extern int mips_dbx_regno[]; /* Map register # to debug register # */ +extern struct rtx_def *branch_cmp[2]; /* operands for compare */ +extern enum cmp_type branch_type; /* what type of branch to use */ +extern enum processor_type mips_cpu; /* which cpu are we scheduling for */ +extern enum mips_abicalls_type mips_abicalls;/* for svr4 abi pic calls */ +extern int mips_isa; /* architectural level */ +extern int mips16; /* whether generating mips16 code */ +extern int mips16_hard_float; /* mips16 without -msoft-float */ +extern int mips_entry; /* generate entry/exit for mips16 */ +extern const char *mips_cpu_string; /* for -mcpu=<xxx> */ +extern const char *mips_isa_string; /* for -mips{1,2,3,4} */ +extern const char *mips_abi_string; /* for -mabi={32,n32,64} */ +extern const char *mips_entry_string; /* for -mentry */ +extern const char *mips_no_mips16_string;/* for -mno-mips16 */ +extern const char *mips_explicit_type_size_string;/* for -mexplicit-type-size */ +extern int mips_split_addresses; /* perform high/lo_sum support */ +extern int dslots_load_total; /* total # load related delay slots */ +extern int dslots_load_filled; /* # filled load delay slots */ +extern int dslots_jump_total; /* total # jump related delay slots */ +extern int dslots_jump_filled; /* # filled jump delay slots */ +extern int dslots_number_nops; /* # of nops needed by previous insn */ +extern int num_refs[3]; /* # 1/2/3 word references */ +extern struct rtx_def *mips_load_reg; /* register to check for load delay */ +extern struct rtx_def *mips_load_reg2; /* 2nd reg to check for load delay */ +extern struct rtx_def *mips_load_reg3; /* 3rd reg to check for load delay */ +extern struct rtx_def *mips_load_reg4; /* 4th reg to check for load delay */ +extern struct rtx_def *embedded_pic_fnaddr_rtx; /* function address */ +extern int mips_string_length; /* length of strings for mips16 */ +extern struct rtx_def *mips16_gp_pseudo_rtx; /* psuedo reg holding $gp */ + +/* Functions within mips.c that we reference. Some of these return + type HOST_WIDE_INT, so define that here. */ + +#include "hwint.h" + +extern int arith32_operand (); +extern int arith_operand (); +extern int cmp_op (); +#ifdef HOST_WIDE_INT +extern HOST_WIDE_INT compute_frame_size (); +#endif +extern int const_float_1_operand (); +extern void expand_block_move (); +extern int equality_op (); +extern void final_prescan_insn (); +extern struct rtx_def * function_arg (); +extern void function_arg_advance (); +extern int function_arg_partial_nregs (); +extern int function_arg_pass_by_reference (); +extern void function_epilogue (); +extern void function_prologue (); +extern void gen_conditional_branch (); +extern void gen_conditional_move (); +extern struct rtx_def * gen_int_relational (); +extern void init_cumulative_args (); +extern int large_int (); +extern void machine_dependent_reorg (); +extern int mips_address_cost (); +extern void mips_asm_file_end (); +extern void mips_asm_file_start (); +extern int mips_can_use_return_insn (); +extern int mips_const_double_ok (); +extern void mips_count_memory_refs (); +#ifdef HOST_WIDE_INT +extern HOST_WIDE_INT mips_debugger_offset (); +#endif +extern void mips_declare_object (); +extern int mips_epilogue_delay_slots (); +extern void mips_expand_epilogue (); +extern void mips_expand_prologue (); +extern int mips_check_split (); +extern char *mips_fill_delay_slot (); +extern const char *mips_move_1word (); +extern const char *mips_move_2words (); +extern void mips_output_double (); +extern int mips_output_external (); +extern void mips_output_float (); +extern void mips_output_filename (); +extern void mips_output_lineno (); +extern const char *output_block_move (); +extern void override_options (); +extern int pc_or_label_operand (); +extern void print_operand_address (); +extern void print_operand (); +extern void print_options (); +extern int reg_or_0_operand (); +extern int true_reg_or_0_operand (); +extern int simple_epilogue_p (); +extern int simple_memory_operand (); +extern int double_memory_operand (); +extern int small_int (); +extern void trace (); +extern int uns_arith_operand (); +extern struct rtx_def * embedded_pic_offset (); +extern void mips_order_regs_for_local_alloc (); +extern struct rtx_def * mips16_gp_pseudo_reg (); +extern struct rtx_def * mips16_gp_offset (); +extern int mips16_gp_offset_p (); +extern int mips16_constant (); +extern int mips16_constant_after_function_p (); +extern int build_mips16_call_stub (); + +/* Recognition functions that return if a condition is true. */ +extern int address_operand (); +extern int call_insn_operand (); +extern int const_double_operand (); +extern int const_int_operand (); +extern int consttable_operand (); +extern int general_operand (); +extern int immediate_operand (); +extern int memory_address_p (); +extern int memory_operand (); +extern int nonimmediate_operand (); +extern int nonmemory_operand (); +extern int pic_address_needs_scratch (); +extern int register_operand (); +extern int scratch_operand (); +extern int move_operand (); +extern int movdi_operand (); +extern int se_register_operand (); +extern int se_reg_or_0_operand (); +extern int se_uns_arith_operand (); +extern int se_arith_operand (); +extern int se_nonmemory_operand (); +extern int se_nonimmediate_operand (); +extern int extend_operator (); +extern int highpart_shift_operator (); +extern int m16_uimm3_b (); +extern int m16_simm4_1 (); +extern int m16_nsimm4_1 (); +extern int m16_simm5_1 (); +extern int m16_nsimm5_1 (); +extern int m16_uimm5_4 (); +extern int m16_nuimm5_4 (); +extern int m16_simm8_1 (); +extern int m16_nsimm8_1 (); +extern int m16_uimm8_1 (); +extern int m16_nuimm8_1 (); +extern int m16_uimm8_m1_1 (); +extern int m16_uimm8_4 (); +extern int m16_nuimm8_4 (); +extern int m16_simm8_8 (); +extern int m16_nsimm8_8 (); +extern int m16_usym8_4 (); +extern int m16_usym5_4 (); + +/* Functions to change what output section we are using. */ +extern void data_section (); +extern void rdata_section (); +extern void readonly_data_section (); +extern void sdata_section (); +extern void text_section (); +extern void mips_select_rtx_section (); +extern void mips_select_section (); + +/* Stubs for half-pic support if not OSF/1 reference platform. */ + +#ifndef HALF_PIC_P +#define HALF_PIC_P() 0 +#define HALF_PIC_NUMBER_PTRS 0 +#define HALF_PIC_NUMBER_REFS 0 +#define HALF_PIC_ENCODE(DECL) +#define HALF_PIC_DECLARE(NAME) +#define HALF_PIC_INIT() error ("half-pic init called on systems that don't support it.") +#define HALF_PIC_ADDRESS_P(X) 0 +#define HALF_PIC_PTR(X) X +#define HALF_PIC_FINISH(STREAM) +#endif + + +/* Run-time compilation parameters selecting different hardware subsets. */ + +/* Macros used in the machine description to test the flags. */ + + /* Bits for real switches */ +#define MASK_INT64 0x00000001 /* ints are 64 bits */ +#define MASK_LONG64 0x00000002 /* longs are 64 bits */ +#define MASK_SPLIT_ADDR 0x00000004 /* Address splitting is enabled. */ +#define MASK_GPOPT 0x00000008 /* Optimize for global pointer */ +#define MASK_GAS 0x00000010 /* Gas used instead of MIPS as */ +#define MASK_NAME_REGS 0x00000020 /* Use MIPS s/w reg name convention */ +#define MASK_STATS 0x00000040 /* print statistics to stderr */ +#define MASK_MEMCPY 0x00000080 /* call memcpy instead of inline code*/ +#define MASK_SOFT_FLOAT 0x00000100 /* software floating point */ +#define MASK_FLOAT64 0x00000200 /* fp registers are 64 bits */ +#define MASK_ABICALLS 0x00000400 /* emit .abicalls/.cprestore/.cpload */ +#define MASK_HALF_PIC 0x00000800 /* Emit OSF-style pic refs to externs*/ +#define MASK_LONG_CALLS 0x00001000 /* Always call through a register */ +#define MASK_64BIT 0x00002000 /* Use 64 bit GP registers and insns */ +#define MASK_EMBEDDED_PIC 0x00004000 /* Generate embedded PIC code */ +#define MASK_EMBEDDED_DATA 0x00008000 /* Reduce RAM usage, not fast code */ +#define MASK_BIG_ENDIAN 0x00010000 /* Generate big endian code */ +#define MASK_SINGLE_FLOAT 0x00020000 /* Only single precision FPU. */ +#define MASK_MAD 0x00040000 /* Generate mad/madu as on 4650. */ +#define MASK_4300_MUL_FIX 0x00080000 /* Work-around early Vr4300 CPU bug */ +#define MASK_MIPS3900 0x00100000 /* like -mips1 only 3900 */ +#define MASK_MIPS16 0x01000000 /* Generate mips16 code */ +#define MASK_NO_CHECK_ZERO_DIV 0x04000000 /* divide by zero checking */ +#define MASK_CHECK_RANGE_DIV 0x08000000 /* divide result range checking */ + + /* Dummy switches used only in spec's*/ +#define MASK_MIPS_TFILE 0x00000000 /* flag for mips-tfile usage */ + + /* Debug switches, not documented */ +#define MASK_DEBUG 0 /* Eliminate version # in .s file */ +#define MASK_DEBUG_A 0x40000000 /* don't allow <label>($reg) addrs */ +#define MASK_DEBUG_B 0x20000000 /* GO_IF_LEGITIMATE_ADDRESS debug */ +#define MASK_DEBUG_C 0x10000000 /* don't expand seq, etc. */ +#define MASK_DEBUG_D 0 /* don't do define_split's */ +#define MASK_DEBUG_E 0 /* function_arg debug */ +#define MASK_DEBUG_F 0 +#define MASK_DEBUG_G 0 /* don't support 64 bit arithmetic */ +#define MASK_DEBUG_H 0 /* allow ints in FP registers */ +#define MASK_DEBUG_I 0 /* unused */ + + /* r4000 64 bit sizes */ +#define TARGET_INT64 (target_flags & MASK_INT64) +#define TARGET_LONG64 (target_flags & MASK_LONG64) +#define TARGET_FLOAT64 (target_flags & MASK_FLOAT64) +#define TARGET_64BIT (target_flags & MASK_64BIT) + + /* Mips vs. GNU linker */ +#define TARGET_SPLIT_ADDRESSES (target_flags & MASK_SPLIT_ADDR) + +/* generate mips 3900 insns */ +#define TARGET_MIPS3900 (target_flags & MASK_MIPS3900) + + /* Mips vs. GNU assembler */ +#define TARGET_GAS (target_flags & MASK_GAS) +#define TARGET_UNIX_ASM (!TARGET_GAS) +#define TARGET_MIPS_AS TARGET_UNIX_ASM + + /* Debug Mode */ +#define TARGET_DEBUG_MODE (target_flags & MASK_DEBUG) +#define TARGET_DEBUG_A_MODE (target_flags & MASK_DEBUG_A) +#define TARGET_DEBUG_B_MODE (target_flags & MASK_DEBUG_B) +#define TARGET_DEBUG_C_MODE (target_flags & MASK_DEBUG_C) +#define TARGET_DEBUG_D_MODE (target_flags & MASK_DEBUG_D) +#define TARGET_DEBUG_E_MODE (target_flags & MASK_DEBUG_E) +#define TARGET_DEBUG_F_MODE (target_flags & MASK_DEBUG_F) +#define TARGET_DEBUG_G_MODE (target_flags & MASK_DEBUG_G) +#define TARGET_DEBUG_H_MODE (target_flags & MASK_DEBUG_H) +#define TARGET_DEBUG_I_MODE (target_flags & MASK_DEBUG_I) + + /* Reg. Naming in .s ($21 vs. $a0) */ +#define TARGET_NAME_REGS (target_flags & MASK_NAME_REGS) + + /* Optimize for Sdata/Sbss */ +#define TARGET_GP_OPT (target_flags & MASK_GPOPT) + + /* print program statistics */ +#define TARGET_STATS (target_flags & MASK_STATS) + + /* call memcpy instead of inline code */ +#define TARGET_MEMCPY (target_flags & MASK_MEMCPY) + + /* .abicalls, etc from Pyramid V.4 */ +#define TARGET_ABICALLS (target_flags & MASK_ABICALLS) + + /* OSF pic references to externs */ +#define TARGET_HALF_PIC (target_flags & MASK_HALF_PIC) + + /* software floating point */ +#define TARGET_SOFT_FLOAT (target_flags & MASK_SOFT_FLOAT) +#define TARGET_HARD_FLOAT (! TARGET_SOFT_FLOAT) + + /* always call through a register */ +#define TARGET_LONG_CALLS (target_flags & MASK_LONG_CALLS) + + /* generate embedded PIC code; + requires gas. */ +#define TARGET_EMBEDDED_PIC (target_flags & MASK_EMBEDDED_PIC) + + /* for embedded systems, optimize for + reduced RAM space instead of for + fastest code. */ +#define TARGET_EMBEDDED_DATA (target_flags & MASK_EMBEDDED_DATA) + + /* generate big endian code. */ +#define TARGET_BIG_ENDIAN (target_flags & MASK_BIG_ENDIAN) + +#define TARGET_SINGLE_FLOAT (target_flags & MASK_SINGLE_FLOAT) +#define TARGET_DOUBLE_FLOAT (! TARGET_SINGLE_FLOAT) + +#define TARGET_MAD (target_flags & MASK_MAD) + +#define TARGET_4300_MUL_FIX (target_flags & MASK_4300_MUL_FIX) + +#define TARGET_NO_CHECK_ZERO_DIV (target_flags & MASK_NO_CHECK_ZERO_DIV) +#define TARGET_CHECK_RANGE_DIV (target_flags & MASK_CHECK_RANGE_DIV) + +/* This is true if we must enable the assembly language file switching + code. */ + +#define TARGET_FILE_SWITCHING (TARGET_GP_OPT && ! TARGET_GAS) + +/* We must disable the function end stabs when doing the file switching trick, + because the Lscope stabs end up in the wrong place, making it impossible + to debug the resulting code. */ +#define NO_DBX_FUNCTION_END TARGET_FILE_SWITCHING + + /* Generate mips16 code */ +#define TARGET_MIPS16 (target_flags & MASK_MIPS16) + +/* 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 \ +{ \ + {"int64", MASK_INT64 | MASK_LONG64, \ + "Use 64-bit int type"}, \ + {"long64", MASK_LONG64, \ + "Use 64-bit long type"}, \ + {"long32", -(MASK_LONG64 | MASK_INT64), \ + "Use 32-bit long type"}, \ + {"split-addresses", MASK_SPLIT_ADDR, \ + "Optimize lui/addiu address loads"}, \ + {"no-split-addresses", -MASK_SPLIT_ADDR, \ + "Don't optimize lui/addiu address loads"}, \ + {"mips-as", -MASK_GAS, \ + "Use MIPS as"}, \ + {"gas", MASK_GAS, \ + "Use GNU as"}, \ + {"rnames", MASK_NAME_REGS, \ + "Use symbolic register names"}, \ + {"no-rnames", -MASK_NAME_REGS, \ + "Don't use symbolic register names"}, \ + {"gpOPT", MASK_GPOPT, \ + "Use GP relative sdata/sbss sections"}, \ + {"gpopt", MASK_GPOPT, \ + "Use GP relative sdata/sbss sections"}, \ + {"no-gpOPT", -MASK_GPOPT, \ + "Don't use GP relative sdata/sbss sections"}, \ + {"no-gpopt", -MASK_GPOPT, \ + "Don't use GP relative sdata/sbss sections"}, \ + {"stats", MASK_STATS, \ + "Output compiler statistics"}, \ + {"no-stats", -MASK_STATS, \ + "Don't output compiler statistics"}, \ + {"memcpy", MASK_MEMCPY, \ + "Don't optimize block moves"}, \ + {"no-memcpy", -MASK_MEMCPY, \ + "Optimize block moves"}, \ + {"mips-tfile", MASK_MIPS_TFILE, \ + "Use mips-tfile asm postpass"}, \ + {"no-mips-tfile", -MASK_MIPS_TFILE, \ + "Don't use mips-tfile asm postpass"}, \ + {"soft-float", MASK_SOFT_FLOAT, \ + "Use software floating point"}, \ + {"hard-float", -MASK_SOFT_FLOAT, \ + "Use hardware floating point"}, \ + {"fp64", MASK_FLOAT64, \ + "Use 64-bit FP registers"}, \ + {"fp32", -MASK_FLOAT64, \ + "Use 32-bit FP registers"}, \ + {"gp64", MASK_64BIT, \ + "Use 64-bit general registers"}, \ + {"gp32", -MASK_64BIT, \ + "Use 32-bit general registers"}, \ + {"abicalls", MASK_ABICALLS, \ + "Use Irix PIC"}, \ + {"no-abicalls", -MASK_ABICALLS, \ + "Don't use Irix PIC"}, \ + {"half-pic", MASK_HALF_PIC, \ + "Use OSF PIC"}, \ + {"no-half-pic", -MASK_HALF_PIC, \ + "Don't use OSF PIC"}, \ + {"long-calls", MASK_LONG_CALLS, \ + "Use indirect calls"}, \ + {"no-long-calls", -MASK_LONG_CALLS, \ + "Don't use indirect calls"}, \ + {"embedded-pic", MASK_EMBEDDED_PIC, \ + "Use embedded PIC"}, \ + {"no-embedded-pic", -MASK_EMBEDDED_PIC, \ + "Don't use embedded PIC"}, \ + {"embedded-data", MASK_EMBEDDED_DATA, \ + "Use ROM instead of RAM"}, \ + {"no-embedded-data", -MASK_EMBEDDED_DATA, \ + "Don't use ROM instead of RAM"}, \ + {"eb", MASK_BIG_ENDIAN, \ + "Use big-endian byte order"}, \ + {"el", -MASK_BIG_ENDIAN, \ + "Use little-endian byte order"}, \ + {"single-float", MASK_SINGLE_FLOAT, \ + "Use single (32-bit) FP only"}, \ + {"double-float", -MASK_SINGLE_FLOAT, \ + "Don't use single (32-bit) FP only"}, \ + {"mad", MASK_MAD, \ + "Use multiply accumulate"}, \ + {"no-mad", -MASK_MAD, \ + "Don't use multiply accumulate"}, \ + {"fix4300", MASK_4300_MUL_FIX, \ + "Work around early 4300 hardware bug"}, \ + {"no-fix4300", -MASK_4300_MUL_FIX, \ + "Don't work around early 4300 hardware bug"}, \ + {"4650", MASK_MAD | MASK_SINGLE_FLOAT, \ + "Optimize for 4650"}, \ + {"3900", MASK_MIPS3900, \ + "Optimize for 3900"}, \ + {"check-zero-division",-MASK_NO_CHECK_ZERO_DIV, \ + "Trap on integer divide by zero"}, \ + {"no-check-zero-division", MASK_NO_CHECK_ZERO_DIV, \ + "Don't trap on integer divide by zero"}, \ + {"check-range-division",MASK_CHECK_RANGE_DIV, \ + "Trap on integer divide overflow"}, \ + {"no-check-range-division",-MASK_CHECK_RANGE_DIV, \ + "Don't trap on integer divide overflow"}, \ + {"debug", MASK_DEBUG, \ + NULL}, \ + {"debuga", MASK_DEBUG_A, \ + NULL}, \ + {"debugb", MASK_DEBUG_B, \ + NULL}, \ + {"debugc", MASK_DEBUG_C, \ + NULL}, \ + {"debugd", MASK_DEBUG_D, \ + NULL}, \ + {"debuge", MASK_DEBUG_E, \ + NULL}, \ + {"debugf", MASK_DEBUG_F, \ + NULL}, \ + {"debugg", MASK_DEBUG_G, \ + NULL}, \ + {"debugh", MASK_DEBUG_H, \ + NULL}, \ + {"debugi", MASK_DEBUG_I, \ + NULL}, \ + {"", (TARGET_DEFAULT \ + | TARGET_CPU_DEFAULT \ + | TARGET_ENDIAN_DEFAULT), \ + NULL}, \ +} + +/* Default target_flags if no switches are specified */ + +#ifndef TARGET_DEFAULT +#define TARGET_DEFAULT 0 +#endif + +#ifndef TARGET_CPU_DEFAULT +#define TARGET_CPU_DEFAULT 0 +#endif + +#ifndef TARGET_ENDIAN_DEFAULT +#ifndef DECSTATION +#define TARGET_ENDIAN_DEFAULT MASK_BIG_ENDIAN +#else +#define TARGET_ENDIAN_DEFAULT 0 +#endif +#endif + +#ifndef MULTILIB_DEFAULTS +#if TARGET_ENDIAN_DEFAULT == 0 +#define MULTILIB_DEFAULTS { "EL", "mips1" } +#else +#define MULTILIB_DEFAULTS { "EB", "mips1" } +#endif +#endif + +/* We must pass -EL to the linker by default for little endian embedded + targets using linker scripts with a OUTPUT_FORMAT line. Otherwise, the + linker will default to using big-endian output files. The OUTPUT_FORMAT + line must be in the linker script, otherwise -EB/-EL will not work. */ + +#ifndef LINKER_ENDIAN_SPEC +#if TARGET_ENDIAN_DEFAULT == 0 +#define LINKER_ENDIAN_SPEC "%{!EB:%{!meb:-EL}}" +#else +#define LINKER_ENDIAN_SPEC "" +#endif +#endif + +/* 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 \ +{ \ + SUBTARGET_TARGET_OPTIONS \ + { "cpu=", &mips_cpu_string, \ + "Specify CPU for scheduling purposes"}, \ + { "ips", &mips_isa_string, \ + "Specify MIPS ISA"}, \ + { "entry", &mips_entry_string, \ + "Use mips16 entry/exit psuedo ops"}, \ + { "no-mips16", &mips_no_mips16_string, \ + "Don't use MIPS16 instructions"}, \ + { "explicit-type-size", &mips_explicit_type_size_string, \ + NULL}, \ +} + +/* This is meant to be redefined in the host dependent files. */ +#define SUBTARGET_TARGET_OPTIONS + +#define GENERATE_BRANCHLIKELY (!TARGET_MIPS16 && (TARGET_MIPS3900 || (mips_isa >= 2))) + +/* Generate three-operand multiply instructions for both SImode and DImode. */ +#define GENERATE_MULT3 (TARGET_MIPS3900 \ + && !TARGET_MIPS16) + +/* Macros to decide whether certain features are available or not, + depending on the instruction set architecture level. */ + +#define BRANCH_LIKELY_P() GENERATE_BRANCHLIKELY +#define HAVE_SQRT_P() (mips_isa >= 2) + +/* CC1_SPEC causes -mips3 and -mips4 to set -mfp64 and -mgp64; -mips1 or + -mips2 sets -mfp32 and -mgp32. This can be overridden by an explicit + -mfp32, -mfp64, -mgp32 or -mgp64. -mfp64 sets MASK_FLOAT64 in + target_flags, and -mgp64 sets MASK_64BIT. + + Setting MASK_64BIT in target_flags will cause gcc to assume that + registers are 64 bits wide. int, long and void * will be 32 bit; + this may be changed with -mint64 or -mlong64. + + The gen* programs link code that refers to MASK_64BIT. They don't + actually use the information in target_flags; they just refer to + it. */ + +/* Switch Recognition by gcc.c. Add -G xx support */ + +#ifdef SWITCH_TAKES_ARG +#undef SWITCH_TAKES_ARG +#endif + +#define SWITCH_TAKES_ARG(CHAR) \ + (DEFAULT_SWITCH_TAKES_ARG (CHAR) || (CHAR) == 'G') + +/* Sometimes certain combinations of command options do not make sense + on a particular target machine. You can define a macro + `OVERRIDE_OPTIONS' to take account of this. This macro, if + defined, is executed once just after all the command options have + been parsed. + + On the MIPS, it is used to handle -G. We also use it to set up all + of the tables referenced in the other macros. */ + +#define OVERRIDE_OPTIONS override_options () + +/* Zero or more C statements that may conditionally modify two + variables `fixed_regs' and `call_used_regs' (both of type `char + []') after they have been initialized from the two preceding + macros. + + This is necessary in case the fixed or call-clobbered registers + depend on target flags. + + You need not define this macro if it has no work to do. + + If the usage of an entire class of registers depends on the target + flags, you may indicate this to GCC by using this macro to modify + `fixed_regs' and `call_used_regs' to 1 for each of the registers in + the classes which should not be used by GCC. Also define the macro + `REG_CLASS_FROM_LETTER' to return `NO_REGS' if it is called with a + letter for a class that shouldn't be used. + + (However, if this class is not included in `GENERAL_REGS' and all + of the insn patterns whose constraints permit this class are + controlled by target switches, then GCC will automatically avoid + using these registers when the target switches are opposed to + them.) */ + +#define CONDITIONAL_REGISTER_USAGE \ +do \ + { \ + if (!TARGET_HARD_FLOAT) \ + { \ + int regno; \ + \ + for (regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++) \ + fixed_regs[regno] = call_used_regs[regno] = 1; \ + for (regno = ST_REG_FIRST; regno <= ST_REG_LAST; regno++) \ + fixed_regs[regno] = call_used_regs[regno] = 1; \ + } \ + else if (mips_isa < 4) \ + { \ + int regno; \ + \ + /* We only have a single condition code register. We \ + implement this by hiding all the condition code registers, \ + and generating RTL that refers directly to ST_REG_FIRST. */ \ + for (regno = ST_REG_FIRST; regno <= ST_REG_LAST; regno++) \ + fixed_regs[regno] = call_used_regs[regno] = 1; \ + } \ + /* In mips16 mode, we permit the $t temporary registers to be used \ + for reload. We prohibit the unused $s registers, since they \ + are caller saved, and saving them via a mips16 register would \ + probably waste more time than just reloading the value. */ \ + if (TARGET_MIPS16) \ + { \ + fixed_regs[18] = call_used_regs[18] = 1; \ + fixed_regs[19] = call_used_regs[19] = 1; \ + fixed_regs[20] = call_used_regs[20] = 1; \ + fixed_regs[21] = call_used_regs[21] = 1; \ + fixed_regs[22] = call_used_regs[22] = 1; \ + fixed_regs[23] = call_used_regs[23] = 1; \ + fixed_regs[26] = call_used_regs[26] = 1; \ + fixed_regs[27] = call_used_regs[27] = 1; \ + fixed_regs[30] = call_used_regs[30] = 1; \ + } \ + SUBTARGET_CONDITIONAL_REGISTER_USAGE \ + } \ +while (0) + +/* This is meant to be redefined in the host dependent files. */ +#define SUBTARGET_CONDITIONAL_REGISTER_USAGE + +/* Show we can debug even without a frame pointer. */ +#define CAN_DEBUG_WITHOUT_FP + +/* Complain about missing specs and predefines that should be defined in each + of the target tm files to override the defaults. This is mostly a place- + holder until I can get each of the files updated [mm]. */ + +#if defined(OSF_OS) \ + || defined(DECSTATION) \ + || defined(SGI_TARGET) \ + || defined(MIPS_NEWS) \ + || defined(MIPS_SYSV) \ + || defined(MIPS_SVR4) \ + || defined(MIPS_BSD43) + +#ifndef CPP_PREDEFINES + #error "Define CPP_PREDEFINES in the appropriate tm.h file" +#endif + +#ifndef LIB_SPEC + #error "Define LIB_SPEC in the appropriate tm.h file" +#endif + +#ifndef STARTFILE_SPEC + #error "Define STARTFILE_SPEC in the appropriate tm.h file" +#endif + +#ifndef MACHINE_TYPE + #error "Define MACHINE_TYPE in the appropriate tm.h file" +#endif +#endif + +/* Tell collect what flags to pass to nm. */ +#ifndef NM_FLAGS +#define NM_FLAGS "-Bn" +#endif + + +/* Names to predefine in the preprocessor for this target machine. */ + +#ifndef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dmips -Dunix -Dhost_mips -DMIPSEB -DR3000 -DSYSTYPE_BSD43 \ +-D_mips -D_unix -D_host_mips -D_MIPSEB -D_R3000 -D_SYSTYPE_BSD43 \ +-Asystem(unix) -Asystem(bsd) -Acpu(mips) -Amachine(mips)" +#endif + +/* Assembler specs. */ + +/* MIPS_AS_ASM_SPEC is passed when using the MIPS assembler rather + than gas. */ + +#define MIPS_AS_ASM_SPEC "\ +%{!.s:-nocpp} %{.s: %{cpp} %{nocpp}} \ +%{pipe: %e-pipe is not supported.} \ +%{K} %(subtarget_mips_as_asm_spec)" + +/* SUBTARGET_MIPS_AS_ASM_SPEC is passed when using the MIPS assembler + rather than gas. It may be overridden by subtargets. */ + +#ifndef SUBTARGET_MIPS_AS_ASM_SPEC +#define SUBTARGET_MIPS_AS_ASM_SPEC "%{v}" +#endif + +/* GAS_ASM_SPEC is passed when using gas, rather than the MIPS + assembler. */ + +#define GAS_ASM_SPEC "%{mcpu=*} %{m4650} %{mmad:-m4650} %{m3900} %{v}" + +/* TARGET_ASM_SPEC is used to select either MIPS_AS_ASM_SPEC or + GAS_ASM_SPEC as the default, depending upon the value of + TARGET_DEFAULT. */ + +#if ((TARGET_CPU_DEFAULT | TARGET_DEFAULT) & MASK_GAS) != 0 +/* GAS */ + +#define TARGET_ASM_SPEC "\ +%{mmips-as: %(mips_as_asm_spec)} \ +%{!mmips-as: %(gas_asm_spec)}" + +#else /* not GAS */ + +#define TARGET_ASM_SPEC "\ +%{!mgas: %(mips_as_asm_spec)} \ +%{mgas: %(gas_asm_spec)}" + +#endif /* not GAS */ + +/* SUBTARGET_ASM_OPTIMIZING_SPEC handles passing optimization options + to the assembler. It may be overridden by subtargets. */ +#ifndef SUBTARGET_ASM_OPTIMIZING_SPEC +#define SUBTARGET_ASM_OPTIMIZING_SPEC "\ +%{noasmopt:-O0} \ +%{!noasmopt:%{O:-O2} %{O1:-O2} %{O2:-O2} %{O3:-O3}}" +#endif + +/* SUBTARGET_ASM_DEBUGGING_SPEC handles passing debugging options to + the assembler. It may be overridden by subtargets. */ +#ifndef SUBTARGET_ASM_DEBUGGING_SPEC +#define SUBTARGET_ASM_DEBUGGING_SPEC "\ +%{g} %{g0} %{g1} %{g2} %{g3} \ +%{ggdb:-g} %{ggdb0:-g0} %{ggdb1:-g1} %{ggdb2:-g2} %{ggdb3:-g3} \ +%{gstabs:-g} %{gstabs0:-g0} %{gstabs1:-g1} %{gstabs2:-g2} %{gstabs3:-g3} \ +%{gstabs+:-g} %{gstabs+0:-g0} %{gstabs+1:-g1} %{gstabs+2:-g2} %{gstabs+3:-g3} \ +%{gcoff:-g} %{gcoff0:-g0} %{gcoff1:-g1} %{gcoff2:-g2} %{gcoff3:-g3}" +#endif + +/* SUBTARGET_ASM_SPEC is always passed to the assembler. It may be + overridden by subtargets. */ + +#ifndef SUBTARGET_ASM_SPEC +#define SUBTARGET_ASM_SPEC "" +#endif + +/* ASM_SPEC is the set of arguments to pass to the assembler. */ + +#define ASM_SPEC "\ +%{G*} %{EB} %{EL} %{mips1} %{mips2} %{mips3} %{mips4} \ +%{mips16:%{!mno-mips16:-mips16}} %{mno-mips16:-no-mips16} \ +%(subtarget_asm_optimizing_spec) \ +%(subtarget_asm_debugging_spec) \ +%{membedded-pic} \ +%{mabi=32:-32}%{mabi=o32:-32}%{mabi=n32:-n32}%{mabi=64:-64}%{mabi=n64:-64} \ +%(target_asm_spec) \ +%(subtarget_asm_spec)" + +/* Specify to run a post-processor, mips-tfile after the assembler + has run to stuff the mips debug information into the object file. + This is needed because the $#!%^ MIPS assembler provides no way + of specifying such information in the assembly file. If we are + cross compiling, disable mips-tfile unless the user specifies + -mmips-tfile. */ + +#ifndef ASM_FINAL_SPEC +#if ((TARGET_CPU_DEFAULT | TARGET_DEFAULT) & MASK_GAS) != 0 +/* GAS */ +#define ASM_FINAL_SPEC "\ +%{mmips-as: %{!mno-mips-tfile: \ + \n mips-tfile %{v*: -v} \ + %{K: -I %b.o~} \ + %{!K: %{save-temps: -I %b.o~}} \ + %{c:%W{o*}%{!o*:-o %b.o}}%{!c:-o %U.o} \ + %{.s:%i} %{!.s:%g.s}}}" + +#else +/* not GAS */ +#define ASM_FINAL_SPEC "\ +%{!mgas: %{!mno-mips-tfile: \ + \n mips-tfile %{v*: -v} \ + %{K: -I %b.o~} \ + %{!K: %{save-temps: -I %b.o~}} \ + %{c:%W{o*}%{!o*:-o %b.o}}%{!c:-o %U.o} \ + %{.s:%i} %{!.s:%g.s}}}" + +#endif +#endif /* ASM_FINAL_SPEC */ + +/* Redefinition of libraries used. Mips doesn't support normal + UNIX style profiling via calling _mcount. It does offer + profiling that samples the PC, so do what we can... */ + +#ifndef LIB_SPEC +#define LIB_SPEC "%{pg:-lprof1} %{p:-lprof1} -lc" +#endif + +/* Extra switches sometimes passed to the linker. */ +/* ??? The bestGnum will never be passed to the linker, because the gcc driver + will interpret it as a -b option. */ + +#ifndef LINK_SPEC +#define LINK_SPEC "\ +%{G*} %{EB} %{EL} %{mips1} %{mips2} %{mips3} %{mips4} \ +%{bestGnum} %{shared} %{non_shared} \ +%(linker_endian_spec)" +#endif /* LINK_SPEC defined */ + +/* Specs for the compiler proper */ + +/* SUBTARGET_CC1_SPEC is passed to the compiler proper. It may be + overridden by subtargets. */ +#ifndef SUBTARGET_CC1_SPEC +#define SUBTARGET_CC1_SPEC "" +#endif + +/* CC1_SPEC is the set of arguments to pass to the compiler proper. */ + +#ifndef CC1_SPEC +#define CC1_SPEC "\ +%{gline:%{!g:%{!g0:%{!g1:%{!g2: -g1}}}}} \ +%{mips1:-mfp32 -mgp32} %{mips2:-mfp32 -mgp32}\ +%{mips3:%{!msingle-float:%{!m4650:-mfp64}} -mgp64} \ +%{mips4:%{!msingle-float:%{!m4650:-mfp64}} -mgp64} \ +%{mfp64:%{msingle-float:%emay not use both -mfp64 and -msingle-float}} \ +%{mfp64:%{m4650:%emay not use both -mfp64 and -m4650}} \ +%{mint64|mlong64|mlong32:-mexplicit-type-size }\ +%{m4650:-mcpu=r4650} \ +%{m3900:-mips1 -mcpu=r3900 -mfp32 -mgp32} \ +%{G*} %{EB:-meb} %{EL:-mel} %{EB:%{EL:%emay not use both -EB and -EL}} \ +%{pic-none: -mno-half-pic} \ +%{pic-lib: -mhalf-pic} \ +%{pic-extern: -mhalf-pic} \ +%{pic-calls: -mhalf-pic} \ +%{save-temps: } \ +%(subtarget_cc1_spec) " +#endif + +/* Preprocessor specs. */ + +/* SUBTARGET_CPP_SIZE_SPEC defines SIZE_TYPE and PTRDIFF_TYPE. It may + be overridden by subtargets. */ + +#ifndef SUBTARGET_CPP_SIZE_SPEC +#define SUBTARGET_CPP_SIZE_SPEC "\ +%{mlong64:%{!mips1:%{!mips2:-D__SIZE_TYPE__=long\\ unsigned\\ int -D__PTRDIFF_TYPE__=long\\ int}}} \ +%{!mlong64:-D__SIZE_TYPE__=unsigned\\ int -D__PTRDIFF_TYPE__=int}" +#endif + +/* SUBTARGET_CPP_SPEC is passed to the preprocessor. It may be + overridden by subtargets. */ +#ifndef SUBTARGET_CPP_SPEC +#define SUBTARGET_CPP_SPEC "" +#endif + +/* If we're using 64bit longs, then we have to define __LONG_MAX__ + correctly. Similarly for 64bit ints and __INT_MAX__. */ +#ifndef LONG_MAX_SPEC +#if ((TARGET_DEFAULT | TARGET_CPU_DEFAULT) & MASK_LONG64) +#define LONG_MAX_SPEC "%{!mlong32:-D__LONG_MAX__=9223372036854775807L}" +#else +#define LONG_MAX_SPEC "%{mlong64:-D__LONG_MAX__=9223372036854775807L}" +#endif +#endif + +/* CPP_SPEC is the set of arguments to pass to the preprocessor. */ + +#ifndef CPP_SPEC +#define CPP_SPEC "\ +%{.cc: -D__LANGUAGE_C_PLUS_PLUS -D_LANGUAGE_C_PLUS_PLUS} \ +%{.cxx: -D__LANGUAGE_C_PLUS_PLUS -D_LANGUAGE_C_PLUS_PLUS} \ +%{.C: -D__LANGUAGE_C_PLUS_PLUS -D_LANGUAGE_C_PLUS_PLUS} \ +%{.m: -D__LANGUAGE_OBJECTIVE_C -D_LANGUAGE_OBJECTIVE_C -D__LANGUAGE_C -D_LANGUAGE_C} \ +%{.S: -D__LANGUAGE_ASSEMBLY -D_LANGUAGE_ASSEMBLY %{!ansi:-DLANGUAGE_ASSEMBLY}} \ +%{.s: -D__LANGUAGE_ASSEMBLY -D_LANGUAGE_ASSEMBLY %{!ansi:-DLANGUAGE_ASSEMBLY}} \ +%{!.S: %{!.s: %{!.cc: %{!.cxx: %{!.C: %{!.m: -D__LANGUAGE_C -D_LANGUAGE_C %{!ansi:-DLANGUAGE_C}}}}}}} \ +%(subtarget_cpp_size_spec) \ +%{mips3:-U__mips -D__mips=3 -D__mips64} \ +%{mips4:-U__mips -D__mips=4 -D__mips64} \ +%{mgp32:-U__mips64} %{mgp64:-D__mips64} \ +%{msingle-float:%{!msoft-float:-D__mips_single_float}} \ +%{m4650:%{!msoft-float:-D__mips_single_float}} \ +%{msoft-float:-D__mips_soft_float} \ +%{mabi=eabi:-D__mips_eabi} \ +%{mips16:%{!mno-mips16:-D__mips16}} \ +%{EB:-UMIPSEL -U_MIPSEL -U__MIPSEL -U__MIPSEL__ -D_MIPSEB -D__MIPSEB -D__MIPSEB__ %{!ansi:-DMIPSEB}} \ +%{EL:-UMIPSEB -U_MIPSEB -U__MIPSEB -U__MIPSEB__ -D_MIPSEL -D__MIPSEL -D__MIPSEL__ %{!ansi:-DMIPSEL}} \ +%(long_max_spec) \ +%(subtarget_cpp_spec) " +#endif + +/* 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 \ + { "subtarget_cc1_spec", SUBTARGET_CC1_SPEC }, \ + { "subtarget_cpp_spec", SUBTARGET_CPP_SPEC }, \ + { "subtarget_cpp_size_spec", SUBTARGET_CPP_SIZE_SPEC }, \ + { "long_max_spec", LONG_MAX_SPEC }, \ + { "mips_as_asm_spec", MIPS_AS_ASM_SPEC }, \ + { "gas_asm_spec", GAS_ASM_SPEC }, \ + { "target_asm_spec", TARGET_ASM_SPEC }, \ + { "subtarget_mips_as_asm_spec", SUBTARGET_MIPS_AS_ASM_SPEC }, \ + { "subtarget_asm_optimizing_spec", SUBTARGET_ASM_OPTIMIZING_SPEC }, \ + { "subtarget_asm_debugging_spec", SUBTARGET_ASM_DEBUGGING_SPEC }, \ + { "subtarget_asm_spec", SUBTARGET_ASM_SPEC }, \ + { "linker_endian_spec", LINKER_ENDIAN_SPEC }, \ + SUBTARGET_EXTRA_SPECS + +#ifndef SUBTARGET_EXTRA_SPECS +#define SUBTARGET_EXTRA_SPECS +#endif + +/* If defined, this macro is an additional prefix to try after + `STANDARD_EXEC_PREFIX'. */ + +#ifndef MD_EXEC_PREFIX +#define MD_EXEC_PREFIX "/usr/lib/cmplrs/cc/" +#endif + +#ifndef MD_STARTFILE_PREFIX +#define MD_STARTFILE_PREFIX "/usr/lib/cmplrs/cc/" +#endif + + +/* Print subsidiary information on the compiler version in use. */ + +#define MIPS_VERSION "[AL 1.1, MM 40]" + +#ifndef MACHINE_TYPE +#define MACHINE_TYPE "BSD Mips" +#endif + +#ifndef TARGET_VERSION_INTERNAL +#define TARGET_VERSION_INTERNAL(STREAM) \ + fprintf (STREAM, " %s %s", MIPS_VERSION, MACHINE_TYPE) +#endif + +#ifndef TARGET_VERSION +#define TARGET_VERSION TARGET_VERSION_INTERNAL (stderr) +#endif + + +#define SDB_DEBUGGING_INFO /* generate info for mips-tfile */ +#define DBX_DEBUGGING_INFO /* generate stabs (OSF/rose) */ +#define MIPS_DEBUGGING_INFO /* MIPS specific debugging info */ + +#ifndef PREFERRED_DEBUGGING_TYPE /* assume SDB_DEBUGGING_INFO */ +#define PREFERRED_DEBUGGING_TYPE SDB_DEBUG +#endif + +/* By default, turn on GDB extensions. */ +#define DEFAULT_GDB_EXTENSIONS 1 + +/* If we are passing smuggling stabs through the MIPS ECOFF object + format, put a comment in front of the .stab<x> operation so + that the MIPS assembler does not choke. The mips-tfile program + will correctly put the stab into the object file. */ + +#define ASM_STABS_OP ((TARGET_GAS) ? ".stabs" : " #.stabs") +#define ASM_STABN_OP ((TARGET_GAS) ? ".stabn" : " #.stabn") +#define ASM_STABD_OP ((TARGET_GAS) ? ".stabd" : " #.stabd") + +/* Local compiler-generated symbols must have a prefix that the assembler + understands. By default, this is $, although some targets (e.g., + NetBSD-ELF) need to override this. */ + +#ifndef LOCAL_LABEL_PREFIX +#define LOCAL_LABEL_PREFIX "$" +#endif + +/* By default on the mips, external symbols do not have an underscore + prepended, but some targets (e.g., NetBSD) require this. */ + +#ifndef USER_LABEL_PREFIX +#define USER_LABEL_PREFIX "" +#endif + +/* Forward references to tags are allowed. */ +#define SDB_ALLOW_FORWARD_REFERENCES + +/* Unknown tags are also allowed. */ +#define SDB_ALLOW_UNKNOWN_REFERENCES + +/* On Sun 4, this limit is 2048. We use 1500 to be safe, + since the length can run past this up to a continuation point. */ +#define DBX_CONTIN_LENGTH 1500 + +/* How to renumber registers for dbx and gdb. */ +#define DBX_REGISTER_NUMBER(REGNO) mips_dbx_regno[ (REGNO) ] + +/* The mapping from gcc register number to DWARF 2 CFA column number. + This mapping does not allow for tracking register 0, since SGI's broken + dwarf reader thinks column 0 is used for the frame address, but since + register 0 is fixed this is not a problem. */ +#define DWARF_FRAME_REGNUM(REG) \ + (REG == GP_REG_FIRST + 31 ? DWARF_FRAME_RETURN_COLUMN : REG) + +/* The DWARF 2 CFA column which tracks the return address. */ +#define DWARF_FRAME_RETURN_COLUMN (FP_REG_LAST + 1) + +/* Before the prologue, RA lives in r31. */ +#define INCOMING_RETURN_ADDR_RTX gen_rtx (REG, VOIDmode, GP_REG_FIRST + 31) + +/* Overrides for the COFF debug format. */ +#define PUT_SDB_SCL(a) \ +do { \ + extern FILE *asm_out_text_file; \ + fprintf (asm_out_text_file, "\t.scl\t%d;", (a)); \ +} while (0) + +#define PUT_SDB_INT_VAL(a) \ +do { \ + extern FILE *asm_out_text_file; \ + fprintf (asm_out_text_file, "\t.val\t%d;", (a)); \ +} while (0) + +#define PUT_SDB_VAL(a) \ +do { \ + extern FILE *asm_out_text_file; \ + fputs ("\t.val\t", asm_out_text_file); \ + output_addr_const (asm_out_text_file, (a)); \ + fputc (';', asm_out_text_file); \ +} while (0) + +#define PUT_SDB_DEF(a) \ +do { \ + extern FILE *asm_out_text_file; \ + fprintf (asm_out_text_file, "\t%s.def\t", \ + (TARGET_GAS) ? "" : "#"); \ + ASM_OUTPUT_LABELREF (asm_out_text_file, a); \ + fputc (';', asm_out_text_file); \ +} while (0) + +#define PUT_SDB_PLAIN_DEF(a) \ +do { \ + extern FILE *asm_out_text_file; \ + fprintf (asm_out_text_file, "\t%s.def\t.%s;", \ + (TARGET_GAS) ? "" : "#", (a)); \ +} while (0) + +#define PUT_SDB_ENDEF \ +do { \ + extern FILE *asm_out_text_file; \ + fprintf (asm_out_text_file, "\t.endef\n"); \ +} while (0) + +#define PUT_SDB_TYPE(a) \ +do { \ + extern FILE *asm_out_text_file; \ + fprintf (asm_out_text_file, "\t.type\t0x%x;", (a)); \ +} while (0) + +#define PUT_SDB_SIZE(a) \ +do { \ + extern FILE *asm_out_text_file; \ + fprintf (asm_out_text_file, "\t.size\t%d;", (a)); \ +} while (0) + +#define PUT_SDB_DIM(a) \ +do { \ + extern FILE *asm_out_text_file; \ + fprintf (asm_out_text_file, "\t.dim\t%d;", (a)); \ +} while (0) + +#ifndef PUT_SDB_START_DIM +#define PUT_SDB_START_DIM \ +do { \ + extern FILE *asm_out_text_file; \ + fprintf (asm_out_text_file, "\t.dim\t"); \ +} while (0) +#endif + +#ifndef PUT_SDB_NEXT_DIM +#define PUT_SDB_NEXT_DIM(a) \ +do { \ + extern FILE *asm_out_text_file; \ + fprintf (asm_out_text_file, "%d,", a); \ +} while (0) +#endif + +#ifndef PUT_SDB_LAST_DIM +#define PUT_SDB_LAST_DIM(a) \ +do { \ + extern FILE *asm_out_text_file; \ + fprintf (asm_out_text_file, "%d;", a); \ +} while (0) +#endif + +#define PUT_SDB_TAG(a) \ +do { \ + extern FILE *asm_out_text_file; \ + fprintf (asm_out_text_file, "\t.tag\t"); \ + ASM_OUTPUT_LABELREF (asm_out_text_file, a); \ + fputc (';', asm_out_text_file); \ +} while (0) + +/* For block start and end, we create labels, so that + later we can figure out where the correct offset is. + The normal .ent/.end serve well enough for functions, + so those are just commented out. */ + +#define PUT_SDB_BLOCK_START(LINE) \ +do { \ + extern FILE *asm_out_text_file; \ + fprintf (asm_out_text_file, \ + "%sLb%d:\n\t%s.begin\t%sLb%d\t%d\n", \ + LOCAL_LABEL_PREFIX, \ + sdb_label_count, \ + (TARGET_GAS) ? "" : "#", \ + LOCAL_LABEL_PREFIX, \ + sdb_label_count, \ + (LINE)); \ + sdb_label_count++; \ +} while (0) + +#define PUT_SDB_BLOCK_END(LINE) \ +do { \ + extern FILE *asm_out_text_file; \ + fprintf (asm_out_text_file, \ + "%sLe%d:\n\t%s.bend\t%sLe%d\t%d\n", \ + LOCAL_LABEL_PREFIX, \ + sdb_label_count, \ + (TARGET_GAS) ? "" : "#", \ + LOCAL_LABEL_PREFIX, \ + sdb_label_count, \ + (LINE)); \ + sdb_label_count++; \ +} while (0) + +#define PUT_SDB_FUNCTION_START(LINE) + +#define PUT_SDB_FUNCTION_END(LINE) \ +do { \ + extern FILE *asm_out_text_file; \ + ASM_OUTPUT_SOURCE_LINE (asm_out_text_file, LINE + sdb_begin_function_line); \ +} while (0) + +#define PUT_SDB_EPILOGUE_END(NAME) + +#define PUT_SDB_SRC_FILE(FILENAME) \ +do { \ + extern FILE *asm_out_text_file; \ + output_file_directive (asm_out_text_file, (FILENAME)); \ +} while (0) + +#define SDB_GENERATE_FAKE(BUFFER, NUMBER) \ + sprintf ((BUFFER), ".%dfake", (NUMBER)); + +/* Correct the offset of automatic variables and arguments. Note that + the MIPS debug format wants all automatic variables and arguments + to be in terms of the virtual frame pointer (stack pointer before + any adjustment in the function), while the MIPS 3.0 linker wants + the frame pointer to be the stack pointer after the initial + adjustment. */ + +#define DEBUGGER_AUTO_OFFSET(X) \ + mips_debugger_offset (X, (HOST_WIDE_INT) 0) +#define DEBUGGER_ARG_OFFSET(OFFSET, X) \ + mips_debugger_offset (X, (HOST_WIDE_INT) OFFSET) + +/* Tell collect that the object format is ECOFF */ +#ifndef OBJECT_FORMAT_ROSE +#define OBJECT_FORMAT_COFF /* Object file looks like COFF */ +#define EXTENDED_COFF /* ECOFF, not normal coff */ +#endif + +#if 0 /* These definitions normally have no effect because + MIPS systems define USE_COLLECT2, so + assemble_constructor does nothing anyway. */ + +/* Don't use the default definitions, because we don't have gld. + Also, we don't want stabs when generating ECOFF output. + Instead we depend on collect to handle these. */ + +#define ASM_OUTPUT_CONSTRUCTOR(file, name) +#define ASM_OUTPUT_DESTRUCTOR(file, name) + +#endif /* 0 */ + +/* Target machine storage layout */ + +/* Define in order to support both big and little endian float formats + in the same gcc binary. */ +#define REAL_ARITHMETIC + +/* Define this if most significant bit is lowest numbered + in instructions that operate on numbered bit-fields. +*/ +#define BITS_BIG_ENDIAN 0 + +/* Define this if most significant byte of a word is the lowest numbered. */ +#define BYTES_BIG_ENDIAN (TARGET_BIG_ENDIAN != 0) + +/* Define this if most significant word of a multiword number is the lowest. */ +#define WORDS_BIG_ENDIAN (TARGET_BIG_ENDIAN != 0) + +/* Define this to set the endianness to use in libgcc2.c, which can + not depend on target_flags. */ +#if !defined(MIPSEL) && !defined(__MIPSEL__) +#define LIBGCC2_WORDS_BIG_ENDIAN 1 +#else +#define LIBGCC2_WORDS_BIG_ENDIAN 0 +#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_64BIT ? 64 : 32) +#define MAX_BITS_PER_WORD 64 + +/* Width of a word, in units (bytes). */ +#define UNITS_PER_WORD (TARGET_64BIT ? 8 : 4) +#define MIN_UNITS_PER_WORD 4 + +/* For MIPS, width of a floating point register. */ +#define UNITS_PER_FPREG (TARGET_FLOAT64 ? 8 : 4) + +/* A C expression for the size in bits of the type `int' on the + target machine. If you don't define this, the default is one + word. */ +#define INT_TYPE_SIZE (TARGET_INT64 ? 64 : 32) +#define MAX_INT_TYPE_SIZE 64 + +/* Tell the preprocessor the maximum size of wchar_t. */ +#ifndef MAX_WCHAR_TYPE_SIZE +#ifndef WCHAR_TYPE_SIZE +#define MAX_WCHAR_TYPE_SIZE MAX_INT_TYPE_SIZE +#endif +#endif + +/* A C expression for the size in bits of the type `short' on the + target machine. If you don't define this, the default is half a + word. (If this would be less than one storage unit, it is + rounded up to one unit.) */ +#define SHORT_TYPE_SIZE 16 + +/* A C expression for the size in bits of the type `long' on the + target machine. If you don't define this, the default is one + word. */ +#define LONG_TYPE_SIZE (TARGET_LONG64 ? 64 : 32) +#define MAX_LONG_TYPE_SIZE 64 + +/* A C expression for the size in bits of the type `long long' on the + target machine. If you don't define this, the default is two + words. */ +#define LONG_LONG_TYPE_SIZE 64 + +/* A C expression for the size in bits of the type `char' on the + target machine. If you don't define this, the default is one + quarter of a word. (If this would be less than one storage unit, + it is rounded up to one unit.) */ +#define CHAR_TYPE_SIZE BITS_PER_UNIT + +/* A C expression for the size in bits of the type `float' on the + target machine. If you don't define this, the default is one + word. */ +#define FLOAT_TYPE_SIZE 32 + +/* A C expression for the size in bits of the type `double' on the + target machine. If you don't define this, the default is two + words. */ +#define DOUBLE_TYPE_SIZE 64 + +/* A C expression for the size in bits of the type `long double' on + the target machine. If you don't define this, the default is two + words. */ +#define LONG_DOUBLE_TYPE_SIZE 64 + +/* Width in bits of a pointer. + See also the macro `Pmode' defined below. */ +#ifndef POINTER_SIZE +#define POINTER_SIZE (Pmode == DImode ? 64 : 32) +#endif + +/* Allocation boundary (in *bits*) for storing pointers in memory. */ +#define POINTER_BOUNDARY (Pmode == DImode ? 64 : 32) + +/* Allocation boundary (in *bits*) for storing arguments in argument list. */ +#define PARM_BOUNDARY (TARGET_64BIT ? 64 : 32) + +/* Allocation boundary (in *bits*) for the code of a function. */ +#define FUNCTION_BOUNDARY 32 + +/* Alignment of field after `int : 0' in a structure. */ +#define EMPTY_FIELD_BOUNDARY 32 + +/* Every structure's size must be a multiple of this. */ +/* 8 is observed right on a DECstation and on riscos 4.02. */ +#define STRUCTURE_SIZE_BOUNDARY 8 + +/* There is no point aligning anything to a rounder boundary than this. */ +#define BIGGEST_ALIGNMENT 64 + +/* Set this nonzero if move instructions will actually fail to work + when given unaligned data. */ +#define STRICT_ALIGNMENT 1 + +/* Define this if you wish to imitate the way many other C compilers + handle alignment of bitfields and the structures that contain + them. + + The behavior is that the type written for a bitfield (`int', + `short', or other integer type) imposes an alignment for the + entire structure, as if the structure really did contain an + ordinary field of that type. In addition, the bitfield is placed + within the structure so that it would fit within such a field, + not crossing a boundary for it. + + Thus, on most machines, a bitfield whose type is written as `int' + would not cross a four-byte boundary, and would force four-byte + alignment for the whole structure. (The alignment used may not + be four bytes; it is controlled by the other alignment + parameters.) + + If the macro is defined, its definition should be a C expression; + a nonzero value for the expression enables this behavior. */ + +#define PCC_BITFIELD_TYPE_MATTERS 1 + +/* If defined, a C expression to compute the alignment given to a + constant that is being placed in memory. CONSTANT is the constant + and ALIGN is the alignment that the object would ordinarily have. + The value of this macro is used instead of that alignment to align + the object. + + If this macro is not defined, then ALIGN is used. + + The typical use of this macro is to increase alignment for string + constants to be word aligned so that `strcpy' calls that copy + constants can be done inline. */ + +#define CONSTANT_ALIGNMENT(EXP, ALIGN) \ + ((TREE_CODE (EXP) == STRING_CST || TREE_CODE (EXP) == CONSTRUCTOR) \ + && (ALIGN) < BITS_PER_WORD \ + ? BITS_PER_WORD \ + : (ALIGN)) + +/* If defined, a C expression to compute the alignment for a static + variable. TYPE is the data type, and ALIGN is the alignment that + the object would ordinarily have. The value of this macro is used + instead of that alignment to align the object. + + If this macro is not defined, then ALIGN is used. + + One use of this macro is to increase alignment of medium-size + data to make it all fit in fewer cache lines. Another is to + cause character arrays to be word-aligned so that `strcpy' calls + that copy constants to character arrays can be done inline. */ + +#undef DATA_ALIGNMENT +#define DATA_ALIGNMENT(TYPE, ALIGN) \ + ((((ALIGN) < BITS_PER_WORD) \ + && (TREE_CODE (TYPE) == ARRAY_TYPE \ + || TREE_CODE (TYPE) == UNION_TYPE \ + || TREE_CODE (TYPE) == RECORD_TYPE)) ? BITS_PER_WORD : (ALIGN)) + +/* Define this macro if an argument declared as `char' or `short' in a + prototype should actually be passed as an `int'. In addition to + avoiding errors in certain cases of mismatch, it also makes for + better code on certain machines. */ + +#define PROMOTE_PROTOTYPES + +/* 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. + + When in 64 bit mode, mips_move_1word will sign extend SImode and CCmode + moves. All other referces are zero extended. */ +#define LOAD_EXTEND_OP(MODE) \ + (TARGET_64BIT && ((MODE) == SImode || (MODE) == CCmode) \ + ? SIGN_EXTEND : ZERO_EXTEND) + +/* Define this macro if it is advisable to hold scalars in registers + in a wider mode than that declared by the program. In such cases, + the value is constrained to be within the bounds of the declared + type, but kept valid in the wider mode. The signedness of the + extension may differ from that of the type. + + We promote any value smaller than SImode up to SImode. We don't + want to promote to DImode when in 64 bit mode, because that would + prevent us from using the faster SImode multiply and divide + instructions. */ + +#define PROMOTE_MODE(MODE, UNSIGNEDP, TYPE) \ + if (GET_MODE_CLASS (MODE) == MODE_INT \ + && GET_MODE_SIZE (MODE) < 4) \ + (MODE) = SImode; + +/* Define this if function arguments should also be promoted using the above + procedure. */ + +#define PROMOTE_FUNCTION_ARGS + +/* Likewise, if the function return value is promoted. */ + +#define PROMOTE_FUNCTION_RETURN + +/* 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. + + On the Mips, we have 32 integer registers, 32 floating point + registers, 8 condition code registers, and the special registers + hi, lo, hilo, and rap. The 8 condition code registers are only + used if mips_isa >= 4. The hilo register is only used in 64 bit + mode. It represents a 64 bit value stored as two 32 bit values in + the hi and lo registers; this is the result of the mult + instruction. rap is a pointer to the stack where the return + address reg ($31) was stored. This is needed for C++ exception + handling. */ + +#define FIRST_PSEUDO_REGISTER 76 + +/* 1 for registers that have pervasive standard uses + and are not available for the register allocator. + + On the MIPS, see conventions, page D-2 */ + +#define FIXED_REGISTERS \ +{ \ + 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, 1, 1, 1, 1, 0, 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, 1 \ +} + + +/* 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 \ +} + + +/* Internal macros to classify a register number as to whether it's a + general purpose register, a floating point register, a + multiply/divide register, or a status register. */ + +#define GP_REG_FIRST 0 +#define GP_REG_LAST 31 +#define GP_REG_NUM (GP_REG_LAST - GP_REG_FIRST + 1) +#define GP_DBX_FIRST 0 + +#define FP_REG_FIRST 32 +#define FP_REG_LAST 63 +#define FP_REG_NUM (FP_REG_LAST - FP_REG_FIRST + 1) +#define FP_DBX_FIRST ((write_symbols == DBX_DEBUG) ? 38 : 32) + +#define MD_REG_FIRST 64 +#define MD_REG_LAST 66 +#define MD_REG_NUM (MD_REG_LAST - MD_REG_FIRST + 1) + +#define ST_REG_FIRST 67 +#define ST_REG_LAST 74 +#define ST_REG_NUM (ST_REG_LAST - ST_REG_FIRST + 1) + +#define RAP_REG_NUM 75 + +#define AT_REGNUM (GP_REG_FIRST + 1) +#define HI_REGNUM (MD_REG_FIRST + 0) +#define LO_REGNUM (MD_REG_FIRST + 1) +#define HILO_REGNUM (MD_REG_FIRST + 2) + +/* FPSW_REGNUM is the single condition code used if mips_isa < 4. If + mips_isa >= 4, it should not be used, and an arbitrary ST_REG + should be used instead. */ +#define FPSW_REGNUM ST_REG_FIRST + +#define GP_REG_P(REGNO) ((unsigned) ((REGNO) - GP_REG_FIRST) < GP_REG_NUM) +#define M16_REG_P(REGNO) \ + (((REGNO) >= 2 && (REGNO) <= 7) || (REGNO) == 16 || (REGNO) == 17) +#define FP_REG_P(REGNO) ((unsigned) ((REGNO) - FP_REG_FIRST) < FP_REG_NUM) +#define MD_REG_P(REGNO) ((unsigned) ((REGNO) - MD_REG_FIRST) < MD_REG_NUM) +#define ST_REG_P(REGNO) ((unsigned) ((REGNO) - ST_REG_FIRST) < ST_REG_NUM) + +/* Return number of consecutive hard regs needed starting at reg REGNO + to hold something of mode MODE. + This is ordinarily the length in words of a value of mode MODE + but can be less for certain modes in special long registers. + + On the MIPS, all general registers are one word long. Except on + the R4000 with the FR bit set, the floating point uses register + pairs, with the second register not being allocable. */ + +#define HARD_REGNO_NREGS(REGNO, MODE) \ + (! FP_REG_P (REGNO) \ + ? ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) \ + : ((GET_MODE_SIZE (MODE) + UNITS_PER_FPREG - 1) / UNITS_PER_FPREG)) + +/* Value is 1 if hard register REGNO can hold a value of machine-mode + MODE. In 32 bit mode, require that DImode and DFmode be in even + registers. For DImode, this makes some of the insns easier to + write, since you don't have to worry about a DImode value in + registers 3 & 4, producing a result in 4 & 5. + + To make the code simpler HARD_REGNO_MODE_OK now just references an + array built in override_options. Because machmodes.h is not yet + included before this file is processed, the MODE bound can't be + expressed here. */ + +extern char mips_hard_regno_mode_ok[][FIRST_PSEUDO_REGISTER]; + +#define HARD_REGNO_MODE_OK(REGNO, MODE) \ + mips_hard_regno_mode_ok[ (int)(MODE) ][ (REGNO) ] + +/* Value is 1 if it is a good idea to tie two pseudo registers + when one has mode MODE1 and one has mode MODE2. + If HARD_REGNO_MODE_OK could produce different values for MODE1 and MODE2, + for any hard reg, then this must be 0 for correct output. */ +#define MODES_TIEABLE_P(MODE1, MODE2) \ + ((GET_MODE_CLASS (MODE1) == MODE_FLOAT || \ + GET_MODE_CLASS (MODE1) == MODE_COMPLEX_FLOAT) \ + == (GET_MODE_CLASS (MODE2) == MODE_FLOAT || \ + GET_MODE_CLASS (MODE2) == MODE_COMPLEX_FLOAT)) + +/* MIPS pc is not overloaded on a register. */ +/* #define PC_REGNUM xx */ + +/* Register to use for pushing function arguments. */ +#define STACK_POINTER_REGNUM (GP_REG_FIRST + 29) + +/* Offset from the stack pointer to the first available location. Use + the default value zero. */ +/* #define STACK_POINTER_OFFSET 0 */ + +/* Base register for access to local variables of the function. We + pretend that the frame pointer is $1, and then eliminate it to + HARD_FRAME_POINTER_REGNUM. We can get away with this because $1 is + a fixed register, and will not be used for anything else. */ +#define FRAME_POINTER_REGNUM (GP_REG_FIRST + 1) + +/* $30 is not available on the mips16, so we use $17 as the frame + pointer. */ +#define HARD_FRAME_POINTER_REGNUM \ + (TARGET_MIPS16 ? GP_REG_FIRST + 17 : GP_REG_FIRST + 30) + +/* 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. */ +#define FRAME_POINTER_REQUIRED (current_function_calls_alloca) + +/* Base register for access to arguments of the function. */ +#define ARG_POINTER_REGNUM GP_REG_FIRST + +/* Fake register that holds the address on the stack of the + current function's return address. */ +#define RETURN_ADDRESS_POINTER_REGNUM RAP_REG_NUM + +/* Register in which static-chain is passed to a function. */ +#define STATIC_CHAIN_REGNUM (GP_REG_FIRST + 2) + +/* If the structure value address is passed in a register, then + `STRUCT_VALUE_REGNUM' should be the number of that register. */ +/* #define STRUCT_VALUE_REGNUM (GP_REG_FIRST + 4) */ + +/* If the structure value address is not passed in a register, define + `STRUCT_VALUE' as an expression returning an RTX for the place + where the address is passed. If it returns 0, the address is + passed as an "invisible" first argument. */ +#define STRUCT_VALUE 0 + +/* Mips registers used in prologue/epilogue code when the stack frame + is larger than 32K bytes. These registers must come from the + scratch register set, and not used for passing and returning + arguments and any other information used in the calling sequence + (such as pic). Must start at 12, since t0/t3 are parameter passing + registers in the 64 bit ABI. */ + +#define MIPS_TEMP1_REGNUM (GP_REG_FIRST + 12) +#define MIPS_TEMP2_REGNUM (GP_REG_FIRST + 13) + +/* Define this macro if it is as good or better to call a constant + function address than to call an address kept in a register. */ +#define NO_FUNCTION_CSE 1 + +/* Define this macro if it is as good or better for a function to + call itself with an explicit address than to call an address + kept in a register. */ +#define NO_RECURSIVE_FUNCTION_CSE 1 + +/* The register number of the register used to address a table of + static data addresses in memory. In some cases this register is + defined by a processor's "application binary interface" (ABI). + When this macro is defined, RTL is generated for this register + once, as with the stack pointer and frame pointer registers. If + this macro is not defined, it is up to the machine-dependent + files to allocate such a register (if necessary). */ +#define PIC_OFFSET_TABLE_REGNUM (GP_REG_FIRST + 28) + +#define PIC_FUNCTION_ADDR_REGNUM (GP_REG_FIRST + 25) + +/* Initialize embedded_pic_fnaddr_rtx before RTL generation for + each function. We used to do this in FINALIZE_PIC, but FINALIZE_PIC + isn't always called for static inline functions. */ +#define INIT_EXPANDERS \ +do { \ + embedded_pic_fnaddr_rtx = NULL; \ + mips16_gp_pseudo_rtx = NULL; \ +} while (0) + +/* 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. */ + +enum reg_class +{ + NO_REGS, /* no registers in set */ + M16_NA_REGS, /* mips16 regs not used to pass args */ + M16_REGS, /* mips16 directly accessible registers */ + T_REG, /* mips16 T register ($24) */ + M16_T_REGS, /* mips16 registers plus T register */ + GR_REGS, /* integer registers */ + FP_REGS, /* floating point registers */ + HI_REG, /* hi register */ + LO_REG, /* lo register */ + HILO_REG, /* hilo register pair for 64 bit mode mult */ + MD_REGS, /* multiply/divide registers (hi/lo) */ + HI_AND_GR_REGS, /* union classes */ + LO_AND_GR_REGS, + HILO_AND_GR_REGS, + ST_REGS, /* status registers (fp status) */ + ALL_REGS, /* all registers */ + LIM_REG_CLASSES /* max value + 1 */ +}; + +#define N_REG_CLASSES (int) LIM_REG_CLASSES + +#define GENERAL_REGS GR_REGS + +/* An initializer containing the names of the register classes as C + string constants. These names are used in writing some of the + debugging dumps. */ + +#define REG_CLASS_NAMES \ +{ \ + "NO_REGS", \ + "M16_NA_REGS", \ + "M16_REGS", \ + "T_REG", \ + "M16_T_REGS", \ + "GR_REGS", \ + "FP_REGS", \ + "HI_REG", \ + "LO_REG", \ + "HILO_REG", \ + "MD_REGS", \ + "HI_AND_GR_REGS", \ + "LO_AND_GR_REGS", \ + "HILO_AND_GR_REGS", \ + "ST_REGS", \ + "ALL_REGS" \ +} + +/* An initializer containing the contents of the register classes, + as integers which are bit masks. The Nth integer specifies the + contents of class N. The way the integer MASK is interpreted is + that register R is in the class if `MASK & (1 << R)' is 1. + + When the machine has more than 32 registers, an integer does not + suffice. Then the integers are replaced by sub-initializers, + braced groupings containing several integers. Each + sub-initializer must be suitable as an initializer for the type + `HARD_REG_SET' which is defined in `hard-reg-set.h'. */ + +#define REG_CLASS_CONTENTS \ +{ \ + { 0x00000000, 0x00000000, 0x00000000 }, /* no registers */ \ + { 0x0003000c, 0x00000000, 0x00000000 }, /* mips16 nonarg regs */\ + { 0x000300fc, 0x00000000, 0x00000000 }, /* mips16 registers */ \ + { 0x01000000, 0x00000000, 0x00000000 }, /* mips16 T register */ \ + { 0x010300fc, 0x00000000, 0x00000000 }, /* mips16 and T regs */ \ + { 0xffffffff, 0x00000000, 0x00000000 }, /* integer registers */ \ + { 0x00000000, 0xffffffff, 0x00000000 }, /* floating registers*/ \ + { 0x00000000, 0x00000000, 0x00000001 }, /* hi register */ \ + { 0x00000000, 0x00000000, 0x00000002 }, /* lo register */ \ + { 0x00000000, 0x00000000, 0x00000004 }, /* hilo register */ \ + { 0x00000000, 0x00000000, 0x00000003 }, /* mul/div registers */ \ + { 0xffffffff, 0x00000000, 0x00000001 }, /* union classes */ \ + { 0xffffffff, 0x00000000, 0x00000002 }, \ + { 0xffffffff, 0x00000000, 0x00000004 }, \ + { 0x00000000, 0x00000000, 0x000007f8 }, /* status registers */ \ + { 0xffffffff, 0xffffffff, 0x000007ff } /* all registers */ \ +} + + +/* A C expression whose value is a register class containing hard + register REGNO. In general there is more that one such class; + choose a class which is "minimal", meaning that no smaller class + also contains the register. */ + +extern enum reg_class mips_regno_to_class[]; + +#define REGNO_REG_CLASS(REGNO) mips_regno_to_class[ (REGNO) ] + +/* A macro whose definition is the name of the class to which a + valid base register must belong. A base register is one used in + an address which is the register value plus a displacement. */ + +#define BASE_REG_CLASS (TARGET_MIPS16 ? M16_REGS : GR_REGS) + +/* A macro whose definition is the name of the class to which a + valid index register must belong. An index register is one used + in an address where its value is either multiplied by a scale + factor or added to another register (as well as added to a + displacement). */ + +#define INDEX_REG_CLASS NO_REGS + +/* When SMALL_REGISTER_CLASSES is nonzero, the compiler allows + registers explicitly used in the rtl to be used as spill registers + but prevents the compiler from extending the lifetime of these + registers. */ + +#define SMALL_REGISTER_CLASSES (TARGET_MIPS16) + +/* This macro is used later on in the file. */ +#define GR_REG_CLASS_P(CLASS) \ + ((CLASS) == GR_REGS || (CLASS) == M16_REGS || (CLASS) == T_REG \ + || (CLASS) == M16_T_REGS || (CLASS) == M16_NA_REGS) + +/* REG_ALLOC_ORDER is to order in which to allocate registers. This + is the default value (allocate the registers in numeric order). We + define it just so that we can override it for the mips16 target in + ORDER_REGS_FOR_LOCAL_ALLOC. */ + +#define REG_ALLOC_ORDER \ +{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, \ + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, \ + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, \ + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, \ + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75 \ +} + +/* ORDER_REGS_FOR_LOCAL_ALLOC is a macro which permits reg_alloc_order + to be rearranged based on a particular function. On the mips16, we + want to allocate $24 (T_REG) before other registers for + instructions for which it is possible. */ + +#define ORDER_REGS_FOR_LOCAL_ALLOC mips_order_regs_for_local_alloc () + +/* REGISTER AND CONSTANT CLASSES */ + +/* Get reg_class from a letter such as appears in the machine + description. + + DEFINED REGISTER CLASSES: + + 'd' General (aka integer) registers + Normally this is GR_REGS, but in mips16 mode this is M16_REGS + 'y' General registers (in both mips16 and non mips16 mode) + 'e' mips16 non argument registers (M16_NA_REGS) + 't' mips16 temporary register ($24) + 'f' Floating point registers + 'h' Hi register + 'l' Lo register + 'x' Multiply/divide registers + 'a' HILO_REG + 'z' FP Status register + 'b' All registers */ + +extern enum reg_class mips_char_to_class[]; + +#define REG_CLASS_FROM_LETTER(C) mips_char_to_class[ (C) ] + +/* The letters I, J, K, L, M, N, O, and P 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. */ + +/* For MIPS: + + `I' is used for the range of constants an arithmetic insn can + actually contain (16 bits signed integers). + + `J' is used for the range which is just zero (ie, $r0). + + `K' is used for the range of constants a logical insn can actually + contain (16 bit zero-extended integers). + + `L' is used for the range of constants that be loaded with lui + (ie, the bottom 16 bits are zero). + + `M' is used for the range of constants that take two words to load + (ie, not matched by `I', `K', and `L'). + + `N' is used for negative 16 bit constants other than -65536. + + `O' is a 15 bit signed integer. + + `P' is used for positive 16 bit constants. */ + +#define SMALL_INT(X) ((unsigned HOST_WIDE_INT) (INTVAL (X) + 0x8000) < 0x10000) +#define SMALL_INT_UNSIGNED(X) ((unsigned HOST_WIDE_INT) (INTVAL (X)) < 0x10000) + +#define CONST_OK_FOR_LETTER_P(VALUE, C) \ + ((C) == 'I' ? ((unsigned HOST_WIDE_INT) ((VALUE) + 0x8000) < 0x10000) \ + : (C) == 'J' ? ((VALUE) == 0) \ + : (C) == 'K' ? ((unsigned HOST_WIDE_INT) (VALUE) < 0x10000) \ + : (C) == 'L' ? (((VALUE) & 0x0000ffff) == 0 \ + && (((VALUE) & ~2147483647) == 0 \ + || ((VALUE) & ~2147483647) == ~2147483647)) \ + : (C) == 'M' ? ((((VALUE) & ~0x0000ffff) != 0) \ + && (((VALUE) & ~0x0000ffff) != ~0x0000ffff) \ + && (((VALUE) & 0x0000ffff) != 0 \ + || (((VALUE) & ~2147483647) != 0 \ + && ((VALUE) & ~2147483647) != ~2147483647))) \ + : (C) == 'N' ? ((unsigned HOST_WIDE_INT) ((VALUE) + 0xffff) < 0xffff) \ + : (C) == 'O' ? ((unsigned HOST_WIDE_INT) ((VALUE) + 0x4000) < 0x8000) \ + : (C) == 'P' ? ((VALUE) != 0 && (((VALUE) & ~0x0000ffff) == 0)) \ + : 0) + +/* Similar, but for floating constants, and defining letters G and H. + Here VALUE is the CONST_DOUBLE rtx itself. */ + +/* For Mips + + 'G' : Floating point 0 */ + +#define CONST_DOUBLE_OK_FOR_LETTER_P(VALUE, C) \ + ((C) == 'G' \ + && (VALUE) == CONST0_RTX (GET_MODE (VALUE))) + +/* Letters in the range `Q' through `U' may be defined in a + machine-dependent fashion to stand for arbitrary operand types. + The machine description macro `EXTRA_CONSTRAINT' is passed the + operand as its first argument and the constraint letter as its + second operand. + + `Q' is for mips16 GP relative constants + `R' is for memory references which take 1 word for the instruction. + `S' is for references to extern items which are PIC for OSF/rose. + `T' is for memory addresses that can be used to load two words. */ + +#define EXTRA_CONSTRAINT(OP,CODE) \ + (((CODE) == 'T') ? double_memory_operand (OP, GET_MODE (OP)) \ + : ((CODE) == 'Q') ? (GET_CODE (OP) == CONST \ + && mips16_gp_offset_p (OP)) \ + : (GET_CODE (OP) != MEM) ? FALSE \ + : ((CODE) == 'R') ? simple_memory_operand (OP, GET_MODE (OP)) \ + : ((CODE) == 'S') ? (HALF_PIC_P () && CONSTANT_P (OP) \ + && HALF_PIC_ADDRESS_P (OP)) \ + : FALSE) + +/* Given an rtx X being reloaded into a reg required to be + in class CLASS, return the class of reg to actually use. + In general this is just CLASS; but on some machines + in some cases it is preferable to use a more restrictive class. */ + +#define PREFERRED_RELOAD_CLASS(X,CLASS) \ + ((CLASS) != ALL_REGS \ + ? (! TARGET_MIPS16 \ + ? (CLASS) \ + : ((CLASS) != GR_REGS \ + ? (CLASS) \ + : M16_REGS)) \ + : ((GET_MODE_CLASS (GET_MODE (X)) == MODE_FLOAT \ + || GET_MODE_CLASS (GET_MODE (X)) == MODE_COMPLEX_FLOAT) \ + ? (TARGET_SOFT_FLOAT \ + ? (TARGET_MIPS16 ? M16_REGS : GR_REGS) \ + : FP_REGS) \ + : ((GET_MODE_CLASS (GET_MODE (X)) == MODE_INT \ + || GET_MODE (X) == VOIDmode) \ + ? (TARGET_MIPS16 ? M16_REGS : GR_REGS) \ + : (CLASS)))) + +/* Certain machines have the property that some registers cannot be + copied to some other registers without using memory. Define this + macro on those machines to be a C expression that is non-zero if + objects of mode MODE in registers of CLASS1 can only be copied to + registers of class CLASS2 by storing a register of CLASS1 into + memory and loading that memory location into a register of CLASS2. + + Do not define this macro if its value would always be zero. */ + +#define SECONDARY_MEMORY_NEEDED(CLASS1, CLASS2, MODE) \ + ((!TARGET_DEBUG_H_MODE \ + && GET_MODE_CLASS (MODE) == MODE_INT \ + && ((CLASS1 == FP_REGS && GR_REG_CLASS_P (CLASS2)) \ + || (GR_REG_CLASS_P (CLASS1) && CLASS2 == FP_REGS))) \ + || (TARGET_FLOAT64 && !TARGET_64BIT && (MODE) == DFmode \ + && ((GR_REG_CLASS_P (CLASS1) && CLASS2 == FP_REGS) \ + || (GR_REG_CLASS_P (CLASS2) && CLASS1 == FP_REGS)))) + +/* The HI and LO registers can only be reloaded via the general + registers. Condition code registers can only be loaded to the + general registers, and from the floating point registers. */ + +#define SECONDARY_INPUT_RELOAD_CLASS(CLASS, MODE, X) \ + mips_secondary_reload_class (CLASS, MODE, X, 1) +#define SECONDARY_OUTPUT_RELOAD_CLASS(CLASS, MODE, X) \ + mips_secondary_reload_class (CLASS, MODE, X, 0) + +/* Not declared above, with the other functions, because enum + reg_class is not declared yet. */ +extern enum reg_class mips_secondary_reload_class (); + +/* Return the maximum number of consecutive registers + needed to represent mode MODE in a register of class CLASS. */ + +#define CLASS_UNITS(mode, size) \ + ((GET_MODE_SIZE (mode) + (size) - 1) / (size)) + +#define CLASS_MAX_NREGS(CLASS, MODE) \ + ((CLASS) == FP_REGS \ + ? (TARGET_FLOAT64 \ + ? CLASS_UNITS (MODE, 8) \ + : 2 * CLASS_UNITS (MODE, 8)) \ + : CLASS_UNITS (MODE, UNITS_PER_WORD)) + +/* If defined, this is a C expression whose value should be + nonzero if the insn INSN has the effect of mysteriously + clobbering the contents of hard register number REGNO. By + "mysterious" we mean that the insn's RTL expression doesn't + describe such an effect. + + If this macro is not defined, it means that no insn clobbers + registers mysteriously. This is the usual situation; all else + being equal, it is best for the RTL expression to show all the + activity. */ + +/* #define INSN_CLOBBERS_REGNO_P(INSN, REGNO) */ + + +/* Stack layout; function entry, exit and calling. */ + +/* Define this if pushing a word on the stack + makes the stack pointer a smaller address. */ +#define STACK_GROWS_DOWNWARD + +/* Define this if the nominal address of the stack frame + is at the high-address end of the local variables; + that is, each additional local variable allocated + goes at a more negative offset in the frame. */ +/* #define FRAME_GROWS_DOWNWARD */ + +/* Offset within stack frame to start allocating local variables at. + If FRAME_GROWS_DOWNWARD, this is the offset to the END of the + first local allocated. Otherwise, it is the offset to the BEGINNING + of the first local allocated. */ +#define STARTING_FRAME_OFFSET \ + (current_function_outgoing_args_size \ + + (TARGET_ABICALLS ? MIPS_STACK_ALIGN (UNITS_PER_WORD) : 0)) + +/* Offset from the stack pointer register to an item dynamically + allocated on the stack, e.g., by `alloca'. + + The default value for this macro is `STACK_POINTER_OFFSET' plus the + length of the outgoing arguments. The default is correct for most + machines. See `function.c' for details. + + The MIPS ABI states that functions which dynamically allocate the + stack must not have 0 for STACK_DYNAMIC_OFFSET, since it looks like + we are trying to create a second frame pointer to the function, so + allocate some stack space to make it happy. + + However, the linker currently complains about linking any code that + dynamically allocates stack space, and there seems to be a bug in + STACK_DYNAMIC_OFFSET, so don't define this right now. */ + +#if 0 +#define STACK_DYNAMIC_OFFSET(FUNDECL) \ + ((current_function_outgoing_args_size == 0 && current_function_calls_alloca) \ + ? 4*UNITS_PER_WORD \ + : current_function_outgoing_args_size) +#endif + +/* The return address for the current frame is in r31 is this is a leaf + function. Otherwise, it is on the stack. It is at a variable offset + from sp/fp/ap, so we define a fake hard register rap which is a + poiner to the return address on the stack. This always gets eliminated + during reload to be either the frame pointer or the stack pointer plus + an offset. */ + +/* ??? This definition fails for leaf functions. There is currently no + general solution for this problem. */ + +/* ??? There appears to be no way to get the return address of any previous + frame except by disassembling instructions in the prologue/epilogue. + So currently we support only the current frame. */ + +#define RETURN_ADDR_RTX(count, frame) \ + ((count == 0) \ + ? gen_rtx (MEM, Pmode, gen_rtx (REG, Pmode, RETURN_ADDRESS_POINTER_REGNUM))\ + : (rtx) 0) + +/* Structure to be filled in by compute_frame_size with register + save masks, and offsets for the current function. */ + +struct mips_frame_info +{ + long total_size; /* # bytes that the entire frame takes up */ + long var_size; /* # bytes that variables take up */ + long args_size; /* # bytes that outgoing arguments take up */ + long extra_size; /* # bytes of extra gunk */ + int gp_reg_size; /* # bytes needed to store gp regs */ + int fp_reg_size; /* # bytes needed to store fp regs */ + long mask; /* mask of saved gp registers */ + long fmask; /* mask of saved fp registers */ + long gp_save_offset; /* offset from vfp to store gp registers */ + long fp_save_offset; /* offset from vfp to store fp registers */ + long gp_sp_offset; /* offset from new sp to store gp registers */ + long fp_sp_offset; /* offset from new sp to store fp registers */ + int initialized; /* != 0 if frame size already calculated */ + int num_gp; /* number of gp registers saved */ + int num_fp; /* number of fp registers saved */ + long insns_len; /* length of insns; mips16 only */ +}; + +extern struct mips_frame_info current_frame_info; + +/* If defined, this macro specifies a table of register pairs used to + eliminate unneeded registers that point into the stack frame. If + it is not defined, the only elimination attempted by the compiler + is to replace references to the frame pointer with references to + the stack pointer. + + The definition of this macro is a list of structure + initializations, each of which specifies an original and + replacement register. + + On some machines, the position of the argument pointer is not + known until the compilation is completed. In such a case, a + separate hard register must be used for the argument pointer. + This register can be eliminated by replacing it with either the + frame pointer or the argument pointer, depending on whether or not + the frame pointer has been eliminated. + + In this case, you might specify: + #define ELIMINABLE_REGS \ + {{ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ + {ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM}, \ + {FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}} + + Note that the elimination of the argument pointer with the stack + pointer is specified first since that is the preferred elimination. + + The eliminations to $17 are only used on the mips16. See the + definition of HARD_FRAME_POINTER_REGNUM. */ + +#define ELIMINABLE_REGS \ +{{ ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ + { ARG_POINTER_REGNUM, GP_REG_FIRST + 30}, \ + { ARG_POINTER_REGNUM, GP_REG_FIRST + 17}, \ + { RETURN_ADDRESS_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ + { RETURN_ADDRESS_POINTER_REGNUM, GP_REG_FIRST + 30}, \ + { RETURN_ADDRESS_POINTER_REGNUM, GP_REG_FIRST + 17}, \ + { RETURN_ADDRESS_POINTER_REGNUM, GP_REG_FIRST + 31}, \ + { FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ + { FRAME_POINTER_REGNUM, GP_REG_FIRST + 30}, \ + { FRAME_POINTER_REGNUM, GP_REG_FIRST + 17}} + +/* A C expression that returns non-zero if the compiler is allowed to + try to replace register number FROM-REG with register number + TO-REG. This macro need only be defined if `ELIMINABLE_REGS' is + defined, and will usually be the constant 1, since most of the + cases preventing register elimination are things that the compiler + already knows about. + + When not in mips16 and mips64, we can always eliminate to the + frame pointer. We can eliminate to the stack pointer unless + a frame pointer is needed. In mips16 mode, we need a frame + pointer for a large frame; otherwise, reload may be unable + to compute the address of a local variable, since there is + no way to add a large constant to the stack pointer + without using a temporary register. + + In mips16, for some instructions (eg lwu), we can't eliminate the + frame pointer for the stack pointer. These instructions are + only generated in TARGET_64BIT mode. + */ + +#define CAN_ELIMINATE(FROM, TO) \ + (((FROM) == RETURN_ADDRESS_POINTER_REGNUM && (! leaf_function_p () \ + || (TO == GP_REG_FIRST + 31 && leaf_function_p))) \ + || ((FROM) != RETURN_ADDRESS_POINTER_REGNUM \ + && ((TO) == HARD_FRAME_POINTER_REGNUM \ + || ((TO) == STACK_POINTER_REGNUM && ! frame_pointer_needed \ + && ! (TARGET_MIPS16 && TARGET_64BIT) \ + && (! TARGET_MIPS16 \ + || compute_frame_size (get_frame_size ()) < 32768))))) + +/* This macro is similar to `INITIAL_FRAME_POINTER_OFFSET'. It + specifies the initial difference between the specified pair of + registers. This macro must be defined if `ELIMINABLE_REGS' is + defined. */ + +#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \ +{ compute_frame_size (get_frame_size ()); \ + if (TARGET_MIPS16 && (FROM) == FRAME_POINTER_REGNUM \ + && (TO) == HARD_FRAME_POINTER_REGNUM) \ + (OFFSET) = - current_function_outgoing_args_size; \ + else if ((FROM) == FRAME_POINTER_REGNUM) \ + (OFFSET) = 0; \ + else if (TARGET_MIPS16 && (FROM) == ARG_POINTER_REGNUM \ + && (TO) == HARD_FRAME_POINTER_REGNUM) \ + (OFFSET) = (current_frame_info.total_size \ + - current_function_outgoing_args_size \ + - ((mips_abi != ABI_32 \ + && mips_abi != ABI_O64 \ + && mips_abi != ABI_EABI) \ + ? current_function_pretend_args_size \ + : 0)); \ + else if ((FROM) == ARG_POINTER_REGNUM) \ + (OFFSET) = (current_frame_info.total_size \ + - ((mips_abi != ABI_32 \ + && mips_abi != ABI_O64 \ + && mips_abi != ABI_EABI) \ + ? current_function_pretend_args_size \ + : 0)); \ + /* Some ABIs store 64 bits to the stack, but Pmode is 32 bits, \ + so we must add 4 bytes to the offset to get the right value. */ \ + else if ((FROM) == RETURN_ADDRESS_POINTER_REGNUM) \ + { \ + if (leaf_function_p ()) \ + (OFFSET) = 0; \ + else (OFFSET) = current_frame_info.gp_sp_offset \ + + ((UNITS_PER_WORD - (POINTER_SIZE / BITS_PER_UNIT)) \ + * (BYTES_BIG_ENDIAN != 0)); \ + } \ +} + +/* If we generate an insn to push BYTES bytes, + this says how many the stack pointer really advances by. + On the vax, sp@- in a byte insn really pushes a word. */ + +/* #define PUSH_ROUNDING(BYTES) 0 */ + +/* If defined, the maximum amount of space required for outgoing + arguments will be computed and placed into the variable + `current_function_outgoing_args_size'. No space will be pushed + onto the stack for each call; instead, the function prologue + should increase the stack frame size by this amount. + + It is not proper to define both `PUSH_ROUNDING' and + `ACCUMULATE_OUTGOING_ARGS'. */ +#define ACCUMULATE_OUTGOING_ARGS + +/* Offset from the argument pointer register to the first argument's + address. On some machines it may depend on the data type of the + function. + + If `ARGS_GROW_DOWNWARD', this is the offset to the location above + the first argument's address. + + On the MIPS, we must skip the first argument position if we are + returning a structure or a union, to account for its address being + passed in $4. However, at the current time, this produces a compiler + that can't bootstrap, so comment it out for now. */ + +#if 0 +#define FIRST_PARM_OFFSET(FNDECL) \ + (FNDECL != 0 \ + && TREE_TYPE (FNDECL) != 0 \ + && TREE_TYPE (TREE_TYPE (FNDECL)) != 0 \ + && (TREE_CODE (TREE_TYPE (TREE_TYPE (FNDECL))) == RECORD_TYPE \ + || TREE_CODE (TREE_TYPE (TREE_TYPE (FNDECL))) == UNION_TYPE) \ + ? UNITS_PER_WORD \ + : 0) +#else +#define FIRST_PARM_OFFSET(FNDECL) 0 +#endif + +/* When a parameter is passed in a register, stack space is still + allocated for it. For the MIPS, stack space must be allocated, cf + Asm Lang Prog Guide page 7-8. + + BEWARE that some space is also allocated for non existing arguments + in register. In case an argument list is of form GF used registers + are a0 (a2,a3), but we should push over a1... */ + +#define REG_PARM_STACK_SPACE(FNDECL) \ + ((MAX_ARGS_IN_REGISTERS*UNITS_PER_WORD) - FIRST_PARM_OFFSET (FNDECL)) + +/* Define this if it is the responsibility of the caller to + allocate the area reserved for arguments passed in registers. + If `ACCUMULATE_OUTGOING_ARGS' is also defined, the only effect + of this macro is to determine whether the space is included in + `current_function_outgoing_args_size'. */ +#define OUTGOING_REG_PARM_STACK_SPACE + +/* Align stack frames on 64 bits (Double Word ). */ +#ifndef STACK_BOUNDARY +#define STACK_BOUNDARY 64 +#endif + +/* Make sure 4 words are always allocated on the stack. */ + +#ifndef STACK_ARGS_ADJUST +#define STACK_ARGS_ADJUST(SIZE) \ +{ \ + if (SIZE.constant < 4 * UNITS_PER_WORD) \ + SIZE.constant = 4 * UNITS_PER_WORD; \ +} +#endif + + +/* A C expression that should indicate the number of bytes of its + own arguments that a function pops on returning, or 0 + if the function pops no arguments and the caller must therefore + pop them all after the function returns. + + FUNDECL is the declaration node of the function (as a tree). + + FUNTYPE is a C variable whose value is a tree node that + describes the function in question. Normally it is a node of + type `FUNCTION_TYPE' that describes the data type of the function. + From this it is possible to obtain the data types of the value + and arguments (if known). + + When a call to a library function is being considered, FUNTYPE + will contain an identifier node for the library function. Thus, + if you need to distinguish among various library functions, you + can do so by their names. Note that "library function" in this + context means a function used to perform arithmetic, whose name + is known specially in the compiler and was not mentioned in the + C code being compiled. + + STACK-SIZE is the number of bytes of arguments passed on the + stack. If a variable number of bytes is passed, it is zero, and + argument popping will always be the responsibility of the + calling function. */ + +#define RETURN_POPS_ARGS(FUNDECL,FUNTYPE,SIZE) 0 + + +/* Symbolic macros for the registers used to return integer and floating + point values. */ + +#define GP_RETURN (GP_REG_FIRST + 2) +#define FP_RETURN ((TARGET_SOFT_FLOAT) ? GP_RETURN : (FP_REG_FIRST + 0)) + +/* Symbolic macros for the first/last argument registers. */ + +#define GP_ARG_FIRST (GP_REG_FIRST + 4) +#define GP_ARG_LAST (GP_REG_FIRST + 7) +#define FP_ARG_FIRST (FP_REG_FIRST + 12) +#define FP_ARG_LAST (FP_REG_FIRST + 15) + +#define MAX_ARGS_IN_REGISTERS 4 + +/* Define how to find the value returned by a library function + assuming the value has mode MODE. Because we define + PROMOTE_FUNCTION_RETURN, we must promote the mode just as + PROMOTE_MODE does. */ + +#define LIBCALL_VALUE(MODE) \ + gen_rtx (REG, \ + ((GET_MODE_CLASS (MODE) != MODE_INT \ + || GET_MODE_SIZE (MODE) >= 4) \ + ? (MODE) \ + : SImode), \ + ((GET_MODE_CLASS (MODE) == MODE_FLOAT \ + && (! TARGET_SINGLE_FLOAT \ + || GET_MODE_SIZE (MODE) <= 4)) \ + ? FP_RETURN \ + : GP_RETURN)) + +/* 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. */ + +#define FUNCTION_VALUE(VALTYPE, FUNC) LIBCALL_VALUE (TYPE_MODE (VALTYPE)) + + +/* 1 if N is a possible register number for a function value. + On the MIPS, R2 R3 and F0 F2 are the only register thus used. + Currently, R2 and F0 are only implemented here (C has no complex type) */ + +#define FUNCTION_VALUE_REGNO_P(N) ((N) == GP_RETURN || (N) == FP_RETURN) + +/* 1 if N is a possible register number for function argument passing. + We have no FP argument registers when soft-float. When FP registers + are 32 bits, we can't directly reference the odd numbered ones. */ + +#define FUNCTION_ARG_REGNO_P(N) \ + (((N) >= GP_ARG_FIRST && (N) <= GP_ARG_LAST) \ + || ((! TARGET_SOFT_FLOAT \ + && ((N) >= FP_ARG_FIRST && (N) <= FP_ARG_LAST) \ + && (TARGET_FLOAT64 || (0 == (N) % 2))) \ + && ! fixed_regs[N])) + +/* A C expression which can inhibit the returning of certain function + values in registers, based on the type of value. A nonzero value says + to return the function value in memory, just as large structures are + always returned. Here TYPE will be a C expression of type + `tree', representing the data type of the value. + + Note that values of mode `BLKmode' must be explicitly + handled by this macro. Also, the option `-fpcc-struct-return' + takes effect regardless of this macro. On most systems, it is + possible to leave the macro undefined; this causes a default + definition to be used, whose value is the constant 1 for BLKmode + values, and 0 otherwise. + + GCC normally converts 1 byte structures into chars, 2 byte + structs into shorts, and 4 byte structs into ints, and returns + them this way. Defining the following macro overrides this, + to give us MIPS cc compatibility. */ + +#define RETURN_IN_MEMORY(TYPE) \ + (TYPE_MODE (TYPE) == BLKmode) + +/* A code distinguishing the floating point format of the target + machine. There are three defined values: IEEE_FLOAT_FORMAT, + VAX_FLOAT_FORMAT, and UNKNOWN_FLOAT_FORMAT. */ + +#define TARGET_FLOAT_FORMAT IEEE_FLOAT_FORMAT + + +/* Define a data type for recording info about an argument list + during the scan of that argument list. This data type should + hold all necessary information about the function itself + and about the args processed so far, enough to enable macros + such as FUNCTION_ARG to determine where the next arg should go. + + On the mips16, we need to keep track of which floating point + arguments were passed in general registers, but would have been + passed in the FP regs if this were a 32 bit function, so that we + can move them to the FP regs if we wind up calling a 32 bit + function. We record this information in fp_code, encoded in base + four. A zero digit means no floating point argument, a one digit + means an SFmode argument, and a two digit means a DFmode argument, + and a three digit is not used. The low order digit is the first + argument. Thus 6 == 1 * 4 + 2 means a DFmode argument followed by + an SFmode argument. ??? A more sophisticated approach will be + needed if MIPS_ABI != ABI_32. */ + +typedef struct mips_args { + int gp_reg_found; /* whether a gp register was found yet */ + int arg_number; /* argument number */ + int arg_words; /* # total words the arguments take */ + int fp_arg_words; /* # words for FP args (MIPS_EABI only) */ + int last_arg_fp; /* nonzero if last arg was FP (EABI only) */ + int fp_code; /* Mode of FP arguments (mips16) */ + int num_adjusts; /* number of adjustments made */ + /* Adjustments made to args pass in regs. */ + /* ??? The size is doubled to work around a + bug in the code that sets the adjustments + in function_arg. */ + struct rtx_def *adjust[MAX_ARGS_IN_REGISTERS*2]; +} CUMULATIVE_ARGS; + +/* Initialize a variable CUM of type CUMULATIVE_ARGS + for a call to a function whose data type is FNTYPE. + For a library call, FNTYPE is 0. + +*/ + +#define INIT_CUMULATIVE_ARGS(CUM,FNTYPE,LIBNAME,INDIRECT) \ + init_cumulative_args (&CUM, FNTYPE, LIBNAME) \ + +/* Update the data in CUM to advance over an argument + of mode MODE and data type TYPE. + (TYPE is null for libcalls where that information may not be available.) */ + +#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \ + 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). */ + +#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \ + function_arg( &CUM, MODE, TYPE, NAMED) + +/* For an arg passed partly in registers and partly in memory, + this is the number of registers used. + For args passed entirely in registers or entirely in memory, zero. */ + +#define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) \ + function_arg_partial_nregs (&CUM, MODE, TYPE, NAMED) + +/* If defined, a C expression that gives the alignment boundary, in + bits, of an argument with the specified mode and type. If it is + not defined, `PARM_BOUNDARY' is used for all arguments. */ + +#define FUNCTION_ARG_BOUNDARY(MODE, TYPE) \ + (((TYPE) != 0) \ + ? ((TYPE_ALIGN(TYPE) <= (unsigned)PARM_BOUNDARY) \ + ? PARM_BOUNDARY \ + : TYPE_ALIGN(TYPE)) \ + : ((GET_MODE_ALIGNMENT(MODE) <= PARM_BOUNDARY) \ + ? PARM_BOUNDARY \ + : GET_MODE_ALIGNMENT(MODE))) + + +/* 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. */ + +#define FUNCTION_PROLOGUE(FILE, SIZE) function_prologue(FILE, 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. */ + +#define FUNCTION_EPILOGUE(FILE, SIZE) function_epilogue(FILE, SIZE) + +/* Tell prologue and epilogue if register REGNO should be saved / restored. */ + +#define MUST_SAVE_REGISTER(regno) \ + ((regs_ever_live[regno] && !call_used_regs[regno]) \ + || (regno == HARD_FRAME_POINTER_REGNUM && frame_pointer_needed) \ + || (regno == (GP_REG_FIRST + 31) && regs_ever_live[GP_REG_FIRST + 31])) + +/* ALIGN FRAMES on double word boundaries */ +#ifndef MIPS_STACK_ALIGN +#define MIPS_STACK_ALIGN(LOC) (((LOC) + 7) & ~7) +#endif + + +/* Output assembler code to FILE to increment profiler label # LABELNO + for profiling a function entry. */ + +#define FUNCTION_PROFILER(FILE, LABELNO) \ +{ \ + if (TARGET_MIPS16) \ + sorry ("mips16 function profiling"); \ + fprintf (FILE, "\t.set\tnoreorder\n"); \ + fprintf (FILE, "\t.set\tnoat\n"); \ + fprintf (FILE, "\tmove\t%s,%s\t\t# save current return address\n", \ + reg_names[GP_REG_FIRST + 1], reg_names[GP_REG_FIRST + 31]); \ + fprintf (FILE, "\tjal\t_mcount\n"); \ + fprintf (FILE, \ + "\t%s\t%s,%s,%d\t\t# _mcount pops 2 words from stack\n", \ + TARGET_64BIT ? "dsubu" : "subu", \ + reg_names[STACK_POINTER_REGNUM], \ + reg_names[STACK_POINTER_REGNUM], \ + Pmode == DImode ? 16 : 8); \ + fprintf (FILE, "\t.set\treorder\n"); \ + fprintf (FILE, "\t.set\tat\n"); \ +} + +/* Define this macro if the code for function profiling should come + before the function prologue. Normally, the profiling code comes + after. */ + +/* #define PROFILE_BEFORE_PROLOGUE */ + +/* 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. */ + +#define EXIT_IGNORE_STACK 1 + + +/* A C statement to output, on the stream FILE, assembler code for a + block of data that contains the constant parts of a trampoline. + This code should not include a label--the label is taken care of + automatically. */ + +#define TRAMPOLINE_TEMPLATE(STREAM) \ +{ \ + fprintf (STREAM, "\t.word\t0x03e00821\t\t# move $1,$31\n"); \ + fprintf (STREAM, "\t.word\t0x04110001\t\t# bgezal $0,.+8\n"); \ + fprintf (STREAM, "\t.word\t0x00000000\t\t# nop\n"); \ + if (Pmode == DImode) \ + { \ + fprintf (STREAM, "\t.word\t0xdfe30014\t\t# ld $3,20($31)\n"); \ + fprintf (STREAM, "\t.word\t0xdfe2001c\t\t# ld $2,28($31)\n"); \ + } \ + else \ + { \ + fprintf (STREAM, "\t.word\t0x8fe30014\t\t# lw $3,20($31)\n"); \ + fprintf (STREAM, "\t.word\t0x8fe20018\t\t# lw $2,24($31)\n"); \ + } \ + fprintf (STREAM, "\t.word\t0x0060c821\t\t# move $25,$3 (abicalls)\n"); \ + fprintf (STREAM, "\t.word\t0x00600008\t\t# jr $3\n"); \ + fprintf (STREAM, "\t.word\t0x0020f821\t\t# move $31,$1\n"); \ + if (Pmode == DImode) \ + { \ + fprintf (STREAM, "\t.dword\t0x00000000\t\t# <function address>\n"); \ + fprintf (STREAM, "\t.dword\t0x00000000\t\t# <static chain value>\n"); \ + } \ + else \ + { \ + fprintf (STREAM, "\t.word\t0x00000000\t\t# <function address>\n"); \ + fprintf (STREAM, "\t.word\t0x00000000\t\t# <static chain value>\n"); \ + } \ +} + +/* A C expression for the size in bytes of the trampoline, as an + integer. */ + +#define TRAMPOLINE_SIZE (32 + (Pmode == DImode ? 16 : 8)) + +/* Alignment required for trampolines, in bits. */ + +#define TRAMPOLINE_ALIGNMENT (Pmode == DImode ? 64 : 32) + +/* INITIALIZE_TRAMPOLINE calls this library function to flush + program and data caches. */ + +#ifndef CACHE_FLUSH_FUNC +#define CACHE_FLUSH_FUNC "_flush_cache" +#endif + +/* A C statement to initialize the variable parts of a trampoline. + ADDR is an RTX for the address of the trampoline; FNADDR is an + RTX for the address of the nested function; STATIC_CHAIN is an + RTX for the static chain value that should be passed to the + function when it is called. */ + +#define INITIALIZE_TRAMPOLINE(ADDR, FUNC, CHAIN) \ +{ \ + rtx addr = ADDR; \ + if (Pmode == DImode) \ + { \ + emit_move_insn (gen_rtx (MEM, DImode, plus_constant (addr, 32)), FUNC); \ + emit_move_insn (gen_rtx (MEM, DImode, plus_constant (addr, 40)), CHAIN);\ + } \ + else \ + { \ + emit_move_insn (gen_rtx (MEM, SImode, plus_constant (addr, 32)), FUNC); \ + emit_move_insn (gen_rtx (MEM, SImode, plus_constant (addr, 36)), CHAIN);\ + } \ + \ + /* Flush both caches. We need to flush the data cache in case \ + the system has a write-back cache. */ \ + /* ??? Should check the return value for errors. */ \ + emit_library_call (gen_rtx (SYMBOL_REF, Pmode, CACHE_FLUSH_FUNC), \ + 0, VOIDmode, 3, addr, Pmode, \ + GEN_INT (TRAMPOLINE_SIZE), TYPE_MODE (integer_type_node),\ + GEN_INT (3), TYPE_MODE (integer_type_node)); \ +} + +/* 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 */ + +/* 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. + These definitions are NOT overridden anywhere. */ + +#define BASE_REG_P(regno, mode) \ + (TARGET_MIPS16 \ + ? (M16_REG_P (regno) \ + || (regno) == FRAME_POINTER_REGNUM \ + || (regno) == ARG_POINTER_REGNUM \ + || ((regno) == STACK_POINTER_REGNUM \ + && (GET_MODE_SIZE (mode) == 4 \ + || GET_MODE_SIZE (mode) == 8))) \ + : GP_REG_P (regno)) + +#define GP_REG_OR_PSEUDO_STRICT_P(regno, mode) \ + BASE_REG_P((regno < FIRST_PSEUDO_REGISTER) ? regno : reg_renumber[regno], \ + (mode)) + +#define GP_REG_OR_PSEUDO_NONSTRICT_P(regno, mode) \ + (((regno) >= FIRST_PSEUDO_REGISTER) || (BASE_REG_P ((regno), (mode)))) + +#define REGNO_OK_FOR_INDEX_P(regno) 0 +#define REGNO_MODE_OK_FOR_BASE_P(regno, mode) \ + GP_REG_OR_PSEUDO_STRICT_P ((regno), (mode)) + +/* 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 all. + 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. + Some source files that are used after register allocation + need to be strict. */ + +#ifndef REG_OK_STRICT + +#define REG_OK_STRICT_P 0 +#define REG_OK_FOR_INDEX_P(X) 0 +#define REG_MODE_OK_FOR_BASE_P(X, MODE) \ + GP_REG_OR_PSEUDO_NONSTRICT_P (REGNO (X), (MODE)) + +#else + +#define REG_OK_STRICT_P 1 +#define REG_OK_FOR_INDEX_P(X) 0 +#define REG_MODE_OK_FOR_BASE_P(X, MODE) \ + REGNO_MODE_OK_FOR_BASE_P (REGNO (X), (MODE)) + +#endif + + +/* Maximum number of registers that can appear in a valid memory address. */ + +#define MAX_REGS_PER_ADDRESS 1 + +/* A C compound statement with a conditional `goto LABEL;' executed + if X (an RTX) is a legitimate memory address on the target + machine for a memory operand of mode MODE. + + It usually pays to define several simpler macros to serve as + subroutines for this one. Otherwise it may be too complicated + to understand. + + This macro must exist in two variants: a strict variant and a + non-strict one. The strict variant is used in the reload pass. + It must be defined so that any pseudo-register that has not been + allocated a hard register is considered a memory reference. In + contexts where some kind of register is required, a + pseudo-register with no hard register must be rejected. + + The non-strict variant is used in other passes. It must be + defined to accept all pseudo-registers in every context where + some kind of register is required. + + Compiler source files that want to use the strict variant of + this macro define the macro `REG_OK_STRICT'. You should use an + `#ifdef REG_OK_STRICT' conditional to define the strict variant + in that case and the non-strict variant otherwise. + + Typically among the subroutines used to define + `GO_IF_LEGITIMATE_ADDRESS' are subroutines to check for + acceptable registers for various purposes (one for base + registers, one for index registers, and so on). Then only these + subroutine macros need have two variants; the higher levels of + macros may be the same whether strict or not. + + Normally, constant addresses which are the sum of a `symbol_ref' + and an integer are stored inside a `const' RTX to mark them as + constant. Therefore, there is no need to recognize such sums + specifically as legitimate addresses. Normally you would simply + recognize any `const' as legitimate. + + Usually `PRINT_OPERAND_ADDRESS' is not prepared to handle + constant sums that are not marked with `const'. It assumes + that a naked `plus' indicates indexing. If so, then you *must* + reject such naked constant sums as illegitimate addresses, so + that none of them will be given to `PRINT_OPERAND_ADDRESS'. + + On some machines, whether a symbolic address is legitimate + depends on the section that the address refers to. On these + machines, define the macro `ENCODE_SECTION_INFO' to store the + information into the `symbol_ref', and then check for it here. + When you see a `const', you will have to look inside it to find + the `symbol_ref' in order to determine the section. */ + +#if 1 +#define GO_PRINTF(x) trace(x) +#define GO_PRINTF2(x,y) trace(x,y) +#define GO_DEBUG_RTX(x) debug_rtx(x) + +#else +#define GO_PRINTF(x) +#define GO_PRINTF2(x,y) +#define GO_DEBUG_RTX(x) +#endif + +#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \ +{ \ + register rtx xinsn = (X); \ + \ + if (TARGET_DEBUG_B_MODE) \ + { \ + GO_PRINTF2 ("\n========== GO_IF_LEGITIMATE_ADDRESS, %sstrict\n", \ + (REG_OK_STRICT_P) ? "" : "not "); \ + GO_DEBUG_RTX (xinsn); \ + } \ + \ + /* The mips16 can only use the stack pointer as a base register when \ + loading SImode or DImode values. */ \ + if (GET_CODE (xinsn) == REG && REG_MODE_OK_FOR_BASE_P (xinsn, MODE)) \ + goto ADDR; \ + \ + if (CONSTANT_ADDRESS_P (xinsn) \ + && ! (mips_split_addresses && mips_check_split (xinsn, MODE)) \ + && (! TARGET_MIPS16 || mips16_constant (xinsn, MODE, 1, 0))) \ + goto ADDR; \ + \ + if (GET_CODE (xinsn) == LO_SUM && mips_split_addresses) \ + { \ + register rtx xlow0 = XEXP (xinsn, 0); \ + register rtx xlow1 = XEXP (xinsn, 1); \ + \ + if (GET_CODE (xlow0) == REG \ + && REG_MODE_OK_FOR_BASE_P (xlow0, MODE) \ + && mips_check_split (xlow1, MODE)) \ + goto ADDR; \ + } \ + \ + if (GET_CODE (xinsn) == PLUS) \ + { \ + register rtx xplus0 = XEXP (xinsn, 0); \ + register rtx xplus1 = XEXP (xinsn, 1); \ + register enum rtx_code code0 = GET_CODE (xplus0); \ + register enum rtx_code code1 = GET_CODE (xplus1); \ + \ + /* The mips16 can only use the stack pointer as a base register \ + when loading SImode or DImode values. */ \ + if (code0 == REG && REG_MODE_OK_FOR_BASE_P (xplus0, MODE)) \ + { \ + if (code1 == CONST_INT \ + && INTVAL (xplus1) >= -32768 \ + && INTVAL (xplus1) + GET_MODE_SIZE (MODE) - 1 <= 32767) \ + goto ADDR; \ + \ + /* On the mips16, we represent GP relative offsets in RTL. \ + These are 16 bit signed values, and can serve as register \ + offsets. */ \ + if (TARGET_MIPS16 \ + && mips16_gp_offset_p (xplus1)) \ + goto ADDR; \ + \ + /* For some code sequences, you actually get better code by \ + pretending that the MIPS supports an address mode of a \ + constant address + a register, even though the real \ + machine doesn't support it. This is because the \ + assembler can use $r1 to load just the high 16 bits, add \ + in the register, and fold the low 16 bits into the memory \ + reference, whereas the compiler generates a 4 instruction \ + sequence. On the other hand, CSE is not as effective. \ + It would be a win to generate the lui directly, but the \ + MIPS assembler does not have syntax to generate the \ + appropriate relocation. */ \ + \ + /* Also accept CONST_INT addresses here, so no else. */ \ + /* Reject combining an embedded PIC text segment reference \ + with a register. That requires an additional \ + instruction. */ \ + /* ??? Reject combining an address with a register for the MIPS \ + 64 bit ABI, because the SGI assembler can not handle this. */ \ + if (!TARGET_DEBUG_A_MODE \ + && (mips_abi == ABI_32 \ + || mips_abi == ABI_O64 \ + || mips_abi == ABI_EABI) \ + && CONSTANT_ADDRESS_P (xplus1) \ + && ! mips_split_addresses \ + && (!TARGET_EMBEDDED_PIC \ + || code1 != CONST \ + || GET_CODE (XEXP (xplus1, 0)) != MINUS) \ + && !TARGET_MIPS16) \ + goto ADDR; \ + } \ + } \ + \ + if (TARGET_DEBUG_B_MODE) \ + GO_PRINTF ("Not a legitimate address\n"); \ +} + + +/* A C expression that is 1 if the RTX X is a constant which is a + valid address. This is defined to be the same as `CONSTANT_P (X)', + but rejecting CONST_DOUBLE. */ +/* When pic, we must reject addresses of the form symbol+large int. + This is because an instruction `sw $4,s+70000' needs to be converted + by the assembler to `lw $at,s($gp);sw $4,70000($at)'. Normally the + assembler would use $at as a temp to load in the large offset. In this + case $at is already in use. We convert such problem addresses to + `la $5,s;sw $4,70000($5)' via LEGITIMIZE_ADDRESS. */ +/* ??? SGI Irix 6 assembler fails for CONST address, so reject them. */ +#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)) \ + && (mips_abi == ABI_32 \ + || mips_abi == ABI_O64 \ + || mips_abi == ABI_EABI))) \ + && (!HALF_PIC_P () || !HALF_PIC_ADDRESS_P (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. + It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE. + + At present, GAS doesn't understand li.[sd], so don't allow it + to be generated at present. Also, the MIPS assembler does not + grok li.d Infinity. */ + +/* ??? SGI Irix 6 assembler fails for CONST address, so reject them. */ +#define LEGITIMATE_CONSTANT_P(X) \ + ((GET_CODE (X) != CONST_DOUBLE \ + || mips_const_double_ok (X, GET_MODE (X))) \ + && ! (GET_CODE (X) == CONST \ + && mips_abi != ABI_32 \ + && mips_abi != ABI_O64 \ + && mips_abi != ABI_EABI) \ + && (! TARGET_MIPS16 || mips16_constant (X, GET_MODE (X), 0, 0))) + +/* A C compound statement that attempts to replace X with a valid + memory address for an operand of mode MODE. WIN will be a C + statement label elsewhere in the code; the macro definition may + use + + GO_IF_LEGITIMATE_ADDRESS (MODE, X, WIN); + + to avoid further processing if the address has become legitimate. + + X will always be the result of a call to `break_out_memory_refs', + and OLDX will be the operand that was given to that function to + produce X. + + The code generated by this macro should not alter the + substructure of X. If it transforms X into a more legitimate + form, it should assign X (which will always be a C variable) a + new value. + + It is not necessary for this macro to come up with a legitimate + address. The compiler has standard ways of doing so in all + cases. In fact, it is safe for this macro to do nothing. But + often a machine-dependent strategy can generate better code. + + For the MIPS, transform: + + memory(X + <large int>) + + into: + + Y = <large int> & ~0x7fff; + Z = X + Y + memory (Z + (<large int> & 0x7fff)); + + This is for CSE to find several similar references, and only use one Z. + + When PIC, convert addresses of the form memory (symbol+large int) to + memory (reg+large int). */ + + +#define LEGITIMIZE_ADDRESS(X,OLDX,MODE,WIN) \ +{ \ + register rtx xinsn = (X); \ + \ + if (TARGET_DEBUG_B_MODE) \ + { \ + GO_PRINTF ("\n========== LEGITIMIZE_ADDRESS\n"); \ + GO_DEBUG_RTX (xinsn); \ + } \ + \ + if (mips_split_addresses && mips_check_split (X, MODE)) \ + { \ + /* ??? Is this ever executed? */ \ + X = gen_rtx (LO_SUM, Pmode, \ + copy_to_mode_reg (Pmode, gen_rtx (HIGH, Pmode, X)), X); \ + goto WIN; \ + } \ + \ + if (GET_CODE (xinsn) == CONST \ + && ((flag_pic && pic_address_needs_scratch (xinsn)) \ + /* ??? SGI's Irix 6 assembler can't handle CONST. */ \ + || (mips_abi != ABI_32 \ + && mips_abi != ABI_O64 \ + && mips_abi != ABI_EABI))) \ + { \ + rtx ptr_reg = gen_reg_rtx (Pmode); \ + rtx constant = XEXP (XEXP (xinsn, 0), 1); \ + \ + emit_move_insn (ptr_reg, XEXP (XEXP (xinsn, 0), 0)); \ + \ + X = gen_rtx (PLUS, Pmode, ptr_reg, constant); \ + if (SMALL_INT (constant)) \ + goto WIN; \ + /* Otherwise we fall through so the code below will fix the \ + constant. */ \ + xinsn = X; \ + } \ + \ + if (GET_CODE (xinsn) == PLUS) \ + { \ + register rtx xplus0 = XEXP (xinsn, 0); \ + register rtx xplus1 = XEXP (xinsn, 1); \ + register enum rtx_code code0 = GET_CODE (xplus0); \ + register enum rtx_code code1 = GET_CODE (xplus1); \ + \ + if (code0 != REG && code1 == REG) \ + { \ + xplus0 = XEXP (xinsn, 1); \ + xplus1 = XEXP (xinsn, 0); \ + code0 = GET_CODE (xplus0); \ + code1 = GET_CODE (xplus1); \ + } \ + \ + if (code0 == REG && REG_MODE_OK_FOR_BASE_P (xplus0, MODE) \ + && code1 == CONST_INT && !SMALL_INT (xplus1)) \ + { \ + rtx int_reg = gen_reg_rtx (Pmode); \ + rtx ptr_reg = gen_reg_rtx (Pmode); \ + \ + emit_move_insn (int_reg, \ + GEN_INT (INTVAL (xplus1) & ~ 0x7fff)); \ + \ + emit_insn (gen_rtx (SET, VOIDmode, \ + ptr_reg, \ + gen_rtx (PLUS, Pmode, xplus0, int_reg))); \ + \ + X = gen_rtx (PLUS, Pmode, ptr_reg, \ + GEN_INT (INTVAL (xplus1) & 0x7fff)); \ + goto WIN; \ + } \ + } \ + \ + if (TARGET_DEBUG_B_MODE) \ + GO_PRINTF ("LEGITIMIZE_ADDRESS could not fix.\n"); \ +} + + +/* A C statement or compound statement with a conditional `goto + LABEL;' executed if memory address X (an RTX) can have different + meanings depending on the machine mode of the memory reference it + is used for. + + Autoincrement and autodecrement addresses typically have + mode-dependent effects because the amount of the increment or + decrement is the size of the operand being addressed. Some + machines have other mode-dependent addresses. Many RISC machines + have no mode-dependent addresses. + + You may assume that ADDR is a valid address for the machine. */ + +#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR,LABEL) {} + + +/* Define this macro if references to a symbol must be treated + differently depending on something about the variable or + function named by the symbol (such as what section it is in). + + The macro definition, if any, is executed immediately after the + rtl for DECL has been created and stored in `DECL_RTL (DECL)'. + The value of the rtl will be a `mem' whose address is a + `symbol_ref'. + + The usual thing for this macro to do is to a flag in the + `symbol_ref' (such as `SYMBOL_REF_FLAG') or to store a modified + name string in the `symbol_ref' (if one bit is not enough + information). + + The best way to modify the name string is by adding text to the + beginning, with suitable punctuation to prevent any ambiguity. + Allocate the new name in `saveable_obstack'. You will have to + modify `ASM_OUTPUT_LABELREF' to remove and decode the added text + and output the name accordingly. + + You can also check the information stored in the `symbol_ref' in + the definition of `GO_IF_LEGITIMATE_ADDRESS' or + `PRINT_OPERAND_ADDRESS'. + + When optimizing for the $gp pointer, SYMBOL_REF_FLAG is set for all + small objects. + + When generating embedded PIC code, SYMBOL_REF_FLAG is set for + symbols which are not in the .text section. + + When generating mips16 code, SYMBOL_REF_FLAG is set for string + constants which are put in the .text section. We also record the + total length of all such strings; this total is used to decide + whether we need to split the constant table, and need not be + precisely correct. + + When not mips16 code nor embedded PIC, if a symbol is in a + gp addresable section, SYMBOL_REF_FLAG is set prevent gcc from + splitting the reference so that gas can generate a gp relative + reference. + + When TARGET_EMBEDDED_DATA is set, we assume that all const + variables will be stored in ROM, which is too far from %gp to use + %gprel addressing. Note that (1) we include "extern const" + variables in this, which mips_select_section doesn't, and (2) we + can't always tell if they're really const (they might be const C++ + objects with non-const constructors), so we err on the side of + caution and won't use %gprel anyway (otherwise we'd have to defer + this decision to the linker/loader). The handling of extern consts + is why the DECL_INITIAL macros differ from mips_select_section. + + If you are changing this macro, you should look at + mips_select_section and see if it needs a similar change. */ + +#ifndef UNIQUE_SECTION_P +#define UNIQUE_SECTION_P(DECL) (0) +#endif + +#define ENCODE_SECTION_INFO(DECL) \ +do \ + { \ + if (TARGET_MIPS16) \ + { \ + if (TREE_CODE (DECL) == STRING_CST \ + && ! flag_writable_strings \ + /* If this string is from a function, and the function will \ + go in a gnu linkonce section, then we can't directly \ + access the string. This gets an assembler error \ + "unsupported PC relative reference to different section".\ + If we modify SELECT_SECTION to put it in function_section\ + instead of text_section, it still fails because \ + DECL_SECTION_NAME isn't set until assemble_start_function.\ + If we fix that, it still fails because strings are shared\ + among multiple functions, and we have cross section \ + references again. We force it to work by putting string \ + addresses in the constant pool and indirecting. */ \ + && (! current_function_decl \ + || ! UNIQUE_SECTION_P (current_function_decl))) \ + { \ + SYMBOL_REF_FLAG (XEXP (TREE_CST_RTL (DECL), 0)) = 1; \ + mips_string_length += TREE_STRING_LENGTH (DECL); \ + } \ + } \ + \ + if (TARGET_EMBEDDED_DATA \ + && (TREE_CODE (DECL) == VAR_DECL \ + && TREE_READONLY (DECL) && !TREE_SIDE_EFFECTS (DECL)) \ + && (!DECL_INITIAL (DECL) \ + || TREE_CONSTANT (DECL_INITIAL (DECL)))) \ + { \ + SYMBOL_REF_FLAG (XEXP (DECL_RTL (DECL), 0)) = 0; \ + } \ + \ + else if (TARGET_EMBEDDED_PIC) \ + { \ + if (TREE_CODE (DECL) == VAR_DECL) \ + SYMBOL_REF_FLAG (XEXP (DECL_RTL (DECL), 0)) = 1; \ + else if (TREE_CODE (DECL) == FUNCTION_DECL) \ + SYMBOL_REF_FLAG (XEXP (DECL_RTL (DECL), 0)) = 0; \ + else if (TREE_CODE (DECL) == STRING_CST \ + && ! flag_writable_strings) \ + SYMBOL_REF_FLAG (XEXP (TREE_CST_RTL (DECL), 0)) = 0; \ + else \ + SYMBOL_REF_FLAG (XEXP (TREE_CST_RTL (DECL), 0)) = 1; \ + } \ + \ + else if (TREE_CODE (DECL) == VAR_DECL \ + && DECL_SECTION_NAME (DECL) != NULL_TREE \ + && (0 == strcmp (TREE_STRING_POINTER (DECL_SECTION_NAME (DECL)), \ + ".sdata") \ + || 0 == strcmp (TREE_STRING_POINTER (DECL_SECTION_NAME (DECL)),\ + ".sbss"))) \ + { \ + SYMBOL_REF_FLAG (XEXP (DECL_RTL (DECL), 0)) = 1; \ + } \ + \ + /* We can not perform GP optimizations on variables which are in \ + specific sections, except for .sdata and .sbss which are \ + handled above. */ \ + else if (TARGET_GP_OPT && TREE_CODE (DECL) == VAR_DECL \ + && DECL_SECTION_NAME (DECL) == NULL_TREE) \ + { \ + int size = int_size_in_bytes (TREE_TYPE (DECL)); \ + \ + if (size > 0 && size <= mips_section_threshold) \ + SYMBOL_REF_FLAG (XEXP (DECL_RTL (DECL), 0)) = 1; \ + } \ + \ + else if (HALF_PIC_P ()) \ + { \ + HALF_PIC_ENCODE (DECL); \ + } \ + } \ +while (0) + +/* The mips16 wants the constant pool to be after the function, + because the PC relative load instructions use unsigned offsets. */ + +#define CONSTANT_POOL_BEFORE_FUNCTION (! TARGET_MIPS16) + +#define ASM_OUTPUT_POOL_EPILOGUE(FILE, FNNAME, FNDECL, SIZE) \ + mips_string_length = 0; + +#if 0 +/* In mips16 mode, put most string constants after the function. */ +#define CONSTANT_AFTER_FUNCTION_P(tree) \ + (TARGET_MIPS16 && mips16_constant_after_function_p (tree)) +#endif + +/* Specify the machine mode that this machine uses + for the index in the tablejump instruction. + ??? Using HImode in mips16 mode can cause overflow. However, the + overflow is no more likely than the overflow in a branch + instruction. Large functions can currently break in both ways. */ +#define CASE_VECTOR_MODE \ + (TARGET_MIPS16 ? HImode : Pmode == DImode ? DImode : SImode) + +/* 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 (TARGET_MIPS16) + +/* 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. */ +#ifndef DEFAULT_SIGNED_CHAR +#define DEFAULT_SIGNED_CHAR 1 +#endif + +/* Max number of bytes we can move from memory to memory + in one reasonably fast instruction. */ +#define MOVE_MAX (TARGET_64BIT ? 8 : 4) +#define MAX_MOVE_MAX 8 + +/* Define this macro as a C expression which is nonzero if + accessing less than a word of memory (i.e. a `char' or a + `short') is no faster than accessing a word of memory, i.e., if + such access require more than one instruction or if there is no + difference in cost between byte and (aligned) word loads. + + On RISC machines, it tends to generate better code to define + this as 1, since it avoids making a QI or HI mode register. */ +#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 + +/* Define this if zero-extension is slow (more than one real instruction). */ +#define SLOW_ZERO_EXTEND + +/* 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. */ +/* In 64 bit mode, 32 bit instructions require that register values be properly + sign-extended to 64 bits. As a result, a truncate is not a no-op if it + converts a value >32 bits to a value <32 bits. */ +/* ??? This results in inefficient code for 64 bit to 32 conversions. + Something needs to be done about this. Perhaps not use any 32 bit + instructions? Perhaps use PROMOTE_MODE? */ +#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) \ + (TARGET_64BIT ? ((INPREC) <= 32 || (OUTPREC) > 32) : 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. + + For MIPS we make pointers are the smaller of longs and gp-registers. */ + +#ifndef Pmode +#define Pmode ((TARGET_LONG64 && TARGET_64BIT) ? DImode : SImode) +#endif + +/* A function address in a call instruction + is a word address (for indexing purposes) + so give the MEM rtx a words's mode. */ + +#define FUNCTION_MODE (Pmode == DImode ? DImode : SImode) + +/* Define TARGET_MEM_FUNCTIONS if we want to use calls to memcpy and + memset, instead of the BSD functions bcopy and bzero. */ + +#if defined(MIPS_SYSV) || defined(OSF_OS) +#define TARGET_MEM_FUNCTIONS +#endif + + +/* A part of a C `switch' statement that describes the relative + costs of constant RTL expressions. It must contain `case' + labels for expression codes `const_int', `const', `symbol_ref', + `label_ref' and `const_double'. Each case must ultimately reach + a `return' statement to return the relative cost of the use of + that kind of constant value in an expression. The cost may + depend on the precise value of the constant, which is available + for examination in X. + + CODE is the expression code--redundant, since it can be obtained + with `GET_CODE (X)'. */ + +#define CONST_COSTS(X,CODE,OUTER_CODE) \ + case CONST_INT: \ + if (! TARGET_MIPS16) \ + { \ + /* Always return 0, since we don't have different sized \ + instructions, hence different costs according to Richard \ + Kenner */ \ + return 0; \ + } \ + if ((OUTER_CODE) == SET) \ + { \ + if (INTVAL (X) >= 0 && INTVAL (X) < 0x100) \ + return 0; \ + else if ((INTVAL (X) >= 0 && INTVAL (X) < 0x10000) \ + || (INTVAL (X) < 0 && INTVAL (X) > -0x100)) \ + return COSTS_N_INSNS (1); \ + else \ + return COSTS_N_INSNS (2); \ + } \ + /* A PLUS could be an address. We don't want to force an address \ + to use a register, so accept any signed 16 bit value without \ + complaint. */ \ + if ((OUTER_CODE) == PLUS \ + && INTVAL (X) >= -0x8000 && INTVAL (X) < 0x8000) \ + return 0; \ + /* A number between 1 and 8 inclusive is efficient for a shift. \ + Otherwise, we will need an extended instruction. */ \ + if ((OUTER_CODE) == ASHIFT || (OUTER_CODE) == ASHIFTRT \ + || (OUTER_CODE) == LSHIFTRT) \ + { \ + if (INTVAL (X) >= 1 && INTVAL (X) <= 8) \ + return 0; \ + return COSTS_N_INSNS (1); \ + } \ + /* We can use cmpi for an xor with an unsigned 16 bit value. */ \ + if ((OUTER_CODE) == XOR \ + && INTVAL (X) >= 0 && INTVAL (X) < 0x10000) \ + return 0; \ + /* We may be able to use slt or sltu for a comparison with a \ + signed 16 bit value. (The boundary conditions aren't quite \ + right, but this is just a heuristic anyhow.) */ \ + if (((OUTER_CODE) == LT || (OUTER_CODE) == LE \ + || (OUTER_CODE) == GE || (OUTER_CODE) == GT \ + || (OUTER_CODE) == LTU || (OUTER_CODE) == LEU \ + || (OUTER_CODE) == GEU || (OUTER_CODE) == GTU) \ + && INTVAL (X) >= -0x8000 && INTVAL (X) < 0x8000) \ + return 0; \ + /* Equality comparisons with 0 are cheap. */ \ + if (((OUTER_CODE) == EQ || (OUTER_CODE) == NE) \ + && INTVAL (X) == 0) \ + return 0; \ + \ + /* Otherwise, work out the cost to load the value into a \ + register. */ \ + if (INTVAL (X) >= 0 && INTVAL (X) < 0x100) \ + return COSTS_N_INSNS (1); \ + else if ((INTVAL (X) >= 0 && INTVAL (X) < 0x10000) \ + || (INTVAL (X) < 0 && INTVAL (X) > -0x100)) \ + return COSTS_N_INSNS (2); \ + else \ + return COSTS_N_INSNS (3); \ + \ + case LABEL_REF: \ + return COSTS_N_INSNS (2); \ + \ + case CONST: \ + { \ + rtx offset = const0_rtx; \ + rtx symref = eliminate_constant_term (XEXP (X, 0), &offset); \ + \ + if (TARGET_MIPS16 && mips16_gp_offset_p (X)) \ + { \ + /* Treat this like a signed 16 bit CONST_INT. */ \ + if ((OUTER_CODE) == PLUS) \ + return 0; \ + else if ((OUTER_CODE) == SET) \ + return COSTS_N_INSNS (1); \ + else \ + return COSTS_N_INSNS (2); \ + } \ + \ + if (GET_CODE (symref) == LABEL_REF) \ + return COSTS_N_INSNS (2); \ + \ + if (GET_CODE (symref) != SYMBOL_REF) \ + return COSTS_N_INSNS (4); \ + \ + /* let's be paranoid.... */ \ + if (INTVAL (offset) < -32768 || INTVAL (offset) > 32767) \ + return COSTS_N_INSNS (2); \ + \ + return COSTS_N_INSNS (SYMBOL_REF_FLAG (symref) ? 1 : 2); \ + } \ + \ + case SYMBOL_REF: \ + return COSTS_N_INSNS (SYMBOL_REF_FLAG (X) ? 1 : 2); \ + \ + case CONST_DOUBLE: \ + { \ + rtx high, low; \ + if (TARGET_MIPS16) \ + return COSTS_N_INSNS (4); \ + split_double (X, &high, &low); \ + return COSTS_N_INSNS ((high == CONST0_RTX (GET_MODE (high)) \ + || low == CONST0_RTX (GET_MODE (low))) \ + ? 2 : 4); \ + } + +/* Like `CONST_COSTS' but applies to nonconstant RTL expressions. + This can be used, for example, to indicate how costly a multiply + instruction is. In writing this macro, you can use the construct + `COSTS_N_INSNS (N)' to specify a cost equal to N fast instructions. + + This macro is optional; do not define it if the default cost + assumptions are adequate for the target machine. + + If -mdebugd is used, change the multiply cost to 2, so multiply by + a constant isn't converted to a series of shifts. This helps + strength reduction, and also makes it easier to identify what the + compiler is doing. */ + +/* ??? Fix this to be right for the R8000. */ +#define RTX_COSTS(X,CODE,OUTER_CODE) \ + case MEM: \ + { \ + int num_words = (GET_MODE_SIZE (GET_MODE (X)) > UNITS_PER_WORD) ? 2 : 1; \ + if (simple_memory_operand (X, GET_MODE (X))) \ + return COSTS_N_INSNS (num_words); \ + \ + return COSTS_N_INSNS (2*num_words); \ + } \ + \ + case FFS: \ + return COSTS_N_INSNS (6); \ + \ + case NOT: \ + return COSTS_N_INSNS ((GET_MODE (X) == DImode && !TARGET_64BIT) ? 2 : 1); \ + \ + case AND: \ + case IOR: \ + case XOR: \ + if (GET_MODE (X) == DImode && !TARGET_64BIT) \ + return COSTS_N_INSNS (2); \ + \ + break; \ + \ + case ASHIFT: \ + case ASHIFTRT: \ + case LSHIFTRT: \ + if (GET_MODE (X) == DImode && !TARGET_64BIT) \ + return COSTS_N_INSNS ((GET_CODE (XEXP (X, 1)) == CONST_INT) ? 4 : 12); \ + \ + break; \ + \ + case ABS: \ + { \ + enum machine_mode xmode = GET_MODE (X); \ + if (xmode == SFmode || xmode == DFmode) \ + return COSTS_N_INSNS (1); \ + \ + return COSTS_N_INSNS (4); \ + } \ + \ + case PLUS: \ + case MINUS: \ + { \ + enum machine_mode xmode = GET_MODE (X); \ + if (xmode == SFmode || xmode == DFmode) \ + { \ + if (mips_cpu == PROCESSOR_R3000 \ + || mips_cpu == PROCESSOR_R3900) \ + return COSTS_N_INSNS (2); \ + else if (mips_cpu == PROCESSOR_R6000) \ + return COSTS_N_INSNS (3); \ + else \ + return COSTS_N_INSNS (6); \ + } \ + \ + if (xmode == DImode && !TARGET_64BIT) \ + return COSTS_N_INSNS (4); \ + \ + break; \ + } \ + \ + case NEG: \ + if (GET_MODE (X) == DImode && !TARGET_64BIT) \ + return 4; \ + \ + break; \ + \ + case MULT: \ + { \ + enum machine_mode xmode = GET_MODE (X); \ + if (xmode == SFmode) \ + { \ + if (mips_cpu == PROCESSOR_R3000 \ + || mips_cpu == PROCESSOR_R3900 \ + || mips_cpu == PROCESSOR_R5000) \ + return COSTS_N_INSNS (4); \ + else if (mips_cpu == PROCESSOR_R6000) \ + return COSTS_N_INSNS (5); \ + else \ + return COSTS_N_INSNS (7); \ + } \ + \ + if (xmode == DFmode) \ + { \ + if (mips_cpu == PROCESSOR_R3000 \ + || mips_cpu == PROCESSOR_R3900 \ + || mips_cpu == PROCESSOR_R5000) \ + return COSTS_N_INSNS (5); \ + else if (mips_cpu == PROCESSOR_R6000) \ + return COSTS_N_INSNS (6); \ + else \ + return COSTS_N_INSNS (8); \ + } \ + \ + if (mips_cpu == PROCESSOR_R3000) \ + return COSTS_N_INSNS (12); \ + else if (mips_cpu == PROCESSOR_R3900) \ + return COSTS_N_INSNS (2); \ + else if (mips_cpu == PROCESSOR_R6000) \ + return COSTS_N_INSNS (17); \ + else if (mips_cpu == PROCESSOR_R5000) \ + return COSTS_N_INSNS (5); \ + else \ + return COSTS_N_INSNS (10); \ + } \ + \ + case DIV: \ + case MOD: \ + { \ + enum machine_mode xmode = GET_MODE (X); \ + if (xmode == SFmode) \ + { \ + if (mips_cpu == PROCESSOR_R3000 \ + || mips_cpu == PROCESSOR_R3900) \ + return COSTS_N_INSNS (12); \ + else if (mips_cpu == PROCESSOR_R6000) \ + return COSTS_N_INSNS (15); \ + else \ + return COSTS_N_INSNS (23); \ + } \ + \ + if (xmode == DFmode) \ + { \ + if (mips_cpu == PROCESSOR_R3000 \ + || mips_cpu == PROCESSOR_R3900) \ + return COSTS_N_INSNS (19); \ + else if (mips_cpu == PROCESSOR_R6000) \ + return COSTS_N_INSNS (16); \ + else \ + return COSTS_N_INSNS (36); \ + } \ + } \ + /* fall through */ \ + \ + case UDIV: \ + case UMOD: \ + if (mips_cpu == PROCESSOR_R3000 \ + || mips_cpu == PROCESSOR_R3900) \ + return COSTS_N_INSNS (35); \ + else if (mips_cpu == PROCESSOR_R6000) \ + return COSTS_N_INSNS (38); \ + else if (mips_cpu == PROCESSOR_R5000) \ + return COSTS_N_INSNS (36); \ + else \ + return COSTS_N_INSNS (69); \ + \ + case SIGN_EXTEND: \ + /* A sign extend from SImode to DImode in 64 bit mode is often \ + zero instructions, because the result can often be used \ + directly by another instruction; we'll call it one. */ \ + if (TARGET_64BIT && GET_MODE (X) == DImode \ + && GET_MODE (XEXP (X, 0)) == SImode) \ + return COSTS_N_INSNS (1); \ + else \ + return COSTS_N_INSNS (2); \ + \ + case ZERO_EXTEND: \ + if (TARGET_64BIT && GET_MODE (X) == DImode \ + && GET_MODE (XEXP (X, 0)) == SImode) \ + return COSTS_N_INSNS (2); \ + else \ + return COSTS_N_INSNS (1); + +/* An expression giving the cost of an addressing mode that + contains ADDRESS. If not defined, the cost is computed from the + form of the ADDRESS expression and the `CONST_COSTS' values. + + For most CISC machines, the default cost is a good approximation + of the true cost of the addressing mode. However, on RISC + machines, all instructions normally have the same length and + execution time. Hence all addresses will have equal costs. + + In cases where more than one form of an address is known, the + form with the lowest cost will be used. If multiple forms have + the same, lowest, cost, the one that is the most complex will be + used. + + For example, suppose an address that is equal to the sum of a + register and a constant is used twice in the same basic block. + When this macro is not defined, the address will be computed in + a register and memory references will be indirect through that + register. On machines where the cost of the addressing mode + containing the sum is no higher than that of a simple indirect + reference, this will produce an additional instruction and + possibly require an additional register. Proper specification + of this macro eliminates this overhead for such machines. + + Similar use of this macro is made in strength reduction of loops. + + ADDRESS need not be valid as an address. In such a case, the + cost is not relevant and can be any value; invalid addresses + need not be assigned a different cost. + + On machines where an address involving more than one register is + as cheap as an address computation involving only one register, + defining `ADDRESS_COST' to reflect this can cause two registers + to be live over a region of code where only one would have been + if `ADDRESS_COST' were not defined in that manner. This effect + should be considered in the definition of this macro. + Equivalent costs should probably only be given to addresses with + different numbers of registers on machines with lots of registers. + + This macro will normally either not be defined or be defined as + a constant. */ + +#define ADDRESS_COST(ADDR) (REG_P (ADDR) ? 1 : mips_address_cost (ADDR)) + +/* A C expression for the cost of moving data from a register in + class FROM to one in class TO. The classes are expressed using + the enumeration values such as `GENERAL_REGS'. A value of 2 is + the default; other values are interpreted relative to that. + + It is not required that the cost always equal 2 when FROM is the + same as TO; on some machines it is expensive to move between + registers if they are not general registers. + + If reload sees an insn consisting of a single `set' between two + hard registers, and if `REGISTER_MOVE_COST' applied to their + classes returns a value of 2, reload does not check to ensure + that the constraints of the insn are met. Setting a cost of + other than 2 will allow reload to verify that the constraints are + met. You should do this if the `movM' pattern's constraints do + not allow such copying. + + ??? We make make the cost of moving from HI/LO/HILO/MD into general + registers the same as for one of moving general registers to + HI/LO/HILO/MD for TARGET_MIPS16 in order to prevent allocating a + pseudo to HI/LO/HILO/MD. This might hurt optimizations though, it + isn't clear if it is wise. And it might not work in all cases. We + could solve the DImode LO reg problem by using a multiply, just like + reload_{in,out}si. We could solve the SImode/HImode HI reg problem + by using divide instructions. divu puts the remainder in the HI + reg, so doing a divide by -1 will move the value in the HI reg for + all values except -1. We could handle that case by using a signed + divide, e.g. -1 / 2 (or maybe 1 / -2?). We'd have to emit a + compare/branch to test the input value to see which instruction we + need to use. This gets pretty messy, but it is feasible. */ + +#define REGISTER_MOVE_COST(FROM, TO) \ + ((FROM) == M16_REGS && GR_REG_CLASS_P (TO) ? 2 \ + : (FROM) == M16_NA_REGS && GR_REG_CLASS_P (TO) ? 2 \ + : GR_REG_CLASS_P (FROM) && (TO) == M16_REGS ? 2 \ + : GR_REG_CLASS_P (FROM) && (TO) == M16_NA_REGS ? 2 \ + : GR_REG_CLASS_P (FROM) && GR_REG_CLASS_P (TO) ? (TARGET_MIPS16 ? 4 : 2) \ + : (FROM) == FP_REGS && (TO) == FP_REGS ? 2 \ + : GR_REG_CLASS_P (FROM) && (TO) == FP_REGS ? 4 \ + : (FROM) == FP_REGS && GR_REG_CLASS_P (TO) ? 4 \ + : (((FROM) == HI_REG || (FROM) == LO_REG \ + || (FROM) == MD_REGS || (FROM) == HILO_REG) \ + && ((TO) == M16_REGS || (TO) == M16_NA_REGS)) ? 6 \ + : (((FROM) == HI_REG || (FROM) == LO_REG \ + || (FROM) == MD_REGS || (FROM) == HILO_REG) \ + && GR_REG_CLASS_P (TO)) ? (TARGET_MIPS16 ? 12 : 6) \ + : (((TO) == HI_REG || (TO) == LO_REG \ + || (TO) == MD_REGS || (TO) == HILO_REG) \ + && GR_REG_CLASS_P (FROM)) ? (TARGET_MIPS16 ? 12 : 6) \ + : (FROM) == ST_REGS && GR_REG_CLASS_P (TO) ? 4 \ + : (FROM) == FP_REGS && (TO) == ST_REGS ? 8 \ + : 12) + +/* ??? Fix this to be right for the R8000. */ +#define MEMORY_MOVE_COST(MODE,CLASS,TO_P) \ + (((mips_cpu == PROCESSOR_R4000 || mips_cpu == PROCESSOR_R6000) ? 6 : 4) \ + + memory_move_secondary_cost ((MODE), (CLASS), (TO_P))) + +/* Define if copies to/from condition code registers should be avoided. + + This is needed for the MIPS because reload_outcc is not complete; + it needs to handle cases where the source is a general or another + condition code register. */ +#define AVOID_CCMODE_COPIES + +/* A C expression for the cost of a branch instruction. A value of + 1 is the default; other values are interpreted relative to that. */ + +/* ??? Fix this to be right for the R8000. */ +#define BRANCH_COST \ + ((! TARGET_MIPS16 \ + && (mips_cpu == PROCESSOR_R4000 || mips_cpu == PROCESSOR_R6000)) \ + ? 2 : 1) + +/* A C statement (sans semicolon) to update the integer variable COST + based on the relationship between INSN that is dependent on + DEP_INSN through the dependence LINK. The default is to make no + adjustment to COST. On the MIPS, ignore the cost of anti- and + output-dependencies. */ + +#define ADJUST_COST(INSN,LINK,DEP_INSN,COST) \ + if (REG_NOTE_KIND (LINK) != 0) \ + (COST) = 0; /* Anti or output dependence. */ + +/* Optionally define this if you have added predicates to + `MACHINE.c'. This macro is called within an initializer of an + array of structures. The first field in the structure is the + name of a predicate and the second field is an array of rtl + codes. For each predicate, list all rtl codes that can be in + expressions matched by the predicate. The list should have a + trailing comma. Here is an example of two entries in the list + for a typical RISC machine: + + #define PREDICATE_CODES \ + {"gen_reg_rtx_operand", {SUBREG, REG}}, \ + {"reg_or_short_cint_operand", {SUBREG, REG, CONST_INT}}, + + Defining this macro does not affect the generated code (however, + incorrect definitions that omit an rtl code that may be matched + by the predicate can cause the compiler to malfunction). + Instead, it allows the table built by `genrecog' to be more + compact and efficient, thus speeding up the compiler. The most + important predicates to include in the list specified by this + macro are thoses used in the most insn patterns. */ + +#define PREDICATE_CODES \ + {"uns_arith_operand", { REG, CONST_INT, SUBREG }}, \ + {"arith_operand", { REG, CONST_INT, SUBREG }}, \ + {"arith32_operand", { REG, CONST_INT, SUBREG }}, \ + {"reg_or_0_operand", { REG, CONST_INT, CONST_DOUBLE, SUBREG }}, \ + {"true_reg_or_0_operand", { REG, CONST_INT, CONST_DOUBLE, SUBREG }}, \ + {"small_int", { CONST_INT }}, \ + {"large_int", { CONST_INT }}, \ + {"mips_const_double_ok", { CONST_DOUBLE }}, \ + {"const_float_1_operand", { CONST_DOUBLE }}, \ + {"simple_memory_operand", { MEM, SUBREG }}, \ + {"equality_op", { EQ, NE }}, \ + {"cmp_op", { EQ, NE, GT, GE, GTU, GEU, LT, LE, \ + LTU, LEU }}, \ + {"pc_or_label_operand", { PC, LABEL_REF }}, \ + {"call_insn_operand", { CONST_INT, CONST, SYMBOL_REF, REG}}, \ + {"move_operand", { CONST_INT, CONST_DOUBLE, CONST, \ + SYMBOL_REF, LABEL_REF, SUBREG, \ + REG, MEM}}, \ + {"movdi_operand", { CONST_INT, CONST_DOUBLE, CONST, \ + SYMBOL_REF, LABEL_REF, SUBREG, REG, \ + MEM, SIGN_EXTEND }}, \ + {"se_register_operand", { SUBREG, REG, SIGN_EXTEND }}, \ + {"se_reg_or_0_operand", { REG, CONST_INT, CONST_DOUBLE, SUBREG, \ + SIGN_EXTEND }}, \ + {"se_uns_arith_operand", { REG, CONST_INT, SUBREG, \ + SIGN_EXTEND }}, \ + {"se_arith_operand", { REG, CONST_INT, SUBREG, \ + SIGN_EXTEND }}, \ + {"se_nonmemory_operand", { CONST_INT, CONST_DOUBLE, CONST, \ + SYMBOL_REF, LABEL_REF, SUBREG, \ + REG, SIGN_EXTEND }}, \ + {"se_nonimmediate_operand", { SUBREG, REG, MEM, SIGN_EXTEND }}, \ + {"consttable_operand", { LABEL_REF, SYMBOL_REF, CONST_INT, \ + CONST_DOUBLE, CONST }}, \ + {"extend_operator", { SIGN_EXTEND, ZERO_EXTEND }}, \ + {"highpart_shift_operator", { ASHIFTRT, LSHIFTRT, ROTATERT, ROTATE }}, + + + +/* If defined, a C statement to be executed just prior to the + output of assembler code for INSN, to modify the extracted + operands so they will be output differently. + + Here the argument OPVEC is the vector containing the operands + extracted from INSN, and NOPERANDS is the number of elements of + the vector which contain meaningful data for this insn. The + contents of this vector are what will be used to convert the + insn template into assembler code, so you can change the + assembler output by changing the contents of the vector. + + We use it to check if the current insn needs a nop in front of it + because of load delays, and also to update the delay slot + statistics. */ + +#define FINAL_PRESCAN_INSN(INSN, OPVEC, NOPERANDS) \ + final_prescan_insn (INSN, OPVEC, NOPERANDS) + + +/* Control the assembler format that we output. */ + +/* Output at beginning of assembler file. + If we are optimizing to use the global pointer, create a temporary + file to hold all of the text stuff, and write it out to the end. + This is needed because the MIPS assembler is evidently one pass, + and if it hasn't seen the relevant .comm/.lcomm/.extern/.sdata + declaration when the code is processed, it generates a two + instruction sequence. */ + +#define ASM_FILE_START(STREAM) mips_asm_file_start (STREAM) + +/* Output to assembler file text saying following lines + may contain character constants, extra white space, comments, etc. */ + +#define ASM_APP_ON " #APP\n" + +/* Output to assembler file text saying following lines + no longer contain unusual constructs. */ + +#define ASM_APP_OFF " #NO_APP\n" + +/* How to refer to registers in assembler output. + This sequence is indexed by compiler's hard-register-number (see above). + + In order to support the two different conventions for register names, + we use the name of a table set up in mips.c, which is overwritten + if -mrnames is used. */ + +#define REGISTER_NAMES \ +{ \ + &mips_reg_names[ 0][0], \ + &mips_reg_names[ 1][0], \ + &mips_reg_names[ 2][0], \ + &mips_reg_names[ 3][0], \ + &mips_reg_names[ 4][0], \ + &mips_reg_names[ 5][0], \ + &mips_reg_names[ 6][0], \ + &mips_reg_names[ 7][0], \ + &mips_reg_names[ 8][0], \ + &mips_reg_names[ 9][0], \ + &mips_reg_names[10][0], \ + &mips_reg_names[11][0], \ + &mips_reg_names[12][0], \ + &mips_reg_names[13][0], \ + &mips_reg_names[14][0], \ + &mips_reg_names[15][0], \ + &mips_reg_names[16][0], \ + &mips_reg_names[17][0], \ + &mips_reg_names[18][0], \ + &mips_reg_names[19][0], \ + &mips_reg_names[20][0], \ + &mips_reg_names[21][0], \ + &mips_reg_names[22][0], \ + &mips_reg_names[23][0], \ + &mips_reg_names[24][0], \ + &mips_reg_names[25][0], \ + &mips_reg_names[26][0], \ + &mips_reg_names[27][0], \ + &mips_reg_names[28][0], \ + &mips_reg_names[29][0], \ + &mips_reg_names[30][0], \ + &mips_reg_names[31][0], \ + &mips_reg_names[32][0], \ + &mips_reg_names[33][0], \ + &mips_reg_names[34][0], \ + &mips_reg_names[35][0], \ + &mips_reg_names[36][0], \ + &mips_reg_names[37][0], \ + &mips_reg_names[38][0], \ + &mips_reg_names[39][0], \ + &mips_reg_names[40][0], \ + &mips_reg_names[41][0], \ + &mips_reg_names[42][0], \ + &mips_reg_names[43][0], \ + &mips_reg_names[44][0], \ + &mips_reg_names[45][0], \ + &mips_reg_names[46][0], \ + &mips_reg_names[47][0], \ + &mips_reg_names[48][0], \ + &mips_reg_names[49][0], \ + &mips_reg_names[50][0], \ + &mips_reg_names[51][0], \ + &mips_reg_names[52][0], \ + &mips_reg_names[53][0], \ + &mips_reg_names[54][0], \ + &mips_reg_names[55][0], \ + &mips_reg_names[56][0], \ + &mips_reg_names[57][0], \ + &mips_reg_names[58][0], \ + &mips_reg_names[59][0], \ + &mips_reg_names[60][0], \ + &mips_reg_names[61][0], \ + &mips_reg_names[62][0], \ + &mips_reg_names[63][0], \ + &mips_reg_names[64][0], \ + &mips_reg_names[65][0], \ + &mips_reg_names[66][0], \ + &mips_reg_names[67][0], \ + &mips_reg_names[68][0], \ + &mips_reg_names[69][0], \ + &mips_reg_names[70][0], \ + &mips_reg_names[71][0], \ + &mips_reg_names[72][0], \ + &mips_reg_names[73][0], \ + &mips_reg_names[74][0], \ + &mips_reg_names[75][0], \ +} + +/* print-rtl.c can't use REGISTER_NAMES, since it depends on mips.c. + So define this for it. */ +#define DEBUG_REGISTER_NAMES \ +{ \ + "$0", "at", "v0", "v1", "a0", "a1", "a2", "a3", \ + "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", \ + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", \ + "t8", "t9", "k0", "k1", "gp", "sp", "$fp", "ra", \ + "$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", \ + "hi", "lo", "accum","$fcc0","$fcc1","$fcc2","$fcc3","$fcc4", \ + "$fcc5","$fcc6","$fcc7","$rap" \ +} + +/* If defined, a C initializer for an array of structures + containing a name and a register number. This macro defines + additional names for hard registers, thus allowing the `asm' + option in declarations to refer to registers using alternate + names. + + We define both names for the integer registers here. */ + +#define ADDITIONAL_REGISTER_NAMES \ +{ \ + { "$0", 0 + GP_REG_FIRST }, \ + { "$1", 1 + GP_REG_FIRST }, \ + { "$2", 2 + GP_REG_FIRST }, \ + { "$3", 3 + GP_REG_FIRST }, \ + { "$4", 4 + GP_REG_FIRST }, \ + { "$5", 5 + GP_REG_FIRST }, \ + { "$6", 6 + GP_REG_FIRST }, \ + { "$7", 7 + GP_REG_FIRST }, \ + { "$8", 8 + GP_REG_FIRST }, \ + { "$9", 9 + GP_REG_FIRST }, \ + { "$10", 10 + GP_REG_FIRST }, \ + { "$11", 11 + GP_REG_FIRST }, \ + { "$12", 12 + GP_REG_FIRST }, \ + { "$13", 13 + GP_REG_FIRST }, \ + { "$14", 14 + GP_REG_FIRST }, \ + { "$15", 15 + GP_REG_FIRST }, \ + { "$16", 16 + GP_REG_FIRST }, \ + { "$17", 17 + GP_REG_FIRST }, \ + { "$18", 18 + GP_REG_FIRST }, \ + { "$19", 19 + GP_REG_FIRST }, \ + { "$20", 20 + GP_REG_FIRST }, \ + { "$21", 21 + GP_REG_FIRST }, \ + { "$22", 22 + GP_REG_FIRST }, \ + { "$23", 23 + GP_REG_FIRST }, \ + { "$24", 24 + GP_REG_FIRST }, \ + { "$25", 25 + GP_REG_FIRST }, \ + { "$26", 26 + GP_REG_FIRST }, \ + { "$27", 27 + GP_REG_FIRST }, \ + { "$28", 28 + GP_REG_FIRST }, \ + { "$29", 29 + GP_REG_FIRST }, \ + { "$30", 30 + GP_REG_FIRST }, \ + { "$31", 31 + GP_REG_FIRST }, \ + { "$sp", 29 + GP_REG_FIRST }, \ + { "$fp", 30 + GP_REG_FIRST }, \ + { "at", 1 + GP_REG_FIRST }, \ + { "v0", 2 + GP_REG_FIRST }, \ + { "v1", 3 + GP_REG_FIRST }, \ + { "a0", 4 + GP_REG_FIRST }, \ + { "a1", 5 + GP_REG_FIRST }, \ + { "a2", 6 + GP_REG_FIRST }, \ + { "a3", 7 + GP_REG_FIRST }, \ + { "t0", 8 + GP_REG_FIRST }, \ + { "t1", 9 + GP_REG_FIRST }, \ + { "t2", 10 + GP_REG_FIRST }, \ + { "t3", 11 + GP_REG_FIRST }, \ + { "t4", 12 + GP_REG_FIRST }, \ + { "t5", 13 + GP_REG_FIRST }, \ + { "t6", 14 + GP_REG_FIRST }, \ + { "t7", 15 + GP_REG_FIRST }, \ + { "s0", 16 + GP_REG_FIRST }, \ + { "s1", 17 + GP_REG_FIRST }, \ + { "s2", 18 + GP_REG_FIRST }, \ + { "s3", 19 + GP_REG_FIRST }, \ + { "s4", 20 + GP_REG_FIRST }, \ + { "s5", 21 + GP_REG_FIRST }, \ + { "s6", 22 + GP_REG_FIRST }, \ + { "s7", 23 + GP_REG_FIRST }, \ + { "t8", 24 + GP_REG_FIRST }, \ + { "t9", 25 + GP_REG_FIRST }, \ + { "k0", 26 + GP_REG_FIRST }, \ + { "k1", 27 + GP_REG_FIRST }, \ + { "gp", 28 + GP_REG_FIRST }, \ + { "sp", 29 + GP_REG_FIRST }, \ + { "fp", 30 + GP_REG_FIRST }, \ + { "ra", 31 + GP_REG_FIRST }, \ + { "$sp", 29 + GP_REG_FIRST }, \ + { "$fp", 30 + GP_REG_FIRST } \ +} + +/* 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 + +/* A C compound statement to output to stdio stream STREAM the + assembler syntax for an instruction operand X. X is an RTL + expression. + + CODE is a value that can be used to specify one of several ways + of printing the operand. It is used when identical operands + must be printed differently depending on the context. CODE + comes from the `%' specification that was used to request + printing of the operand. If the specification was just `%DIGIT' + then CODE is 0; if the specification was `%LTR DIGIT' then CODE + is the ASCII code for LTR. + + If X is a register, this macro should print the register's name. + The names can be found in an array `reg_names' whose type is + `char *[]'. `reg_names' is initialized from `REGISTER_NAMES'. + + When the machine description has a specification `%PUNCT' (a `%' + followed by a punctuation character), this macro is called with + a null pointer for X and the punctuation character for CODE. + + See mips.c for the MIPS specific codes. */ + +#define PRINT_OPERAND(FILE, X, CODE) print_operand (FILE, X, CODE) + +/* A C expression which evaluates to true if CODE is a valid + punctuation character for use in the `PRINT_OPERAND' macro. If + `PRINT_OPERAND_PUNCT_VALID_P' is not defined, it means that no + punctuation characters (except for the standard one, `%') are + used in this way. */ + +#define PRINT_OPERAND_PUNCT_VALID_P(CODE) mips_print_operand_punct[CODE] + +/* A C compound statement to output to stdio stream STREAM the + assembler syntax for an instruction operand that is a memory + reference whose address is ADDR. ADDR is an RTL expression. + + On some machines, the syntax for a symbolic address depends on + the section that the address refers to. On these machines, + define the macro `ENCODE_SECTION_INFO' to store the information + into the `symbol_ref', and then check for it here. */ + +#define PRINT_OPERAND_ADDRESS(FILE, ADDR) print_operand_address (FILE, ADDR) + + +/* A C statement, to be executed after all slot-filler instructions + have been output. If necessary, call `dbr_sequence_length' to + determine the number of slots filled in a sequence (zero if not + currently outputting a sequence), to decide how many no-ops to + output, or whatever. + + Don't define this macro if it has nothing to do, but it is + helpful in reading assembly output if the extent of the delay + sequence is made explicit (e.g. with white space). + + Note that output routines for instructions with delay slots must + be prepared to deal with not being output as part of a sequence + (i.e. when the scheduling pass is not run, or when no slot + fillers could be found.) The variable `final_sequence' is null + when not processing a sequence, otherwise it contains the + `sequence' rtx being output. */ + +#define DBR_OUTPUT_SEQEND(STREAM) \ +do \ + { \ + if (set_nomacro > 0 && --set_nomacro == 0) \ + fputs ("\t.set\tmacro\n", STREAM); \ + \ + if (set_noreorder > 0 && --set_noreorder == 0) \ + fputs ("\t.set\treorder\n", STREAM); \ + \ + dslots_jump_filled++; \ + fputs ("\n", STREAM); \ + } \ +while (0) + + +/* How to tell the debugger about changes of source files. Note, the + mips ECOFF format cannot deal with changes of files inside of + functions, which means the output of parser generators like bison + is generally not debuggable without using the -l switch. Lose, + lose, lose. Silicon graphics seems to want all .file's hardwired + to 1. */ + +#ifndef SET_FILE_NUMBER +#define SET_FILE_NUMBER() ++num_source_filenames +#endif + +#define ASM_OUTPUT_SOURCE_FILENAME(STREAM, NAME) \ + mips_output_filename (STREAM, NAME) + +/* This is defined so that it can be overridden in iris6.h. */ +#define ASM_OUTPUT_FILENAME(STREAM, NUM_SOURCE_FILENAMES, NAME) \ +do \ + { \ + fprintf (STREAM, "\t.file\t%d ", NUM_SOURCE_FILENAMES); \ + output_quoted_string (STREAM, NAME); \ + fputs ("\n", STREAM); \ + } \ +while (0) + +/* This is how to output a note the debugger telling it the line number + to which the following sequence of instructions corresponds. + Silicon graphics puts a label after each .loc. */ + +#ifndef LABEL_AFTER_LOC +#define LABEL_AFTER_LOC(STREAM) +#endif + +#define ASM_OUTPUT_SOURCE_LINE(STREAM, LINE) \ + mips_output_lineno (STREAM, LINE) + +/* The MIPS implementation uses some labels for its own purpose. The + following lists what labels are created, and are all formed by the + pattern $L[a-z].*. The machine independent portion of GCC creates + labels matching: $L[A-Z][0-9]+ and $L[0-9]+. + + LM[0-9]+ Silicon Graphics/ECOFF stabs label before each stmt. + $Lb[0-9]+ Begin blocks for MIPS debug support + $Lc[0-9]+ Label for use in s<xx> operation. + $Le[0-9]+ End blocks for MIPS debug support + $Lp\..+ Half-pic labels. */ + +/* 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. + + If we are optimizing the gp, remember that this label has been put + out, so we know not to emit an .extern for it in mips_asm_file_end. + We use one of the common bits in the IDENTIFIER tree node for this, + since those bits seem to be unused, and we don't have any method + of getting the decl nodes from the name. */ + +#define ASM_OUTPUT_LABEL(STREAM,NAME) \ +do { \ + assemble_name (STREAM, NAME); \ + fputs (":\n", STREAM); \ +} while (0) + + +/* A C statement (sans semicolon) to output to the stdio stream + STREAM any text necessary for declaring the name NAME of an + initialized variable which is being defined. This macro must + output the label definition (perhaps using `ASM_OUTPUT_LABEL'). + The argument DECL is the `VAR_DECL' tree node representing the + variable. + + If this macro is not defined, then the variable name is defined + in the usual manner as a label (by means of `ASM_OUTPUT_LABEL'). */ + +#define ASM_DECLARE_OBJECT_NAME(STREAM, NAME, DECL) \ +do \ + { \ + mips_declare_object (STREAM, NAME, "", ":\n", 0); \ + HALF_PIC_DECLARE (NAME); \ + } \ +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(STREAM,NAME) \ + do { \ + fputs ("\t.globl\t", STREAM); \ + assemble_name (STREAM, NAME); \ + fputs ("\n", STREAM); \ + } while (0) + +/* This says how to define a global common symbol. */ + +#define ASM_OUTPUT_COMMON(STREAM, NAME, SIZE, ROUNDED) \ + mips_declare_object (STREAM, NAME, "\n\t.comm\t", ",%u\n", (SIZE)) + +/* This says how to define a local common symbol (ie, not visible to + linker). */ + +#define ASM_OUTPUT_LOCAL(STREAM, NAME, SIZE, ROUNDED) \ + mips_declare_object (STREAM, NAME, "\n\t.lcomm\t", ",%u\n", (SIZE)) + + +/* This says how to output an external. It would be possible not to + output anything and let undefined symbol become external. However + the assembler uses length information on externals to allocate in + data/sdata bss/sbss, thereby saving exec time. */ + +#define ASM_OUTPUT_EXTERNAL(STREAM,DECL,NAME) \ + mips_output_external(STREAM,DECL,NAME) + +/* This says what to print at the end of the assembly file */ +#define ASM_FILE_END(STREAM) mips_asm_file_end(STREAM) + + +/* This is how to declare a function name. The actual work of + emitting the label is moved to function_prologue, so that we can + get the line number correctly emitted before the .ent directive, + and after any .file directives. + + Also, switch files if we are optimizing the global pointer. */ + +#define ASM_DECLARE_FUNCTION_NAME(STREAM,NAME,DECL) \ +{ \ + extern FILE *asm_out_text_file; \ + if (TARGET_GP_OPT && ! TARGET_MIPS16) \ + { \ + STREAM = asm_out_text_file; \ + /* ??? text_section gets called too soon. If the previous \ + function is in a special section and we're not, we have \ + to switch back to the text section. We can't call \ + text_section again as gcc thinks we're already there. */ \ + /* ??? See varasm.c. There are other things that get output \ + too early, like alignment (before we've switched STREAM). */ \ + if (DECL_SECTION_NAME (DECL) == NULL_TREE) \ + fprintf (STREAM, "%s\n", TEXT_SECTION_ASM_OP); \ + } \ + \ + HALF_PIC_DECLARE (NAME); \ +} + +/* This is how to output an internal numbered label where + PREFIX is the class of label and NUM is the number within the class. */ + +#define ASM_OUTPUT_INTERNAL_LABEL(STREAM,PREFIX,NUM) \ + fprintf (STREAM, "%s%s%d:\n", LOCAL_LABEL_PREFIX, 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%s%ld", (LOCAL_LABEL_PREFIX), (PREFIX), (long)(NUM)) + +/* This is how to output an assembler line defining a `double' constant. */ + +#define ASM_OUTPUT_DOUBLE(STREAM,VALUE) \ + mips_output_double (STREAM, VALUE) + + +/* This is how to output an assembler line defining a `float' constant. */ + +#define ASM_OUTPUT_FLOAT(STREAM,VALUE) \ + mips_output_float (STREAM, VALUE) + + +/* This is how to output an assembler line defining an `int' constant. */ + +#define ASM_OUTPUT_INT(STREAM,VALUE) \ +do { \ + fprintf (STREAM, "\t.word\t"); \ + output_addr_const (STREAM, (VALUE)); \ + fprintf (STREAM, "\n"); \ +} while (0) + +/* Likewise for 64 bit, `char' and `short' constants. */ + +#define ASM_OUTPUT_DOUBLE_INT(STREAM,VALUE) \ +do { \ + if (TARGET_64BIT) \ + { \ + fprintf (STREAM, "\t.dword\t"); \ + if (HOST_BITS_PER_WIDE_INT < 64 || GET_CODE (VALUE) != CONST_INT) \ + /* We can't use 'X' for negative numbers, because then we won't \ + get the right value for the upper 32 bits. */ \ + output_addr_const (STREAM, VALUE); \ + else \ + /* We must use 'X', because otherwise LONG_MIN will print as \ + a number that the Irix 6 assembler won't accept. */ \ + print_operand (STREAM, VALUE, 'X'); \ + fprintf (STREAM, "\n"); \ + } \ + else \ + { \ + assemble_integer (operand_subword ((VALUE), 0, 0, DImode), \ + UNITS_PER_WORD, 1); \ + assemble_integer (operand_subword ((VALUE), 1, 0, DImode), \ + UNITS_PER_WORD, 1); \ + } \ +} while (0) + +#define ASM_OUTPUT_SHORT(STREAM,VALUE) \ +{ \ + fprintf (STREAM, "\t.half\t"); \ + output_addr_const (STREAM, (VALUE)); \ + fprintf (STREAM, "\n"); \ +} + +#define ASM_OUTPUT_CHAR(STREAM,VALUE) \ +{ \ + fprintf (STREAM, "\t.byte\t"); \ + output_addr_const (STREAM, (VALUE)); \ + fprintf (STREAM, "\n"); \ +} + +/* This is how to output an assembler line for a numeric constant byte. */ + +#define ASM_OUTPUT_BYTE(STREAM,VALUE) \ + fprintf (STREAM, "\t.byte\t0x%x\n", (VALUE)) + +/* This is how to output an element of a case-vector that is absolute. */ + +#define ASM_OUTPUT_ADDR_VEC_ELT(STREAM, VALUE) \ + fprintf (STREAM, "\t%s\t%sL%d\n", \ + Pmode == DImode ? ".dword" : ".word", \ + LOCAL_LABEL_PREFIX, \ + VALUE) + +/* This is how to output an element of a case-vector that is relative. + This is used for pc-relative code (e.g. when TARGET_ABICALLS or + TARGET_EMBEDDED_PIC). */ + +#define ASM_OUTPUT_ADDR_DIFF_ELT(STREAM, BODY, VALUE, REL) \ +do { \ + if (TARGET_MIPS16) \ + fprintf (STREAM, "\t.half\t%sL%d-%sL%d\n", \ + LOCAL_LABEL_PREFIX, VALUE, LOCAL_LABEL_PREFIX, REL); \ + else if (TARGET_EMBEDDED_PIC) \ + fprintf (STREAM, "\t%s\t%sL%d-%sLS%d\n", \ + Pmode == DImode ? ".dword" : ".word", \ + LOCAL_LABEL_PREFIX, VALUE, LOCAL_LABEL_PREFIX, REL); \ + else if (mips_abi == ABI_32 || mips_abi == ABI_O64) \ + fprintf (STREAM, "\t%s\t%sL%d\n", \ + Pmode == DImode ? ".gpdword" : ".gpword", \ + LOCAL_LABEL_PREFIX, VALUE); \ + else \ + fprintf (STREAM, "\t%s\t%sL%d\n", \ + Pmode == DImode ? ".dword" : ".word", \ + LOCAL_LABEL_PREFIX, VALUE); \ +} while (0) + +/* When generating embedded PIC or mips16 code we want to put the jump + table in the .text section. In all other cases, we want to put the + jump table in the .rdata section. Unfortunately, we can't use + JUMP_TABLES_IN_TEXT_SECTION, because it is not conditional. + Instead, we use ASM_OUTPUT_CASE_LABEL to switch back to the .text + section if appropriate. */ +#define ASM_OUTPUT_CASE_LABEL(FILE, PREFIX, NUM, INSN) \ +do { \ + if (TARGET_EMBEDDED_PIC || TARGET_MIPS16) \ + function_section (current_function_decl); \ + ASM_OUTPUT_INTERNAL_LABEL (FILE, PREFIX, NUM); \ +} while (0) + +/* 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(STREAM,LOG) \ + fprintf (STREAM, "\t.align\t%d\n", (LOG)) + +/* This is how to output an assembler line to advance the location + counter by SIZE bytes. */ + +#define ASM_OUTPUT_SKIP(STREAM,SIZE) \ + fprintf (STREAM, "\t.space\t%u\n", (SIZE)) + +/* This is how to output a string. */ +#define ASM_OUTPUT_ASCII(STREAM, STRING, LEN) \ +do { \ + register int i, c, len = (LEN), cur_pos = 17; \ + register unsigned char *string = (unsigned char *)(STRING); \ + fprintf ((STREAM), "\t.ascii\t\""); \ + for (i = 0; i < len; i++) \ + { \ + register int c = string[i]; \ + \ + switch (c) \ + { \ + case '\"': \ + case '\\': \ + putc ('\\', (STREAM)); \ + putc (c, (STREAM)); \ + cur_pos += 2; \ + break; \ + \ + case TARGET_NEWLINE: \ + fputs ("\\n", (STREAM)); \ + if (i+1 < len \ + && (((c = string[i+1]) >= '\040' && c <= '~') \ + || c == TARGET_TAB)) \ + cur_pos = 32767; /* break right here */ \ + else \ + cur_pos += 2; \ + break; \ + \ + case TARGET_TAB: \ + fputs ("\\t", (STREAM)); \ + cur_pos += 2; \ + break; \ + \ + case TARGET_FF: \ + fputs ("\\f", (STREAM)); \ + cur_pos += 2; \ + break; \ + \ + case TARGET_BS: \ + fputs ("\\b", (STREAM)); \ + cur_pos += 2; \ + break; \ + \ + case TARGET_CR: \ + fputs ("\\r", (STREAM)); \ + cur_pos += 2; \ + break; \ + \ + default: \ + if (c >= ' ' && c < 0177) \ + { \ + putc (c, (STREAM)); \ + cur_pos++; \ + } \ + else \ + { \ + fprintf ((STREAM), "\\%03o", c); \ + cur_pos += 4; \ + } \ + } \ + \ + if (cur_pos > 72 && i+1 < len) \ + { \ + cur_pos = 17; \ + fprintf ((STREAM), "\"\n\t.ascii\t\""); \ + } \ + } \ + fprintf ((STREAM), "\"\n"); \ +} while (0) + +/* Handle certain cpp directives used in header files on sysV. */ +#define SCCS_DIRECTIVE + +/* Output #ident as a in the read-only data section. */ +#define ASM_OUTPUT_IDENT(FILE, STRING) \ +{ \ + char *p = STRING; \ + int size = strlen (p) + 1; \ + rdata_section (); \ + assemble_string (p, size); \ +} + +/* Default to -G 8 */ +#ifndef MIPS_DEFAULT_GVALUE +#define MIPS_DEFAULT_GVALUE 8 +#endif + +/* Define the strings to put out for each section in the object file. */ +#define TEXT_SECTION_ASM_OP "\t.text" /* instructions */ +#define DATA_SECTION_ASM_OP "\t.data" /* large data */ +#define SDATA_SECTION_ASM_OP "\t.sdata" /* small data */ +#define RDATA_SECTION_ASM_OP "\t.rdata" /* read-only data */ +#define READONLY_DATA_SECTION rdata_section +#define SMALL_DATA_SECTION sdata_section + +/* What other sections we support other than the normal .data/.text. */ + +#define EXTRA_SECTIONS in_sdata, in_rdata + +/* Define the additional functions to select our additional sections. */ + +/* on the MIPS it is not a good idea to put constants in the text + section, since this defeats the sdata/data mechanism. This is + especially true when -O is used. In this case an effort is made to + address with faster (gp) register relative addressing, which can + only get at sdata and sbss items (there is no stext !!) However, + if the constant is too large for sdata, and it's readonly, it + will go into the .rdata section. */ + +#define EXTRA_SECTION_FUNCTIONS \ +void \ +sdata_section () \ +{ \ + if (in_section != in_sdata) \ + { \ + fprintf (asm_out_file, "%s\n", SDATA_SECTION_ASM_OP); \ + in_section = in_sdata; \ + } \ +} \ + \ +void \ +rdata_section () \ +{ \ + if (in_section != in_rdata) \ + { \ + fprintf (asm_out_file, "%s\n", RDATA_SECTION_ASM_OP); \ + in_section = in_rdata; \ + } \ +} + +/* Given a decl node or constant node, choose the section to output it in + and select that section. */ + +#define SELECT_RTX_SECTION(MODE,RTX) mips_select_rtx_section (MODE, RTX) + +#define SELECT_SECTION(DECL, RELOC) mips_select_section (DECL, RELOC) + + +/* 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 ASM_OUTPUT_REG_PUSH(STREAM,REGNO) \ +do \ + { \ + fprintf (STREAM, "\t%s\t%s,%s,8\n\t%s\t%s,0(%s)\n", \ + TARGET_64BIT ? "dsubu" : "subu", \ + reg_names[STACK_POINTER_REGNUM], \ + reg_names[STACK_POINTER_REGNUM], \ + TARGET_64BIT ? "sd" : "sw", \ + reg_names[REGNO], \ + reg_names[STACK_POINTER_REGNUM]); \ + } \ +while (0) + +#define ASM_OUTPUT_REG_POP(STREAM,REGNO) \ +do \ + { \ + if (! set_noreorder) \ + fprintf (STREAM, "\t.set\tnoreorder\n"); \ + \ + dslots_load_total++; \ + dslots_load_filled++; \ + fprintf (STREAM, "\t%s\t%s,0(%s)\n\t%s\t%s,%s,8\n", \ + TARGET_64BIT ? "ld" : "lw", \ + reg_names[REGNO], \ + reg_names[STACK_POINTER_REGNUM], \ + TARGET_64BIT ? "daddu" : "addu", \ + reg_names[STACK_POINTER_REGNUM], \ + reg_names[STACK_POINTER_REGNUM]); \ + \ + if (! set_noreorder) \ + fprintf (STREAM, "\t.set\treorder\n"); \ + } \ +while (0) + +/* Define the parentheses used to group arithmetic operations + in assembler code. */ + +#define ASM_OPEN_PAREN "(" +#define ASM_CLOSE_PAREN ")" + +/* How to start an assembler comment. + The leading space is important (the mips native assembler requires it). */ +#ifndef ASM_COMMENT_START +#define ASM_COMMENT_START " #" +#endif + + +/* Macros for mips-tfile.c to encapsulate stabs in ECOFF, and for + and mips-tdump.c to print them out. + + These must match the corresponding definitions in gdb/mipsread.c. + Unfortunately, gcc and gdb do not currently share any directories. */ + +#define CODE_MASK 0x8F300 +#define MIPS_IS_STAB(sym) (((sym)->index & 0xFFF00) == CODE_MASK) +#define MIPS_MARK_STAB(code) ((code)+CODE_MASK) +#define MIPS_UNMARK_STAB(code) ((code)-CODE_MASK) + + +/* Default definitions for size_t and ptrdiff_t. */ + +#ifndef SIZE_TYPE +#define NO_BUILTIN_SIZE_TYPE +#define SIZE_TYPE (Pmode == DImode ? "long unsigned int" : "unsigned int") +#endif + +#ifndef PTRDIFF_TYPE +#define NO_BUILTIN_PTRDIFF_TYPE +#define PTRDIFF_TYPE (Pmode == DImode ? "long int" : "int") +#endif + +/* See mips_expand_prologue's use of loadgp for when this should be + true. */ + +#define DONT_ACCESS_GBLS_AFTER_EPILOGUE (TARGET_ABICALLS \ + && mips_abi != ABI_32 \ + && mips_abi != ABI_O64) + +/* In mips16 mode, we need to look through the function to check for + PC relative loads that are out of range. */ +#define MACHINE_DEPENDENT_REORG(X) machine_dependent_reorg (X) + +/* We need to use a special set of functions to handle hard floating + point code in mips16 mode. */ + +#ifndef INIT_SUBTARGET_OPTABS +#define INIT_SUBTARGET_OPTABS +#endif + +#define INIT_TARGET_OPTABS \ +do \ + { \ + if (! TARGET_MIPS16 || ! mips16_hard_float) \ + INIT_SUBTARGET_OPTABS; \ + else \ + { \ + add_optab->handlers[(int) SFmode].libfunc = \ + gen_rtx (SYMBOL_REF, Pmode, "__mips16_addsf3"); \ + sub_optab->handlers[(int) SFmode].libfunc = \ + gen_rtx (SYMBOL_REF, Pmode, "__mips16_subsf3"); \ + smul_optab->handlers[(int) SFmode].libfunc = \ + gen_rtx (SYMBOL_REF, Pmode, "__mips16_mulsf3"); \ + flodiv_optab->handlers[(int) SFmode].libfunc = \ + gen_rtx (SYMBOL_REF, Pmode, "__mips16_divsf3"); \ + \ + eqsf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__mips16_eqsf2"); \ + nesf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__mips16_nesf2"); \ + gtsf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__mips16_gtsf2"); \ + gesf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__mips16_gesf2"); \ + ltsf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__mips16_ltsf2"); \ + lesf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__mips16_lesf2"); \ + \ + floatsisf_libfunc = \ + gen_rtx (SYMBOL_REF, Pmode, "__mips16_floatsisf"); \ + fixsfsi_libfunc = \ + gen_rtx (SYMBOL_REF, Pmode, "__mips16_fixsfsi"); \ + \ + if (TARGET_DOUBLE_FLOAT) \ + { \ + add_optab->handlers[(int) DFmode].libfunc = \ + gen_rtx (SYMBOL_REF, Pmode, "__mips16_adddf3"); \ + sub_optab->handlers[(int) DFmode].libfunc = \ + gen_rtx (SYMBOL_REF, Pmode, "__mips16_subdf3"); \ + smul_optab->handlers[(int) DFmode].libfunc = \ + gen_rtx (SYMBOL_REF, Pmode, "__mips16_muldf3"); \ + flodiv_optab->handlers[(int) DFmode].libfunc = \ + gen_rtx (SYMBOL_REF, Pmode, "__mips16_divdf3"); \ + \ + extendsfdf2_libfunc = \ + gen_rtx (SYMBOL_REF, Pmode, "__mips16_extendsfdf2"); \ + truncdfsf2_libfunc = \ + gen_rtx (SYMBOL_REF, Pmode, "__mips16_truncdfsf2"); \ + \ + eqdf2_libfunc = \ + gen_rtx (SYMBOL_REF, Pmode, "__mips16_eqdf2"); \ + nedf2_libfunc = \ + gen_rtx (SYMBOL_REF, Pmode, "__mips16_nedf2"); \ + gtdf2_libfunc = \ + gen_rtx (SYMBOL_REF, Pmode, "__mips16_gtdf2"); \ + gedf2_libfunc = \ + gen_rtx (SYMBOL_REF, Pmode, "__mips16_gedf2"); \ + ltdf2_libfunc = \ + gen_rtx (SYMBOL_REF, Pmode, "__mips16_ltdf2"); \ + ledf2_libfunc = \ + gen_rtx (SYMBOL_REF, Pmode, "__mips16_ledf2"); \ + \ + floatsidf_libfunc = \ + gen_rtx (SYMBOL_REF, Pmode, "__mips16_floatsidf"); \ + fixdfsi_libfunc = \ + gen_rtx (SYMBOL_REF, Pmode, "__mips16_fixdfsi"); \ + } \ + } \ + } \ +while (0) diff --git a/contrib/gcc/config/mips/mips.md b/contrib/gcc/config/mips/mips.md new file mode 100644 index 000000000000..81c5cd38e381 --- /dev/null +++ b/contrib/gcc/config/mips/mips.md @@ -0,0 +1,10442 @@ +;; Mips.md Machine Description for MIPS based processors +;; Contributed by A. Lichnewsky, lich@inria.inria.fr +;; Changes by Michael Meissner, meissner@osf.org +;; 64 bit r4000 support by Ian Lance Taylor, ian@cygnus.com, and +;; Brendan Eich, brendan@microunity.com. +;; Copyright (C) 1989, 90-98, 1999 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. + +;; ??? Currently does not have define_function_unit support for the R8000. +;; Must include new entries for fmadd in addition to existing entries. + + + +;; .................... +;; +;; Attributes +;; +;; .................... + +;; Classification of each insn. +;; branch conditional branch +;; jump unconditional jump +;; call unconditional call +;; load load instruction(s) +;; store store instruction(s) +;; move data movement within same register set +;; xfer transfer to/from coprocessor +;; hilo transfer of hi/lo registers +;; arith integer arithmetic instruction +;; darith double precision integer arithmetic instructions +;; imul integer multiply +;; idiv integer divide +;; icmp integer compare +;; fadd floating point add/subtract +;; fmul floating point multiply +;; fmadd floating point multiply-add +;; fdiv floating point divide +;; fabs floating point absolute value +;; fneg floating point negation +;; fcmp floating point compare +;; fcvt floating point convert +;; fsqrt floating point square root +;; multi multiword sequence (or user asm statements) +;; nop no operation + +(define_attr "type" + "unknown,branch,jump,call,load,store,move,xfer,hilo,arith,darith,imul,idiv,icmp,fadd,fmul,fmadd,fdiv,fabs,fneg,fcmp,fcvt,fsqrt,multi,nop" + (const_string "unknown")) + +;; Main data type used by the insn +(define_attr "mode" "unknown,none,QI,HI,SI,DI,SF,DF,FPSW" (const_string "unknown")) + +;; # instructions (4 bytes each) +(define_attr "length" "" (const_int 1)) + +;; Attribute describing the processor. This attribute must match exactly +;; with the processor_type enumeration in mips.h. + +;; Attribute describing the processor +;; (define_attr "cpu" "default,r3000,r6000,r4000" +;; (const +;; (cond [(eq (symbol_ref "mips_cpu") (symbol_ref "PROCESSOR_R3000")) (const_string "r3000") +;; (eq (symbol_ref "mips_cpu") (symbol_ref "PROCESSOR_R4000")) (const_string "r4000") +;; (eq (symbol_ref "mips_cpu") (symbol_ref "PROCESSOR_R6000")) (const_string "r6000")] +;; (const_string "default")))) + +;; ??? Fix everything that tests this attribute. +(define_attr "cpu" + "default,r3000,r3900,r6000,r4000,r4100,r4300,r4600,r4650,r5000,r8000" + (const (symbol_ref "mips_cpu_attr"))) + +;; Does the instruction have a mandatory delay slot? +;; The 3900, is (mostly) mips1, but does not have a manditory load delay +;; slot. +(define_attr "dslot" "no,yes" + (if_then_else (ior (eq_attr "type" "branch,jump,call,xfer,hilo,fcmp") + (and (eq_attr "type" "load") + (and (eq (symbol_ref "mips_isa") (const_int 1)) + (and (eq (symbol_ref "mips16") (const_int 0)) + (eq_attr "cpu" "!r3900"))))) + (const_string "yes") + (const_string "no"))) + +;; Attribute defining whether or not we can use the branch-likely instructions + +(define_attr "branch_likely" "no,yes" + (const + (if_then_else (ne (symbol_ref "GENERATE_BRANCHLIKELY") (const_int 0)) + (const_string "yes") + (const_string "no")))) + + +;; Describe a user's asm statement. +(define_asm_attributes + [(set_attr "type" "multi")]) + +;; whether or not generating calls to position independent functions +(define_attr "abicalls" "no,yes" + (const (symbol_ref "mips_abicalls_attr"))) + + + +;; ......................... +;; +;; Delay slots, can't describe load/fcmp/xfer delay slots here +;; +;; ......................... + +(define_delay (and (eq_attr "type" "branch") + (eq (symbol_ref "mips16") (const_int 0))) + [(and (eq_attr "dslot" "no") (eq_attr "length" "1")) + (nil) + (and (eq_attr "branch_likely" "yes") (and (eq_attr "dslot" "no") (eq_attr "length" "1")))]) + +(define_delay (eq_attr "type" "jump") + [(and (eq_attr "dslot" "no") (eq_attr "length" "1")) + (nil) + (nil)]) + +(define_delay (and (eq_attr "type" "call") (eq_attr "abicalls" "no")) + [(and (eq_attr "dslot" "no") (eq_attr "length" "1")) + (nil) + (nil)]) + + + +;; ......................... +;; +;; Functional units +;; +;; ......................... + +; (define_function_unit NAME MULTIPLICITY SIMULTANEITY +; TEST READY-DELAY ISSUE-DELAY [CONFLICT-LIST]) + +;; Make the default case (PROCESSOR_DEFAULT) handle the worst case + +(define_function_unit "memory" 1 0 + (and (eq_attr "type" "load") + (eq_attr "cpu" "!r3000,r3900,r4600,r4650,r4100,r4300,r5000")) + 3 0) + +(define_function_unit "memory" 1 0 + (and (eq_attr "type" "load") + (eq_attr "cpu" "r3000,r3900,r4600,r4650,r4100,r4300,r5000")) + 2 0) + +(define_function_unit "memory" 1 0 (eq_attr "type" "store") 1 0) + +(define_function_unit "memory" 1 0 (eq_attr "type" "xfer") 2 0) + +(define_function_unit "imuldiv" 1 0 + (eq_attr "type" "hilo") + 1 3) + +(define_function_unit "imuldiv" 1 0 + (and (eq_attr "type" "imul") + (eq_attr "cpu" "!r3000,r3900,r4000,r4600,r4650,r4100,r4300,r5000")) + 17 17) + +;; On them mips16, we want to stronly discourage a mult from appearing +;; after an mflo, since that requires explicit nop instructions. We +;; do this by pretending that mflo ties up the function unit for long +;; enough that the scheduler will ignore load stalls and the like when +;; selecting instructions to between the two instructions. + +(define_function_unit "imuldiv" 1 0 + (and (eq_attr "type" "hilo") (ne (symbol_ref "mips16") (const_int 0))) + 1 5) + +(define_function_unit "imuldiv" 1 0 + (and (eq_attr "type" "imul") (eq_attr "cpu" "r3000,r3900")) + 12 12) + +(define_function_unit "imuldiv" 1 0 + (and (eq_attr "type" "imul") (eq_attr "cpu" "r4000,r4600")) + 10 10) + +(define_function_unit "imuldiv" 1 0 + (and (eq_attr "type" "imul") (eq_attr "cpu" "r4650")) + 4 4) + +(define_function_unit "imuldiv" 1 0 + (and (eq_attr "type" "imul") + (and (eq_attr "mode" "SI") (eq_attr "cpu" "r4100"))) + 1 1) + +(define_function_unit "imuldiv" 1 0 + (and (eq_attr "type" "imul") + (and (eq_attr "mode" "DI") (eq_attr "cpu" "r4100"))) + 4 4) + +(define_function_unit "imuldiv" 1 0 + (and (eq_attr "type" "imul") + (and (eq_attr "mode" "SI") (eq_attr "cpu" "r4300,r5000"))) + 5 5) + +(define_function_unit "imuldiv" 1 0 + (and (eq_attr "type" "imul") + (and (eq_attr "mode" "DI") (eq_attr "cpu" "r4300"))) + 8 8) + +(define_function_unit "imuldiv" 1 0 + (and (eq_attr "type" "imul") + (and (eq_attr "mode" "DI") (eq_attr "cpu" "r5000"))) + 9 9) + +(define_function_unit "imuldiv" 1 0 + (and (eq_attr "type" "idiv") + (eq_attr "cpu" "!r3000,r3900,r4000,r4600,r4650,r4100,r4300,r5000")) + 38 38) + +(define_function_unit "imuldiv" 1 0 + (and (eq_attr "type" "idiv") (eq_attr "cpu" "r3000,r3900")) + 35 35) + +(define_function_unit "imuldiv" 1 0 + (and (eq_attr "type" "idiv") (eq_attr "cpu" "r4600")) + 42 42) + +(define_function_unit "imuldiv" 1 0 + (and (eq_attr "type" "idiv") (eq_attr "cpu" "r4650")) + 36 36) + +(define_function_unit "imuldiv" 1 0 + (and (eq_attr "type" "idiv") (eq_attr "cpu" "r4000")) + 69 69) + +(define_function_unit "imuldiv" 1 0 + (and (eq_attr "type" "idiv") + (and (eq_attr "mode" "SI") (eq_attr "cpu" "r4100"))) + 35 35) + +(define_function_unit "imuldiv" 1 0 + (and (eq_attr "type" "idiv") + (and (eq_attr "mode" "DI") (eq_attr "cpu" "r4100"))) + 67 67) + +(define_function_unit "imuldiv" 1 0 + (and (eq_attr "type" "idiv") + (and (eq_attr "mode" "SI") (eq_attr "cpu" "r4300"))) + 37 37) + +(define_function_unit "imuldiv" 1 0 + (and (eq_attr "type" "idiv") + (and (eq_attr "mode" "DI") (eq_attr "cpu" "r4300"))) + 69 69) + +(define_function_unit "imuldiv" 1 0 + (and (eq_attr "type" "idiv") + (and (eq_attr "mode" "SI") (eq_attr "cpu" "r5000"))) + 36 36) + +(define_function_unit "imuldiv" 1 0 + (and (eq_attr "type" "idiv") + (and (eq_attr "mode" "DI") (eq_attr "cpu" "r5000"))) + 68 68) + +;; The R4300 does *NOT* have a separate Floating Point Unit, instead +;; the FP hardware is part of the normal ALU circuitry. This means FP +;; instructions affect the pipe-line, and no functional unit +;; parallelism can occur on R4300 processors. To force GCC into coding +;; for only a single functional unit, we force the R4300 FP +;; instructions to be processed in the "imuldiv" unit. + +(define_function_unit "adder" 1 1 + (and (eq_attr "type" "fcmp") (eq_attr "cpu" "!r3000,r3900,r6000,r4300,r5000")) + 3 0) + +(define_function_unit "adder" 1 1 + (and (eq_attr "type" "fcmp") (eq_attr "cpu" "r3000,r3900,r6000")) + 2 0) + +(define_function_unit "adder" 1 1 + (and (eq_attr "type" "fcmp") (eq_attr "cpu" "r5000")) + 1 0) + +(define_function_unit "adder" 1 1 + (and (eq_attr "type" "fadd") (eq_attr "cpu" "!r3000,r3900,r6000,r4300")) + 4 0) + +(define_function_unit "adder" 1 1 + (and (eq_attr "type" "fadd") (eq_attr "cpu" "r3000,r3900")) + 2 0) + +(define_function_unit "adder" 1 1 + (and (eq_attr "type" "fadd") (eq_attr "cpu" "r6000")) + 3 0) + +(define_function_unit "adder" 1 1 + (and (eq_attr "type" "fabs,fneg") + (eq_attr "cpu" "!r3000,r3900,r4600,r4650,r4300,r5000")) + 2 0) + +(define_function_unit "adder" 1 1 + (and (eq_attr "type" "fabs,fneg") (eq_attr "cpu" "r3000,r3900,r4600,r4650,r5000")) + 1 0) + +(define_function_unit "mult" 1 1 + (and (eq_attr "type" "fmul") + (and (eq_attr "mode" "SF") + (eq_attr "cpu" "!r3000,r3900,r6000,r4600,r4650,r4300,r5000"))) + 7 0) + +(define_function_unit "mult" 1 1 + (and (eq_attr "type" "fmul") + (and (eq_attr "mode" "SF") (eq_attr "cpu" "r3000,r3900,r5000"))) + 4 0) + +(define_function_unit "mult" 1 1 + (and (eq_attr "type" "fmul") + (and (eq_attr "mode" "SF") (eq_attr "cpu" "r6000"))) + 5 0) + +(define_function_unit "mult" 1 1 + (and (eq_attr "type" "fmul") + (and (eq_attr "mode" "SF") (eq_attr "cpu" "r4600,r4650"))) + 8 0) + +(define_function_unit "mult" 1 1 + (and (eq_attr "type" "fmul") + (and (eq_attr "mode" "DF") (eq_attr "cpu" "!r3000,r3900,r6000,r4300,r5000"))) + 8 0) + +(define_function_unit "mult" 1 1 + (and (eq_attr "type" "fmul") + (and (eq_attr "mode" "DF") (eq_attr "cpu" "r3000,r3900,r5000"))) + 5 0) + +(define_function_unit "mult" 1 1 + (and (eq_attr "type" "fmul") + (and (eq_attr "mode" "DF") (eq_attr "cpu" "r6000"))) + 6 0) + +(define_function_unit "divide" 1 1 + (and (eq_attr "type" "fdiv") + (and (eq_attr "mode" "SF") + (eq_attr "cpu" "!r3000,r3900,r6000,r4600,r4650,r4300,r5000"))) + 23 0) + +(define_function_unit "divide" 1 1 + (and (eq_attr "type" "fdiv") + (and (eq_attr "mode" "SF") (eq_attr "cpu" "r3000,r3900"))) + 12 0) + +(define_function_unit "divide" 1 1 + (and (eq_attr "type" "fdiv") + (and (eq_attr "mode" "SF") (eq_attr "cpu" "r6000"))) + 15 0) + +(define_function_unit "divide" 1 1 + (and (eq_attr "type" "fdiv") + (and (eq_attr "mode" "SF") (eq_attr "cpu" "r4600,r4650"))) + 32 0) + +(define_function_unit "divide" 1 1 + (and (eq_attr "type" "fdiv") + (and (eq_attr "mode" "SF") (eq_attr "cpu" "r5000"))) + 21 0) + +(define_function_unit "divide" 1 1 + (and (eq_attr "type" "fdiv") + (and (eq_attr "mode" "DF") + (eq_attr "cpu" "!r3000,r3900,r6000,r4600,r4650,r4300"))) + 36 0) + +(define_function_unit "divide" 1 1 + (and (eq_attr "type" "fdiv") + (and (eq_attr "mode" "DF") (eq_attr "cpu" "r3000,r3900"))) + 19 0) + +(define_function_unit "divide" 1 1 + (and (eq_attr "type" "fdiv") + (and (eq_attr "mode" "DF") (eq_attr "cpu" "r6000"))) + 16 0) + +(define_function_unit "divide" 1 1 + (and (eq_attr "type" "fdiv") + (and (eq_attr "mode" "DF") (eq_attr "cpu" "r4600,r4650"))) + 61 0) + +;;; ??? Is this number right? +(define_function_unit "divide" 1 1 + (and (eq_attr "type" "fsqrt") + (and (eq_attr "mode" "SF") (eq_attr "cpu" "!r4600,r4650,r4300,r5000"))) + 54 0) + +(define_function_unit "divide" 1 1 + (and (eq_attr "type" "fsqrt") + (and (eq_attr "mode" "SF") (eq_attr "cpu" "r4600,r4650"))) + 31 0) + +(define_function_unit "divide" 1 1 + (and (eq_attr "type" "fsqrt") + (and (eq_attr "mode" "SF") (eq_attr "cpu" "r5000"))) + 21 0) + +;;; ??? Is this number right? +(define_function_unit "divide" 1 1 + (and (eq_attr "type" "fsqrt") + (and (eq_attr "mode" "DF") (eq_attr "cpu" "!r4600,r4650,r4300,r5000"))) + 112 0) + +(define_function_unit "divide" 1 1 + (and (eq_attr "type" "fsqrt") + (and (eq_attr "mode" "DF") (eq_attr "cpu" "r4600,r4650"))) + 60 0) + +(define_function_unit "divide" 1 1 + (and (eq_attr "type" "fsqrt") + (and (eq_attr "mode" "DF") (eq_attr "cpu" "r5000"))) + 36 0) + +;; R4300 FP instruction classes treated as part of the "imuldiv" +;; functional unit: + +(define_function_unit "imuldiv" 1 0 + (and (eq_attr "type" "fadd") (eq_attr "cpu" "r4300")) + 3 3) + +(define_function_unit "imuldiv" 1 0 + (and (eq_attr "type" "fcmp,fabs,fneg") (eq_attr "cpu" "r4300")) + 1 1) + +(define_function_unit "imuldiv" 1 0 + (and (eq_attr "type" "fmul") (and (eq_attr "mode" "SF") (eq_attr "cpu" "r4300"))) + 5 5) +(define_function_unit "imuldiv" 1 0 + (and (eq_attr "type" "fmul") (and (eq_attr "mode" "DF") (eq_attr "cpu" "r4300"))) + 8 8) + +(define_function_unit "imuldiv" 1 0 + (and (and (eq_attr "type" "fdiv") (eq_attr "type" "fsqrt")) + (and (eq_attr "mode" "SF") (eq_attr "cpu" "r4300"))) + 29 29) +(define_function_unit "imuldiv" 1 0 + (and (and (eq_attr "type" "fdiv") (eq_attr "type" "fsqrt")) + (and (eq_attr "mode" "DF") (eq_attr "cpu" "r4300"))) + 58 58) + +;; The following functional units do not use the cpu type, and use +;; much less memory in genattrtab.c. + +;; (define_function_unit "memory" 1 0 (eq_attr "type" "load") 3 0) +;; (define_function_unit "memory" 1 0 (eq_attr "type" "store") 1 0) +;; +;; (define_function_unit "fp_comp" 1 0 (eq_attr "type" "fcmp") 2 0) +;; +;; (define_function_unit "transfer" 1 0 (eq_attr "type" "xfer") 2 0) +;; (define_function_unit "transfer" 1 0 (eq_attr "type" "hilo") 3 0) +;; +;; (define_function_unit "imuldiv" 1 1 (eq_attr "type" "imul") 17 0) +;; (define_function_unit "imuldiv" 1 1 (eq_attr "type" "idiv") 38 0) +;; +;; (define_function_unit "adder" 1 1 (eq_attr "type" "fadd") 4 0) +;; (define_function_unit "adder" 1 1 (eq_attr "type" "fabs,fneg") 2 0) +;; +;; (define_function_unit "mult" 1 1 (and (eq_attr "type" "fmul") (eq_attr "mode" "SF")) 7 0) +;; (define_function_unit "mult" 1 1 (and (eq_attr "type" "fmul") (eq_attr "mode" "DF")) 8 0) +;; +;; (define_function_unit "divide" 1 1 (and (eq_attr "type" "fdiv") (eq_attr "mode" "SF")) 23 0) +;; (define_function_unit "divide" 1 1 (and (eq_attr "type" "fdiv") (eq_attr "mode" "DF")) 36 0) +;; +;; (define_function_unit "sqrt" 1 1 (and (eq_attr "type" "fsqrt") (eq_attr "mode" "SF")) 54 0) +;; (define_function_unit "sqrt" 1 1 (and (eq_attr "type" "fsqrt") (eq_attr "mode" "DF")) 112 0) + + +;; +;; .................... +;; +;; ADDITION +;; +;; .................... +;; + +(define_insn "adddf3" + [(set (match_operand:DF 0 "register_operand" "=f") + (plus:DF (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT" + "add.d\\t%0,%1,%2" + [(set_attr "type" "fadd") + (set_attr "mode" "DF") + (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_HARD_FLOAT" + "add.s\\t%0,%1,%2" + [(set_attr "type" "fadd") + (set_attr "mode" "SF") + (set_attr "length" "1")]) + +(define_expand "addsi3" + [(set (match_operand:SI 0 "register_operand" "=d") + (plus:SI (match_operand:SI 1 "reg_or_0_operand" "dJ") + (match_operand:SI 2 "arith_operand" "dI")))] + "" + " +{ + /* The mips16 assembler handles -32768 correctly, and so does gas, + but some other MIPS assemblers think that -32768 needs to be + loaded into a register before it can be added in. */ + if (! TARGET_MIPS16 + && ! TARGET_GAS + && GET_CODE (operands[2]) == CONST_INT + && INTVAL (operands[2]) == -32768) + operands[2] = force_reg (SImode, operands[2]); +}") + +(define_insn "addsi3_internal" + [(set (match_operand:SI 0 "register_operand" "=d") + (plus:SI (match_operand:SI 1 "reg_or_0_operand" "dJ") + (match_operand:SI 2 "arith_operand" "dI")))] + "! TARGET_MIPS16 + && (TARGET_GAS + || GET_CODE (operands[2]) != CONST_INT + || INTVAL (operands[2]) != -32768)" + "addu\\t%0,%z1,%2" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +;; For the mips16, we need to recognize stack pointer additions +;; explicitly, since we don't have a constraint for $sp. These insns +;; will be generated by the save_restore_insns functions. + +(define_insn "" + [(set (reg:SI 29) + (plus:SI (reg:SI 29) + (match_operand:SI 0 "small_int" "I")))] + "TARGET_MIPS16" + "addu\\t%$,%$,%0" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set (attr "length") (if_then_else (match_operand:VOID 0 "m16_simm8_8" "") + (const_int 1) + (const_int 2)))]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d") + (plus:SI (reg:SI 29) + (match_operand:SI 1 "small_int" "I")))] + "TARGET_MIPS16" + "addu\\t%0,%$,%1" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set (attr "length") (if_then_else (match_operand:VOID 1 "m16_uimm8_4" "") + (const_int 1) + (const_int 2)))]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d,d,d") + (plus:SI (match_operand:SI 1 "register_operand" "0,d,d") + (match_operand:SI 2 "arith_operand" "IQ,O,d")))] + "TARGET_MIPS16 + && (GET_CODE (operands[1]) != REG + || REGNO (operands[1]) >= FIRST_PSEUDO_REGISTER + || M16_REG_P (REGNO (operands[1])) + || REGNO (operands[1]) == ARG_POINTER_REGNUM + || REGNO (operands[1]) == FRAME_POINTER_REGNUM + || REGNO (operands[1]) == STACK_POINTER_REGNUM) + && (GET_CODE (operands[2]) != REG + || REGNO (operands[2]) >= FIRST_PSEUDO_REGISTER + || M16_REG_P (REGNO (operands[2])) + || REGNO (operands[2]) == ARG_POINTER_REGNUM + || REGNO (operands[2]) == FRAME_POINTER_REGNUM + || REGNO (operands[2]) == STACK_POINTER_REGNUM)" + "* +{ + if (REGNO (operands[0]) == REGNO (operands[1])) + return \"addu\\t%0,%2\"; + return \"addu\\t%0,%1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr_alternative "length" + [(if_then_else (match_operand:VOID 2 "m16_simm8_1" "") + (const_int 1) + (const_int 2)) + (if_then_else (match_operand:VOID 2 "m16_simm4_1" "") + (const_int 1) + (const_int 2)) + (const_int 1)])]) + + +;; On the mips16, we can sometimes split an add of a constant which is +;; a 4 byte instruction into two adds which are both 2 byte +;; instructions. There are two cases: one where we are adding a +;; constant plus a register to another register, and one where we are +;; simply adding a constant to a register. + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (plus:SI (match_dup 0) + (match_operand:SI 1 "const_int_operand" "")))] + "TARGET_MIPS16 && reload_completed + && GET_CODE (operands[0]) == REG + && M16_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == CONST_INT + && ((INTVAL (operands[1]) > 0x7f + && INTVAL (operands[1]) <= 0x7f + 0x7f) + || (INTVAL (operands[1]) < - 0x80 + && INTVAL (operands[1]) >= - 0x80 - 0x80))" + [(set (match_dup 0) (plus:SI (match_dup 0) (match_dup 1))) + (set (match_dup 0) (plus:SI (match_dup 0) (match_dup 2)))] + " +{ + HOST_WIDE_INT val = INTVAL (operands[1]); + + if (val >= 0) + { + operands[1] = GEN_INT (0x7f); + operands[2] = GEN_INT (val - 0x7f); + } + else + { + operands[1] = GEN_INT (- 0x80); + operands[2] = GEN_INT (val + 0x80); + } +}") + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (plus:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "const_int_operand" "")))] + "TARGET_MIPS16 && reload_completed + && GET_CODE (operands[0]) == REG + && M16_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == REG + && M16_REG_P (REGNO (operands[1])) + && REGNO (operands[0]) != REGNO (operands[1]) + && GET_CODE (operands[2]) == CONST_INT + && ((INTVAL (operands[2]) > 0x7 + && INTVAL (operands[2]) <= 0x7 + 0x7f) + || (INTVAL (operands[2]) < - 0x8 + && INTVAL (operands[2]) >= - 0x8 - 0x80))" + [(set (match_dup 0) (plus:SI (match_dup 1) (match_dup 2))) + (set (match_dup 0) (plus:SI (match_dup 0) (match_dup 3)))] + " +{ + HOST_WIDE_INT val = INTVAL (operands[2]); + + if (val >= 0) + { + operands[2] = GEN_INT (0x7); + operands[3] = GEN_INT (val - 0x7); + } + else + { + operands[2] = GEN_INT (- 0x8); + operands[3] = GEN_INT (val + 0x8); + } +}") + +(define_expand "adddi3" + [(parallel [(set (match_operand:DI 0 "register_operand" "") + (plus:DI (match_operand:DI 1 "se_register_operand" "") + (match_operand:DI 2 "se_arith_operand" ""))) + (clobber (match_dup 3))])] + "TARGET_64BIT || (!TARGET_DEBUG_G_MODE && !TARGET_MIPS16)" + " +{ + /* The mips16 assembler handles -32768 correctly, and so does gas, + but some other MIPS assemblers think that -32768 needs to be + loaded into a register before it can be added in. */ + if (! TARGET_MIPS16 + && ! TARGET_GAS + && GET_CODE (operands[2]) == CONST_INT + && INTVAL (operands[2]) == -32768) + operands[2] = force_reg (DImode, operands[2]); + + if (TARGET_64BIT) + { + emit_insn (gen_adddi3_internal_3 (operands[0], operands[1], + operands[2])); + DONE; + } + + operands[3] = gen_reg_rtx (SImode); +}") + +(define_insn "adddi3_internal_1" + [(set (match_operand:DI 0 "register_operand" "=d,&d") + (plus:DI (match_operand:DI 1 "register_operand" "0,d") + (match_operand:DI 2 "register_operand" "d,d"))) + (clobber (match_operand:SI 3 "register_operand" "=d,d"))] + "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16" + "* +{ + return (REGNO (operands[0]) == REGNO (operands[1]) + && REGNO (operands[0]) == REGNO (operands[2])) + ? \"srl\\t%3,%L0,31\;sll\\t%M0,%M0,1\;sll\\t%L0,%L1,1\;addu\\t%M0,%M0,%3\" + : \"addu\\t%L0,%L1,%L2\;sltu\\t%3,%L0,%L2\;addu\\t%M0,%M1,%M2\;addu\\t%M0,%M0,%3\"; +}" + [(set_attr "type" "darith") + (set_attr "mode" "DI") + (set_attr "length" "4")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (plus:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "register_operand" ""))) + (clobber (match_operand:SI 3 "register_operand" ""))] + "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT + && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16 + && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1])) + && GET_CODE (operands[2]) == REG && GP_REG_P (REGNO (operands[2])) + && (REGNO (operands[0]) != REGNO (operands[1]) + || REGNO (operands[0]) != REGNO (operands[2]))" + + [(set (subreg:SI (match_dup 0) 0) + (plus:SI (subreg:SI (match_dup 1) 0) + (subreg:SI (match_dup 2) 0))) + + (set (match_dup 3) + (ltu:SI (subreg:SI (match_dup 0) 0) + (subreg:SI (match_dup 2) 0))) + + (set (subreg:SI (match_dup 0) 1) + (plus:SI (subreg:SI (match_dup 1) 1) + (subreg:SI (match_dup 2) 1))) + + (set (subreg:SI (match_dup 0) 1) + (plus:SI (subreg:SI (match_dup 0) 1) + (match_dup 3)))] + "") + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (plus:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "register_operand" ""))) + (clobber (match_operand:SI 3 "register_operand" ""))] + "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT + && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16 + && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1])) + && GET_CODE (operands[2]) == REG && GP_REG_P (REGNO (operands[2])) + && (REGNO (operands[0]) != REGNO (operands[1]) + || REGNO (operands[0]) != REGNO (operands[2]))" + + [(set (subreg:SI (match_dup 0) 1) + (plus:SI (subreg:SI (match_dup 1) 1) + (subreg:SI (match_dup 2) 1))) + + (set (match_dup 3) + (ltu:SI (subreg:SI (match_dup 0) 1) + (subreg:SI (match_dup 2) 1))) + + (set (subreg:SI (match_dup 0) 0) + (plus:SI (subreg:SI (match_dup 1) 0) + (subreg:SI (match_dup 2) 0))) + + (set (subreg:SI (match_dup 0) 0) + (plus:SI (subreg:SI (match_dup 0) 0) + (match_dup 3)))] + "") + +(define_insn "adddi3_internal_2" + [(set (match_operand:DI 0 "register_operand" "=d,d,d") + (plus:DI (match_operand:DI 1 "register_operand" "%d,%d,%d") + (match_operand:DI 2 "small_int" "P,J,N"))) + (clobber (match_operand:SI 3 "register_operand" "=d,d,d"))] + "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16 + && (TARGET_GAS + || GET_CODE (operands[2]) != CONST_INT + || INTVAL (operands[2]) != -32768)" + "@ + addu\\t%L0,%L1,%2\;sltu\\t%3,%L0,%2\;addu\\t%M0,%M1,%3 + move\\t%L0,%L1\;move\\t%M0,%M1 + subu\\t%L0,%L1,%n2\;sltu\\t%3,%L0,%2\;subu\\t%M0,%M1,1\;addu\\t%M0,%M0,%3" + [(set_attr "type" "darith") + (set_attr "mode" "DI") + (set_attr "length" "3,2,4")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (plus:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "small_int" ""))) + (clobber (match_operand:SI 3 "register_operand" "=d"))] + "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT + && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16 + && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1])) + && INTVAL (operands[2]) > 0" + + [(set (subreg:SI (match_dup 0) 0) + (plus:SI (subreg:SI (match_dup 1) 0) + (match_dup 2))) + + (set (match_dup 3) + (ltu:SI (subreg:SI (match_dup 0) 0) + (match_dup 2))) + + (set (subreg:SI (match_dup 0) 1) + (plus:SI (subreg:SI (match_dup 1) 1) + (match_dup 3)))] + "") + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (plus:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "small_int" ""))) + (clobber (match_operand:SI 3 "register_operand" "=d"))] + "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT + && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16 + && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1])) + && INTVAL (operands[2]) > 0" + + [(set (subreg:SI (match_dup 0) 1) + (plus:SI (subreg:SI (match_dup 1) 1) + (match_dup 2))) + + (set (match_dup 3) + (ltu:SI (subreg:SI (match_dup 0) 1) + (match_dup 2))) + + (set (subreg:SI (match_dup 0) 0) + (plus:SI (subreg:SI (match_dup 1) 0) + (match_dup 3)))] + "") + +(define_insn "adddi3_internal_3" + [(set (match_operand:DI 0 "register_operand" "=d") + (plus:DI (match_operand:DI 1 "se_reg_or_0_operand" "dJ") + (match_operand:DI 2 "se_arith_operand" "dI")))] + "TARGET_64BIT + && !TARGET_MIPS16 + && (TARGET_GAS + || GET_CODE (operands[2]) != CONST_INT + || INTVAL (operands[2]) != -32768)" + "* +{ + return (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0) + ? \"dsubu\\t%0,%z1,%n2\" + : \"daddu\\t%0,%z1,%2\"; +}" + [(set_attr "type" "darith") + (set_attr "mode" "DI") + (set_attr "length" "1")]) + +;; For the mips16, we need to recognize stack pointer additions +;; explicitly, since we don't have a constraint for $sp. These insns +;; will be generated by the save_restore_insns functions. + +(define_insn "" + [(set (reg:DI 29) + (plus:DI (reg:DI 29) + (match_operand:DI 0 "small_int" "I")))] + "TARGET_MIPS16 && TARGET_64BIT" + "daddu\\t%$,%$,%0" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set (attr "length") (if_then_else (match_operand:VOID 0 "m16_simm8_8" "") + (const_int 1) + (const_int 2)))]) + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=d") + (plus:DI (reg:DI 29) + (match_operand:DI 1 "small_int" "I")))] + "TARGET_MIPS16 && TARGET_64BIT" + "daddu\\t%0,%$,%1" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set (attr "length") (if_then_else (match_operand:VOID 0 "m16_uimm5_4" "") + (const_int 1) + (const_int 2)))]) + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=d,d,d") + (plus:DI (match_operand:DI 1 "register_operand" "0,d,d") + (match_operand:DI 2 "arith_operand" "IQ,O,d")))] + "TARGET_MIPS16 && TARGET_64BIT + && (GET_CODE (operands[1]) != REG + || REGNO (operands[1]) >= FIRST_PSEUDO_REGISTER + || M16_REG_P (REGNO (operands[1])) + || REGNO (operands[1]) == ARG_POINTER_REGNUM + || REGNO (operands[1]) == FRAME_POINTER_REGNUM + || REGNO (operands[1]) == STACK_POINTER_REGNUM) + && (GET_CODE (operands[2]) != REG + || REGNO (operands[2]) >= FIRST_PSEUDO_REGISTER + || M16_REG_P (REGNO (operands[2])) + || REGNO (operands[2]) == ARG_POINTER_REGNUM + || REGNO (operands[2]) == FRAME_POINTER_REGNUM + || REGNO (operands[2]) == STACK_POINTER_REGNUM)" + "* +{ + if (REGNO (operands[0]) == REGNO (operands[1])) + return \"daddu\\t%0,%2\"; + return \"daddu\\t%0,%1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr_alternative "length" + [(if_then_else (match_operand:VOID 2 "m16_simm5_1" "") + (const_int 1) + (const_int 2)) + (if_then_else (match_operand:VOID 2 "m16_simm4_1" "") + (const_int 1) + (const_int 2)) + (const_int 1)])]) + + +;; On the mips16, we can sometimes split an add of a constant which is +;; a 4 byte instruction into two adds which are both 2 byte +;; instructions. There are two cases: one where we are adding a +;; constant plus a register to another register, and one where we are +;; simply adding a constant to a register. + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (plus:DI (match_dup 0) + (match_operand:DI 1 "const_int_operand" "")))] + "TARGET_MIPS16 && TARGET_64BIT && reload_completed + && GET_CODE (operands[0]) == REG + && M16_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == CONST_INT + && ((INTVAL (operands[1]) > 0xf + && INTVAL (operands[1]) <= 0xf + 0xf) + || (INTVAL (operands[1]) < - 0x10 + && INTVAL (operands[1]) >= - 0x10 - 0x10))" + [(set (match_dup 0) (plus:DI (match_dup 0) (match_dup 1))) + (set (match_dup 0) (plus:DI (match_dup 0) (match_dup 2)))] + " +{ + HOST_WIDE_INT val = INTVAL (operands[1]); + + if (val >= 0) + { + operands[1] = GEN_INT (0xf); + operands[2] = GEN_INT (val - 0xf); + } + else + { + operands[1] = GEN_INT (- 0x10); + operands[2] = GEN_INT (val + 0x10); + } +}") + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (plus:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "const_int_operand" "")))] + "TARGET_MIPS16 && TARGET_64BIT && reload_completed + && GET_CODE (operands[0]) == REG + && M16_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == REG + && M16_REG_P (REGNO (operands[1])) + && REGNO (operands[0]) != REGNO (operands[1]) + && GET_CODE (operands[2]) == CONST_INT + && ((INTVAL (operands[2]) > 0x7 + && INTVAL (operands[2]) <= 0x7 + 0xf) + || (INTVAL (operands[2]) < - 0x8 + && INTVAL (operands[2]) >= - 0x8 - 0x10))" + [(set (match_dup 0) (plus:DI (match_dup 1) (match_dup 2))) + (set (match_dup 0) (plus:DI (match_dup 0) (match_dup 3)))] + " +{ + HOST_WIDE_INT val = INTVAL (operands[2]); + + if (val >= 0) + { + operands[2] = GEN_INT (0x7); + operands[3] = GEN_INT (val - 0x7); + } + else + { + operands[2] = GEN_INT (- 0x8); + operands[3] = GEN_INT (val + 0x8); + } +}") + +(define_insn "addsi3_internal_2" + [(set (match_operand:DI 0 "register_operand" "=d") + (sign_extend:DI (plus:SI (match_operand:SI 1 "reg_or_0_operand" "dJ") + (match_operand:SI 2 "arith_operand" "dI"))))] + "TARGET_64BIT + && !TARGET_MIPS16 + && (TARGET_GAS + || GET_CODE (operands[2]) != CONST_INT + || INTVAL (operands[2]) != -32768)" + "* +{ + return (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0) + ? \"subu\\t%0,%z1,%n2\" + : \"addu\\t%0,%z1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=d,d,d") + (sign_extend:DI (plus:SI (match_operand:SI 1 "register_operand" "0,d,d") + (match_operand:SI 2 "arith_operand" "I,O,d"))))] + "TARGET_MIPS16 && TARGET_64BIT" + "* +{ + if (REGNO (operands[0]) == REGNO (operands[1])) + return \"addu\\t%0,%2\"; + return \"addu\\t%0,%1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr_alternative "length" + [(if_then_else (match_operand:VOID 2 "m16_simm8_1" "") + (const_int 1) + (const_int 2)) + (if_then_else (match_operand:VOID 2 "m16_simm4_1" "") + (const_int 1) + (const_int 2)) + (const_int 1)])]) + + +;; +;; .................... +;; +;; SUBTRACTION +;; +;; .................... +;; + +(define_insn "subdf3" + [(set (match_operand:DF 0 "register_operand" "=f") + (minus:DF (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT" + "sub.d\\t%0,%1,%2" + [(set_attr "type" "fadd") + (set_attr "mode" "DF") + (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_HARD_FLOAT" + "sub.s\\t%0,%1,%2" + [(set_attr "type" "fadd") + (set_attr "mode" "SF") + (set_attr "length" "1")]) + +(define_expand "subsi3" + [(set (match_operand:SI 0 "register_operand" "=d") + (minus:SI (match_operand:SI 1 "reg_or_0_operand" "dJ") + (match_operand:SI 2 "arith_operand" "dI")))] + "" + " +{ + if (GET_CODE (operands[2]) == CONST_INT + && (INTVAL (operands[2]) == -32768 + || (TARGET_MIPS16 + && INTVAL (operands[2]) == -0x4000))) + operands[2] = force_reg (SImode, operands[2]); +}") + +(define_insn "subsi3_internal" + [(set (match_operand:SI 0 "register_operand" "=d") + (minus:SI (match_operand:SI 1 "reg_or_0_operand" "dJ") + (match_operand:SI 2 "arith_operand" "dI")))] + "!TARGET_MIPS16 + && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != -32768)" + "subu\\t%0,%z1,%2" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +;; For the mips16, we need to recognize stack pointer subtractions +;; explicitly, since we don't have a constraint for $sp. These insns +;; will be generated by the save_restore_insns functions. + +(define_insn "" + [(set (reg:SI 29) + (minus:SI (reg:SI 29) + (match_operand:SI 0 "small_int" "I")))] + "TARGET_MIPS16 + && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != -32768)" + "addu\\t%$,%$,%n0" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set (attr "length") (if_then_else (match_operand:VOID 0 "m16_nsimm8_8" "") + (const_int 1) + (const_int 2)))]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d") + (minus:SI (reg:SI 29) + (match_operand:SI 1 "small_int" "I")))] + "TARGET_MIPS16 + && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != -32768)" + "addu\\t%0,%$,%n1" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set (attr "length") (if_then_else (match_operand:VOID 1 "m16_nuimm8_4" "") + (const_int 1) + (const_int 2)))]) + + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d,d,d") + (minus:SI (match_operand:SI 1 "register_operand" "0,d,d") + (match_operand:SI 2 "arith_operand" "I,O,d")))] + "TARGET_MIPS16 + && (GET_CODE (operands[2]) != CONST_INT + || (INTVAL (operands[2]) != -32768 && INTVAL (operands[2]) != -0x4000))" + "* +{ + if (REGNO (operands[0]) == REGNO (operands[1])) + return \"subu\\t%0,%2\"; + return \"subu\\t%0,%1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr_alternative "length" + [(if_then_else (match_operand:VOID 2 "m16_nsimm8_1" "") + (const_int 1) + (const_int 2)) + (if_then_else (match_operand:VOID 2 "m16_nsimm4_1" "") + (const_int 1) + (const_int 2)) + (const_int 1)])]) + +;; On the mips16, we can sometimes split an subtract of a constant +;; which is a 4 byte instruction into two adds which are both 2 byte +;; instructions. There are two cases: one where we are setting a +;; register to a register minus a constant, and one where we are +;; simply subtracting a constant from a register. + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (minus:SI (match_dup 0) + (match_operand:SI 1 "const_int_operand" "")))] + "TARGET_MIPS16 && reload_completed + && GET_CODE (operands[0]) == REG + && M16_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == CONST_INT + && ((INTVAL (operands[1]) > 0x80 + && INTVAL (operands[1]) <= 0x80 + 0x80) + || (INTVAL (operands[1]) < - 0x7f + && INTVAL (operands[1]) >= - 0x7f - 0x7f))" + [(set (match_dup 0) (minus:SI (match_dup 0) (match_dup 1))) + (set (match_dup 0) (minus:SI (match_dup 0) (match_dup 2)))] + " +{ + HOST_WIDE_INT val = INTVAL (operands[1]); + + if (val >= 0) + { + operands[1] = GEN_INT (0x80); + operands[2] = GEN_INT (val - 0x80); + } + else + { + operands[1] = GEN_INT (- 0x7f); + operands[2] = GEN_INT (val + 0x7f); + } +}") + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (minus:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "const_int_operand" "")))] + "TARGET_MIPS16 && reload_completed + && GET_CODE (operands[0]) == REG + && M16_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == REG + && M16_REG_P (REGNO (operands[1])) + && REGNO (operands[0]) != REGNO (operands[1]) + && GET_CODE (operands[2]) == CONST_INT + && ((INTVAL (operands[2]) > 0x8 + && INTVAL (operands[2]) <= 0x8 + 0x80) + || (INTVAL (operands[2]) < - 0x7 + && INTVAL (operands[2]) >= - 0x7 - 0x7f))" + [(set (match_dup 0) (minus:SI (match_dup 1) (match_dup 2))) + (set (match_dup 0) (minus:SI (match_dup 0) (match_dup 3)))] + " +{ + HOST_WIDE_INT val = INTVAL (operands[2]); + + if (val >= 0) + { + operands[2] = GEN_INT (0x8); + operands[3] = GEN_INT (val - 0x8); + } + else + { + operands[2] = GEN_INT (- 0x7); + operands[3] = GEN_INT (val + 0x7); + } +}") + +(define_expand "subdi3" + [(parallel [(set (match_operand:DI 0 "register_operand" "=d") + (minus:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_register_operand" "d"))) + (clobber (match_dup 3))])] + "TARGET_64BIT || (!TARGET_DEBUG_G_MODE && !TARGET_MIPS16)" + " +{ + if (TARGET_64BIT) + { + emit_insn (gen_subdi3_internal_3 (operands[0], operands[1], + operands[2])); + DONE; + } + + operands[3] = gen_reg_rtx (SImode); +}") + +(define_insn "subdi3_internal" + [(set (match_operand:DI 0 "register_operand" "=d") + (minus:DI (match_operand:DI 1 "register_operand" "d") + (match_operand:DI 2 "register_operand" "d"))) + (clobber (match_operand:SI 3 "register_operand" "=d"))] + "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16" + "sltu\\t%3,%L1,%L2\;subu\\t%L0,%L1,%L2\;subu\\t%M0,%M1,%M2\;subu\\t%M0,%M0,%3" + [(set_attr "type" "darith") + (set_attr "mode" "DI") + (set_attr "length" "4")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (minus:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "register_operand" ""))) + (clobber (match_operand:SI 3 "register_operand" ""))] + "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT + && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16 + && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1])) + && GET_CODE (operands[2]) == REG && GP_REG_P (REGNO (operands[2]))" + + [(set (match_dup 3) + (ltu:SI (subreg:SI (match_dup 1) 0) + (subreg:SI (match_dup 2) 0))) + + (set (subreg:SI (match_dup 0) 0) + (minus:SI (subreg:SI (match_dup 1) 0) + (subreg:SI (match_dup 2) 0))) + + (set (subreg:SI (match_dup 0) 1) + (minus:SI (subreg:SI (match_dup 1) 1) + (subreg:SI (match_dup 2) 1))) + + (set (subreg:SI (match_dup 0) 1) + (minus:SI (subreg:SI (match_dup 0) 1) + (match_dup 3)))] + "") + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (minus:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "register_operand" ""))) + (clobber (match_operand:SI 3 "register_operand" ""))] + "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT + && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16 + && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1])) + && GET_CODE (operands[2]) == REG && GP_REG_P (REGNO (operands[2]))" + + [(set (match_dup 3) + (ltu:SI (subreg:SI (match_dup 1) 1) + (subreg:SI (match_dup 2) 1))) + + (set (subreg:SI (match_dup 0) 1) + (minus:SI (subreg:SI (match_dup 1) 1) + (subreg:SI (match_dup 2) 1))) + + (set (subreg:SI (match_dup 0) 0) + (minus:SI (subreg:SI (match_dup 1) 0) + (subreg:SI (match_dup 2) 0))) + + (set (subreg:SI (match_dup 0) 0) + (minus:SI (subreg:SI (match_dup 0) 0) + (match_dup 3)))] + "") + +(define_insn "subdi3_internal_2" + [(set (match_operand:DI 0 "register_operand" "=d,d,d") + (minus:DI (match_operand:DI 1 "register_operand" "d,d,d") + (match_operand:DI 2 "small_int" "P,J,N"))) + (clobber (match_operand:SI 3 "register_operand" "=d,d,d"))] + "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16 + && INTVAL (operands[2]) != -32768" + "@ + sltu\\t%3,%L1,%2\;subu\\t%L0,%L1,%2\;subu\\t%M0,%M1,%3 + move\\t%L0,%L1\;move\\t%M0,%M1 + sltu\\t%3,%L1,%2\;subu\\t%L0,%L1,%2\;subu\\t%M0,%M1,1\;subu\\t%M0,%M0,%3" + [(set_attr "type" "darith") + (set_attr "mode" "DI") + (set_attr "length" "3,2,4")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (minus:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "small_int" ""))) + (clobber (match_operand:SI 3 "register_operand" ""))] + "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT + && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16 + && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1])) + && INTVAL (operands[2]) > 0" + + [(set (match_dup 3) + (ltu:SI (subreg:SI (match_dup 1) 0) + (match_dup 2))) + + (set (subreg:SI (match_dup 0) 0) + (minus:SI (subreg:SI (match_dup 1) 0) + (match_dup 2))) + + (set (subreg:SI (match_dup 0) 1) + (minus:SI (subreg:SI (match_dup 1) 1) + (match_dup 3)))] + "") + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (minus:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "small_int" ""))) + (clobber (match_operand:SI 3 "register_operand" ""))] + "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT + && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16 + && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1])) + && INTVAL (operands[2]) > 0" + + [(set (match_dup 3) + (ltu:SI (subreg:SI (match_dup 1) 1) + (match_dup 2))) + + (set (subreg:SI (match_dup 0) 1) + (minus:SI (subreg:SI (match_dup 1) 1) + (match_dup 2))) + + (set (subreg:SI (match_dup 0) 0) + (minus:SI (subreg:SI (match_dup 1) 0) + (match_dup 3)))] + "") + +(define_insn "subdi3_internal_3" + [(set (match_operand:DI 0 "register_operand" "=d") + (minus:DI (match_operand:DI 1 "se_reg_or_0_operand" "dJ") + (match_operand:DI 2 "se_arith_operand" "dI")))] + "TARGET_64BIT && !TARGET_MIPS16 + && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != -32768)" + "* +{ + return (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0) + ? \"daddu\\t%0,%z1,%n2\" + : \"dsubu\\t%0,%z1,%2\"; +}" + [(set_attr "type" "darith") + (set_attr "mode" "DI") + (set_attr "length" "1")]) + +;; For the mips16, we need to recognize stack pointer subtractions +;; explicitly, since we don't have a constraint for $sp. These insns +;; will be generated by the save_restore_insns functions. + +(define_insn "" + [(set (reg:DI 29) + (minus:DI (reg:DI 29) + (match_operand:DI 0 "small_int" "I")))] + "TARGET_MIPS16 + && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != -32768)" + "daddu\\t%$,%$,%n0" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set (attr "length") (if_then_else (match_operand:VOID 0 "m16_nsimm8_8" "") + (const_int 1) + (const_int 2)))]) + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=d") + (minus:DI (reg:DI 29) + (match_operand:DI 1 "small_int" "I")))] + "TARGET_MIPS16 + && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != -32768)" + "daddu\\t%0,%$,%n1" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set (attr "length") (if_then_else (match_operand:VOID 0 "m16_nuimm5_4" "") + (const_int 1) + (const_int 2)))]) + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=d,d,d") + (minus:DI (match_operand:DI 1 "register_operand" "0,d,d") + (match_operand:DI 2 "arith_operand" "I,O,d")))] + "TARGET_MIPS16 + && (GET_CODE (operands[2]) != CONST_INT + || (INTVAL (operands[2]) != -32768 && INTVAL (operands[2]) != -0x4000))" + "* +{ + if (REGNO (operands[0]) == REGNO (operands[1])) + return \"dsubu\\t%0,%2\"; + return \"dsubu\\t%0,%1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr_alternative "length" + [(if_then_else (match_operand:VOID 2 "m16_nsimm5_1" "") + (const_int 1) + (const_int 2)) + (if_then_else (match_operand:VOID 2 "m16_nsimm4_1" "") + (const_int 1) + (const_int 2)) + (const_int 1)])]) + +;; On the mips16, we can sometimes split an add of a constant which is +;; a 4 byte instruction into two adds which are both 2 byte +;; instructions. There are two cases: one where we are adding a +;; constant plus a register to another register, and one where we are +;; simply adding a constant to a register. + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (minus:DI (match_dup 0) + (match_operand:DI 1 "const_int_operand" "")))] + "TARGET_MIPS16 && TARGET_64BIT && reload_completed + && GET_CODE (operands[0]) == REG + && M16_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == CONST_INT + && ((INTVAL (operands[1]) > 0x10 + && INTVAL (operands[1]) <= 0x10 + 0x10) + || (INTVAL (operands[1]) < - 0xf + && INTVAL (operands[1]) >= - 0xf - 0xf))" + [(set (match_dup 0) (minus:DI (match_dup 0) (match_dup 1))) + (set (match_dup 0) (minus:DI (match_dup 0) (match_dup 2)))] + " +{ + HOST_WIDE_INT val = INTVAL (operands[1]); + + if (val >= 0) + { + operands[1] = GEN_INT (0xf); + operands[2] = GEN_INT (val - 0xf); + } + else + { + operands[1] = GEN_INT (- 0x10); + operands[2] = GEN_INT (val + 0x10); + } +}") + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (minus:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "const_int_operand" "")))] + "TARGET_MIPS16 && TARGET_64BIT && reload_completed + && GET_CODE (operands[0]) == REG + && M16_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == REG + && M16_REG_P (REGNO (operands[1])) + && REGNO (operands[0]) != REGNO (operands[1]) + && GET_CODE (operands[2]) == CONST_INT + && ((INTVAL (operands[2]) > 0x8 + && INTVAL (operands[2]) <= 0x8 + 0x10) + || (INTVAL (operands[2]) < - 0x7 + && INTVAL (operands[2]) >= - 0x7 - 0xf))" + [(set (match_dup 0) (minus:DI (match_dup 1) (match_dup 2))) + (set (match_dup 0) (minus:DI (match_dup 0) (match_dup 3)))] + " +{ + HOST_WIDE_INT val = INTVAL (operands[2]); + + if (val >= 0) + { + operands[2] = GEN_INT (0x8); + operands[3] = GEN_INT (val - 0x8); + } + else + { + operands[2] = GEN_INT (- 0x7); + operands[3] = GEN_INT (val + 0x7); + } +}") + +(define_insn "subsi3_internal_2" + [(set (match_operand:DI 0 "register_operand" "=d") + (sign_extend:DI (minus:SI (match_operand:SI 1 "reg_or_0_operand" "dJ") + (match_operand:SI 2 "arith_operand" "dI"))))] + "TARGET_64BIT && !TARGET_MIPS16 + && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != -32768)" + "* +{ + return (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0) + ? \"addu\\t%0,%z1,%n2\" + : \"subu\\t%0,%z1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=d,d,d") + (sign_extend:DI (minus:SI (match_operand:SI 1 "register_operand" "0,d,d") + (match_operand:SI 2 "arith_operand" "I,O,d"))))] + "TARGET_64BIT && TARGET_MIPS16 + && (GET_CODE (operands[2]) != CONST_INT + || (INTVAL (operands[2]) != -32768 && INTVAL (operands[2]) != -0x4000))" + "* +{ + if (REGNO (operands[0]) == REGNO (operands[1])) + return \"subu\\t%0,%2\"; + return \"subu\\t%0,%1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr_alternative "length" + [(if_then_else (match_operand:VOID 2 "m16_nsimm8_1" "") + (const_int 1) + (const_int 2)) + (if_then_else (match_operand:VOID 2 "m16_nsimm4_1" "") + (const_int 1) + (const_int 2)) + (const_int 1)])]) + + + +;; +;; .................... +;; +;; MULTIPLICATION +;; +;; .................... +;; + +;; Early Vr4300 silicon has a CPU bug where multiplies with certain +;; operands may corrupt immediately following multiplies. This is a +;; simple fix to insert NOPs. + +(define_expand "muldf3" + [(set (match_operand:DF 0 "register_operand" "=f") + (mult:DF (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT" + " +{ + if (mips_cpu != PROCESSOR_R4300) + emit_insn (gen_muldf3_internal (operands[0], operands[1], operands[2])); + else + emit_insn (gen_muldf3_r4300 (operands[0], operands[1], operands[2])); + DONE; +}") + +(define_insn "muldf3_internal" + [(set (match_operand:DF 0 "register_operand" "=f") + (mult:DF (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && mips_cpu != PROCESSOR_R4300" + "mul.d\\t%0,%1,%2" + [(set_attr "type" "fmul") + (set_attr "mode" "DF") + (set_attr "length" "1")]) + +(define_insn "muldf3_r4300" + [(set (match_operand:DF 0 "register_operand" "=f") + (mult:DF (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && mips_cpu == PROCESSOR_R4300" + "* +{ + output_asm_insn (\"mul.d\\t%0,%1,%2\", operands); + if (TARGET_4300_MUL_FIX) + output_asm_insn (\"nop\", operands); + return \"\"; +}" + [(set_attr "type" "fmul") + (set_attr "mode" "DF") + (set_attr "length" "2")]) ;; mul.d + nop + +(define_expand "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_HARD_FLOAT" + " +{ + if (mips_cpu != PROCESSOR_R4300) + emit_insn( gen_mulsf3_internal (operands[0], operands[1], operands[2])); + else + emit_insn( gen_mulsf3_r4300 (operands[0], operands[1], operands[2])); + DONE; +}") + +(define_insn "mulsf3_internal" + [(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_HARD_FLOAT && mips_cpu != PROCESSOR_R4300" + "mul.s\\t%0,%1,%2" + [(set_attr "type" "fmul") + (set_attr "mode" "SF") + (set_attr "length" "1")]) + +(define_insn "mulsf3_r4300" + [(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_HARD_FLOAT && mips_cpu == PROCESSOR_R4300" + "* +{ + output_asm_insn (\"mul.s\\t%0,%1,%2\", operands); + if (TARGET_4300_MUL_FIX) + output_asm_insn (\"nop\", operands); + return \"\"; +}" + [(set_attr "type" "fmul") + (set_attr "mode" "SF") + (set_attr "length" "2")]) ;; mul.s + nop + + +;; ??? The R4000 (only) has a cpu bug. If a double-word shift executes while +;; a multiply is in progress, it may give an incorrect result. Avoid +;; this by keeping the mflo with the mult on the R4000. + +(define_expand "mulsi3" + [(set (match_operand:SI 0 "register_operand" "=l") + (mult:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "register_operand" "d"))) + (clobber (match_scratch:SI 3 "=h")) + (clobber (match_scratch:SI 4 "=a"))] + "" + " +{ + if (HAVE_mulsi3_mult3) + emit_insn (gen_mulsi3_mult3 (operands[0], operands[1], operands[2])); + else if (mips_cpu != PROCESSOR_R4000 || TARGET_MIPS16) + emit_insn (gen_mulsi3_internal (operands[0], operands[1], operands[2])); + else + emit_insn (gen_mulsi3_r4000 (operands[0], operands[1], operands[2])); + DONE; +}") + +(define_insn "mulsi3_mult3" + [(set (match_operand:SI 0 "register_operand" "=d,l") + (mult:SI (match_operand:SI 1 "register_operand" "d,d") + (match_operand:SI 2 "register_operand" "d,d"))) + (clobber (match_scratch:SI 3 "=h,h")) + (clobber (match_scratch:SI 4 "=l,X")) + (clobber (match_scratch:SI 5 "=a,a"))] + "GENERATE_MULT3 + || TARGET_MAD" + "* +{ + if (which_alternative == 1) + return \"mult\\t%1,%2\"; + if (TARGET_MAD) + return \"mul\\t%0,%1,%2\"; + return \"mult\\t%0,%1,%2\"; +}" + [(set_attr "type" "imul") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_insn "mulsi3_internal" + [(set (match_operand:SI 0 "register_operand" "=l") + (mult:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "register_operand" "d"))) + (clobber (match_scratch:SI 3 "=h")) + (clobber (match_scratch:SI 4 "=a"))] + "mips_cpu != PROCESSOR_R4000 || TARGET_MIPS16" + "mult\\t%1,%2" + [(set_attr "type" "imul") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_insn "mulsi3_r4000" + [(set (match_operand:SI 0 "register_operand" "=d") + (mult:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "register_operand" "d"))) + (clobber (match_scratch:SI 3 "=h")) + (clobber (match_scratch:SI 4 "=l")) + (clobber (match_scratch:SI 5 "=a"))] + "mips_cpu == PROCESSOR_R4000 && !TARGET_MIPS16" + "* +{ + rtx xoperands[10]; + + xoperands[0] = operands[0]; + xoperands[1] = gen_rtx (REG, SImode, LO_REGNUM); + + output_asm_insn (\"mult\\t%1,%2\", operands); + output_asm_insn (mips_move_1word (xoperands, insn, FALSE), xoperands); + return \"\"; +}" + [(set_attr "type" "imul") + (set_attr "mode" "SI") + (set_attr "length" "3")]) ;; mult + mflo + delay + +;; Multiply-accumulate patterns + +;; For processors that can copy the output to a general register: +;; +;; The all-d alternative is needed because the combiner will find this +;; pattern and then register alloc/reload will move registers around to +;; make them fit, and we don't want to trigger unnecessary loads to LO. +;; +;; The last alternative should be made slightly less desirable, but adding +;; "?" to the constraint is too strong, and causes values to be loaded into +;; LO even when that's more costly. For now, using "*d" mostly does the +;; trick. +(define_insn "*mul_acc_si" + [(set (match_operand:SI 0 "register_operand" "=l,*d,*d") + (plus:SI (mult:SI (match_operand:SI 1 "register_operand" "d,d,d") + (match_operand:SI 2 "register_operand" "d,d,d")) + (match_operand:SI 3 "register_operand" "0,l,*d"))) + (clobber (match_scratch:SI 4 "=h,h,h")) + (clobber (match_scratch:SI 5 "=X,3,l")) + (clobber (match_scratch:SI 6 "=a,a,a")) + (clobber (match_scratch:SI 7 "=X,X,d"))] + "TARGET_MIPS3900 + && !TARGET_MIPS16" + "* +{ + static const char *const madd[] = { \"madd\\t%1,%2\", \"madd\\t%0,%1,%2\" }; + if (which_alternative == 2) + return \"#\"; + return madd[which_alternative]; +}" + [(set_attr "type" "imul,imul,multi") + (set_attr "mode" "SI") + (set_attr "length" "1,1,2")]) + +;; Split the above insn if we failed to get LO allocated. +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (plus:SI (mult:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "register_operand" "")) + (match_operand:SI 3 "register_operand" ""))) + (clobber (match_scratch:SI 4 "")) + (clobber (match_scratch:SI 5 "")) + (clobber (match_scratch:SI 6 "")) + (clobber (match_scratch:SI 7 ""))] + "reload_completed && GP_REG_P (true_regnum (operands[0])) && GP_REG_P (true_regnum (operands[3]))" + [(parallel [(set (match_dup 7) + (mult:SI (match_dup 1) (match_dup 2))) + (clobber (match_dup 4)) + (clobber (match_dup 5)) + (clobber (match_dup 6))]) + (set (match_dup 0) (plus:SI (match_dup 7) (match_dup 3)))] + "") + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (minus:SI (match_operand:SI 1 "register_operand" "") + (mult:SI (match_operand:SI 2 "register_operand" "") + (match_operand:SI 3 "register_operand" "")))) + (clobber (match_scratch:SI 4 "")) + (clobber (match_scratch:SI 5 "")) + (clobber (match_scratch:SI 6 "")) + (clobber (match_scratch:SI 7 ""))] + "reload_completed && GP_REG_P (true_regnum (operands[0])) && GP_REG_P (true_regnum (operands[1]))" + [(parallel [(set (match_dup 7) + (mult:SI (match_dup 2) (match_dup 3))) + (clobber (match_dup 4)) + (clobber (match_dup 5)) + (clobber (match_dup 6))]) + (set (match_dup 0) (minus:SI (match_dup 1) (match_dup 7)))] + "") + +(define_expand "muldi3" + [(set (match_operand:DI 0 "register_operand" "=l") + (mult:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "register_operand" "d"))) + (clobber (match_scratch:DI 3 "=h")) + (clobber (match_scratch:DI 4 "=a"))] + "TARGET_64BIT" + + " +{ + if (GENERATE_MULT3 || mips_cpu == PROCESSOR_R4000 || TARGET_MIPS16) + emit_insn (gen_muldi3_internal2 (operands[0], operands[1], operands[2])); + else + emit_insn (gen_muldi3_internal (operands[0], operands[1], operands[2])); + DONE; +}") + +;; Don't accept both operands using se_register_operand, because if +;; both operands are sign extended we would prefer to use mult in the +;; mulsidi3 pattern. Commutativity should permit either operand to be +;; sign extended. + +(define_insn "muldi3_internal" + [(set (match_operand:DI 0 "register_operand" "=l") + (mult:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "register_operand" "d"))) + (clobber (match_scratch:DI 3 "=h")) + (clobber (match_scratch:DI 4 "=a"))] + "TARGET_64BIT && mips_cpu != PROCESSOR_R4000 && !TARGET_MIPS16" + "dmult\\t%1,%2" + [(set_attr "type" "imul") + (set_attr "mode" "DI") + (set_attr "length" "1")]) + +(define_insn "muldi3_internal2" + [(set (match_operand:DI 0 "register_operand" "=d") + (mult:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "register_operand" "d"))) + (clobber (match_scratch:DI 3 "=h")) + (clobber (match_scratch:DI 4 "=l")) + (clobber (match_scratch:DI 5 "=a"))] + "TARGET_64BIT && (GENERATE_MULT3 || mips_cpu == PROCESSOR_R4000 || TARGET_MIPS16)" + "* +{ + if (GENERATE_MULT3) + output_asm_insn (\"dmult\\t%0,%1,%2\", operands); + else + { + rtx xoperands[10]; + + xoperands[0] = operands[0]; + xoperands[1] = gen_rtx (REG, DImode, LO_REGNUM); + + output_asm_insn (\"dmult\\t%1,%2\", operands); + output_asm_insn (mips_move_1word (xoperands, insn, FALSE), xoperands); + } + return \"\"; +}" + [(set_attr "type" "imul") + (set_attr "mode" "DI") + (set (attr "length") + (if_then_else (ne (symbol_ref "GENERATE_MULT3") (const_int 0)) + (const_int 1) + (const_int 3)))]) ;; mult + mflo + delay + +;; ??? We could define a mulditi3 pattern when TARGET_64BIT. + +(define_expand "mulsidi3" + [(set (match_operand:DI 0 "register_operand" "=x") + (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "d")) + (sign_extend:DI (match_operand:SI 2 "register_operand" "d"))))] + "" + " +{ + rtx dummy = gen_rtx (SIGN_EXTEND, DImode, const0_rtx); + if (TARGET_64BIT) + emit_insn (gen_mulsidi3_64bit (operands[0], operands[1], operands[2], + dummy, dummy)); + else + emit_insn (gen_mulsidi3_internal (operands[0], operands[1], operands[2], + dummy, dummy)); + DONE; +}") + +(define_expand "umulsidi3" + [(set (match_operand:DI 0 "register_operand" "=x") + (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "d")) + (zero_extend:DI (match_operand:SI 2 "register_operand" "d"))))] + "" + " +{ + rtx dummy = gen_rtx (ZERO_EXTEND, DImode, const0_rtx); + if (TARGET_64BIT) + emit_insn (gen_mulsidi3_64bit (operands[0], operands[1], operands[2], + dummy, dummy)); + else + emit_insn (gen_mulsidi3_internal (operands[0], operands[1], operands[2], + dummy, dummy)); + DONE; +}") + +(define_insn "mulsidi3_internal" + [(set (match_operand:DI 0 "register_operand" "=x") + (mult:DI (match_operator:DI 3 "extend_operator" + [(match_operand:SI 1 "register_operand" "d")]) + (match_operator:DI 4 "extend_operator" + [(match_operand:SI 2 "register_operand" "d")]))) + (clobber (match_scratch:SI 5 "=a"))] + "!TARGET_64BIT && GET_CODE (operands[3]) == GET_CODE (operands[4])" + "* +{ + if (GET_CODE (operands[3]) == SIGN_EXTEND) + return \"mult\\t%1,%2\"; + return \"multu\\t%1,%2\"; +}" + [(set_attr "type" "imul") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_insn "mulsidi3_64bit" + [(set (match_operand:DI 0 "register_operand" "=a") + (mult:DI (match_operator:DI 3 "extend_operator" + [(match_operand:SI 1 "register_operand" "d")]) + (match_operator:DI 4 "extend_operator" + [(match_operand:SI 2 "register_operand" "d")]))) + (clobber (match_scratch:DI 5 "=l")) + (clobber (match_scratch:DI 6 "=h"))] + "TARGET_64BIT && GET_CODE (operands[3]) == GET_CODE (operands[4])" + "* +{ + if (GET_CODE (operands[3]) == SIGN_EXTEND) + return \"mult\\t%1,%2\"; + return \"multu\\t%1,%2\"; +}" + [(set_attr "type" "imul") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +;; _highpart patterns +(define_expand "smulsi3_highpart" + [(set (match_operand:SI 0 "register_operand" "=h") + (truncate:SI + (lshiftrt:DI (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "d")) + (sign_extend:DI (match_operand:SI 2 "register_operand" "d"))) + (const_int 32))))] + "" + " +{ + rtx dummy = gen_rtx (SIGN_EXTEND, DImode, const0_rtx); + rtx dummy2 = gen_rtx_LSHIFTRT (DImode, const0_rtx, const0_rtx); +#ifndef NO_MD_PROTOTYPES + rtx (*genfn) PROTO((rtx, rtx, rtx, rtx, rtx, rtx)); +#else + rtx (*genfn) (); +#endif + genfn = gen_xmulsi3_highpart_internal; + emit_insn ((*genfn) (operands[0], operands[1], operands[2], dummy, + dummy, dummy2)); + DONE; +}") + +(define_expand "umulsi3_highpart" + [(set (match_operand:SI 0 "register_operand" "=h") + (truncate:SI + (lshiftrt:DI (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "d")) + (zero_extend:DI (match_operand:SI 2 "register_operand" "d"))) + (const_int 32))))] + "" + " +{ + rtx dummy = gen_rtx (ZERO_EXTEND, DImode, const0_rtx); + rtx dummy2 = gen_rtx_LSHIFTRT (DImode, const0_rtx, const0_rtx); +#ifndef NO_MD_PROTOTYPES + rtx (*genfn) PROTO((rtx, rtx, rtx, rtx, rtx, rtx)); +#else + rtx (*genfn) (); +#endif + genfn = gen_xmulsi3_highpart_internal; + emit_insn ((*genfn) (operands[0], operands[1], operands[2], dummy, + dummy, dummy2)); + DONE; +}") + +(define_insn "xmulsi3_highpart_internal" + [(set (match_operand:SI 0 "register_operand" "=h") + (truncate:SI + (match_operator:DI 5 "highpart_shift_operator" + [(mult:DI (match_operator:DI 3 "extend_operator" + [(match_operand:SI 1 "register_operand" "d")]) + (match_operator:DI 4 "extend_operator" + [(match_operand:SI 2 "register_operand" "d")])) + (const_int 32)]))) + (clobber (match_scratch:SI 6 "=l")) + (clobber (match_scratch:SI 7 "=a"))] + "GET_CODE (operands[3]) == GET_CODE (operands[4])" + "* +{ + if (GET_CODE (operands[3]) == SIGN_EXTEND) + return \"mult\\t%1,%2\"; + else + return \"multu\\t%1,%2\"; +}" + [(set_attr "type" "imul") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_insn "smuldi3_highpart" + [(set (match_operand:DI 0 "register_operand" "=h") + (truncate:DI + (lshiftrt:TI (mult:TI (sign_extend:TI (match_operand:DI 1 "se_register_operand" "d")) + (sign_extend:TI (match_operand:DI 2 "se_register_operand" "d"))) + (const_int 64)))) + (clobber (match_scratch:DI 3 "=l")) + (clobber (match_scratch:DI 4 "=a"))] + "TARGET_64BIT" + "dmult\\t%1,%2" + [(set_attr "type" "imul") + (set_attr "mode" "DI") + (set_attr "length" "1")]) + +(define_insn "umuldi3_highpart" + [(set (match_operand:DI 0 "register_operand" "=h") + (truncate:DI + (lshiftrt:TI (mult:TI (zero_extend:TI (match_operand:DI 1 "se_register_operand" "d")) + (zero_extend:TI (match_operand:DI 2 "se_register_operand" "d"))) + (const_int 64)))) + (clobber (match_scratch:DI 3 "=l")) + (clobber (match_scratch:DI 4 "=a"))] + "TARGET_64BIT" + "dmultu\\t%1,%2" + [(set_attr "type" "imul") + (set_attr "mode" "DI") + (set_attr "length" "1")]) + +;; The R4650 supports a 32 bit multiply/ 64 bit accumulate +;; instruction. The HI/LO registers are used as a 64 bit accumulator. + +(define_insn "madsi" + [(set (match_operand:SI 0 "register_operand" "+l") + (plus:SI (mult:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "register_operand" "d")) + (match_dup 0))) + (clobber (match_scratch:SI 3 "=h")) + (clobber (match_scratch:SI 4 "=a"))] + "TARGET_MAD" + "mad\\t%1,%2" + [(set_attr "type" "imul") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_insn "*mul_acc_di" + [(set (match_operand:DI 0 "register_operand" "+x") + (plus:DI (mult:DI (match_operator:DI 3 "extend_operator" + [(match_operand:SI 1 "register_operand" "d")]) + (match_operator:DI 4 "extend_operator" + [(match_operand:SI 2 "register_operand" "d")])) + (match_dup 0))) + (clobber (match_scratch:SI 5 "=a"))] + "TARGET_MAD + && ! TARGET_64BIT + && GET_CODE (operands[3]) == GET_CODE (operands[4])" + "* +{ + if (GET_CODE (operands[3]) == SIGN_EXTEND) + return \"mad\\t%1,%2\"; + else + return \"madu\\t%1,%2\"; +}" + [(set_attr "type" "imul") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_insn "*mul_acc_64bit_di" + [(set (match_operand:DI 0 "register_operand" "+a") + (plus:DI (mult:DI (match_operator:DI 3 "extend_operator" + [(match_operand:SI 1 "register_operand" "d")]) + (match_operator:DI 4 "extend_operator" + [(match_operand:SI 2 "register_operand" "d")])) + (match_dup 0))) + (clobber (match_scratch:SI 5 "=h")) + (clobber (match_scratch:SI 6 "=l"))] + "TARGET_MAD + && TARGET_64BIT + && GET_CODE (operands[3]) == GET_CODE (operands[4])" + "* +{ + if (GET_CODE (operands[3]) == SIGN_EXTEND) + return \"mad\\t%1,%2\"; + else + return \"madu\\t%1,%2\"; +}" + [(set_attr "type" "imul") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +;; Floating point multiply accumulate instructions. + +(define_insn "" + [(set (match_operand:DF 0 "register_operand" "=f") + (plus:DF (mult:DF (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f")) + (match_operand:DF 3 "register_operand" "f")))] + "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT" + "madd.d\\t%0,%3,%1,%2" + [(set_attr "type" "fmadd") + (set_attr "mode" "DF") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:SF 0 "register_operand" "=f") + (plus:SF (mult:SF (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f")) + (match_operand:SF 3 "register_operand" "f")))] + "mips_isa >= 4 && TARGET_HARD_FLOAT" + "madd.s\\t%0,%3,%1,%2" + [(set_attr "type" "fmadd") + (set_attr "mode" "SF") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:DF 0 "register_operand" "=f") + (minus:DF (mult:DF (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f")) + (match_operand:DF 3 "register_operand" "f")))] + "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT" + "msub.d\\t%0,%3,%1,%2" + [(set_attr "type" "fmadd") + (set_attr "mode" "DF") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:SF 0 "register_operand" "=f") + (minus:SF (mult:SF (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f")) + (match_operand:SF 3 "register_operand" "f")))] + + "mips_isa >= 4 && TARGET_HARD_FLOAT" + "msub.s\\t%0,%3,%1,%2" + [(set_attr "type" "fmadd") + (set_attr "mode" "SF") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:DF 0 "register_operand" "=f") + (neg:DF (plus:DF (mult:DF (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f")) + (match_operand:DF 3 "register_operand" "f"))))] + "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT" + "nmadd.d\\t%0,%3,%1,%2" + [(set_attr "type" "fmadd") + (set_attr "mode" "DF") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:SF 0 "register_operand" "=f") + (neg:SF (plus:SF (mult:SF (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f")) + (match_operand:SF 3 "register_operand" "f"))))] + "mips_isa >= 4 && TARGET_HARD_FLOAT" + "nmadd.s\\t%0,%3,%1,%2" + [(set_attr "type" "fmadd") + (set_attr "mode" "SF") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:DF 0 "register_operand" "=f") + (minus:DF (match_operand:DF 1 "register_operand" "f") + (mult:DF (match_operand:DF 2 "register_operand" "f") + (match_operand:DF 3 "register_operand" "f"))))] + "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT" + "nmsub.d\\t%0,%1,%2,%3" + [(set_attr "type" "fmadd") + (set_attr "mode" "DF") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:SF 0 "register_operand" "=f") + (minus:SF (match_operand:SF 1 "register_operand" "f") + (mult:SF (match_operand:SF 2 "register_operand" "f") + (match_operand:SF 3 "register_operand" "f"))))] + "mips_isa >= 4 && TARGET_HARD_FLOAT" + "nmsub.s\\t%0,%1,%2,%3" + [(set_attr "type" "fmadd") + (set_attr "mode" "SF") + (set_attr "length" "1")]) + +;; +;; .................... +;; +;; DIVISION and REMAINDER +;; +;; .................... +;; + +(define_insn "divdf3" + [(set (match_operand:DF 0 "register_operand" "=f") + (div:DF (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT" + "div.d\\t%0,%1,%2" + [(set_attr "type" "fdiv") + (set_attr "mode" "DF") + (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_HARD_FLOAT" + "div.s\\t%0,%1,%2" + [(set_attr "type" "fdiv") + (set_attr "mode" "SF") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:DF 0 "register_operand" "=f") + (div:DF (match_operand:DF 1 "const_float_1_operand" "") + (match_operand:DF 2 "register_operand" "f")))] + "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && flag_fast_math" + "recip.d\\t%0,%2" + [(set_attr "type" "fdiv") + (set_attr "mode" "DF") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:SF 0 "register_operand" "=f") + (div:SF (match_operand:SF 1 "const_float_1_operand" "") + (match_operand:SF 2 "register_operand" "f")))] + "mips_isa >= 4 && TARGET_HARD_FLOAT && flag_fast_math" + "recip.s\\t%0,%2" + [(set_attr "type" "fdiv") + (set_attr "mode" "SF") + (set_attr "length" "1")]) + +;; If optimizing, prefer the divmod functions over separate div and +;; mod functions, since this will allow using one instruction for both +;; the quotient and remainder. At present, the divmod is not moved out +;; of loops if it is constant within the loop, so allow -mdebugc to +;; use the old method of doing things. + +;; 64 is the multiply/divide hi register +;; 65 is the multiply/divide lo register + +;; ??? We can't accept constants here, because the MIPS assembler will replace +;; a divide by power of 2 with a shift, and then the remainder is no longer +;; available. + +(define_expand "divmodsi4" + [(set (match_operand:SI 0 "register_operand" "=d") + (div:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "register_operand" "d"))) + (set (match_operand:SI 3 "register_operand" "=d") + (mod:SI (match_dup 1) + (match_dup 2))) + (clobber (match_scratch:SI 4 "=l")) + (clobber (match_scratch:SI 5 "=h")) + (clobber (match_scratch:SI 6 "=a"))] + "optimize" + " +{ + emit_insn (gen_divmodsi4_internal (operands[0], operands[1], operands[2], + operands[3])); + if (!TARGET_NO_CHECK_ZERO_DIV) + { + emit_insn (gen_div_trap (operands[2], + GEN_INT (0), + GEN_INT (0x7))); + } + if (TARGET_CHECK_RANGE_DIV) + { + emit_insn (gen_div_trap (operands[2], + copy_to_mode_reg (SImode, GEN_INT (-1)), + GEN_INT (0x6))); + emit_insn (gen_div_trap (operands[2], + copy_to_mode_reg (SImode, GEN_INT (0x80000000)), + GEN_INT (0x6))); + } + + DONE; +}") + +(define_insn "divmodsi4_internal" + [(set (match_operand:SI 0 "register_operand" "=l") + (div:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "register_operand" "d"))) + (set (match_operand:SI 3 "register_operand" "=h") + (mod:SI (match_dup 1) + (match_dup 2))) + (clobber (match_scratch:SI 6 "=a"))] + "optimize" + "div\\t$0,%1,%2" + [(set_attr "type" "idiv") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_expand "divmoddi4" + [(set (match_operand:DI 0 "register_operand" "=d") + (div:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_register_operand" "d"))) + (set (match_operand:DI 3 "register_operand" "=d") + (mod:DI (match_dup 1) + (match_dup 2))) + (clobber (match_scratch:DI 4 "=l")) + (clobber (match_scratch:DI 5 "=h")) + (clobber (match_scratch:DI 6 "=a"))] + "TARGET_64BIT && optimize" + " +{ + emit_insn (gen_divmoddi4_internal (operands[0], operands[1], operands[2], + operands[3])); + if (!TARGET_NO_CHECK_ZERO_DIV) + { + emit_insn (gen_div_trap (operands[2], + GEN_INT (0), + GEN_INT (0x7))); + } + if (TARGET_CHECK_RANGE_DIV) + { + emit_insn (gen_div_trap (operands[2], + copy_to_mode_reg (DImode, GEN_INT (-1)), + GEN_INT (0x6))); + emit_insn (gen_div_trap (operands[2], + copy_to_mode_reg (DImode, GEN_INT (0x80000000)), + GEN_INT (0x6))); + } + + DONE; +}") + +(define_insn "divmoddi4_internal" + [(set (match_operand:DI 0 "register_operand" "=l") + (div:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_register_operand" "d"))) + (set (match_operand:DI 3 "register_operand" "=h") + (mod:DI (match_dup 1) + (match_dup 2))) + (clobber (match_scratch:DI 6 "=a"))] + "TARGET_64BIT && optimize" + "ddiv\\t$0,%1,%2" + [(set_attr "type" "idiv") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_expand "udivmodsi4" + [(set (match_operand:SI 0 "register_operand" "=d") + (udiv:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "register_operand" "d"))) + (set (match_operand:SI 3 "register_operand" "=d") + (umod:SI (match_dup 1) + (match_dup 2))) + (clobber (match_scratch:SI 4 "=l")) + (clobber (match_scratch:SI 5 "=h")) + (clobber (match_scratch:SI 6 "=a"))] + "optimize" + " +{ + emit_insn (gen_udivmodsi4_internal (operands[0], operands[1], operands[2], + operands[3])); + if (!TARGET_NO_CHECK_ZERO_DIV) + { + emit_insn (gen_div_trap (operands[2], + GEN_INT (0), + GEN_INT (0x7))); + } + + DONE; +}") + +(define_insn "udivmodsi4_internal" + [(set (match_operand:SI 0 "register_operand" "=l") + (udiv:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "register_operand" "d"))) + (set (match_operand:SI 3 "register_operand" "=h") + (umod:SI (match_dup 1) + (match_dup 2))) + (clobber (match_scratch:SI 6 "=a"))] + "optimize" + "divu\\t$0,%1,%2" + [(set_attr "type" "idiv") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_expand "udivmoddi4" + [(set (match_operand:DI 0 "register_operand" "=d") + (udiv:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_register_operand" "d"))) + (set (match_operand:DI 3 "register_operand" "=d") + (umod:DI (match_dup 1) + (match_dup 2))) + (clobber (match_scratch:DI 4 "=l")) + (clobber (match_scratch:DI 5 "=h")) + (clobber (match_scratch:DI 6 "=a"))] + "TARGET_64BIT && optimize" + " +{ + emit_insn (gen_udivmoddi4_internal (operands[0], operands[1], operands[2], + operands[3])); + if (!TARGET_NO_CHECK_ZERO_DIV) + { + emit_insn (gen_div_trap (operands[2], + GEN_INT (0), + GEN_INT (0x7))); + } + + DONE; +}") + +(define_insn "udivmoddi4_internal" + [(set (match_operand:DI 0 "register_operand" "=l") + (udiv:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_register_operand" "d"))) + (set (match_operand:DI 3 "register_operand" "=h") + (umod:DI (match_dup 1) + (match_dup 2))) + (clobber (match_scratch:DI 6 "=a"))] + "TARGET_64BIT && optimize" + "ddivu\\t$0,%1,%2" + [(set_attr "type" "idiv") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +;; Division trap + +(define_expand "div_trap" + [(trap_if (eq (match_operand 0 "register_operand" "d") + (match_operand 1 "true_reg_or_0_operand" "dJ")) + (match_operand 2 "immediate_operand" ""))] + "" + " +{ + if (TARGET_MIPS16) + emit_insn (gen_div_trap_mips16 (operands[0],operands[1],operands[2])); + else + emit_insn (gen_div_trap_normal (operands[0],operands[1],operands[2])); + DONE; +}") + +(define_insn "div_trap_normal" + [(trap_if (eq (match_operand 0 "register_operand" "d") + (match_operand 1 "true_reg_or_0_operand" "dJ")) + (match_operand 2 "immediate_operand" ""))] + "!TARGET_MIPS16" + "* +{ + rtx link; + int have_dep_anti = 0; + + /* For divmod if one division is not needed then we don't need an extra + divide by zero trap, which is anti dependent on previous trap */ + for (link = LOG_LINKS (insn); link; link = XEXP (link, 1)) + + if ((int) REG_DEP_ANTI == (int) REG_NOTE_KIND (link) + && GET_CODE (XEXP (link, 0)) == INSN + && GET_CODE (PATTERN (XEXP (link, 0))) == TRAP_IF + && REGNO (operands[1]) == 0) + have_dep_anti = 1; + if (! have_dep_anti) + { + if (GENERATE_BRANCHLIKELY) + { + if (GET_CODE (operands[1]) == CONST_INT) + return \"%(beql\\t%0,$0,1f\\n\\tbreak\\t%2\\n1:%)\"; + else + return \"%(beql\\t%0,%1,1f\\n\\tbreak\\t%2\\n1:%)\"; + } + else + { + if (GET_CODE (operands[1]) == CONST_INT) + return \"%(bne\\t%0,$0,1f\\n\\tnop\\n\\tbreak\\t%2\\n1:%)\"; + else + return \"%(bne\\t%0,%1,1f\\n\\tnop\\n\\tbreak\\t%2\\n1:%)\"; + } + } + return \"\"; +}" + [(set_attr "type" "unknown") + (set_attr "length" "3")]) + + +;; The mips16 bne insns is a macro which uses reg 24 as an intermediate. + +(define_insn "div_trap_mips16" + [(trap_if (eq (match_operand 0 "register_operand" "d") + (match_operand 1 "true_reg_or_0_operand" "dJ")) + (match_operand 2 "immediate_operand" "")) + (clobber (reg:SI 24))] + "TARGET_MIPS16" + "* +{ + rtx link; + int have_dep_anti = 0; + + /* For divmod if one division is not needed then we don't need an extra + divide by zero trap, which is anti dependent on previous trap */ + for (link = LOG_LINKS (insn); link; link = XEXP (link, 1)) + + if ((int) REG_DEP_ANTI == (int) REG_NOTE_KIND (link) + && GET_CODE (XEXP (link, 0)) == INSN + && GET_CODE (PATTERN (XEXP (link, 0))) == TRAP_IF + && REGNO (operands[1]) == 0) + have_dep_anti = 1; + if (! have_dep_anti) + { + /* No branch delay slots on mips16. */ + if (GET_CODE (operands[1]) == CONST_INT) + return \"%(bnez\\t%0,1f\\n\\tbreak\\t%2\\n1:%)\"; + else + return \"%(bne\\t%0,%1,1f\\n\\tbreak\\t%2\\n1:%)\"; + } + return \"\"; +}" + [(set_attr "type" "unknown") + (set_attr "length" "3")]) + +(define_expand "divsi3" + [(set (match_operand:SI 0 "register_operand" "=l") + (div:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "register_operand" "d"))) + (clobber (match_scratch:SI 3 "=h")) + (clobber (match_scratch:SI 4 "=a"))] + "!optimize" + " +{ + emit_insn (gen_divsi3_internal (operands[0], operands[1], operands[2])); + if (!TARGET_NO_CHECK_ZERO_DIV) + { + emit_insn (gen_div_trap (operands[2], + GEN_INT (0), + GEN_INT (0x7))); + } + if (TARGET_CHECK_RANGE_DIV) + { + emit_insn (gen_div_trap (operands[2], + copy_to_mode_reg (SImode, GEN_INT (-1)), + GEN_INT (0x6))); + emit_insn (gen_div_trap (operands[2], + copy_to_mode_reg (SImode, GEN_INT (0x80000000)), + GEN_INT (0x6))); + } + + DONE; +}") + +(define_insn "divsi3_internal" + [(set (match_operand:SI 0 "register_operand" "=l") + (div:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "nonmemory_operand" "di"))) + (clobber (match_scratch:SI 3 "=h")) + (clobber (match_scratch:SI 4 "=a"))] + "!optimize" + "div\\t$0,%1,%2" + [(set_attr "type" "idiv") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_expand "divdi3" + [(set (match_operand:DI 0 "register_operand" "=l") + (div:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_register_operand" "d"))) + (clobber (match_scratch:DI 3 "=h")) + (clobber (match_scratch:DI 4 "=a"))] + "TARGET_64BIT && !optimize" + " +{ + emit_insn (gen_divdi3_internal (operands[0], operands[1], operands[2])); + if (!TARGET_NO_CHECK_ZERO_DIV) + { + emit_insn (gen_div_trap (operands[2], + GEN_INT (0), + GEN_INT (0x7))); + } + if (TARGET_CHECK_RANGE_DIV) + { + emit_insn (gen_div_trap (operands[2], + copy_to_mode_reg (DImode, GEN_INT (-1)), + GEN_INT (0x6))); + emit_insn (gen_div_trap (operands[2], + copy_to_mode_reg (DImode, GEN_INT (0x80000000)), + GEN_INT (0x6))); + } + + DONE; +}") + +(define_insn "divdi3_internal" + [(set (match_operand:DI 0 "register_operand" "=l") + (div:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_nonmemory_operand" "di"))) + (clobber (match_scratch:SI 3 "=h")) + (clobber (match_scratch:SI 4 "=a"))] + "TARGET_64BIT && !optimize" + "ddiv\\t$0,%1,%2" + [(set_attr "type" "idiv") + (set_attr "mode" "DI") + (set_attr "length" "1")]) + +(define_expand "modsi3" + [(set (match_operand:SI 0 "register_operand" "=h") + (mod:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "register_operand" "d"))) + (clobber (match_scratch:SI 3 "=l")) + (clobber (match_scratch:SI 4 "=a"))] + "!optimize" + " +{ + emit_insn (gen_modsi3_internal (operands[0], operands[1], operands[2])); + if (!TARGET_NO_CHECK_ZERO_DIV) + { + emit_insn (gen_div_trap (operands[2], + GEN_INT (0), + GEN_INT (0x7))); + } + if (TARGET_CHECK_RANGE_DIV) + { + emit_insn (gen_div_trap (operands[2], + copy_to_mode_reg (SImode, GEN_INT (-1)), + GEN_INT (0x6))); + emit_insn (gen_div_trap (operands[2], + copy_to_mode_reg (SImode, GEN_INT (0x80000000)), + GEN_INT (0x6))); + } + + DONE; +}") + +(define_insn "modsi3_internal" + [(set (match_operand:SI 0 "register_operand" "=h") + (mod:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "nonmemory_operand" "di"))) + (clobber (match_scratch:SI 3 "=l")) + (clobber (match_scratch:SI 4 "=a"))] + "!optimize" + "div\\t$0,%1,%2" + [(set_attr "type" "idiv") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_expand "moddi3" + [(set (match_operand:DI 0 "register_operand" "=h") + (mod:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_register_operand" "d"))) + (clobber (match_scratch:DI 3 "=l")) + (clobber (match_scratch:DI 4 "=a"))] + "TARGET_64BIT && !optimize" + " +{ + emit_insn (gen_moddi3_internal (operands[0], operands[1], operands[2])); + if (!TARGET_NO_CHECK_ZERO_DIV) + { + emit_insn (gen_div_trap (operands[2], + GEN_INT (0), + GEN_INT (0x7))); + } + if (TARGET_CHECK_RANGE_DIV) + { + emit_insn (gen_div_trap (operands[2], + copy_to_mode_reg (DImode, GEN_INT (-1)), + GEN_INT (0x6))); + emit_insn (gen_div_trap (operands[2], + copy_to_mode_reg (DImode, GEN_INT (0x80000000)), + GEN_INT (0x6))); + } + + DONE; +}") + +(define_insn "moddi3_internal" + [(set (match_operand:DI 0 "register_operand" "=h") + (mod:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_nonmemory_operand" "di"))) + (clobber (match_scratch:SI 3 "=l")) + (clobber (match_scratch:SI 4 "=a"))] + "TARGET_64BIT && !optimize" + "ddiv\\t$0,%1,%2" + [(set_attr "type" "idiv") + (set_attr "mode" "DI") + (set_attr "length" "1")]) + +(define_expand "udivsi3" + [(set (match_operand:SI 0 "register_operand" "=l") + (udiv:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "register_operand" "d"))) + (clobber (match_scratch:SI 3 "=h")) + (clobber (match_scratch:SI 4 "=a"))] + "!optimize" + " +{ + emit_insn (gen_udivsi3_internal (operands[0], operands[1], operands[2])); + if (!TARGET_NO_CHECK_ZERO_DIV) + { + emit_insn (gen_div_trap (operands[2], + GEN_INT (0), + GEN_INT (0x7))); + } + + DONE; +}") + +(define_insn "udivsi3_internal" + [(set (match_operand:SI 0 "register_operand" "=l") + (udiv:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "nonmemory_operand" "di"))) + (clobber (match_scratch:SI 3 "=h")) + (clobber (match_scratch:SI 4 "=a"))] + "!optimize" + "divu\\t$0,%1,%2" + [(set_attr "type" "idiv") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_expand "udivdi3" + [(set (match_operand:DI 0 "register_operand" "=l") + (udiv:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_register_operand" "di"))) + (clobber (match_scratch:DI 3 "=h")) + (clobber (match_scratch:DI 4 "=a"))] + "TARGET_64BIT && !optimize" + " +{ + emit_insn (gen_udivdi3_internal (operands[0], operands[1], operands[2])); + if (!TARGET_NO_CHECK_ZERO_DIV) + { + emit_insn (gen_div_trap (operands[2], + GEN_INT (0), + GEN_INT (0x7))); + } + + DONE; +}") + +(define_insn "udivdi3_internal" + [(set (match_operand:DI 0 "register_operand" "=l") + (udiv:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_nonmemory_operand" "di"))) + (clobber (match_scratch:SI 3 "=h")) + (clobber (match_scratch:SI 4 "=a"))] + "TARGET_64BIT && !optimize" + "ddivu\\t$0,%1,%2" + [(set_attr "type" "idiv") + (set_attr "mode" "DI") + (set_attr "length" "1")]) + +(define_expand "umodsi3" + [(set (match_operand:SI 0 "register_operand" "=h") + (umod:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "register_operand" "d"))) + (clobber (match_scratch:SI 3 "=l")) + (clobber (match_scratch:SI 4 "=a"))] + "!optimize" + " +{ + emit_insn (gen_umodsi3_internal (operands[0], operands[1], operands[2])); + if (!TARGET_NO_CHECK_ZERO_DIV) + { + emit_insn (gen_div_trap (operands[2], + GEN_INT (0), + GEN_INT (0x7))); + } + + DONE; +}") + +(define_insn "umodsi3_internal" + [(set (match_operand:SI 0 "register_operand" "=h") + (umod:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "nonmemory_operand" "di"))) + (clobber (match_scratch:SI 3 "=l")) + (clobber (match_scratch:SI 4 "=a"))] + "!optimize" + "divu\\t$0,%1,%2" + [(set_attr "type" "idiv") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_expand "umoddi3" + [(set (match_operand:DI 0 "register_operand" "=h") + (umod:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_register_operand" "di"))) + (clobber (match_scratch:DI 3 "=l")) + (clobber (match_scratch:DI 4 "=a"))] + "TARGET_64BIT && !optimize" + " +{ + emit_insn (gen_umoddi3_internal (operands[0], operands[1], operands[2])); + if (!TARGET_NO_CHECK_ZERO_DIV) + { + emit_insn (gen_div_trap (operands[2], + GEN_INT (0), + GEN_INT (0x7))); + } + + DONE; +}") + +(define_insn "umoddi3_internal" + [(set (match_operand:DI 0 "register_operand" "=h") + (umod:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_nonmemory_operand" "di"))) + (clobber (match_scratch:SI 3 "=l")) + (clobber (match_scratch:SI 4 "=a"))] + "TARGET_64BIT && !optimize" + "ddivu\\t$0,%1,%2" + [(set_attr "type" "idiv") + (set_attr "mode" "DI") + (set_attr "length" "1")]) + +;; +;; .................... +;; +;; SQUARE ROOT +;; +;; .................... + +(define_insn "sqrtdf2" + [(set (match_operand:DF 0 "register_operand" "=f") + (sqrt:DF (match_operand:DF 1 "register_operand" "f")))] + "TARGET_HARD_FLOAT && HAVE_SQRT_P() && TARGET_DOUBLE_FLOAT" + "sqrt.d\\t%0,%1" + [(set_attr "type" "fsqrt") + (set_attr "mode" "DF") + (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_HARD_FLOAT && HAVE_SQRT_P()" + "sqrt.s\\t%0,%1" + [(set_attr "type" "fsqrt") + (set_attr "mode" "SF") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:DF 0 "register_operand" "=f") + (div:DF (match_operand:DF 1 "const_float_1_operand" "") + (sqrt:DF (match_operand:DF 2 "register_operand" "f"))))] + "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && flag_fast_math" + "rsqrt.d\\t%0,%2" + [(set_attr "type" "fsqrt") + (set_attr "mode" "DF") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:SF 0 "register_operand" "=f") + (div:SF (match_operand:SF 1 "const_float_1_operand" "") + (sqrt:SF (match_operand:SF 2 "register_operand" "f"))))] + "mips_isa >= 4 && TARGET_HARD_FLOAT && flag_fast_math" + "rsqrt.s\\t%0,%2" + [(set_attr "type" "fsqrt") + (set_attr "mode" "SF") + (set_attr "length" "1")]) + + +;; +;; .................... +;; +;; ABSOLUTE VALUE +;; +;; .................... + +;; Do not use the integer abs macro instruction, since that signals an +;; exception on -2147483648 (sigh). + +(define_insn "abssi2" + [(set (match_operand:SI 0 "register_operand" "=d") + (abs:SI (match_operand:SI 1 "register_operand" "d")))] + "!TARGET_MIPS16" + "* +{ + dslots_jump_total++; + dslots_jump_filled++; + operands[2] = const0_rtx; + + if (REGNO (operands[0]) == REGNO (operands[1])) + { + if (GENERATE_BRANCHLIKELY) + return \"%(bltzl\\t%1,1f\\n\\tsubu\\t%0,%z2,%0\\n1:%)\"; + else + return \"bgez\\t%1,1f%#\\n\\tsubu\\t%0,%z2,%0\\n1:\"; + } + else + return \"%(bgez\\t%1,1f\\n\\tmove\\t%0,%1\\n\\tsubu\\t%0,%z2,%0\\n1:%)\"; +}" + [(set_attr "type" "multi") + (set_attr "mode" "SI") + (set_attr "length" "3")]) + +(define_insn "absdi2" + [(set (match_operand:DI 0 "register_operand" "=d") + (abs:DI (match_operand:DI 1 "se_register_operand" "d")))] + "TARGET_64BIT && !TARGET_MIPS16" + "* +{ + dslots_jump_total++; + dslots_jump_filled++; + operands[2] = const0_rtx; + + if (REGNO (operands[0]) == REGNO (operands[1])) + return \"%(bltzl\\t%1,1f\\n\\tdsubu\\t%0,%z2,%0\\n1:%)\"; + else + return \"%(bgez\\t%1,1f\\n\\tmove\\t%0,%1\\n\\tdsubu\\t%0,%z2,%0\\n1:%)\"; +}" + [(set_attr "type" "multi") + (set_attr "mode" "DI") + (set_attr "length" "3")]) + +(define_insn "absdf2" + [(set (match_operand:DF 0 "register_operand" "=f") + (abs:DF (match_operand:DF 1 "register_operand" "f")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT" + "abs.d\\t%0,%1" + [(set_attr "type" "fabs") + (set_attr "mode" "DF") + (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_HARD_FLOAT" + "abs.s\\t%0,%1" + [(set_attr "type" "fabs") + (set_attr "mode" "SF") + (set_attr "length" "1")]) + + +;; +;; .................... +;; +;; FIND FIRST BIT INSTRUCTION +;; +;; .................... +;; + +(define_insn "ffssi2" + [(set (match_operand:SI 0 "register_operand" "=&d") + (ffs:SI (match_operand:SI 1 "register_operand" "d"))) + (clobber (match_scratch:SI 2 "=&d")) + (clobber (match_scratch:SI 3 "=&d"))] + "!TARGET_MIPS16" + "* +{ + dslots_jump_total += 2; + dslots_jump_filled += 2; + operands[4] = const0_rtx; + + if (optimize && find_reg_note (insn, REG_DEAD, operands[1])) + return \"%(\\ +move\\t%0,%z4\\n\\ +\\tbeq\\t%1,%z4,2f\\n\\ +1:\\tand\\t%2,%1,0x0001\\n\\ +\\taddu\\t%0,%0,1\\n\\ +\\tbeq\\t%2,%z4,1b\\n\\ +\\tsrl\\t%1,%1,1\\n\\ +2:%)\"; + + return \"%(\\ +move\\t%0,%z4\\n\\ +\\tmove\\t%3,%1\\n\\ +\\tbeq\\t%3,%z4,2f\\n\\ +1:\\tand\\t%2,%3,0x0001\\n\\ +\\taddu\\t%0,%0,1\\n\\ +\\tbeq\\t%2,%z4,1b\\n\\ +\\tsrl\\t%3,%3,1\\n\\ +2:%)\"; +}" + [(set_attr "type" "multi") + (set_attr "mode" "SI") + (set_attr "length" "6")]) + +(define_insn "ffsdi2" + [(set (match_operand:DI 0 "register_operand" "=&d") + (ffs:DI (match_operand:DI 1 "se_register_operand" "d"))) + (clobber (match_scratch:DI 2 "=&d")) + (clobber (match_scratch:DI 3 "=&d"))] + "TARGET_64BIT && !TARGET_MIPS16" + "* +{ + dslots_jump_total += 2; + dslots_jump_filled += 2; + operands[4] = const0_rtx; + + if (optimize && find_reg_note (insn, REG_DEAD, operands[1])) + return \"%(\\ +move\\t%0,%z4\\n\\ +\\tbeq\\t%1,%z4,2f\\n\\ +1:\\tand\\t%2,%1,0x0001\\n\\ +\\tdaddu\\t%0,%0,1\\n\\ +\\tbeq\\t%2,%z4,1b\\n\\ +\\tdsrl\\t%1,%1,1\\n\\ +2:%)\"; + + return \"%(\\ +move\\t%0,%z4\\n\\ +\\tmove\\t%3,%1\\n\\ +\\tbeq\\t%3,%z4,2f\\n\\ +1:\\tand\\t%2,%3,0x0001\\n\\ +\\tdaddu\\t%0,%0,1\\n\\ +\\tbeq\\t%2,%z4,1b\\n\\ +\\tdsrl\\t%3,%3,1\\n\\ +2:%)\"; +}" + [(set_attr "type" "multi") + (set_attr "mode" "DI") + (set_attr "length" "6")]) + + +;; +;; .................... +;; +;; NEGATION and ONE'S COMPLEMENT +;; +;; .................... + +(define_insn "negsi2" + [(set (match_operand:SI 0 "register_operand" "=d") + (neg:SI (match_operand:SI 1 "register_operand" "d")))] + "" + "* +{ + if (TARGET_MIPS16) + return \"neg\\t%0,%1\"; + operands[2] = const0_rtx; + return \"subu\\t%0,%z2,%1\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_expand "negdi2" + [(parallel [(set (match_operand:DI 0 "register_operand" "=d") + (neg:DI (match_operand:DI 1 "se_register_operand" "d"))) + (clobber (match_dup 2))])] + "(TARGET_64BIT || !TARGET_DEBUG_G_MODE) && !TARGET_MIPS16" + " +{ + if (TARGET_64BIT) + { + emit_insn (gen_negdi2_internal_2 (operands[0], operands[1])); + DONE; + } + + operands[2] = gen_reg_rtx (SImode); +}") + +(define_insn "negdi2_internal" + [(set (match_operand:DI 0 "register_operand" "=d") + (neg:DI (match_operand:DI 1 "register_operand" "d"))) + (clobber (match_operand:SI 2 "register_operand" "=d"))] + "! TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16" + "* +{ + operands[3] = const0_rtx; + return \"subu\\t%L0,%z3,%L1\;subu\\t%M0,%z3,%M1\;sltu\\t%2,%z3,%L0\;subu\\t%M0,%M0,%2\"; +}" + [(set_attr "type" "darith") + (set_attr "mode" "DI") + (set_attr "length" "4")]) + +(define_insn "negdi2_internal_2" + [(set (match_operand:DI 0 "register_operand" "=d") + (neg:DI (match_operand:DI 1 "se_register_operand" "d")))] + "TARGET_64BIT && !TARGET_MIPS16" + "* +{ + operands[2] = const0_rtx; + return \"dsubu\\t%0,%z2,%1\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr "length" "1")]) + +(define_insn "negdf2" + [(set (match_operand:DF 0 "register_operand" "=f") + (neg:DF (match_operand:DF 1 "register_operand" "f")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT" + "neg.d\\t%0,%1" + [(set_attr "type" "fneg") + (set_attr "mode" "DF") + (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_HARD_FLOAT" + "neg.s\\t%0,%1" + [(set_attr "type" "fneg") + (set_attr "mode" "SF") + (set_attr "length" "1")]) + +(define_insn "one_cmplsi2" + [(set (match_operand:SI 0 "register_operand" "=d") + (not:SI (match_operand:SI 1 "register_operand" "d")))] + "" + "* +{ + if (TARGET_MIPS16) + return \"not\\t%0,%1\"; + operands[2] = const0_rtx; + return \"nor\\t%0,%z2,%1\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_insn "one_cmpldi2" + [(set (match_operand:DI 0 "register_operand" "=d") + (not:DI (match_operand:DI 1 "se_register_operand" "d")))] + "" + "* +{ + if (TARGET_MIPS16) + { + if (TARGET_64BIT) + return \"not\\t%0,%1\"; + return \"not\\t%M0,%M1\;not\\t%L0,%L1\"; + } + operands[2] = const0_rtx; + if (TARGET_64BIT) + return \"nor\\t%0,%z2,%1\"; + return \"nor\\t%M0,%z2,%M1\;nor\\t%L0,%z2,%L1\"; +}" + [(set_attr "type" "darith") + (set_attr "mode" "DI") + (set (attr "length") + (if_then_else (ge (symbol_ref "mips_isa") (const_int 3)) + (const_int 1) + (const_int 2)))]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (not:DI (match_operand:DI 1 "register_operand" "")))] + "reload_completed && !TARGET_64BIT && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE + && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))" + + [(set (subreg:SI (match_dup 0) 0) (not:SI (subreg:SI (match_dup 1) 0))) + (set (subreg:SI (match_dup 0) 1) (not:SI (subreg:SI (match_dup 1) 1)))] + "") + + +;; +;; .................... +;; +;; LOGICAL +;; +;; .................... +;; + +;; Many of these instructions uses trivial define_expands, because we +;; want to use a different set of constraints when TARGET_MIPS16. + +(define_expand "andsi3" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (and:SI (match_operand:SI 1 "uns_arith_operand" "%d,d") + (match_operand:SI 2 "uns_arith_operand" "d,K")))] + "" + " +{ + if (TARGET_MIPS16) + operands[2] = force_reg (SImode, operands[2]); +}") + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (and:SI (match_operand:SI 1 "uns_arith_operand" "%d,d") + (match_operand:SI 2 "uns_arith_operand" "d,K")))] + "!TARGET_MIPS16" + "@ + and\\t%0,%1,%2 + andi\\t%0,%1,%x2" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d") + (and:SI (match_operand:SI 1 "register_operand" "%0") + (match_operand:SI 2 "register_operand" "d")))] + "TARGET_MIPS16" + "and\\t%0,%2" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_expand "anddi3" + [(set (match_operand:DI 0 "register_operand" "=d") + (and:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_register_operand" "d")))] + "TARGET_64BIT || !TARGET_DEBUG_G_MODE" + " +{ + if (TARGET_MIPS16) + operands[2] = force_reg (DImode, operands[2]); +}") + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=d") + (and:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_register_operand" "d")))] + "(TARGET_64BIT || !TARGET_DEBUG_G_MODE) && !TARGET_MIPS16" + "* +{ + if (TARGET_64BIT) + return \"and\\t%0,%1,%2\"; + return \"and\\t%M0,%M1,%M2\;and\\t%L0,%L1,%L2\"; +}" + [(set_attr "type" "darith") + (set_attr "mode" "DI") + (set (attr "length") + (if_then_else (ne (symbol_ref "TARGET_64BIT") (const_int 0)) + (const_int 1) + (const_int 2)))]) + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=d") + (and:DI (match_operand:DI 1 "se_register_operand" "0") + (match_operand:DI 2 "se_register_operand" "d")))] + "(TARGET_64BIT || !TARGET_DEBUG_G_MODE) && TARGET_MIPS16" + "* +{ + if (TARGET_64BIT) + return \"and\\t%0,%2\"; + return \"and\\t%M0,%M2\;and\\t%L0,%L2\"; +}" + [(set_attr "type" "darith") + (set_attr "mode" "DI") + (set (attr "length") + (if_then_else (ge (symbol_ref "mips_isa") (const_int 3)) + (const_int 1) + (const_int 2)))]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (and:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "register_operand" "")))] + "reload_completed && !TARGET_64BIT && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE + && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1])) + && GET_CODE (operands[2]) == REG && GP_REG_P (REGNO (operands[2]))" + + [(set (subreg:SI (match_dup 0) 0) (and:SI (subreg:SI (match_dup 1) 0) (subreg:SI (match_dup 2) 0))) + (set (subreg:SI (match_dup 0) 1) (and:SI (subreg:SI (match_dup 1) 1) (subreg:SI (match_dup 2) 1)))] + "") + +(define_insn "anddi3_internal1" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (and:DI (match_operand:DI 1 "se_register_operand" "%d,d") + (match_operand:DI 2 "se_uns_arith_operand" "d,K")))] + "TARGET_64BIT && !TARGET_MIPS16" + "@ + and\\t%0,%1,%2 + andi\\t%0,%1,%x2" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr "length" "1")]) + +(define_expand "iorsi3" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (ior:SI (match_operand:SI 1 "uns_arith_operand" "%d,d") + (match_operand:SI 2 "uns_arith_operand" "d,K")))] + "" + " +{ + if (TARGET_MIPS16) + operands[2] = force_reg (SImode, operands[2]); +}") + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (ior:SI (match_operand:SI 1 "uns_arith_operand" "%d,d") + (match_operand:SI 2 "uns_arith_operand" "d,K")))] + "!TARGET_MIPS16" + "@ + or\\t%0,%1,%2 + ori\\t%0,%1,%x2" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d") + (ior:SI (match_operand:SI 1 "register_operand" "%0") + (match_operand:SI 2 "register_operand" "d")))] + "TARGET_MIPS16" + "or\\t%0,%2" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +;;; ??? There is no iordi3 pattern which accepts 'K' constants when +;;; TARGET_64BIT + +(define_expand "iordi3" + [(set (match_operand:DI 0 "register_operand" "=d") + (ior:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_register_operand" "d")))] + "TARGET_64BIT || !TARGET_DEBUG_G_MODE" + "") + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=d") + (ior:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_register_operand" "d")))] + "(TARGET_64BIT || !TARGET_DEBUG_G_MODE) && !TARGET_MIPS16" + "* +{ + if (TARGET_64BIT) + return \"or\\t%0,%1,%2\"; + return \"or\\t%M0,%M1,%M2\;or\\t%L0,%L1,%L2\"; +}" + [(set_attr "type" "darith") + (set_attr "mode" "DI") + (set (attr "length") + (if_then_else (ne (symbol_ref "TARGET_64BIT") (const_int 0)) + (const_int 1) + (const_int 2)))]) + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=d") + (ior:DI (match_operand:DI 1 "se_register_operand" "0") + (match_operand:DI 2 "se_register_operand" "d")))] + "(TARGET_64BIT || !TARGET_DEBUG_G_MODE) && TARGET_MIPS16" + "* +{ + if (TARGET_64BIT) + return \"or\\t%0,%2\"; + return \"or\\t%M0,%M2\;or\\t%L0,%L2\"; +}" + [(set_attr "type" "darith") + (set_attr "mode" "DI") + (set (attr "length") + (if_then_else (ge (symbol_ref "mips_isa") (const_int 3)) + (const_int 1) + (const_int 2)))]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (ior:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "register_operand" "")))] + "reload_completed && !TARGET_64BIT && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE + && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1])) + && GET_CODE (operands[2]) == REG && GP_REG_P (REGNO (operands[2]))" + + [(set (subreg:SI (match_dup 0) 0) (ior:SI (subreg:SI (match_dup 1) 0) (subreg:SI (match_dup 2) 0))) + (set (subreg:SI (match_dup 0) 1) (ior:SI (subreg:SI (match_dup 1) 1) (subreg:SI (match_dup 2) 1)))] + "") + +(define_expand "xorsi3" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (xor:SI (match_operand:SI 1 "uns_arith_operand" "%d,d") + (match_operand:SI 2 "uns_arith_operand" "d,K")))] + "" + "") + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (xor:SI (match_operand:SI 1 "uns_arith_operand" "%d,d") + (match_operand:SI 2 "uns_arith_operand" "d,K")))] + "!TARGET_MIPS16" + "@ + xor\\t%0,%1,%2 + xori\\t%0,%1,%x2" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d,t,t") + (xor:SI (match_operand:SI 1 "uns_arith_operand" "%0,d,d") + (match_operand:SI 2 "uns_arith_operand" "d,K,d")))] + "TARGET_MIPS16" + "@ + xor\\t%0,%2 + cmpi\\t%1,%2 + cmp\\t%1,%2" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr_alternative "length" + [(const_int 1) + (if_then_else (match_operand:VOID 2 "m16_uimm8_1" "") + (const_int 1) + (const_int 2)) + (const_int 1)])]) + +;; ??? If delete the 32-bit long long patterns, then could merge this with +;; the following xordi3_internal pattern. +(define_expand "xordi3" + [(set (match_operand:DI 0 "register_operand" "=d") + (xor:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_register_operand" "d")))] + "TARGET_64BIT || !TARGET_DEBUG_G_MODE" + "") + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=d") + (xor:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_register_operand" "d")))] + "(TARGET_64BIT || !TARGET_DEBUG_G_MODE) && !TARGET_MIPS16" + "* +{ + if (TARGET_64BIT) + return \"xor\\t%0,%1,%2\"; + return \"xor\\t%M0,%M1,%M2\;xor\\t%L0,%L1,%L2\"; +}" + [(set_attr "type" "darith") + (set_attr "mode" "DI") + (set (attr "length") + (if_then_else (ne (symbol_ref "TARGET_64BIT") (const_int 0)) + (const_int 1) + (const_int 2)))]) + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=d") + (xor:DI (match_operand:DI 1 "se_register_operand" "0") + (match_operand:DI 2 "se_register_operand" "d")))] + "!TARGET_64BIT && TARGET_MIPS16" + "xor\\t%M0,%M2\;xor\\t%L0,%L2" + [(set_attr "type" "darith") + (set_attr "mode" "DI") + (set_attr "length" "2")]) + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=d,t,t") + (xor:DI (match_operand:DI 1 "se_register_operand" "%0,d,d") + (match_operand:DI 2 "se_uns_arith_operand" "d,K,d")))] + "TARGET_64BIT && TARGET_MIPS16" + "@ + xor\\t%0,%2 + cmpi\\t%1,%2 + cmp\\t%1,%2" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr_alternative "length" + [(const_int 1) + (if_then_else (match_operand:VOID 2 "m16_uimm8_1" "") + (const_int 1) + (const_int 2)) + (const_int 1)])]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (xor:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "register_operand" "")))] + "reload_completed && !TARGET_64BIT && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE + && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1])) + && GET_CODE (operands[2]) == REG && GP_REG_P (REGNO (operands[2]))" + + [(set (subreg:SI (match_dup 0) 0) (xor:SI (subreg:SI (match_dup 1) 0) (subreg:SI (match_dup 2) 0))) + (set (subreg:SI (match_dup 0) 1) (xor:SI (subreg:SI (match_dup 1) 1) (subreg:SI (match_dup 2) 1)))] + "") + +(define_insn "xordi3_immed" + [(set (match_operand:DI 0 "register_operand" "=d") + (xor:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_uns_arith_operand" "K")))] + "TARGET_64BIT && !TARGET_MIPS16" + "xori\\t%0,%1,%x2" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr "length" "1")]) + +(define_insn "*norsi3" + [(set (match_operand:SI 0 "register_operand" "=d") + (and:SI (not:SI (match_operand:SI 1 "register_operand" "d")) + (not:SI (match_operand:SI 2 "register_operand" "d"))))] + "!TARGET_MIPS16" + "nor\\t%0,%z1,%z2" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_insn "*nordi3" + [(set (match_operand:DI 0 "register_operand" "=d") + (and:DI (not:DI (match_operand:DI 1 "se_register_operand" "d")) + (not:DI (match_operand:DI 2 "se_register_operand" "d"))))] + "!TARGET_MIPS16" + "* +{ + if (TARGET_64BIT) + return \"nor\\t%0,%z1,%z2\"; + return \"nor\\t%M0,%M1,%M2\;nor\\t%L0,%L1,%L2\"; +}" + [(set_attr "type" "darith") + (set_attr "mode" "DI") + (set (attr "length") + (if_then_else (ne (symbol_ref "TARGET_64BIT") (const_int 0)) + (const_int 1) + (const_int 2)))]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (and:DI (not:DI (match_operand:DI 1 "register_operand" "")) + (not:DI (match_operand:DI 2 "register_operand" ""))))] + "reload_completed && !TARGET_MIPS16 && !TARGET_64BIT && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE + && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1])) + && GET_CODE (operands[2]) == REG && GP_REG_P (REGNO (operands[2]))" + + [(set (subreg:SI (match_dup 0) 0) (and:SI (not:SI (subreg:SI (match_dup 1) 0)) (not:SI (subreg:SI (match_dup 2) 0)))) + (set (subreg:SI (match_dup 0) 1) (and:SI (not:SI (subreg:SI (match_dup 1) 1)) (not:SI (subreg:SI (match_dup 2) 1))))] + "") + +;; +;; .................... +;; +;; TRUNCATION +;; +;; .................... + +(define_insn "truncdfsf2" + [(set (match_operand:SF 0 "register_operand" "=f") + (float_truncate:SF (match_operand:DF 1 "register_operand" "f")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT" + "cvt.s.d\\t%0,%1" + [(set_attr "type" "fcvt") + (set_attr "mode" "SF") + (set_attr "length" "1")]) + +(define_insn "truncdisi2" + [(set (match_operand:SI 0 "register_operand" "=d") + (truncate:SI (match_operand:DI 1 "se_register_operand" "d")))] + "TARGET_64BIT" + "* +{ + if (TARGET_MIPS16) + return \"dsll\\t%0,%1,32\;dsra\\t%0,32\"; + return \"dsll\\t%0,%1,32\;dsra\\t%0,%0,32\"; +}" + [(set_attr "type" "darith") + (set_attr "mode" "SI") + (set (attr "length") (if_then_else (eq (symbol_ref "mips16") (const_int 0)) + (const_int 2) + (const_int 4)))]) + +(define_insn "truncdihi2" + [(set (match_operand:HI 0 "register_operand" "=d") + (truncate:HI (match_operand:DI 1 "se_register_operand" "d")))] + "TARGET_64BIT" + "* +{ + if (TARGET_MIPS16) + return \"dsll\\t%0,%1,48\;dsra\\t%0,48\"; + return \"andi\\t%0,%1,0xffff\"; +}" + [(set_attr "type" "darith") + (set_attr "mode" "HI") + (set (attr "length") (if_then_else (eq (symbol_ref "mips16") (const_int 0)) + (const_int 1) + (const_int 4)))]) +(define_insn "truncdiqi2" + [(set (match_operand:QI 0 "register_operand" "=d") + (truncate:QI (match_operand:DI 1 "se_register_operand" "d")))] + "TARGET_64BIT" + "* +{ + if (TARGET_MIPS16) + return \"dsll\\t%0,%1,56\;dsra\\t%0,56\"; + return \"andi\\t%0,%1,0x00ff\"; +}" + [(set_attr "type" "darith") + (set_attr "mode" "QI") + (set (attr "length") (if_then_else (eq (symbol_ref "mips16") (const_int 0)) + (const_int 1) + (const_int 4)))]) + +;; Combiner patterns to optimize shift/truncate combinations. +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d") + (truncate:SI (ashiftrt:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "small_int" "I"))))] + "TARGET_64BIT && !TARGET_MIPS16" + "* +{ + int shift_amt = INTVAL (operands[2]) & 0x3f; + + if (shift_amt < 32) + { + operands[2] = GEN_INT (32 - shift_amt); + return \"dsll\\t%0,%1,%2\;dsra\\t%0,%0,32\"; + } + else + { + operands[2] = GEN_INT (shift_amt); + return \"dsra\\t%0,%1,%2\"; + } +}" + [(set_attr "type" "darith") + (set_attr "mode" "SI") + (set_attr "length" "2")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d") + (truncate:SI (lshiftrt:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "small_int" "I"))))] + "TARGET_64BIT && !TARGET_MIPS16" + "* +{ + int shift_amt = INTVAL (operands[2]) & 0x3f; + + if (shift_amt < 32) + { + operands[2] = GEN_INT (32 - shift_amt); + return \"dsll\\t%0,%1,%2\;dsra\\t%0,%0,32\"; + } + else if (shift_amt == 32) + return \"dsra\\t%0,%1,32\"; + else + { + operands[2] = GEN_INT (shift_amt); + return \"dsrl\\t%0,%1,%2\"; + } +}" + [(set_attr "type" "darith") + (set_attr "mode" "SI") + (set_attr "length" "2")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d") + (truncate:SI (ashift:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "small_int" "I"))))] + "TARGET_64BIT" + "* +{ + int shift_amt = INTVAL (operands[2]) & 0x3f; + + if (shift_amt < 32) + { + operands[2] = GEN_INT (32 + shift_amt); + if (TARGET_MIPS16) + return \"dsll\\t%0,%1,%2\;dsra\\t%0,32\"; + return \"dsll\\t%0,%1,%2\;dsra\\t%0,%0,32\"; + } + else + return \"move\\t%0,%.\"; +}" + [(set_attr "type" "darith") + (set_attr "mode" "SI") + (set_attr "length" "2")]) + +;; Combiner patterns to optimize truncate/zero_extend combinations. + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d") + (zero_extend:SI (truncate:HI + (match_operand:DI 1 "se_register_operand" "d"))))] + "TARGET_64BIT && !TARGET_MIPS16" + "andi\\t%0,%1,0xffff" + [(set_attr "type" "darith") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d") + (zero_extend:SI (truncate:QI + (match_operand:DI 1 "se_register_operand" "d"))))] + "TARGET_64BIT && !TARGET_MIPS16" + "andi\\t%0,%1,0xff" + [(set_attr "type" "darith") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:HI 0 "register_operand" "=d") + (zero_extend:HI (truncate:QI + (match_operand:DI 1 "se_register_operand" "d"))))] + "TARGET_64BIT && !TARGET_MIPS16" + "andi\\t%0,%1,0xff" + [(set_attr "type" "darith") + (set_attr "mode" "HI") + (set_attr "length" "1")]) + +;; +;; .................... +;; +;; ZERO EXTENSION +;; +;; .................... + +;; Extension insns. +;; Those for integer source operand are ordered widest source type first. + +(define_expand "zero_extendsidi2" + [(set (match_operand:DI 0 "register_operand" "") + (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "")))] + "TARGET_64BIT" + " +{ + if (optimize && GET_CODE (operands[1]) == MEM) + operands[1] = force_not_mem (operands[1]); + + if (GET_CODE (operands[1]) != MEM) + { + rtx op1 = gen_lowpart (DImode, operands[1]); + rtx temp = gen_reg_rtx (DImode); + rtx shift = GEN_INT (32); + + emit_insn (gen_ashldi3 (temp, op1, shift)); + emit_insn (gen_lshrdi3 (operands[0], temp, shift)); + DONE; + } +}") + +(define_insn "zero_extendsidi2_internal" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (zero_extend:DI (match_operand:SI 1 "memory_operand" "R,m")))] + "TARGET_64BIT" + "* return mips_move_1word (operands, insn, TRUE);" + [(set_attr "type" "load") + (set_attr "mode" "DI") + (set_attr "length" "1,2")]) + +(define_expand "zero_extendhisi2" + [(set (match_operand:SI 0 "register_operand" "") + (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "")))] + "" + " +{ + if (TARGET_MIPS16 && GET_CODE (operands[1]) != MEM) + { + rtx op = gen_lowpart (SImode, operands[1]); + rtx temp = force_reg (SImode, GEN_INT (0xffff)); + + emit_insn (gen_andsi3 (operands[0], op, temp)); + DONE; + } +}") + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d,d,d") + (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "d,R,m")))] + "!TARGET_MIPS16" + "* +{ + if (which_alternative == 0) + return \"andi\\t%0,%1,0xffff\"; + else + return mips_move_1word (operands, insn, TRUE); +}" + [(set_attr "type" "arith,load,load") + (set_attr "mode" "SI") + (set_attr "length" "1,1,2")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (zero_extend:SI (match_operand:HI 1 "memory_operand" "R,m")))] + "TARGET_MIPS16" + "* return mips_move_1word (operands, insn, TRUE);" + [(set_attr "type" "load,load") + (set_attr "mode" "SI") + (set_attr "length" "1,2")]) + +(define_expand "zero_extendhidi2" + [(set (match_operand:DI 0 "register_operand" "") + (zero_extend:DI (match_operand:HI 1 "nonimmediate_operand" "")))] + "TARGET_64BIT" + " +{ + if (TARGET_MIPS16 && GET_CODE (operands[1]) != MEM) + { + rtx op = gen_lowpart (DImode, operands[1]); + rtx temp = force_reg (DImode, GEN_INT (0xffff)); + + emit_insn (gen_anddi3 (operands[0], op, temp)); + DONE; + } +}") + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=d,d,d") + (zero_extend:DI (match_operand:HI 1 "nonimmediate_operand" "d,R,m")))] + "TARGET_64BIT && !TARGET_MIPS16" + "* +{ + if (which_alternative == 0) + return \"andi\\t%0,%1,0xffff\"; + else + return mips_move_1word (operands, insn, TRUE); +}" + [(set_attr "type" "arith,load,load") + (set_attr "mode" "DI") + (set_attr "length" "1,1,2")]) + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (zero_extend:DI (match_operand:HI 1 "memory_operand" "R,m")))] + "TARGET_64BIT && TARGET_MIPS16" + "* return mips_move_1word (operands, insn, TRUE);" + [(set_attr "type" "load,load") + (set_attr "mode" "DI") + (set_attr "length" "1,2")]) + +(define_expand "zero_extendqihi2" + [(set (match_operand:HI 0 "register_operand" "") + (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "")))] + "" + " +{ + if (TARGET_MIPS16 && GET_CODE (operands[1]) != MEM) + { + rtx op0 = gen_lowpart (SImode, operands[0]); + rtx op1 = gen_lowpart (SImode, operands[1]); + rtx temp = force_reg (SImode, GEN_INT (0xff)); + + emit_insn (gen_andsi3 (op0, op1, temp)); + DONE; + } +}") + +(define_insn "" + [(set (match_operand:HI 0 "register_operand" "=d,d,d") + (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "d,R,m")))] + "!TARGET_MIPS16" + "* +{ + if (which_alternative == 0) + return \"andi\\t%0,%1,0x00ff\"; + else + return mips_move_1word (operands, insn, TRUE); +}" + [(set_attr "type" "arith,load,load") + (set_attr "mode" "HI") + (set_attr "length" "1,1,2")]) + +(define_insn "" + [(set (match_operand:HI 0 "register_operand" "=d,d") + (zero_extend:HI (match_operand:QI 1 "memory_operand" "R,m")))] + "TARGET_MIPS16" + "* return mips_move_1word (operands, insn, TRUE);" + [(set_attr "type" "load,load") + (set_attr "mode" "HI") + (set_attr "length" "1,2")]) + +(define_expand "zero_extendqisi2" + [(set (match_operand:SI 0 "register_operand" "") + (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "")))] + "" + " +{ + if (TARGET_MIPS16 && GET_CODE (operands[1]) != MEM) + { + rtx op = gen_lowpart (SImode, operands[1]); + rtx temp = force_reg (SImode, GEN_INT (0xff)); + + emit_insn (gen_andsi3 (operands[0], op, temp)); + DONE; + } +}") + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d,d,d") + (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "d,R,m")))] + "!TARGET_MIPS16" + "* +{ + if (which_alternative == 0) + return \"andi\\t%0,%1,0x00ff\"; + else + return mips_move_1word (operands, insn, TRUE); +}" + [(set_attr "type" "arith,load,load") + (set_attr "mode" "SI") + (set_attr "length" "1,1,2")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (zero_extend:SI (match_operand:QI 1 "memory_operand" "R,m")))] + "TARGET_MIPS16" + "* return mips_move_1word (operands, insn, TRUE);" + [(set_attr "type" "load,load") + (set_attr "mode" "SI") + (set_attr "length" "1,2")]) + +(define_expand "zero_extendqidi2" + [(set (match_operand:DI 0 "register_operand" "") + (zero_extend:DI (match_operand:QI 1 "nonimmediate_operand" "")))] + "TARGET_64BIT" + " +{ + if (TARGET_MIPS16 && GET_CODE (operands[1]) != MEM) + { + rtx op = gen_lowpart (DImode, operands[1]); + rtx temp = force_reg (DImode, GEN_INT (0xff)); + + emit_insn (gen_anddi3 (operands[0], op, temp)); + DONE; + } +}") + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=d,d,d") + (zero_extend:DI (match_operand:QI 1 "nonimmediate_operand" "d,R,m")))] + "TARGET_64BIT && !TARGET_MIPS16" + "* +{ + if (which_alternative == 0) + return \"andi\\t%0,%1,0x00ff\"; + else + return mips_move_1word (operands, insn, TRUE); +}" + [(set_attr "type" "arith,load,load") + (set_attr "mode" "DI") + (set_attr "length" "1,1,2")]) + +;; These can be created when a paradoxical subreg operand with an implicit +;; sign_extend operator is reloaded. Because of the subreg, this is really +;; a zero extend. +;; ??? It might be possible to eliminate the need for these patterns by adding +;; more support to reload for implicit sign_extend operators. +(define_insn "*paradoxical_extendhidi2" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (sign_extend:DI + (subreg:SI (match_operand:HI 1 "memory_operand" "R,m") 0)))] + "TARGET_64BIT" + "* +{ + return mips_move_1word (operands, insn, TRUE); +}" + [(set_attr "type" "load,load") + (set_attr "mode" "DI") + (set_attr "length" "1,2")]) + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (zero_extend:DI (match_operand:QI 1 "memory_operand" "R,m")))] + "TARGET_64BIT && TARGET_MIPS16" + "* return mips_move_1word (operands, insn, TRUE);" + [(set_attr "type" "load,load") + (set_attr "mode" "DI") + (set_attr "length" "1,2")]) + +;; +;; .................... +;; +;; SIGN EXTENSION +;; +;; .................... + +;; Extension insns. +;; Those for integer source operand are ordered widest source type first. + +;; In 64 bit mode, 32 bit values in general registers are always +;; correctly sign extended. That means that if the target is a +;; general register, we can sign extend from SImode to DImode just by +;; doing a move. + +(define_insn "extendsidi2" + [(set (match_operand:DI 0 "register_operand" "=d,y,d,*d,d,d") + (sign_extend:DI (match_operand:SI 1 "nonimmediate_operand" "d,d,y,*x,R,m")))] + "TARGET_64BIT" + "* return mips_move_1word (operands, insn, FALSE);" + [(set_attr "type" "move,move,move,hilo,load,load") + (set_attr "mode" "DI") + (set_attr "length" "1,1,1,1,1,2")]) + +;; These patterns originally accepted general_operands, however, slightly +;; better code is generated by only accepting register_operands, and then +;; letting combine generate the lh and lb insns. + +(define_expand "extendhidi2" + [(set (match_operand:DI 0 "register_operand" "") + (sign_extend:DI (match_operand:HI 1 "nonimmediate_operand" "")))] + "TARGET_64BIT" + " +{ + if (optimize && GET_CODE (operands[1]) == MEM) + operands[1] = force_not_mem (operands[1]); + + if (GET_CODE (operands[1]) != MEM) + { + rtx op1 = gen_lowpart (DImode, operands[1]); + rtx temp = gen_reg_rtx (DImode); + rtx shift = GEN_INT (48); + + emit_insn (gen_ashldi3 (temp, op1, shift)); + emit_insn (gen_ashrdi3 (operands[0], temp, shift)); + DONE; + } +}") + +(define_insn "extendhidi2_internal" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (sign_extend:DI (match_operand:HI 1 "memory_operand" "R,m")))] + "TARGET_64BIT" + "* return mips_move_1word (operands, insn, FALSE);" + [(set_attr "type" "load") + (set_attr "mode" "DI") + (set_attr "length" "1,2")]) + +(define_expand "extendhisi2" + [(set (match_operand:SI 0 "register_operand" "") + (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "")))] + "" + " +{ + if (optimize && GET_CODE (operands[1]) == MEM) + operands[1] = force_not_mem (operands[1]); + + if (GET_CODE (operands[1]) != MEM) + { + rtx op1 = gen_lowpart (SImode, operands[1]); + rtx temp = gen_reg_rtx (SImode); + rtx shift = GEN_INT (16); + + emit_insn (gen_ashlsi3 (temp, op1, shift)); + emit_insn (gen_ashrsi3 (operands[0], temp, shift)); + DONE; + } +}") + +(define_insn "extendhisi2_internal" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (sign_extend:SI (match_operand:HI 1 "memory_operand" "R,m")))] + "" + "* return mips_move_1word (operands, insn, FALSE);" + [(set_attr "type" "load") + (set_attr "mode" "SI") + (set_attr "length" "1,2")]) + +(define_expand "extendqihi2" + [(set (match_operand:HI 0 "register_operand" "") + (sign_extend:HI (match_operand:QI 1 "nonimmediate_operand" "")))] + "" + " +{ + if (optimize && GET_CODE (operands[1]) == MEM) + operands[1] = force_not_mem (operands[1]); + + if (GET_CODE (operands[1]) != MEM) + { + rtx op0 = gen_lowpart (SImode, operands[0]); + rtx op1 = gen_lowpart (SImode, operands[1]); + rtx temp = gen_reg_rtx (SImode); + rtx shift = GEN_INT (24); + + emit_insn (gen_ashlsi3 (temp, op1, shift)); + emit_insn (gen_ashrsi3 (op0, temp, shift)); + DONE; + } +}") + +(define_insn "extendqihi2_internal" + [(set (match_operand:HI 0 "register_operand" "=d,d") + (sign_extend:HI (match_operand:QI 1 "memory_operand" "R,m")))] + "" + "* return mips_move_1word (operands, insn, FALSE);" + [(set_attr "type" "load") + (set_attr "mode" "SI") + (set_attr "length" "1,2")]) + + +(define_expand "extendqisi2" + [(set (match_operand:SI 0 "register_operand" "") + (sign_extend:SI (match_operand:QI 1 "nonimmediate_operand" "")))] + "" + " +{ + if (optimize && GET_CODE (operands[1]) == MEM) + operands[1] = force_not_mem (operands[1]); + + if (GET_CODE (operands[1]) != MEM) + { + rtx op1 = gen_lowpart (SImode, operands[1]); + rtx temp = gen_reg_rtx (SImode); + rtx shift = GEN_INT (24); + + emit_insn (gen_ashlsi3 (temp, op1, shift)); + emit_insn (gen_ashrsi3 (operands[0], temp, shift)); + DONE; + } +}") + +(define_insn "extendqisi2_insn" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (sign_extend:SI (match_operand:QI 1 "memory_operand" "R,m")))] + "" + "* return mips_move_1word (operands, insn, FALSE);" + [(set_attr "type" "load") + (set_attr "mode" "SI") + (set_attr "length" "1,2")]) + +(define_expand "extendqidi2" + [(set (match_operand:DI 0 "register_operand" "") + (sign_extend:DI (match_operand:QI 1 "nonimmediate_operand" "")))] + "TARGET_64BIT" + " +{ + if (optimize && GET_CODE (operands[1]) == MEM) + operands[1] = force_not_mem (operands[1]); + + if (GET_CODE (operands[1]) != MEM) + { + rtx op1 = gen_lowpart (DImode, operands[1]); + rtx temp = gen_reg_rtx (DImode); + rtx shift = GEN_INT (56); + + emit_insn (gen_ashldi3 (temp, op1, shift)); + emit_insn (gen_ashrdi3 (operands[0], temp, shift)); + DONE; + } +}") + +(define_insn "extendqidi2_insn" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (sign_extend:DI (match_operand:QI 1 "memory_operand" "R,m")))] + "TARGET_64BIT" + "* return mips_move_1word (operands, insn, FALSE);" + [(set_attr "type" "load") + (set_attr "mode" "DI") + (set_attr "length" "1,2")]) + + +(define_insn "extendsfdf2" + [(set (match_operand:DF 0 "register_operand" "=f") + (float_extend:DF (match_operand:SF 1 "register_operand" "f")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT" + "cvt.d.s\\t%0,%1" + [(set_attr "type" "fcvt") + (set_attr "mode" "DF") + (set_attr "length" "1")]) + + + +;; +;; .................... +;; +;; CONVERSIONS +;; +;; .................... + +;; The SImode scratch register can not be shared with address regs used for +;; operand zero, because then the address in the move instruction will be +;; clobbered. We mark the scratch register as early clobbered to prevent this. + +;; We need the ?X in alternative 1 so that it will be choosen only if the +;; destination is a floating point register. Otherwise, alternative 1 can +;; have lower cost than alternative 0 (because there is one less loser), and +;; can be choosen when it won't work (because integral reloads into FP +;; registers are not supported). + +(define_insn "fix_truncdfsi2" + [(set (match_operand:SI 0 "general_operand" "=d,*f,R,To") + (fix:SI (match_operand:DF 1 "register_operand" "f,*f,f,f"))) + (clobber (match_scratch:SI 2 "=d,*d,&d,&d")) + (clobber (match_scratch:DF 3 "=f,?*X,f,f"))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT" + "* +{ + rtx xoperands[10]; + + if (which_alternative == 1) + return \"trunc.w.d %0,%1,%2\"; + + output_asm_insn (\"trunc.w.d %3,%1,%2\", operands); + + xoperands[0] = operands[0]; + xoperands[1] = operands[3]; + output_asm_insn (mips_move_1word (xoperands, insn, FALSE), xoperands); + return \"\"; +}" + [(set_attr "type" "fcvt") + (set_attr "mode" "DF") + (set_attr "length" "11,9,10,11")]) + + +(define_insn "fix_truncsfsi2" + [(set (match_operand:SI 0 "general_operand" "=d,*f,R,To") + (fix:SI (match_operand:SF 1 "register_operand" "f,*f,f,f"))) + (clobber (match_scratch:SI 2 "=d,*d,&d,&d")) + (clobber (match_scratch:SF 3 "=f,?*X,f,f"))] + "TARGET_HARD_FLOAT" + "* +{ + rtx xoperands[10]; + + if (which_alternative == 1) + return \"trunc.w.s %0,%1,%2\"; + + output_asm_insn (\"trunc.w.s %3,%1,%2\", operands); + + xoperands[0] = operands[0]; + xoperands[1] = operands[3]; + output_asm_insn (mips_move_1word (xoperands, insn, FALSE), xoperands); + return \"\"; +}" + [(set_attr "type" "fcvt") + (set_attr "mode" "SF") + (set_attr "length" "11,9,10,11")]) + + +;;; ??? trunc.l.d is mentioned in the appendix of the 1993 r4000/r4600 manuals +;;; but not in the chapter that describes the FPU. It is not mentioned at all +;;; in the 1991 manuals. The r4000 at Cygnus does not have this instruction. + +;;; Deleting this means that we now need two libgcc2.a libraries. One for +;;; the 32 bit calling convention and one for the 64 bit calling convention. + +;;; If this is disabled, then fixuns_truncdfdi2 must be disabled also. + +(define_insn "fix_truncdfdi2" + [(set (match_operand:DI 0 "general_operand" "=d,*f,R,To") + (fix:DI (match_operand:DF 1 "register_operand" "f,*f,f,f"))) + (clobber (match_scratch:DF 2 "=f,?*X,f,f"))] + "TARGET_HARD_FLOAT && TARGET_64BIT && TARGET_DOUBLE_FLOAT" + "* +{ + rtx xoperands[10]; + + if (which_alternative == 1) + return \"trunc.l.d %0,%1\"; + + output_asm_insn (\"trunc.l.d %2,%1\", operands); + + xoperands[0] = operands[0]; + xoperands[1] = operands[2]; + output_asm_insn (mips_move_2words (xoperands, insn, FALSE), xoperands); + return \"\"; +}" + [(set_attr "type" "fcvt") + (set_attr "mode" "DF") + (set_attr "length" "2,1,2,3")]) + + +;;; ??? trunc.l.s is mentioned in the appendix of the 1993 r4000/r4600 manuals +;;; but not in the chapter that describes the FPU. It is not mentioned at all +;;; in the 1991 manuals. The r4000 at Cygnus does not have this instruction. +(define_insn "fix_truncsfdi2" + [(set (match_operand:DI 0 "general_operand" "=d,*f,R,To") + (fix:DI (match_operand:SF 1 "register_operand" "f,*f,f,f"))) + (clobber (match_scratch:DF 2 "=f,?*X,f,f"))] + "TARGET_HARD_FLOAT && TARGET_64BIT && TARGET_DOUBLE_FLOAT" + "* +{ + rtx xoperands[10]; + + if (which_alternative == 1) + return \"trunc.l.s %0,%1\"; + + output_asm_insn (\"trunc.l.s %2,%1\", operands); + + xoperands[0] = operands[0]; + xoperands[1] = operands[2]; + output_asm_insn (mips_move_2words (xoperands, insn, FALSE), xoperands); + return \"\"; +}" + [(set_attr "type" "fcvt") + (set_attr "mode" "SF") + (set_attr "length" "2,1,2,3")]) + + +(define_insn "floatsidf2" + [(set (match_operand:DF 0 "register_operand" "=f,f,f") + (float:DF (match_operand:SI 1 "nonimmediate_operand" "d,R,m")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT" + "* +{ + dslots_load_total++; + if (GET_CODE (operands[1]) == MEM) + return \"l.s\\t%0,%1%#\;cvt.d.w\\t%0,%0\"; + + return \"mtc1\\t%1,%0%#\;cvt.d.w\\t%0,%0\"; +}" + [(set_attr "type" "fcvt") + (set_attr "mode" "DF") + (set_attr "length" "3,4,3")]) + + +(define_insn "floatdidf2" + [(set (match_operand:DF 0 "register_operand" "=f,f,f") + (float:DF (match_operand:DI 1 "se_nonimmediate_operand" "d,R,m")))] + "TARGET_HARD_FLOAT && TARGET_64BIT && TARGET_DOUBLE_FLOAT" + "* +{ + dslots_load_total++; + if (GET_CODE (operands[1]) == MEM) + return \"l.d\\t%0,%1%#\;cvt.d.l\\t%0,%0\"; + + return \"dmtc1\\t%1,%0%#\;cvt.d.l\\t%0,%0\"; +}" + [(set_attr "type" "fcvt") + (set_attr "mode" "DF") + (set_attr "length" "3,4,3")]) + + +(define_insn "floatsisf2" + [(set (match_operand:SF 0 "register_operand" "=f,f,f") + (float:SF (match_operand:SI 1 "nonimmediate_operand" "d,R,m")))] + "TARGET_HARD_FLOAT" + "* +{ + dslots_load_total++; + if (GET_CODE (operands[1]) == MEM) + return \"l.s\\t%0,%1%#\;cvt.s.w\\t%0,%0\"; + + return \"mtc1\\t%1,%0%#\;cvt.s.w\\t%0,%0\"; +}" + [(set_attr "type" "fcvt") + (set_attr "mode" "SF") + (set_attr "length" "3,4,3")]) + + +(define_insn "floatdisf2" + [(set (match_operand:SF 0 "register_operand" "=f,f,f") + (float:SF (match_operand:DI 1 "se_nonimmediate_operand" "d,R,m")))] + "TARGET_HARD_FLOAT && TARGET_64BIT && TARGET_DOUBLE_FLOAT" + "* +{ + dslots_load_total++; + if (GET_CODE (operands[1]) == MEM) + return \"l.d\\t%0,%1%#\;cvt.s.l\\t%0,%0\"; + + return \"dmtc1\\t%1,%0%#\;cvt.s.l\\t%0,%0\"; +}" + [(set_attr "type" "fcvt") + (set_attr "mode" "SF") + (set_attr "length" "3,4,3")]) + + +(define_expand "fixuns_truncdfsi2" + [(set (match_operand:SI 0 "register_operand" "") + (unsigned_fix:SI (match_operand:DF 1 "register_operand" "")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT" + " +{ + rtx reg1 = gen_reg_rtx (DFmode); + rtx reg2 = gen_reg_rtx (DFmode); + rtx reg3 = gen_reg_rtx (SImode); + rtx label1 = gen_label_rtx (); + rtx label2 = gen_label_rtx (); + REAL_VALUE_TYPE offset = REAL_VALUE_LDEXP (1.0, 31); + + if (reg1) /* turn off complaints about unreached code */ + { + emit_move_insn (reg1, immed_real_const_1 (offset, DFmode)); + do_pending_stack_adjust (); + + emit_insn (gen_cmpdf (operands[1], reg1)); + emit_jump_insn (gen_bge (label1)); + + emit_insn (gen_fix_truncdfsi2 (operands[0], operands[1])); + emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, + gen_rtx (LABEL_REF, VOIDmode, label2))); + emit_barrier (); + + emit_label (label1); + emit_move_insn (reg2, gen_rtx (MINUS, DFmode, operands[1], reg1)); + emit_move_insn (reg3, GEN_INT (0x80000000)); + + emit_insn (gen_fix_truncdfsi2 (operands[0], reg2)); + emit_insn (gen_iorsi3 (operands[0], operands[0], reg3)); + + emit_label (label2); + + /* allow REG_NOTES to be set on last insn (labels don't have enough + fields, and can't be used for REG_NOTES anyway). */ + emit_insn (gen_rtx (USE, VOIDmode, stack_pointer_rtx)); + DONE; + } +}") + + +(define_expand "fixuns_truncdfdi2" + [(set (match_operand:DI 0 "register_operand" "") + (unsigned_fix:DI (match_operand:DF 1 "register_operand" "")))] + "TARGET_HARD_FLOAT && TARGET_64BIT && TARGET_DOUBLE_FLOAT" + " +{ + rtx reg1 = gen_reg_rtx (DFmode); + rtx reg2 = gen_reg_rtx (DFmode); + rtx reg3 = gen_reg_rtx (DImode); + rtx label1 = gen_label_rtx (); + rtx label2 = gen_label_rtx (); + REAL_VALUE_TYPE offset = REAL_VALUE_LDEXP (1.0, 63); + + if (reg1) /* turn off complaints about unreached code */ + { + emit_move_insn (reg1, immed_real_const_1 (offset, DFmode)); + do_pending_stack_adjust (); + + emit_insn (gen_cmpdf (operands[1], reg1)); + emit_jump_insn (gen_bge (label1)); + + emit_insn (gen_fix_truncdfdi2 (operands[0], operands[1])); + emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, + gen_rtx (LABEL_REF, VOIDmode, label2))); + emit_barrier (); + + emit_label (label1); + emit_move_insn (reg2, gen_rtx (MINUS, DFmode, operands[1], reg1)); + emit_move_insn (reg3, GEN_INT (0x80000000)); + emit_insn (gen_ashldi3 (reg3, reg3, GEN_INT (32))); + + emit_insn (gen_fix_truncdfdi2 (operands[0], reg2)); + emit_insn (gen_iordi3 (operands[0], operands[0], reg3)); + + emit_label (label2); + + /* allow REG_NOTES to be set on last insn (labels don't have enough + fields, and can't be used for REG_NOTES anyway). */ + emit_insn (gen_rtx (USE, VOIDmode, stack_pointer_rtx)); + DONE; + } +}") + + +(define_expand "fixuns_truncsfsi2" + [(set (match_operand:SI 0 "register_operand" "") + (unsigned_fix:SI (match_operand:SF 1 "register_operand" "")))] + "TARGET_HARD_FLOAT" + " +{ + rtx reg1 = gen_reg_rtx (SFmode); + rtx reg2 = gen_reg_rtx (SFmode); + rtx reg3 = gen_reg_rtx (SImode); + rtx label1 = gen_label_rtx (); + rtx label2 = gen_label_rtx (); + REAL_VALUE_TYPE offset = REAL_VALUE_LDEXP (1.0, 31); + + if (reg1) /* turn off complaints about unreached code */ + { + emit_move_insn (reg1, immed_real_const_1 (offset, SFmode)); + do_pending_stack_adjust (); + + emit_insn (gen_cmpsf (operands[1], reg1)); + emit_jump_insn (gen_bge (label1)); + + emit_insn (gen_fix_truncsfsi2 (operands[0], operands[1])); + emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, + gen_rtx (LABEL_REF, VOIDmode, label2))); + emit_barrier (); + + emit_label (label1); + emit_move_insn (reg2, gen_rtx (MINUS, SFmode, operands[1], reg1)); + emit_move_insn (reg3, GEN_INT (0x80000000)); + + emit_insn (gen_fix_truncsfsi2 (operands[0], reg2)); + emit_insn (gen_iorsi3 (operands[0], operands[0], reg3)); + + emit_label (label2); + + /* allow REG_NOTES to be set on last insn (labels don't have enough + fields, and can't be used for REG_NOTES anyway). */ + emit_insn (gen_rtx (USE, VOIDmode, stack_pointer_rtx)); + DONE; + } +}") + + +(define_expand "fixuns_truncsfdi2" + [(set (match_operand:DI 0 "register_operand" "") + (unsigned_fix:DI (match_operand:SF 1 "register_operand" "")))] + "TARGET_HARD_FLOAT && TARGET_64BIT && TARGET_DOUBLE_FLOAT" + " +{ + rtx reg1 = gen_reg_rtx (SFmode); + rtx reg2 = gen_reg_rtx (SFmode); + rtx reg3 = gen_reg_rtx (DImode); + rtx label1 = gen_label_rtx (); + rtx label2 = gen_label_rtx (); + REAL_VALUE_TYPE offset = REAL_VALUE_LDEXP (1.0, 63); + + if (reg1) /* turn off complaints about unreached code */ + { + emit_move_insn (reg1, immed_real_const_1 (offset, SFmode)); + do_pending_stack_adjust (); + + emit_insn (gen_cmpsf (operands[1], reg1)); + emit_jump_insn (gen_bge (label1)); + + emit_insn (gen_fix_truncsfdi2 (operands[0], operands[1])); + emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, + gen_rtx (LABEL_REF, VOIDmode, label2))); + emit_barrier (); + + emit_label (label1); + emit_move_insn (reg2, gen_rtx (MINUS, SFmode, operands[1], reg1)); + emit_move_insn (reg3, GEN_INT (0x80000000)); + emit_insn (gen_ashldi3 (reg3, reg3, GEN_INT (32))); + + emit_insn (gen_fix_truncsfdi2 (operands[0], reg2)); + emit_insn (gen_iordi3 (operands[0], operands[0], reg3)); + + emit_label (label2); + + /* allow REG_NOTES to be set on last insn (labels don't have enough + fields, and can't be used for REG_NOTES anyway). */ + emit_insn (gen_rtx (USE, VOIDmode, stack_pointer_rtx)); + DONE; + } +}") + + +;; +;; .................... +;; +;; DATA MOVEMENT +;; +;; .................... + +;; Bit field extract patterns which use lwl/lwr. + +;; ??? There could be HImode variants for the ulh/ulhu/ush macros. +;; It isn't clear whether this will give better code. + +;; Only specify the mode operand 1, the rest are assumed to be word_mode. +(define_expand "extv" + [(set (match_operand 0 "register_operand" "") + (sign_extract (match_operand:QI 1 "memory_operand" "") + (match_operand 2 "immediate_operand" "") + (match_operand 3 "immediate_operand" "")))] + "!TARGET_MIPS16" + " +{ + /* If the field does not start on a byte boundary, then fail. */ + if (INTVAL (operands[3]) % 8 != 0) + FAIL; + + /* MIPS I and MIPS II can only handle a 32bit field. */ + if (!TARGET_64BIT && INTVAL (operands[2]) != 32) + FAIL; + + /* MIPS III and MIPS IV can handle both 32bit and 64bit fields. */ + if (TARGET_64BIT + && INTVAL (operands[2]) != 64 + && INTVAL (operands[2]) != 32) + FAIL; + + /* This can happen for a 64 bit target, when extracting a value from + a 64 bit union member. extract_bit_field doesn't verify that our + source matches the predicate, so we force it to be a MEM here. */ + if (GET_CODE (operands[1]) != MEM) + FAIL; + + /* Change the mode to BLKmode for aliasing purposes. */ + operands[1] = change_address (operands[1], BLKmode, XEXP (operands[1], 0)); + + /* Otherwise, emit a l[wd]l/l[wd]r pair to load the value. */ + if (INTVAL (operands[2]) == 64) + emit_insn (gen_movdi_uld (operands[0], operands[1])); + else + { + if (TARGET_64BIT) + { + operands[0] = gen_lowpart (SImode, operands[0]); + if (operands[0] == NULL_RTX) + FAIL; + } + emit_insn (gen_movsi_ulw (operands[0], operands[1])); + } + DONE; +}") + +;; Only specify the mode operand 1, the rest are assumed to be word_mode. +(define_expand "extzv" + [(set (match_operand 0 "register_operand" "") + (zero_extract (match_operand:QI 1 "memory_operand" "") + (match_operand 2 "immediate_operand" "") + (match_operand 3 "immediate_operand" "")))] + "!TARGET_MIPS16" + " +{ + /* If the field does not start on a byte boundary, then fail. */ + if (INTVAL (operands[3]) % 8 != 0) + FAIL; + + /* MIPS I and MIPS II can only handle a 32bit field. */ + if (!TARGET_64BIT && INTVAL (operands[2]) != 32) + FAIL; + + /* MIPS III and MIPS IV can handle both 32bit and 64bit fields. */ + if (TARGET_64BIT + && INTVAL (operands[2]) != 64 + && INTVAL (operands[2]) != 32) + FAIL; + + /* This can happen for a 64 bit target, when extracting a value from + a 64 bit union member. extract_bit_field doesn't verify that our + source matches the predicate, so we force it to be a MEM here. */ + if (GET_CODE (operands[1]) != MEM) + FAIL; + + /* Change the mode to BLKmode for aliasing purposes. */ + operands[1] = change_address (operands[1], BLKmode, XEXP (operands[1], 0)); + + /* Otherwise, emit a lwl/lwr pair to load the value. */ + if (INTVAL (operands[2]) == 64) + emit_insn (gen_movdi_uld (operands[0], operands[1])); + else + { + if (TARGET_64BIT) + { + operands[0] = gen_lowpart (SImode, operands[0]); + if (operands[0] == NULL_RTX) + FAIL; + } + emit_insn (gen_movsi_ulw (operands[0], operands[1])); + } + DONE; +}") + +;; Only specify the mode operands 0, the rest are assumed to be word_mode. +(define_expand "insv" + [(set (zero_extract (match_operand:QI 0 "memory_operand" "") + (match_operand 1 "immediate_operand" "") + (match_operand 2 "immediate_operand" "")) + (match_operand 3 "register_operand" ""))] + "!TARGET_MIPS16" + " +{ + /* If the field does not start on a byte boundary, then fail. */ + if (INTVAL (operands[2]) % 8 != 0) + FAIL; + + /* MIPS I and MIPS II can only handle a 32bit field. */ + if (!TARGET_64BIT && INTVAL (operands[1]) != 32) + FAIL; + + /* MIPS III and MIPS IV can handle both 32bit and 64bit fields. */ + if (TARGET_64BIT + && INTVAL (operands[1]) != 64 + && INTVAL (operands[1]) != 32) + FAIL; + + /* This can happen for a 64 bit target, when storing into a 32 bit union + member. store_bit_field doesn't verify that our target matches the + predicate, so we force it to be a MEM here. */ + if (GET_CODE (operands[0]) != MEM) + FAIL; + + /* Change the mode to BLKmode for aliasing purposes. */ + operands[0] = change_address (operands[0], BLKmode, XEXP (operands[0], 0)); + + /* Otherwise, emit a s[wd]l/s[wd]r pair to load the value. */ + if (INTVAL (operands[1]) == 64) + emit_insn (gen_movdi_usd (operands[0], operands[3])); + else + { + if (TARGET_64BIT) + { + operands[3] = gen_lowpart (SImode, operands[3]); + if (operands[3] == NULL_RTX) + FAIL; + } + emit_insn (gen_movsi_usw (operands[0], operands[3])); + } + DONE; +}") + +;; unaligned word moves generated by the bit field patterns + +(define_insn "movsi_ulw" + [(set (match_operand:SI 0 "register_operand" "=&d,&d") + (unspec:SI [(match_operand:BLK 1 "general_operand" "R,o")] 0))] + "!TARGET_MIPS16" + "* +{ + rtx offset = const0_rtx; + rtx addr = XEXP (operands[1], 0); + rtx mem_addr = eliminate_constant_term (addr, &offset); + const char *ret; + + if (TARGET_STATS) + mips_count_memory_refs (operands[1], 2); + + /* The stack/frame pointers are always aligned, so we can convert + to the faster lw if we are referencing an aligned stack location. */ + + if ((INTVAL (offset) & 3) == 0 + && (mem_addr == stack_pointer_rtx || mem_addr == frame_pointer_rtx)) + ret = \"lw\\t%0,%1\"; + else + ret = \"ulw\\t%0,%1\"; + + return mips_fill_delay_slot (ret, DELAY_LOAD, operands, insn); +}" + [(set_attr "type" "load,load") + (set_attr "mode" "SI") + (set_attr "length" "2,4")]) + +(define_insn "movsi_usw" + [(set (match_operand:BLK 0 "memory_operand" "=R,o") + (unspec:BLK [(match_operand:SI 1 "reg_or_0_operand" "dJ,dJ")] 1))] + "!TARGET_MIPS16" + "* +{ + rtx offset = const0_rtx; + rtx addr = XEXP (operands[0], 0); + rtx mem_addr = eliminate_constant_term (addr, &offset); + + if (TARGET_STATS) + mips_count_memory_refs (operands[0], 2); + + /* The stack/frame pointers are always aligned, so we can convert + to the faster sw if we are referencing an aligned stack location. */ + + if ((INTVAL (offset) & 3) == 0 + && (mem_addr == stack_pointer_rtx || mem_addr == frame_pointer_rtx)) + return \"sw\\t%1,%0\"; + + return \"usw\\t%z1,%0\"; +}" + [(set_attr "type" "store") + (set_attr "mode" "SI") + (set_attr "length" "2,4")]) + +;; Bit field extract patterns which use ldl/ldr. + +;; unaligned double word moves generated by the bit field patterns + +(define_insn "movdi_uld" + [(set (match_operand:DI 0 "register_operand" "=&d,&d") + (unspec:DI [(match_operand:BLK 1 "general_operand" "R,o")] 0))] + "" + "* +{ + rtx offset = const0_rtx; + rtx addr = XEXP (operands[1], 0); + rtx mem_addr = eliminate_constant_term (addr, &offset); + const char *ret; + + if (TARGET_STATS) + mips_count_memory_refs (operands[1], 2); + + /* The stack/frame pointers are always aligned, so we can convert + to the faster lw if we are referencing an aligned stack location. */ + + if ((INTVAL (offset) & 7) == 0 + && (mem_addr == stack_pointer_rtx || mem_addr == frame_pointer_rtx)) + ret = \"ld\\t%0,%1\"; + else + ret = \"uld\\t%0,%1\"; + + return mips_fill_delay_slot (ret, DELAY_LOAD, operands, insn); +}" + [(set_attr "type" "load,load") + (set_attr "mode" "SI") + (set_attr "length" "2,4")]) + +(define_insn "movdi_usd" + [(set (match_operand:BLK 0 "memory_operand" "=R,o") + (unspec:BLK [(match_operand:DI 1 "reg_or_0_operand" "dJ,dJ")] 1))] + "" + "* +{ + rtx offset = const0_rtx; + rtx addr = XEXP (operands[0], 0); + rtx mem_addr = eliminate_constant_term (addr, &offset); + + if (TARGET_STATS) + mips_count_memory_refs (operands[0], 2); + + /* The stack/frame pointers are always aligned, so we can convert + to the faster sw if we are referencing an aligned stack location. */ + + if ((INTVAL (offset) & 7) == 0 + && (mem_addr == stack_pointer_rtx || mem_addr == frame_pointer_rtx)) + return \"sd\\t%1,%0\"; + + return \"usd\\t%z1,%0\"; +}" + [(set_attr "type" "store") + (set_attr "mode" "SI") + (set_attr "length" "2,4")]) + +;; These two patterns support loading addresses with two instructions instead +;; of using the macro instruction la. + +;; ??? mips_move_1word has support for HIGH, so this pattern may be +;; unnecessary. + +(define_insn "high" + [(set (match_operand:SI 0 "register_operand" "=r") + (high:SI (match_operand:SI 1 "immediate_operand" "")))] + "mips_split_addresses && !TARGET_MIPS16" + "lui\\t%0,%%hi(%1) # high" + [(set_attr "type" "move") + (set_attr "length" "1")]) + +(define_insn "low" + [(set (match_operand:SI 0 "register_operand" "=r") + (lo_sum:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "immediate_operand" "")))] + "mips_split_addresses && !TARGET_MIPS16" + "addiu\\t%0,%1,%%lo(%2) # low" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +;; 64-bit integer moves + +;; Unlike most other insns, the move insns can't be split with +;; different predicates, because register spilling and other parts of +;; the compiler, have memoized the insn number already. + +(define_expand "movdi" + [(set (match_operand:DI 0 "nonimmediate_operand" "") + (match_operand:DI 1 "general_operand" ""))] + "" + " +{ + if (mips_split_addresses && mips_check_split (operands[1], DImode)) + { + enum machine_mode mode = GET_MODE (operands[0]); + rtx tem = ((reload_in_progress | reload_completed) + ? operands[0] : gen_reg_rtx (mode)); + + emit_insn (gen_rtx (SET, VOIDmode, tem, + gen_rtx (HIGH, mode, operands[1]))); + + operands[1] = gen_rtx (LO_SUM, mode, tem, operands[1]); + } + + /* If we are generating embedded PIC code, and we are referring to a + symbol in the .text section, we must use an offset from the start + of the function. */ + if (TARGET_EMBEDDED_PIC + && (GET_CODE (operands[1]) == LABEL_REF + || (GET_CODE (operands[1]) == SYMBOL_REF + && ! SYMBOL_REF_FLAG (operands[1])))) + { + rtx temp; + + temp = embedded_pic_offset (operands[1]); + temp = gen_rtx (PLUS, Pmode, embedded_pic_fnaddr_rtx, + force_reg (DImode, temp)); + emit_move_insn (operands[0], force_reg (DImode, temp)); + DONE; + } + + /* If operands[1] is a constant address illegal for pic, then we need to + handle it just like LEGITIMIZE_ADDRESS does. */ + if (flag_pic && pic_address_needs_scratch (operands[1])) + { + rtx temp = force_reg (DImode, XEXP (XEXP (operands[1], 0), 0)); + rtx temp2 = XEXP (XEXP (operands[1], 0), 1); + + if (! SMALL_INT (temp2)) + temp2 = force_reg (DImode, temp2); + + emit_move_insn (operands[0], gen_rtx (PLUS, DImode, temp, temp2)); + DONE; + } + + /* On the mips16, we can handle a GP relative reference by adding in + $gp. We need to check the name to see whether this is a string + constant. */ + if (TARGET_MIPS16 + && register_operand (operands[0], DImode) + && GET_CODE (operands[1]) == SYMBOL_REF + && SYMBOL_REF_FLAG (operands[1])) + { + char *name = XSTR (operands[1], 0); + + if (name[0] != '*' + || strncmp (name + 1, LOCAL_LABEL_PREFIX, + sizeof LOCAL_LABEL_PREFIX - 1) != 0) + { + rtx base_reg; + + if (reload_in_progress || reload_completed) + { + /* In movsi we use the constant table here. However, in + this case, we're better off copying $28 into a + register and adding, because the constant table entry + would be 8 bytes. */ + base_reg = operands[0]; + emit_move_insn (base_reg, + gen_rtx (CONST, DImode, + gen_rtx (REG, DImode, + GP_REG_FIRST + 28))); + } + else + { + base_reg = gen_reg_rtx (Pmode); + emit_move_insn (base_reg, mips16_gp_pseudo_reg ()); + } + + emit_move_insn (operands[0], + gen_rtx (PLUS, Pmode, base_reg, + mips16_gp_offset (operands[1]))); + DONE; + } + } + + if ((reload_in_progress | reload_completed) == 0 + && !register_operand (operands[0], DImode) + && !register_operand (operands[1], DImode) + && (TARGET_MIPS16 + || ((GET_CODE (operands[1]) != CONST_INT || INTVAL (operands[1]) != 0) + && operands[1] != CONST0_RTX (DImode)))) + { + rtx temp = force_reg (DImode, operands[1]); + emit_move_insn (operands[0], temp); + DONE; + } +}") + +;; For mips16, we need a special case to handle storing $31 into +;; memory, since we don't have a constraint to match $31. This +;; instruction can be generated by save_restore_insns. + +(define_insn "" + [(set (match_operand:DI 0 "memory_operand" "R,m") + (reg:DI 31))] + "TARGET_MIPS16 && TARGET_64BIT" + "* +{ + operands[1] = gen_rtx (REG, DImode, 31); + return mips_move_2words (operands, insn); +}" + [(set_attr "type" "store") + (set_attr "mode" "DI") + (set_attr "length" "1,2")]) + +(define_insn "movdi_internal" + [(set (match_operand:DI 0 "nonimmediate_operand" "=d,d,d,d,R,o,*x,*d,*x") + (match_operand:DI 1 "general_operand" "d,iF,R,o,d,d,J,*x,*d"))] + "!TARGET_64BIT && !TARGET_MIPS16 + && (register_operand (operands[0], DImode) + || register_operand (operands[1], DImode) + || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0) + || operands[1] == CONST0_RTX (DImode))" + "* return mips_move_2words (operands, insn); " + [(set_attr "type" "move,arith,load,load,store,store,hilo,hilo,hilo") + (set_attr "mode" "DI") + (set_attr "length" "2,4,2,4,2,4,2,2,2")]) + +(define_insn "" + [(set (match_operand:DI 0 "nonimmediate_operand" "=d,y,d,d,d,d,d,R,To,*d") + (match_operand:DI 1 "general_operand" "d,d,y,K,N,R,To,d,d,*x"))] + "!TARGET_64BIT && TARGET_MIPS16 + && (register_operand (operands[0], DImode) + || register_operand (operands[1], DImode))" + "* return mips_move_2words (operands, insn);" + [(set_attr "type" "move,move,move,arith,arith,load,load,store,store,hilo") + (set_attr "mode" "DI") + (set_attr "length" "2,2,2,2,3,2,4,2,4,2")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (match_operand:DI 1 "register_operand" ""))] + "reload_completed && !TARGET_64BIT && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE + && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))" + + [(set (subreg:SI (match_dup 0) 0) (subreg:SI (match_dup 1) 0)) + (set (subreg:SI (match_dup 0) 1) (subreg:SI (match_dup 1) 1))] + "") + +(define_insn "movdi_internal2" + [(set (match_operand:DI 0 "nonimmediate_operand" "=d,d,d,d,d,d,R,m,*x,*d,*x,*a") + (match_operand:DI 1 "movdi_operand" "d,S,IKL,Mnis,R,m,dJ,dJ,J,*x,*d,*J"))] + "TARGET_64BIT && !TARGET_MIPS16 + && (register_operand (operands[0], DImode) + || se_register_operand (operands[1], DImode) + || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0) + || operands[1] == CONST0_RTX (DImode))" + "* return mips_move_2words (operands, insn); " + [(set_attr "type" "move,load,arith,arith,load,load,store,store,hilo,hilo,hilo,hilo") + (set_attr "mode" "DI") + (set_attr "length" "1,2,1,2,1,2,1,2,1,1,1,2")]) + +(define_insn "" + [(set (match_operand:DI 0 "nonimmediate_operand" "=d,y,d,d,d,d,d,d,R,m,*d") + (match_operand:DI 1 "movdi_operand" "d,d,y,K,N,s,R,m,d,d,*x"))] + "TARGET_64BIT && TARGET_MIPS16 + && (register_operand (operands[0], DImode) + || se_register_operand (operands[1], DImode))" + "* return mips_move_2words (operands, insn);" + [(set_attr "type" "move,move,move,arith,arith,arith,load,load,store,store,hilo") + (set_attr "mode" "DI") + (set_attr_alternative "length" + [(const_int 1) + (const_int 1) + (const_int 1) + (if_then_else (match_operand:VOID 1 "m16_uimm8_1" "") + (const_int 1) + (const_int 2)) + (if_then_else (match_operand:VOID 1 "m16_nuimm8_1" "") + (const_int 2) + (const_int 3)) + (if_then_else (match_operand:VOID 1 "m16_usym5_4" "") + (const_int 1) + (const_int 2)) + (const_int 1) + (const_int 2) + (const_int 1) + (const_int 2) + (const_int 1)])]) + +;; On the mips16, we can split ld $r,N($r) into an add and a load, +;; when the original load is a 4 byte instruction but the add and the +;; load are 2 2 byte instructions. + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (mem:DI (plus:DI (match_dup 0) + (match_operand:DI 1 "const_int_operand" ""))))] + "TARGET_64BIT && TARGET_MIPS16 && reload_completed + && GET_CODE (operands[0]) == REG + && M16_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == CONST_INT + && ((INTVAL (operands[1]) < 0 + && INTVAL (operands[1]) >= -0x10) + || (INTVAL (operands[1]) >= 32 * 8 + && INTVAL (operands[1]) <= 31 * 8 + 0x8) + || (INTVAL (operands[1]) >= 0 + && INTVAL (operands[1]) < 32 * 8 + && (INTVAL (operands[1]) & 7) != 0))" + [(set (match_dup 0) (plus:DI (match_dup 0) (match_dup 1))) + (set (match_dup 0) (mem:DI (plus:DI (match_dup 0) (match_dup 2))))] + " +{ + HOST_WIDE_INT val = INTVAL (operands[1]); + + if (val < 0) + operands[2] = GEN_INT (0); + else if (val >= 32 * 8) + { + int off = val & 7; + + operands[1] = GEN_INT (0x8 + off); + operands[2] = GEN_INT (val - off - 0x8); + } + else + { + int off = val & 7; + + operands[1] = GEN_INT (off); + operands[2] = GEN_INT (val - off); + } +}") + +;; Handle input reloads in DImode. +;; This is mainly to handle reloading HILO_REGNUM. Note that we may +;; see it as the source or the destination, depending upon which way +;; reload handles the instruction. +;; Making the second operand TImode is a trick. The compiler may +;; reuse the same register for operand 0 and operand 2. Using TImode +;; gives us two registers, so we can always use the one which is not +;; used. + +(define_expand "reload_indi" + [(set (match_operand:DI 0 "register_operand" "=b") + (match_operand:DI 1 "" "b")) + (clobber (match_operand:TI 2 "register_operand" "=&d"))] + "TARGET_64BIT" + " +{ + rtx scratch = gen_rtx (REG, DImode, + (REGNO (operands[0]) == REGNO (operands[2]) + ? REGNO (operands[2]) + 1 + : REGNO (operands[2]))); + + if (GET_CODE (operands[0]) == REG && REGNO (operands[0]) == HILO_REGNUM) + { + if (GET_CODE (operands[1]) == MEM) + { + rtx memword, offword, hiword, loword; + rtx addr = find_replacement (&XEXP (operands[1], 0)); + rtx op1 = change_address (operands[1], VOIDmode, addr); + + scratch = gen_rtx (REG, SImode, REGNO (scratch)); + memword = change_address (op1, SImode, NULL_RTX); + offword = change_address (adj_offsettable_operand (op1, 4), + SImode, NULL_RTX); + if (BYTES_BIG_ENDIAN) + { + hiword = memword; + loword = offword; + } + else + { + hiword = offword; + loword = memword; + } + emit_move_insn (scratch, hiword); + emit_move_insn (gen_rtx (REG, SImode, 64), scratch); + emit_move_insn (scratch, loword); + emit_move_insn (gen_rtx (REG, SImode, 65), scratch); + emit_insn (gen_rtx_USE (VOIDmode, operands[0])); + } + else + { + emit_insn (gen_ashrdi3 (scratch, operands[1], GEN_INT (32))); + emit_insn (gen_movdi (gen_rtx (REG, DImode, 64), scratch)); + emit_insn (gen_ashldi3 (scratch, operands[1], GEN_INT (32))); + emit_insn (gen_ashrdi3 (scratch, scratch, GEN_INT (32))); + emit_insn (gen_movdi (gen_rtx (REG, DImode, 65), scratch)); + emit_insn (gen_rtx_USE (VOIDmode, operands[0])); + } + DONE; + } + if (GET_CODE (operands[1]) == REG && REGNO (operands[1]) == HILO_REGNUM) + { + emit_insn (gen_movdi (scratch, gen_rtx (REG, DImode, 65))); + emit_insn (gen_ashldi3 (scratch, scratch, GEN_INT (32))); + emit_insn (gen_lshrdi3 (scratch, scratch, GEN_INT (32))); + emit_insn (gen_movdi (operands[0], gen_rtx (REG, DImode, 64))); + emit_insn (gen_ashldi3 (operands[0], operands[0], GEN_INT (32))); + emit_insn (gen_iordi3 (operands[0], operands[0], scratch)); + emit_insn (gen_rtx_USE (VOIDmode, operands[1])); + DONE; + } + /* This handles moves between a float register and HI/LO. */ + emit_move_insn (scratch, operands[1]); + emit_move_insn (operands[0], scratch); + DONE; +}") + +;; Handle output reloads in DImode. + +;; Reloading HILO_REG in MIPS16 mode requires two scratch registers, so we +;; use a TImode scratch reg. + +(define_expand "reload_outdi" + [(set (match_operand:DI 0 "" "=b") + (match_operand:DI 1 "se_register_operand" "b")) + (clobber (match_operand:TI 2 "register_operand" "=&d"))] + "TARGET_64BIT" + " +{ + rtx scratch = gen_rtx_REG (DImode, REGNO (operands[2])); + + if (GET_CODE (operands[0]) == REG && REGNO (operands[0]) == HILO_REGNUM) + { + emit_insn (gen_ashrdi3 (scratch, operands[1], GEN_INT (32))); + emit_insn (gen_movdi (gen_rtx (REG, DImode, 64), scratch)); + emit_insn (gen_ashldi3 (scratch, operands[1], GEN_INT (32))); + emit_insn (gen_ashrdi3 (scratch, scratch, GEN_INT (32))); + emit_insn (gen_movdi (gen_rtx (REG, DImode, 65), scratch)); + emit_insn (gen_rtx_USE (VOIDmode, operands[0])); + DONE; + } + if (GET_CODE (operands[1]) == REG && REGNO (operands[1]) == HILO_REGNUM) + { + if (GET_CODE (operands[0]) == MEM) + { + rtx scratch, memword, offword, hiword, loword; + rtx addr = find_replacement (&XEXP (operands[0], 0)); + rtx op0 = change_address (operands[0], VOIDmode, addr); + + scratch = gen_rtx (REG, SImode, REGNO (operands[2])); + memword = change_address (op0, SImode, NULL_RTX); + offword = change_address (adj_offsettable_operand (op0, 4), + SImode, NULL_RTX); + if (BYTES_BIG_ENDIAN) + { + hiword = memword; + loword = offword; + } + else + { + hiword = offword; + loword = memword; + } + emit_move_insn (scratch, gen_rtx (REG, SImode, 64)); + emit_move_insn (hiword, scratch); + emit_move_insn (scratch, gen_rtx (REG, SImode, 65)); + emit_move_insn (loword, scratch); + emit_insn (gen_rtx_USE (VOIDmode, operands[1])); + } + else if (TARGET_MIPS16 && ! M16_REG_P (REGNO (operands[0]))) + { + /* Handle the case where operand[0] is not a 'd' register, + and hence we can not directly move from the HILO register + into it. */ + rtx scratch2 = gen_rtx_REG (DImode, REGNO (operands[2]) + 1); + emit_insn (gen_movdi (scratch, gen_rtx (REG, DImode, 65))); + emit_insn (gen_ashldi3 (scratch, scratch, GEN_INT (32))); + emit_insn (gen_lshrdi3 (scratch, scratch, GEN_INT (32))); + emit_insn (gen_movdi (scratch2, gen_rtx (REG, DImode, 64))); + emit_insn (gen_ashldi3 (scratch2, scratch2, GEN_INT (32))); + emit_insn (gen_iordi3 (scratch, scratch, scratch2)); + emit_insn (gen_movdi (operands[0], scratch)); + emit_insn (gen_rtx_USE (VOIDmode, operands[1])); + } + else + { + emit_insn (gen_movdi (scratch, gen_rtx (REG, DImode, 65))); + emit_insn (gen_ashldi3 (scratch, scratch, GEN_INT (32))); + emit_insn (gen_lshrdi3 (scratch, scratch, GEN_INT (32))); + emit_insn (gen_movdi (operands[0], gen_rtx (REG, DImode, 64))); + emit_insn (gen_ashldi3 (operands[0], operands[0], GEN_INT (32))); + emit_insn (gen_iordi3 (operands[0], operands[0], scratch)); + emit_insn (gen_rtx_USE (VOIDmode, operands[1])); + } + DONE; + } + /* This handles moves between a float register and HI/LO. */ + emit_move_insn (scratch, operands[1]); + emit_move_insn (operands[0], scratch); + DONE; +}") + +;; 32-bit Integer moves + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "large_int" ""))] + "!TARGET_DEBUG_D_MODE && !TARGET_MIPS16" + [(set (match_dup 0) + (match_dup 2)) + (set (match_dup 0) + (ior:SI (match_dup 0) + (match_dup 3)))] + " +{ + operands[2] = GEN_INT (INTVAL (operands[1]) & 0xffff0000); + operands[3] = GEN_INT (INTVAL (operands[1]) & 0x0000ffff); +}") + +;; Unlike most other insns, the move insns can't be split with +;; different predicates, because register spilling and other parts of +;; the compiler, have memoized the insn number already. + +(define_expand "movsi" + [(set (match_operand:SI 0 "nonimmediate_operand" "") + (match_operand:SI 1 "general_operand" ""))] + "" + " +{ + if (mips_split_addresses && mips_check_split (operands[1], SImode)) + { + enum machine_mode mode = GET_MODE (operands[0]); + rtx tem = ((reload_in_progress | reload_completed) + ? operands[0] : gen_reg_rtx (mode)); + + emit_insn (gen_rtx (SET, VOIDmode, tem, + gen_rtx (HIGH, mode, operands[1]))); + + operands[1] = gen_rtx (LO_SUM, mode, tem, operands[1]); + } + + /* If we are generating embedded PIC code, and we are referring to a + symbol in the .text section, we must use an offset from the start + of the function. */ + if (TARGET_EMBEDDED_PIC + && (GET_CODE (operands[1]) == LABEL_REF + || (GET_CODE (operands[1]) == SYMBOL_REF + && ! SYMBOL_REF_FLAG (operands[1])))) + { + rtx temp; + + temp = embedded_pic_offset (operands[1]); + temp = gen_rtx (PLUS, Pmode, embedded_pic_fnaddr_rtx, + force_reg (SImode, temp)); + emit_move_insn (operands[0], force_reg (SImode, temp)); + DONE; + } + + /* If operands[1] is a constant address invalid for pic, then we need to + handle it just like LEGITIMIZE_ADDRESS does. */ + if (flag_pic && pic_address_needs_scratch (operands[1])) + { + rtx temp = force_reg (SImode, XEXP (XEXP (operands[1], 0), 0)); + rtx temp2 = XEXP (XEXP (operands[1], 0), 1); + + if (! SMALL_INT (temp2)) + temp2 = force_reg (SImode, temp2); + + emit_move_insn (operands[0], gen_rtx (PLUS, SImode, temp, temp2)); + DONE; + } + + /* On the mips16, we can handle a GP relative reference by adding in + $gp. We need to check the name to see whether this is a string + constant. */ + if (TARGET_MIPS16 + && register_operand (operands[0], SImode) + && GET_CODE (operands[1]) == SYMBOL_REF + && SYMBOL_REF_FLAG (operands[1])) + { + char *name = XSTR (operands[1], 0); + + if (name[0] != '*' + || strncmp (name + 1, LOCAL_LABEL_PREFIX, + sizeof LOCAL_LABEL_PREFIX - 1) != 0) + { + rtx base_reg; + + if (reload_in_progress || reload_completed) + { + /* We need to reload this address. In this case we + aren't going to have a chance to combine loading the + address with the load or store. That means that we + can either generate a 2 byte move followed by a 4 + byte addition, or a 2 byte load with a 4 byte entry + in the constant table. Since the entry in the + constant table might be shared, we're better off, on + average, loading the address from the constant table. */ + emit_move_insn (operands[0], + force_const_mem (SImode, operands[1])); + DONE; + } + + base_reg = gen_reg_rtx (Pmode); + emit_move_insn (base_reg, mips16_gp_pseudo_reg ()); + + emit_move_insn (operands[0], + gen_rtx (PLUS, Pmode, base_reg, + mips16_gp_offset (operands[1]))); + DONE; + } + } + + if ((reload_in_progress | reload_completed) == 0 + && !register_operand (operands[0], SImode) + && !register_operand (operands[1], SImode) + && (TARGET_MIPS16 + || GET_CODE (operands[1]) != CONST_INT + || INTVAL (operands[1]) != 0)) + { + rtx temp = force_reg (SImode, operands[1]); + emit_move_insn (operands[0], temp); + DONE; + } +}") + +;; For mips16, we need a special case to handle storing $31 into +;; memory, since we don't have a constraint to match $31. This +;; instruction can be generated by save_restore_insns. + +(define_insn "" + [(set (match_operand:SI 0 "memory_operand" "R,m") + (reg:SI 31))] + "TARGET_MIPS16" + "* +{ + operands[1] = gen_rtx (REG, SImode, 31); + return mips_move_1word (operands, insn, FALSE); +}" + [(set_attr "type" "store") + (set_attr "mode" "SI") + (set_attr "length" "1,2")]) + +;; The difference between these two is whether or not ints are allowed +;; in FP registers (off by default, use -mdebugh to enable). + +(define_insn "movsi_internal1" + [(set (match_operand:SI 0 "nonimmediate_operand" "=d,d,d,d,d,d,R,m,*d,*f*z,*f,*f,*f,*R,*m,*x,*x,*d,*d") + (match_operand:SI 1 "move_operand" "d,S,IKL,Mnis,R,m,dJ,dJ,*f*z,*d,*f,*R,*m,*f,*f,J,*d,*x,*a"))] + "TARGET_DEBUG_H_MODE && !TARGET_MIPS16 + && (register_operand (operands[0], SImode) + || register_operand (operands[1], SImode) + || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0))" + "* return mips_move_1word (operands, insn, FALSE);" + [(set_attr "type" "move,load,arith,arith,load,load,store,store,xfer,xfer,move,load,load,store,store,hilo,hilo,hilo,hilo") + (set_attr "mode" "SI") + (set_attr "length" "1,2,1,2,1,2,1,2,1,1,1,1,2,1,2,1,1,1,1")]) + +(define_insn "movsi_internal2" + [(set (match_operand:SI 0 "nonimmediate_operand" "=d,d,d,d,d,d,R,m,*d,*z,*x,*d,*x,*d") + (match_operand:SI 1 "move_operand" "d,S,IKL,Mnis,R,m,dJ,dJ,*z,*d,J,*x,*d,*a"))] + "!TARGET_DEBUG_H_MODE && !TARGET_MIPS16 + && (register_operand (operands[0], SImode) + || register_operand (operands[1], SImode) + || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0))" + "* return mips_move_1word (operands, insn, FALSE);" + [(set_attr "type" "move,load,arith,arith,load,load,store,store,xfer,xfer,hilo,hilo,hilo,hilo") + (set_attr "mode" "SI") + (set_attr "length" "1,2,1,2,1,2,1,2,1,1,1,1,1,1")]) + +;; This is the mips16 movsi instruction. We accept a small integer as +;; the source if the destination is a GP memory reference. This is +;; because we want the combine pass to turn adding a GP reference to a +;; register into a direct GP reference, but the combine pass will pass +;; in the source as a constant if it finds an equivalent one. If the +;; instruction is recognized, reload will force the constant back out +;; into a register. + +(define_insn "" + [(set (match_operand:SI 0 "nonimmediate_operand" "=d,y,d,d,d,d,d,d,d,R,m,*d,*d") + (match_operand:SI 1 "move_operand" "d,d,y,S,K,N,s,R,m,d,d,*x,*a"))] + "TARGET_MIPS16 + && (register_operand (operands[0], SImode) + || register_operand (operands[1], SImode) + || (GET_CODE (operands[0]) == MEM + && GET_CODE (XEXP (operands[0], 0)) == PLUS + && GET_CODE (XEXP (XEXP (operands[0], 0), 1)) == CONST + && mips16_gp_offset_p (XEXP (XEXP (operands[0], 0), 1)) + && GET_CODE (operands[1]) == CONST_INT + && (SMALL_INT (operands[1]) + || SMALL_INT_UNSIGNED (operands[1]))))" + "* return mips_move_1word (operands, insn, FALSE);" + [(set_attr "type" "move,move,move,load,arith,arith,arith,load,load,store,store,hilo,hilo") + (set_attr "mode" "SI") + (set_attr_alternative "length" + [(const_int 1) + (const_int 1) + (const_int 1) + (const_int 2) + (if_then_else (match_operand:VOID 1 "m16_uimm8_1" "") + (const_int 1) + (const_int 2)) + (if_then_else (match_operand:VOID 1 "m16_nuimm8_1" "") + (const_int 2) + (const_int 3)) + (if_then_else (match_operand:VOID 1 "m16_usym8_4" "") + (const_int 1) + (const_int 2)) + (const_int 1) + (const_int 2) + (const_int 1) + (const_int 2) + (const_int 1) + (const_int 1)])]) + +;; On the mips16, we can split lw $r,N($r) into an add and a load, +;; when the original load is a 4 byte instruction but the add and the +;; load are 2 2 byte instructions. + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (mem:SI (plus:SI (match_dup 0) + (match_operand:SI 1 "const_int_operand" ""))))] + "TARGET_MIPS16 && reload_completed + && GET_CODE (operands[0]) == REG + && M16_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == CONST_INT + && ((INTVAL (operands[1]) < 0 + && INTVAL (operands[1]) >= -0x80) + || (INTVAL (operands[1]) >= 32 * 4 + && INTVAL (operands[1]) <= 31 * 4 + 0x7c) + || (INTVAL (operands[1]) >= 0 + && INTVAL (operands[1]) < 32 * 4 + && (INTVAL (operands[1]) & 3) != 0))" + [(set (match_dup 0) (plus:SI (match_dup 0) (match_dup 1))) + (set (match_dup 0) (mem:SI (plus:SI (match_dup 0) (match_dup 2))))] + " +{ + HOST_WIDE_INT val = INTVAL (operands[1]); + + if (val < 0) + operands[2] = GEN_INT (0); + else if (val >= 32 * 4) + { + int off = val & 3; + + operands[1] = GEN_INT (0x7c + off); + operands[2] = GEN_INT (val - off - 0x7c); + } + else + { + int off = val & 3; + + operands[1] = GEN_INT (off); + operands[2] = GEN_INT (val - off); + } +}") + +;; On the mips16, we can split a load of certain constants into a load +;; and an add. This turns a 4 byte instruction into 2 2 byte +;; instructions. + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "const_int_operand" ""))] + "TARGET_MIPS16 && reload_completed + && GET_CODE (operands[0]) == REG + && M16_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == CONST_INT + && INTVAL (operands[1]) >= 0x100 + && INTVAL (operands[1]) <= 0xff + 0x7f" + [(set (match_dup 0) (match_dup 1)) + (set (match_dup 0) (plus:SI (match_dup 0) (match_dup 2)))] + " +{ + int val = INTVAL (operands[1]); + + operands[1] = GEN_INT (0xff); + operands[2] = GEN_INT (val - 0xff); +}") + +;; On the mips16, we can split a load of a negative constant into a +;; load and a neg. That's what mips_move_1word will generate anyhow. + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "const_int_operand" ""))] + "TARGET_MIPS16 && reload_completed + && GET_CODE (operands[0]) == REG + && M16_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == CONST_INT + && INTVAL (operands[1]) < 0 + && INTVAL (operands[1]) > - 0x8000" + [(set (match_dup 0) (match_dup 1)) + (set (match_dup 0) (neg:SI (match_dup 0)))] + " +{ + operands[1] = GEN_INT (- INTVAL (operands[1])); +}") + +;; Reload HILO_REGNUM in SI mode. This needs a scratch register in +;; order to set the sign bit correctly in the HI register. + +(define_expand "reload_outsi" + [(set (match_operand:SI 0 "general_operand" "=b") + (match_operand:SI 1 "register_operand" "b")) + (clobber (match_operand:SI 2 "register_operand" "=&d"))] + "TARGET_64BIT || TARGET_MIPS16" + " +{ + if (TARGET_64BIT + && GET_CODE (operands[0]) == REG && REGNO (operands[0]) == HILO_REGNUM) + { + emit_insn (gen_movsi (gen_rtx (REG, SImode, 65), operands[1])); + emit_insn (gen_ashrsi3 (operands[2], operands[1], GEN_INT (31))); + emit_insn (gen_movsi (gen_rtx (REG, SImode, 64), operands[2])); + emit_insn (gen_rtx_USE (VOIDmode, operands[0])); + DONE; + } + /* Use a mult to reload LO on mips16. ??? This is hideous. */ + if (TARGET_MIPS16 + && GET_CODE (operands[0]) == REG && REGNO (operands[0]) == LO_REGNUM) + { + emit_insn (gen_movsi (operands[2], GEN_INT (1))); + /* This is gen_mulsi3_internal, but we need to fill in the + scratch registers. */ + emit_insn (gen_rtx (PARALLEL, VOIDmode, + gen_rtvec (3, + gen_rtx (SET, VOIDmode, + operands[0], + gen_rtx (MULT, SImode, + operands[1], + operands[2])), + gen_rtx (CLOBBER, VOIDmode, + gen_rtx (REG, SImode, 64)), + gen_rtx (CLOBBER, VOIDmode, + gen_rtx (REG, SImode, 66))))); + DONE; + } + /* FIXME: I don't know how to get a value into the HI register. */ + if (GET_CODE (operands[0]) == REG + && (TARGET_MIPS16 ? M16_REG_P (REGNO (operands[0])) + : GP_REG_P (REGNO (operands[0])))) + { + emit_move_insn (operands[0], operands[1]); + DONE; + } + /* This handles moves between a float register and HI/LO. */ + emit_move_insn (operands[2], operands[1]); + emit_move_insn (operands[0], operands[2]); + DONE; +}") + +;; Reload a value into HI or LO. There is no mthi or mtlo on mips16, +;; so we use a mult. ??? This is hideous, and we ought to figure out +;; something better. + +(define_expand "reload_insi" + [(set (match_operand:SI 0 "register_operand" "=b") + (match_operand:SI 1 "register_operand" "b")) + (clobber (match_operand:SI 2 "register_operand" "=&d"))] + "TARGET_MIPS16" + " +{ + if (TARGET_MIPS16 + && GET_CODE (operands[0]) == REG && REGNO (operands[0]) == LO_REGNUM) + { + emit_insn (gen_movsi (operands[2], GEN_INT (1))); + /* This is gen_mulsi3_internal, but we need to fill in the + scratch registers. */ + emit_insn (gen_rtx (PARALLEL, VOIDmode, + gen_rtvec (3, + gen_rtx (SET, VOIDmode, + operands[0], + gen_rtx (MULT, SImode, + operands[1], + operands[2])), + gen_rtx (CLOBBER, VOIDmode, + gen_rtx (REG, SImode, 64)), + gen_rtx (CLOBBER, VOIDmode, + gen_rtx (REG, SImode, 66))))); + DONE; + } + /* FIXME: I don't know how to get a value into the HI register. */ + emit_move_insn (operands[0], operands[1]); + DONE; +}") + +;; This insn handles moving CCmode values. It's really just a +;; slightly simplified copy of movsi_internal2, with additional cases +;; to move a condition register to a general register and to move +;; between the general registers and the floating point registers. + +(define_insn "movcc" + [(set (match_operand:CC 0 "nonimmediate_operand" "=d,*d,*d,*d,*R,*m,*d,*f,*f,*f,*f,*R,*m") + (match_operand:CC 1 "general_operand" "z,*d,*R,*m,*d,*d,*f,*d,*f,*R,*m,*f,*f"))] + "mips_isa >= 4 && TARGET_HARD_FLOAT" + "* return mips_move_1word (operands, insn, FALSE);" + [(set_attr "type" "move,move,load,load,store,store,xfer,xfer,move,load,load,store,store") + (set_attr "mode" "SI") + (set_attr "length" "2,1,1,2,1,2,1,1,1,1,2,1,2")]) + +;; Reload condition code registers. These need scratch registers. + +(define_expand "reload_incc" + [(set (match_operand:CC 0 "register_operand" "=z") + (match_operand:CC 1 "general_operand" "z")) + (clobber (match_operand:TF 2 "register_operand" "=&f"))] + "mips_isa >= 4 && TARGET_HARD_FLOAT" + " +{ + rtx source; + rtx fp1, fp2; + + /* This is called when are copying some value into a condition code + register. Operand 0 is the condition code register. Operand 1 + is the source. Operand 2 is a scratch register; we use TFmode + because we actually need two floating point registers. */ + if (! ST_REG_P (true_regnum (operands[0])) + || ! FP_REG_P (true_regnum (operands[2]))) + abort (); + + /* We need to get the source in SFmode so that the insn is + recognized. */ + if (GET_CODE (operands[1]) == MEM) + source = change_address (operands[1], SFmode, NULL_RTX); + else if (GET_CODE (operands[1]) == REG || GET_CODE (operands[1]) == SUBREG) + source = gen_rtx (REG, SFmode, true_regnum (operands[1])); + else + source = operands[1]; + + fp1 = gen_rtx (REG, SFmode, REGNO (operands[2])); + fp2 = gen_rtx (REG, SFmode, REGNO (operands[2]) + 1); + + emit_insn (gen_move_insn (fp1, source)); + emit_insn (gen_move_insn (fp2, gen_rtx (REG, SFmode, 0))); + emit_insn (gen_rtx (SET, VOIDmode, operands[0], + gen_rtx (LT, CCmode, fp2, fp1))); + + DONE; +}") + +(define_expand "reload_outcc" + [(set (match_operand:CC 0 "general_operand" "=z") + (match_operand:CC 1 "register_operand" "z")) + (clobber (match_operand:CC 2 "register_operand" "=&d"))] + "mips_isa >= 4 && TARGET_HARD_FLOAT" + " +{ + /* This is called when we are copying a condition code register out + to save it somewhere. Operand 0 should be the location we are + going to save it to. Operand 1 should be the condition code + register. Operand 2 should be a scratch general purpose register + created for us by reload. The mips_secondary_reload_class + function should have told reload that we don't need a scratch + register if the destination is a general purpose register anyhow. */ + if (ST_REG_P (true_regnum (operands[0])) + || GP_REG_P (true_regnum (operands[0])) + || ! ST_REG_P (true_regnum (operands[1])) + || ! GP_REG_P (true_regnum (operands[2]))) + abort (); + + /* All we have to do is copy the value from the condition code to + the data register, which movcc can handle, and then store the + value into the real final destination. */ + emit_insn (gen_move_insn (operands[2], operands[1])); + emit_insn (gen_move_insn (operands[0], operands[2])); + + DONE; +}") + +;; MIPS4 supports loading and storing a floating point register from +;; the sum of two general registers. We use two versions for each of +;; these four instructions: one where the two general registers are +;; SImode, and one where they are DImode. This is because general +;; registers will be in SImode when they hold 32 bit values, but, +;; since the 32 bit values are always sign extended, the [ls][wd]xc1 +;; instructions will still work correctly. + +;; ??? Perhaps it would be better to support these instructions by +;; modifying GO_IF_LEGITIMATE_ADDRESS and friends. However, since +;; these instructions can only be used to load and store floating +;; point registers, that would probably cause trouble in reload. + +(define_insn "" + [(set (match_operand:SF 0 "register_operand" "=f") + (mem:SF (plus:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "register_operand" "d"))))] + "mips_isa >= 4 && TARGET_HARD_FLOAT" + "lwxc1\\t%0,%1(%2)" + [(set_attr "type" "load") + (set_attr "mode" "SF") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:SF 0 "register_operand" "=f") + (mem:SF (plus:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_register_operand" "d"))))] + "mips_isa >= 4 && TARGET_HARD_FLOAT" + "lwxc1\\t%0,%1(%2)" + [(set_attr "type" "load") + (set_attr "mode" "SF") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:DF 0 "register_operand" "=f") + (mem:DF (plus:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "register_operand" "d"))))] + "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT" + "ldxc1\\t%0,%1(%2)" + [(set_attr "type" "load") + (set_attr "mode" "DF") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:DF 0 "register_operand" "=f") + (mem:DF (plus:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_register_operand" "d"))))] + "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT" + "ldxc1\\t%0,%1(%2)" + [(set_attr "type" "load") + (set_attr "mode" "DF") + (set_attr "length" "1")]) + +(define_insn "" + [(set (mem:SF (plus:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "register_operand" "d"))) + (match_operand:SF 0 "register_operand" "=f"))] + "mips_isa >= 4 && TARGET_HARD_FLOAT" + "swxc1\\t%0,%1(%2)" + [(set_attr "type" "store") + (set_attr "mode" "SF") + (set_attr "length" "1")]) + +(define_insn "" + [(set (mem:SF (plus:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_register_operand" "d"))) + (match_operand:SF 0 "register_operand" "=f"))] + "mips_isa >= 4 && TARGET_HARD_FLOAT" + "swxc1\\t%0,%1(%2)" + [(set_attr "type" "store") + (set_attr "mode" "SF") + (set_attr "length" "1")]) + +(define_insn "" + [(set (mem:DF (plus:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "register_operand" "d"))) + (match_operand:DF 0 "register_operand" "=f"))] + "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT" + "sdxc1\\t%0,%1(%2)" + [(set_attr "type" "store") + (set_attr "mode" "DF") + (set_attr "length" "1")]) + +(define_insn "" + [(set (mem:DF (plus:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_register_operand" "d"))) + (match_operand:DF 0 "register_operand" "=f"))] + "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT" + "sdxc1\\t%0,%1(%2)" + [(set_attr "type" "store") + (set_attr "mode" "DF") + (set_attr "length" "1")]) + +;; 16-bit Integer moves + +;; Unlike most other insns, the move insns can't be split with +;; different predicates, because register spilling and other parts of +;; the compiler, have memoized the insn number already. +;; Unsigned loads are used because BYTE_LOADS_ZERO_EXTEND is defined + +(define_expand "movhi" + [(set (match_operand:HI 0 "nonimmediate_operand" "") + (match_operand:HI 1 "general_operand" ""))] + "" + " +{ + if ((reload_in_progress | reload_completed) == 0 + && !register_operand (operands[0], HImode) + && !register_operand (operands[1], HImode) + && (TARGET_MIPS16 + || (GET_CODE (operands[1]) != CONST_INT + || INTVAL (operands[1]) != 0))) + { + rtx temp = force_reg (HImode, operands[1]); + emit_move_insn (operands[0], temp); + DONE; + } +}") + +;; The difference between these two is whether or not ints are allowed +;; in FP registers (off by default, use -mdebugh to enable). + +(define_insn "movhi_internal1" + [(set (match_operand:HI 0 "nonimmediate_operand" "=d,d,d,d,R,m,*d,*f,*f*z,*x,*d") + (match_operand:HI 1 "general_operand" "d,IK,R,m,dJ,dJ,*f*z,*d,*f,*d,*x"))] + "TARGET_DEBUG_H_MODE && !TARGET_MIPS16 + && (register_operand (operands[0], HImode) + || register_operand (operands[1], HImode) + || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0))" + "* return mips_move_1word (operands, insn, TRUE);" + [(set_attr "type" "move,arith,load,load,store,store,xfer,xfer,move,hilo,hilo") + (set_attr "mode" "HI") + (set_attr "length" "1,1,1,2,1,2,1,1,1,1,1")]) + +(define_insn "movhi_internal2" + [(set (match_operand:HI 0 "nonimmediate_operand" "=d,d,d,d,R,m,*d,*z,*x,*d") + (match_operand:HI 1 "general_operand" "d,IK,R,m,dJ,dJ,*z,*d,*d,*x"))] + "!TARGET_DEBUG_H_MODE && !TARGET_MIPS16 + && (register_operand (operands[0], HImode) + || register_operand (operands[1], HImode) + || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0))" + "* return mips_move_1word (operands, insn, TRUE);" + [(set_attr "type" "move,arith,load,load,store,store,xfer,xfer,hilo,hilo") + (set_attr "mode" "HI") + (set_attr "length" "1,1,1,2,1,2,1,1,1,1")]) + +(define_insn "" + [(set (match_operand:HI 0 "nonimmediate_operand" "=d,y,d,d,d,d,d,R,m,*d") + (match_operand:HI 1 "general_operand" "d,d,y,K,N,R,m,d,d,*x"))] + "TARGET_MIPS16 + && (register_operand (operands[0], HImode) + || register_operand (operands[1], HImode))" + "* return mips_move_1word (operands, insn, TRUE);" + [(set_attr "type" "move,move,move,arith,arith,load,load,store,store,hilo") + (set_attr "mode" "HI") + (set_attr_alternative "length" + [(const_int 1) + (const_int 1) + (const_int 1) + (if_then_else (match_operand:VOID 1 "m16_uimm8_1" "") + (const_int 1) + (const_int 2)) + (if_then_else (match_operand:VOID 1 "m16_nuimm8_1" "") + (const_int 2) + (const_int 3)) + (const_int 1) + (const_int 2) + (const_int 1) + (const_int 2) + (const_int 1)])]) + + +;; On the mips16, we can split lh $r,N($r) into an add and a load, +;; when the original load is a 4 byte instruction but the add and the +;; load are 2 2 byte instructions. + +(define_split + [(set (match_operand:HI 0 "register_operand" "") + (mem:HI (plus:SI (match_dup 0) + (match_operand:SI 1 "const_int_operand" ""))))] + "TARGET_MIPS16 && reload_completed + && GET_CODE (operands[0]) == REG + && M16_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == CONST_INT + && ((INTVAL (operands[1]) < 0 + && INTVAL (operands[1]) >= -0x80) + || (INTVAL (operands[1]) >= 32 * 2 + && INTVAL (operands[1]) <= 31 * 2 + 0x7e) + || (INTVAL (operands[1]) >= 0 + && INTVAL (operands[1]) < 32 * 2 + && (INTVAL (operands[1]) & 1) != 0))" + [(set (match_dup 0) (plus:SI (match_dup 0) (match_dup 1))) + (set (match_dup 0) (mem:HI (plus:SI (match_dup 0) (match_dup 2))))] + " +{ + HOST_WIDE_INT val = INTVAL (operands[1]); + + if (val < 0) + operands[2] = GEN_INT (0); + else if (val >= 32 * 2) + { + int off = val & 1; + + operands[1] = GEN_INT (0x7e + off); + operands[2] = GEN_INT (val - off - 0x7e); + } + else + { + int off = val & 1; + + operands[1] = GEN_INT (off); + operands[2] = GEN_INT (val - off); + } +}") + +;; 8-bit Integer moves + +;; Unlike most other insns, the move insns can't be split with +;; different predicates, because register spilling and other parts of +;; the compiler, have memoized the insn number already. +;; Unsigned loads are used because BYTE_LOADS_ZERO_EXTEND is defined + +(define_expand "movqi" + [(set (match_operand:QI 0 "nonimmediate_operand" "") + (match_operand:QI 1 "general_operand" ""))] + "" + " +{ + if ((reload_in_progress | reload_completed) == 0 + && !register_operand (operands[0], QImode) + && !register_operand (operands[1], QImode) + && (TARGET_MIPS16 + || (GET_CODE (operands[1]) != CONST_INT + || INTVAL (operands[1]) != 0))) + { + rtx temp = force_reg (QImode, operands[1]); + emit_move_insn (operands[0], temp); + DONE; + } +}") + +;; The difference between these two is whether or not ints are allowed +;; in FP registers (off by default, use -mdebugh to enable). + +(define_insn "movqi_internal1" + [(set (match_operand:QI 0 "nonimmediate_operand" "=d,d,d,d,R,m,*d,*f*z,*f,*x,*d") + (match_operand:QI 1 "general_operand" "d,IK,R,m,dJ,dJ,*f*z,*d,*f,*d,*x"))] + "TARGET_DEBUG_H_MODE && !TARGET_MIPS16 + && (register_operand (operands[0], QImode) + || register_operand (operands[1], QImode) + || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0))" + "* return mips_move_1word (operands, insn, TRUE);" + [(set_attr "type" "move,arith,load,load,store,store,xfer,xfer,move,hilo,hilo") + (set_attr "mode" "QI") + (set_attr "length" "1,1,1,2,1,2,1,1,1,1,1")]) + +(define_insn "movqi_internal2" + [(set (match_operand:QI 0 "nonimmediate_operand" "=d,d,d,d,R,m,*d,*z,*x,*d") + (match_operand:QI 1 "general_operand" "d,IK,R,m,dJ,dJ,*z,*d,*d,*x"))] + "!TARGET_DEBUG_H_MODE && !TARGET_MIPS16 + && (register_operand (operands[0], QImode) + || register_operand (operands[1], QImode) + || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0))" + "* return mips_move_1word (operands, insn, TRUE);" + [(set_attr "type" "move,arith,load,load,store,store,xfer,xfer,hilo,hilo") + (set_attr "mode" "QI") + (set_attr "length" "1,1,1,2,1,2,1,1,1,1")]) + +(define_insn "" + [(set (match_operand:QI 0 "nonimmediate_operand" "=d,y,d,d,d,d,d,R,m,*d") + (match_operand:QI 1 "general_operand" "d,d,y,K,N,R,m,d,d,*x"))] + "TARGET_MIPS16 + && (register_operand (operands[0], QImode) + || register_operand (operands[1], QImode))" + "* return mips_move_1word (operands, insn, TRUE);" + [(set_attr "type" "move,move,move,arith,arith,load,load,store,store,hilo") + (set_attr "mode" "QI") + (set_attr_alternative "length" + [(const_int 1) + (const_int 1) + (const_int 1) + (if_then_else (match_operand:VOID 1 "m16_uimm8_1" "") + (const_int 1) + (const_int 2)) + (if_then_else (match_operand:VOID 1 "m16_nuimm8_1" "") + (const_int 2) + (const_int 3)) + (const_int 1) + (const_int 2) + (const_int 1) + (const_int 2) + (const_int 1)])]) + + +;; On the mips16, we can split lb $r,N($r) into an add and a load, +;; when the original load is a 4 byte instruction but the add and the +;; load are 2 2 byte instructions. + +(define_split + [(set (match_operand:QI 0 "register_operand" "") + (mem:QI (plus:SI (match_dup 0) + (match_operand:SI 1 "const_int_operand" ""))))] + "TARGET_MIPS16 && reload_completed + && GET_CODE (operands[0]) == REG + && M16_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == CONST_INT + && ((INTVAL (operands[1]) < 0 + && INTVAL (operands[1]) >= -0x80) + || (INTVAL (operands[1]) >= 32 + && INTVAL (operands[1]) <= 31 + 0x7f))" + [(set (match_dup 0) (plus:SI (match_dup 0) (match_dup 1))) + (set (match_dup 0) (mem:QI (plus:SI (match_dup 0) (match_dup 2))))] + " +{ + HOST_WIDE_INT val = INTVAL (operands[1]); + + if (val < 0) + operands[2] = GEN_INT (0); + else + { + operands[1] = GEN_INT (0x7f); + operands[2] = GEN_INT (val - 0x7f); + } +}") + +;; 32-bit floating point moves + +(define_expand "movsf" + [(set (match_operand:SF 0 "nonimmediate_operand" "") + (match_operand:SF 1 "general_operand" ""))] + "" + " +{ + if ((reload_in_progress | reload_completed) == 0 + && !register_operand (operands[0], SFmode) + && !register_operand (operands[1], SFmode) + && (TARGET_MIPS16 + || ((GET_CODE (operands[1]) != CONST_INT || INTVAL (operands[1]) != 0) + && operands[1] != CONST0_RTX (SFmode)))) + { + rtx temp = force_reg (SFmode, operands[1]); + emit_move_insn (operands[0], temp); + DONE; + } +}") + +(define_insn "movsf_internal1" + [(set (match_operand:SF 0 "nonimmediate_operand" "=f,f,f,f,R,m,*f,*d,*d,*d,*d,*R,*m") + (match_operand:SF 1 "general_operand" "f,G,R,Fm,fG,fG,*d,*f,*G*d,*R,*F*m,*d,*d"))] + "TARGET_HARD_FLOAT + && (register_operand (operands[0], SFmode) + || register_operand (operands[1], SFmode) + || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0) + || operands[1] == CONST0_RTX (SFmode))" + "* return mips_move_1word (operands, insn, FALSE);" + [(set_attr "type" "move,xfer,load,load,store,store,xfer,xfer,move,load,load,store,store") + (set_attr "mode" "SF") + (set_attr "length" "1,1,1,2,1,2,1,1,1,1,2,1,2")]) + + +(define_insn "movsf_internal2" + [(set (match_operand:SF 0 "nonimmediate_operand" "=d,d,d,R,m") + (match_operand:SF 1 "general_operand" " Gd,R,Fm,d,d"))] + "TARGET_SOFT_FLOAT && !TARGET_MIPS16 + && (register_operand (operands[0], SFmode) + || register_operand (operands[1], SFmode) + || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0) + || operands[1] == CONST0_RTX (SFmode))" + "* return mips_move_1word (operands, insn, FALSE);" + [(set_attr "type" "move,load,load,store,store") + (set_attr "mode" "SF") + (set_attr "length" "1,1,2,1,2")]) + +(define_insn "" + [(set (match_operand:SF 0 "nonimmediate_operand" "=d,y,d,d,d,R,m") + (match_operand:SF 1 "general_operand" "d,d,y,R,Fm,d,d"))] + "TARGET_MIPS16 + && (register_operand (operands[0], SFmode) + || register_operand (operands[1], SFmode))" + "* return mips_move_1word (operands, insn, FALSE);" + [(set_attr "type" "move,move,move,load,load,store,store") + (set_attr "mode" "SF") + (set_attr "length" "1,1,1,1,2,1,2")]) + + +;; 64-bit floating point moves + +(define_expand "movdf" + [(set (match_operand:DF 0 "nonimmediate_operand" "") + (match_operand:DF 1 "general_operand" ""))] + "" + " +{ + if ((reload_in_progress | reload_completed) == 0 + && !register_operand (operands[0], DFmode) + && !register_operand (operands[1], DFmode) + && (TARGET_MIPS16 + || ((GET_CODE (operands[1]) != CONST_INT || INTVAL (operands[1]) != 0) + && operands[1] != CONST0_RTX (DFmode)))) + { + rtx temp = force_reg (DFmode, operands[1]); + emit_move_insn (operands[0], temp); + DONE; + } +}") + +(define_insn "movdf_internal1" + [(set (match_operand:DF 0 "nonimmediate_operand" "=f,f,f,R,To,f,*f,*d,*d,*d,*d,*R,*T") + (match_operand:DF 1 "general_operand" "f,R,To,fG,fG,F,*d,*f,*d*G,*R,*T*F,*d,*d"))] + "TARGET_HARD_FLOAT && !(TARGET_FLOAT64 && !TARGET_64BIT) + && TARGET_DOUBLE_FLOAT + && (register_operand (operands[0], DFmode) + || register_operand (operands[1], DFmode) + || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0) + || operands[1] == CONST0_RTX (DFmode))" + "* return mips_move_2words (operands, insn); " + [(set_attr "type" "move,load,load,store,store,load,xfer,xfer,move,load,load,store,store") + (set_attr "mode" "DF") + (set_attr "length" "1,2,4,2,4,4,2,2,2,2,4,2,4")]) + +(define_insn "movdf_internal1a" + [(set (match_operand:DF 0 "nonimmediate_operand" "=f,f,R,R,To,To,f,*d,*d,*d,*To,*R") + (match_operand:DF 1 "general_operand" " f,To,f,G,f,G,F,*F,*To,*R,*d,*d"))] + "TARGET_HARD_FLOAT && (TARGET_FLOAT64 && !TARGET_64BIT) + && TARGET_DOUBLE_FLOAT + && (register_operand (operands[0], DFmode) + || register_operand (operands[1], DFmode) + || (GET_CODE (operands [0]) == MEM + && ((GET_CODE (operands[1]) == CONST_INT + && INTVAL (operands[1]) == 0) + || operands[1] == CONST0_RTX (DFmode))))" + "* return mips_move_2words (operands, insn); " + [(set_attr "type" "move,load,store,store,store,store,load,load,load,load,store,store") + (set_attr "mode" "DF") + (set_attr "length" "1,2,1,1,2,2,2,2,2,1,2,1")]) + +(define_insn "movdf_internal2" + [(set (match_operand:DF 0 "nonimmediate_operand" "=d,d,d,R,To") + (match_operand:DF 1 "general_operand" "dG,R,ToF,d,d"))] + "(TARGET_SOFT_FLOAT || TARGET_SINGLE_FLOAT) && !TARGET_MIPS16 + && (register_operand (operands[0], DFmode) + || register_operand (operands[1], DFmode) + || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0) + || operands[1] == CONST0_RTX (DFmode))" + "* return mips_move_2words (operands, insn); " + [(set_attr "type" "move,load,load,store,store") + (set_attr "mode" "DF") + (set_attr "length" "2,2,4,2,4")]) + +(define_insn "" + [(set (match_operand:DF 0 "nonimmediate_operand" "=d,y,d,d,d,R,To") + (match_operand:DF 1 "general_operand" "d,d,y,R,ToF,d,d"))] + "TARGET_MIPS16 + && (register_operand (operands[0], DFmode) + || register_operand (operands[1], DFmode))" + "* return mips_move_2words (operands, insn);" + [(set_attr "type" "move,move,move,load,load,store,store") + (set_attr "mode" "DF") + (set_attr "length" "2,2,2,2,4,2,4")]) + +(define_split + [(set (match_operand:DF 0 "register_operand" "") + (match_operand:DF 1 "register_operand" ""))] + "reload_completed && !TARGET_64BIT && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE + && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))" + [(set (subreg:SI (match_dup 0) 0) (subreg:SI (match_dup 1) 0)) + (set (subreg:SI (match_dup 0) 1) (subreg:SI (match_dup 1) 1))] + "") + +;; Instructions to load the global pointer register. +;; This is volatile to make sure that the scheduler won't move any symbol_ref +;; uses in front of it. All symbol_refs implicitly use the gp reg. + +(define_insn "loadgp" + [(set (reg:DI 28) + (unspec_volatile:DI [(match_operand:DI 0 "address_operand" "") + (match_operand:DI 1 "register_operand" "")] 2)) + (clobber (reg:DI 1))] + "" + "%[lui\\t$1,%%hi(%%neg(%%gp_rel(%a0)))\\n\\taddiu\\t$1,$1,%%lo(%%neg(%%gp_rel(%a0)))\\n\\tdaddu\\t$gp,$1,%1%]" + [(set_attr "type" "move") + (set_attr "mode" "DI") + (set_attr "length" "3")]) + +;; Block moves, see mips.c for more details. +;; Argument 0 is the destination +;; Argument 1 is the source +;; Argument 2 is the length +;; Argument 3 is the alignment + +(define_expand "movstrsi" + [(parallel [(set (match_operand:BLK 0 "general_operand" "") + (match_operand:BLK 1 "general_operand" "")) + (use (match_operand:SI 2 "arith32_operand" "")) + (use (match_operand:SI 3 "immediate_operand" ""))])] + "!TARGET_MIPS16" + " +{ + if (operands[0]) /* avoid unused code messages */ + { + expand_block_move (operands); + DONE; + } +}") + +;; Insn generated by block moves + +(define_insn "movstrsi_internal" + [(set (match_operand:BLK 0 "memory_operand" "=o") ;; destination + (match_operand:BLK 1 "memory_operand" "o")) ;; source + (clobber (match_scratch:SI 4 "=&d")) ;; temp 1 + (clobber (match_scratch:SI 5 "=&d")) ;; temp 2 + (clobber (match_scratch:SI 6 "=&d")) ;; temp 3 + (clobber (match_scratch:SI 7 "=&d")) ;; temp 4 + (use (match_operand:SI 2 "small_int" "I")) ;; # bytes to move + (use (match_operand:SI 3 "small_int" "I")) ;; alignment + (use (const_int 0))] ;; normal block move + "" + "* return output_block_move (insn, operands, 4, BLOCK_MOVE_NORMAL);" + [(set_attr "type" "store") + (set_attr "mode" "none") + (set_attr "length" "20")]) + +;; We need mips16 versions, because an offset from the stack pointer +;; is not offsettable, since the stack pointer can only handle 4 and 8 +;; byte loads. + +(define_insn "" + [(set (match_operand:BLK 0 "memory_operand" "=d") ;; destination + (match_operand:BLK 1 "memory_operand" "d")) ;; source + (clobber (match_scratch:SI 4 "=&d")) ;; temp 1 + (clobber (match_scratch:SI 5 "=&d")) ;; temp 2 + (clobber (match_scratch:SI 6 "=&d")) ;; temp 3 + (clobber (match_scratch:SI 7 "=&d")) ;; temp 4 + (use (match_operand:SI 2 "small_int" "I")) ;; # bytes to move + (use (match_operand:SI 3 "small_int" "I")) ;; alignment + (use (const_int 0))] ;; normal block move + "TARGET_MIPS16" + "* return output_block_move (insn, operands, 4, BLOCK_MOVE_NORMAL);" + [(set_attr "type" "multi") + (set_attr "mode" "none") + (set_attr "length" "20")]) + +(define_insn "" + [(set (match_operand:BLK 0 "memory_operand" "=d") ;; destination + (match_operand:BLK 1 "memory_operand" "o")) ;; source + (clobber (match_scratch:SI 4 "=&d")) ;; temp 1 + (clobber (match_scratch:SI 5 "=&d")) ;; temp 2 + (clobber (match_scratch:SI 6 "=&d")) ;; temp 3 + (clobber (match_scratch:SI 7 "=&d")) ;; temp 4 + (use (match_operand:SI 2 "small_int" "I")) ;; # bytes to move + (use (match_operand:SI 3 "small_int" "I")) ;; alignment + (use (const_int 0))] ;; normal block move + "TARGET_MIPS16" + "* return output_block_move (insn, operands, 4, BLOCK_MOVE_NORMAL);" + [(set_attr "type" "multi") + (set_attr "mode" "none") + (set_attr "length" "20")]) + +(define_insn "" + [(set (match_operand:BLK 0 "memory_operand" "=o") ;; destination + (match_operand:BLK 1 "memory_operand" "d")) ;; source + (clobber (match_scratch:SI 4 "=&d")) ;; temp 1 + (clobber (match_scratch:SI 5 "=&d")) ;; temp 2 + (clobber (match_scratch:SI 6 "=&d")) ;; temp 3 + (clobber (match_scratch:SI 7 "=&d")) ;; temp 4 + (use (match_operand:SI 2 "small_int" "I")) ;; # bytes to move + (use (match_operand:SI 3 "small_int" "I")) ;; alignment + (use (const_int 0))] ;; normal block move + "TARGET_MIPS16" + "* return output_block_move (insn, operands, 4, BLOCK_MOVE_NORMAL);" + [(set_attr "type" "multi") + (set_attr "mode" "none") + (set_attr "length" "20")]) + +;; Split a block move into 2 parts, the first part is everything +;; except for the last move, and the second part is just the last +;; store, which is exactly 1 instruction (ie, not a usw), so it can +;; fill a delay slot. This also prevents a bug in delayed branches +;; from showing up, which reuses one of the registers in our clobbers. + +(define_split + [(set (mem:BLK (match_operand:SI 0 "register_operand" "")) + (mem:BLK (match_operand:SI 1 "register_operand" ""))) + (clobber (match_operand:SI 4 "register_operand" "")) + (clobber (match_operand:SI 5 "register_operand" "")) + (clobber (match_operand:SI 6 "register_operand" "")) + (clobber (match_operand:SI 7 "register_operand" "")) + (use (match_operand:SI 2 "small_int" "")) + (use (match_operand:SI 3 "small_int" "")) + (use (const_int 0))] + + "reload_completed && !TARGET_DEBUG_D_MODE && INTVAL (operands[2]) > 0" + + ;; All but the last move + [(parallel [(set (mem:BLK (match_dup 0)) + (mem:BLK (match_dup 1))) + (clobber (match_dup 4)) + (clobber (match_dup 5)) + (clobber (match_dup 6)) + (clobber (match_dup 7)) + (use (match_dup 2)) + (use (match_dup 3)) + (use (const_int 1))]) + + ;; The last store, so it can fill a delay slot + (parallel [(set (mem:BLK (match_dup 0)) + (mem:BLK (match_dup 1))) + (clobber (match_dup 4)) + (clobber (match_dup 5)) + (clobber (match_dup 6)) + (clobber (match_dup 7)) + (use (match_dup 2)) + (use (match_dup 3)) + (use (const_int 2))])] + + "") + +(define_insn "movstrsi_internal2" + [(set (match_operand:BLK 0 "memory_operand" "=o") ;; destination + (match_operand:BLK 1 "memory_operand" "o")) ;; source + (clobber (match_scratch:SI 4 "=&d")) ;; temp 1 + (clobber (match_scratch:SI 5 "=&d")) ;; temp 2 + (clobber (match_scratch:SI 6 "=&d")) ;; temp 3 + (clobber (match_scratch:SI 7 "=&d")) ;; temp 4 + (use (match_operand:SI 2 "small_int" "I")) ;; # bytes to move + (use (match_operand:SI 3 "small_int" "I")) ;; alignment + (use (const_int 1))] ;; all but last store + "" + "* return output_block_move (insn, operands, 4, BLOCK_MOVE_NOT_LAST);" + [(set_attr "type" "store") + (set_attr "mode" "none") + (set_attr "length" "20")]) + +(define_insn "" + [(set (match_operand:BLK 0 "memory_operand" "=d") ;; destination + (match_operand:BLK 1 "memory_operand" "d")) ;; source + (clobber (match_scratch:SI 4 "=&d")) ;; temp 1 + (clobber (match_scratch:SI 5 "=&d")) ;; temp 2 + (clobber (match_scratch:SI 6 "=&d")) ;; temp 3 + (clobber (match_scratch:SI 7 "=&d")) ;; temp 4 + (use (match_operand:SI 2 "small_int" "I")) ;; # bytes to move + (use (match_operand:SI 3 "small_int" "I")) ;; alignment + (use (const_int 1))] ;; all but last store + "TARGET_MIPS16" + "* return output_block_move (insn, operands, 4, BLOCK_MOVE_NOT_LAST);" + [(set_attr "type" "multi") + (set_attr "mode" "none") + (set_attr "length" "20")]) + +(define_insn "movstrsi_internal3" + [(set (match_operand:BLK 0 "memory_operand" "=Ro") ;; destination + (match_operand:BLK 1 "memory_operand" "Ro")) ;; source + (clobber (match_scratch:SI 4 "=&d")) ;; temp 1 + (clobber (match_scratch:SI 5 "=&d")) ;; temp 2 + (clobber (match_scratch:SI 6 "=&d")) ;; temp 3 + (clobber (match_scratch:SI 7 "=&d")) ;; temp 4 + (use (match_operand:SI 2 "small_int" "I")) ;; # bytes to move + (use (match_operand:SI 3 "small_int" "I")) ;; alignment + (use (const_int 2))] ;; just last store of block move + "" + "* return output_block_move (insn, operands, 4, BLOCK_MOVE_LAST);" + [(set_attr "type" "store") + (set_attr "mode" "none") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:BLK 0 "memory_operand" "=d") ;; destination + (match_operand:BLK 1 "memory_operand" "d")) ;; source + (clobber (match_scratch:SI 4 "=&d")) ;; temp 1 + (clobber (match_scratch:SI 5 "=&d")) ;; temp 2 + (clobber (match_scratch:SI 6 "=&d")) ;; temp 3 + (clobber (match_scratch:SI 7 "=&d")) ;; temp 4 + (use (match_operand:SI 2 "small_int" "I")) ;; # bytes to move + (use (match_operand:SI 3 "small_int" "I")) ;; alignment + (use (const_int 2))] ;; just last store of block move + "TARGET_MIPS16" + "* return output_block_move (insn, operands, 4, BLOCK_MOVE_LAST);" + [(set_attr "type" "store") + (set_attr "mode" "none") + (set_attr "length" "1")]) + + +;; +;; .................... +;; +;; SHIFTS +;; +;; .................... + +;; Many of these instructions uses trivial define_expands, because we +;; want to use a different set of constraints when TARGET_MIPS16. + +(define_expand "ashlsi3" + [(set (match_operand:SI 0 "register_operand" "=d") + (ashift:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "arith_operand" "dI")))] + "" + " +{ + /* On the mips16, a shift of more than 8 is a four byte instruction, + so, for a shift between 8 and 16, it is just as fast to do two + shifts of 8 or less. If there is a lot of shifting going on, we + may win in CSE. Otherwise combine will put the shifts back + together again. This can be called by function_arg, so we must + be careful not to allocate a new register if we've reached the + reload pass. */ + if (TARGET_MIPS16 + && optimize + && GET_CODE (operands[2]) == CONST_INT + && INTVAL (operands[2]) > 8 + && INTVAL (operands[2]) <= 16 + && ! reload_in_progress + && ! reload_completed) + { + rtx temp = gen_reg_rtx (SImode); + + emit_insn (gen_ashlsi3_internal2 (temp, operands[1], GEN_INT (8))); + emit_insn (gen_ashlsi3_internal2 (operands[0], temp, + GEN_INT (INTVAL (operands[2]) - 8))); + DONE; + } +}") + +(define_insn "ashlsi3_internal1" + [(set (match_operand:SI 0 "register_operand" "=d") + (ashift:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "arith_operand" "dI")))] + "!TARGET_MIPS16" + "* +{ + if (GET_CODE (operands[2]) == CONST_INT) + operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f); + + return \"sll\\t%0,%1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_insn "ashlsi3_internal2" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (ashift:SI (match_operand:SI 1 "register_operand" "0,d") + (match_operand:SI 2 "arith_operand" "d,I")))] + "TARGET_MIPS16" + "* +{ + if (which_alternative == 0) + return \"sll\\t%0,%2\"; + + if (GET_CODE (operands[2]) == CONST_INT) + operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f); + + return \"sll\\t%0,%1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr_alternative "length" + [(const_int 1) + (if_then_else (match_operand:VOID 2 "m16_uimm3_b" "") + (const_int 1) + (const_int 2))])]) + +;; On the mips16, we can split a 4 byte shift into 2 2 byte shifts. + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (ashift:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "const_int_operand" "")))] + "TARGET_MIPS16 + && reload_completed + && GET_CODE (operands[2]) == CONST_INT + && INTVAL (operands[2]) > 8 + && INTVAL (operands[2]) <= 16" + [(set (match_dup 0) (ashift:SI (match_dup 1) (const_int 8))) + (set (match_dup 0) (ashift:SI (match_dup 0) (match_dup 2)))] +" +{ + operands[2] = GEN_INT (INTVAL (operands[2]) - 8); +}") + +(define_expand "ashldi3" + [(parallel [(set (match_operand:DI 0 "register_operand" "") + (ashift:DI (match_operand:DI 1 "se_register_operand" "") + (match_operand:SI 2 "arith_operand" ""))) + (clobber (match_dup 3))])] + "TARGET_64BIT || (!TARGET_DEBUG_G_MODE && !TARGET_MIPS16)" + " +{ + if (TARGET_64BIT) + { + /* On the mips16, a shift of more than 8 is a four byte + instruction, so, for a shift between 8 and 16, it is just as + fast to do two shifts of 8 or less. If there is a lot of + shifting going on, we may win in CSE. Otherwise combine will + put the shifts back together again. This can be called by + function_arg, so we must be careful not to allocate a new + register if we've reached the reload pass. */ + if (TARGET_MIPS16 + && optimize + && GET_CODE (operands[2]) == CONST_INT + && INTVAL (operands[2]) > 8 + && INTVAL (operands[2]) <= 16 + && ! reload_in_progress + && ! reload_completed) + { + rtx temp = gen_reg_rtx (DImode); + + emit_insn (gen_ashldi3_internal4 (temp, operands[1], GEN_INT (8))); + emit_insn (gen_ashldi3_internal4 (operands[0], temp, + GEN_INT (INTVAL (operands[2]) - 8))); + DONE; + } + + emit_insn (gen_ashldi3_internal4 (operands[0], operands[1], + operands[2])); + DONE; + } + + operands[3] = gen_reg_rtx (SImode); +}") + + +(define_insn "ashldi3_internal" + [(set (match_operand:DI 0 "register_operand" "=&d") + (ashift:DI (match_operand:DI 1 "register_operand" "d") + (match_operand:SI 2 "register_operand" "d"))) + (clobber (match_operand:SI 3 "register_operand" "=d"))] + "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16" + "* +{ + operands[4] = const0_rtx; + dslots_jump_total += 3; + dslots_jump_filled += 2; + + return \"sll\\t%3,%2,26\\n\\ +\\tbgez\\t%3,1f\\n\\ +\\tsll\\t%M0,%L1,%2\\n\\ +\\t%(b\\t3f\\n\\ +\\tmove\\t%L0,%z4%)\\n\\ +\\n\\ +1:\\n\\ +\\t%(beq\\t%3,%z4,2f\\n\\ +\\tsll\\t%M0,%M1,%2%)\\n\\ +\\n\\ +\\tsubu\\t%3,%z4,%2\\n\\ +\\tsrl\\t%3,%L1,%3\\n\\ +\\tor\\t%M0,%M0,%3\\n\\ +2:\\n\\ +\\tsll\\t%L0,%L1,%2\\n\\ +3:\"; +}" + [(set_attr "type" "darith") + (set_attr "mode" "SI") + (set_attr "length" "12")]) + + +(define_insn "ashldi3_internal2" + [(set (match_operand:DI 0 "register_operand" "=d") + (ashift:DI (match_operand:DI 1 "register_operand" "d") + (match_operand:SI 2 "small_int" "IJK"))) + (clobber (match_operand:SI 3 "register_operand" "=d"))] + "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16 + && (INTVAL (operands[2]) & 32) != 0" + "* +{ + operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f); + operands[4] = const0_rtx; + return \"sll\\t%M0,%L1,%2\;move\\t%L0,%z4\"; +}" + [(set_attr "type" "darith") + (set_attr "mode" "DI") + (set_attr "length" "2")]) + + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (ashift:DI (match_operand:DI 1 "register_operand" "") + (match_operand:SI 2 "small_int" ""))) + (clobber (match_operand:SI 3 "register_operand" ""))] + "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT + && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16 + && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER + && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER + && (INTVAL (operands[2]) & 32) != 0" + + [(set (subreg:SI (match_dup 0) 1) (ashift:SI (subreg:SI (match_dup 1) 0) (match_dup 2))) + (set (subreg:SI (match_dup 0) 0) (const_int 0))] + + "operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);") + + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (ashift:DI (match_operand:DI 1 "register_operand" "") + (match_operand:SI 2 "small_int" ""))) + (clobber (match_operand:SI 3 "register_operand" ""))] + "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT + && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16 + && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER + && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER + && (INTVAL (operands[2]) & 32) != 0" + + [(set (subreg:SI (match_dup 0) 0) (ashift:SI (subreg:SI (match_dup 1) 1) (match_dup 2))) + (set (subreg:SI (match_dup 0) 1) (const_int 0))] + + "operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);") + + +(define_insn "ashldi3_internal3" + [(set (match_operand:DI 0 "register_operand" "=d") + (ashift:DI (match_operand:DI 1 "register_operand" "d") + (match_operand:SI 2 "small_int" "IJK"))) + (clobber (match_operand:SI 3 "register_operand" "=d"))] + "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16 + && (INTVAL (operands[2]) & 63) < 32 + && (INTVAL (operands[2]) & 63) != 0" + "* +{ + int amount = INTVAL (operands[2]); + + operands[2] = GEN_INT ((amount & 31)); + operands[4] = const0_rtx; + operands[5] = GEN_INT (((-amount) & 31)); + + return \"sll\\t%M0,%M1,%2\;srl\\t%3,%L1,%5\;or\\t%M0,%M0,%3\;sll\\t%L0,%L1,%2\"; +}" + [(set_attr "type" "darith") + (set_attr "mode" "DI") + (set_attr "length" "4")]) + + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (ashift:DI (match_operand:DI 1 "register_operand" "") + (match_operand:SI 2 "small_int" ""))) + (clobber (match_operand:SI 3 "register_operand" ""))] + "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT + && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16 + && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER + && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER + && (INTVAL (operands[2]) & 63) < 32 + && (INTVAL (operands[2]) & 63) != 0" + + [(set (subreg:SI (match_dup 0) 1) + (ashift:SI (subreg:SI (match_dup 1) 1) + (match_dup 2))) + + (set (match_dup 3) + (lshiftrt:SI (subreg:SI (match_dup 1) 0) + (match_dup 4))) + + (set (subreg:SI (match_dup 0) 1) + (ior:SI (subreg:SI (match_dup 0) 1) + (match_dup 3))) + + (set (subreg:SI (match_dup 0) 0) + (ashift:SI (subreg:SI (match_dup 1) 0) + (match_dup 2)))] + " +{ + int amount = INTVAL (operands[2]); + operands[2] = GEN_INT ((amount & 31)); + operands[4] = GEN_INT (((-amount) & 31)); +}") + + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (ashift:DI (match_operand:DI 1 "register_operand" "") + (match_operand:SI 2 "small_int" ""))) + (clobber (match_operand:SI 3 "register_operand" ""))] + "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT + && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16 + && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER + && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER + && (INTVAL (operands[2]) & 63) < 32 + && (INTVAL (operands[2]) & 63) != 0" + + [(set (subreg:SI (match_dup 0) 0) + (ashift:SI (subreg:SI (match_dup 1) 0) + (match_dup 2))) + + (set (match_dup 3) + (lshiftrt:SI (subreg:SI (match_dup 1) 1) + (match_dup 4))) + + (set (subreg:SI (match_dup 0) 0) + (ior:SI (subreg:SI (match_dup 0) 0) + (match_dup 3))) + + (set (subreg:SI (match_dup 0) 1) + (ashift:SI (subreg:SI (match_dup 1) 1) + (match_dup 2)))] + " +{ + int amount = INTVAL (operands[2]); + operands[2] = GEN_INT ((amount & 31)); + operands[4] = GEN_INT (((-amount) & 31)); +}") + + +(define_insn "ashldi3_internal4" + [(set (match_operand:DI 0 "register_operand" "=d") + (ashift:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:SI 2 "arith_operand" "dI")))] + "TARGET_64BIT && !TARGET_MIPS16" + "* +{ + if (GET_CODE (operands[2]) == CONST_INT) + operands[2] = GEN_INT (INTVAL (operands[2]) & 0x3f); + + return \"dsll\\t%0,%1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (ashift:DI (match_operand:DI 1 "se_register_operand" "0,d") + (match_operand:SI 2 "arith_operand" "d,I")))] + "TARGET_64BIT && TARGET_MIPS16" + "* +{ + if (which_alternative == 0) + return \"dsll\\t%0,%2\"; + + if (GET_CODE (operands[2]) == CONST_INT) + operands[2] = GEN_INT (INTVAL (operands[2]) & 0x3f); + + return \"dsll\\t%0,%1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr_alternative "length" + [(const_int 1) + (if_then_else (match_operand:VOID 2 "m16_uimm3_b" "") + (const_int 1) + (const_int 2))])]) + + +;; On the mips16, we can split a 4 byte shift into 2 2 byte shifts. + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (ashift:DI (match_operand:DI 1 "register_operand" "") + (match_operand:SI 2 "const_int_operand" "")))] + "TARGET_MIPS16 && TARGET_64BIT + && reload_completed + && GET_CODE (operands[2]) == CONST_INT + && INTVAL (operands[2]) > 8 + && INTVAL (operands[2]) <= 16" + [(set (match_dup 0) (ashift:DI (match_dup 1) (const_int 8))) + (set (match_dup 0) (ashift:DI (match_dup 0) (match_dup 2)))] +" +{ + operands[2] = GEN_INT (INTVAL (operands[2]) - 8); +}") + +(define_expand "ashrsi3" + [(set (match_operand:SI 0 "register_operand" "=d") + (ashiftrt:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "arith_operand" "dI")))] + "" + " +{ + /* On the mips16, a shift of more than 8 is a four byte instruction, + so, for a shift between 8 and 16, it is just as fast to do two + shifts of 8 or less. If there is a lot of shifting going on, we + may win in CSE. Otherwise combine will put the shifts back + together again. */ + if (TARGET_MIPS16 + && optimize + && GET_CODE (operands[2]) == CONST_INT + && INTVAL (operands[2]) > 8 + && INTVAL (operands[2]) <= 16) + { + rtx temp = gen_reg_rtx (SImode); + + emit_insn (gen_ashrsi3_internal2 (temp, operands[1], GEN_INT (8))); + emit_insn (gen_ashrsi3_internal2 (operands[0], temp, + GEN_INT (INTVAL (operands[2]) - 8))); + DONE; + } +}") + +(define_insn "ashrsi3_internal1" + [(set (match_operand:SI 0 "register_operand" "=d") + (ashiftrt:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "arith_operand" "dI")))] + "!TARGET_MIPS16" + "* +{ + if (GET_CODE (operands[2]) == CONST_INT) + operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f); + + return \"sra\\t%0,%1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_insn "ashrsi3_internal2" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (ashiftrt:SI (match_operand:SI 1 "register_operand" "0,d") + (match_operand:SI 2 "arith_operand" "d,I")))] + "TARGET_MIPS16" + "* +{ + if (which_alternative == 0) + return \"sra\\t%0,%2\"; + + if (GET_CODE (operands[2]) == CONST_INT) + operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f); + + return \"sra\\t%0,%1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr_alternative "length" + [(const_int 1) + (if_then_else (match_operand:VOID 2 "m16_uimm3_b" "") + (const_int 1) + (const_int 2))])]) + + +;; On the mips16, we can split a 4 byte shift into 2 2 byte shifts. + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (ashiftrt:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "const_int_operand" "")))] + "TARGET_MIPS16 + && reload_completed + && GET_CODE (operands[2]) == CONST_INT + && INTVAL (operands[2]) > 8 + && INTVAL (operands[2]) <= 16" + [(set (match_dup 0) (ashiftrt:SI (match_dup 1) (const_int 8))) + (set (match_dup 0) (ashiftrt:SI (match_dup 0) (match_dup 2)))] +" +{ + operands[2] = GEN_INT (INTVAL (operands[2]) - 8); +}") + +(define_expand "ashrdi3" + [(parallel [(set (match_operand:DI 0 "register_operand" "") + (ashiftrt:DI (match_operand:DI 1 "se_register_operand" "") + (match_operand:SI 2 "arith_operand" ""))) + (clobber (match_dup 3))])] + "TARGET_64BIT || (!TARGET_DEBUG_G_MODE && !TARGET_MIPS16)" + " +{ + if (TARGET_64BIT) + { + /* On the mips16, a shift of more than 8 is a four byte + instruction, so, for a shift between 8 and 16, it is just as + fast to do two shifts of 8 or less. If there is a lot of + shifting going on, we may win in CSE. Otherwise combine will + put the shifts back together again. */ + if (TARGET_MIPS16 + && optimize + && GET_CODE (operands[2]) == CONST_INT + && INTVAL (operands[2]) > 8 + && INTVAL (operands[2]) <= 16) + { + rtx temp = gen_reg_rtx (DImode); + + emit_insn (gen_ashrdi3_internal4 (temp, operands[1], GEN_INT (8))); + emit_insn (gen_ashrdi3_internal4 (operands[0], temp, + GEN_INT (INTVAL (operands[2]) - 8))); + DONE; + } + + emit_insn (gen_ashrdi3_internal4 (operands[0], operands[1], + operands[2])); + DONE; + } + + operands[3] = gen_reg_rtx (SImode); +}") + + +(define_insn "ashrdi3_internal" + [(set (match_operand:DI 0 "register_operand" "=&d") + (ashiftrt:DI (match_operand:DI 1 "register_operand" "d") + (match_operand:SI 2 "register_operand" "d"))) + (clobber (match_operand:SI 3 "register_operand" "=d"))] + "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16" + "* +{ + operands[4] = const0_rtx; + dslots_jump_total += 3; + dslots_jump_filled += 2; + + return \"sll\\t%3,%2,26\\n\\ +\\tbgez\\t%3,1f\\n\\ +\\tsra\\t%L0,%M1,%2\\n\\ +\\t%(b\\t3f\\n\\ +\\tsra\\t%M0,%M1,31%)\\n\\ +\\n\\ +1:\\n\\ +\\t%(beq\\t%3,%z4,2f\\n\\ +\\tsrl\\t%L0,%L1,%2%)\\n\\ +\\n\\ +\\tsubu\\t%3,%z4,%2\\n\\ +\\tsll\\t%3,%M1,%3\\n\\ +\\tor\\t%L0,%L0,%3\\n\\ +2:\\n\\ +\\tsra\\t%M0,%M1,%2\\n\\ +3:\"; +}" + [(set_attr "type" "darith") + (set_attr "mode" "DI") + (set_attr "length" "12")]) + + +(define_insn "ashrdi3_internal2" + [(set (match_operand:DI 0 "register_operand" "=d") + (ashiftrt:DI (match_operand:DI 1 "register_operand" "d") + (match_operand:SI 2 "small_int" "IJK"))) + (clobber (match_operand:SI 3 "register_operand" "=d"))] + "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && (INTVAL (operands[2]) & 32) != 0" + "* +{ + operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f); + return \"sra\\t%L0,%M1,%2\;sra\\t%M0,%M1,31\"; +}" + [(set_attr "type" "darith") + (set_attr "mode" "DI") + (set_attr "length" "2")]) + + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (ashiftrt:DI (match_operand:DI 1 "register_operand" "") + (match_operand:SI 2 "small_int" ""))) + (clobber (match_operand:SI 3 "register_operand" ""))] + "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE + && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER + && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER + && (INTVAL (operands[2]) & 32) != 0" + + [(set (subreg:SI (match_dup 0) 0) (ashiftrt:SI (subreg:SI (match_dup 1) 1) (match_dup 2))) + (set (subreg:SI (match_dup 0) 1) (ashiftrt:SI (subreg:SI (match_dup 1) 1) (const_int 31)))] + + "operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);") + + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (ashiftrt:DI (match_operand:DI 1 "register_operand" "") + (match_operand:SI 2 "small_int" ""))) + (clobber (match_operand:SI 3 "register_operand" ""))] + "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE + && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER + && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER + && (INTVAL (operands[2]) & 32) != 0" + + [(set (subreg:SI (match_dup 0) 1) (ashiftrt:SI (subreg:SI (match_dup 1) 0) (match_dup 2))) + (set (subreg:SI (match_dup 0) 0) (ashiftrt:SI (subreg:SI (match_dup 1) 0) (const_int 31)))] + + "operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);") + + +(define_insn "ashrdi3_internal3" + [(set (match_operand:DI 0 "register_operand" "=d") + (ashiftrt:DI (match_operand:DI 1 "register_operand" "d") + (match_operand:SI 2 "small_int" "IJK"))) + (clobber (match_operand:SI 3 "register_operand" "=d"))] + "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16 + && (INTVAL (operands[2]) & 63) < 32 + && (INTVAL (operands[2]) & 63) != 0" + "* +{ + int amount = INTVAL (operands[2]); + + operands[2] = GEN_INT ((amount & 31)); + operands[4] = GEN_INT (((-amount) & 31)); + + return \"srl\\t%L0,%L1,%2\;sll\\t%3,%M1,%4\;or\\t%L0,%L0,%3\;sra\\t%M0,%M1,%2\"; +}" + [(set_attr "type" "darith") + (set_attr "mode" "DI") + (set_attr "length" "4")]) + + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (ashiftrt:DI (match_operand:DI 1 "register_operand" "") + (match_operand:SI 2 "small_int" ""))) + (clobber (match_operand:SI 3 "register_operand" ""))] + "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT + && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16 + && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER + && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER + && (INTVAL (operands[2]) & 63) < 32 + && (INTVAL (operands[2]) & 63) != 0" + + [(set (subreg:SI (match_dup 0) 0) + (lshiftrt:SI (subreg:SI (match_dup 1) 0) + (match_dup 2))) + + (set (match_dup 3) + (ashift:SI (subreg:SI (match_dup 1) 1) + (match_dup 4))) + + (set (subreg:SI (match_dup 0) 0) + (ior:SI (subreg:SI (match_dup 0) 0) + (match_dup 3))) + + (set (subreg:SI (match_dup 0) 1) + (ashiftrt:SI (subreg:SI (match_dup 1) 1) + (match_dup 2)))] + " +{ + int amount = INTVAL (operands[2]); + operands[2] = GEN_INT ((amount & 31)); + operands[4] = GEN_INT (((-amount) & 31)); +}") + + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (ashiftrt:DI (match_operand:DI 1 "register_operand" "") + (match_operand:SI 2 "small_int" ""))) + (clobber (match_operand:SI 3 "register_operand" ""))] + "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT + && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16 + && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER + && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER + && (INTVAL (operands[2]) & 63) < 32 + && (INTVAL (operands[2]) & 63) != 0" + + [(set (subreg:SI (match_dup 0) 1) + (lshiftrt:SI (subreg:SI (match_dup 1) 1) + (match_dup 2))) + + (set (match_dup 3) + (ashift:SI (subreg:SI (match_dup 1) 0) + (match_dup 4))) + + (set (subreg:SI (match_dup 0) 1) + (ior:SI (subreg:SI (match_dup 0) 1) + (match_dup 3))) + + (set (subreg:SI (match_dup 0) 0) + (ashiftrt:SI (subreg:SI (match_dup 1) 0) + (match_dup 2)))] + " +{ + int amount = INTVAL (operands[2]); + operands[2] = GEN_INT ((amount & 31)); + operands[4] = GEN_INT (((-amount) & 31)); +}") + + +(define_insn "ashrdi3_internal4" + [(set (match_operand:DI 0 "register_operand" "=d") + (ashiftrt:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:SI 2 "arith_operand" "dI")))] + "TARGET_64BIT && !TARGET_MIPS16" + "* +{ + if (GET_CODE (operands[2]) == CONST_INT) + operands[2] = GEN_INT (INTVAL (operands[2]) & 0x3f); + + return \"dsra\\t%0,%1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (ashiftrt:DI (match_operand:DI 1 "se_register_operand" "0,0") + (match_operand:SI 2 "arith_operand" "d,I")))] + "TARGET_64BIT && TARGET_MIPS16" + "* +{ + if (GET_CODE (operands[2]) == CONST_INT) + operands[2] = GEN_INT (INTVAL (operands[2]) & 0x3f); + + return \"dsra\\t%0,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr_alternative "length" + [(const_int 1) + (if_then_else (match_operand:VOID 2 "m16_uimm3_b" "") + (const_int 1) + (const_int 2))])]) + +;; On the mips16, we can split a 4 byte shift into 2 2 byte shifts. + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (ashiftrt:DI (match_operand:DI 1 "register_operand" "") + (match_operand:SI 2 "const_int_operand" "")))] + "TARGET_MIPS16 && TARGET_64BIT + && reload_completed + && GET_CODE (operands[2]) == CONST_INT + && INTVAL (operands[2]) > 8 + && INTVAL (operands[2]) <= 16" + [(set (match_dup 0) (ashiftrt:DI (match_dup 1) (const_int 8))) + (set (match_dup 0) (ashiftrt:DI (match_dup 0) (match_dup 2)))] +" +{ + operands[2] = GEN_INT (INTVAL (operands[2]) - 8); +}") + +(define_expand "lshrsi3" + [(set (match_operand:SI 0 "register_operand" "=d") + (lshiftrt:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "arith_operand" "dI")))] + "" + " +{ + /* On the mips16, a shift of more than 8 is a four byte instruction, + so, for a shift between 8 and 16, it is just as fast to do two + shifts of 8 or less. If there is a lot of shifting going on, we + may win in CSE. Otherwise combine will put the shifts back + together again. */ + if (TARGET_MIPS16 + && optimize + && GET_CODE (operands[2]) == CONST_INT + && INTVAL (operands[2]) > 8 + && INTVAL (operands[2]) <= 16) + { + rtx temp = gen_reg_rtx (SImode); + + emit_insn (gen_lshrsi3_internal2 (temp, operands[1], GEN_INT (8))); + emit_insn (gen_lshrsi3_internal2 (operands[0], temp, + GEN_INT (INTVAL (operands[2]) - 8))); + DONE; + } +}") + +(define_insn "lshrsi3_internal1" + [(set (match_operand:SI 0 "register_operand" "=d") + (lshiftrt:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "arith_operand" "dI")))] + "!TARGET_MIPS16" + "* +{ + if (GET_CODE (operands[2]) == CONST_INT) + operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f); + + return \"srl\\t%0,%1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_insn "lshrsi3_internal2" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (lshiftrt:SI (match_operand:SI 1 "register_operand" "0,d") + (match_operand:SI 2 "arith_operand" "d,I")))] + "TARGET_MIPS16" + "* +{ + if (which_alternative == 0) + return \"srl\\t%0,%2\"; + + if (GET_CODE (operands[2]) == CONST_INT) + operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f); + + return \"srl\\t%0,%1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr_alternative "length" + [(const_int 1) + (if_then_else (match_operand:VOID 2 "m16_uimm3_b" "") + (const_int 1) + (const_int 2))])]) + + +;; On the mips16, we can split a 4 byte shift into 2 2 byte shifts. + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (lshiftrt:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "const_int_operand" "")))] + "TARGET_MIPS16 + && reload_completed + && GET_CODE (operands[2]) == CONST_INT + && INTVAL (operands[2]) > 8 + && INTVAL (operands[2]) <= 16" + [(set (match_dup 0) (lshiftrt:SI (match_dup 1) (const_int 8))) + (set (match_dup 0) (lshiftrt:SI (match_dup 0) (match_dup 2)))] +" +{ + operands[2] = GEN_INT (INTVAL (operands[2]) - 8); +}") + +;; If we load a byte on the mips16 as a bitfield, the resulting +;; sequence of instructions is too complicated for combine, because it +;; involves four instructions: a load, a shift, a constant load into a +;; register, and an and (the key problem here is that the mips16 does +;; not have and immediate). We recognize a shift of a load in order +;; to make it simple enough for combine to understand. + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (lshiftrt:SI (match_operand:SI 1 "memory_operand" "R,m") + (match_operand:SI 2 "immediate_operand" "I,I")))] + "TARGET_MIPS16" + "lw\\t%0,%1\;srl\\t%0,%2" + [(set_attr "type" "load") + (set_attr "mode" "SI") + (set_attr_alternative "length" + [(if_then_else (match_operand:VOID 2 "m16_uimm3_b" "") + (const_int 2) + (const_int 3)) + (if_then_else (match_operand:VOID 2 "m16_uimm3_b" "") + (const_int 3) + (const_int 4))])]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (lshiftrt:SI (match_operand:SI 1 "memory_operand" "") + (match_operand:SI 2 "immediate_operand" "")))] + "TARGET_MIPS16" + [(set (match_dup 0) (match_dup 1)) + (set (match_dup 0) (lshiftrt:SI (match_dup 0) (match_dup 2)))] + "") + +(define_expand "lshrdi3" + [(parallel [(set (match_operand:DI 0 "register_operand" "") + (lshiftrt:DI (match_operand:DI 1 "se_register_operand" "") + (match_operand:SI 2 "arith_operand" ""))) + (clobber (match_dup 3))])] + "TARGET_64BIT || (!TARGET_DEBUG_G_MODE && !TARGET_MIPS16)" + " +{ + if (TARGET_64BIT) + { + /* On the mips16, a shift of more than 8 is a four byte + instruction, so, for a shift between 8 and 16, it is just as + fast to do two shifts of 8 or less. If there is a lot of + shifting going on, we may win in CSE. Otherwise combine will + put the shifts back together again. */ + if (TARGET_MIPS16 + && optimize + && GET_CODE (operands[2]) == CONST_INT + && INTVAL (operands[2]) > 8 + && INTVAL (operands[2]) <= 16) + { + rtx temp = gen_reg_rtx (DImode); + + emit_insn (gen_lshrdi3_internal4 (temp, operands[1], GEN_INT (8))); + emit_insn (gen_lshrdi3_internal4 (operands[0], temp, + GEN_INT (INTVAL (operands[2]) - 8))); + DONE; + } + + emit_insn (gen_lshrdi3_internal4 (operands[0], operands[1], + operands[2])); + DONE; + } + + operands[3] = gen_reg_rtx (SImode); +}") + + +(define_insn "lshrdi3_internal" + [(set (match_operand:DI 0 "register_operand" "=&d") + (lshiftrt:DI (match_operand:DI 1 "register_operand" "d") + (match_operand:SI 2 "register_operand" "d"))) + (clobber (match_operand:SI 3 "register_operand" "=d"))] + "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16" + "* +{ + operands[4] = const0_rtx; + dslots_jump_total += 3; + dslots_jump_filled += 2; + + return \"sll\\t%3,%2,26\\n\\ +\\tbgez\\t%3,1f\\n\\ +\\tsrl\\t%L0,%M1,%2\\n\\ +\\t%(b\\t3f\\n\\ +\\tmove\\t%M0,%z4%)\\n\\ +\\n\\ +1:\\n\\ +\\t%(beq\\t%3,%z4,2f\\n\\ +\\tsrl\\t%L0,%L1,%2%)\\n\\ +\\n\\ +\\tsubu\\t%3,%z4,%2\\n\\ +\\tsll\\t%3,%M1,%3\\n\\ +\\tor\\t%L0,%L0,%3\\n\\ +2:\\n\\ +\\tsrl\\t%M0,%M1,%2\\n\\ +3:\"; +}" + [(set_attr "type" "darith") + (set_attr "mode" "DI") + (set_attr "length" "12")]) + + +(define_insn "lshrdi3_internal2" + [(set (match_operand:DI 0 "register_operand" "=d") + (lshiftrt:DI (match_operand:DI 1 "register_operand" "d") + (match_operand:SI 2 "small_int" "IJK"))) + (clobber (match_operand:SI 3 "register_operand" "=d"))] + "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16 + && (INTVAL (operands[2]) & 32) != 0" + "* +{ + operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f); + operands[4] = const0_rtx; + return \"srl\\t%L0,%M1,%2\;move\\t%M0,%z4\"; +}" + [(set_attr "type" "darith") + (set_attr "mode" "DI") + (set_attr "length" "2")]) + + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (lshiftrt:DI (match_operand:DI 1 "register_operand" "") + (match_operand:SI 2 "small_int" ""))) + (clobber (match_operand:SI 3 "register_operand" ""))] + "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT + && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16 + && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER + && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER + && (INTVAL (operands[2]) & 32) != 0" + + [(set (subreg:SI (match_dup 0) 0) (lshiftrt:SI (subreg:SI (match_dup 1) 1) (match_dup 2))) + (set (subreg:SI (match_dup 0) 1) (const_int 0))] + + "operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);") + + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (lshiftrt:DI (match_operand:DI 1 "register_operand" "") + (match_operand:SI 2 "small_int" ""))) + (clobber (match_operand:SI 3 "register_operand" ""))] + "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT + && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16 + && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER + && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER + && (INTVAL (operands[2]) & 32) != 0" + + [(set (subreg:SI (match_dup 0) 1) (lshiftrt:SI (subreg:SI (match_dup 1) 0) (match_dup 2))) + (set (subreg:SI (match_dup 0) 0) (const_int 0))] + + "operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);") + + +(define_insn "lshrdi3_internal3" + [(set (match_operand:DI 0 "register_operand" "=d") + (lshiftrt:DI (match_operand:DI 1 "register_operand" "d") + (match_operand:SI 2 "small_int" "IJK"))) + (clobber (match_operand:SI 3 "register_operand" "=d"))] + "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16 + && (INTVAL (operands[2]) & 63) < 32 + && (INTVAL (operands[2]) & 63) != 0" + "* +{ + int amount = INTVAL (operands[2]); + + operands[2] = GEN_INT ((amount & 31)); + operands[4] = GEN_INT (((-amount) & 31)); + + return \"srl\\t%L0,%L1,%2\;sll\\t%3,%M1,%4\;or\\t%L0,%L0,%3\;srl\\t%M0,%M1,%2\"; +}" + [(set_attr "type" "darith") + (set_attr "mode" "DI") + (set_attr "length" "4")]) + + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (lshiftrt:DI (match_operand:DI 1 "register_operand" "") + (match_operand:SI 2 "small_int" ""))) + (clobber (match_operand:SI 3 "register_operand" ""))] + "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT + && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16 + && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER + && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER + && (INTVAL (operands[2]) & 63) < 32 + && (INTVAL (operands[2]) & 63) != 0" + + [(set (subreg:SI (match_dup 0) 0) + (lshiftrt:SI (subreg:SI (match_dup 1) 0) + (match_dup 2))) + + (set (match_dup 3) + (ashift:SI (subreg:SI (match_dup 1) 1) + (match_dup 4))) + + (set (subreg:SI (match_dup 0) 0) + (ior:SI (subreg:SI (match_dup 0) 0) + (match_dup 3))) + + (set (subreg:SI (match_dup 0) 1) + (lshiftrt:SI (subreg:SI (match_dup 1) 1) + (match_dup 2)))] + " +{ + int amount = INTVAL (operands[2]); + operands[2] = GEN_INT ((amount & 31)); + operands[4] = GEN_INT (((-amount) & 31)); +}") + + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (lshiftrt:DI (match_operand:DI 1 "register_operand" "") + (match_operand:SI 2 "small_int" ""))) + (clobber (match_operand:SI 3 "register_operand" ""))] + "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT + && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16 + && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER + && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER + && (INTVAL (operands[2]) & 63) < 32 + && (INTVAL (operands[2]) & 63) != 0" + + [(set (subreg:SI (match_dup 0) 1) + (lshiftrt:SI (subreg:SI (match_dup 1) 1) + (match_dup 2))) + + (set (match_dup 3) + (ashift:SI (subreg:SI (match_dup 1) 0) + (match_dup 4))) + + (set (subreg:SI (match_dup 0) 1) + (ior:SI (subreg:SI (match_dup 0) 1) + (match_dup 3))) + + (set (subreg:SI (match_dup 0) 0) + (lshiftrt:SI (subreg:SI (match_dup 1) 0) + (match_dup 2)))] + " +{ + int amount = INTVAL (operands[2]); + operands[2] = GEN_INT ((amount & 31)); + operands[4] = GEN_INT (((-amount) & 31)); +}") + + +(define_insn "lshrdi3_internal4" + [(set (match_operand:DI 0 "register_operand" "=d") + (lshiftrt:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:SI 2 "arith_operand" "dI")))] + "TARGET_64BIT && !TARGET_MIPS16" + "* +{ + if (GET_CODE (operands[2]) == CONST_INT) + operands[2] = GEN_INT (INTVAL (operands[2]) & 0x3f); + + return \"dsrl\\t%0,%1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (lshiftrt:DI (match_operand:DI 1 "se_register_operand" "0,0") + (match_operand:SI 2 "arith_operand" "d,I")))] + "TARGET_64BIT && TARGET_MIPS16" + "* +{ + if (GET_CODE (operands[2]) == CONST_INT) + operands[2] = GEN_INT (INTVAL (operands[2]) & 0x3f); + + return \"dsrl\\t%0,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr_alternative "length" + [(const_int 1) + (if_then_else (match_operand:VOID 2 "m16_uimm3_b" "") + (const_int 1) + (const_int 2))])]) + +;; On the mips16, we can split a 4 byte shift into 2 2 byte shifts. + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (lshiftrt:DI (match_operand:DI 1 "register_operand" "") + (match_operand:SI 2 "const_int_operand" "")))] + "TARGET_MIPS16 + && reload_completed + && GET_CODE (operands[2]) == CONST_INT + && INTVAL (operands[2]) > 8 + && INTVAL (operands[2]) <= 16" + [(set (match_dup 0) (lshiftrt:DI (match_dup 1) (const_int 8))) + (set (match_dup 0) (lshiftrt:DI (match_dup 0) (match_dup 2)))] +" +{ + operands[2] = GEN_INT (INTVAL (operands[2]) - 8); +}") + + +;; +;; .................... +;; +;; COMPARISONS +;; +;; .................... + +;; Flow here is rather complex: +;; +;; 1) The cmp{si,di,sf,df} routine is called. It deposits the +;; arguments into the branch_cmp array, and the type into +;; branch_type. No RTL is generated. +;; +;; 2) The appropriate branch define_expand is called, which then +;; creates the appropriate RTL for the comparison and branch. +;; Different CC modes are used, based on what type of branch is +;; done, so that we can constrain things appropriately. There +;; are assumptions in the rest of GCC that break if we fold the +;; operands into the branchs for integer operations, and use cc0 +;; for floating point, so we use the fp status register instead. +;; If needed, an appropriate temporary is created to hold the +;; of the integer compare. + +(define_expand "cmpsi" + [(set (cc0) + (compare:CC (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "arith_operand" "")))] + "" + " +{ + if (operands[0]) /* avoid unused code message */ + { + branch_cmp[0] = operands[0]; + branch_cmp[1] = operands[1]; + branch_type = CMP_SI; + DONE; + } +}") + +(define_expand "tstsi" + [(set (cc0) + (match_operand:SI 0 "register_operand" ""))] + "" + " +{ + if (operands[0]) /* avoid unused code message */ + { + branch_cmp[0] = operands[0]; + branch_cmp[1] = const0_rtx; + branch_type = CMP_SI; + DONE; + } +}") + +(define_expand "cmpdi" + [(set (cc0) + (compare:CC (match_operand:DI 0 "se_register_operand" "") + (match_operand:DI 1 "se_arith_operand" "")))] + "TARGET_64BIT" + " +{ + if (operands[0]) /* avoid unused code message */ + { + branch_cmp[0] = operands[0]; + branch_cmp[1] = operands[1]; + branch_type = CMP_DI; + DONE; + } +}") + +(define_expand "tstdi" + [(set (cc0) + (match_operand:DI 0 "se_register_operand" ""))] + "TARGET_64BIT" + " +{ + if (operands[0]) /* avoid unused code message */ + { + branch_cmp[0] = operands[0]; + branch_cmp[1] = const0_rtx; + branch_type = CMP_DI; + DONE; + } +}") + +(define_expand "cmpdf" + [(set (cc0) + (compare:CC (match_operand:DF 0 "register_operand" "") + (match_operand:DF 1 "register_operand" "")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT" + " +{ + if (operands[0]) /* avoid unused code message */ + { + branch_cmp[0] = operands[0]; + branch_cmp[1] = operands[1]; + branch_type = CMP_DF; + DONE; + } +}") + +(define_expand "cmpsf" + [(set (cc0) + (compare:CC (match_operand:SF 0 "register_operand" "") + (match_operand:SF 1 "register_operand" "")))] + "TARGET_HARD_FLOAT" + " +{ + if (operands[0]) /* avoid unused code message */ + { + branch_cmp[0] = operands[0]; + branch_cmp[1] = operands[1]; + branch_type = CMP_SF; + DONE; + } +}") + + +;; +;; .................... +;; +;; CONDITIONAL BRANCHES +;; +;; .................... + +(define_insn "branch_fp_ne" + [(set (pc) + (if_then_else (ne:CC (match_operand:CC 0 "register_operand" "z") + (const_int 0)) + (match_operand 1 "pc_or_label_operand" "") + (match_operand 2 "pc_or_label_operand" "")))] + "TARGET_HARD_FLOAT" + "* +{ + mips_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn)); + return (operands[1] != pc_rtx) ? \"%*bc1t%?\\t%Z0%1\" : \"%*bc1f%?\\t%Z0%2\"; +}" + [(set_attr "type" "branch") + (set_attr "mode" "none") + (set_attr "length" "1")]) + +(define_insn "branch_fp_eq" + [(set (pc) + (if_then_else (eq:CC (match_operand:CC 0 "register_operand" "z") + (const_int 0)) + (match_operand 1 "pc_or_label_operand" "") + (match_operand 2 "pc_or_label_operand" "")))] + "TARGET_HARD_FLOAT" + "* +{ + mips_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn)); + return (operands[1] != pc_rtx) ? \"%*bc1f%?\\t%Z0%1\" : \"%*bc1t%?\\t%Z0%2\"; +}" + [(set_attr "type" "branch") + (set_attr "mode" "none") + (set_attr "length" "1")]) + +(define_insn "branch_zero" + [(set (pc) + (if_then_else (match_operator:SI 0 "cmp_op" + [(match_operand:SI 1 "register_operand" "d") + (const_int 0)]) + (match_operand 2 "pc_or_label_operand" "") + (match_operand 3 "pc_or_label_operand" "")))] + "!TARGET_MIPS16" + "* +{ + mips_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn)); + if (operands[2] != pc_rtx) + { /* normal jump */ + switch (GET_CODE (operands[0])) + { + case EQ: return \"%*beq%?\\t%z1,%.,%2\"; + case NE: return \"%*bne%?\\t%z1,%.,%2\"; + case GTU: return \"%*bne%?\\t%z1,%.,%2\"; + case LEU: return \"%*beq%?\\t%z1,%.,%2\"; + case GEU: return \"%*j\\t%2\"; + case LTU: return \"%*bne%?\\t%.,%.,%2\"; + default: + break; + } + + return \"%*b%C0z%?\\t%z1,%2\"; + } + else + { /* inverted jump */ + switch (GET_CODE (operands[0])) + { + case EQ: return \"%*bne%?\\t%z1,%.,%3\"; + case NE: return \"%*beq%?\\t%z1,%.,%3\"; + case GTU: return \"%*beq%?\\t%z1,%.,%3\"; + case LEU: return \"%*bne%?\\t%z1,%.,%3\"; + case GEU: return \"%*beq%?\\t%.,%.,%3\"; + case LTU: return \"%*j\\t%3\"; + default: + break; + } + + return \"%*b%N0z%?\\t%z1,%3\"; + } +}" + [(set_attr "type" "branch") + (set_attr "mode" "none") + (set_attr "length" "1")]) + + +(define_insn "" + [(set (pc) + (if_then_else (match_operator:SI 0 "equality_op" + [(match_operand:SI 1 "register_operand" "d,t") + (const_int 0)]) + (match_operand 2 "pc_or_label_operand" "") + (match_operand 3 "pc_or_label_operand" "")))] + "TARGET_MIPS16" + "* +{ + if (operands[2] != pc_rtx) + { + if (which_alternative == 0) + return \"%*b%C0z\\t%1,%2\"; + else + return \"%*bt%C0z\\t%2\"; + } + else + { + if (which_alternative == 0) + return \"%*b%N0z\\t%1,%3\"; + else + return \"%*bt%N0z\\t%3\"; + } +}" + [(set_attr "type" "branch") + (set_attr "mode" "none") + (set_attr "length" "2")]) + +(define_insn "branch_zero_di" + [(set (pc) + (if_then_else (match_operator:DI 0 "cmp_op" + [(match_operand:DI 1 "se_register_operand" "d") + (const_int 0)]) + (match_operand 2 "pc_or_label_operand" "") + (match_operand 3 "pc_or_label_operand" "")))] + "!TARGET_MIPS16" + "* +{ + mips_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn)); + if (operands[2] != pc_rtx) + { /* normal jump */ + switch (GET_CODE (operands[0])) + { + case EQ: return \"%*beq%?\\t%z1,%.,%2\"; + case NE: return \"%*bne%?\\t%z1,%.,%2\"; + case GTU: return \"%*bne%?\\t%z1,%.,%2\"; + case LEU: return \"%*beq%?\\t%z1,%.,%2\"; + case GEU: return \"%*j\\t%2\"; + case LTU: return \"%*bne%?\\t%.,%.,%2\"; + default: + break; + } + + return \"%*b%C0z%?\\t%z1,%2\"; + } + else + { /* inverted jump */ + switch (GET_CODE (operands[0])) + { + case EQ: return \"%*bne%?\\t%z1,%.,%3\"; + case NE: return \"%*beq%?\\t%z1,%.,%3\"; + case GTU: return \"%*beq%?\\t%z1,%.,%3\"; + case LEU: return \"%*bne%?\\t%z1,%.,%3\"; + case GEU: return \"%*beq%?\\t%.,%.,%3\"; + case LTU: return \"%*j\\t%3\"; + default: + break; + } + + return \"%*b%N0z%?\\t%z1,%3\"; + } +}" + [(set_attr "type" "branch") + (set_attr "mode" "none") + (set_attr "length" "1")]) + +(define_insn "" + [(set (pc) + (if_then_else (match_operator:DI 0 "equality_op" + [(match_operand:DI 1 "se_register_operand" "d,t") + (const_int 0)]) + (match_operand 2 "pc_or_label_operand" "") + (match_operand 3 "pc_or_label_operand" "")))] + "TARGET_MIPS16" + "* +{ + if (operands[2] != pc_rtx) + { + if (which_alternative == 0) + return \"%*b%C0z\\t%1,%2\"; + else + return \"%*bt%C0z\\t%2\"; + } + else + { + if (which_alternative == 0) + return \"%*b%N0z\\t%1,%3\"; + else + return \"%*bt%N0z\\t%3\"; + } +}" + [(set_attr "type" "branch") + (set_attr "mode" "none") + (set_attr "length" "2")]) + + +(define_insn "branch_equality" + [(set (pc) + (if_then_else (match_operator:SI 0 "equality_op" + [(match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "register_operand" "d")]) + (match_operand 3 "pc_or_label_operand" "") + (match_operand 4 "pc_or_label_operand" "")))] + "!TARGET_MIPS16" + "* +{ + mips_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn)); + return (operands[3] != pc_rtx) + ? \"%*b%C0%?\\t%z1,%z2,%3\" + : \"%*b%N0%?\\t%z1,%z2,%4\"; +}" + [(set_attr "type" "branch") + (set_attr "mode" "none") + (set_attr "length" "1")]) + + +(define_insn "branch_equality_di" + [(set (pc) + (if_then_else (match_operator:DI 0 "equality_op" + [(match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_register_operand" "d")]) + (match_operand 3 "pc_or_label_operand" "") + (match_operand 4 "pc_or_label_operand" "")))] + "!TARGET_MIPS16" + "* +{ + mips_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn)); + return (operands[3] != pc_rtx) + ? \"%*b%C0%?\\t%z1,%z2,%3\" + : \"%*b%N0%?\\t%z1,%z2,%4\"; +}" + [(set_attr "type" "branch") + (set_attr "mode" "none") + (set_attr "length" "1")]) + + +(define_expand "beq" + [(set (pc) + (if_then_else (eq:CC (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (operands[0]) /* avoid unused code warning */ + { + gen_conditional_branch (operands, EQ); + DONE; + } +}") + +(define_expand "bne" + [(set (pc) + (if_then_else (ne:CC (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (operands[0]) /* avoid unused code warning */ + { + gen_conditional_branch (operands, NE); + DONE; + } +}") + +(define_expand "bgt" + [(set (pc) + (if_then_else (gt:CC (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (operands[0]) /* avoid unused code warning */ + { + gen_conditional_branch (operands, GT); + DONE; + } +}") + +(define_expand "bge" + [(set (pc) + (if_then_else (ge:CC (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (operands[0]) /* avoid unused code warning */ + { + gen_conditional_branch (operands, GE); + DONE; + } +}") + +(define_expand "blt" + [(set (pc) + (if_then_else (lt:CC (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (operands[0]) /* avoid unused code warning */ + { + gen_conditional_branch (operands, LT); + DONE; + } +}") + +(define_expand "ble" + [(set (pc) + (if_then_else (le:CC (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (operands[0]) /* avoid unused code warning */ + { + gen_conditional_branch (operands, LE); + DONE; + } +}") + +(define_expand "bgtu" + [(set (pc) + (if_then_else (gtu:CC (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (operands[0]) /* avoid unused code warning */ + { + gen_conditional_branch (operands, GTU); + DONE; + } +}") + +(define_expand "bgeu" + [(set (pc) + (if_then_else (geu:CC (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (operands[0]) /* avoid unused code warning */ + { + gen_conditional_branch (operands, GEU); + DONE; + } +}") + + +(define_expand "bltu" + [(set (pc) + (if_then_else (ltu:CC (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (operands[0]) /* avoid unused code warning */ + { + gen_conditional_branch (operands, LTU); + DONE; + } +}") + +(define_expand "bleu" + [(set (pc) + (if_then_else (leu:CC (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (operands[0]) /* avoid unused code warning */ + { + gen_conditional_branch (operands, LEU); + DONE; + } +}") + + +;; +;; .................... +;; +;; SETTING A REGISTER FROM A COMPARISON +;; +;; .................... + +(define_expand "seq" + [(set (match_operand:SI 0 "register_operand" "=d") + (eq:SI (match_dup 1) + (match_dup 2)))] + "" + " +{ + if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI)) + FAIL; + + /* set up operands from compare. */ + operands[1] = branch_cmp[0]; + operands[2] = branch_cmp[1]; + + if (TARGET_64BIT || !TARGET_DEBUG_C_MODE || TARGET_MIPS16) + { + gen_int_relational (EQ, operands[0], operands[1], operands[2], (int *)0); + DONE; + } + + if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0) + operands[2] = force_reg (SImode, operands[2]); + + /* fall through and generate default code */ +}") + + +(define_insn "seq_si_zero" + [(set (match_operand:SI 0 "register_operand" "=d") + (eq:SI (match_operand:SI 1 "register_operand" "d") + (const_int 0)))] + "!TARGET_MIPS16" + "sltu\\t%0,%1,1" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=t") + (eq:SI (match_operand:SI 1 "register_operand" "d") + (const_int 0)))] + "TARGET_MIPS16" + "sltu\\t%1,1" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_insn "seq_di_zero" + [(set (match_operand:DI 0 "register_operand" "=d") + (eq:DI (match_operand:DI 1 "se_register_operand" "d") + (const_int 0)))] + "TARGET_64BIT && !TARGET_MIPS16" + "sltu\\t%0,%1,1" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=t") + (eq:DI (match_operand:DI 1 "se_register_operand" "d") + (const_int 0)))] + "TARGET_64BIT && TARGET_MIPS16" + "sltu\\t%1,1" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr "length" "1")]) + +(define_insn "seq_si" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (eq:SI (match_operand:SI 1 "register_operand" "%d,d") + (match_operand:SI 2 "uns_arith_operand" "d,K")))] + "TARGET_DEBUG_C_MODE && !TARGET_MIPS16" + "@ + xor\\t%0,%1,%2\;sltu\\t%0,%0,1 + xori\\t%0,%1,%2\;sltu\\t%0,%0,1" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "2")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (eq:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "uns_arith_operand" "")))] + "TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE && !TARGET_MIPS16 + && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != 0)" + [(set (match_dup 0) + (xor:SI (match_dup 1) + (match_dup 2))) + (set (match_dup 0) + (ltu:SI (match_dup 0) + (const_int 1)))] + "") + +(define_insn "seq_di" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (eq:DI (match_operand:DI 1 "se_register_operand" "%d,d") + (match_operand:DI 2 "se_uns_arith_operand" "d,K")))] + "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_MIPS16" + "@ + xor\\t%0,%1,%2\;sltu\\t%0,%0,1 + xori\\t%0,%1,%2\;sltu\\t%0,%0,1" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr "length" "2")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (eq:DI (match_operand:DI 1 "se_register_operand" "") + (match_operand:DI 2 "se_uns_arith_operand" "")))] + "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE + && !TARGET_MIPS16 + && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != 0)" + [(set (match_dup 0) + (xor:DI (match_dup 1) + (match_dup 2))) + (set (match_dup 0) + (ltu:DI (match_dup 0) + (const_int 1)))] + "") + +;; On the mips16 the default code is better than using sltu. + +(define_expand "sne" + [(set (match_operand:SI 0 "register_operand" "=d") + (ne:SI (match_dup 1) + (match_dup 2)))] + "!TARGET_MIPS16" + " +{ + if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI)) + FAIL; + + /* set up operands from compare. */ + operands[1] = branch_cmp[0]; + operands[2] = branch_cmp[1]; + + if (TARGET_64BIT || !TARGET_DEBUG_C_MODE) + { + gen_int_relational (NE, operands[0], operands[1], operands[2], (int *)0); + DONE; + } + + if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0) + operands[2] = force_reg (SImode, operands[2]); + + /* fall through and generate default code */ +}") + +(define_insn "sne_si_zero" + [(set (match_operand:SI 0 "register_operand" "=d") + (ne:SI (match_operand:SI 1 "register_operand" "d") + (const_int 0)))] + "!TARGET_MIPS16" + "sltu\\t%0,%.,%1" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_insn "sne_di_zero" + [(set (match_operand:DI 0 "register_operand" "=d") + (ne:DI (match_operand:DI 1 "se_register_operand" "d") + (const_int 0)))] + "TARGET_64BIT && !TARGET_MIPS16" + "sltu\\t%0,%.,%1" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr "length" "1")]) + +(define_insn "sne_si" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (ne:SI (match_operand:SI 1 "register_operand" "%d,d") + (match_operand:SI 2 "uns_arith_operand" "d,K")))] + "TARGET_DEBUG_C_MODE && !TARGET_MIPS16" + "@ + xor\\t%0,%1,%2\;sltu\\t%0,%.,%0 + xori\\t%0,%1,%x2\;sltu\\t%0,%.,%0" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "2")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (ne:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "uns_arith_operand" "")))] + "TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE && !TARGET_MIPS16 + && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != 0)" + [(set (match_dup 0) + (xor:SI (match_dup 1) + (match_dup 2))) + (set (match_dup 0) + (gtu:SI (match_dup 0) + (const_int 0)))] + "") + +(define_insn "sne_di" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (ne:DI (match_operand:DI 1 "se_register_operand" "%d,d") + (match_operand:DI 2 "se_uns_arith_operand" "d,K")))] + "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_MIPS16" + "@ + xor\\t%0,%1,%2\;sltu\\t%0,%.,%0 + xori\\t%0,%1,%x2\;sltu\\t%0,%.,%0" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr "length" "2")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (ne:DI (match_operand:DI 1 "se_register_operand" "") + (match_operand:DI 2 "se_uns_arith_operand" "")))] + "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE + && !TARGET_MIPS16 + && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != 0)" + [(set (match_dup 0) + (xor:DI (match_dup 1) + (match_dup 2))) + (set (match_dup 0) + (gtu:DI (match_dup 0) + (const_int 0)))] + "") + +(define_expand "sgt" + [(set (match_operand:SI 0 "register_operand" "=d") + (gt:SI (match_dup 1) + (match_dup 2)))] + "" + " +{ + if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI)) + FAIL; + + /* set up operands from compare. */ + operands[1] = branch_cmp[0]; + operands[2] = branch_cmp[1]; + + if (TARGET_64BIT || !TARGET_DEBUG_C_MODE || TARGET_MIPS16) + { + gen_int_relational (GT, operands[0], operands[1], operands[2], (int *)0); + DONE; + } + + if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) != 0) + operands[2] = force_reg (SImode, operands[2]); + + /* fall through and generate default code */ +}") + +(define_insn "sgt_si" + [(set (match_operand:SI 0 "register_operand" "=d") + (gt:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "reg_or_0_operand" "dJ")))] + "!TARGET_MIPS16" + "slt\\t%0,%z2,%1" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=t") + (gt:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "register_operand" "d")))] + "TARGET_MIPS16" + "slt\\t%2,%1" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_insn "sgt_di" + [(set (match_operand:DI 0 "register_operand" "=d") + (gt:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_reg_or_0_operand" "dJ")))] + "TARGET_64BIT && !TARGET_MIPS16" + "slt\\t%0,%z2,%1" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=d") + (gt:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_register_operand" "d")))] + "TARGET_64BIT && TARGET_MIPS16" + "slt\\t%2,%1" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr "length" "1")]) + +(define_expand "sge" + [(set (match_operand:SI 0 "register_operand" "=d") + (ge:SI (match_dup 1) + (match_dup 2)))] + "" + " +{ + if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI)) + FAIL; + + /* set up operands from compare. */ + operands[1] = branch_cmp[0]; + operands[2] = branch_cmp[1]; + + if (TARGET_64BIT || !TARGET_DEBUG_C_MODE || TARGET_MIPS16) + { + gen_int_relational (GE, operands[0], operands[1], operands[2], (int *)0); + DONE; + } + + /* fall through and generate default code */ +}") + +(define_insn "sge_si" + [(set (match_operand:SI 0 "register_operand" "=d") + (ge:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "arith_operand" "dI")))] + "TARGET_DEBUG_C_MODE && !TARGET_MIPS16" + "slt\\t%0,%1,%2\;xori\\t%0,%0,0x0001" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "2")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (ge:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "arith_operand" "")))] + "TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE && !TARGET_MIPS16" + [(set (match_dup 0) + (lt:SI (match_dup 1) + (match_dup 2))) + (set (match_dup 0) + (xor:SI (match_dup 0) + (const_int 1)))] + "") + +(define_insn "sge_di" + [(set (match_operand:DI 0 "register_operand" "=d") + (ge:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_arith_operand" "dI")))] + "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_MIPS16" + "slt\\t%0,%1,%2\;xori\\t%0,%0,0x0001" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr "length" "2")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (ge:DI (match_operand:DI 1 "se_register_operand" "") + (match_operand:DI 2 "se_arith_operand" "")))] + "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE + && !TARGET_MIPS16" + [(set (match_dup 0) + (lt:DI (match_dup 1) + (match_dup 2))) + (set (match_dup 0) + (xor:DI (match_dup 0) + (const_int 1)))] + "") + +(define_expand "slt" + [(set (match_operand:SI 0 "register_operand" "=d") + (lt:SI (match_dup 1) + (match_dup 2)))] + "" + " +{ + if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI)) + FAIL; + + /* set up operands from compare. */ + operands[1] = branch_cmp[0]; + operands[2] = branch_cmp[1]; + + if (TARGET_64BIT || !TARGET_DEBUG_C_MODE || TARGET_MIPS16) + { + gen_int_relational (LT, operands[0], operands[1], operands[2], (int *)0); + DONE; + } + + /* fall through and generate default code */ +}") + +(define_insn "slt_si" + [(set (match_operand:SI 0 "register_operand" "=d") + (lt:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "arith_operand" "dI")))] + "!TARGET_MIPS16" + "slt\\t%0,%1,%2" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=t,t") + (lt:SI (match_operand:SI 1 "register_operand" "d,d") + (match_operand:SI 2 "arith_operand" "d,I")))] + "TARGET_MIPS16" + "slt\\t%1,%2" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr_alternative "length" + [(const_int 1) + (if_then_else (match_operand:VOID 2 "m16_uimm8_1" "") + (const_int 1) + (const_int 2))])]) + +(define_insn "slt_di" + [(set (match_operand:DI 0 "register_operand" "=d") + (lt:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_arith_operand" "dI")))] + "TARGET_64BIT && !TARGET_MIPS16" + "slt\\t%0,%1,%2" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=t,t") + (lt:DI (match_operand:DI 1 "se_register_operand" "d,d") + (match_operand:DI 2 "se_arith_operand" "d,I")))] + "TARGET_64BIT && TARGET_MIPS16" + "slt\\t%1,%2" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr_alternative "length" + [(const_int 1) + (if_then_else (match_operand:VOID 2 "m16_uimm8_1" "") + (const_int 1) + (const_int 2))])]) + +(define_expand "sle" + [(set (match_operand:SI 0 "register_operand" "=d") + (le:SI (match_dup 1) + (match_dup 2)))] + "" + " +{ + if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI)) + FAIL; + + /* set up operands from compare. */ + operands[1] = branch_cmp[0]; + operands[2] = branch_cmp[1]; + + if (TARGET_64BIT || !TARGET_DEBUG_C_MODE || TARGET_MIPS16) + { + gen_int_relational (LE, operands[0], operands[1], operands[2], (int *)0); + DONE; + } + + if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) >= 32767) + operands[2] = force_reg (SImode, operands[2]); + + /* fall through and generate default code */ +}") + +(define_insn "sle_si_const" + [(set (match_operand:SI 0 "register_operand" "=d") + (le:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "small_int" "I")))] + "!TARGET_MIPS16 && INTVAL (operands[2]) < 32767" + "* +{ + operands[2] = GEN_INT (INTVAL (operands[2])+1); + return \"slt\\t%0,%1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=t") + (le:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "small_int" "I")))] + "TARGET_MIPS16 && INTVAL (operands[2]) < 32767" + "* +{ + operands[2] = GEN_INT (INTVAL (operands[2])+1); + return \"slt\\t%1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set (attr "length") (if_then_else (match_operand:VOID 2 "m16_uimm8_m1_1" "") + (const_int 1) + (const_int 2)))]) + +(define_insn "sle_di_const" + [(set (match_operand:DI 0 "register_operand" "=d") + (le:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "small_int" "I")))] + "TARGET_64BIT && !TARGET_MIPS16 && INTVAL (operands[2]) < 32767" + "* +{ + operands[2] = GEN_INT (INTVAL (operands[2])+1); + return \"slt\\t%0,%1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=t") + (le:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "small_int" "I")))] + "TARGET_64BIT && TARGET_MIPS16 && INTVAL (operands[2]) < 32767" + "* +{ + operands[2] = GEN_INT (INTVAL (operands[2])+1); + return \"slt\\t%1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set (attr "length") (if_then_else (match_operand:VOID 2 "m16_uimm8_m1_1" "") + (const_int 1) + (const_int 2)))]) + +(define_insn "sle_si_reg" + [(set (match_operand:SI 0 "register_operand" "=d") + (le:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "register_operand" "d")))] + "TARGET_DEBUG_C_MODE && !TARGET_MIPS16" + "slt\\t%0,%z2,%1\;xori\\t%0,%0,0x0001" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "2")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (le:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "register_operand" "")))] + "TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE && !TARGET_MIPS16" + [(set (match_dup 0) + (lt:SI (match_dup 2) + (match_dup 1))) + (set (match_dup 0) + (xor:SI (match_dup 0) + (const_int 1)))] + "") + +(define_insn "sle_di_reg" + [(set (match_operand:DI 0 "register_operand" "=d") + (le:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_register_operand" "d")))] + "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_MIPS16" + "slt\\t%0,%z2,%1\;xori\\t%0,%0,0x0001" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr "length" "2")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (le:DI (match_operand:DI 1 "se_register_operand" "") + (match_operand:DI 2 "se_register_operand" "")))] + "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE + && !TARGET_MIPS16" + [(set (match_dup 0) + (lt:DI (match_dup 2) + (match_dup 1))) + (set (match_dup 0) + (xor:DI (match_dup 0) + (const_int 1)))] + "") + +(define_expand "sgtu" + [(set (match_operand:SI 0 "register_operand" "=d") + (gtu:SI (match_dup 1) + (match_dup 2)))] + "" + " +{ + if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI)) + FAIL; + + /* set up operands from compare. */ + operands[1] = branch_cmp[0]; + operands[2] = branch_cmp[1]; + + if (TARGET_64BIT || !TARGET_DEBUG_C_MODE || TARGET_MIPS16) + { + gen_int_relational (GTU, operands[0], operands[1], operands[2], (int *)0); + DONE; + } + + if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) != 0) + operands[2] = force_reg (SImode, operands[2]); + + /* fall through and generate default code */ +}") + +(define_insn "sgtu_si" + [(set (match_operand:SI 0 "register_operand" "=d") + (gtu:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "reg_or_0_operand" "dJ")))] + "" + "sltu\\t%0,%z2,%1" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=t") + (gtu:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "register_operand" "d")))] + "" + "sltu\\t%2,%1" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_insn "sgtu_di" + [(set (match_operand:DI 0 "register_operand" "=d") + (gtu:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_reg_or_0_operand" "dJ")))] + "TARGET_64BIT" + "sltu\\t%0,%z2,%1" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=t") + (gtu:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_register_operand" "d")))] + "TARGET_64BIT" + "sltu\\t%2,%1" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr "length" "1")]) + +(define_expand "sgeu" + [(set (match_operand:SI 0 "register_operand" "=d") + (geu:SI (match_dup 1) + (match_dup 2)))] + "" + " +{ + if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI)) + FAIL; + + /* set up operands from compare. */ + operands[1] = branch_cmp[0]; + operands[2] = branch_cmp[1]; + + if (TARGET_64BIT || !TARGET_DEBUG_C_MODE || TARGET_MIPS16) + { + gen_int_relational (GEU, operands[0], operands[1], operands[2], (int *)0); + DONE; + } + + /* fall through and generate default code */ +}") + +(define_insn "sgeu_si" + [(set (match_operand:SI 0 "register_operand" "=d") + (geu:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "arith_operand" "dI")))] + "TARGET_DEBUG_C_MODE && !TARGET_MIPS16" + "sltu\\t%0,%1,%2\;xori\\t%0,%0,0x0001" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "2")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (geu:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "arith_operand" "")))] + "TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE && !TARGET_MIPS16" + [(set (match_dup 0) + (ltu:SI (match_dup 1) + (match_dup 2))) + (set (match_dup 0) + (xor:SI (match_dup 0) + (const_int 1)))] + "") + +(define_insn "sgeu_di" + [(set (match_operand:DI 0 "register_operand" "=d") + (geu:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_arith_operand" "dI")))] + "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_MIPS16" + "sltu\\t%0,%1,%2\;xori\\t%0,%0,0x0001" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr "length" "2")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (geu:DI (match_operand:DI 1 "se_register_operand" "") + (match_operand:DI 2 "se_arith_operand" "")))] + "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE + && !TARGET_MIPS16" + [(set (match_dup 0) + (ltu:DI (match_dup 1) + (match_dup 2))) + (set (match_dup 0) + (xor:DI (match_dup 0) + (const_int 1)))] + "") + +(define_expand "sltu" + [(set (match_operand:SI 0 "register_operand" "=d") + (ltu:SI (match_dup 1) + (match_dup 2)))] + "" + " +{ + if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI)) + FAIL; + + /* set up operands from compare. */ + operands[1] = branch_cmp[0]; + operands[2] = branch_cmp[1]; + + if (TARGET_64BIT || !TARGET_DEBUG_C_MODE || TARGET_MIPS16) + { + gen_int_relational (LTU, operands[0], operands[1], operands[2], (int *)0); + DONE; + } + + /* fall through and generate default code */ +}") + +(define_insn "sltu_si" + [(set (match_operand:SI 0 "register_operand" "=d") + (ltu:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "arith_operand" "dI")))] + "!TARGET_MIPS16" + "sltu\\t%0,%1,%2" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=t,t") + (ltu:SI (match_operand:SI 1 "register_operand" "d,d") + (match_operand:SI 2 "arith_operand" "d,I")))] + "TARGET_MIPS16" + "sltu\\t%1,%2" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr_alternative "length" + [(const_int 1) + (if_then_else (match_operand:VOID 2 "m16_uimm8_1" "") + (const_int 1) + (const_int 2))])]) + +(define_insn "sltu_di" + [(set (match_operand:DI 0 "register_operand" "=d") + (ltu:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_arith_operand" "dI")))] + "TARGET_64BIT && !TARGET_MIPS16" + "sltu\\t%0,%1,%2" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=t,t") + (ltu:DI (match_operand:DI 1 "se_register_operand" "d,d") + (match_operand:DI 2 "se_arith_operand" "d,I")))] + "TARGET_64BIT && TARGET_MIPS16" + "sltu\\t%1,%2" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr_alternative "length" + [(const_int 1) + (if_then_else (match_operand:VOID 2 "m16_uimm8_1" "") + (const_int 1) + (const_int 2))])]) + +(define_expand "sleu" + [(set (match_operand:SI 0 "register_operand" "=d") + (leu:SI (match_dup 1) + (match_dup 2)))] + "" + " +{ + if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI)) + FAIL; + + /* set up operands from compare. */ + operands[1] = branch_cmp[0]; + operands[2] = branch_cmp[1]; + + if (TARGET_64BIT || !TARGET_DEBUG_C_MODE || TARGET_MIPS16) + { + gen_int_relational (LEU, operands[0], operands[1], operands[2], (int *)0); + DONE; + } + + if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) >= 32767) + operands[2] = force_reg (SImode, operands[2]); + + /* fall through and generate default code */ +}") + +(define_insn "sleu_si_const" + [(set (match_operand:SI 0 "register_operand" "=d") + (leu:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "small_int" "I")))] + "!TARGET_MIPS16 && INTVAL (operands[2]) < 32767" + "* +{ + operands[2] = GEN_INT (INTVAL (operands[2])+1); + return \"sltu\\t%0,%1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=t") + (leu:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "small_int" "I")))] + "TARGET_MIPS16 && INTVAL (operands[2]) < 32767" + "* +{ + operands[2] = GEN_INT (INTVAL (operands[2])+1); + return \"sltu\\t%1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set (attr "length") (if_then_else (match_operand:VOID 2 "m16_uimm8_m1_1" "") + (const_int 1) + (const_int 2)))]) + +(define_insn "sleu_di_const" + [(set (match_operand:DI 0 "register_operand" "=d") + (leu:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "small_int" "I")))] + "TARGET_64BIT && !TARGET_MIPS16 && INTVAL (operands[2]) < 32767" + "* +{ + operands[2] = GEN_INT (INTVAL (operands[2])+1); + return \"sltu\\t%0,%1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr "length" "1")]) + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=t") + (leu:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "small_int" "I")))] + "TARGET_64BIT && TARGET_MIPS16 && INTVAL (operands[2]) < 32767" + "* +{ + operands[2] = GEN_INT (INTVAL (operands[2])+1); + return \"sltu\\t%1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set (attr "length") (if_then_else (match_operand:VOID 2 "m16_uimm8_m1_1" "") + (const_int 1) + (const_int 2)))]) + +(define_insn "sleu_si_reg" + [(set (match_operand:SI 0 "register_operand" "=d") + (leu:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "register_operand" "d")))] + "TARGET_DEBUG_C_MODE && !TARGET_MIPS16" + "sltu\\t%0,%z2,%1\;xori\\t%0,%0,0x0001" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "2")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (leu:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "register_operand" "")))] + "TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE && !TARGET_MIPS16" + [(set (match_dup 0) + (ltu:SI (match_dup 2) + (match_dup 1))) + (set (match_dup 0) + (xor:SI (match_dup 0) + (const_int 1)))] + "") + +(define_insn "sleu_di_reg" + [(set (match_operand:DI 0 "register_operand" "=d") + (leu:DI (match_operand:DI 1 "se_register_operand" "d") + (match_operand:DI 2 "se_register_operand" "d")))] + "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_MIPS16" + "sltu\\t%0,%z2,%1\;xori\\t%0,%0,0x0001" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr "length" "2")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (leu:DI (match_operand:DI 1 "se_register_operand" "") + (match_operand:DI 2 "se_register_operand" "")))] + "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE + && !TARGET_MIPS16" + [(set (match_dup 0) + (ltu:DI (match_dup 2) + (match_dup 1))) + (set (match_dup 0) + (xor:DI (match_dup 0) + (const_int 1)))] + "") + + +;; +;; .................... +;; +;; FLOATING POINT COMPARISONS +;; +;; .................... + +(define_insn "seq_df" + [(set (match_operand:CC 0 "register_operand" "=z") + (eq:CC (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT" + "* +{ + return mips_fill_delay_slot (\"c.eq.d\\t%Z0%1,%2\", DELAY_FCMP, operands, insn); +}" + [(set_attr "type" "fcmp") + (set_attr "mode" "FPSW") + (set_attr "length" "1")]) + +(define_insn "slt_df" + [(set (match_operand:CC 0 "register_operand" "=z") + (lt:CC (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT" + "* +{ + return mips_fill_delay_slot (\"c.lt.d\\t%Z0%1,%2\", DELAY_FCMP, operands, insn); +}" + [(set_attr "type" "fcmp") + (set_attr "mode" "FPSW") + (set_attr "length" "1")]) + +(define_insn "sle_df" + [(set (match_operand:CC 0 "register_operand" "=z") + (le:CC (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT" + "* +{ + return mips_fill_delay_slot (\"c.le.d\\t%Z0%1,%2\", DELAY_FCMP, operands, insn); +}" + [(set_attr "type" "fcmp") + (set_attr "mode" "FPSW") + (set_attr "length" "1")]) + +(define_insn "sgt_df" + [(set (match_operand:CC 0 "register_operand" "=z") + (gt:CC (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT" + "* +{ + return mips_fill_delay_slot (\"c.lt.d\\t%Z0%2,%1\", DELAY_FCMP, operands, insn); +}" + [(set_attr "type" "fcmp") + (set_attr "mode" "FPSW") + (set_attr "length" "1")]) + +(define_insn "sge_df" + [(set (match_operand:CC 0 "register_operand" "=z") + (ge:CC (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT" + "* +{ + return mips_fill_delay_slot (\"c.le.d\\t%Z0%2,%1\", DELAY_FCMP, operands, insn); +}" + [(set_attr "type" "fcmp") + (set_attr "mode" "FPSW") + (set_attr "length" "1")]) + +(define_insn "seq_sf" + [(set (match_operand:CC 0 "register_operand" "=z") + (eq:CC (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f")))] + "TARGET_HARD_FLOAT" + "* +{ + return mips_fill_delay_slot (\"c.eq.s\\t%Z0%1,%2\", DELAY_FCMP, operands, insn); +}" + [(set_attr "type" "fcmp") + (set_attr "mode" "FPSW") + (set_attr "length" "1")]) + +(define_insn "slt_sf" + [(set (match_operand:CC 0 "register_operand" "=z") + (lt:CC (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f")))] + "TARGET_HARD_FLOAT" + "* +{ + return mips_fill_delay_slot (\"c.lt.s\\t%Z0%1,%2\", DELAY_FCMP, operands, insn); +}" + [(set_attr "type" "fcmp") + (set_attr "mode" "FPSW") + (set_attr "length" "1")]) + +(define_insn "sle_sf" + [(set (match_operand:CC 0 "register_operand" "=z") + (le:CC (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f")))] + "TARGET_HARD_FLOAT" + "* +{ + return mips_fill_delay_slot (\"c.le.s\\t%Z0%1,%2\", DELAY_FCMP, operands, insn); +}" + [(set_attr "type" "fcmp") + (set_attr "mode" "FPSW") + (set_attr "length" "1")]) + +(define_insn "sgt_sf" + [(set (match_operand:CC 0 "register_operand" "=z") + (gt:CC (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f")))] + "TARGET_HARD_FLOAT" + "* +{ + return mips_fill_delay_slot (\"c.lt.s\\t%Z0%2,%1\", DELAY_FCMP, operands, insn); +}" + [(set_attr "type" "fcmp") + (set_attr "mode" "FPSW") + (set_attr "length" "1")]) + +(define_insn "sge_sf" + [(set (match_operand:CC 0 "register_operand" "=z") + (ge:CC (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f")))] + "TARGET_HARD_FLOAT" + "* +{ + return mips_fill_delay_slot (\"c.le.s\\t%Z0%2,%1\", DELAY_FCMP, operands, insn); +}" + [(set_attr "type" "fcmp") + (set_attr "mode" "FPSW") + (set_attr "length" "1")]) + + +;; +;; .................... +;; +;; UNCONDITIONAL BRANCHES +;; +;; .................... + +;; Unconditional branches. + +(define_insn "jump" + [(set (pc) + (label_ref (match_operand 0 "" "")))] + "!TARGET_MIPS16" + "* +{ + if (GET_CODE (operands[0]) == REG) + return \"%*j\\t%0\"; + /* ??? I don't know why this is necessary. This works around an + assembler problem that appears when a label is defined, then referenced + in a switch table, then used in a `j' instruction. */ + else if (mips_abi != ABI_32 && mips_abi != ABI_O64) + return \"%*b\\t%l0\"; + else + return \"%*j\\t%l0\"; +}" + [(set_attr "type" "jump") + (set_attr "mode" "none") + (set_attr "length" "1")]) + +;; We need a different insn for the mips16, because a mips16 branch +;; does not have a delay slot. + +(define_insn "" + [(set (pc) + (label_ref (match_operand 0 "" "")))] + "TARGET_MIPS16 && GET_CODE (operands[0]) != REG" + "b\\t%l0" + [(set_attr "type" "branch") + (set_attr "mode" "none") + (set_attr "length" "2")]) + +(define_expand "indirect_jump" + [(set (pc) (match_operand 0 "register_operand" "d"))] + "" + " +{ + rtx dest; + + if (operands[0]) /* eliminate unused code warnings */ + { + dest = operands[0]; + if (GET_CODE (dest) != REG || GET_MODE (dest) != Pmode) + operands[0] = copy_to_mode_reg (Pmode, dest); + + if (!(Pmode == DImode)) + emit_jump_insn (gen_indirect_jump_internal1 (operands[0])); + else + emit_jump_insn (gen_indirect_jump_internal2 (operands[0])); + + DONE; + } +}") + +(define_insn "indirect_jump_internal1" + [(set (pc) (match_operand:SI 0 "register_operand" "d"))] + "!(Pmode == DImode)" + "%*j\\t%0" + [(set_attr "type" "jump") + (set_attr "mode" "none") + (set_attr "length" "1")]) + +(define_insn "indirect_jump_internal2" + [(set (pc) (match_operand:DI 0 "se_register_operand" "d"))] + "Pmode == DImode" + "%*j\\t%0" + [(set_attr "type" "jump") + (set_attr "mode" "none") + (set_attr "length" "1")]) + +(define_expand "tablejump" + [(set (pc) + (match_operand 0 "register_operand" "d")) + (use (label_ref (match_operand 1 "" "")))] + "" + " +{ + if (operands[0]) /* eliminate unused code warnings */ + { + if (TARGET_MIPS16) + { + if (GET_MODE (operands[0]) != HImode) + abort (); + if (!(Pmode == DImode)) + emit_jump_insn (gen_tablejump_mips161 (operands[0], operands[1])); + else + emit_jump_insn (gen_tablejump_mips162 (operands[0], operands[1])); + DONE; + } + + if (GET_MODE (operands[0]) != Pmode) + abort (); + + if (! flag_pic) + { + if (!(Pmode == DImode)) + emit_jump_insn (gen_tablejump_internal1 (operands[0], operands[1])); + else + emit_jump_insn (gen_tablejump_internal2 (operands[0], operands[1])); + } + else + { + if (!(Pmode == DImode)) + emit_jump_insn (gen_tablejump_internal3 (operands[0], operands[1])); + else + emit_jump_insn (gen_tablejump_internal4 (operands[0], operands[1])); + } + + DONE; + } +}") + +(define_insn "tablejump_internal1" + [(set (pc) + (match_operand:SI 0 "register_operand" "d")) + (use (label_ref (match_operand 1 "" "")))] + "!(Pmode == DImode)" + "%*j\\t%0" + [(set_attr "type" "jump") + (set_attr "mode" "none") + (set_attr "length" "1")]) + +(define_insn "tablejump_internal2" + [(set (pc) + (match_operand:DI 0 "se_register_operand" "d")) + (use (label_ref (match_operand 1 "" "")))] + "Pmode == DImode" + "%*j\\t%0" + [(set_attr "type" "jump") + (set_attr "mode" "none") + (set_attr "length" "1")]) + +(define_expand "tablejump_internal3" + [(parallel [(set (pc) + (plus:SI (match_operand:SI 0 "register_operand" "d") + (label_ref:SI (match_operand:SI 1 "" "")))) + (use (label_ref:SI (match_dup 1)))])] + "" + "") + +(define_expand "tablejump_mips161" + [(set (pc) (plus:SI (sign_extend:SI + (match_operand:HI 0 "register_operand" "d")) + (label_ref:SI (match_operand:SI 1 "" ""))))] + "TARGET_MIPS16 && !(Pmode == DImode)" + " +{ + if (operands[0]) /* eliminate unused code warnings. */ + { + rtx t1, t2, t3; + + t1 = gen_reg_rtx (SImode); + t2 = gen_reg_rtx (SImode); + t3 = gen_reg_rtx (SImode); + emit_insn (gen_extendhisi2 (t1, operands[0])); + emit_move_insn (t2, gen_rtx (LABEL_REF, SImode, operands[1])); + emit_insn (gen_addsi3 (t3, t1, t2)); + emit_insn (gen_tablejump_internal1 (t3, operands[1])); + DONE; + } +}") + +(define_expand "tablejump_mips162" + [(set (pc) (plus:DI (sign_extend:DI + (match_operand:HI 0 "register_operand" "d")) + (label_ref:DI (match_operand:SI 1 "" ""))))] + "TARGET_MIPS16 && Pmode == DImode" + " +{ + if (operands[0]) /* eliminate unused code warnings. */ + { + rtx t1, t2, t3; + + t1 = gen_reg_rtx (DImode); + t2 = gen_reg_rtx (DImode); + t3 = gen_reg_rtx (DImode); + emit_insn (gen_extendhidi2 (t1, operands[0])); + emit_move_insn (t2, gen_rtx (LABEL_REF, DImode, operands[1])); + emit_insn (gen_adddi3 (t3, t1, t2)); + emit_insn (gen_tablejump_internal2 (t3, operands[1])); + DONE; + } +}") + +;;; Make sure that this only matches the insn before ADDR_DIFF_VEC. Otherwise +;;; it is not valid. ??? With the USE, the condition tests may not be required +;;; any longer. + +;;; ??? The length depends on the ABI. It is two for o32, and one for n32. +;;; We just use the conservative number here. + +(define_insn "" + [(set (pc) + (plus:SI (match_operand:SI 0 "register_operand" "d") + (label_ref:SI (match_operand:SI 1 "" "")))) + (use (label_ref:SI (match_dup 1)))] + "!(Pmode == DImode) && next_active_insn (insn) != 0 + && GET_CODE (PATTERN (next_active_insn (insn))) == ADDR_DIFF_VEC + && PREV_INSN (next_active_insn (insn)) == operands[1]" + "* +{ + /* .cpadd expands to add REG,REG,$gp when pic, and nothing when not pic. */ + if (mips_abi == ABI_32 || mips_abi == ABI_O64) + output_asm_insn (\".cpadd\\t%0\", operands); + return \"%*j\\t%0\"; +}" + [(set_attr "type" "jump") + (set_attr "mode" "none") + (set_attr "length" "2")]) + +(define_expand "tablejump_internal4" + [(parallel [(set (pc) + (plus:DI (match_operand:DI 0 "se_register_operand" "d") + (label_ref:DI (match_operand:SI 1 "" "")))) + (use (label_ref:DI (match_dup 1)))])] + "" + "") + +;;; Make sure that this only matches the insn before ADDR_DIFF_VEC. Otherwise +;;; it is not valid. ??? With the USE, the condition tests may not be required +;;; any longer. + +(define_insn "" + [(set (pc) + (plus:DI (match_operand:DI 0 "se_register_operand" "d") + (label_ref:DI (match_operand:SI 1 "" "")))) + (use (label_ref:DI (match_dup 1)))] + "Pmode == DImode && next_active_insn (insn) != 0 + && GET_CODE (PATTERN (next_active_insn (insn))) == ADDR_DIFF_VEC + && PREV_INSN (next_active_insn (insn)) == operands[1]" + "%*j\\t%0" + [(set_attr "type" "jump") + (set_attr "mode" "none") + (set_attr "length" "1")]) + +;; Implement a switch statement when generating embedded PIC code. +;; Switches are implemented by `tablejump' when not using -membedded-pic. + +(define_expand "casesi" + [(set (match_dup 5) + (minus:SI (match_operand:SI 0 "register_operand" "d") + (match_operand:SI 1 "arith_operand" "dI"))) + (set (cc0) + (compare:CC (match_dup 5) + (match_operand:SI 2 "arith_operand" ""))) + (set (pc) + (if_then_else (gtu (cc0) + (const_int 0)) + (label_ref (match_operand 4 "" "")) + (pc))) + (parallel + [(set (pc) + (mem:SI (plus:SI (mult:SI (match_dup 5) + (const_int 4)) + (label_ref (match_operand 3 "" ""))))) + (clobber (match_scratch:SI 6 "")) + (clobber (reg:SI 31))])] + "TARGET_EMBEDDED_PIC" + " +{ + /* We need slightly different code for eight byte table entries. */ + if (Pmode == DImode) + abort (); + + if (operands[0]) + { + rtx reg = gen_reg_rtx (SImode); + + /* If the index is too large, go to the default label. */ + emit_insn (gen_subsi3 (reg, operands[0], operands[1])); + emit_insn (gen_cmpsi (reg, operands[2])); + emit_insn (gen_bgtu (operands[4])); + + /* Do the PIC jump. */ + emit_insn (gen_casesi_internal (reg, operands[3], gen_reg_rtx (SImode))); + + DONE; + } +}") + +;; An embedded PIC switch statement looks like this: +;; bal $LS1 +;; sll $reg,$index,2 +;; $LS1: +;; addu $reg,$reg,$31 +;; lw $reg,$L1-$LS1($reg) +;; addu $reg,$reg,$31 +;; j $reg +;; $L1: +;; .word case1-$LS1 +;; .word case2-$LS1 +;; ... + +(define_insn "casesi_internal" + [(set (pc) + (mem:SI (plus:SI (mult:SI (match_operand:SI 0 "register_operand" "d") + (const_int 4)) + (label_ref (match_operand 1 "" ""))))) + (clobber (match_operand:SI 2 "register_operand" "d")) + (clobber (reg:SI 31))] + "TARGET_EMBEDDED_PIC" + "* +{ + output_asm_insn (\"%(bal\\t%S1\;sll\\t%0,2\\n%S1:\", operands); + output_asm_insn (\"addu\\t%0,%0,$31%)\", operands); + output_asm_insn (\"lw\\t%0,%1-%S1(%0)\;addu\\t%0,%0,$31\", operands); + return \"j\\t%0\"; +}" + [(set_attr "type" "jump") + (set_attr "mode" "none") + (set_attr "length" "6")]) + +;; For o32/n32/n64, we save the gp in the jmp_buf as well. While it is +;; possible to either pull it off the stack (in the o32 case) or recalculate +;; it given t9 and our target label, it takes 3 or 4 insns to do so, and +;; this is easy. + +(define_expand "builtin_setjmp_setup" + [(unspec [(match_operand 0 "register_operand" "r")] 20)] + "TARGET_ABICALLS" + " +{ + if (Pmode == DImode) + emit_insn (gen_builtin_setjmp_setup_64 (operands[0])); + else + emit_insn (gen_builtin_setjmp_setup_32 (operands[0])); + DONE; +}") + +(define_expand "builtin_setjmp_setup_32" + [(set (mem:SI (plus:SI (match_operand:SI 0 "register_operand" "r") + (const_int 12))) + (reg:SI 28))] + "TARGET_ABICALLS && ! (Pmode == DImode)" + "") + +(define_expand "builtin_setjmp_setup_64" + [(set (mem:DI (plus:DI (match_operand:DI 0 "register_operand" "r") + (const_int 24))) + (reg:DI 28))] + "TARGET_ABICALLS && Pmode == DImode" + "") + +;; For o32/n32/n64, we need to arrange for longjmp to put the +;; target address in t9 so that we can use it for loading $gp. + +(define_expand "builtin_longjmp" + [(unspec_volatile [(match_operand 0 "register_operand" "r")] 3)] + "TARGET_ABICALLS" + " +{ + /* The elements of the buffer are, in order: */ + int W = (Pmode == DImode ? 8 : 4); + rtx fp = gen_rtx_MEM (Pmode, operands[0]); + rtx lab = gen_rtx_MEM (Pmode, plus_constant (operands[0], 1*W)); + rtx stack = gen_rtx_MEM (Pmode, plus_constant (operands[0], 2*W)); + rtx gpv = gen_rtx_MEM (Pmode, plus_constant (operands[0], 3*W)); + rtx pv = gen_rtx_REG (Pmode, 25); + rtx gp = gen_rtx_REG (Pmode, 28); + + /* This bit is the same as expand_builtin_longjmp. */ + emit_move_insn (hard_frame_pointer_rtx, fp); + emit_move_insn (pv, lab); + emit_stack_restore (SAVE_NONLOCAL, stack, NULL_RTX); + emit_move_insn (gp, gpv); + emit_insn (gen_rtx_USE (VOIDmode, hard_frame_pointer_rtx)); + emit_insn (gen_rtx_USE (VOIDmode, stack_pointer_rtx)); + emit_insn (gen_rtx_USE (VOIDmode, gp)); + emit_indirect_jump (pv); + DONE; +}") + +;; +;; .................... +;; +;; Function prologue/epilogue +;; +;; .................... +;; + +(define_expand "prologue" + [(const_int 1)] + "" + " +{ + if (mips_isa >= 0) /* avoid unused code warnings */ + { + mips_expand_prologue (); + DONE; + } +}") + +;; Block any insns from being moved before this point, since the +;; profiling call to mcount can use various registers that aren't +;; saved or used to pass arguments. + +(define_insn "blockage" + [(unspec_volatile [(const_int 0)] 0)] + "" + "" + [(set_attr "type" "unknown") + (set_attr "mode" "none") + (set_attr "length" "0")]) + +(define_expand "epilogue" + [(const_int 2)] + "" + " +{ + if (mips_isa >= 0) /* avoid unused code warnings */ + { + mips_expand_epilogue (); + DONE; + } +}") + +;; Trivial return. Make it look like a normal return insn as that +;; allows jump optimizations to work better . +(define_insn "return" + [(return)] + "mips_can_use_return_insn ()" + "%*j\\t$31" + [(set_attr "type" "jump") + (set_attr "mode" "none") + (set_attr "length" "1")]) + +;; Normal return. +;; We match any mode for the return address, so that this will work with +;; both 32 bit and 64 bit targets. +(define_insn "return_internal" + [(use (match_operand 0 "register_operand" "")) + (return)] + "" + "* +{ + return \"%*j\\t%0\"; +}" + [(set_attr "type" "jump") + (set_attr "mode" "none") + (set_attr "length" "1")]) + +;; When generating embedded PIC code we need to get the address of the +;; current function. This specialized instruction does just that. + +(define_insn "get_fnaddr" + [(set (match_operand 0 "register_operand" "=d") + (unspec [(match_operand 1 "" "")] 1)) + (clobber (reg:SI 31))] + "TARGET_EMBEDDED_PIC + && GET_CODE (operands[1]) == SYMBOL_REF" + "%($LF%= = . + 8\;bal\\t$LF%=\;la\\t%0,%1-$LF%=%)\;addu\\t%0,%0,$31" + [(set_attr "type" "call") + (set_attr "mode" "none") + (set_attr "length" "4")]) + + +;; +;; .................... +;; +;; FUNCTION CALLS +;; +;; .................... + +;; calls.c now passes a third argument, make saber happy + +(define_expand "call" + [(parallel [(call (match_operand 0 "memory_operand" "m") + (match_operand 1 "" "i")) + (clobber (reg:SI 31)) + (use (match_operand 2 "" "")) ;; next_arg_reg + (use (match_operand 3 "" ""))])] ;; struct_value_size_rtx + "" + " +{ + rtx addr; + + if (operands[0]) /* eliminate unused code warnings */ + { + addr = XEXP (operands[0], 0); + if ((GET_CODE (addr) != REG && (!CONSTANT_ADDRESS_P (addr) || TARGET_LONG_CALLS)) + || ! call_insn_operand (addr, VOIDmode)) + XEXP (operands[0], 0) = copy_to_mode_reg (Pmode, addr); + + /* In order to pass small structures by value in registers + compatibly with the MIPS compiler, we need to shift the value + into the high part of the register. Function_arg has encoded + a PARALLEL rtx, holding a vector of adjustments to be made + as the next_arg_reg variable, so we split up the insns, + and emit them separately. */ + + if (operands[2] != (rtx)0 && GET_CODE (operands[2]) == PARALLEL) + { + rtvec adjust = XVEC (operands[2], 0); + int num = GET_NUM_ELEM (adjust); + int i; + + for (i = 0; i < num; i++) + emit_insn (RTVEC_ELT (adjust, i)); + } + + if (TARGET_MIPS16 + && mips16_hard_float + && operands[2] != 0 + && (int) GET_MODE (operands[2]) != 0) + { + if (build_mips16_call_stub (NULL_RTX, operands[0], operands[1], + (int) GET_MODE (operands[2]))) + DONE; + } + + emit_call_insn (gen_call_internal0 (operands[0], operands[1], + gen_rtx (REG, SImode, GP_REG_FIRST + 31))); + + DONE; + } +}") + +(define_expand "call_internal0" + [(parallel [(call (match_operand 0 "" "") + (match_operand 1 "" "")) + (clobber (match_operand:SI 2 "" ""))])] + "" + "") + +;; We need to recognize reg:SI 31 specially for the mips16, because we +;; don't have a constraint letter for it. + +(define_insn "" + [(call (mem (match_operand 0 "call_insn_operand" "ei")) + (match_operand 1 "" "i")) + (clobber (match_operand:SI 2 "register_operand" "=y"))] + "TARGET_MIPS16 && !TARGET_ABICALLS && !TARGET_LONG_CALLS + && GET_CODE (operands[2]) == REG && REGNO (operands[2]) == 31" + "%*jal\\t%0" + [(set_attr "type" "call") + (set_attr "mode" "none") + (set_attr "length" "2")]) + +(define_insn "call_internal1" + [(call (mem (match_operand 0 "call_insn_operand" "ri")) + (match_operand 1 "" "i")) + (clobber (match_operand:SI 2 "register_operand" "=d"))] + "!TARGET_ABICALLS && !TARGET_LONG_CALLS" + "* +{ + register rtx target = operands[0]; + + if (GET_CODE (target) == SYMBOL_REF) + return \"%*jal\\t%0\"; + else if (GET_CODE (target) == CONST_INT) + return \"%[li\\t%@,%0\\n\\t%*jal\\t%2,%@%]\"; + else + return \"%*jal\\t%2,%0\"; +}" + [(set_attr "type" "call") + (set_attr "mode" "none") + (set_attr "length" "1")]) + +(define_insn "call_internal2" + [(call (mem (match_operand 0 "call_insn_operand" "ri")) + (match_operand 1 "" "i")) + (clobber (match_operand:SI 2 "register_operand" "=d"))] + "TARGET_ABICALLS && !TARGET_LONG_CALLS" + "* +{ + register rtx target = operands[0]; + + if (GET_CODE (target) == SYMBOL_REF) + { + if (GET_MODE (target) == SImode) + return \"la\\t%^,%0\\n\\tjal\\t%2,%^\"; + else + return \"dla\\t%^,%0\\n\\tjal\\t%2,%^\"; + } + else if (GET_CODE (target) == CONST_INT) + return \"li\\t%^,%0\\n\\tjal\\t%2,%^\"; + else if (REGNO (target) != PIC_FUNCTION_ADDR_REGNUM) + return \"move\\t%^,%0\\n\\tjal\\t%2,%^\"; + else + return \"jal\\t%2,%0\"; +}" + [(set_attr "type" "call") + (set_attr "mode" "none") + (set_attr "length" "2")]) + +(define_insn "call_internal3a" + [(call (mem:SI (match_operand:SI 0 "register_operand" "r")) + (match_operand 1 "" "i")) + (clobber (match_operand:SI 2 "register_operand" "=d"))] + "!(Pmode == DImode) && !TARGET_ABICALLS && TARGET_LONG_CALLS" + "%*jal\\t%2,%0" + [(set_attr "type" "call") + (set_attr "mode" "none") + (set_attr "length" "1")]) + +(define_insn "call_internal3b" + [(call (mem:DI (match_operand:DI 0 "se_register_operand" "r")) + (match_operand 1 "" "i")) + (clobber (match_operand:SI 2 "register_operand" "=d"))] + "Pmode == DImode && !TARGET_ABICALLS && TARGET_LONG_CALLS" + "%*jal\\t%2,%0" + [(set_attr "type" "call") + (set_attr "mode" "none") + (set_attr "length" "1")]) + +(define_insn "call_internal4a" + [(call (mem:SI (match_operand:SI 0 "register_operand" "r")) + (match_operand 1 "" "i")) + (clobber (match_operand:SI 2 "register_operand" "=d"))] + "!(Pmode == DImode) && TARGET_ABICALLS && TARGET_LONG_CALLS" + "* +{ + if (REGNO (operands[0]) != PIC_FUNCTION_ADDR_REGNUM) + return \"move\\t%^,%0\\n\\tjal\\t%2,%^\"; + else + return \"jal\\t%2,%0\"; +}" + [(set_attr "type" "call") + (set_attr "mode" "none") + (set_attr "length" "2")]) + +(define_insn "call_internal4b" + [(call (mem:DI (match_operand:DI 0 "se_register_operand" "r")) + (match_operand 1 "" "i")) + (clobber (match_operand:SI 2 "register_operand" "=d"))] + "Pmode == DImode && TARGET_ABICALLS && TARGET_LONG_CALLS" + "* +{ + if (REGNO (operands[0]) != PIC_FUNCTION_ADDR_REGNUM) + return \"move\\t%^,%0\\n\\tjal\\t%2,%^\"; + else + return \"jal\\t%2,%0\"; +}" + [(set_attr "type" "call") + (set_attr "mode" "none") + (set_attr "length" "2")]) + +;; calls.c now passes a fourth argument, make saber happy + +(define_expand "call_value" + [(parallel [(set (match_operand 0 "register_operand" "=df") + (call (match_operand 1 "memory_operand" "m") + (match_operand 2 "" "i"))) + (clobber (reg:SI 31)) + (use (match_operand 3 "" ""))])] ;; next_arg_reg + "" + " +{ + rtx addr; + + if (operands[0]) /* eliminate unused code warning */ + { + addr = XEXP (operands[1], 0); + if ((GET_CODE (addr) != REG && (!CONSTANT_ADDRESS_P (addr) || TARGET_LONG_CALLS)) + || ! call_insn_operand (addr, VOIDmode)) + XEXP (operands[1], 0) = copy_to_mode_reg (Pmode, addr); + + /* In order to pass small structures by value in registers + compatibly with the MIPS compiler, we need to shift the value + into the high part of the register. Function_arg has encoded + a PARALLEL rtx, holding a vector of adjustments to be made + as the next_arg_reg variable, so we split up the insns, + and emit them separately. */ + + if (operands[3] != (rtx)0 && GET_CODE (operands[3]) == PARALLEL) + { + rtvec adjust = XVEC (operands[3], 0); + int num = GET_NUM_ELEM (adjust); + int i; + + for (i = 0; i < num; i++) + emit_insn (RTVEC_ELT (adjust, i)); + } + + if (TARGET_MIPS16 + && mips16_hard_float + && ((operands[3] != 0 + && (int) GET_MODE (operands[3]) != 0) + || GET_MODE_CLASS (GET_MODE (operands[0])) == MODE_FLOAT)) + { + if (build_mips16_call_stub (operands[0], operands[1], operands[2], + (operands[3] == 0 ? 0 + : (int) GET_MODE (operands[3])))) + DONE; + } + + /* Handle Irix6 function calls that have multiple non-contiguous + results. */ + if (GET_CODE (operands[0]) == PARALLEL && XVECLEN (operands[0], 0) > 1) + { + emit_call_insn (gen_call_value_multiple_internal0 + (XEXP (XVECEXP (operands[0], 0, 0), 0), + operands[1], operands[2], + XEXP (XVECEXP (operands[0], 0, 1), 0), + gen_rtx (REG, SImode, GP_REG_FIRST + 31))); + DONE; + } + + /* We have a call returning a DImode structure in an FP reg. + Strip off the now unnecessary PARALLEL. */ + if (GET_CODE (operands[0]) == PARALLEL) + operands[0] = XEXP (XVECEXP (operands[0], 0, 0), 0); + + emit_call_insn (gen_call_value_internal0 (operands[0], operands[1], operands[2], + gen_rtx (REG, SImode, GP_REG_FIRST + 31))); + + DONE; + } +}") + +(define_expand "call_value_internal0" + [(parallel [(set (match_operand 0 "" "") + (call (match_operand 1 "" "") + (match_operand 2 "" ""))) + (clobber (match_operand:SI 3 "" ""))])] + "" + "") + +;; Recognize $31 specially on the mips16, because we don't have a +;; constraint letter for it. + +(define_insn "" + [(set (match_operand 0 "register_operand" "=d") + (call (mem (match_operand 1 "call_insn_operand" "ei")) + (match_operand 2 "" "i"))) + (clobber (match_operand:SI 3 "register_operand" "=y"))] + "TARGET_MIPS16 && !TARGET_ABICALLS && !TARGET_LONG_CALLS + && GET_CODE (operands[3]) == REG && REGNO (operands[3]) == 31" + "%*jal\\t%1" + [(set_attr "type" "call") + (set_attr "mode" "none") + (set_attr "length" "2")]) + +(define_insn "call_value_internal1" + [(set (match_operand 0 "register_operand" "=df") + (call (mem (match_operand 1 "call_insn_operand" "ri")) + (match_operand 2 "" "i"))) + (clobber (match_operand:SI 3 "register_operand" "=d"))] + "!TARGET_ABICALLS && !TARGET_LONG_CALLS" + "* +{ + register rtx target = operands[1]; + + if (GET_CODE (target) == SYMBOL_REF) + return \"%*jal\\t%1\"; + else if (GET_CODE (target) == CONST_INT) + return \"%[li\\t%@,%1\\n\\t%*jal\\t%3,%@%]\"; + else + return \"%*jal\\t%3,%1\"; +}" + [(set_attr "type" "call") + (set_attr "mode" "none") + (set_attr "length" "1")]) + +(define_insn "call_value_internal2" + [(set (match_operand 0 "register_operand" "=df") + (call (mem (match_operand 1 "call_insn_operand" "ri")) + (match_operand 2 "" "i"))) + (clobber (match_operand:SI 3 "register_operand" "=d"))] + "TARGET_ABICALLS && !TARGET_LONG_CALLS" + "* +{ + register rtx target = operands[1]; + + if (GET_CODE (target) == SYMBOL_REF) + { + if (GET_MODE (target) == SImode) + return \"la\\t%^,%1\\n\\tjal\\t%3,%^\"; + else + return \"dla\\t%^,%1\\n\\tjal\\t%3,%^\"; + } + else if (GET_CODE (target) == CONST_INT) + return \"li\\t%^,%1\\n\\tjal\\t%3,%^\"; + else if (REGNO (target) != PIC_FUNCTION_ADDR_REGNUM) + return \"move\\t%^,%1\\n\\tjal\\t%3,%^\"; + else + return \"jal\\t%3,%1\"; +}" + [(set_attr "type" "call") + (set_attr "mode" "none") + (set_attr "length" "2")]) + +(define_insn "call_value_internal3a" + [(set (match_operand 0 "register_operand" "=df") + (call (mem:SI (match_operand:SI 1 "register_operand" "r")) + (match_operand 2 "" "i"))) + (clobber (match_operand:SI 3 "register_operand" "=d"))] + "!TARGET_MIPS16 + && !(Pmode == DImode) && !TARGET_ABICALLS && TARGET_LONG_CALLS" + "%*jal\\t%3,%1" + [(set_attr "type" "call") + (set_attr "mode" "none") + (set_attr "length" "1")]) + +(define_insn "call_value_internal3b" + [(set (match_operand 0 "register_operand" "=df") + (call (mem:DI (match_operand:DI 1 "se_register_operand" "r")) + (match_operand 2 "" "i"))) + (clobber (match_operand:SI 3 "register_operand" "=d"))] + "!TARGET_MIPS16 + && Pmode == DImode && !TARGET_ABICALLS && TARGET_LONG_CALLS" + "%*jal\\t%3,%1" + [(set_attr "type" "call") + (set_attr "mode" "none") + (set_attr "length" "1")]) + +(define_insn "call_value_internal3c" + [(set (match_operand 0 "register_operand" "=df") + (call (mem:SI (match_operand:SI 1 "register_operand" "e")) + (match_operand 2 "" "i"))) + (clobber (match_operand:SI 3 "register_operand" "=y"))] + "TARGET_MIPS16 && !(Pmode == DImode) && !TARGET_ABICALLS && TARGET_LONG_CALLS + && GET_CODE (operands[3]) == REG && REGNO (operands[3]) == 31" + "%*jal\\t%3,%1" + [(set_attr "type" "call") + (set_attr "mode" "none") + (set_attr "length" "1")]) + +(define_insn "call_value_internal4a" + [(set (match_operand 0 "register_operand" "=df") + (call (mem:SI (match_operand:SI 1 "register_operand" "r")) + (match_operand 2 "" "i"))) + (clobber (match_operand:SI 3 "register_operand" "=d"))] + "!(Pmode == DImode) && TARGET_ABICALLS && TARGET_LONG_CALLS" + "* +{ + if (REGNO (operands[1]) != PIC_FUNCTION_ADDR_REGNUM) + return \"move\\t%^,%1\\n\\tjal\\t%3,%^\"; + else + return \"jal\\t%3,%1\"; +}" + [(set_attr "type" "call") + (set_attr "mode" "none") + (set_attr "length" "2")]) + +(define_insn "call_value_internal4b" + [(set (match_operand 0 "register_operand" "=df") + (call (mem:DI (match_operand:DI 1 "se_register_operand" "r")) + (match_operand 2 "" "i"))) + (clobber (match_operand:SI 3 "register_operand" "=d"))] + "Pmode == DImode && TARGET_ABICALLS && TARGET_LONG_CALLS" + "* +{ + if (REGNO (operands[1]) != PIC_FUNCTION_ADDR_REGNUM) + return \"move\\t%^,%1\\n\\tjal\\t%3,%^\"; + else + return \"jal\\t%3,%1\"; +}" + [(set_attr "type" "call") + (set_attr "mode" "none") + (set_attr "length" "2")]) + +(define_expand "call_value_multiple_internal0" + [(parallel [(set (match_operand 0 "" "") + (call (match_operand 1 "" "") + (match_operand 2 "" ""))) + (set (match_operand 3 "" "") + (call (match_dup 1) + (match_dup 2))) + (clobber (match_operand:SI 4 "" ""))])] + "" + "") + +;; ??? May eventually need all 6 versions of the call patterns with multiple +;; return values. + +(define_insn "call_value_multiple_internal2" + [(set (match_operand 0 "register_operand" "=df") + (call (mem (match_operand 1 "call_insn_operand" "ri")) + (match_operand 2 "" "i"))) + (set (match_operand 3 "register_operand" "=df") + (call (mem (match_dup 1)) + (match_dup 2))) + (clobber (match_operand:SI 4 "register_operand" "=d"))] + "TARGET_ABICALLS && !TARGET_LONG_CALLS" + "* +{ + register rtx target = operands[1]; + + if (GET_CODE (target) == SYMBOL_REF) + { + if (GET_MODE (target) == SImode) + return \"la\\t%^,%1\\n\\tjal\\t%4,%^\"; + else + return \"la\\t%^,%1\\n\\tjal\\t%4,%^\"; + } + else if (GET_CODE (target) == CONST_INT) + return \"li\\t%^,%1\\n\\tjal\\t%4,%^\"; + else if (REGNO (target) != PIC_FUNCTION_ADDR_REGNUM) + return \"move\\t%^,%1\\n\\tjal\\t%4,%^\"; + else + return \"jal\\t%4,%1\"; +}" + [(set_attr "type" "call") + (set_attr "mode" "none") + (set_attr "length" "2")]) + + +;; Call subroutine returning any type. + +(define_expand "untyped_call" + [(parallel [(call (match_operand 0 "" "") + (const_int 0)) + (match_operand 1 "" "") + (match_operand 2 "" "")])] + "" + " +{ + if (operands[0]) /* silence statement not reached warnings */ + { + int i; + + emit_call_insn (gen_call (operands[0], const0_rtx, NULL, const0_rtx)); + + for (i = 0; i < XVECLEN (operands[2], 0); i++) + { + rtx set = XVECEXP (operands[2], 0, i); + emit_move_insn (SET_DEST (set), SET_SRC (set)); + } + + emit_insn (gen_blockage ()); + DONE; + } +}") + +;; +;; .................... +;; +;; MISC. +;; +;; .................... +;; + +(define_insn "nop" + [(const_int 0)] + "" + "%(nop%)" + [(set_attr "type" "nop") + (set_attr "mode" "none") + (set_attr "length" "1")]) + +;; The MIPS chip does not seem to require stack probes. +;; +;; (define_expand "probe" +;; [(set (match_dup 0) +;; (match_dup 1))] +;; "" +;; " +;; { +;; operands[0] = gen_reg_rtx (SImode); +;; operands[1] = gen_rtx (MEM, SImode, stack_pointer_rtx); +;; MEM_VOLATILE_P (operands[1]) = TRUE; +;; +;; /* fall through and generate default code */ +;; }") +;; + +;; +;; MIPS4 Conditional move instructions. + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (if_then_else:SI + (match_operator 4 "equality_op" + [(match_operand:SI 1 "register_operand" "d,d") + (const_int 0)]) + (match_operand:SI 2 "reg_or_0_operand" "dJ,0") + (match_operand:SI 3 "reg_or_0_operand" "0,dJ")))] + "mips_isa >= 4" + "@ + mov%B4\\t%0,%z2,%1 + mov%b4\\t%0,%z3,%1" + [(set_attr "type" "move") + (set_attr "mode" "SI")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (if_then_else:SI + (match_operator 4 "equality_op" + [(match_operand:DI 1 "se_register_operand" "d,d") + (const_int 0)]) + (match_operand:SI 2 "reg_or_0_operand" "dJ,0") + (match_operand:SI 3 "reg_or_0_operand" "0,dJ")))] + "mips_isa >= 4" + "@ + mov%B4\\t%0,%z2,%1 + mov%b4\\t%0,%z3,%1" + [(set_attr "type" "move") + (set_attr "mode" "SI")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (if_then_else:SI + (match_operator 3 "equality_op" [(match_operand:CC 4 + "register_operand" + "z,z") + (const_int 0)]) + (match_operand:SI 1 "reg_or_0_operand" "dJ,0") + (match_operand:SI 2 "reg_or_0_operand" "0,dJ")))] + "mips_isa >= 4 && TARGET_HARD_FLOAT" + "@ + mov%T3\\t%0,%z1,%4 + mov%t3\\t%0,%z2,%4" + [(set_attr "type" "move") + (set_attr "mode" "SI")]) + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (if_then_else:DI + (match_operator 4 "equality_op" + [(match_operand:SI 1 "register_operand" "d,d") + (const_int 0)]) + (match_operand:DI 2 "se_reg_or_0_operand" "dJ,0") + (match_operand:DI 3 "se_reg_or_0_operand" "0,dJ")))] + "mips_isa >= 4" + "@ + mov%B4\\t%0,%z2,%1 + mov%b4\\t%0,%z3,%1" + [(set_attr "type" "move") + (set_attr "mode" "DI")]) + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (if_then_else:DI + (match_operator 4 "equality_op" + [(match_operand:DI 1 "se_register_operand" "d,d") + (const_int 0)]) + (match_operand:DI 2 "se_reg_or_0_operand" "dJ,0") + (match_operand:DI 3 "se_reg_or_0_operand" "0,dJ")))] + "mips_isa >= 4" + "@ + mov%B4\\t%0,%z2,%1 + mov%b4\\t%0,%z3,%1" + [(set_attr "type" "move") + (set_attr "mode" "DI")]) + +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (if_then_else:DI + (match_operator 3 "equality_op" [(match_operand:CC 4 + "register_operand" + "z,z") + (const_int 0)]) + (match_operand:DI 1 "se_reg_or_0_operand" "dJ,0") + (match_operand:DI 2 "se_reg_or_0_operand" "0,dJ")))] + "mips_isa >= 4 && TARGET_HARD_FLOAT" + "@ + mov%T3\\t%0,%z1,%4 + mov%t3\\t%0,%z2,%4" + [(set_attr "type" "move") + (set_attr "mode" "DI")]) + +(define_insn "" + [(set (match_operand:SF 0 "register_operand" "=f,f") + (if_then_else:SF + (match_operator 4 "equality_op" + [(match_operand:SI 1 "register_operand" "d,d") + (const_int 0)]) + (match_operand:SF 2 "register_operand" "f,0") + (match_operand:SF 3 "register_operand" "0,f")))] + "mips_isa >= 4 && TARGET_HARD_FLOAT" + "@ + mov%B4.s\\t%0,%2,%1 + mov%b4.s\\t%0,%3,%1" + [(set_attr "type" "move") + (set_attr "mode" "SF")]) + +(define_insn "" + [(set (match_operand:SF 0 "register_operand" "=f,f") + (if_then_else:SF + (match_operator 3 "equality_op" [(match_operand:CC 4 + "register_operand" + "z,z") + (const_int 0)]) + (match_operand:SF 1 "register_operand" "f,0") + (match_operand:SF 2 "register_operand" "0,f")))] + "mips_isa >= 4 && TARGET_HARD_FLOAT" + "@ + mov%T3.s\\t%0,%1,%4 + mov%t3.s\\t%0,%2,%4" + [(set_attr "type" "move") + (set_attr "mode" "SF")]) + +(define_insn "" + [(set (match_operand:DF 0 "register_operand" "=f,f") + (if_then_else:DF + (match_operator 4 "equality_op" + [(match_operand:SI 1 "register_operand" "d,d") + (const_int 0)]) + (match_operand:DF 2 "register_operand" "f,0") + (match_operand:DF 3 "register_operand" "0,f")))] + "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT" + "@ + mov%B4.d\\t%0,%2,%1 + mov%b4.d\\t%0,%3,%1" + [(set_attr "type" "move") + (set_attr "mode" "DF")]) + +(define_insn "" + [(set (match_operand:DF 0 "register_operand" "=f,f") + (if_then_else:DF + (match_operator 3 "equality_op" [(match_operand:CC 4 + "register_operand" + "z,z") + (const_int 0)]) + (match_operand:DF 1 "register_operand" "f,0") + (match_operand:DF 2 "register_operand" "0,f")))] + "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT" + "@ + mov%T3.d\\t%0,%1,%4 + mov%t3.d\\t%0,%2,%4" + [(set_attr "type" "move") + (set_attr "mode" "DF")]) + +;; These are the main define_expand's used to make conditional moves. + +(define_expand "movsicc" + [(set (match_dup 4) (match_operand 1 "comparison_operator" "")) + (set (match_operand:SI 0 "register_operand" "") + (if_then_else:SI (match_dup 5) + (match_operand:SI 2 "reg_or_0_operand" "") + (match_operand:SI 3 "reg_or_0_operand" "")))] + "mips_isa >= 4" + " +{ + gen_conditional_move (operands); + DONE; +}") + +(define_expand "movdicc" + [(set (match_dup 4) (match_operand 1 "comparison_operator" "")) + (set (match_operand:DI 0 "register_operand" "") + (if_then_else:DI (match_dup 5) + (match_operand:DI 2 "se_reg_or_0_operand" "") + (match_operand:DI 3 "se_reg_or_0_operand" "")))] + "mips_isa >= 4" + " +{ + gen_conditional_move (operands); + DONE; +}") + +(define_expand "movsfcc" + [(set (match_dup 4) (match_operand 1 "comparison_operator" "")) + (set (match_operand:SF 0 "register_operand" "") + (if_then_else:SF (match_dup 5) + (match_operand:SF 2 "register_operand" "") + (match_operand:SF 3 "register_operand" "")))] + "mips_isa >= 4 && TARGET_HARD_FLOAT" + " +{ + gen_conditional_move (operands); + DONE; +}") + +(define_expand "movdfcc" + [(set (match_dup 4) (match_operand 1 "comparison_operator" "")) + (set (match_operand:DF 0 "register_operand" "") + (if_then_else:DF (match_dup 5) + (match_operand:DF 2 "register_operand" "") + (match_operand:DF 3 "register_operand" "")))] + "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT" + " +{ + gen_conditional_move (operands); + DONE; +}") + +;; +;; .................... +;; +;; mips16 inline constant tables +;; +;; .................... +;; + +(define_insn "consttable_qi" + [(unspec_volatile [(match_operand:QI 0 "consttable_operand" "=g")] 10)] + "TARGET_MIPS16" + "* +{ + assemble_integer (operands[0], 1, 1); + return \"\"; +}" + [(set_attr "type" "unknown") + (set_attr "mode" "QI") + (set_attr "length" "2")]) + +(define_insn "consttable_hi" + [(unspec_volatile [(match_operand:HI 0 "consttable_operand" "=g")] 11)] + "TARGET_MIPS16" + "* +{ + assemble_integer (operands[0], 2, 1); + return \"\"; +}" + [(set_attr "type" "unknown") + (set_attr "mode" "HI") + (set_attr "length" "2")]) + +(define_insn "consttable_si" + [(unspec_volatile [(match_operand:SI 0 "consttable_operand" "=g")] 12)] + "TARGET_MIPS16" + "* +{ + assemble_integer (operands[0], 4, 1); + return \"\"; +}" + [(set_attr "type" "unknown") + (set_attr "mode" "SI") + (set_attr "length" "2")]) + +(define_insn "consttable_di" + [(unspec_volatile [(match_operand:DI 0 "consttable_operand" "=g")] 13)] + "TARGET_MIPS16" + "* +{ + assemble_integer (operands[0], 8, 1); + return \"\"; +}" + [(set_attr "type" "unknown") + (set_attr "mode" "DI") + (set_attr "length" "4")]) + +(define_insn "consttable_sf" + [(unspec_volatile [(match_operand:SF 0 "consttable_operand" "=g")] 14)] + "TARGET_MIPS16" + "* +{ + union real_extract u; + + if (GET_CODE (operands[0]) != CONST_DOUBLE) + abort (); + bcopy ((char *) &CONST_DOUBLE_LOW (operands[0]), (char *) &u, sizeof u); + assemble_real (u.d, SFmode); + return \"\"; +}" + [(set_attr "type" "unknown") + (set_attr "mode" "SF") + (set_attr "length" "2")]) + +(define_insn "consttable_df" + [(unspec_volatile [(match_operand:DF 0 "consttable_operand" "=g")] 15)] + "TARGET_MIPS16" + "* +{ + union real_extract u; + + if (GET_CODE (operands[0]) != CONST_DOUBLE) + abort (); + bcopy ((char *) &CONST_DOUBLE_LOW (operands[0]), (char *) &u, sizeof u); + assemble_real (u.d, DFmode); + return \"\"; +}" + [(set_attr "type" "unknown") + (set_attr "mode" "DF") + (set_attr "length" "4")]) + +(define_insn "align_2" + [(unspec_volatile [(const_int 0)] 16)] + "TARGET_MIPS16" + ".align 1" + [(set_attr "type" "unknown") + (set_attr "mode" "HI") + (set_attr "length" "2")]) + +(define_insn "align_4" + [(unspec_volatile [(const_int 0)] 17)] + "TARGET_MIPS16" + ".align 2" + [(set_attr "type" "unknown") + (set_attr "mode" "SI") + (set_attr "length" "2")]) + +(define_insn "align_8" + [(unspec_volatile [(const_int 0)] 18)] + "TARGET_MIPS16" + ".align 3" + [(set_attr "type" "unknown") + (set_attr "mode" "DI") + (set_attr "length" "3")]) + +;; +;; .................... +;; +;; mips16 peepholes +;; +;; .................... +;; + +;; On the mips16, reload will sometimes decide that a pseudo register +;; should go into $24, and then later on have to reload that register. +;; When that happens, we get a load of a general register followed by +;; a move from the general register to $24 followed by a branch. +;; These peepholes catch the common case, and fix it to just use the +;; general register for the branch. + +(define_peephole + [(set (match_operand:SI 0 "register_operand" "=t") + (match_operand:SI 1 "register_operand" "d")) + (set (pc) + (if_then_else (match_operator:SI 2 "equality_op" [(match_dup 0) + (const_int 0)]) + (match_operand 3 "pc_or_label_operand" "") + (match_operand 4 "pc_or_label_operand" "")))] + "TARGET_MIPS16 + && GET_CODE (operands[0]) == REG + && REGNO (operands[0]) == 24 + && dead_or_set_p (insn, operands[0]) + && GET_CODE (operands[1]) == REG + && M16_REG_P (REGNO (operands[1]))" + "* +{ + if (operands[3] != pc_rtx) + return \"%*b%C2z\\t%1,%3\"; + else + return \"%*b%N2z\\t%1,%4\"; +}" + [(set_attr "type" "branch") + (set_attr "mode" "none") + (set_attr "length" "2")]) + +(define_peephole + [(set (match_operand:DI 0 "register_operand" "=t") + (match_operand:DI 1 "register_operand" "d")) + (set (pc) + (if_then_else (match_operator:DI 2 "equality_op" [(match_dup 0) + (const_int 0)]) + (match_operand 3 "pc_or_label_operand" "") + (match_operand 4 "pc_or_label_operand" "")))] + "TARGET_MIPS16 && TARGET_64BIT + && GET_CODE (operands[0]) == REG + && REGNO (operands[0]) == 24 + && dead_or_set_p (insn, operands[0]) + && GET_CODE (operands[1]) == REG + && M16_REG_P (REGNO (operands[1]))" + "* +{ + if (operands[3] != pc_rtx) + return \"%*b%C2z\\t%1,%3\"; + else + return \"%*b%N2z\\t%1,%4\"; +}" + [(set_attr "type" "branch") + (set_attr "mode" "none") + (set_attr "length" "2")]) + +;; We can also have the reverse reload: reload will spill $24 into +;; another register, and then do a branch on that register when it +;; could have just stuck with $24. + +(define_peephole + [(set (match_operand:SI 0 "register_operand" "=d") + (match_operand:SI 1 "register_operand" "t")) + (set (pc) + (if_then_else (match_operator:SI 2 "equality_op" [(match_dup 0) + (const_int 0)]) + (match_operand 3 "pc_or_label_operand" "") + (match_operand 4 "pc_or_label_operand" "")))] + "TARGET_MIPS16 + && GET_CODE (operands[1]) == REG + && REGNO (operands[1]) == 24 + && GET_CODE (operands[0]) == REG + && M16_REG_P (REGNO (operands[0])) + && dead_or_set_p (insn, operands[0])" + "* +{ + if (operands[3] != pc_rtx) + return \"%*bt%C2z\\t%3\"; + else + return \"%*bt%N2z\\t%4\"; +}" + [(set_attr "type" "branch") + (set_attr "mode" "none") + (set_attr "length" "2")]) + +(define_peephole + [(set (match_operand:DI 0 "register_operand" "=d") + (match_operand:DI 1 "register_operand" "t")) + (set (pc) + (if_then_else (match_operator:DI 2 "equality_op" [(match_dup 0) + (const_int 0)]) + (match_operand 3 "pc_or_label_operand" "") + (match_operand 4 "pc_or_label_operand" "")))] + "TARGET_MIPS16 && TARGET_64BIT + && GET_CODE (operands[1]) == REG + && REGNO (operands[1]) == 24 + && GET_CODE (operands[0]) == REG + && M16_REG_P (REGNO (operands[0])) + && dead_or_set_p (insn, operands[0])" + "* +{ + if (operands[3] != pc_rtx) + return \"%*bt%C2z\\t%3\"; + else + return \"%*bt%N2z\\t%4\"; +}" + [(set_attr "type" "branch") + (set_attr "mode" "none") + (set_attr "length" "2")]) + +;; For the rare case where we need to load an address into a register +;; that can not be recognized by the normal movsi/addsi instructions. +;; I have no idea how many insns this can actually generate. It should +;; be rare, so over-estimating as 10 instructions should not have any +;; real performance impact. +(define_insn "leasi" + [(set (match_operand:SI 0 "register_operand" "=d") + (match_operand:SI 1 "address_operand" "p"))] + "Pmode == SImode" + "la %0,%a1" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "10")]) + +;; Similarly for targets where we have 64bit pointers. +(define_insn "leadi" + [(set (match_operand:DI 0 "register_operand" "=d") + (match_operand:DI 1 "address_operand" "p"))] + "Pmode == DImode" + "la %0,%a1" + [(set_attr "type" "arith") + (set_attr "mode" "DI") + (set_attr "length" "10")]) diff --git a/contrib/gcc/config/mips/mips16.S b/contrib/gcc/config/mips/mips16.S new file mode 100644 index 000000000000..f21f10f2118b --- /dev/null +++ b/contrib/gcc/config/mips/mips16.S @@ -0,0 +1,740 @@ +/* mips16 floating point support code + Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc. + Contributed by Cygnus Support + +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 other files, + some of which are compiled with GCC, to produce an executable, + this library does not by itself cause the resulting executable + to be covered by the GNU General Public License. + This exception does not however invalidate any other reasons why + the executable file might be covered by the GNU General Public License. */ + +/* This file contains mips16 floating point support functions. These + functions are called by mips16 code to handle floating point when + -msoft-float is not used. They accept the arguments and return + values using the soft-float calling convention, but do the actual + operation using the hard floating point instructions. */ + +/* This file contains 32 bit assembly code. */ + .set nomips16 + +/* Start a function. */ + +#define STARTFN(NAME) .globl NAME; .ent NAME; NAME: + +/* Finish a function. */ + +#define ENDFN(NAME) .end NAME + +/* Single precision math. */ + +/* This macro defines a function which loads two single precision + values, performs an operation, and returns the single precision + result. */ + +#define SFOP(NAME, OPCODE) \ +STARTFN (NAME); \ + .set noreorder; \ + mtc1 $4,$f0; \ + mtc1 $5,$f2; \ + nop; \ + OPCODE $f0,$f0,$f2; \ + mfc1 $2,$f0; \ + j $31; \ + nop; \ + .set reorder; \ + ENDFN (NAME) + +#ifdef L_m16addsf3 +SFOP(__mips16_addsf3, add.s) +#endif +#ifdef L_m16subsf3 +SFOP(__mips16_subsf3, sub.s) +#endif +#ifdef L_m16mulsf3 +SFOP(__mips16_mulsf3, mul.s) +#endif +#ifdef L_m16divsf3 +SFOP(__mips16_divsf3, div.s) +#endif + +#define SFOP2(NAME, OPCODE) \ +STARTFN (NAME); \ + .set noreorder; \ + mtc1 $4,$f0; \ + nop; \ + OPCODE $f0,$f0; \ + mfc1 $2,$f0; \ + j $31; \ + nop; \ + .set reorder; \ + ENDFN (NAME) + +#ifdef L_m16negsf2 +SFOP2(__mips16_negsf2, neg.s) +#endif +#ifdef L_m16abssf2 +SFOP2(__mips16_abssf2, abs.s) +#endif + +/* Single precision comparisons. */ + +/* This macro defines a function which loads two single precision + values, performs a floating point comparison, and returns the + specified values according to whether the comparison is true or + false. */ + +#define SFCMP(NAME, OPCODE, TRUE, FALSE) \ +STARTFN (NAME); \ + mtc1 $4,$f0; \ + mtc1 $5,$f2; \ + OPCODE $f0,$f2; \ + li $2,TRUE; \ + bc1t 1f; \ + li $2,FALSE; \ +1:; \ + j $31; \ + ENDFN (NAME) + +/* This macro is like SFCMP, but it reverses the comparison. */ + +#define SFREVCMP(NAME, OPCODE, TRUE, FALSE) \ +STARTFN (NAME); \ + mtc1 $4,$f0; \ + mtc1 $5,$f2; \ + OPCODE $f2,$f0; \ + li $2,TRUE; \ + bc1t 1f; \ + li $2,FALSE; \ +1:; \ + j $31; \ + ENDFN (NAME) + +#ifdef L_m16eqsf2 +SFCMP(__mips16_eqsf2, c.eq.s, 0, 1) +#endif +#ifdef L_m16nesf2 +SFCMP(__mips16_nesf2, c.eq.s, 0, 1) +#endif +#ifdef L_m16gtsf2 +SFREVCMP(__mips16_gtsf2, c.lt.s, 1, 0) +#endif +#ifdef L_m16gesf2 +SFREVCMP(__mips16_gesf2, c.le.s, 0, -1) +#endif +#ifdef L_m16lesf2 +SFCMP(__mips16_lesf2, c.le.s, 0, 1) +#endif +#ifdef L_m16ltsf2 +SFCMP(__mips16_ltsf2, c.lt.s, -1, 0) +#endif + +/* Single precision conversions. */ + +#ifdef L_m16fltsisf +STARTFN (__mips16_floatsisf) + .set noreorder + mtc1 $4,$f0 + nop + cvt.s.w $f0,$f0 + mfc1 $2,$f0 + j $31 + nop + .set reorder + ENDFN (__mips16_floatsisf) +#endif + +#ifdef L_m16fixsfsi +STARTFN (__mips16_fixsfsi) + .set noreorder + mtc1 $4,$f0 + nop + trunc.w.s $f0,$f0,$4 + mfc1 $2,$f0 + j $31 + nop + .set reorder + ENDFN (__mips16_fixsfsi) +#endif + +#if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT) + +/* The double precision operations. We need to use different code + based on the preprocessor symbol __mips64, because the way in which + double precision values will change. Without __mips64, the value + is passed in two 32 bit registers. With __mips64, the value is + passed in a single 64 bit register. */ + +/* Load the first double precision operand. */ + +#if defined(__mips64) +#define LDDBL1 dmtc1 $4,$f12 +#elif defined(__mipsfp64) +#define LDDBL1 sw $4,0($29); sw $5,4($29); l.d $f12,0($29) +#elif defined(__MIPSEB__) +#define LDDBL1 mtc1 $4,$f13; mtc1 $5,$f12 +#else +#define LDDBL1 mtc1 $4,$f12; mtc1 $5,$f13 +#endif + +/* Load the second double precision operand. */ + +#if defined(__mips64) +/* XXX this should be $6 for Algo arg passing model */ +#define LDDBL2 dmtc1 $5,$f14 +#elif defined(__mipsfp64) +#define LDDBL2 sw $6,8($29); sw $7,12($29); l.d $f14,8($29) +#elif defined(__MIPSEB__) +#define LDDBL2 mtc1 $6,$f15; mtc1 $7,$f14 +#else +#define LDDBL2 mtc1 $6,$f14; mtc1 $7,$f15 +#endif + +/* Move the double precision return value to the right place. */ + +#if defined(__mips64) +#define RETDBL dmfc1 $2,$f0 +#elif defined(__mipsfp64) +#define RETDBL s.d $f0,0($29); lw $2,0($29); lw $3,4($29) +#elif defined(__MIPSEB__) +#define RETDBL mfc1 $2,$f1; mfc1 $3,$f0 +#else +#define RETDBL mfc1 $2,$f0; mfc1 $3,$f1 +#endif + +/* Double precision math. */ + +/* This macro defines a function which loads two double precision + values, performs an operation, and returns the double precision + result. */ + +#define DFOP(NAME, OPCODE) \ +STARTFN (NAME); \ + .set noreorder; \ + LDDBL1; \ + LDDBL2; \ + nop; \ + OPCODE $f0,$f12,$f14; \ + RETDBL; \ + j $31; \ + nop; \ + .set reorder; \ + ENDFN (NAME) + +#ifdef L_m16adddf3 +DFOP(__mips16_adddf3, add.d) +#endif +#ifdef L_m16subdf3 +DFOP(__mips16_subdf3, sub.d) +#endif +#ifdef L_m16muldf3 +DFOP(__mips16_muldf3, mul.d) +#endif +#ifdef L_m16divdf3 +DFOP(__mips16_divdf3, div.d) +#endif + +#define DFOP2(NAME, OPCODE) \ +STARTFN (NAME); \ + .set noreorder; \ + LDDBL1; \ + nop; \ + OPCODE $f0,$f12; \ + RETDBL; \ + j $31; \ + nop; \ + .set reorder; \ + ENDFN (NAME) + +#ifdef L_m16negdf2 +DFOP2(__mips16_negdf2, neg.d) +#endif +#ifdef L_m16absdf2 +DFOP2(__mips16_absdf2, abs.d) +#endif + + +/* Conversions between single and double precision. */ + +#ifdef L_m16extsfdf2 +STARTFN (__mips16_extendsfdf2) + .set noreorder + mtc1 $4,$f12 + nop + cvt.d.s $f0,$f12 + RETDBL + j $31 + nop + .set reorder + ENDFN (__mips16_extendsfdf2) +#endif + +#ifdef L_m16trdfsf2 +STARTFN (__mips16_truncdfsf2) + .set noreorder + LDDBL1 + nop + cvt.s.d $f0,$f12 + mfc1 $2,$f0 + j $31 + nop + .set reorder + ENDFN (__mips16_truncdfsf2) +#endif + +/* Double precision comparisons. */ + +/* This macro defines a function which loads two double precision + values, performs a floating point comparison, and returns the + specified values according to whether the comparison is true or + false. */ + +#define DFCMP(NAME, OPCODE, TRUE, FALSE) \ +STARTFN (NAME); \ + LDDBL1; \ + LDDBL2; \ + OPCODE $f12,$f14; \ + li $2,TRUE; \ + bc1t 1f; \ + li $2,FALSE; \ +1:; \ + j $31; \ + ENDFN (NAME) + +/* This macro is like DFCMP, but it reverses the comparison. */ + +#define DFREVCMP(NAME, OPCODE, TRUE, FALSE) \ +STARTFN (NAME); \ + LDDBL1; \ + LDDBL2; \ + OPCODE $f14,$f12; \ + li $2,TRUE; \ + bc1t 1f; \ + li $2,FALSE; \ +1:; \ + j $31; \ + ENDFN (NAME) + +#ifdef L_m16eqdf2 +DFCMP(__mips16_eqdf2, c.eq.d, 0, 1) +#endif +#ifdef L_m16nedf2 +DFCMP(__mips16_nedf2, c.eq.d, 0, 1) +#endif +#ifdef L_m16gtdf2 +DFREVCMP(__mips16_gtdf2, c.lt.d, 1, 0) +#endif +#ifdef L_m16gedf2 +DFREVCMP(__mips16_gedf2, c.le.d, 0, -1) +#endif +#ifdef L_m16ledf2 +DFCMP(__mips16_ledf2, c.le.d, 0, 1) +#endif +#ifdef L_m16ltdf2 +DFCMP(__mips16_ltdf2, c.lt.d, -1, 0) +#endif + +/* Double precision conversions. */ + +#ifdef L_m16fltsidf +STARTFN (__mips16_floatsidf) + .set noreorder + mtc1 $4,$f12 + nop + cvt.d.w $f0,$f12 + RETDBL + j $31 + nop + .set reorder + ENDFN (__mips16_floatsidf) +#endif + +#ifdef L_m16fixdfsi +STARTFN (__mips16_fixdfsi) + .set noreorder + LDDBL1 + nop + trunc.w.d $f0,$f12,$4 + mfc1 $2,$f0 + j $31 + nop + .set reorder + ENDFN (__mips16_fixdfsi) +#endif +#endif /* !__mips_single_float */ + +/* These functions are used to return floating point values from + mips16 functions which do not use -mentry. In this case we can + put mtc1 in a jump delay slot, because we know that the next + instruction will not refer to a floating point register. */ + +#ifdef L_m16retsf +STARTFN (__mips16_ret_sf) + .set noreorder + j $31 + mtc1 $2,$f0 + .set reorder + ENDFN (__mips16_ret_sf) +#endif + +#if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT) +#ifdef L_m16retdf +STARTFN (__mips16_ret_df) + .set noreorder +#if defined(__mips64) + j $31 + dmtc1 $2,$f0 +#elif defined(__mipsfp64) + sw $2,0($29) + sw $3,4($29) + l.d $f0,0($29) +#elif defined(__MIPSEB__) + mtc1 $2,$f1 + j $31 + mtc1 $3,$f0 +#else + mtc1 $2,$f0 + j $31 + mtc1 $3,$f1 +#endif + .set reorder + ENDFN (__mips16_ret_df) +#endif +#endif /* !__mips_single_float */ + +/* These functions are used by 16 bit code when calling via a function + pointer. They must copy the floating point arguments from the gp + regs into the fp regs. The function to call will be in $2. The + exact set of floating point arguments to copy is encoded in the + function name; the final number is an fp_code, as described in + mips.h in the comment about CUMULATIVE_ARGS. */ + +#ifdef L_m16stub1 +/* (float) */ +STARTFN (__mips16_call_stub_1) + .set noreorder + mtc1 $4,$f12 + j $2 + nop + .set reorder + ENDFN (__mips16_call_stub_1) +#endif + +#ifdef L_m16stub5 +/* (float, float) */ +STARTFN (__mips16_call_stub_5) + .set noreorder + mtc1 $4,$f12 + mtc1 $5,$f14 + j $2 + nop + .set reorder + ENDFN (__mips16_call_stub_5) +#endif + +#if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT) + +#ifdef L_m16stub2 +/* (double) */ +STARTFN (__mips16_call_stub_2) + .set noreorder + LDDBL1 + j $2 + nop + .set reorder + ENDFN (__mips16_call_stub_2) +#endif + +#ifdef L_m16stub6 +/* (double, float) */ +STARTFN (__mips16_call_stub_6) + .set noreorder + LDDBL1 + mtc1 $6,$f14 + j $2 + nop + .set reorder + ENDFN (__mips16_call_stub_6) +#endif + +#ifdef L_m16stub9 +/* (float, double) */ +STARTFN (__mips16_call_stub_9) + .set noreorder + mtc1 $4,$f12 + LDDBL2 + j $2 + nop + .set reorder + ENDFN (__mips16_call_stub_9) +#endif + +#ifdef L_m16stub10 +/* (double, double) */ +STARTFN (__mips16_call_stub_10) + .set noreorder + LDDBL1 + LDDBL2 + j $2 + nop + .set reorder + ENDFN (__mips16_call_stub_10) +#endif +#endif /* !__mips_single_float */ + +/* Now we have the same set of functions, except that this time the + function being called returns an SFmode value. The calling + function will arrange to preserve $18, so these functions are free + to use it to hold the return address. + + Note that we do not know whether the function we are calling is 16 + bit or 32 bit. However, it does not matter, because 16 bit + functions always return floating point values in both the gp and + the fp regs. It would be possible to check whether the function + being called is 16 bits, in which case the copy is unnecessary; + however, it's faster to always do the copy. */ + +#ifdef L_m16stubsf0 +/* () */ +STARTFN (__mips16_call_stub_sf_0) + .set noreorder + move $18,$31 + jal $2 + nop + mfc1 $2,$f0 + j $18 + nop + .set reorder + ENDFN (__mips16_call_stub_sf_0) +#endif + +#ifdef L_m16stubsf1 +/* (float) */ +STARTFN (__mips16_call_stub_sf_1) + .set noreorder + mtc1 $4,$f12 + move $18,$31 + jal $2 + nop + mfc1 $2,$f0 + j $18 + nop + .set reorder + ENDFN (__mips16_call_stub_sf_1) +#endif + +#ifdef L_m16stubsf5 +/* (float, float) */ +STARTFN (__mips16_call_stub_sf_5) + .set noreorder + mtc1 $4,$f12 + mtc1 $5,$f14 + move $18,$31 + jal $2 + nop + mfc1 $2,$f0 + j $18 + nop + .set reorder + ENDFN (__mips16_call_stub_sf_5) +#endif + +#if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT) +#ifdef L_m16stubsf2 +/* (double) */ +STARTFN (__mips16_call_stub_sf_2) + .set noreorder + LDDBL1 + move $18,$31 + jal $2 + nop + mfc1 $2,$f0 + j $18 + nop + .set reorder + ENDFN (__mips16_call_stub_sf_2) +#endif + +#ifdef L_m16stubsf6 +/* (double, float) */ +STARTFN (__mips16_call_stub_sf_6) + .set noreorder + LDDBL1 + mtc1 $6,$f14 + move $18,$31 + jal $2 + nop + mfc1 $2,$f0 + j $18 + nop + .set reorder + ENDFN (__mips16_call_stub_sf_6) +#endif + +#ifdef L_m16stubsf9 +/* (float, double) */ +STARTFN (__mips16_call_stub_sf_9) + .set noreorder + mtc1 $4,$f12 + LDDBL2 + move $18,$31 + jal $2 + nop + mfc1 $2,$f0 + j $18 + nop + .set reorder + ENDFN (__mips16_call_stub_sf_9) +#endif + +#ifdef L_m16stubsf10 +/* (double, double) */ +STARTFN (__mips16_call_stub_sf_10) + .set noreorder + LDDBL1 + LDDBL2 + move $18,$31 + jal $2 + nop + mfc1 $2,$f0 + j $18 + nop + .set reorder + ENDFN (__mips16_call_stub_sf_10) +#endif + +/* Now we have the same set of functions again, except that this time + the function being called returns an DFmode value. */ + +#ifdef L_m16stubdf0 +/* () */ +STARTFN (__mips16_call_stub_df_0) + .set noreorder + move $18,$31 + jal $2 + nop + RETDBL + j $18 + nop + .set reorder + ENDFN (__mips16_call_stub_df_0) +#endif + +#ifdef L_m16stubdf1 +/* (float) */ +STARTFN (__mips16_call_stub_df_1) + .set noreorder + mtc1 $4,$f12 + move $18,$31 + jal $2 + nop + RETDBL + j $18 + nop + .set reorder + ENDFN (__mips16_call_stub_df_1) +#endif + +#ifdef L_m16stubdf2 +/* (double) */ +STARTFN (__mips16_call_stub_df_2) + .set noreorder + LDDBL1 + move $18,$31 + jal $2 + nop + RETDBL + j $18 + nop + .set reorder + ENDFN (__mips16_call_stub_df_2) +#endif + +#ifdef L_m16stubdf5 +/* (float, float) */ +STARTFN (__mips16_call_stub_df_5) + .set noreorder + mtc1 $4,$f12 + mtc1 $5,$f14 + move $18,$31 + jal $2 + nop + RETDBL + j $18 + nop + .set reorder + ENDFN (__mips16_call_stub_df_5) +#endif + +#ifdef L_m16stubdf6 +/* (double, float) */ +STARTFN (__mips16_call_stub_df_6) + .set noreorder + LDDBL1 + mtc1 $6,$f14 + move $18,$31 + jal $2 + nop + RETDBL + j $18 + nop + .set reorder + ENDFN (__mips16_call_stub_df_6) +#endif + +#ifdef L_m16stubdf9 +/* (float, double) */ +STARTFN (__mips16_call_stub_df_9) + .set noreorder + mtc1 $4,$f12 + LDDBL2 + move $18,$31 + jal $2 + nop + RETDBL + j $18 + nop + .set reorder + ENDFN (__mips16_call_stub_df_9) +#endif + +#ifdef L_m16stubdf10 +/* (double, double) */ +STARTFN (__mips16_call_stub_df_10) + .set noreorder + LDDBL1 + LDDBL2 + move $18,$31 + jal $2 + nop + RETDBL + j $18 + nop + .set reorder + ENDFN (__mips16_call_stub_df_10) +#endif +#endif /* !__mips_single_float */ + diff --git a/contrib/gcc/config/mips/netbsd.h b/contrib/gcc/config/mips/netbsd.h new file mode 100644 index 000000000000..3fce9fb1de3f --- /dev/null +++ b/contrib/gcc/config/mips/netbsd.h @@ -0,0 +1,227 @@ +/* Definitions for DECstation running BSD as target machine for GNU compiler. + Copyright (C) 1993, 1995, 1996, 1997 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#define DECSTATION + +/* Look for the include files in the system-defined places. */ + +#ifndef CROSS_COMPILE +#undef GPLUSPLUS_INCLUDE_DIR +#define GPLUSPLUS_INCLUDE_DIR "/usr/include/g++" + +#undef GCC_INCLUDE_DIR +#define GCC_INCLUDE_DIR "/usr/include" + +#undef INCLUDE_DEFAULTS +#define INCLUDE_DEFAULTS \ + { \ + { GPLUSPLUS_INCLUDE_DIR, "G++", 1, 1 }, \ + { GCC_INCLUDE_DIR, "GCC", 0, 0 }, \ + { 0, 0, 0, 0 } \ + } + +/* Under NetBSD, the normal location of the various *crt*.o files is the + /usr/lib directory. */ + +#undef STANDARD_STARTFILE_PREFIX +#define STANDARD_STARTFILE_PREFIX "/usr/lib/" +#endif + +/* Provide a LINK_SPEC appropriate for NetBSD. Here we provide support + for the special GCC options -static, -assert, and -nostdlib. */ + +#undef LINK_SPEC +#define LINK_SPEC \ + "%{G*} %{EB} %{EL} %{mips1} %{mips2} %{mips3} \ + %{!nostartfiles:%{!r*:%{!e*:-e __start}}} -dc -dp %{static:-Bstatic} %{assert*}" + +/* We have atexit(3). */ + +#define HAVE_ATEXIT + +/* Implicit library calls should use memcpy, not bcopy, etc. */ + +#define TARGET_MEM_FUNCTIONS + +/* Define mips-specific netbsd predefines... */ +#ifndef CPP_PREDEFINES +#define CPP_PREDEFINES "-D__ANSI_COMPAT \ +-DMIPSEL -DR3000 -DSYSTYPE_BSD -D_SYSTYPE_BSD -D__NetBSD__ -Dmips \ +-D__NO_LEADING_UNDERSCORES__ -D__GP_SUPPORT__ \ +-Dunix -D_R3000 \ +-Asystem(unix) -Asystem(NetBSD) -Amachine(mips)" +#endif + +#ifndef SUBTARGET_CPP_SPEC +#define SUBTARGET_CPP_SPEC "%{posix:-D_POSIX_SOURCE}" +#endif + +#define LIB_SPEC "%{!p:%{!pg:-lc}}%{p:-lc_p}%{pg:-lc_p}" +#define STARTFILE_SPEC "" + +#ifndef MACHINE_TYPE +#define MACHINE_TYPE "NetBSD/pmax" +#endif + +#define TARGET_DEFAULT MASK_GAS +#define PREFERRED_DEBUGGING_TYPE DBX_DEBUG + +#include "mips/mips.h" + +/* + * Some imports from svr4.h in support of shared libraries. + * Currently, we need the DECLARE_OBJECT_SIZE stuff. + */ + +/* Define the strings used for the special svr4 .type and .size directives. + These strings generally do not vary from one system running svr4 to + another, but if a given system (e.g. m88k running svr) needs to use + different pseudo-op names for these, they may be overridden in the + file which includes this one. */ + +#undef TYPE_ASM_OP +#undef SIZE_ASM_OP +#undef WEAK_ASM_OP +#define TYPE_ASM_OP ".type" +#define SIZE_ASM_OP ".size" +#define WEAK_ASM_OP ".weak" + +/* The following macro defines the format used to output the second + operand of the .type assembler directive. Different svr4 assemblers + expect various different forms for this operand. The one given here + is just a default. You may need to override it in your machine- + specific tm.h file (depending upon the particulars of your assembler). */ + +#undef TYPE_OPERAND_FMT +#define TYPE_OPERAND_FMT "@%s" + +/* Write the extra assembler code needed to declare a function's result. + Most svr4 assemblers don't require any special declaration of the + result value, but there are exceptions. */ + +#ifndef ASM_DECLARE_RESULT +#define ASM_DECLARE_RESULT(FILE, RESULT) +#endif + +/* These macros generate the special .type and .size directives which + are used to set the corresponding fields of the linker symbol table + entries in an ELF object file under SVR4. These macros also output + the starting labels for the relevant functions/objects. */ + +/* Write the extra assembler code needed to declare a function properly. + Some svr4 assemblers need to also have something extra said about the + function's return value. We allow for that here. */ + +#undef ASM_DECLARE_FUNCTION_NAME +#define ASM_DECLARE_FUNCTION_NAME(FILE, NAME, DECL) \ + do { \ + fprintf (FILE, "\t%s\t ", TYPE_ASM_OP); \ + assemble_name (FILE, NAME); \ + putc (',', FILE); \ + fprintf (FILE, TYPE_OPERAND_FMT, "function"); \ + putc ('\n', FILE); \ + ASM_DECLARE_RESULT (FILE, DECL_RESULT (DECL)); \ + } while (0) + +/* Write the extra assembler code needed to declare an object properly. */ + +#undef ASM_DECLARE_OBJECT_NAME +#define ASM_DECLARE_OBJECT_NAME(FILE, NAME, DECL) \ + do { \ + fprintf (FILE, "\t%s\t ", TYPE_ASM_OP); \ + assemble_name (FILE, NAME); \ + putc (',', FILE); \ + fprintf (FILE, TYPE_OPERAND_FMT, "object"); \ + putc ('\n', FILE); \ + size_directive_output = 0; \ + if (!flag_inhibit_size_directive && DECL_SIZE (DECL)) \ + { \ + size_directive_output = 1; \ + fprintf (FILE, "\t%s\t ", SIZE_ASM_OP); \ + assemble_name (FILE, NAME); \ + fprintf (FILE, ",%d\n", int_size_in_bytes (TREE_TYPE (DECL))); \ + } \ + ASM_OUTPUT_LABEL(FILE, NAME); \ + } while (0) + +/* Output the size directive for a decl in rest_of_decl_compilation + in the case where we did not do so before the initializer. + Once we find the error_mark_node, we know that the value of + size_directive_output was set + by ASM_DECLARE_OBJECT_NAME when it was run for the same decl. */ + +#undef ASM_FINISH_DECLARE_OBJECT +#define ASM_FINISH_DECLARE_OBJECT(FILE, DECL, TOP_LEVEL, AT_END) \ +do { \ + char *name = XSTR (XEXP (DECL_RTL (DECL), 0), 0); \ + if (!flag_inhibit_size_directive && DECL_SIZE (DECL) \ + && ! AT_END && TOP_LEVEL \ + && DECL_INITIAL (DECL) == error_mark_node \ + && !size_directive_output) \ + { \ + size_directive_output = 1; \ + fprintf (FILE, "\t%s\t ", SIZE_ASM_OP); \ + assemble_name (FILE, name); \ + fprintf (FILE, ",%d\n", int_size_in_bytes (TREE_TYPE (DECL))); \ + } \ + } while (0) + +/* This is how to declare the size of a function. */ + +#undef ASM_DECLARE_FUNCTION_SIZE +#define ASM_DECLARE_FUNCTION_SIZE(FILE, FNAME, DECL) \ + do { \ + if (!flag_inhibit_size_directive) \ + { \ + char label[256]; \ + static int labelno; \ + labelno++; \ + ASM_GENERATE_INTERNAL_LABEL (label, "Lfe", labelno); \ + ASM_OUTPUT_INTERNAL_LABEL (FILE, "Lfe", labelno); \ + fprintf (FILE, "\t%s\t ", SIZE_ASM_OP); \ + assemble_name (FILE, (FNAME)); \ + fprintf (FILE, ","); \ + assemble_name (FILE, label); \ + fprintf (FILE, "-"); \ + assemble_name (FILE, (FNAME)); \ + putc ('\n', FILE); \ + } \ + } while (0) + +/* + 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. +*/ +#define ASM_OUTPUT_SECTION_NAME(F, DECL, NAME, RELOC) \ +do { \ + extern FILE *asm_out_text_file; \ + if ((DECL) && TREE_CODE (DECL) == FUNCTION_DECL) \ + fprintf (asm_out_text_file, "\t.section %s,\"ax\",@progbits\n", (NAME)); \ + else if ((DECL) && DECL_READONLY_SECTION (DECL, RELOC)) \ + fprintf (F, "\t.section %s,\"a\",@progbits\n", (NAME)); \ + else \ + fprintf (F, "\t.section %s,\"aw\",@progbits\n", (NAME)); \ +} while (0) + +/* Since gas and gld are standard on NetBSD, we don't need these */ +#undef ASM_FINAL_SPEC +#undef STARTFILE_SPEC diff --git a/contrib/gcc/config/mips/news4.h b/contrib/gcc/config/mips/news4.h new file mode 100644 index 000000000000..502affa67c70 --- /dev/null +++ b/contrib/gcc/config/mips/news4.h @@ -0,0 +1,39 @@ +/* Definitions of target machine for GNU compiler. Sony RISC NEWS (mips) + Copyright (C) 1991, 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. */ + +#define MIPS_NEWS + +#define CPP_PREDEFINES "\ +-Dr3000 -Dnews3700 -DLANGUAGE_C -DMIPSEB -DSYSTYPE_BSD \ +-Dsony_news -Dsony -Dunix -Dmips -Dhost_mips \ +-Asystem(unix) -Asystem(bsd) -Acpu(mips) -Amachine(mips)" + +#define SYSTEM_INCLUDE_DIR "/usr/include2.0" + +#define LIB_SPEC "%{p:-lprof1} %{pg:-lprof1} -lc" + +#define STARTFILE_SPEC "%{pg:gcrt0.o%s}%{!pg:%{p:mcrt0.o%s}%{!p:crt0.o%s}}" + +#define MACHINE_TYPE "RISC NEWS-OS" + +/* INITIALIZE_TRAMPOLINE calls this library function to flush + program and data caches. */ +#define CACHE_FLUSH_FUNC "cacheflush" + diff --git a/contrib/gcc/config/mips/news5.h b/contrib/gcc/config/mips/news5.h new file mode 100644 index 000000000000..a776064d1930 --- /dev/null +++ b/contrib/gcc/config/mips/news5.h @@ -0,0 +1,62 @@ +/* Definitions of target machine for GNU compiler. + Sony RISC NEWS (mips) System V version. + Copyright (C) 1992 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 MIPS_SYSV + +#define CPP_PREDEFINES "\ +-Dmips -Dunix -Dhost_mips -Dsony -Dsonyrisc -DMIPSEB -DSYSTYPE_SYSV \ +-Asystem(unix) -Asystem(svr3) -Acpu(mips) -Amachine(mips)" + +#define MD_STARTFILE_PREFIX "/usr/ccs/lib/" + +#define LIB_SPEC "\ +%{ZBSD43: -L/usr/ucblib -lucb -lresolv -lsocket -lnsl} \ +-nocount %{p:-lprof1} %{pg:-lprof1} -lc crtn.o%s values-Xt.o%s" + +#define STARTFILE_SPEC "%{pg:gcrt0.o%s}%{!pg:%{p:mcrt1.o%s}%{!p:-nocount crt1.o%s -count}}" + +#define MACHINE_TYPE "Sony RISC NEWS (SVR4 mips)" + +#define NO_LIB_PROTOTYPE + +#define NO_DOLLAR_IN_LABEL + +#define NM_FLAGS "-Bp" + +/* Generate calls to memcpy, etc., not bcopy, etc. */ +#define TARGET_MEM_FUNCTIONS + +/* Mips System V.4 doesn't have a getpagesize() function needed by the + trampoline code, so use the POSIX sysconf function to get it. + This is only done when compiling the trampoline code. */ + +#ifdef L_trampoline +#include <sys/param.h> +#include <unistd.h> + +#ifdef _SC_PAGE_SIZE +#define getpagesize() sysconf(_SC_PAGE_SIZE) + +#else /* older rev of OS */ +#define getpagesize() (NBPC) +#endif /* !_SC_PAGE_SIZE */ +#endif /* L_trampoline */ + diff --git a/contrib/gcc/config/mips/nws3250v4.h b/contrib/gcc/config/mips/nws3250v4.h new file mode 100644 index 000000000000..611effe02a16 --- /dev/null +++ b/contrib/gcc/config/mips/nws3250v4.h @@ -0,0 +1,36 @@ +/* Definitions of target machine for GNU compiler. Sony RISC NEWS (mips) + Copyright (C) 1991 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 MIPS_NEWS + +#define CPP_PREDEFINES "\ +-Dmips -Dhost_mips -Dsony -Dsonyrisc -Dunix \ +-DLANGUAGE_C -DMIPSEB -DSYSTYPE_SYSV \ +-Asystem(unix) -Asystem(svr3) -Acpu(mips) -Amachine(mips)" + +#define MD_STARTFILE_PREFIX "/usr/ccs/lib/" + +#define LIB_SPEC "%{p:-lprof1} %{pg:-lprof1} -lc crtn.o%s values-Xt.o%s" + +#define STARTFILE_SPEC "%{pg:gcrt0.o%s}%{!pg:%{p:mcrt0.o%s}%{!p:crt1.o%s}}" + +#define MACHINE_TYPE "RISC NEWS-OS SVr4" + +#include "mips/mips.h" diff --git a/contrib/gcc/config/mips/osfrose.h b/contrib/gcc/config/mips/osfrose.h new file mode 100644 index 000000000000..3d92619c3e58 --- /dev/null +++ b/contrib/gcc/config/mips/osfrose.h @@ -0,0 +1,150 @@ +/* Definitions of target machine for GNU compiler. + DECstation (OSF/1 reference port with OSF/rose) version. + Copyright (C) 1991, 1992, 1995, 1996, 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. */ + +#define DECSTATION +#define OSF_OS + +#define HALF_PIC_DEBUG TARGET_DEBUG_B_MODE +#define HALF_PIC_PREFIX "$Lp." + +#include "halfpic.h" + +#define WORD_SWITCH_TAKES_ARG(STR) \ + (DEFAULT_WORD_SWITCH_TAKES_ARG (STR) || !strcmp (STR, "pic-names")) + +#define CPP_PREDEFINES "\ +-DOSF -DOSF1 -Dbsd4_2 -DMIPSEL -Dhost_mips -Dmips -Dunix -DR3000 -DSYSTYPE_BSD \ +-Asystem(unix) -Asystem(xpg4) -Acpu(mips) -Amachine(mips)" + +#define SUBTARGET_CPP_SIZE_SPEC "\ +%{mlong64:-D__PTRDIFF_TYPE__=long\\ int} \ +%{!mlong64:-D__PTRDIFF_TYPE__=int}" + +#define SUBTARGET_CPP_SPEC "\ +%{.S: %{!ansi:%{!traditional:%{!traditional-cpp:%{!ftraditional: -traditional}}}}} \ +%{.s: %{!ansi:%{!traditional:%{!traditional-cpp:%{!ftraditional: -traditional}}}}}" + +/* ??? This assumes that GNU as is always used with GNU ld, and MIPS as is + always used with MIPS ld. */ +#define LINK_SPEC "\ +%{G*} %{EL} %{EB} %{mips1} %{mips2} %{mips3} \ +%{bestGnum} \ +%{!mmips-as: \ + %{v*: -v} \ + %{!noshrlib: %{pic-none: -noshrlib} %{!pic-none: -warn_nopic}} \ + %{nostdlib} %{noshrlib} %{glue}}" + +#define LIB_SPEC "-lc" + +/* Define this macro meaning that `gcc' should find the library + `libgcc.a' by hand, rather than passing the argument `-lgcc' to + tell the linker to do the search. */ + +#define LINK_LIBGCC_SPECIAL 1 + +#define STARTFILE_SPEC "%{pg:gcrt0.o%s}%{!pg:%{p:mcrt0.o%s}%{!p:crt0.o%s}}" + +#define MACHINE_TYPE "DECstation with OSF/rose objects" + +#ifndef MD_EXEC_PREFIX +#define MD_EXEC_PREFIX "/usr/ccs/gcc/" +#endif + +#ifndef MD_STARTFILE_PREFIX +#define MD_STARTFILE_PREFIX "/usr/ccs/lib/" +#endif + +/* Turn on -mpic-extern by default. */ +#define CC1_SPEC "\ +%{gline:%{!g:%{!g0:%{!g1:%{!g2: -g1}}}}} \ +%{mips1:-mfp32 -mgp32} %{mips2:-mfp32 -mgp32} %{mips3:-mfp64 -mgp64} \ +%{mint64|mlong64|mlong32:-mexplicit-type-size }\ +%{G*} \ +%{pic-none: -mno-half-pic} \ +%{pic-lib: -mhalf-pic} \ +%{pic-extern: -mhalf-pic} \ +%{pic-calls: -mhalf-pic} \ +%{pic-names*: -mhalf-pic} \ +%{!pic-*: -mhalf-pic}" + +/* Specify size_t and wchar_t types. */ +#define SIZE_TYPE "long unsigned int" +#define WCHAR_TYPE "unsigned int" +#define WCHAR_TYPE_SIZE BITS_PER_WORD +#define MAX_WCHAR_TYPE_SIZE MAX_LONG_TYPE_SIZE + +/* OSF/1 uses gas, not the mips assembler. */ +#define TARGET_DEFAULT MASK_GAS + +/* OSF/rose uses stabs, not ECOFF. */ +#define PREFERRED_DEBUGGING_TYPE DBX_DEBUG + +/* enable dwarf debugging for testing */ +#define DWARF_DEBUGGING_INFO +/* This is needed by dwarfout.c. */ +#define SET_ASM_OP ".set" + +/* Tell collect that the object format is OSF/rose. */ +#define OBJECT_FORMAT_ROSE + +/* Tell collect where the appropriate binaries are. */ +#define REAL_LD_FILE_NAME "/usr/ccs/gcc/gld" +#define REAL_NM_FILE_NAME "/usr/ccs/bin/nm" +#define REAL_STRIP_FILE_NAME "/usr/ccs/bin/strip" + +/* Default to -G 0 unless doing ecoff work. */ +#define MIPS_DEFAULT_GVALUE ((TARGET_MIPS_AS) ? 8 : 0) + +/* Use atexit for static constructors/destructors, instead of defining + our own exit function. */ +#define HAVE_ATEXIT + +/* Generate calls to memcpy, etc., not bcopy, etc. */ +#define TARGET_MEM_FUNCTIONS + +/* A C statement to output assembler commands which will identify + the object file as having been compiled with GNU CC (or another + GNU compiler). + + If you don't define this macro, the string `gcc2_compiled.:' is + output. This string is calculated to define a symbol which, on + BSD systems, will never be defined for any other reason. GDB + checks for the presence of this symbol when reading the symbol + table of an executable. + + On non-BSD systems, you must arrange communication with GDB in + some other fashion. If GDB is not used on your system, you can + define this macro with an empty body. + + On OSF/1, gcc2_compiled. confuses the kernel debugger, so don't + put it out. */ + +#define ASM_IDENTIFY_GCC(STREAM) + +/* Identify the front-end which produced this file. To keep symbol + space down, and not confuse kdb, only do this if the language is + not C. */ + +#define ASM_IDENTIFY_LANGUAGE(STREAM) \ +{ \ + if (strcmp (lang_identify (), "c") != 0) \ + output_lang_identify (STREAM); \ +} diff --git a/contrib/gcc/config/mips/r3900.h b/contrib/gcc/config/mips/r3900.h new file mode 100644 index 000000000000..3d7cac5edc11 --- /dev/null +++ b/contrib/gcc/config/mips/r3900.h @@ -0,0 +1,72 @@ +/* Definitions of MIPS sub target machine for GNU compiler. + Toshiba r3900. You should include mips.h after this. + + Copyright (C) 1989, 90-6, 1997 Free Software Foundation, Inc. + Contributed by Gavin Koch (gavin@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. */ + +#define SUBTARGET_CPP_SPEC "\ +%{!mabi=32: %{!mabi=n32: %{!mabi=64: -D__mips_eabi}}} \ +%{!msingle-float:-D__mips_soft_float} \ +%{mhard-float:%e-mhard-float not supported.} \ +%{msingle-float:%{msoft-float: \ + %e-msingle-float and -msoft-float can not both be specified.}}" + +/* The following is needed because -mips3 and -mips4 set gp64 which in + combination with abi=eabi, causes long64 to be set. */ +#define SUBTARGET_CPP_SIZE_SPEC "\ +%{mips3:-D__SIZE_TYPE__=long\\ unsigned\\ int -D__PTRDIFF_TYPE__=long\\ int} \ +%{mips4:-D__SIZE_TYPE__=long\\ unsigned\\ int -D__PTRDIFF_TYPE__=long\\ int} \ +%{!mips3:%{!mips4:%{!m4650:\ + -D__SIZE_TYPE__=unsigned\\ int -D__PTRDIFF_TYPE__=int}}} " + +/* by default (if not mips-something-else) produce code for the r3900 */ +#define SUBTARGET_CC1_SPEC "\ +%{mhard-float:%e-mhard-float not supported.} \ +%{msingle-float:%{msoft-float: \ + %e-msingle-float and -msoft-float can not both be specified.}}" + +#define TARGET_DEFAULT (MASK_SOFT_FLOAT | MASK_MIPS3900) +#define MIPS_CPU_STRING_DEFAULT "R3900" +#define MIPS_ISA_DEFAULT 1 + +#define MULTILIB_DEFAULTS { "EB", "msoft-float" } + +/* We use the MIPS EABI by default. */ +#define MIPS_ABI_DEFAULT ABI_EABI + + +/* Debugging */ + +#define DWARF2_DEBUGGING_INFO +#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG + +/* For the 'preferred' cases ("gN" and "ggdbN") we need to tell the + gnu assembler not to generate debugging information. */ + +#define SUBTARGET_ASM_DEBUGGING_SPEC "\ +%{!mmips-as: \ + %{g:-g0} %{g0:-g0} %{g1:-g0} %{g2:-g0} %{g3:-g0} \ + %{ggdb:-g0} %{ggdb0:-g0} %{ggdb1:-g0} %{ggdb2:-g0} %{ggdb3:-g0} \ + %{gdwarf-2*:-g0}} \ +%{gstabs:-g} %{gstabs0:-g0} %{gstabs1:-g1} %{gstabs2:-g2} %{gstabs3:-g3} \ +%{gstabs+:-g} %{gstabs+0:-g0} %{gstabs+1:-g1} %{gstabs+2:-g2} %{gstabs+3:-g3} \ +%{gcoff:-g} %{gcoff0:-g0} %{gcoff1:-g1} %{gcoff2:-g2} %{gcoff3:-g3}" + +/* eof */ diff --git a/contrib/gcc/config/mips/rtems64.h b/contrib/gcc/config/mips/rtems64.h new file mode 100644 index 000000000000..cd7997086385 --- /dev/null +++ b/contrib/gcc/config/mips/rtems64.h @@ -0,0 +1,63 @@ +/* Definitions for rtems targeting a MIPS ORION using ecoff. + Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + Contributed by Joel Sherrill (joel@OARcorp.com). + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Specify predefined symbols in preprocessor. */ + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dmips -DMIPSEB -DR4000 -D_mips -D_MIPSEB -D_R4000 \ + -Drtems -D__rtems__ -Asystem(rtems)" + +/* Generate calls to memcpy, memcmp and memset. */ +#ifndef TARGET_MEM_FUNCTIONS +#define TARGET_MEM_FUNCTIONS +#endif + +/* Undefine the following which were defined in elf64.h. This will cause the rtems64 + port to continue to use collect2 for constructors/destructors. These may be removed + when .ctor/.dtor section support is desired. */ + +#undef CTORS_SECTION_ASM_OP +#undef DTORS_SECTION_ASM_OP + +#undef EXTRA_SECTIONS +#define EXTRA_SECTIONS in_sdata, in_rdata + +#undef INVOKE__main +#undef NAME__MAIN +#undef SYMBOL__MAIN + +#undef EXTRA_SECTION_FUNCTIONS +#define EXTRA_SECTION_FUNCTIONS \ + SECTION_FUNCTION_TEMPLATE(sdata_section, in_sdata, SDATA_SECTION_ASM_OP) \ + SECTION_FUNCTION_TEMPLATE(rdata_section, in_rdata, RDATA_SECTION_ASM_OP) + +#undef ASM_OUTPUT_CONSTRUCTOR +#undef ASM_OUTPUT_DESTRUCTOR + +#undef CTOR_LIST_BEGIN +#undef CTOR_LIST_END +#undef DTOR_LIST_BEGIN +#undef DTOR_LIST_END + +#undef STARTFILE_SPEC +#undef ENDFILE_SPEC + +/* End of undefines to turn off .ctor/.dtor section support */ diff --git a/contrib/gcc/config/mips/sni-gas.h b/contrib/gcc/config/mips/sni-gas.h new file mode 100644 index 000000000000..5b3699820f37 --- /dev/null +++ b/contrib/gcc/config/mips/sni-gas.h @@ -0,0 +1,38 @@ +/* Enable debugging. */ +#define DBX_DEBUGGING_INFO +#define SDB_DEBUGGING_INFO +#define MIPS_DEBUGGING_INFO + +#define DWARF_DEBUGGING_INFO +#undef PREFERRED_DEBUGGING_TYPE +#define PREFERRED_DEBUGGING_TYPE DWARF_DEBUG + +/* We need to use .esize and .etype instead of .size and .type to + avoid conflicting with ELF directives. These are only recognized + by gas, anyhow, not the native assembler. */ +#undef PUT_SDB_SIZE +#define PUT_SDB_SIZE(a) \ +do { \ + extern FILE *asm_out_text_file; \ + fprintf (asm_out_text_file, "\t.esize\t%d;", (a)); \ +} while (0) + +#undef PUT_SDB_TYPE +#define PUT_SDB_TYPE(a) \ +do { \ + extern FILE *asm_out_text_file; \ + fprintf (asm_out_text_file, "\t.etype\t0x%x;", (a)); \ +} 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) diff --git a/contrib/gcc/config/mips/sni-svr4.h b/contrib/gcc/config/mips/sni-svr4.h new file mode 100644 index 000000000000..cf6edbccad6c --- /dev/null +++ b/contrib/gcc/config/mips/sni-svr4.h @@ -0,0 +1,100 @@ +/* Definitions of target machine for GNU compiler. SNI SINIX version. + Copyright (C) 1996, 1997 Free Software Foundation, Inc. + Contributed by Marco Walther (Marco.Walther@mch.sni.de). + +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 MIPS_SVR4 + +#define CPP_PREDEFINES "\ +-Dmips -Dunix -Dhost_mips -DMIPSEB -DR3000 -DSYSTYPE_SVR4 -Dsinix -DSNI \ +-D_mips -D_unix -D_host_mips -D_MIPSEB -D_R3000 -D_SYSTYPE_SVR4 \ +-Asystem(unix) -Asystem(svr4) -Acpu(mips) -Amachine(mips)" + +#define SUBTARGET_CPP_SIZE_SPEC "\ +-D__SIZE_TYPE__=unsigned\\ int -D__PTRDIFF_TYPE__=int" + +#define LINK_SPEC "\ +%{G*} \ +%{!mgas: \ + %{dy} %{dn}}" + +#define LIB_SPEC "\ + %{p:-lprof1} \ + %{!p:%{pg:-lprof1} \ + %{!pg:-L/usr/ccs/lib/ -lc /usr/ccs/lib/crtn.o%s}}" + +#define STARTFILE_SPEC "\ + %{pg:gcrt0.o%s} \ + %{!pg:%{p:mcrt0.o%s} \ + %{!p:/usr/ccs/lib/crt1.o /usr/ccs/lib/crti.o /usr/ccs/lib/values-Xt.o%s}}" + +/* Mips System V.4 doesn't have a getpagesize() function needed by the + trampoline code, so use the POSIX sysconf function to get it. + This is only done when compiling the trampoline code. */ + +#ifdef L_trampoline +#include <unistd.h> + +#define getpagesize() sysconf(_SC_PAGE_SIZE) +#endif /* L_trampoline */ + +/* Use atexit for static constructors/destructors, instead of defining + our own exit function. */ +#define HAVE_ATEXIT + +/* Generate calls to memcpy, etc., not bcopy, etc. */ +#define TARGET_MEM_FUNCTIONS + +#define OBJECT_FORMAT_ELF + +#define TARGET_DEFAULT MASK_ABICALLS +#define ABICALLS_ASM_OP ".option pic2" + +#define MACHINE_TYPE "SNI running SINIX 5.42" + +#define MIPS_DEFAULT_GVALUE 0 + +#define NM_FLAGS "-p" + +/* wir haben ein Problem, wenn in einem Assembler-File keine .text-section + erzeugt wird. Dann landen diese Pseudo-Labels in irgendeiner anderen + section, z.B. .reginfo. Das macht den ld sehr ungluecklich. */ + +#define ASM_IDENTIFY_GCC(mw_stream) \ + fprintf(mw_stream, "\t.ident \"gcc2_compiled.\"\n"); + +#define ASM_IDENTIFY_LANGUAGE(STREAM) + +#define ASM_LONG ".word\t" +#define ASM_GLOBAL ".rdata\n\t\t.globl\t" + +#include "mips/mips.h" + +/* We do not want to run mips-tfile! */ +#undef ASM_FINAL_SPEC + +#undef OBJECT_FORMAT_COFF + +/* We don't support debugging info for now. */ +#undef DBX_DEBUGGING_INFO +#undef SDB_DEBUGGING_INFO +#undef MIPS_DEBUGGING_INFO +#undef PREFERRED_DEBUGGING_TYPE + +#define DWARF2_UNWIND_INFO 0 diff --git a/contrib/gcc/config/mips/svr3-4.h b/contrib/gcc/config/mips/svr3-4.h new file mode 100644 index 000000000000..18303ac24ec1 --- /dev/null +++ b/contrib/gcc/config/mips/svr3-4.h @@ -0,0 +1,63 @@ +/* Definitions of target machine for GNU compiler. + MIPS RISC-OS System V version. + Copyright (C) 1991 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 MIPS_SYSV + +#define CPP_PREDEFINES "\ +-Dmips -Dunix -Dhost_mips -DMIPSEB -DR3000 -DSYSTYPE_SYSV \ +-D_mips -D_unix -D_host_mips -D_MIPSEB -D_R3000 -D_SYSTYPE_SYSV \ +-Asystem(unix) -Asystem(svr3) -Acpu(mips) -Amachine(mips)" + +#define STANDARD_INCLUDE_DIR "/sysv/usr/include" + +#define LINK_SPEC "\ +%{G*} %{EB} %{EL} %{mips1} %{mips2} %{mips3} \ +%{bestGnum} %{shared} %{non_shared} \ +-systype /sysv/" + +#define LIB_SPEC "%{p:-lprof1} %{pg:-lprof1} -lc crtn.o%s" + +#define STARTFILE_SPEC "%{pg:gcrt0.o%s}%{!pg:%{p:mcrt0.o%s}%{!p:crt1.o%s}}" + +#define MACHINE_TYPE "RISC-OS System V Mips" + +/* Override defaults for finding the MIPS tools. */ +#define MD_STARTFILE_PREFIX "/sysv/usr/lib/cmplrs/cc/" +#define MD_EXEC_PREFIX "/sysv/usr/lib/cmplrs/cc/" + +/* Mips System V doesn't have a getpagesize() function needed by the + trampoline code, so use the POSIX sysconf function to get it. + This is only done when compiling the trampoline code. */ + +#ifdef L_trampoline +#include <sys/param.h> +#include <unistd.h> + +#ifdef _SC_PAGE_SIZE +#define getpagesize() sysconf(_SC_PAGE_SIZE) + +#else /* older rev of OS */ +#define getpagesize() (NBPC) +#endif /* !_SC_PAGE_SIZE */ +#endif /* L_trampoline */ + +/* Generate calls to memcpy, etc., not bcopy, etc. */ +#define TARGET_MEM_FUNCTIONS diff --git a/contrib/gcc/config/mips/svr3-5.h b/contrib/gcc/config/mips/svr3-5.h new file mode 100644 index 000000000000..495b389989a8 --- /dev/null +++ b/contrib/gcc/config/mips/svr3-5.h @@ -0,0 +1,89 @@ +/* Definitions of target machine for GNU compiler. + MIPS RISC-OS 5.0 System V version. + Copyright (C) 1991, 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. */ + +#define MIPS_SYSV + +#define CPP_PREDEFINES "\ +-Dmips -Dunix -Dhost_mips -DMIPSEB -DR3000 -DSYSTYPE_SYSV \ +-D_mips -D_unix -D_host_mips -D_MIPSEB -D_R3000 -D_SYSTYPE_SYSV \ +-Asystem(unix) -Asystem(svr3) -Acpu(mips) -Amachine(mips)" + +#define STANDARD_INCLUDE_DIR "/sysv/usr/include" + +#define LINK_SPEC "\ +%{G*} %{EB} %{EL} %{mips1} %{mips2} %{mips3} \ +%{bestGnum} %{shared} %{non_shared} \ +%{call_shared} %{no_archive} %{exact_version} \ +%{!shared: %{!non_shared: %{!call_shared: -non_shared}}} \ +-systype /sysv/ " + +#define LIB_SPEC "%{p:-lprof1} %{pg:-lprof1} -lc crtn.o%s" + +#define STARTFILE_SPEC "%{pg:gcrt0.o%s}%{!pg:%{p:mcrt0.o%s}%{!p:crt1.o%s}}" + +#define MACHINE_TYPE "RISC-OS System V Mips" + +/* Override defaults for finding the MIPS tools. */ +#define MD_STARTFILE_PREFIX "/sysv/usr/lib/cmplrs/cc/" +#define MD_EXEC_PREFIX "/sysv/usr/lib/cmplrs/cc/" + +/* Mips System V doesn't have a getpagesize() function needed by the + trampoline code, so use the POSIX sysconf function to get it. + This is only done when compiling the trampoline code. */ + +#ifdef L_trampoline +#include <sys/param.h> +#include <unistd.h> + +/* In at least 5.0 and 5.01, there is no _SC_PAGE_SIZE macro, only a + _SC_PAGESIZE macro. */ +#ifdef _SC_PAGESIZE +#define _SC_PAGE_SIZE _SC_PAGESIZE +#endif + +#ifdef _SC_PAGE_SIZE +#define getpagesize() sysconf(_SC_PAGE_SIZE) + +#else /* older rev of OS */ +#define getpagesize() (NBPC) +#endif /* !_SC_PAGE_SIZE */ +#endif /* L_trampoline */ + +/* Generate calls to memcpy, etc., not bcopy, etc. */ +#define TARGET_MEM_FUNCTIONS + +#include "mips/mips.h" + +/* Some assemblers have a bug that causes backslash escaped chars in .ascii + to be misassembled, so we just completely avoid it. */ +#undef ASM_OUTPUT_ASCII +#define ASM_OUTPUT_ASCII(FILE,PTR,LEN) \ +do { \ + unsigned char *s; \ + int i; \ + for (i = 0, s = (unsigned char *)(PTR); i < (LEN); s++, i++) \ + { \ + if ((i % 8) == 0) \ + fputs ("\n\t.byte\t", (FILE)); \ + fprintf ((FILE), "%s0x%x", (i%8?",":""), (unsigned)*s); \ + } \ + fputs ("\n", (FILE)); \ +} while (0) diff --git a/contrib/gcc/config/mips/svr4-4.h b/contrib/gcc/config/mips/svr4-4.h new file mode 100644 index 000000000000..d1ba64de0290 --- /dev/null +++ b/contrib/gcc/config/mips/svr4-4.h @@ -0,0 +1,61 @@ +/* Definitions of target machine for GNU compiler. + MIPS RISC-OS System V.4 version. + Copyright (C) 1992, 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. */ + +#define MIPS_SVR4 + +#define CPP_PREDEFINES "\ +-Dmips -Dunix -Dhost_mips -DMIPSEB -DR3000 -DSYSTYPE_SVR4 \ +-D_mips -D_unix -D_host_mips -D_MIPSEB -D_R3000 -D_SYSTYPE_SVR4 \ +-Asystem(unix) -Asystem(svr4) -Acpu(mips) -Amachine(mips)" + +#define STANDARD_INCLUDE_DIR "/svr4/usr/include" + +#define LINK_SPEC "\ +%{G*} %{EB} %{EL} %{mips1} %{mips2} %{mips3} \ +%{bestGnum} %{shared} %{non_shared} \ +-systype /svr4/" + +#define LIB_SPEC "%{p:-lprof1} %{pg:-lprof1} -lc crtn.o%s" + +#define STARTFILE_SPEC "%{pg:gcrt0.o%s}%{!pg:%{p:mcrt0.o%s}%{!p:crt1.o%s}}" + +#define MACHINE_TYPE "RISC-OS System V.4 Mips" + +/* Override defaults for finding the MIPS tools. */ +#define MD_STARTFILE_PREFIX "/svr4/usr/lib/cmplrs/cc/" +#define MD_EXEC_PREFIX "/svr4/usr/lib/cmplrs/cc/" + +/* Mips System V.4 doesn't have a getpagesize() function needed by the + trampoline code, so use the POSIX sysconf function to get it. + This is only done when compiling the trampoline code. */ + +#ifdef L_trampoline +#include <unistd.h> + +#define getpagesize() sysconf(_SC_PAGE_SIZE) +#endif /* L_trampoline */ + +/* Use atexit for static constructors/destructors, instead of defining + our own exit function. */ +#define HAVE_ATEXIT + +/* Generate calls to memcpy, etc., not bcopy, etc. */ +#define TARGET_MEM_FUNCTIONS diff --git a/contrib/gcc/config/mips/svr4-5.h b/contrib/gcc/config/mips/svr4-5.h new file mode 100644 index 000000000000..799e1cdf1e75 --- /dev/null +++ b/contrib/gcc/config/mips/svr4-5.h @@ -0,0 +1,92 @@ +/* Definitions of target machine for GNU compiler. + MIPS RISC-OS 5.0 System V.4 version. + Copyright (C) 1992 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 MIPS_SVR4 + +#define CPP_PREDEFINES \ +"-Dmips -Dunix -Dhost_mips -DMIPSEB -DR3000 -DSYSTYPE_SVR4 \ +-D_mips -D_unix -D_host_mips -D_MIPSEB -D_R3000 -D_SYSTYPE_SVR4 \ +-D_MIPS_SZINT=32 -D_MIPS_SZLONG=32 -D_MIPS_SZPTR=32 \ +-Asystem(unix) -Asystem(svr4) -Acpu(mips) -Amachine(mips)" + +#define STANDARD_INCLUDE_DIR "/svr4/usr/include" + +#define LINK_SPEC "\ +%{G*} %{EB} %{EL} %{mips1} %{mips2} %{mips3} \ +%{bestGnum} %{shared} %{non_shared} \ +%{call_shared} %{no_archive} %{exact_version} \ +%{!shared: %{!non_shared: %{!call_shared: -non_shared}}} \ +-systype /svr4/ " + +#define LIB_SPEC "%{p:-lprof1} %{pg:-lprof1} -lc crtn.o%s" + +#define STARTFILE_SPEC "%{pg:gcrt0.o%s}%{!pg:%{p:mcrt0.o%s}%{!p:crt1.o%s}}\ + %{ansi:/svr4/usr/ccs/lib/values-Xc.o%s} \ + %{!ansi: \ + %{traditional:/svr4/usr/ccs/lib/values-Xt.o%s} \ + %{!traditional:/svr4/usr/ccs/lib/values-Xa.o%s}}" + +#define MACHINE_TYPE "RISC-OS System V.4 Mips" + +/* Override defaults for finding the MIPS tools. */ +#define MD_STARTFILE_PREFIX "/svr4/usr/lib/cmplrs/cc/" +#define MD_EXEC_PREFIX "/svr4/usr/lib/cmplrs/cc/" + +/* Mips System V.4 doesn't have a getpagesize() function needed by the + trampoline code, so use the POSIX sysconf function to get it. + This is only done when compiling the trampoline code. */ + +#ifdef L_trampoline +#include <unistd.h> + +/* In at least 5.0 and 5.01, there is no _SC_PAGE_SIZE macro, only a + _SC_PAGESIZE macro. */ +#ifdef _SC_PAGESIZE +#define _SC_PAGE_SIZE _SC_PAGESIZE +#endif + +#define getpagesize() sysconf(_SC_PAGE_SIZE) +#endif /* L_trampoline */ + +/* Use atexit for static constructors/destructors, instead of defining + our own exit function. */ +#define HAVE_ATEXIT + +/* Generate calls to memcpy, etc., not bcopy, etc. */ +#define TARGET_MEM_FUNCTIONS + +#include "mips/mips.h" + +/* Some assemblers have a bug that causes backslash escaped chars in .ascii + to be misassembled, so we just completely avoid it. */ +#undef ASM_OUTPUT_ASCII +#define ASM_OUTPUT_ASCII(FILE,PTR,LEN) \ +do { \ + unsigned char *s; \ + int i; \ + for (i = 0, s = (unsigned char *)(PTR); i < (LEN); s++, i++) \ + { \ + if ((i % 8) == 0) \ + fputs ("\n\t.byte\t", (FILE)); \ + fprintf ((FILE), "%s0x%x", (i%8?",":""), (unsigned)*s); \ + } \ + fputs ("\n", (FILE)); \ +} while (0) diff --git a/contrib/gcc/config/mips/svr4-t.h b/contrib/gcc/config/mips/svr4-t.h new file mode 100644 index 000000000000..b457aa5f07dc --- /dev/null +++ b/contrib/gcc/config/mips/svr4-t.h @@ -0,0 +1,29 @@ +/* Definitions of target machine for GNU compiler. Tandem S2 w/ NonStop UX. */ + +/* Use the default value for this. */ +#undef STANDARD_INCLUDE_DIR + +#undef MACHINE_TYPE +#define MACHINE_TYPE "TANDEM System V.4 Mips" + +/* Use the default values in mips.h. */ +#undef MD_STARTFILE_PREFIX +#undef MD_EXEC_PREFIX +#define MD_STARTFILE_PREFIX "/usr/lib/cmplrs/cc/" +#define MD_EXEC_PREFIX "/usr/lib/cmplrs/cc/" + +/* These are the same as the ones in svr4-5.h, except that references to + /svr4/ have been removed. */ +#undef STARTFILE_SPEC +#define STARTFILE_SPEC "%{pg:gcrt0.o%s}%{!pg:%{p:mcrt0.o%s}%{!p:crt1.o%s}}\ + %{ansi:/usr/lib/values-Xc.o%s} \ + %{!ansi: \ + %{traditional:/usr/lib/values-Xt.o%s} \ + %{!traditional:/usr/lib/values-Xa.o%s}}" + +#undef LINK_SPEC +#define LINK_SPEC "\ +%{G*} %{EB} %{EL} %{mips1} %{mips2} %{mips3} \ +%{bestGnum} %{shared} %{non_shared} \ +%{call_shared} %{no_archive} %{exact_version} \ +%{!shared: %{!non_shared: %{!call_shared: -non_shared}}}" diff --git a/contrib/gcc/config/mips/t-bsd b/contrib/gcc/config/mips/t-bsd new file mode 100644 index 000000000000..f9c6fc840049 --- /dev/null +++ b/contrib/gcc/config/mips/t-bsd @@ -0,0 +1,12 @@ +# Exactly the same as t-mips, except we must define SYSTEM_HEADER_DIR +# to point to the bsd43 include files. +SYSTEM_HEADER_DIR = /bsd43/usr/include + +# We have a premade insn-attrtab.c to save the hour it takes to run genattrtab. +# PREMADE_ATTRTAB = $(srcdir)/config/mips/mips-at.c +# PREMADE_ATTRTAB_MD = $(srcdir)/config/mips/mips-at.md + +# Suppress building libgcc1.a, since the MIPS compiler port is complete +# and does not need anything from libgcc1.a. +LIBGCC1 = +CROSS_LIBGCC1 = diff --git a/contrib/gcc/config/mips/t-bsd-gas b/contrib/gcc/config/mips/t-bsd-gas new file mode 100644 index 000000000000..bfa5a7ec38b3 --- /dev/null +++ b/contrib/gcc/config/mips/t-bsd-gas @@ -0,0 +1,8 @@ +# Exactly the same as t-mips-gas, except we must define SYSTEM_HEADER_DIR +# to point to the bsd43 include files. +SYSTEM_HEADER_DIR = /bsd43/usr/include + +# Suppress building libgcc1.a, since the MIPS compiler port is complete +# and does not need anything from libgcc1.a. +LIBGCC1 = +CROSS_LIBGCC1 = diff --git a/contrib/gcc/config/mips/t-cross64 b/contrib/gcc/config/mips/t-cross64 new file mode 100644 index 000000000000..bfca95072f71 --- /dev/null +++ b/contrib/gcc/config/mips/t-cross64 @@ -0,0 +1,17 @@ +SYSTEM_HEADER_DIR = /usr/cross64/usr/include + +AR = /usr/cross64/usr/bin/ar + +# The rest of the file is identical to t-iris6. + +# Suppress building libgcc1.a, since the MIPS compiler port is complete +# and does not need anything from libgcc1.a. +LIBGCC1 = +CROSS_LIBGCC1 = + +MULTILIB_OPTIONS=mips1/mips2/mips3/mips4 +MULTILIB_DIRNAMES= +MULTILIB_MATCHES= + +LIBGCC = stmp-multilib +INSTALL_LIBGCC = install-multilib diff --git a/contrib/gcc/config/mips/t-ecoff b/contrib/gcc/config/mips/t-ecoff new file mode 100644 index 000000000000..8de03ffd758a --- /dev/null +++ b/contrib/gcc/config/mips/t-ecoff @@ -0,0 +1,92 @@ +CONFIG2_H = $(srcdir)/config/mips/ecoff.h + +# We have a premade insn-attrtab.c to save the hour it takes to run genattrtab. +# PREMADE_ATTRTAB = $(srcdir)/config/mips/mips-at.c +# PREMADE_ATTRTAB_MD = $(srcdir)/config/mips/mips-at.md + +# Suppress building libgcc1.a, since the MIPS compiler port is complete +# and does not need anything from libgcc1.a. +LIBGCC1 = + +# When building a cross compiler, put the mips16 support functions in +# libgcc1.a. +CROSS_LIBGCC1 = libgcc1-asm.a +LIB1ASMSRC = mips/mips16.S +LIB1ASMFUNCS = _m16addsf3 _m16subsf3 _m16mulsf3 _m16divsf3 \ + _m16eqsf2 _m16nesf2 _m16gtsf2 _m16gesf2 _m16lesf2 _m16ltsf2 \ + _m16fltsisf _m16fixsfsi \ + _m16adddf3 _m16subdf3 _m16muldf3 _m16divdf3 \ + _m16extsfdf2 _m16trdfsf2 \ + _m16eqdf2 _m16nedf2 _m16gtdf2 _m16gedf2 _m16ledf2 _m16ltdf2 \ + _m16fltsidf _m16fixdfsi \ + _m16retsf _m16retdf \ + _m16stub1 _m16stub2 _m16stub5 _m16stub6 _m16stub9 _m16stub10 \ + _m16stubsf0 _m16stubsf1 _m16stubsf2 _m16stubsf5 _m16stubsf6 \ + _m16stubsf9 _m16stubsf10 \ + _m16stubdf0 _m16stubdf1 _m16stubdf2 _m16stubdf5 _m16stubdf6 \ + _m16stubdf9 _m16stubdf10 + +# We must build libgcc2.a with -G 0, in case the user wants to link +# without the $gp register. +TARGET_LIBGCC2_CFLAGS = -G 0 + +# fp-bit and dp-bit are really part of libgcc1, but this will cause +# them to be built correctly, so... [taken from t-sparclite] +LIB2FUNCS_EXTRA = fp-bit.c dp-bit.c + +dp-bit.c: $(srcdir)/config/fp-bit.c + echo '#ifdef __MIPSEL__' > dp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' >> dp-bit.c + echo '#endif' >> dp-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 '#ifdef __MIPSEL__' >> fp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' >> fp-bit.c + echo '#endif' >> fp-bit.c + echo '#define US_SOFTWARE_GOFAST' >> fp-bit.c + cat $(srcdir)/config/fp-bit.c >> fp-bit.c + +# Build the libraries for both hard and soft floating point + +MULTILIB_OPTIONS = msoft-float/msingle-float EL/EB mips1/mips3 +MULTILIB_DIRNAMES = soft-float single el eb mips1 mips3 +MULTILIB_MATCHES = msingle-float=m4650 + +LIBGCC = stmp-multilib +INSTALL_LIBGCC = install-multilib + +# Add additional dependencies to recompile selected modules whenever the +# tm.h file changes. The files compiled are: +# +# gcc.c (*_SPEC changes) +# toplev.c (new switches + assembly output changes) +# sdbout.c (debug format changes) +# dbxout.c (debug format changes) +# dwarfout.c (debug format changes) +# final.c (assembly output changes) +# varasm.c (assembly output changes) +# cse.c (cost functions) +# insn-output.c (possible ifdef changes in tm.h) +# regclass.c (fixed/call used register changes) +# cccp.c (new preprocessor macros, -v version #) +# explow.c (GO_IF_LEGITIMATE_ADDRESS) +# recog.c (GO_IF_LEGITIMATE_ADDRESS) +# reload.c (GO_IF_LEGITIMATE_ADDRESS) + +gcc.o: $(CONFIG2_H) +toplev.o: $(CONFIG2_H) +sdbout.o: $(CONFIG2_H) +dbxout.o: $(CONFIG2_H) +dwarfout.o: $(CONFIG2_H) +final.o: $(CONFIG2_H) +varasm.o: $(CONFIG2_H) +cse.o: $(CONFIG2_H) +insn-output.o: $(CONFIG2_H) +regclass.o: $(CONFIG2_H) +cccp.o: $(CONFIG2_H) +explow.o: $(CONFIG2_H) +recog.o: $(CONFIG2_H) +reload.o: $(CONFIG2_H) diff --git a/contrib/gcc/config/mips/t-elf b/contrib/gcc/config/mips/t-elf new file mode 100644 index 000000000000..dd01b7fd4a71 --- /dev/null +++ b/contrib/gcc/config/mips/t-elf @@ -0,0 +1,96 @@ +CONFIG2_H = $(srcdir)/config/mips/ecoff.h + +# We have a premade insn-attrtab.c to save the hour it takes to run genattrtab. +# PREMADE_ATTRTAB = $(srcdir)/config/mips/mips-at.c +# PREMADE_ATTRTAB_MD = $(srcdir)/config/mips/mips-at.md + +# Suppress building libgcc1.a, since the MIPS compiler port is complete +# and does not need anything from libgcc1.a. +LIBGCC1 = + +EXTRA_MULTILIB_PARTS = crtbegin.o crtend.o +# Don't let CTOR_LIST end up in sdata section. +CRTSTUFF_T_CFLAGS = -G 0 + +# When building a cross compiler, put the mips16 support functions in +# libgcc1.a. +CROSS_LIBGCC1 = libgcc1-asm.a +LIB1ASMSRC = mips/mips16.S +LIB1ASMFUNCS = _m16addsf3 _m16subsf3 _m16mulsf3 _m16divsf3 \ + _m16eqsf2 _m16nesf2 _m16gtsf2 _m16gesf2 _m16lesf2 _m16ltsf2 \ + _m16fltsisf _m16fixsfsi \ + _m16adddf3 _m16subdf3 _m16muldf3 _m16divdf3 \ + _m16extsfdf2 _m16trdfsf2 \ + _m16eqdf2 _m16nedf2 _m16gtdf2 _m16gedf2 _m16ledf2 _m16ltdf2 \ + _m16fltsidf _m16fixdfsi \ + _m16retsf _m16retdf \ + _m16stub1 _m16stub2 _m16stub5 _m16stub6 _m16stub9 _m16stub10 \ + _m16stubsf0 _m16stubsf1 _m16stubsf2 _m16stubsf5 _m16stubsf6 \ + _m16stubsf9 _m16stubsf10 \ + _m16stubdf0 _m16stubdf1 _m16stubdf2 _m16stubdf5 _m16stubdf6 \ + _m16stubdf9 _m16stubdf10 + +# We must build libgcc2.a with -G 0, in case the user wants to link +# without the $gp register. +TARGET_LIBGCC2_CFLAGS = -G 0 + +# fp-bit and dp-bit are really part of libgcc1, but this will cause +# them to be built correctly, so... [taken from t-sparclite] +LIB2FUNCS_EXTRA = fp-bit.c dp-bit.c + +dp-bit.c: $(srcdir)/config/fp-bit.c + echo '#ifdef __MIPSEL__' > dp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' >> dp-bit.c + echo '#endif' >> dp-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 '#ifdef __MIPSEL__' >> fp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' >> fp-bit.c + echo '#endif' >> fp-bit.c + echo '#define US_SOFTWARE_GOFAST' >> fp-bit.c + cat $(srcdir)/config/fp-bit.c >> fp-bit.c + +# Build the libraries for both hard and soft floating point + +MULTILIB_OPTIONS = msoft-float/msingle-float EL/EB mips1/mips3 +MULTILIB_DIRNAMES = soft-float single el eb mips1 mips3 +MULTILIB_MATCHES = msingle-float=m4650 + +LIBGCC = stmp-multilib +INSTALL_LIBGCC = install-multilib + +# Add additional dependencies to recompile selected modules whenever the +# tm.h file changes. The files compiled are: +# +# gcc.c (*_SPEC changes) +# toplev.c (new switches + assembly output changes) +# sdbout.c (debug format changes) +# dbxout.c (debug format changes) +# dwarfout.c (debug format changes) +# final.c (assembly output changes) +# varasm.c (assembly output changes) +# cse.c (cost functions) +# insn-output.c (possible ifdef changes in tm.h) +# regclass.c (fixed/call used register changes) +# cccp.c (new preprocessor macros, -v version #) +# explow.c (GO_IF_LEGITIMATE_ADDRESS) +# recog.c (GO_IF_LEGITIMATE_ADDRESS) +# reload.c (GO_IF_LEGITIMATE_ADDRESS) + +gcc.o: $(CONFIG2_H) +toplev.o: $(CONFIG2_H) +sdbout.o: $(CONFIG2_H) +dbxout.o: $(CONFIG2_H) +dwarfout.o: $(CONFIG2_H) +final.o: $(CONFIG2_H) +varasm.o: $(CONFIG2_H) +cse.o: $(CONFIG2_H) +insn-output.o: $(CONFIG2_H) +regclass.o: $(CONFIG2_H) +cccp.o: $(CONFIG2_H) +explow.o: $(CONFIG2_H) +recog.o: $(CONFIG2_H) +reload.o: $(CONFIG2_H) diff --git a/contrib/gcc/config/mips/t-iris6 b/contrib/gcc/config/mips/t-iris6 new file mode 100644 index 000000000000..85a63f06e2e7 --- /dev/null +++ b/contrib/gcc/config/mips/t-iris6 @@ -0,0 +1,20 @@ +# Suppress building libgcc1.a, since the MIPS compiler port is complete +# and does not need anything from libgcc1.a. +LIBGCC1 = +CROSS_LIBGCC1 = + +# ??? If no mabi=X option given, but a mipsX option is, then should deal +# with that. +# ??? mabi=32 is deliberately left off the list because it doesn't work yet. +MULTILIB_OPTIONS=mabi=n32/mabi=64 +MULTILIB_DIRNAMES= +MULTILIB_MATCHES= + +LIBGCC = stmp-multilib +INSTALL_LIBGCC = install-multilib + +# For svr4 we build crtbegin.o and crtend.o which serve to add begin and +# end labels to the .ctors and .dtors section when we link using gcc. + +EXTRA_MULTILIB_PARTS=crtbegin.o crtend.o +CRTSTUFF_T_CFLAGS=-g1 diff --git a/contrib/gcc/config/mips/t-mips b/contrib/gcc/config/mips/t-mips new file mode 100644 index 000000000000..e57a55af5de9 --- /dev/null +++ b/contrib/gcc/config/mips/t-mips @@ -0,0 +1,8 @@ +# We have a premade insn-attrtab.c to save the hour it takes to run genattrtab. +# PREMADE_ATTRTAB = $(srcdir)/config/mips/mips-at.c +# PREMADE_ATTRTAB_MD = $(srcdir)/config/mips/mips-at.md + +# Suppress building libgcc1.a, since the MIPS compiler port is complete +# and does not need anything from libgcc1.a. +LIBGCC1 = +CROSS_LIBGCC1 = diff --git a/contrib/gcc/config/mips/t-mips-gas b/contrib/gcc/config/mips/t-mips-gas new file mode 100644 index 000000000000..94f1c442b68a --- /dev/null +++ b/contrib/gcc/config/mips/t-mips-gas @@ -0,0 +1,4 @@ +# Suppress building libgcc1.a, since the MIPS compiler port is complete +# and does not need anything from libgcc1.a. +LIBGCC1 = +CROSS_LIBGCC1 = diff --git a/contrib/gcc/config/mips/t-osfrose b/contrib/gcc/config/mips/t-osfrose new file mode 100644 index 000000000000..e57a55af5de9 --- /dev/null +++ b/contrib/gcc/config/mips/t-osfrose @@ -0,0 +1,8 @@ +# We have a premade insn-attrtab.c to save the hour it takes to run genattrtab. +# PREMADE_ATTRTAB = $(srcdir)/config/mips/mips-at.c +# PREMADE_ATTRTAB_MD = $(srcdir)/config/mips/mips-at.md + +# Suppress building libgcc1.a, since the MIPS compiler port is complete +# and does not need anything from libgcc1.a. +LIBGCC1 = +CROSS_LIBGCC1 = diff --git a/contrib/gcc/config/mips/t-r3900 b/contrib/gcc/config/mips/t-r3900 new file mode 100644 index 000000000000..055143ff05c7 --- /dev/null +++ b/contrib/gcc/config/mips/t-r3900 @@ -0,0 +1,95 @@ +CONFIG2_H = $(srcdir)/config/mips/ecoff.h + +# We have a premade insn-attrtab.c to save the hour it takes to run genattrtab. +# PREMADE_ATTRTAB = $(srcdir)/config/mips/mips-at.c +# PREMADE_ATTRTAB_MD = $(srcdir)/config/mips/mips-at.md + +# Suppress building libgcc1.a, since the MIPS compiler port is complete +# and does not need anything from libgcc1.a. +LIBGCC1 = + +# When building a cross compiler, put the mips16 support functions in +# libgcc1.a. +CROSS_LIBGCC1 = libgcc1-asm.a +LIB1ASMSRC = mips/mips16.S +LIB1ASMFUNCS = _m16addsf3 _m16subsf3 _m16mulsf3 _m16divsf3 \ + _m16eqsf2 _m16nesf2 _m16gtsf2 _m16gesf2 _m16lesf2 _m16ltsf2 \ + _m16fltsisf _m16fixsfsi \ + _m16adddf3 _m16subdf3 _m16muldf3 _m16divdf3 \ + _m16extsfdf2 _m16trdfsf2 \ + _m16eqdf2 _m16nedf2 _m16gtdf2 _m16gedf2 _m16ledf2 _m16ltdf2 \ + _m16fltsidf _m16fixdfsi \ + _m16retsf _m16retdf \ + _m16stub1 _m16stub2 _m16stub5 _m16stub6 _m16stub9 _m16stub10 \ + _m16stubsf0 _m16stubsf1 _m16stubsf2 _m16stubsf5 _m16stubsf6 \ + _m16stubsf9 _m16stubsf10 \ + _m16stubdf0 _m16stubdf1 _m16stubdf2 _m16stubdf5 _m16stubdf6 \ + _m16stubdf9 _m16stubdf10 + +# We must build libgcc2.a with -G 0, in case the user wants to link +# without the $gp register. +TARGET_LIBGCC2_CFLAGS = -G 0 + +# fp-bit and dp-bit are really part of libgcc1, but this will cause +# them to be built correctly, so... [taken from t-sparclite] +LIB2FUNCS_EXTRA = fp-bit.c dp-bit.c + +dp-bit.c: $(srcdir)/config/fp-bit.c + echo '#ifdef __MIPSEL__' > dp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' >> dp-bit.c + echo '#endif' >> dp-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 '#ifdef __MIPSEL__' >> fp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' >> fp-bit.c + echo '#endif' >> fp-bit.c + echo '#define US_SOFTWARE_GOFAST' >> fp-bit.c + cat $(srcdir)/config/fp-bit.c >> fp-bit.c + +EXTRA_MULTILIB_PARTS = crtbegin.o crtend.o +# Don't let CTOR_LIST end up in sdata section. +CRTSTUFF_T_CFLAGS = -G 0 + +# Build the libraries for both hard and soft floating point + +MULTILIB_OPTIONS = msoft-float/msingle-float EL/EB +MULTILIB_DIRNAMES = soft-float single el eb + +LIBGCC = stmp-multilib +INSTALL_LIBGCC = install-multilib + +# Add additional dependencies to recompile selected modules whenever the +# tm.h file changes. The files compiled are: +# +# gcc.c (*_SPEC changes) +# toplev.c (new switches + assembly output changes) +# sdbout.c (debug format changes) +# dbxout.c (debug format changes) +# dwarfout.c (debug format changes) +# final.c (assembly output changes) +# varasm.c (assembly output changes) +# cse.c (cost functions) +# insn-output.c (possible ifdef changes in tm.h) +# regclass.c (fixed/call used register changes) +# cccp.c (new preprocessor macros, -v version #) +# explow.c (GO_IF_LEGITIMATE_ADDRESS) +# recog.c (GO_IF_LEGITIMATE_ADDRESS) +# reload.c (GO_IF_LEGITIMATE_ADDRESS) + +gcc.o: $(CONFIG2_H) +toplev.o: $(CONFIG2_H) +sdbout.o: $(CONFIG2_H) +dbxout.o: $(CONFIG2_H) +dwarfout.o: $(CONFIG2_H) +final.o: $(CONFIG2_H) +varasm.o: $(CONFIG2_H) +cse.o: $(CONFIG2_H) +insn-output.o: $(CONFIG2_H) +regclass.o: $(CONFIG2_H) +cccp.o: $(CONFIG2_H) +explow.o: $(CONFIG2_H) +recog.o: $(CONFIG2_H) +reload.o: $(CONFIG2_H) diff --git a/contrib/gcc/config/mips/t-svr3 b/contrib/gcc/config/mips/t-svr3 new file mode 100644 index 000000000000..273c710882ac --- /dev/null +++ b/contrib/gcc/config/mips/t-svr3 @@ -0,0 +1,12 @@ +# Exactly the same as t-mips, except we must define SYSTEM_HEADER_DIR +# to point to the svr3 include files. +SYSTEM_HEADER_DIR = /sysv/usr/include + +# We have a premade insn-attrtab.c to save the hour it takes to run genattrtab. +# PREMADE_ATTRTAB = $(srcdir)/config/mips/mips-at.c +# PREMADE_ATTRTAB_MD = $(srcdir)/config/mips/mips-at.md + +# Suppress building libgcc1.a, since the MIPS compiler port is complete +# and does not need anything from libgcc1.a. +LIBGCC1 = +CROSS_LIBGCC1 = diff --git a/contrib/gcc/config/mips/t-svr3-gas b/contrib/gcc/config/mips/t-svr3-gas new file mode 100644 index 000000000000..99238f25928d --- /dev/null +++ b/contrib/gcc/config/mips/t-svr3-gas @@ -0,0 +1,8 @@ +# Exactly the same as t-mips-gas, except we must define SYSTEM_HEADER_DIR +# to point to the svr3 include files. +SYSTEM_HEADER_DIR = /sysv/usr/include + +# Suppress building libgcc1.a, since the MIPS compiler port is complete +# and does not need anything from libgcc1.a. +LIBGCC1 = +CROSS_LIBGCC1 = diff --git a/contrib/gcc/config/mips/t-svr4 b/contrib/gcc/config/mips/t-svr4 new file mode 100644 index 000000000000..88029b92a552 --- /dev/null +++ b/contrib/gcc/config/mips/t-svr4 @@ -0,0 +1,12 @@ +# Exactly the same as t-mips, except we must define SYSTEM_HEADER_DIR +# to point to the svr4 include files. +SYSTEM_HEADER_DIR = /svr4/usr/include + +# We have a premade insn-attrtab.c to save the hour it takes to run genattrtab. +# PREMADE_ATTRTAB = $(srcdir)/config/mips/mips-at.c +# PREMADE_ATTRTAB_MD = $(srcdir)/config/mips/mips-at.md + +# Suppress building libgcc1.a, since the MIPS compiler port is complete +# and does not need anything from libgcc1.a. +LIBGCC1 = +CROSS_LIBGCC1 = diff --git a/contrib/gcc/config/mips/t-svr4-gas b/contrib/gcc/config/mips/t-svr4-gas new file mode 100644 index 000000000000..845b091ef383 --- /dev/null +++ b/contrib/gcc/config/mips/t-svr4-gas @@ -0,0 +1,8 @@ +# Exactly the same as t-mips-gas, except we must define SYSTEM_HEADER_DIR +# to point to the svr4 include files. +SYSTEM_HEADER_DIR = /svr4/usr/include + +# Suppress building libgcc1.a, since the MIPS compiler port is complete +# and does not need anything from libgcc1.a. +LIBGCC1 = +CROSS_LIBGCC1 = diff --git a/contrib/gcc/config/mips/vxworks.h b/contrib/gcc/config/mips/vxworks.h new file mode 100644 index 000000000000..0856c37343a7 --- /dev/null +++ b/contrib/gcc/config/mips/vxworks.h @@ -0,0 +1,50 @@ +/* Copyright (C) 1999 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. */ + +/* Undefine the following which were defined in elf.h. Thise will cause the mips-vxworks + port to continue to use collect2 for constructors/destructors. This entire file may + be removed when .ctor/.dtor section support is desired. */ + +#undef CTORS_SECTION_ASM_OP +#undef DTORS_SECTION_ASM_OP + +#undef EXTRA_SECTIONS +#define EXTRA_SECTIONS in_sdata, in_rdata + +#undef INVOKE__main +#undef NAME__MAIN +#undef SYMBOL__MAIN + +#undef EXTRA_SECTION_FUNCTIONS +#define EXTRA_SECTION_FUNCTIONS \ + SECTION_FUNCTION_TEMPLATE(sdata_section, in_sdata, SDATA_SECTION_ASM_OP) \ + SECTION_FUNCTION_TEMPLATE(rdata_section, in_rdata, RDATA_SECTION_ASM_OP) + +#undef ASM_OUTPUT_CONSTRUCTOR +#undef ASM_OUTPUT_DESTRUCTOR + +#undef CTOR_LIST_BEGIN +#undef CTOR_LIST_END +#undef DTOR_LIST_BEGIN +#undef DTOR_LIST_END + +#undef STARTFILE_SPEC +#undef ENDFILE_SPEC + +/* End of undefines to turn off .ctor/.dtor section support */ diff --git a/contrib/gcc/config/mips/x-dec-osf1 b/contrib/gcc/config/mips/x-dec-osf1 new file mode 100644 index 000000000000..6e46f0eafd50 --- /dev/null +++ b/contrib/gcc/config/mips/x-dec-osf1 @@ -0,0 +1,17 @@ +# Define CC and OLDCC as the same, so that the tests: +# if [ x"$(OLDCC)" = x"$(CC)" ] ... +# +# will succeed (if OLDCC != CC, it is assumed that GCC is +# being used in secondary stage builds). We need to pass +# the -Wf,-XNg1500 option so the compiler can compile the +# G++ file cp-parse.c. Otherwise it complains about +# too many case statements. -Olimit is so the user +# can use -O2. Down with fixed size tables! + +CC = $(OLDCC) +OPT = -O1 +OLDCC = cc -Wf,-XNg1500 -Olimit 3000 $(OPT) + +# The bison output files are machine-indep, +# so different flags for a particular machine are not useful. +#BISONFLAGS = -l diff --git a/contrib/gcc/config/mips/x-iris b/contrib/gcc/config/mips/x-iris new file mode 100644 index 000000000000..cf135d172bdc --- /dev/null +++ b/contrib/gcc/config/mips/x-iris @@ -0,0 +1,31 @@ +# Define CC and OLDCC as the same, so that the tests: +# if [ x"$(OLDCC)" = x"$(CC)" ] ... +# +# will succeed (if OLDCC != CC, it is assumed that GCC is +# being used in secondary stage builds). We need to pass +# the -Wf,-XNg1500 option so the compiler can compile the +# G++ file cp-parse.c. Otherwise it complains about +# too many case statements. -Olimit is so the user +# can use -O2. Down with fixed size tables! +# The -cckr is to turn off strict ANSI checking. + +# These definitions are commented out because they cause trouble with +# autoconf. It is believed that they aren't needed anymore. +#CC = $(OLDCC) +#OPT = -O1 +#OLDCC = cc -Wf,-XNh2000,-XNg1500 -Olimit 3000 -cckr $(OPT) + +# The bison output files are machine-indep, +# so different flags for a particular machine are not useful. +#BISONFLAGS = -l + +# -lmld is so we can link collect2 running native. +# -lmalloc is supposed to be faster than the normal malloc +CLIB = -lmld -lmalloc + +# Show we need to use the C version of ALLOCA +ALLOCA = alloca.o + +# Find all of the declarations from the header files +FIXPROTO_DEFINES= -D__EXTENSIONS__ -D_SGI_SOURCE -D_LANGUAGE_C_PLUS_PLUS + diff --git a/contrib/gcc/config/mips/x-iris3 b/contrib/gcc/config/mips/x-iris3 new file mode 100644 index 000000000000..2743ab743d93 --- /dev/null +++ b/contrib/gcc/config/mips/x-iris3 @@ -0,0 +1,30 @@ +# Define CC and OLDCC as the same, so that the tests: +# if [ x"$(OLDCC)" = x"$(CC)" ] ... +# +# will succeed (if OLDCC != CC, it is assumed that GCC is +# being used in secondary stage builds). We need to pass +# the -Wf,-XNg1500 option so the compiler can compile the +# G++ file cp-parse.c. Otherwise it complains about +# too many case statements. -Olimit is so the user +# can use -O2. Down with fixed size tables! + +# In at least one version of Irix, v3.3.2, the compiler does not accept +# the -cckr option, so, lets try without it for all versions of Irix 3.x. +# The -cckr is to turn off strict ANSI checking. + +# These definitions are commented out because they cause trouble with +# autoconf. It is believed that they aren't needed anymore. +#CC = $(OLDCC) +#OPT = -O1 +#OLDCC = cc -Wf,-XNh2000,-XNg1500 -Olimit 3000 $(OPT) + +# The bison output files are machine-indep, +# so different flags for a particular machine are not useful. +#BISONFLAGS = -l + +# -lmld is so we can link collect2 running native. +# -lmalloc is supposed to be faster than the normal malloc +CLIB = -lmld -lmalloc + +# Show we need to use the C version of ALLOCA +ALLOCA = alloca.o diff --git a/contrib/gcc/config/mips/x-iris6 b/contrib/gcc/config/mips/x-iris6 new file mode 100644 index 000000000000..88c41f4dfe45 --- /dev/null +++ b/contrib/gcc/config/mips/x-iris6 @@ -0,0 +1,11 @@ +# We force the use of the O32 ABI for two reasons. +# 1) For consistency, because some versions of Irix 6 default to the O32 ABI +# and some versions default to the N64 ABI. +# 2) To avoid SGI compiler bugs. The v6.x and v7.0 compilers from SGI have +# bugs that cause gcc to be miscompiled when the N32 or N64 ABIs are used. +# The O32 ABI is known to be OK. +CC = $(OLDCC) +OLDCC = cc -32 + +# Find all of the declarations from the header files +FIXPROTO_DEFINES= -D__EXTENSIONS__ -D_SGI_SOURCE -D_LANGUAGE_C_PLUS_PLUS diff --git a/contrib/gcc/config/mips/x-mips b/contrib/gcc/config/mips/x-mips new file mode 100644 index 000000000000..7b407431a810 --- /dev/null +++ b/contrib/gcc/config/mips/x-mips @@ -0,0 +1,20 @@ +# Define CC and OLDCC as the same, so that the tests: +# if [ x"$(OLDCC)" = x"$(CC)" ] ... +# +# will succeed (if OLDCC != CC, it is assumed that GCC is +# being used in secondary stage builds). We need to pass +# the -Wf,-XNg1500 option so the compiler can compile the +# G++ file cp-parse.c. Otherwise it complains about +# too many case statements. The -Olimit is so the user +# can use -O2. Down with fixed size tables! + +CC = $(OLDCC) +OPT = -O1 +OLDCC = cc -Wf,-XNg1500,-XNh2000 -Olimit 3000 $(OPT) + +# The bison output files are machine-indep, +# so different flags for a particular machine are not useful. +#BISONFLAGS = -l + +# This is so we can link collect2 running native. +CLIB = -lmld diff --git a/contrib/gcc/config/mips/x-netbsd b/contrib/gcc/config/mips/x-netbsd new file mode 100644 index 000000000000..49a89f35d1bd --- /dev/null +++ b/contrib/gcc/config/mips/x-netbsd @@ -0,0 +1,17 @@ +# Don't run fixproto +STMP_FIXPROTO = + +# We don't need GCC's own include files. +USER_H = $(srcdir)/ginclude/stdarg.h $(srcdir)/ginclude/varargs.h \ + $(srcdir)/ginclude/va-mips.h $(EXTRA_HEADERS) $(LANG_EXTRA_HEADERS) + +XLIMITS_H = + +# We don't need even the files GCC insists we need. +GENINCLUDES = Makefile.in + +# A lot of stuff needs to go elsewhere. +includedir=$(exec_prefix)/include +infodir=$(exec_prefix)/share/info +tooldir=$(libdir)/cross/$(target) +mandir=$(exec_prefix)/share/man/man1 diff --git a/contrib/gcc/config/mips/x-nws3250v4 b/contrib/gcc/config/mips/x-nws3250v4 new file mode 100644 index 000000000000..4f5cf26eede7 --- /dev/null +++ b/contrib/gcc/config/mips/x-nws3250v4 @@ -0,0 +1,19 @@ +# Define CC and OLDCC as the same, so that the tests: +# if [ x"$(OLDCC)" = x"$(CC)" ] ... +# +# will succeed (if OLDCC != CC, it is assumed that GCC is +# being used in secondary stage builds). We need to pass +# the -Wf,-XNg1500 option so the compiler can compile the +# G++ file cp-parse.c. Otherwise it complains about +# too many case statements. Down with fixed size tables! + +CC = $(OLDCC) +OLDCC = cc -Wf,-XNg1500 +CCLIBFLAGS = -G 0 + +ALLOCA = alloca.o + +# The bison output files are machine-indep, +# so different flags for a particular machine are not useful. +#BISONFLAGS = -l + diff --git a/contrib/gcc/config/mips/x-osfrose b/contrib/gcc/config/mips/x-osfrose new file mode 100644 index 000000000000..825276c25c71 --- /dev/null +++ b/contrib/gcc/config/mips/x-osfrose @@ -0,0 +1,32 @@ +# Define CC and OLDCC as the same, so that the tests: +# if [ x"$(OLDCC)" = x"$(CC)" ] ... +# +# will succeed (if OLDCC != CC, it is assumed that GCC is +# being used in secondary stage builds). + +BUILD = +CC = $(OLDCC) +CLIB = -lld +X_CFLAGS = $(DEB_OPT) $(MSTATS) $(SHLIB) $(X_DEFINES) +X_CFLAGS_NODEBUG = $(NO_DEBUG) $(MSTATS) $(OPT) $(PROFILE) $(SHLIB) $(X_DEFINES) $(XCFLAGS) +CCLIBFLAGS = -O -pic-extern +CPP_ABORT = # -Dabort=fancy_abort +CPPFLAGS = $(CPP_ABORT) $(SYSTEM_INCLUDES) +DEB_OPT = $(OPT) $(DEBUG) $(PROFILE) +DEBUG = +DEBUG_COLLECT = # -DDEBUG +CCLIBFLAGS = -O -DNO_HALF_PIC +GCC_CFLAGS = $(INTERNAL_CFLAGS) $(X_CFLAGS) $(T_CFLAGS) $(CFLAGS) -B./ -DPOSIX -DNO_HALF_PIC +LIBGCC2_CFLAGS = -O2 $(GCC_CFLAGS) -g1 -pic-extern +LDFLAGS = +MSTATS = # -mstats +OLDCC = /usr/ccs/gcc/gcc +OPT = -O2 +PROFILE = +SHLIB = -pic-none +SYSTEM_INCLUDES = # -I${BUILD}/usr/include +X_DEFINES = -Dvfork=fork + +libdir = /usr/ccs +mandir = /usr/ccs/gcc/$(target)/$(version) +bindir = /usr/ccs/gcc/$(target)/$(version) diff --git a/contrib/gcc/config/mips/x-sni-svr4 b/contrib/gcc/config/mips/x-sni-svr4 new file mode 100644 index 000000000000..f986f88162f4 --- /dev/null +++ b/contrib/gcc/config/mips/x-sni-svr4 @@ -0,0 +1,18 @@ +# Define CC and OLDCC as the same, so that the tests: +# if [ x"$(OLDCC)" = x"$(CC)" ] ... +# +# will succeed (if OLDCC != CC, it is assumed that GCC is +# being used in secondary stage builds). +# -Olimit is so the user can use -O2. Down with fixed +# size tables! + +CC = $(OLDCC) +OPT = +OLDCC = cc -Olimit 3000 $(OPT) + +X_CFLAGS = -DNO_SYS_SIGLIST + +# Show we need to use the C version of ALLOCA +# The SVR3 configurations have it, but the SVR4 configurations don't. +# For now, just try using it for all SVR* configurations. +ALLOCA = alloca.o diff --git a/contrib/gcc/config/mips/x-sony b/contrib/gcc/config/mips/x-sony new file mode 100644 index 000000000000..c64593d7d7d3 --- /dev/null +++ b/contrib/gcc/config/mips/x-sony @@ -0,0 +1,18 @@ +# Make internal tables bigger. +OLDCC=cc -Wf,-XNg1500,-XNh2000 -Olimit 3000 $(OPT) + +# Define CC and OLDCC as the same, so that the tests: +# if [ x"$(OLDCC)" = x"$(CC)" ] ... +# +# will succeed (if OLDCC != CC, it is assumed that GCC is +# being used in secondary stage builds). We need to pass +# the -Wf,-XNg1500 option so the compiler can compile the +# G++ file cp-parse.c. Otherwise it complains about +# too many case statements. The -Olimit is so the user +# can use -O2. Down with fixed size tables! + +CC = $(OLDCC) +OPT = -O1 + +# This is so we can link collect2 running native. +CLIB = -lmld diff --git a/contrib/gcc/config/mips/x-sysv b/contrib/gcc/config/mips/x-sysv new file mode 100644 index 000000000000..2c173b159a56 --- /dev/null +++ b/contrib/gcc/config/mips/x-sysv @@ -0,0 +1,26 @@ +# Define CC and OLDCC as the same, so that the tests: +# if [ x"$(OLDCC)" = x"$(CC)" ] ... +# +# will succeed (if OLDCC != CC, it is assumed that GCC is +# being used in secondary stage builds). We need to pass +# the -Wf,-XNg1500 option so the compiler can compile the +# G++ file cp-parse.c. Otherwise it complains about +# too many case statements. -Olimit is so the user +# can use -O2. Down with fixed size tables! + +CC = $(OLDCC) +OPT = -O1 +OLDCC = cc -Wf,-XNg1500,-XNh2000 -Olimit 3000 $(OPT) + +# The bison output files are machine-indep, +# so different flags for a particular machine are not useful. +#BISONFLAGS = -l + +# This enables collect2 to link. +# Some systems use version 2.11 of the compilers. Some use version 3.11. +CLIB= -L/usr/lib/cmplrs/cc2.11 -L/usr/lib/cmplrs/cc3.11 -lmld + +# Show we need to use the C version of ALLOCA +# The SVR3 configurations have it, but the SVR4 configurations don't. +# For now, just try using it for all SVR* configurations. +ALLOCA = alloca.o diff --git a/contrib/gcc/config/mips/xm-iris3.h b/contrib/gcc/config/mips/xm-iris3.h new file mode 100644 index 000000000000..448b7ac80bb3 --- /dev/null +++ b/contrib/gcc/config/mips/xm-iris3.h @@ -0,0 +1,3 @@ +#include "mips/xm-mips.h" + +#define USG diff --git a/contrib/gcc/config/mips/xm-iris4.h b/contrib/gcc/config/mips/xm-iris4.h new file mode 100644 index 000000000000..c01d3f48a789 --- /dev/null +++ b/contrib/gcc/config/mips/xm-iris4.h @@ -0,0 +1,13 @@ +#include "mips/xm-mips.h" + +#define USG + +#if 0 +#ifdef __GNUC__ +/* The normal irix compiler requires alloca.h or alloca doesn't work. + However, the IRIX compiler doesn't allow alloca to be stored in + something like ptr->field = alloca(), so we just use the normal + C alloca. */ +#include <alloca.h> +#endif +#endif diff --git a/contrib/gcc/config/mips/xm-iris5.h b/contrib/gcc/config/mips/xm-iris5.h new file mode 100644 index 000000000000..616055f27f72 --- /dev/null +++ b/contrib/gcc/config/mips/xm-iris5.h @@ -0,0 +1,9 @@ +#include "mips/xm-mips.h" + +/* On SGI IRIX 5.3, inttypes.h clashes with sys/types.h, but the clash + (when compiled with GCC) is a warning, so configure.in thinks it's OK + to use it. Work around this problem. */ + +#ifdef HAVE_INTTYPES_H +#undef HAVE_INTTYPES_H +#endif diff --git a/contrib/gcc/config/mips/xm-iris6.h b/contrib/gcc/config/mips/xm-iris6.h new file mode 100644 index 000000000000..4d429c72edcc --- /dev/null +++ b/contrib/gcc/config/mips/xm-iris6.h @@ -0,0 +1,17 @@ +#define MIPS_OVERRIDE_ALLOCA +#ifndef __GNUC__ +#include <alloca.h> +#else +extern void *alloca (); +#endif + +#include "mips/xm-mips.h" + +#define USG + +#undef HOST_BITS_PER_LONG +#define HOST_BITS_PER_LONG _MIPS_SZLONG + +#ifndef inhibit_libc +#include "string.h" +#endif diff --git a/contrib/gcc/config/mips/xm-mips.h b/contrib/gcc/config/mips/xm-mips.h new file mode 100644 index 000000000000..ad49d7f48378 --- /dev/null +++ b/contrib/gcc/config/mips/xm-mips.h @@ -0,0 +1,76 @@ +/* Configuration for GNU C-compiler for MIPS Rx000 family + Copyright (C) 1989, 1990, 1991, 1993, 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. */ + + +/* #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 + +#if !defined(MIPSEL) && !defined(__MIPSEL__) +#define HOST_WORDS_BIG_ENDIAN +#endif + +/* Enable host-conditionals for MIPS machines. */ +#ifndef MIPS +#define MIPS 1 +#endif + +/* A code distinguishing the floating point format of the host + machine. There are three defined values: IEEE_FLOAT_FORMAT, + VAX_FLOAT_FORMAT, and UNKNOWN_FLOAT_FORMAT. */ + +#define HOST_FLOAT_FORMAT IEEE_FLOAT_FORMAT + +/* 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__ +/* The MIPS compiler gets it wrong, and treats enumerated bitfields + as signed quantities, making it impossible to use an 8-bit enum + for compiling GNU C++. */ +#define ONLY_INT_FIELDS 1 +#endif + +#ifndef MIPS_OVERRIDE_ALLOCA +#ifndef __GNUC__ +#define USE_C_ALLOCA + +#ifdef __STDC__ +extern void * alloca (); +#else +extern char * alloca (); +#endif + +/* for the emacs version of alloca */ +#define STACK_DIRECTION -1 +#endif +#endif /* not MIPS_OVERRIDE_ALLOCA */ diff --git a/contrib/gcc/config/mips/xm-news.h b/contrib/gcc/config/mips/xm-news.h new file mode 100644 index 000000000000..e3eda9db1410 --- /dev/null +++ b/contrib/gcc/config/mips/xm-news.h @@ -0,0 +1,8 @@ +/* This file is for the Sony Mips News running "NewsOS Version 5", + which is really System V. */ +#include "mips/xm-sysv.h" + +/* Sony has a funny name for this symbol. */ +#define sys_siglist _sys_siglist +#undef SYS_SIGLIST_DECLARED +#define SYS_SIGLIST_DECLARED diff --git a/contrib/gcc/config/mips/xm-nws3250v4.h b/contrib/gcc/config/mips/xm-nws3250v4.h new file mode 100644 index 000000000000..34ab631acb48 --- /dev/null +++ b/contrib/gcc/config/mips/xm-nws3250v4.h @@ -0,0 +1,9 @@ +#define USG + +#include "xm-mips.h" + +/* If compiling with mips compiler, we are probably using alloca.c, + so help it work right. */ +#ifndef __GNUC__ +#define USE_C_ALLOCA +#endif diff --git a/contrib/gcc/config/mips/xm-sysv.h b/contrib/gcc/config/mips/xm-sysv.h new file mode 100644 index 000000000000..05a8d6c1154e --- /dev/null +++ b/contrib/gcc/config/mips/xm-sysv.h @@ -0,0 +1,30 @@ +/* Configuration for GNU C-compiler for UMIPS operating system + Copyright (C) 1989, 1990, 1991, 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. */ + +/* + * Notes for compiling gcc on umips (v3.0) + * - change the -g in the CFLAGS to a -g3 or take it out all together. + * - do not define DBX_DEBUGGING_INFO in tm.h, it doesn't exist (unless + * you get one from a bsd system) + */ + +#define USG + +#include "mips/xm-mips.h" diff --git a/contrib/gcc/config/mips/xm-sysv4.h b/contrib/gcc/config/mips/xm-sysv4.h new file mode 100644 index 000000000000..b79664bf1fba --- /dev/null +++ b/contrib/gcc/config/mips/xm-sysv4.h @@ -0,0 +1,7 @@ +#include "mips/xm-sysv.h" + +/* SVR4 provides no sys_siglist, + but does offer the same data under another name. */ +#define sys_siglist _sys_siglist +#undef SYS_SIGLIST_DECLARED +#define SYS_SIGLIST_DECLARED diff --git a/contrib/gcc/config/sparc/aout.h b/contrib/gcc/config/sparc/aout.h new file mode 100644 index 000000000000..478d710f82fd --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/bsd.h b/contrib/gcc/config/sparc/bsd.h new file mode 100644 index 000000000000..761abe2671b8 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/elf.h b/contrib/gcc/config/sparc/elf.h new file mode 100644 index 000000000000..635238f5b1f2 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/gmon-sol2.c b/contrib/gcc/config/sparc/gmon-sol2.c new file mode 100644 index 000000000000..a6abcabcc51f --- /dev/null +++ b/contrib/gcc/config/sparc/gmon-sol2.c @@ -0,0 +1,425 @@ +/*- + * 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. + */ + +#include "config.h" +#include "system.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 PROTO ((int)); +extern void monstartup PROTO ((char *, char *)); +extern void _mcleanup PROTO ((void)); + +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; + const 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] + */ + +static void internal_mcount PROTO((char *, unsigned short *)) ATTRIBUTE_UNUSED; + +/* 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 char etext[]; + already_setup = 1; + monstartup(0, (char *)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/contrib/gcc/config/sparc/hal.h b/contrib/gcc/config/sparc/hal.h new file mode 100644 index 000000000000..0222b819e0eb --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/lb1spc.asm b/contrib/gcc/config/sparc/lb1spc.asm new file mode 100644 index 000000000000..831f33a988fb --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/lb1spl.asm b/contrib/gcc/config/sparc/lb1spl.asm new file mode 100644 index 000000000000..4c8bc30b83d5 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/linux-aout.h b/contrib/gcc/config/sparc/linux-aout.h new file mode 100644 index 000000000000..76d7653eaae6 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/linux.h b/contrib/gcc/config/sparc/linux.h new file mode 100644 index 000000000000..40694908b68d --- /dev/null +++ b/contrib/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 2 +#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/contrib/gcc/config/sparc/linux64.h b/contrib/gcc/config/sparc/linux64.h new file mode 100644 index 000000000000..705b5ca33a04 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/lite.h b/contrib/gcc/config/sparc/lite.h new file mode 100644 index 000000000000..55c232ac7798 --- /dev/null +++ b/contrib/gcc/config/sparc/lite.h @@ -0,0 +1,38 @@ +/* Definitions of target machine for GNU compiler, for SPARClite w/o FPU. + Copyright (C) 1993, 1996 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 app-regs and epilogue options. Do not enable the fpu. */ + +#undef TARGET_DEFAULT +#define TARGET_DEFAULT (MASK_APP_REGS + MASK_EPILOGUE) + +/* US Software GOFAST library support. */ +#include "gofast.h" +#undef INIT_SUBTARGET_OPTABS +#define INIT_SUBTARGET_OPTABS INIT_GOFAST_OPTABS diff --git a/contrib/gcc/config/sparc/litecoff.h b/contrib/gcc/config/sparc/litecoff.h new file mode 100644 index 000000000000..bd89e1b46865 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/lynx-ng.h b/contrib/gcc/config/sparc/lynx-ng.h new file mode 100644 index 000000000000..9e9f82cf10b6 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/lynx.h b/contrib/gcc/config/sparc/lynx.h new file mode 100644 index 000000000000..99b319a0df22 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/netbsd.h b/contrib/gcc/config/sparc/netbsd.h new file mode 100644 index 000000000000..a512f41e1553 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/openbsd.h b/contrib/gcc/config/sparc/openbsd.h new file mode 100644 index 000000000000..19ece975e99e --- /dev/null +++ b/contrib/gcc/config/sparc/openbsd.h @@ -0,0 +1,68 @@ +/* Configuration file for sparc OpenBSD target. + Copyright (C) 1999 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> + +/* Get generic OpenBSD definitions. */ +#define OBSD_OLD_GAS +#include <openbsd.h> + +/* Run-time target specifications. */ +#define CPP_PREDEFINES "-D__unix__ -D__sparc__ -D__OpenBSD__ -Asystem(unix) -Asystem(OpenBSD) -Acpu(sparc) -Amachine(sparc)" + +/* Layout of source language data types */ + +/* This must 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_TYPE_SIZE +#define WCHAR_TYPE_SIZE 32 + +/* Specific options for DBX Output. */ + +/* This is BSD, so it wants DBX format. */ +#define DBX_DEBUGGING_INFO + +/* This is the char to use for continuation */ +#define DBX_CONTIN_CHAR '?' + +/* Stack & calling: aggregate returns. */ + +/* 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 + +/* Assembler format: exception region output. */ + +/* All configurations that don't use elf must be explicit about not using + dwarf unwind information. egcs doesn't try too hard to check internal + configuration files... */ +#define DWARF2_UNWIND_INFO 0 + +/* Default sparc.h does already define ASM_OUTPUT_MI_THUNK */ + diff --git a/contrib/gcc/config/sparc/pbd.h b/contrib/gcc/config/sparc/pbd.h new file mode 100644 index 000000000000..b70fdcb259b5 --- /dev/null +++ b/contrib/gcc/config/sparc/pbd.h @@ -0,0 +1,156 @@ +/* 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) + +/* 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/contrib/gcc/config/sparc/rtems.h b/contrib/gcc/config/sparc/rtems.h new file mode 100644 index 000000000000..1ab0a4216fd9 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/sol2-c1.asm b/contrib/gcc/config/sparc/sol2-c1.asm new file mode 100644 index 000000000000..894a8c34c084 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/sol2-ci.asm b/contrib/gcc/config/sparc/sol2-ci.asm new file mode 100644 index 000000000000..3dc793c0c89c --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/sol2-cn.asm b/contrib/gcc/config/sparc/sol2-cn.asm new file mode 100644 index 000000000000..49e070f34f4c --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/sol2-g1.asm b/contrib/gcc/config/sparc/sol2-g1.asm new file mode 100644 index 000000000000..b9d878856f8d --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/sol2-sld-64.h b/contrib/gcc/config/sparc/sol2-sld-64.h new file mode 100644 index 000000000000..c2518d8def0b --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/sol2-sld.h b/contrib/gcc/config/sparc/sol2-sld.h new file mode 100644 index 000000000000..a82498791b90 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/sol2.h b/contrib/gcc/config/sparc/sol2.h new file mode 100644 index 000000000000..9274f9d9108e --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/sp64-aout.h b/contrib/gcc/config/sparc/sp64-aout.h new file mode 100644 index 000000000000..e3056dfbc54e --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/sp64-elf.h b/contrib/gcc/config/sparc/sp64-elf.h new file mode 100644 index 000000000000..4fd81c55c470 --- /dev/null +++ b/contrib/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, "Generate code for big endian" }, \ +{"little-endian", MASK_LITTLE_ENDIAN, "Generate code for 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/contrib/gcc/config/sparc/sparc.c b/contrib/gcc/config/sparc/sparc.c new file mode 100644 index 000000000000..45862a77bc73 --- /dev/null +++ b/contrib/gcc/config/sparc/sparc.c @@ -0,0 +1,7843 @@ +/* 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 const 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, const char *, + int, int, int)); +static int restore_regs PROTO((FILE *, int, int, const char *, int, int)); +static void build_big_number PROTO((FILE *, int, const 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. */ +const char *sparc_cmodel_string; +/* Parsed value. */ +enum cmodel sparc_cmodel; + +/* Record alignment options as passed by user. */ +const char *sparc_align_loops_string; +const char *sparc_align_jumps_string; +const 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 { + const 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; + const 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 { + const 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_V8 }, + { "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 (); + + if ((profile_flag || profile_block_flag) + && sparc_cmodel != CM_MEDLOW) + { + error ("profiling does not support code models other than medlow"); + } +} + +/* 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 (current_function_uses_only_leaf_regs) + { + 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; + const 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; + const 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; + const 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; + const 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; +{ + const 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; + const 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. */ + +const 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 (current_function_uses_only_leaf_regs) + { + /* 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]; + const 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]; + const 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; + default: + 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 const 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 (current_function_uses_only_leaf_regs) + 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); +} + +/* 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. */ + +void +sparc_function_profiler (file, labelno) + FILE *file; + int labelno; +{ + char buf[32]; + ASM_GENERATE_INTERNAL_LABEL (buf, "LP", labelno); + + if (! TARGET_ARCH64) + fputs ("\tst\t%g2,[%fp-4]\n", file); + + fputs ("\tsethi\t%hi(", file); + assemble_name (file, buf); + fputs ("),%o0\n", file); + + fputs ("\tcall\t", file); + assemble_name (file, MCOUNT_FUNCTION); + putc ('\n', file); + + fputs ("\t or\t%o0,%lo(", file); + assemble_name (file, buf); + fputs ("),%o0\n", file); + + if (! TARGET_ARCH64) + fputs ("\tld\t[%fp-4],%g2\n", file); +} + + +/* 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: + +*/ + +void +sparc_function_block_profiler(file, block_or_label) + FILE *file; + int block_or_label; +{ + char LPBX[32]; + ASM_GENERATE_INTERNAL_LABEL (LPBX, "LPBX", 0); + + if (profile_block_flag == 2) + { + fputs ("\tsethi\t%hi(", file); + assemble_name (file, LPBX); + fputs ("),%o0\n", file); + + fprintf (file, "\tsethi\t%%hi(%d),%%o1\n", block_or_label); + + fputs ("\tor\t%o0,%lo(", file); + assemble_name (file, LPBX); + fputs ("),%o0\n", file); + + fprintf (file, "\tcall\t%s__bb_init_trace_func\n", user_label_prefix); + + fprintf (file, "\t or\t%%o1,%%lo(%d),%%o1\n", block_or_label); + } + else if (profile_block_flag != 0) + { + char LPBY[32]; + ASM_GENERATE_INTERNAL_LABEL (LPBY, "LPBY", block_or_label); + + fputs ("\tsethi\t%hi(", file); + assemble_name (file, LPBX); + fputs ("),%o0\n", file); + + fputs ("\tld\t[%lo(", file); + assemble_name (file, LPBX); + fputs (")+%o0],%o1\n", file); + + fputs ("\ttst\t%o1\n", file); + + if (TARGET_V9) + { + fputs ("\tbne,pn\t%icc,", file); + assemble_name (file, LPBY); + putc ('\n', file); + } + else + { + fputs ("\tbne\t", file); + assemble_name (file, LPBY); + putc ('\n', file); + } + + fputs ("\t or\t%o0,%lo(", file); + assemble_name (file, LPBX); + fputs ("),%o0\n", file); + + fprintf (file, "\tcall\t%s__bb_init_func\n\t nop\n", user_label_prefix); + + ASM_OUTPUT_INTERNAL_LABEL (file, "LPBY", block_or_label); + } +} + +/* 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) + +*/ + +void +sparc_block_profiler(file, blockno) + FILE *file; + int blockno; +{ + char LPBX[32]; + + if (profile_block_flag == 2) + { + ASM_GENERATE_INTERNAL_LABEL (LPBX, "LPBX", 0); + + fprintf (file, "\tsethi\t%%hi(%s__bb),%%g1\n", user_label_prefix); + fprintf (file, "\tsethi\t%%hi(%d),%%g2\n", blockno); + fprintf (file, "\tor\t%%g1,%%lo(%s__bb),%%g1\n", user_label_prefix); + fprintf (file, "\tor\t%%g2,%%lo(%d),%%g2\n", blockno); + + fputs ("\tst\t%g2,[%g1]\n", file); + + fputs ("\tsethi\t%hi(", file); + assemble_name (file, LPBX); + fputs ("),%g2\n", file); + + fputs ("\tor\t%g2,%lo(", file); + assemble_name (file, LPBX); + fputs ("),%g2\n", file); + + fputs ("\tst\t%g2,[%g1+4]\n", file); + fputs ("\tmov\t%o7,%g2\n", file); + + fprintf (file, "\tcall\t%s__bb_trace_func\n\t nop\n", user_label_prefix); + + fputs ("\tmov\t%g2,%o7\n", file); + } + else if (profile_block_flag != 0) + { + ASM_GENERATE_INTERNAL_LABEL (LPBX, "LPBX", 2); + + fputs ("\tsethi\t%hi(", file); + assemble_name (file, LPBX); + fprintf (file, "+%d),%%g1\n", blockno*4); + + fputs ("\tld\t[%g1+%lo(", file); + assemble_name (file, LPBX); + fprintf (file, "+%d)],%%g2\n", blockno*4); + + fputs ("\tadd\t%g2,1,%g2\n", file); + + fputs ("\tst\t%g2,[%g1+%lo(", file); + assemble_name (file, LPBX); + fprintf (file, "+%d)]\n", blockno*4); + } +} + +/* The following macro shall output assembler code to FILE + to indicate a return from function during basic-block profiling. + + If profile_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 profile_block_flag != 0: + + The macro will not be used, so it need not distinguish + these cases. +*/ + +void +sparc_function_block_profiler_exit(file) + FILE *file; +{ + if (profile_block_flag == 2) + fprintf (file, "\tcall\t%s__bb_trace_ret\n\t nop\n", user_label_prefix); + else + abort (); +} diff --git a/contrib/gcc/config/sparc/sparc.h b/contrib/gcc/config/sparc/sparc.h new file mode 100644 index 000000000000..ad11d74afc14 --- /dev/null +++ b/contrib/gcc/config/sparc/sparc.h @@ -0,0 +1,3309 @@ +/* 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 const 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__ -D__sparc_v8__" +#define ASM_CPU32_DEFAULT_SPEC "-Av8" +#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__ -D__sparc_v8__} \ +%{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=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) \ + { \ + const 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, "Use hardware fp" }, \ + {"no-fpu", -MASK_FPU, "Do not use hardware fp" }, \ + {"no-fpu", MASK_FPU_SET, "Do not use hardware fp" }, \ + {"hard-float", MASK_FPU | MASK_FPU_SET, "Use hardware fp" }, \ + {"soft-float", -MASK_FPU, "Do not use hardware fp" }, \ + {"soft-float", MASK_FPU_SET, "Do not use hardware fp" }, \ + {"epilogue", MASK_EPILOGUE, "Use FUNCTION_EPILOGUE" }, \ + {"no-epilogue", -MASK_EPILOGUE, "Do not use FUNCTION_EPILOGUE" }, \ + {"unaligned-doubles", MASK_UNALIGNED_DOUBLES, "Assume possible double misalignment" },\ + {"no-unaligned-doubles", -MASK_UNALIGNED_DOUBLES, "Assume all doubles are aligned" }, \ + {"impure-text", MASK_IMPURE_TEXT, "Pass -assert pure-text to linker" }, \ + {"no-impure-text", -MASK_IMPURE_TEXT, "Do not pass -assert pure-text to linker" }, \ + {"flat", MASK_FLAT, "Use flat register window model" }, \ + {"no-flat", -MASK_FLAT, "Do not use flat register window model" }, \ + {"app-regs", MASK_APP_REGS, "Use ABI reserved registers" }, \ + {"no-app-regs", -MASK_APP_REGS, "Do not use ABI reserved registers" }, \ + {"hard-quad-float", MASK_HARD_QUAD, "Use hardware quad fp instructions" }, \ + {"soft-quad-float", -MASK_HARD_QUAD, "Do not use hardware quad fp instructions" }, \ + {"v8plus", MASK_V8PLUS, "Compile for v8plus ABI" }, \ + {"no-v8plus", -MASK_V8PLUS, "Do not compile for v8plus ABI" }, \ + {"vis", MASK_VIS, "Utilize Visual Instruction Set" }, \ + {"no-vis", -MASK_VIS, "Do not utilize Visual Instruction Set" }, \ + /* ??? These are deprecated, coerced to -mcpu=. Delete in 2.9. */ \ + {"cypress", 0, "Optimize for Cypress processors" }, \ + {"sparclite", 0, "Optimize for SparcLite processors" }, \ + {"f930", 0, "Optimize for F930 processors" }, \ + {"f934", 0, "Optimize for F934 processors" }, \ + {"v8", 0, "Use V8 Sparc ISA" }, \ + {"supersparc", 0, "Optimize for SuperSparc processors" }, \ + /* End of deprecated options. */ \ + {"ptr64", MASK_PTR64, "Pointers are 64-bit" }, \ + {"ptr32", -MASK_PTR64, "Pointers are 32-bit" }, \ + {"32", -MASK_64BIT, "Use 32-bit ABI" }, \ + {"64", MASK_64BIT, "Use 64-bit ABI" }, \ + {"stack-bias", MASK_STACK_BIAS, "Use stack bias" }, \ + {"no-stack-bias", -MASK_STACK_BIAS, "Do not use 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, "Use features of and schedule code for given CPU" }, \ + { "tune=", &sparc_select[2].string, "Schedule code for given CPU" }, \ + { "cmodel=", &sparc_cmodel_string, "Use given Sparc code model" }, \ + { "align-loops=", &sparc_align_loops_string, "Loop code aligned to this power of 2" }, \ + { "align-jumps=", &sparc_align_jumps_string, "Jump targets are aligned to this power of 2" }, \ + { "align-functions=", &sparc_align_funcs_string, "Function starts are aligned to this power of 2" }, \ + 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 +{ + const char *string; + const 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 const char *sparc_align_loops_string; +extern const char *sparc_align_jumps_string; +extern const 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. */ + +#define FUNCTION_PROLOGUE(FILE, SIZE) \ + (TARGET_FLAT ? sparc_flat_output_function_prologue (FILE, (int)SIZE) \ + : output_function_prologue (FILE, (int)SIZE, \ + current_function_uses_only_leaf_regs)) + +/* Output assembler code to FILE to increment profiler label # LABELNO + for profiling a function entry. */ + +#define FUNCTION_PROFILER(FILE, LABELNO) \ + sparc_function_profiler(FILE, LABELNO) + +/* Set the name of the mcount function for the system. */ + +#define MCOUNT_FUNCTION "*mcount" + +/* The following macro shall output assembler code to FILE + to initialize basic-block profiling. */ + +#define FUNCTION_BLOCK_PROFILER(FILE, BLOCK_OR_LABEL) \ + sparc_function_block_profiler(FILE, BLOCK_OR_LABEL) + +/* The following macro shall output assembler code to FILE + to increment a counter associated with basic block number BLOCKNO. */ + +#define BLOCK_PROFILER(FILE, BLOCKNO) \ + sparc_block_profiler (FILE, BLOCKNO) + +/* The following macro shall output assembler code to FILE + to indicate a return from function during basic-block profiling. */ + +#define FUNCTION_BLOCK_PROFILER_EXIT(FILE) \ + sparc_function_block_profiler_exit(FILE) + +/* 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, \ + current_function_uses_only_leaf_regs)) + +#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 do this \ + because REG+REG is not an offsetable \ + address. If we get the situation \ + in reload where source and destination \ + of a movtf pattern are both MEMs with \ + REG+REG address, then 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. + + Do nothing when generating PIC code and the address is a + symbolic operand or requires a scratch register. */ + +#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 \ + && ! (flag_pic \ + && (symbolic_operand (X, Pmode) \ + || pic_address_needs_scratch (X)))) \ + { \ + 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 const 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 void sparc_function_profiler (); +extern void sparc_function_block_profiler (); +extern void sparc_block_profiler (); +extern void sparc_function_block_profiler_exit (); + +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/contrib/gcc/config/sparc/sparc.md b/contrib/gcc/config/sparc/sparc.md new file mode 100644 index 000000000000..aafb7a63ea41 --- /dev/null +++ b/contrib/gcc/config/sparc/sparc.md @@ -0,0 +1,8236 @@ +;;- 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 "current_function_uses_only_leaf_regs"))) + +(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 == 32 + && ((CONST_DOUBLE_HIGH (operands[1]) == 0 + && (CONST_DOUBLE_LOW (operands[1]) & 0x80000000) == 0) + || (CONST_DOUBLE_HIGH (operands[1]) == (HOST_WIDE_INT) 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,2")]) + +;; 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,2,2")]) + +(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)); + + if (GET_CODE (operands[0]) == SUBREG) + operands[0] = alter_subreg (operands[0]); + + if (reg_overlap_mentioned_p (gen_highpart (SFmode, operands[0]), word1)) + { + emit_insn (gen_movsf (gen_lowpart (SFmode, operands[0]), + word1)); + emit_insn (gen_movsf (gen_highpart (SFmode, operands[0]), + word0)); + } + else + { + emit_insn (gen_movsf (gen_highpart (SFmode, operands[0]), + word0)); + emit_insn (gen_movsf (gen_lowpart (SFmode, operands[0]), + 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)); + + if (GET_CODE (operands[1]) == SUBREG) + operands[1] = alter_subreg (operands[1]); + emit_insn (gen_movsf (word0, + gen_highpart (SFmode, operands[1]))); + emit_insn (gen_movsf (word1, + gen_lowpart (SFmode, operands[1]))); + 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_expand "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 (DFmode, REGNO (operands[0]) + 2); + operands[7] = gen_rtx_raw_REG (DFmode, REGNO (operands[1]) + 2);") + +(define_insn "*abstf2_hq_v9" + [(set (match_operand:TF 0 "register_operand" "=e,e") + (abs:TF (match_operand:TF 1 "register_operand" "0,e")))] + "TARGET_FPU && TARGET_V9 && TARGET_HARD_QUAD" + "@ + fabsd\\t%0, %0 + fabsq\\t%1, %0" + [(set_attr "type" "fpmove") + (set_attr "length" "1")]) + +(define_insn "*abstf2_v9" + [(set (match_operand:TF 0 "register_operand" "=e,e") + (abs:TF (match_operand:TF 1 "register_operand" "0,e")))] + "TARGET_FPU && TARGET_V9 && !TARGET_HARD_QUAD" + "@ + 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%1, %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" "=r") + (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" "=r") + (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" "=r") + (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" "=r") + (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)), + operands[3], + 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), + operands[3], + 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)] + "" + "" + [(set_attr "length" "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, (current_function_uses_only_leaf_regs + ? 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 "" "")] + "" + " +{ +#if 0 + rtx chain = operands[0]; +#endif + 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" + "" + [(set_attr "length" "0")]) + +(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/contrib/gcc/config/sparc/splet.h b/contrib/gcc/config/sparc/splet.h new file mode 100644 index 000000000000..d924e7089963 --- /dev/null +++ b/contrib/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, "Generate code for big endian" }, \ +{"little-endian", MASK_LITTLE_ENDIAN, "Generate code for little endian" }, \ +{"live-g0", MASK_LIVE_G0, "Use g0 as a normal register" }, \ +{"no-live-g0", -MASK_LIVE_G0, "Register g0 is fixed with a zero value" }, \ +{"broken-saverestore", MASK_BROKEN_SAVERESTORE, "Enable save/restore bug workarounds" }, \ +{"no-broken-saverestore", -MASK_BROKEN_SAVERESTORE, "Disable save/restore bug workarouns" }, + +#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/contrib/gcc/config/sparc/sun4gas.h b/contrib/gcc/config/sparc/sun4gas.h new file mode 100644 index 000000000000..3cea9560b4fe --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/sun4o3.h b/contrib/gcc/config/sparc/sun4o3.h new file mode 100644 index 000000000000..d2a53c1f2372 --- /dev/null +++ b/contrib/gcc/config/sparc/sun4o3.h @@ -0,0 +1,29 @@ +#include "sparc/sparc.h" + +/* Override the name of the mcount profiling function. */ + +#undef MCOUNT_FUNCTION +#define MCOUNT_FUNCTION "*.mcount" + +/* 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/contrib/gcc/config/sparc/sunos4.h b/contrib/gcc/config/sparc/sunos4.h new file mode 100644 index 000000000000..14c7a437d67c --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/sysv4.h b/contrib/gcc/config/sparc/sysv4.h new file mode 100644 index 000000000000..5f9bba9e594d --- /dev/null +++ b/contrib/gcc/config/sparc/sysv4.h @@ -0,0 +1,216 @@ +/* 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) + +/* 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) + +/* Override the name of the mcount profiling function. */ + +#undef MCOUNT_FUNCTION +#define MCOUNT_FUNCTION "*_mcount" diff --git a/contrib/gcc/config/sparc/t-elf b/contrib/gcc/config/sparc/t-elf new file mode 100644 index 000000000000..da9df38368ee --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/t-halos b/contrib/gcc/config/sparc/t-halos new file mode 100644 index 000000000000..0bd5496ac238 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/t-linux64 b/contrib/gcc/config/sparc/t-linux64 new file mode 100644 index 000000000000..077cf69e7193 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/t-sol2 b/contrib/gcc/config/sparc/t-sol2 new file mode 100644 index 000000000000..a9b6ee147939 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/t-sol2-64 b/contrib/gcc/config/sparc/t-sol2-64 new file mode 100644 index 000000000000..8d42c4453542 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/t-sp64 b/contrib/gcc/config/sparc/t-sp64 new file mode 100644 index 000000000000..99acd5d54235 --- /dev/null +++ b/contrib/gcc/config/sparc/t-sp64 @@ -0,0 +1,2 @@ +LIBGCC1 = +CROSS_LIBGCC1 = diff --git a/contrib/gcc/config/sparc/t-sparcbare b/contrib/gcc/config/sparc/t-sparcbare new file mode 100644 index 000000000000..8bd978b068d9 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/t-sparclite b/contrib/gcc/config/sparc/t-sparclite new file mode 100644 index 000000000000..7cdfbb04551c --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/t-splet b/contrib/gcc/config/sparc/t-splet new file mode 100644 index 000000000000..3329e0bef072 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/t-sunos40 b/contrib/gcc/config/sparc/t-sunos40 new file mode 100644 index 000000000000..3e10575eaea5 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/t-sunos41 b/contrib/gcc/config/sparc/t-sunos41 new file mode 100644 index 000000000000..5783d6a26258 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/t-vxsparc b/contrib/gcc/config/sparc/t-vxsparc new file mode 100644 index 000000000000..0c7a14a44295 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/vxsim.h b/contrib/gcc/config/sparc/vxsim.h new file mode 100644 index 000000000000..6c80375f56b9 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/vxsparc.h b/contrib/gcc/config/sparc/vxsparc.h new file mode 100644 index 000000000000..18ce6ed97b70 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/x-sysv4 b/contrib/gcc/config/sparc/x-sysv4 new file mode 100644 index 000000000000..2a661e359993 --- /dev/null +++ b/contrib/gcc/config/sparc/x-sysv4 @@ -0,0 +1,2 @@ +X_CFLAGS=-DSVR4 +ALLOCA=alloca.o diff --git a/contrib/gcc/config/sparc/xm-linux.h b/contrib/gcc/config/sparc/xm-linux.h new file mode 100644 index 000000000000..691c7d167847 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/xm-lynx.h b/contrib/gcc/config/sparc/xm-lynx.h new file mode 100644 index 000000000000..90fef8543b91 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/xm-openbsd.h b/contrib/gcc/config/sparc/xm-openbsd.h new file mode 100644 index 000000000000..2df7fb3e3639 --- /dev/null +++ b/contrib/gcc/config/sparc/xm-openbsd.h @@ -0,0 +1,23 @@ +/* Configuration file for an host running sparc OpenBSD. + Copyright (C) 1999 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-openbsd.h> +#include <sparc/xm-sparc.h> + diff --git a/contrib/gcc/config/sparc/xm-pbd.h b/contrib/gcc/config/sparc/xm-pbd.h new file mode 100644 index 000000000000..1c3f47590c79 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/xm-sol2.h b/contrib/gcc/config/sparc/xm-sol2.h new file mode 100644 index 000000000000..5613b086b572 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/xm-sp64.h b/contrib/gcc/config/sparc/xm-sp64.h new file mode 100644 index 000000000000..b673161282f4 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/xm-sparc.h b/contrib/gcc/config/sparc/xm-sparc.h new file mode 100644 index 000000000000..e553a0df0b25 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/xm-sysv4-64.h b/contrib/gcc/config/sparc/xm-sysv4-64.h new file mode 100644 index 000000000000..c506d22dd360 --- /dev/null +++ b/contrib/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/contrib/gcc/config/sparc/xm-sysv4.h b/contrib/gcc/config/sparc/xm-sysv4.h new file mode 100644 index 000000000000..6e663d12cfa1 --- /dev/null +++ b/contrib/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 diff --git a/contrib/gcc/configure b/contrib/gcc/configure index 0578cd747d2f..afa0166923f1 100755 --- a/contrib/gcc/configure +++ b/contrib/gcc/configure @@ -1,6 +1,6 @@ #! /bin/sh -# $FreeBSD$ +# $FreeBSD: src/contrib/gcc/configure,v 1.8 2000/01/22 16:05:31 obrien Exp $ # Guess values for system-dependent variables and create Makefiles. # Generated automatically using autoconf version 2.13 diff --git a/contrib/gcc/cp/decl.c b/contrib/gcc/cp/decl.c index bd69ca5f4bef..a61a9e5df06d 100644 --- a/contrib/gcc/cp/decl.c +++ b/contrib/gcc/cp/decl.c @@ -19,7 +19,7 @@ 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. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/gcc/cp/decl.c,v 1.4.2.2 2000/08/07 10:06:54 obrien Exp $ */ /* Process declarations and symbol lookup for C front end. diff --git a/contrib/gcc/cp/except.c b/contrib/gcc/cp/except.c index f2896c760f60..480b748f3fe7 100644 --- a/contrib/gcc/cp/except.c +++ b/contrib/gcc/cp/except.c @@ -21,7 +21,7 @@ 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. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/gcc/cp/except.c,v 1.5 1999/10/16 07:53:19 obrien Exp $ */ #include "config.h" diff --git a/contrib/gcc/cp/gxxint.texi b/contrib/gcc/cp/gxxint.texi index 5a665c72f942..95a99e1a6714 100644 --- a/contrib/gcc/cp/gxxint.texi +++ b/contrib/gcc/cp/gxxint.texi @@ -1,5 +1,5 @@ \input texinfo @c -*-texinfo-*- -@c %** $FreeBSD$ +@c %** $FreeBSD: src/contrib/gcc/cp/gxxint.texi,v 1.4.2.1 2000/07/04 06:01:43 obrien Exp $ @c %**start of header @setfilename g++int.info @settitle G++ internals diff --git a/contrib/gcc/cp/ptree.c b/contrib/gcc/cp/ptree.c index 3920c3071505..595edd269033 100644 --- a/contrib/gcc/cp/ptree.c +++ b/contrib/gcc/cp/ptree.c @@ -19,7 +19,7 @@ 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. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/gcc/cp/ptree.c,v 1.4 1999/10/16 07:57:37 obrien Exp $ */ #include "config.h" diff --git a/contrib/gcc/dbxout.c b/contrib/gcc/dbxout.c index 3f561d631437..7389654223fb 100644 --- a/contrib/gcc/dbxout.c +++ b/contrib/gcc/dbxout.c @@ -18,7 +18,7 @@ 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. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/gcc/dbxout.c,v 1.4 1999/10/26 08:47:58 obrien Exp $ */ /* Output dbx-format symbol table data. diff --git a/contrib/gcc/dwarfout.c b/contrib/gcc/dwarfout.c index 6bdd954fb17b..e13098892039 100644 --- a/contrib/gcc/dwarfout.c +++ b/contrib/gcc/dwarfout.c @@ -19,7 +19,7 @@ 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. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/gcc/dwarfout.c,v 1.4 1999/10/26 08:38:21 obrien Exp $ */ #include "config.h" diff --git a/contrib/gcc/extend.texi b/contrib/gcc/extend.texi index b72a02ac4360..171bfe962f45 100644 --- a/contrib/gcc/extend.texi +++ b/contrib/gcc/extend.texi @@ -2,7 +2,7 @@ @c This is part of the GCC manual. @c For copying conditions, see the file gcc.texi. -@c $FreeBSD$ +@c $FreeBSD: src/contrib/gcc/extend.texi,v 1.4 1999/10/27 09:41:10 obrien Exp $ @node C Extensions @chapter Extensions to the C Language Family diff --git a/contrib/gcc/final.c b/contrib/gcc/final.c index 9769409d1363..bebf41af065e 100644 --- a/contrib/gcc/final.c +++ b/contrib/gcc/final.c @@ -18,7 +18,7 @@ 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. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/gcc/final.c,v 1.7 1999/11/07 10:38:07 obrien Exp $ */ /* This is the final pass of the compiler. diff --git a/contrib/gcc/flags.h b/contrib/gcc/flags.h index a7c1b7e2c151..b69704ce8408 100644 --- a/contrib/gcc/flags.h +++ b/contrib/gcc/flags.h @@ -18,7 +18,7 @@ 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. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/gcc/flags.h,v 1.4 1999/10/26 08:45:23 obrien Exp $ */ /* Name of the input .c file being compiled. */ extern char *main_input_filename; diff --git a/contrib/gcc/function.c b/contrib/gcc/function.c index cd8f2490f165..9d9c70713ecb 100644 --- a/contrib/gcc/function.c +++ b/contrib/gcc/function.c @@ -18,7 +18,7 @@ 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. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/gcc/function.c,v 1.6.2.1 2000/07/04 06:01:24 obrien Exp $ */ /* This file handles the generation of rtl code from tree structure diff --git a/contrib/gcc/gcc.1 b/contrib/gcc/gcc.1 index d81841d4308c..60f307812a47 100644 --- a/contrib/gcc/gcc.1 +++ b/contrib/gcc/gcc.1 @@ -1,4 +1,4 @@ -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/gcc/gcc.1,v 1.15 1999/10/26 08:57:00 obrien Exp $ .\" Copyright (c) 1991, 1992, 1993, 1994 Free Software Foundation -*-Text-*- .\" See section COPYING for conditions for redistribution .\" diff --git a/contrib/gcc/gcc.c b/contrib/gcc/gcc.c index 3128db0d88de..54260fa4a50b 100644 --- a/contrib/gcc/gcc.c +++ b/contrib/gcc/gcc.c @@ -21,7 +21,7 @@ Boston, MA 02111-1307, USA. This paragraph is here to try to keep Sun CC from dying. The number of chars here seems crucial!!!! */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/gcc/gcc.c,v 1.17 2000/03/09 10:11:08 obrien Exp $ */ /* This program is the user interface to the C compiler and possibly to other compilers. It is used because compilation is a complicated procedure diff --git a/contrib/gcc/ginclude/stdarg.h b/contrib/gcc/ginclude/stdarg.h index e3842f78639f..8f54c27693a5 100644 --- a/contrib/gcc/ginclude/stdarg.h +++ b/contrib/gcc/ginclude/stdarg.h @@ -3,7 +3,7 @@ actual type **after default promotions**. Thus, va_arg (..., short) is not valid. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/gcc/ginclude/stdarg.h,v 1.4 1999/10/16 07:12:34 obrien Exp $ */ #ifndef _STDARG_H #ifndef _ANSI_STDARG_H_ diff --git a/contrib/gcc/ginclude/stddef.h b/contrib/gcc/ginclude/stddef.h index 11093912068a..744ec7bcf799 100644 --- a/contrib/gcc/ginclude/stddef.h +++ b/contrib/gcc/ginclude/stddef.h @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/gcc/ginclude/stddef.h,v 1.3 1999/10/13 15:55:31 obrien Exp $ */ #if (!defined(_STDDEF_H) && !defined(_STDDEF_H_) && !defined(_ANSI_STDDEF_H) \ && !defined(__STDDEF_H__)) \ diff --git a/contrib/gcc/ginclude/varargs.h b/contrib/gcc/ginclude/varargs.h index 65191402ea5a..fa1639ac3257 100644 --- a/contrib/gcc/ginclude/varargs.h +++ b/contrib/gcc/ginclude/varargs.h @@ -1,6 +1,6 @@ /* Record that this is varargs.h; this turns off stdarg.h. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/gcc/ginclude/varargs.h,v 1.4 1999/10/16 07:12:34 obrien Exp $ */ #ifndef _VARARGS_H #define _VARARGS_H diff --git a/contrib/gcc/invoke.texi b/contrib/gcc/invoke.texi index 962a7073535a..53a4714ece55 100644 --- a/contrib/gcc/invoke.texi +++ b/contrib/gcc/invoke.texi @@ -2,7 +2,7 @@ @c This is part of the GCC manual. @c For copying conditions, see the file gcc.texi. -@c $FreeBSD$ +@c $FreeBSD: src/contrib/gcc/invoke.texi,v 1.6.2.1 2000/07/04 06:01:25 obrien Exp $ @node Invoking GCC @chapter GCC Command Options diff --git a/contrib/gcc/libgcc2.c b/contrib/gcc/libgcc2.c index 6618dfe3cf1d..aaf480fe2fd9 100644 --- a/contrib/gcc/libgcc2.c +++ b/contrib/gcc/libgcc2.c @@ -26,7 +26,7 @@ Boston, MA 02111-1307, USA. */ This exception does not however invalidate any other reasons why the executable file might be covered by the GNU General Public License. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/gcc/libgcc2.c,v 1.4 1999/10/27 09:45:47 obrien Exp $ */ /* It is incorrect to include config.h here, because this file is being compiled for the target, and hence definitions concerning only the host diff --git a/contrib/gcc/pexecute.c b/contrib/gcc/pexecute.c index cd24f5b99613..78d90f573920 100644 --- a/contrib/gcc/pexecute.c +++ b/contrib/gcc/pexecute.c @@ -23,7 +23,7 @@ Boston, MA 02111-1307, USA. */ /* This file lives in at least two places: libiberty and gcc. Don't change one without the other. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/gcc/pexecute.c,v 1.3 1999/11/04 10:26:31 obrien Exp $ */ #ifdef HAVE_CONFIG_H #include "config.h" diff --git a/contrib/gcc/print-tree.c b/contrib/gcc/print-tree.c index 7b2945415737..3d94a9431692 100644 --- a/contrib/gcc/print-tree.c +++ b/contrib/gcc/print-tree.c @@ -18,7 +18,7 @@ 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. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/gcc/print-tree.c,v 1.4 1999/10/26 09:17:58 obrien Exp $ */ #include "config.h" diff --git a/contrib/gcc/reload.c b/contrib/gcc/reload.c index 7be6edc1b4b4..c8e835a45d59 100644 --- a/contrib/gcc/reload.c +++ b/contrib/gcc/reload.c @@ -18,7 +18,7 @@ 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. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/gcc/reload.c,v 1.4 1999/10/27 09:23:37 obrien Exp $ */ /* This file contains subroutines used only from the file reload1.c. diff --git a/contrib/gcc/toplev.c b/contrib/gcc/toplev.c index 217305acfcae..44ccfda06f02 100644 --- a/contrib/gcc/toplev.c +++ b/contrib/gcc/toplev.c @@ -18,7 +18,7 @@ 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. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/gcc/toplev.c,v 1.6.2.1 2000/07/04 05:55:22 obrien Exp $ */ /* This is the top level of cc1/c++. It parses command args, opens files, invokes the various passes diff --git a/contrib/gdb/gdb/29k-share/README b/contrib/gdb/gdb/29k-share/README new file mode 100644 index 000000000000..5e19715ffad4 --- /dev/null +++ b/contrib/gdb/gdb/29k-share/README @@ -0,0 +1,9 @@ +The files in this directory are shared with other debuggers and +debug interfaces that use Advanced Micro Devices' UDI (universal debug +interface) protocol. The protocol provides a common interface among +debuggers, logic analyzers, emulators, and embedded systems that use +AMD 29000 family processors. + +Do not change these files without coordinating with Advanced Micro +Devices, Embedded Processor Division, 5204 E. Ben White Blvd, Austin, TX 78741. +Maybe postmaster@cayman.amd.com can direct you to the current maintainers. diff --git a/contrib/gdb/gdb/29k-share/udi/udi2go32.c b/contrib/gdb/gdb/29k-share/udi/udi2go32.c new file mode 100644 index 000000000000..63d98ae40177 --- /dev/null +++ b/contrib/gdb/gdb/29k-share/udi/udi2go32.c @@ -0,0 +1,607 @@ +/* + +Interface from UDI calls in 32-bit mode to go32 in 16-bit mode. +Communication is done through a single interrupt vector, which passes +data through two linear buffers. + +Call: + AH = 0xfe + AL = UDI function number + ECX = IN length + ESI = pointer to IN buffer + EDI = pointer to OUT buffer + +Return: + EAX = return value of UDI function + +Vector: + 0x21 + +*/ +#ifdef __GO32__ + +#include <stdlib.h> +#include "udiproc.h" +#include "udisoc.h" + +char dfe_errmsg[500]; + +static char in_buffer[4096]; +static char out_buffer[4096]; +static char *in_ptr; +static char *out_ptr; + +#define IN_INIT() in_ptr = in_buffer +#define IN_VAL(t,v) *((t *)in_ptr)++ = v +#define IN_DATA(ptr, cnt) memcpy(in_ptr, ptr, cnt), in_ptr += cnt + +#define OUT_INIT() out_ptr = out_buffer +#define OUT_VAL(t) (*((t *)out_ptr)++) +#define OUT_DATA(ptr, cnt) memcpy(ptr, out_ptr, cnt), out_ptr += cnt + +static int DO_CALL(int function) +{ + asm("pushl %esi"); + asm("pushl %edi"); + asm("movb %0, %%al" : : "g" (function)); + asm("movl _in_ptr, %ecx"); + asm("movl $_in_buffer, %esi"); + asm("subl %esi, %ecx"); + asm("movl $_out_buffer, %edi"); + asm("movb $0xfe, %ah"); + asm("int $0x21"); + asm("popl %edi"); + asm("popl %esi"); +} + +/*----------------------------------------------------------------------*/ + +#ifdef TEST_UDI +int main() +{ + int r; + long p2; + short p1; + IN_INIT(); + IN_VAL(long, 11111111); + IN_VAL(short, 2222); + IN_DATA("Hello, world\n", 17); + + r = DO_CALL(42); + + OUT_INIT(); + p1 = OUT_VAL(short); + p2 = OUT_VAL(long); + printf("main: p1=%d p2=%d rv=%d\n", p1, p2, r); + return r; +} +#endif + +/*----------------------------------------------------------------------*/ + +unsupported(char *s) +{ + printf("unsupported UDI host call %s\n", s); + abort(); +} + +UDIError UDIConnect ( + char *Configuration, /* In */ + UDISessionId *Session /* Out */ + ) +{ + int r; + out_buffer[0] = 0; /* DJ - test */ + IN_INIT(); + IN_DATA(Configuration, strlen(Configuration)+1); + + r = DO_CALL(UDIConnect_c); + + OUT_INIT(); + *Session = OUT_VAL(UDISessionId); + return r; +} + +UDIError UDIDisconnect ( + UDISessionId Session, /* In */ + UDIBool Terminate /* In */ + ) +{ + int r; + IN_INIT(); + IN_VAL(UDISessionId, Session); + IN_VAL(UDIBool, Terminate); + + return DO_CALL(UDIDisconnect_c); +} + +UDIError UDISetCurrentConnection ( + UDISessionId Session /* In */ + ) +{ + IN_INIT(); + IN_VAL(UDISessionId, Session); + + return DO_CALL(UDISetCurrentConnection_c); +} + +UDIError UDICapabilities ( + UDIUInt32 *TIPId, /* Out */ + UDIUInt32 *TargetId, /* Out */ + UDIUInt32 DFEId, /* In */ + UDIUInt32 DFE, /* In */ + UDIUInt32 *TIP, /* Out */ + UDIUInt32 *DFEIPCId, /* Out */ + UDIUInt32 *TIPIPCId, /* Out */ + char *TIPString /* Out */ + ) +{ + int r; + IN_INIT(); + IN_VAL(UDIUInt32, DFEId); + IN_VAL(UDIUInt32, DFE); + r = DO_CALL(UDICapabilities_c); + OUT_INIT(); + *TIPId = OUT_VAL(UDIUInt32); + *TargetId = OUT_VAL(UDIUInt32); + *TIP = OUT_VAL(UDIUInt32); + *DFEIPCId = OUT_VAL(UDIUInt32); + *TIPIPCId = OUT_VAL(UDIUInt32); + strcpy(TIPString, out_ptr); + return r; +} + +UDIError UDIEnumerateTIPs ( + UDIInt (*UDIETCallback) /* In */ + ( char *Configuration ) /* In to callback() */ + ) +{ + UDIETCallback("montip.exe"); +} + +UDIError UDIGetErrorMsg ( + UDIError ErrorCode, /* In */ + UDISizeT MsgSize, /* In */ + char *Msg, /* Out */ + UDISizeT *CountDone /* Out */ + ) +{ + int r; + if (MsgSize > 4000) + MsgSize = 4000; + IN_INIT(); + IN_VAL(UDIError, ErrorCode); + IN_VAL(UDISizeT, MsgSize); + + r = DO_CALL(UDIGetErrorMsg_c); + + OUT_INIT(); + *CountDone = OUT_VAL(UDISizeT); + OUT_DATA(Msg, *CountDone); + return r; +} + +UDIError UDIGetTargetConfig ( + UDIMemoryRange KnownMemory[], /* Out */ + UDIInt *NumberOfRanges, /* In/Out */ + UDIUInt32 ChipVersions[], /* Out */ + UDIInt *NumberOfChips /* In/Out */ + ) +{ + int r, i; + int nr = *NumberOfRanges; + int nc = *NumberOfChips; + IN_INIT(); + IN_VAL(UDIInt, *NumberOfRanges); + IN_VAL(UDIInt, *NumberOfChips); + r = DO_CALL(UDIGetTargetConfig_c); + if (r == UDIErrorIncomplete) + return r; + OUT_INIT(); + *NumberOfRanges = OUT_VAL(UDIInt); + *NumberOfChips = OUT_VAL(UDIInt); + for (i=0; i<nr; i++) + { + KnownMemory[i].Space = OUT_VAL(short); + KnownMemory[i].Offset = OUT_VAL(CPUOffset); + KnownMemory[i].Size = OUT_VAL(CPUSizeT); + } + for (i=0; i<nc; i++) + { + ChipVersions[i] = OUT_VAL(UDIUInt32); + } + return r; +} + +UDIError UDICreateProcess ( + UDIPId *PId /* Out */ + ) +{ + int r = DO_CALL(UDICreateProcess_c); + + OUT_INIT(); + *PId = OUT_VAL(UDIPId); + + return r; +} + +UDIError UDISetCurrentProcess ( + UDIPId PId /* In */ + ) +{ + IN_INIT(); + IN_VAL(UDIPId, PId); + + return DO_CALL(UDISetCurrentProcess_c); +} + +UDIError UDIDestroyProcess ( + UDIPId PId /* In */ + ) +{ + IN_INIT(); + IN_VAL(UDIPId, PId); + + return DO_CALL(UDIDestroyProcess_c); +} + +UDIError UDIInitializeProcess ( + UDIMemoryRange ProcessMemory[], /* In */ + UDIInt NumberOfRanges, /* In */ + UDIResource EntryPoint, /* In */ + CPUSizeT StackSizes[], /* In */ + UDIInt NumberOfStacks, /* In */ + char *ArgString /* In */ + ) +{ + int i, r; + IN_INIT(); + IN_VAL(UDIInt, NumberOfRanges); + for (i=0; i<NumberOfRanges; i++) + { + IN_VAL(short, ProcessMemory[i].Space); + IN_VAL(CPUOffset, ProcessMemory[i].Offset); + IN_VAL(CPUSizeT, ProcessMemory[i].Size); + } + IN_VAL(short, EntryPoint.Space); + IN_VAL(CPUOffset, EntryPoint.Offset); + IN_VAL(UDIInt, NumberOfStacks); + for (i=0; i<NumberOfStacks; i++) + IN_VAL(CPUSizeT, StackSizes[i]); + IN_DATA(ArgString, strlen(ArgString)+1); + + return DO_CALL(UDIInitializeProcess_c); +} + +UDIError UDIRead ( + UDIResource From, /* In */ + UDIHostMemPtr To, /* Out */ + UDICount Count, /* In */ + UDISizeT Size, /* In */ + UDICount *CountDone, /* Out */ + UDIBool HostEndian /* In */ + ) +{ + int cleft = Count, cthis, dthis; + int cdone = 0, r, bsize=2048/Size; + + while (cleft) + { + cthis = (cleft<bsize) ? cleft : bsize; + IN_INIT(); + IN_VAL(short, From.Space); + IN_VAL(CPUOffset, From.Offset); + IN_VAL(UDICount, cthis); + IN_VAL(UDISizeT, Size); + IN_VAL(UDIBool, HostEndian); + + r = DO_CALL(UDIRead_c); + + OUT_INIT(); + dthis = OUT_VAL(UDICount); + OUT_DATA(To, dthis*Size); + cdone += dthis; + To += dthis*Size; + + if (r != UDINoError) + { + *CountDone = cdone; + return r; + } + cleft -= cthis; + } + *CountDone = cdone; + return UDINoError; +} + +UDIError UDIWrite ( + UDIHostMemPtr From, /* In */ + UDIResource To, /* In */ + UDICount Count, /* In */ + UDISizeT Size, /* In */ + UDICount *CountDone, /* Out */ + UDIBool HostEndian /* In */ + ) +{ + int cleft = Count, cthis, dthis; + int cdone = 0, r, bsize=2048/Size; + + while (cleft) + { + cthis = (cleft<bsize) ? cleft : bsize; + IN_INIT(); + IN_VAL(short, To.Space); + IN_VAL(CPUOffset, To.Offset); + IN_VAL(UDICount, cthis); + IN_VAL(UDISizeT, Size); + IN_VAL(UDIBool, HostEndian); + IN_DATA(From, cthis*Size); + From += cthis*Size; + + r = DO_CALL(UDIWrite_c); + + OUT_INIT(); + cdone += OUT_VAL(UDICount); + + if (r != UDINoError) + { + *CountDone = cdone; + return r; + } + cleft -= cthis; + } + *CountDone = cdone; + return UDINoError; +} + +UDIError UDICopy ( + UDIResource From, /* In */ + UDIResource To, /* In */ + UDICount Count, /* In */ + UDISizeT Size, /* In */ + UDICount *CountDone, /* Out */ + UDIBool Direction /* In */ + ) +{ + int r; + IN_INIT(); + IN_VAL(short, From.Space); + IN_VAL(CPUOffset, From.Offset); + IN_VAL(short, To.Space); + IN_VAL(CPUOffset, To.Offset); + IN_VAL(UDICount, Count); + IN_VAL(UDISizeT, Size); + IN_VAL(UDIBool, Direction); + + r = DO_CALL(UDICopy_c); + + OUT_INIT(); + *CountDone = OUT_VAL(UDICount); + + return r; +} + +UDIError UDIExecute ( + void + ) +{ + return DO_CALL(UDIExecute_c); +} + +UDIError UDIStep ( + UDIUInt32 Steps, /* In */ + UDIStepType StepType, /* In */ + UDIRange Range /* In */ + ) +{ + IN_INIT(); + IN_VAL(UDIUInt32, Steps); + IN_VAL(UDIStepType, StepType); + IN_VAL(UDIRange, Range); + + return DO_CALL(UDIStep_c); +} + +UDIVoid UDIStop ( + void + ) +{ + DO_CALL(UDIStop_c); +} + +UDIError UDIWait ( + UDIInt32 MaxTime, /* In */ + UDIPId *PId, /* Out */ + UDIUInt32 *StopReason /* Out */ + ) +{ + int r; + IN_INIT(); + IN_VAL(UDIInt32, MaxTime); + r = DO_CALL(UDIWait_c); + OUT_INIT(); + *PId = OUT_VAL(UDIPId); + *StopReason = OUT_VAL(UDIUInt32); + return r; +} + +UDIError UDISetBreakpoint ( + UDIResource Addr, /* In */ + UDIInt32 PassCount, /* In */ + UDIBreakType Type, /* In */ + UDIBreakId *BreakId /* Out */ + ) +{ + int r; + IN_INIT(); + IN_VAL(short, Addr.Space); + IN_VAL(CPUOffset, Addr.Offset); + IN_VAL(UDIInt32, PassCount); + IN_VAL(UDIBreakType, Type); + + r = DO_CALL(UDISetBreakpoint_c); + + OUT_INIT(); + *BreakId = OUT_VAL(UDIBreakId); + return r; +} + +UDIError UDIQueryBreakpoint ( + UDIBreakId BreakId, /* In */ + UDIResource *Addr, /* Out */ + UDIInt32 *PassCount, /* Out */ + UDIBreakType *Type, /* Out */ + UDIInt32 *CurrentCount /* Out */ + ) +{ + int r; + IN_INIT(); + IN_VAL(UDIBreakId, BreakId); + + r = DO_CALL(UDIQueryBreakpoint_c); + + OUT_INIT(); + Addr->Space = OUT_VAL(short); + Addr->Offset = OUT_VAL(CPUOffset); + *PassCount = OUT_VAL(UDIInt32); + *Type = OUT_VAL(UDIBreakType); + *CurrentCount = OUT_VAL(UDIInt32); + + return r; +} + +UDIError UDIClearBreakpoint ( + UDIBreakId BreakId /* In */ + ) +{ + IN_INIT(); + IN_VAL(UDIBreakId, BreakId); + + return DO_CALL(UDIClearBreakpoint_c); +} + +UDIError UDIGetStdout ( + UDIHostMemPtr Buf, /* Out */ + UDISizeT BufSize, /* In */ + UDISizeT *CountDone /* Out */ + ) +{ + int r; + IN_INIT(); + if (BufSize > 4000) + BufSize = 4000; + IN_VAL(UDISizeT,BufSize); + r = DO_CALL(UDIGetStdout_c); + OUT_INIT(); + *CountDone = OUT_VAL(UDISizeT); + if (*CountDone <= BufSize) + OUT_DATA(Buf, *CountDone); + return r; +} + +UDIError UDIGetStderr ( + UDIHostMemPtr Buf, /* Out */ + UDISizeT BufSize, /* In */ + UDISizeT *CountDone /* Out */ + ) +{ + int r; + IN_INIT(); + if (BufSize > 4000) + BufSize = 4000; + IN_VAL(UDISizeT,BufSize); + r = DO_CALL(UDIGetStderr_c); + OUT_INIT(); + *CountDone = OUT_VAL(UDISizeT); + OUT_DATA(Buf, *CountDone); + return r; +} + +UDIError UDIPutStdin ( + UDIHostMemPtr Buf, /* In */ + UDISizeT Count, /* In */ + UDISizeT *CountDone /* Out */ + ) +{ + int r; + IN_INIT(); + if (Count > 4000) + Count = 4000; + IN_VAL(UDISizeT,Count); + IN_DATA(Buf, Count); + r = DO_CALL(UDIPutStdin_c); + OUT_INIT(); + *CountDone = OUT_VAL(UDISizeT); + return r; +} + +UDIError UDIStdinMode ( + UDIMode *Mode /* Out */ + ) +{ + int r; + IN_INIT(); + r = DO_CALL(UDIStdinMode_c); + OUT_INIT(); + *Mode = OUT_VAL(UDIMode); + return r; +} + +UDIError UDIPutTrans ( + UDIHostMemPtr Buf, /* In */ + UDISizeT Count, /* In */ + UDISizeT *CountDone /* Out */ + ) +{ + int r; + IN_INIT(); + if (Count > 4000) + Count = 4000; + IN_VAL(UDISizeT,Count); + IN_DATA(Buf, Count); + r = DO_CALL(UDIPutTrans_c); + OUT_INIT(); + *CountDone = OUT_VAL(UDISizeT); + return r; +} + +UDIError UDIGetTrans ( + UDIHostMemPtr Buf, /* Out */ + UDISizeT BufSize, /* In */ + UDISizeT *CountDone /* Out */ + ) +{ + int r; + IN_INIT(); + if (BufSize > 4000) + BufSize = 4000; + IN_VAL(UDISizeT,BufSize); + r = DO_CALL(UDIGetTrans_c); + OUT_INIT(); + *CountDone = OUT_VAL(UDISizeT); + OUT_DATA(Buf, *CountDone); + return r; +} + +UDIError UDITransMode ( + UDIMode *Mode /* Out */ + ) +{ + int r; + IN_INIT(); + r = DO_CALL(UDITransMode_c); + OUT_INIT(); + *Mode = OUT_VAL(UDIMode); + return r; +} + +#define DFEIPCIdCompany 0x0001 /* Company ID AMD */ +#define DFEIPCIdProduct 0x1 /* Product ID 0 */ +#define DFEIPCIdVersion 0x125 /* 1.2.5 */ + +unsigned UDIGetDFEIPCId () +{ + return((((UDIUInt32)DFEIPCIdCompany) << 16) |(DFEIPCIdProduct << 12) | DFEIPCIdVersion); +} + +#endif /* __GO32__ */ diff --git a/contrib/gdb/gdb/29k-share/udi/udiids.h b/contrib/gdb/gdb/29k-share/udi/udiids.h new file mode 100644 index 000000000000..5f805e469050 --- /dev/null +++ b/contrib/gdb/gdb/29k-share/udi/udiids.h @@ -0,0 +1,48 @@ +/* This file contains the DFE and TIP IDs to be used by AMD products for + the UDICapabilities call. + + Copyright 1993 Free Software Foundation, Inc. + + This file is part of GDB. + + This program 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 of the License, or + (at your option) any later version. + + This program 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; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + + /* Company Codes -- AMD assigns these */ +#define UDICompanyCode_AMD 1 + + /* Build a UDIID given a CompanyProdCode and 3 version pieces */ +#define UDIID(CompanyProdCode, v1,v2,v3) ((((CompanyProdCode) & 0xfffff)<<12)+\ + (((v1)&0xf)<<8) + (((v2)&0xf)<<4) + ((v3)&0xf)) + + + /* Extract a CompanyProdCode or a Version from a UDIID */ +#define UDIID_CompanyProdCode(id) (((id)>>12) & 0xfffff) +#define UDIID_Version(id) ((id)&0xfff) + + +#define UDIAMDProduct(ProdCode) ((UDICompanyCode_AMD<<4) + (ProdCode&0xf)) + + /* AMD DFE Product Codes */ +#define UDIProductCode_Mondfe UDIAMDProduct(0) +#define UDIProductCode_XRAY UDIAMDProduct(1) +#define UDIProductCode_TIPTester UDIAMDProduct(2) + + /* AMD TIP Product Codes (need not be distinct from DFE Product Codes) */ +#define UDIProductCode_Montip UDIAMDProduct(0) +#define UDIProductCode_Isstip UDIAMDProduct(1) + + +#define UDILatestVersion 0x120 /* UDI 1.2.0, can be used in DFE and TIP desired UDI params */ + diff --git a/contrib/gdb/gdb/29k-share/udi/udip2soc.c b/contrib/gdb/gdb/29k-share/udi/udip2soc.c new file mode 100644 index 000000000000..969e8502eb46 --- /dev/null +++ b/contrib/gdb/gdb/29k-share/udi/udip2soc.c @@ -0,0 +1,1250 @@ +/* Copyright 1993 Free Software Foundation, Inc. + + This file is part of GDB. + + This program 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 of the License, or + (at your option) any later version. + + This program 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; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +static char udip2soc_c[]="@(#)udip2soc.c 2.11 Daniel Mann"; +static char udip2soc_c_AMD[]="@(#)udip2soc.c 2.8, AMD"; +/* +* This module converts UDI Procedural calls into +* UDI socket messages for UNIX. +* It is used by DFE client processes +********************************************************************** HISTORY +*/ +/* This is all unneeded on DOS machines. */ +#ifndef __GO32__ + +#include <stdio.h> +#include <string.h> + +/* Before sys/file.h for Unixware. */ +#include <sys/types.h> + +#include <sys/file.h> + +/* This used to say sys/fcntl.h, but the only systems I know of that + require that are old (pre-4.3, at least) BSD systems, which we + probably don't need to worry about. */ +#include <fcntl.h> + +#include <sys/wait.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <signal.h> +#include <sys/errno.h> +#include "udiproc.h" +#include "udisoc.h" + +extern int errno; +extern int sys_nerr; +extern int udr_errno; +extern char* getenv(); + +/* local type decs. and macro defs. not in a .h file ************* MACRO/TYPE +*/ +#define version_c 0x121 /* DFE-IPC version id */ +#define TRUE -1 +#define FALSE 0 +#define PORT_NUM 7000 +#define MAX_SESSIONS 5 /* maximum DFE-TIP connections */ +#define SOC_BUF_SIZE 4* 1024 /* size of socket comms buffer */ +#define SBUF_SIZE 500 /* size of string buffer */ +#define ERRMSG_SIZE 500 /* size of error message buffer */ + +typedef struct connection_str /* record of connect session */ +{ + int in_use; + char connect_id[20]; /* connection identifier */ + char domain_string[20]; /* dommaing for conection */ + char tip_string[30]; /* TIP host name for AF_INET */ + char tip_exe[80]; /* TIP exe name */ + int dfe_sd; /* associated DFE socket */ + int tip_pid; /* pid of TIP process */ + struct sockaddr_in dfe_sockaddr; + struct sockaddr_in tip_sockaddr_in; + struct sockaddr tip_sockaddr; +} connection_t; + +typedef struct session_str +{ + int in_use; + connection_t* soc_con_p; /* associated connection */ + UDISessionId tip_id; /* associated TIP session ID */ +} session_t; + +/* global dec/defs. which are not in a .h file ************* EXPORT DEC/DEFS +*/ +UDIError dfe_errno; +char dfe_errmsg[ERRMSG_SIZE];/* error string */ + +/* local dec/defs. which are not in a .h file *************** LOCAL DEC/DEFS +*/ +LOCAL connection_t soc_con[MAX_SESSIONS]; +LOCAL session_t session[MAX_SESSIONS]; +LOCAL UDR udr; +LOCAL UDR* udrs = &udr; /* UDR for current session */ +LOCAL int current; /* int-id for current session */ +LOCAL char sbuf[SBUF_SIZE]; /* String handler buffer */ +LOCAL char config_file[80]; /* path/name for config file */ + +/***************************************************************** UDI_CONNECT +* Establish a new FDE to TIP conection. The file "./udi_soc" or +* "/etc/udi_soc" may be examined to obtain the conection information +* if the "Config" parameter is not a completd "line entry". +* +* NOTE: the Session string must not start whith white-space characters. +* Format of string is: +* <session> <domain> <soc_name|host_name> <tip_exe|port> <pass to UDIconnect> +* soc2cayman AF_INET cayman 7000 <not supported> +* soc2tip AF_UNIX astring tip.exe ... +*/ +UDIError +UDIConnect(Config, Session) + char *Config; /* in -- identification string */ + UDISessionId *Session; /* out -- session ID */ +{ + UDIInt32 service_id = UDIConnect_c; + int domain; + int cnt=0; + int rcnt, pos, params_pos=0; + char *tip_main_string; + char *env_p; + struct hostent *tip_info_p; + FILE *fd; +#if 0 + FILE *f_p; +#endif + UDIUInt32 TIPIPCId; + UDIUInt32 DFEIPCId; + +#if 0 /* This is crap. It assumes that udi_soc is executable! */ + sprintf(sbuf, "which udi_soc"); + f_p = popen(sbuf, "r"); + if(f_p) + { while( (sbuf[cnt++]=getc(f_p)) != EOF); + sbuf[cnt-2]=0; + } + pclose(f_p); +#endif + + for (rcnt=0; + rcnt < MAX_SESSIONS && session[rcnt].in_use; + rcnt++); + + if (rcnt >= MAX_SESSIONS) + { + sprintf(dfe_errmsg, "DFE-ipc ERROR: Too many sessions already open"); + return UDIErrorIPCLimitation; + } + + /* One connection can be multiplexed between several sessions. */ + + for (cnt=0; + cnt < MAX_SESSIONS && soc_con[cnt].in_use; + cnt++); + + if (cnt >= MAX_SESSIONS) + { + sprintf(dfe_errmsg, + "DFE-ipc ERROR: Too many connections already open"); + return UDIErrorIPCLimitation; + } + + *Session = rcnt; + session[rcnt].soc_con_p = &soc_con[cnt]; + + if (strchr(Config, ' ')) /* test if file entry given */ + { + soc_con[cnt].in_use = TRUE; + sscanf(Config, "%s %s %s %s %n", + soc_con[cnt].connect_id, + soc_con[cnt].domain_string, + soc_con[cnt].tip_string, + soc_con[cnt].tip_exe, + ¶ms_pos); + tip_main_string = Config + params_pos; + } + else /* here if need to read udi_soc file */ + { + strcpy(config_file, "udi_soc"); + env_p = getenv("UDICONF"); + if (env_p) + strcpy(config_file, env_p); + + fd = fopen(config_file, "r"); + + if (!fd) + { + sprintf(dfe_errmsg, "UDIConnect, can't open udi_soc file:\n%s ", + strerror(errno)); + dfe_errno = UDIErrorCantOpenConfigFile; + goto tip_failure; + } + + while (1) + { + if (fscanf(fd, "%s %s %s %s %[^\n]\n", + soc_con[cnt].connect_id, + soc_con[cnt].domain_string, + soc_con[cnt].tip_string, + soc_con[cnt].tip_exe, + sbuf) == EOF) + break; + + if (strcmp(Config, soc_con[cnt].connect_id) != 0) + continue; + + soc_con[cnt].in_use = TRUE; /* here if entry found */ + + tip_main_string = sbuf; + break; + } + + fclose(fd); + if (!soc_con[cnt].in_use) + { + sprintf(dfe_errmsg, + "UDIConnect, can't find `%s' entry in udi_soc file", + Config); + dfe_errno = UDIErrorNoSuchConfiguration; + goto tip_failure; + } + } +/*----------------------------------------------------------- SELECT DOMAIN */ + if (strcmp(soc_con[cnt].domain_string, "AF_UNIX") == 0) + domain = AF_UNIX; + else if (strcmp(soc_con[cnt].domain_string, "AF_INET") == 0) + domain = AF_INET; + else + { + sprintf(dfe_errmsg, "DFE-ipc ERROR: socket address family not known"); + dfe_errno = UDIErrorBadConfigFileEntry; + goto tip_failure; + } + +/*---------------------------------------------------- MULTIPLEXED SOCKET ? */ +/* If the requested session requires communication with + a TIP which already has a socket connection established, + then we do not create a new socket but multiplex the + existing one. A TIP is said to use the same socket if + socket-name/host-name and the domain are the same. + */ + for (rcnt=0; rcnt < MAX_SESSIONS; rcnt++) + { + if (soc_con[rcnt].in_use + && rcnt != cnt + && strcmp(soc_con[cnt].domain_string, + soc_con[rcnt].domain_string) == 0 + && strcmp(soc_con[cnt].tip_string, + soc_con[rcnt].tip_string) == 0) + { + session[*Session].soc_con_p = &soc_con[rcnt]; + soc_con[cnt].in_use = FALSE; /* don't need new connect */ + goto tip_connect; + } + } +/*------------------------------------------------------------------ SOCKET */ + soc_con[cnt].dfe_sd = socket(domain, SOCK_STREAM, 0); + if (soc_con[cnt].dfe_sd == -1) + { + sprintf(dfe_errmsg, "DFE-ipc ERROR, socket() call failed %s ", + strerror (errno)); + dfe_errno = UDIErrorUnknownError; + goto tip_failure; + } + +/*--------------------------------------------------------- AF_UNIX CONNECT */ + if (domain == AF_UNIX) + { + if (strcmp(soc_con[cnt].tip_string, "*") == 0) + { + for (pos = 0; pos < 20; pos++) + { + int f; + + sprintf(soc_con[cnt].tip_string,"/tmp/U%d", getpid() + pos); + f = open(soc_con[cnt].tip_string, O_CREAT); + if (f == -1) + continue; + + close(f); + unlink(soc_con[cnt].tip_string); + break; + } + + if (pos >= 20) + { + sprintf(dfe_errmsg, + "DFE-ipc ERROR, can't create random socket name"); + dfe_errno = UDIErrorCantConnect; + goto tip_failure; + } + } + + soc_con[cnt].tip_sockaddr.sa_family = domain; + memcpy(soc_con[cnt].tip_sockaddr.sa_data, + soc_con[cnt].tip_string, + sizeof(soc_con[cnt].tip_sockaddr.sa_data)); + if (connect(soc_con[cnt].dfe_sd, + &soc_con[cnt].tip_sockaddr, + sizeof(soc_con[cnt].tip_sockaddr))) + { /* if connect() fails assume TIP not yet started */ +/*------------------------------------------------------------ AF_UNIX EXEC */ + int pid; + int statusp; + char *arg0; + + arg0 = strrchr(soc_con[cnt].tip_exe,'/'); + + if (arg0) + arg0++; + else + arg0 = soc_con[cnt].tip_exe; + + pid = vfork(); + + if (pid == 0) /* Child */ + { + execlp(soc_con[cnt].tip_exe, + arg0, + soc_con[cnt].domain_string, + soc_con[cnt].tip_string, + NULL); + _exit(1); + } + + if (waitpid(pid, &statusp, WNOHANG)) + { + sprintf(dfe_errmsg, "DFE-ipc ERROR: can't exec the TIP"); + dfe_errno = UDIErrorCantStartTIP; + goto tip_failure; + } + + pos = 3; + for (pos = 3; pos > 0; pos--) + { + if (!connect(soc_con[cnt].dfe_sd, + &soc_con[cnt].tip_sockaddr, + sizeof(soc_con[cnt].tip_sockaddr))) + break; + sleep(1); + } + + if (pos == 0) + { + sprintf(dfe_errmsg, "DFE-ipc ERROR, connect() call failed: %s", + strerror (errno)); + dfe_errno = UDIErrorCantConnect; + goto tip_failure; + } + } + } +/*--------------------------------------------------------- AF_INET CONNECT */ + else if (domain == AF_INET) + { + fprintf(stderr, + "DFE-ipc WARNING, need to have first started remote TIP"); + + soc_con[cnt].tip_sockaddr_in.sin_family = domain; + soc_con[cnt].tip_sockaddr_in.sin_addr.s_addr = + inet_addr(soc_con[cnt].tip_string); + if (soc_con[cnt].tip_sockaddr_in.sin_addr.s_addr == -1) + { + tip_info_p = gethostbyname(soc_con[cnt].tip_string); + if (tip_info_p == NULL) + { + sprintf(dfe_errmsg,"DFE-ipc ERROR, No such host %s", + soc_con[cnt].tip_string); + dfe_errno = UDIErrorNoSuchConnection; + goto tip_failure; + } + memcpy((char *)&soc_con[cnt].tip_sockaddr_in.sin_addr, + tip_info_p->h_addr, + tip_info_p->h_length); + } + soc_con[cnt].tip_sockaddr_in.sin_port + = htons(atoi(soc_con[cnt].tip_exe)); + + if (connect(soc_con[cnt].dfe_sd, + (struct sockaddr *) &soc_con[cnt].tip_sockaddr_in, + sizeof(soc_con[cnt].tip_sockaddr_in))) + { + sprintf(dfe_errmsg, "DFE-ipc ERROR, connect() call failed %s ", + strerror (errno)); + dfe_errno = UDIErrorCantConnect; + goto tip_failure; + } + } +/*------------------------------------------------------------- TIP CONNECT */ + if (cnt == 0) udr_create(udrs, soc_con[cnt].dfe_sd, SOC_BUF_SIZE); + +tip_connect: + current = cnt; + session[*Session].in_use = TRUE; /* session id is now in use */ + + udr_errno = 0; + udrs->udr_op = UDR_ENCODE; /* send all "in" parameters */ + udr_UDIInt32(udrs, &service_id); + + DFEIPCId = (company_c << 16) + (product_c << 12) + version_c; + udr_UDIUInt32(udrs, &DFEIPCId); + + udr_string(udrs, tip_main_string); + + udr_sendnow(udrs); + + udrs->udr_op = UDR_DECODE; /* recv all "out" parameters */ + udr_UDIUInt32(udrs, &TIPIPCId); + if ((TIPIPCId & 0xfff) < version_c) + sprintf(dfe_errmsg, "DFE-ipc: Obsolete TIP Specified"); + + udr_UDIInt32(udrs, &soc_con[cnt].tip_pid); + + udr_UDISessionId(udrs, &session[*Session].tip_id); + + udr_UDIError(udrs, &dfe_errno); + if (dfe_errno > 0) UDIKill(*Session, 0); + + return dfe_errno; + +tip_failure: + + soc_con[cnt].in_use = FALSE; + session[*Session].in_use = FALSE; +/* XXX - Should also close dfe_sd, but not sure what to do if muxed */ + return dfe_errno; +} + +/************************************************************** UDI_Disconnect +* UDIDisconnect() should be called before exiting the +* DFE to ensure proper shut down of the TIP. +*/ +UDIError UDIDisconnect(Session, Terminate) +UDISessionId Session; +UDIBool Terminate; +{ + int cnt; + UDIInt32 service_id = UDIDisconnect_c; + if(Session < 0 || Session > MAX_SESSIONS) + { + sprintf(dfe_errmsg," SessionId not valid (%d)", Session); + return UDIErrorNoSuchConfiguration; + } + udr_errno = 0; + udrs->udr_op = UDR_ENCODE; /* send all "in" parameters */ + udr_UDIInt32(udrs, &service_id); + udr_UDISessionId(udrs, &session[Session].tip_id); + udr_UDIBool(udrs, &Terminate); + udr_sendnow(udrs); + + session[Session].in_use = FALSE; /* session id is now free */ + for (cnt=0; cnt < MAX_SESSIONS; cnt++) + if(session[cnt].in_use + && session[cnt].soc_con_p == session[Session].soc_con_p + ) break; + if(cnt >= MAX_SESSIONS) /* test if socket not multiplexed */ + if(shutdown(session[Session].soc_con_p->dfe_sd, 2)) + { + sprintf(dfe_errmsg, "DFE-ipc WARNING: socket shutdown failed"); + return UDIErrorIPCInternal; + } + else + session[Session].soc_con_p->in_use = 0; + + udrs->udr_op = UDR_DECODE; /* receive all "out" parameters */ + udr_UDIError(udrs, &dfe_errno); /* get any TIP error */ + return dfe_errno; +} + +/******************************************************************** UDI_KILL +* UDIKill() is used to send a signal to the TIP. +* This is a private IPC call. +*/ +UDIError UDIKill(Session, Signal) +UDISessionId Session; +UDIInt32 Signal; +{ + int cnt; + UDIInt32 service_id = UDIKill_c; + if(Session < 0 || Session > MAX_SESSIONS) + { + sprintf(dfe_errmsg," SessionId not valid (%d)", Session); + return UDIErrorNoSuchConfiguration; + } + udr_errno = 0; + udrs->udr_op = UDR_ENCODE; /* send all "in" parameters */ + udr_UDIInt32(udrs, &service_id); + udr_UDISessionId(udrs, &session[Session].tip_id); + udr_UDIInt32(udrs, &Signal); + udr_sendnow(udrs); + + session[Session].in_use = FALSE; /* session id is now free */ + for (cnt=0; cnt < MAX_SESSIONS; cnt++) + if(session[cnt].in_use + && session[cnt].soc_con_p == session[Session].soc_con_p + ) break; + if(cnt < MAX_SESSIONS) /* test if socket not multiplexed */ + if(shutdown(session[Session].soc_con_p->dfe_sd, 2)) + { + sprintf(dfe_errmsg, "DFE-ipc WARNING: socket shutdown failed"); + return UDIErrorIPCInternal; + } + else + session[Session].soc_con_p->in_use = 0; + + udrs->udr_op = UDR_DECODE; /* receive all "out" parameters */ + udr_UDIError(udrs, &dfe_errno); /* get any TIP error */ + return dfe_errno; +} + +/************************************************** UDI_Set_Current_Connection +* If you are connected to multiple TIPs, you can change +* TIPs using UDISetCurrentConnection(). +*/ +UDIError UDISetCurrentConnection(Session) +UDISessionId Session; +{ + UDIInt32 service_id = UDISetCurrentConnection_c; + + if(Session < 0 || Session > MAX_SESSIONS) + return UDIErrorNoSuchConfiguration; + if(!session[Session].in_use) /* test if not in use yet */ + return UDIErrorNoSuchConnection; + + current = Session; + /* change socket or multiplex the same socket */ + udrs->sd = session[Session].soc_con_p->dfe_sd; + + udr_errno = 0; + udrs->udr_op = UDR_ENCODE; /* send all "in" parameters */ + udr_UDIInt32(udrs, &service_id); + udr_UDISessionId(udrs, &session[Session].tip_id); + udr_sendnow(udrs); + if(udr_errno) return udr_errno; + + udrs->udr_op = UDR_DECODE; /* receive all "out" parameters */ + udr_UDIError(udrs, &dfe_errno); /* get any TIP error */ + return dfe_errno; +} + +/************************************************************ UDI_Capabilities +* The DFE uses UDICapabilities() to both inform the TIP +* of what services the DFE offers and to inquire of the +* TIP what services the TIP offers. +*/ +UDIError UDICapabilities(TIPId, TargetId, DFEId, DFE, TIP, DFEIPCId, + TIPIPCId, TIPString) +UDIUInt32 *TIPId; /* out */ +UDIUInt32 *TargetId; /* out */ +UDIUInt32 DFEId; /* in */ +UDIUInt32 DFE; /* in */ +UDIUInt32 *TIP; /* out */ +UDIUInt32 *DFEIPCId; /* out */ +UDIUInt32 *TIPIPCId; /* out */ +char *TIPString; /* out */ +{ + UDIInt32 service_id = UDICapabilities_c; + int size; + + udr_errno = 0; + udrs->udr_op = UDR_ENCODE; /* send all "in" parameters */ + udr_UDIInt32(udrs, &service_id); + udr_UDIInt32(udrs, &DFEId); + udr_UDIInt32(udrs, &DFE); + udr_sendnow(udrs); + if(udr_errno) return udr_errno; + + udrs->udr_op = UDR_DECODE; /* receive all "out" paramters */ + udr_UDIInt32(udrs, TIPId); + udr_UDIInt32(udrs, TargetId); + udr_UDIInt32(udrs, TIP); + udr_UDIInt32(udrs, DFEIPCId); + *DFEIPCId = (company_c << 16) + (product_c << 12) + version_c; + udr_UDIInt32(udrs, TIPIPCId); + udr_string(udrs, sbuf); + udr_UDIError(udrs, &dfe_errno); /* get any TIP error */ + size = strlen(sbuf); + if(size +1 > 80) return -1; /* test if sufficient space */ + strcpy(TIPString, sbuf); + return dfe_errno; +} + +/********************************************************** UDI_Enumerate_TIPs +* Used by the DFE to enquire about available TIP +* connections. +*/ +UDIError UDIEnumerateTIPs(UDIETCallback) + int (*UDIETCallback)(); /* In -- function to callback */ +{ + FILE *fp; + + fp = fopen(config_file, "r"); + if(fp == NULL) + return UDIErrorCantOpenConfigFile; + while(fgets( sbuf, SBUF_SIZE, fp)) + if(UDIETCallback( sbuf) == UDITerminateEnumeration) + break; + fclose( fp); + return UDINoError; /* return success */ +} + +/*********************************************************** UDI_GET_ERROR_MSG +* Some errors are target specific. They are indicated +* by a negative error return value. The DFE uses +* UDIGetErrorMsg() to get the descriptive text for +* the error message which can then be displayed to +* the user. +*/ +UDIError UDIGetErrorMsg(error_code, msg_len, msg, CountDone) +UDIError error_code; /* In */ +UDISizeT msg_len; /* In -- allowed message space */ +char* msg; /* Out -- length of message*/ +UDISizeT *CountDone; /* Out -- number of characters */ +{ + UDIInt32 service_id = UDIGetErrorMsg_c; + int size; + + udr_errno = 0; + udrs->udr_op = UDR_ENCODE; /* send all "in" parameters */ + udr_UDIInt32(udrs, &service_id); + udr_UDIError(udrs, &error_code); + udr_UDISizeT(udrs, &msg_len); + udr_sendnow(udrs); + if(udr_errno) return udr_errno; + + udrs->udr_op = UDR_DECODE; /* receive all "out" parameters */ + udr_string(udrs, sbuf); + udr_UDISizeT(udrs, CountDone); + udr_UDIError(udrs, &dfe_errno); /* get any TIP error */ + size = strlen(sbuf); + if(size +1 > msg_len) return -1; /* test if sufficient space */ + strcpy(msg, sbuf); + return dfe_errno; +} + +/******************************************************* UDI_GET_TARGET_CONFIG +* UDIGetTargetConfig() gets information about the target. +*/ +UDIError UDIGetTargetConfig(KnownMemory, NumberOfRanges, ChipVersions, + NumberOfChips) +UDIMemoryRange KnownMemory[]; /* Out */ +UDIInt *NumberOfRanges; /* In and Out */ +UDIUInt32 ChipVersions[]; /* Out */ +UDIInt *NumberOfChips; /* In and Out */ +{ + UDIInt32 service_id = UDIGetTargetConfig_c; + int cnt; + int MaxOfRanges = *NumberOfRanges; + + udr_errno = 0; + udrs->udr_op = UDR_ENCODE; /* send all "in" parameters */ + udr_UDIInt32(udrs, &service_id); + udr_UDIInt(udrs, NumberOfRanges); + udr_UDIInt(udrs, NumberOfChips); + udr_sendnow(udrs); + if(udr_errno) return udr_errno; + + udrs->udr_op = UDR_DECODE; /* receive all "out" paramters */ + for(cnt=1; cnt <= MaxOfRanges; cnt++) + udr_UDIMemoryRange(udrs, &KnownMemory[cnt-1]); + udr_UDIInt(udrs, NumberOfRanges); + udr_UDIInt(udrs, NumberOfChips); + for(cnt=1; cnt <= *NumberOfChips; cnt++) + udr_UDIUInt32(udrs, &ChipVersions[cnt -1]); + udr_UDIError(udrs, &dfe_errno); /* get any TIP error */ + return dfe_errno; +} + +/********************************************************** UDI_CREATE_PRCOESS +* UDICreateProcess() tells the target OS that a +* process is to be created and gets a PID back unless +* there is some error. +*/ +UDIError UDICreateProcess(pid) +UDIPId *pid; /* out */ +{ + UDIInt32 service_id = UDICreateProcess_c; + + udr_errno = 0; + udrs->udr_op = UDR_ENCODE; /* send all "in" parameters */ + udr_UDIInt32(udrs, &service_id); + udr_sendnow(udrs); + if(udr_errno) return udr_errno; + + udrs->udr_op = UDR_DECODE; /* receive all "out" parameters */ + udr_UDIPId(udrs, pid); + udr_UDIError(udrs, &dfe_errno); /* get any TIP error */ + return dfe_errno; +} + +/***************************************************** UDI_Set_Current_Process +* UDISetCurrentProcess uses a pid supplied by +* UDICreateProcess and sets it as the default for all +* udi calls until a new one is set. A user of a +*/ +UDIError UDISetCurrentProcess (pid) +UDIPId pid; /* In */ +{ + UDIInt32 service_id = UDISetCurrentProcess_c; + + udr_errno = 0; + udrs->udr_op = UDR_ENCODE; /* send all "in" parameters */ + udr_UDIInt32(udrs, &service_id); + udr_UDIPId(udrs, &pid); + udr_sendnow(udrs); + if(udr_errno) return udr_errno; + + udrs->udr_op = UDR_DECODE; /* receive all "out" parameters */ + udr_UDIError(udrs, &dfe_errno); /* get any TIP error */ + return dfe_errno; +} + +/****************************************************** UDI_INITIALISE_PROCESS +* UDIInitializeProcess() prepare process for +* execution. (Reset processor if process os processor). +*/ +UDIError UDIInitializeProcess( ProcessMemory, NumberOfRanges, EntryPoint, + StackSizes, NumberOfStacks, ArgString) +UDIMemoryRange ProcessMemory[]; /* In */ +UDIInt NumberOfRanges; /* In */ +UDIResource EntryPoint; /* In */ +CPUSizeT *StackSizes; /* In */ +UDIInt NumberOfStacks; /* In */ +char *ArgString; /* In */ +{ + UDIInt32 service_id = UDIInitializeProcess_c; + int cnt; + + udr_errno = 0; + udrs->udr_op = UDR_ENCODE; /* send all "in" parameters */ + udr_UDIInt32(udrs, &service_id); + udr_UDIInt(udrs, &NumberOfRanges); + for(cnt = 0; cnt < NumberOfRanges; cnt++) + udr_UDIMemoryRange(udrs, &ProcessMemory[cnt] ); + udr_UDIResource(udrs, &EntryPoint); + udr_UDIInt(udrs, &NumberOfStacks); + for(cnt = 0; cnt < NumberOfStacks; cnt++) + udr_CPUSizeT(udrs, &StackSizes[cnt]); + udr_string(udrs, ArgString); + udr_sendnow(udrs); + if(udr_errno) return udr_errno; + + udrs->udr_op = UDR_DECODE; /* receive all "out" parameters */ + udr_UDIError(udrs, &dfe_errno); /* get any TIP error */ + return dfe_errno; +} + +/********************************************************* UDI_DESTROY_PROCESS +* UDIDestroyProcess() frees a process resource +* previously created by UDICreateProcess(). +*/ +UDIError UDIDestroyProcess(pid) +UDIPId pid; /* in */ +{ + UDIInt32 service_id = UDIDestroyProcess_c; + + udr_errno = 0; + udrs->udr_op = UDR_ENCODE; /* send all "in" parameters */ + udr_UDIInt32(udrs, &service_id); + udr_UDIPId(udrs, &pid); + udr_sendnow(udrs); + if(udr_errno) return udr_errno; + + udrs->udr_op = UDR_DECODE; /* receive all "out" parameters */ + udr_UDIError(udrs, &dfe_errno); /* get any TIP error */ + return dfe_errno; +} + +/****************************************************************** UDI_READ +* UDIRead() reads a block of objects from a target +* address space to host space. +*/ + +UDIError UDIRead (from, to, count, size, count_done, host_endian) +UDIResource from; /* in - source address on target */ +UDIHostMemPtr to; /* out - destination address on host */ +UDICount count; /* in -- count of objects to be transferred */ +UDISizeT size; /* in -- size of each object */ +UDICount *count_done; /* out - count actually transferred */ +UDIBool host_endian; /* in -- flag for endian information */ +{ + UDIInt32 service_id = UDIRead_c; + int byte_count; + + udr_errno = 0; + udrs->udr_op = UDR_ENCODE; /* send all "in" parameters */ + udr_UDIInt32(udrs, &service_id); + udr_UDIResource(udrs, &from); + udr_UDICount(udrs, &count); + udr_UDISizeT(udrs, &size); + udr_UDIBool(udrs, &host_endian); + udr_sendnow(udrs); + if(udr_errno) return udr_errno; + + udrs->udr_op = UDR_DECODE; /* receive all "out" paramters */ + udr_UDICount(udrs, count_done); + byte_count = (*count_done) * size; + if(*count_done > 0 && *count_done <= count) + udr_bytes(udrs, to, byte_count); + if(udr_errno) return udr_errno; + udr_UDIError(udrs, &dfe_errno); /* get any TIP error */ + return dfe_errno; +} + +/****************************************************************** UDI_WRITE +* UDIWrite() writes a block of objects from host +* space to a target address+space. +*/ +UDIError UDIWrite( from, to, count, size, count_done, host_endian ) +UDIHostMemPtr from; /* in -- source address on host */ +UDIResource to; /* in -- destination address on target */ +UDICount count; /* in -- count of objects to be transferred */ +UDISizeT size; /* in -- size of each object */ +UDICount *count_done; /* out - count actually transferred */ +UDIBool host_endian; /* in -- flag for endian information */ +{ + UDIInt32 service_id = UDIWrite_c; + int byte_count = count * size; + + udr_errno = 0; + udrs->udr_op = UDR_ENCODE; /* send all "in" parameters */ + udr_UDIInt32(udrs, &service_id); + udr_UDIResource(udrs, &to); + udr_UDICount(udrs, &count); + udr_UDISizeT(udrs, &size); + udr_UDIBool(udrs, &host_endian); + udr_bytes(udrs, from, byte_count); + udr_sendnow(udrs); + if(udr_errno) return udr_errno; + + udrs->udr_op = UDR_DECODE; /* receive all "out" paramters */ + udr_UDICount(udrs, count_done); + udr_UDIError(udrs, &dfe_errno); /* get any TIP error */ + return dfe_errno; +} + +/******************************************************************** UDI_COPY +* UDICopy() copies a block of objects from one target +* get address/space to another target address/space. +*/ +UDIError UDICopy(from, to, count, size, count_done, direction ) +UDIResource from; /* in -- destination address on target */ +UDIResource to; /* in -- source address on target */ +UDICount count; /* in -- count of objects to be transferred */ +UDISizeT size; /* in -- size of each object */ +UDICount *count_done; /* out - count actually transferred */ +UDIBool direction; /* in -- high-to-low or reverse */ +{ + UDIInt32 service_id = UDICopy_c; + + udr_errno = 0; + udrs->udr_op = UDR_ENCODE; /* send all "in" parameters */ + udr_UDIInt32(udrs, &service_id); + udr_UDIResource(udrs, &from); + udr_UDIResource(udrs, &to); + udr_UDICount(udrs, &count); + udr_UDISizeT(udrs, &size); + udr_UDIBool(udrs, &direction); + udr_sendnow(udrs); + if(udr_errno) return udr_errno; + + udrs->udr_op = UDR_DECODE; /* receive all "out" parameters */ + udr_UDICount(udrs, count_done); + udr_UDIError(udrs, &dfe_errno); /* get any TIP error */ + return dfe_errno; +} + +/***************************************************************** UDI_EXECUTE +* UDIExecute() continues execution of the default +* process from the current PC. +*/ +UDIError UDIExecute() +{ + UDIInt32 service_id = UDIExecute_c; + + udr_errno = 0; + udrs->udr_op = UDR_ENCODE; /* send all "in" parameters */ + udr_UDIInt32(udrs, &service_id); + udr_sendnow(udrs); + if(udr_errno) return udr_errno; + + udrs->udr_op = UDR_DECODE; /* receive all "out" parameters */ + udr_UDIError(udrs, &dfe_errno); /* get any TIP error */ + return dfe_errno; +} + +/******************************************************************** UDI_STEP +* UDIStep() specifies a number of "instruction" +* steps to make. +*/ +UDIError UDIStep(steps, steptype, range) +UDIUInt32 steps; /* in -- number of steps */ +UDIStepType steptype; /* in -- type of stepping to be done */ +UDIRange range; /* in -- range if StepInRange is TRUE */ +{ + UDIInt32 service_id = UDIStep_c; + + udr_errno = 0; + udrs->udr_op = UDR_ENCODE; /* send all "in" parameters */ + udr_UDIInt32(udrs, &service_id); + udr_UDIInt32(udrs, &steps); + udr_UDIStepType(udrs, &steptype); + udr_UDIRange(udrs, &range); + udr_sendnow(udrs); + if(udr_errno) return udr_errno; + + udrs->udr_op = UDR_DECODE; /* receive all "out" parameters */ + udr_UDIError(udrs, &dfe_errno); /* get any TIP error */ + return dfe_errno; +} + +/******************************************************************** UDI_STOP +* UDIStop() stops the default process +*/ +UDIVoid UDIStop() +{ + if (strcmp(session[current].soc_con_p->domain_string, "AF_UNIX") == 0) + kill(session[current].soc_con_p->tip_pid, SIGINT); + else + udr_signal(udrs); + +/* XXX - should clean up session[] and soc_con[] structs here as well... */ + + return; +} + +/******************************************************************** UDI_WAIT +* UDIWait() returns the state of the target procesor. +*/ +UDIError UDIWait(maxtime, pid, stop_reason) +UDIInt32 maxtime; /* in -- maximum time to wait for completion */ +UDIPId *pid; /* out -- pid of process which stopped if any */ +UDIUInt32 *stop_reason; /* out -- PC where process stopped */ +{ + UDIInt32 service_id = UDIWait_c; + + udr_errno = 0; + udrs->udr_op = UDR_ENCODE; /* send all "in" parameters */ + udr_UDIInt32(udrs, &service_id); + udr_UDIInt32(udrs, &maxtime); + udr_sendnow(udrs); + if(udr_errno) return udr_errno; + + udrs->udr_op = UDR_DECODE; /* receive all "out" parameters */ + udr_UDIPId(udrs, pid); + udr_UDIUInt32(udrs, stop_reason); + udr_UDIError(udrs, &dfe_errno); /* get any TIP error */ + return dfe_errno; +} + +/********************************************************** UDI_SET_BREAKPOINT +* UDISetBreakpoint() sets a breakpoint at an adress +* and uses the passcount to state how many +* times that instruction should be hit before the +* break occurs. +*/ +UDIError UDISetBreakpoint (addr, passcount, type, break_id) +UDIResource addr; /* in -- where breakpoint gets set */ +UDIInt32 passcount; /* in -- passcount for breakpoint */ +UDIBreakType type; /* in -- breakpoint type */ +UDIBreakId *break_id; /* out - assigned break id */ +{ + UDIInt32 service_id = UDISetBreakpoint_c; + + udr_errno = 0; + udrs->udr_op = UDR_ENCODE; /* send all "in" parameters */ + udr_UDIInt32(udrs, &service_id); + udr_UDIResource(udrs, &addr); + udr_UDIInt32(udrs, &passcount); + udr_UDIBreakType(udrs, &type); + udr_sendnow(udrs); + if(udr_errno) return udr_errno; + + udrs->udr_op = UDR_DECODE; /* receive all "out" parameters */ + udr_UDIBreakId(udrs, break_id); + udr_UDIError(udrs, &dfe_errno); /* get any TIP error */ + return dfe_errno; +} + +/******************************************************** UDI_QUERY_BREAKPOINT +*/ +UDIError UDIQueryBreakpoint (break_id, addr, passcount, type, current_count) +UDIBreakId break_id; /* in -- assigned break id */ +UDIResource *addr; /* out - where breakpoint was set */ +UDIInt32 *passcount; /* out - trigger passcount for breakpoint */ +UDIBreakType *type; /* out - breakpoint type */ +UDIInt32 *current_count; /* out - current count for breakpoint */ +{ + UDIInt32 service_id = UDIQueryBreakpoint_c; + + udr_errno = 0; + udrs->udr_op = UDR_ENCODE; /* send all "in" parameters */ + udr_UDIInt32(udrs, &service_id); + udr_UDIBreakId(udrs, &break_id); + udr_sendnow(udrs); + if(udr_errno) return udr_errno; + + udrs->udr_op = UDR_DECODE; /* receive all "out" parameters */ + udr_UDIResource(udrs, addr); + udr_UDIInt32(udrs, passcount); + udr_UDIBreakType(udrs, type); + udr_UDIInt32(udrs, current_count); + udr_UDIError(udrs, &dfe_errno); /* get any TIP error */ + return dfe_errno; +} + +/******************************************************** UDI_CLEAR_BREAKPOINT +* UDIClearBreakpoint() is used to clear a breakpoint. +*/ +UDIError UDIClearBreakpoint (break_id) +UDIBreakId break_id; /* in -- assigned break id */ +{ + UDIInt32 service_id = UDIClearBreakpoint_c; + + udr_errno = 0; + udrs->udr_op = UDR_ENCODE; /* send all "in" parameters */ + udr_UDIInt32(udrs, &service_id); + udr_UDIBreakId(udrs, &break_id); + udr_sendnow(udrs); + if(udr_errno) return udr_errno; + + udrs->udr_op = UDR_DECODE; /* receive all "out" parameters */ + udr_UDIError(udrs, &dfe_errno); /* get any TIP error */ + return dfe_errno; +} + +/************************************************************** UDI_GET_STDOUT +* UDIGetStdout() is called when a call to +* UDIWait() indicates there is STD output data ready. +*/ +UDIError UDIGetStdout(buf, bufsize, count_done) +UDIHostMemPtr buf; /* out -- buffer to be filled */ +UDISizeT bufsize; /* in -- buffer size in bytes */ +UDISizeT *count_done; /* out -- number of bytes written to buf */ +{ + UDIInt32 service_id = UDIGetStdout_c; + + udr_errno = 0; + udrs->udr_op = UDR_ENCODE; /* send all "in" parameters */ + udr_UDIInt32(udrs, &service_id); + udr_UDISizeT(udrs, &bufsize); + udr_sendnow(udrs); + if(udr_errno) return udr_errno; + + udrs->udr_op = UDR_DECODE; /* receive all "out" parameters */ + udr_UDISizeT(udrs, count_done); + udr_bytes(udrs, buf, *count_done); + udr_UDIError(udrs, &dfe_errno); /* get any TIP error */ + return dfe_errno; +} + +/************************************************************** UDI_GET_STDERR +* UDIGetStderr() is called when a call to +* UDIWait() indicates there is STDERR output data ready +*/ +UDIError UDIGetStderr(buf, bufsize, count_done) +UDIHostMemPtr buf; /* out -- buffer to be filled */ +UDISizeT bufsize; /* in -- buffer size in bytes */ +UDISizeT *count_done; /* out -- number of bytes written to buf */ +{ + UDIInt32 service_id = UDIGetStderr_c; + + udr_errno = 0; + udrs->udr_op = UDR_ENCODE; /* send all "in" parameters */ + udr_UDIInt32(udrs, &service_id); + udr_UDISizeT(udrs, &bufsize); + udr_sendnow(udrs); + if(udr_errno) return udr_errno; + + udrs->udr_op = UDR_DECODE; /* receive all "out" parameters */ + udr_UDISizeT(udrs, count_done); + udr_bytes(udrs, buf, *count_done); + udr_UDIError(udrs, &dfe_errno); /* get any TIP error */ + return dfe_errno; +} + +/*************************************************************** UDI_PUT_STDIN +* UDIPutStdin() is called whenever the DFE wants to +* deliver an input character to the TIP. +*/ +UDIError UDIPutStdin (buf, count, count_done) +UDIHostMemPtr buf; /* in -- buffer to be filled */ +UDISizeT count; /* in -- buffer size in bytes */ +UDISizeT *count_done; /* out - number of bytes written to buf */ +{ + UDIInt32 service_id = UDIPutStdin_c; + + udr_errno = 0; + udrs->udr_op = UDR_ENCODE; /* send all "in" parameters */ + udr_UDIInt32(udrs, &service_id); + udr_UDISizeT(udrs, &count); + udr_bytes(udrs, buf, count); + udr_sendnow(udrs); + if(udr_errno) return udr_errno; + + udrs->udr_op = UDR_DECODE; /* receive all "out" parameters */ + udr_UDISizeT(udrs, count_done); + udr_UDIError(udrs, &dfe_errno); /* get any TIP error */ + return dfe_errno; +} + +/************************************************************** UDI_STDIN_MODE +* UDIStdinMode() is used to change the mode that chazcters +* are fetched from the user. +*/ +UDIError UDIStdinMode(mode) +UDIMode *mode; /* out - */ +{ + UDIInt32 service_id = UDIStdinMode_c; + + udr_errno = 0; + udrs->udr_op = UDR_ENCODE; /* send all "in" parameters */ + udr_UDIInt32(udrs, &service_id); + udr_sendnow(udrs); + if(udr_errno) return udr_errno; + + udrs->udr_op = UDR_DECODE; /* receive all "out" parameters */ + udr_UDIMode(udrs, mode); + udr_UDIError(udrs, &dfe_errno); /* get any TIP error */ + return dfe_errno; +} + +/*************************************************************** UDI_PUT_TRANS +* UDIPutTrans() is used to feed input to the passthru mode. +*/ +UDIError UDIPutTrans (buf, count, count_done) +UDIHostMemPtr buf; /* in -- buffer address containing input data */ +UDISizeT count; /* in -- number of bytes in buf */ +UDISizeT *count_done; /* out-- number of bytes transfered */ +{ + UDIInt32 service_id = UDIPutTrans_c; + + udr_errno = 0; + udrs->udr_op = UDR_ENCODE; /* send all "in" parameters */ + udr_UDIInt32(udrs, &service_id); + udr_UDISizeT(udrs, &count); + udr_bytes(udrs, buf, count); + udr_sendnow(udrs); + if(udr_errno) return udr_errno; + + udrs->udr_op = UDR_DECODE; /* receive all "out" parameters */ + udr_UDISizeT(udrs, count_done); + udr_UDIError(udrs, &dfe_errno); /* get any TIP error */ + return dfe_errno; +} + +/*************************************************************** UDI_GET_TRANS +* UDIGetTrans() is used to get output lines from the +* passthru mode. +*/ +UDIError UDIGetTrans (buf, bufsize, count_done) +UDIHostMemPtr buf; /* out -- buffer to be filled */ +UDISizeT bufsize; /* in -- size of buf */ +UDISizeT *count_done; /* out -- number of bytes in buf */ +{ + UDIInt32 service_id = UDIGetTrans_c; + + udr_errno = 0; + udrs->udr_op = UDR_ENCODE; /* send all "in" parameters */ + udr_UDIInt32(udrs, &service_id); + udr_UDISizeT(udrs, &bufsize); + udr_sendnow(udrs); + if(udr_errno) return udr_errno; + + udrs->udr_op = UDR_DECODE; /* receive all "out" parameters */ + udr_UDISizeT(udrs, count_done); + udr_bytes(udrs, buf, *count_done); + udr_UDIError(udrs, &dfe_errno); /* get any TIP error */ + return dfe_errno; +} + +/************************************************************** UDI_Trans_Mode +* UDITransMode() is used to change the mode that the +* transparent routines operate in. +*/ +UDIError UDITransMode(mode) +UDIMode *mode; /* out -- selected mode */ +{ + UDIInt32 service_id = UDITransMode_c; + + udr_errno = 0; + udrs->udr_op = UDR_ENCODE; /* send all "in" parameters */ + udr_UDIInt32(udrs, &service_id); + udr_UDIMode(udrs, mode); + udr_sendnow(udrs); + if(udr_errno) return udr_errno; + + udrs->udr_op = UDR_DECODE; /* receive all "out" parameters */ + udr_UDIError(udrs, &dfe_errno); + return dfe_errno; +} + +/******************************************************************** UDI_TEST +*/ +UDIError UDITest( cnt, str_p, array) +UDISizeT cnt; +UDIHostMemPtr str_p; +UDIInt32 array[]; +{ + UDIInt32 service_id = UDITest_c; + UDIInt16 scnt = cnt; + UDISizeT r_cnt; + char buf[256]; + + udr_errno = 0; + udrs->udr_op = UDR_ENCODE; /* send all "in" parameters */ + udr_UDIInt32(udrs, &service_id); + + printf("send cnt=%d scnt=%d\n", cnt, scnt); + udr_UDISizeT(udrs, &cnt); + udr_UDIInt16(udrs, &scnt); + printf(" array[0]=0x%x array[1]=0x%x array[2]=0x%x array[3]=0x%x\n", + array[0], array[1], array[2], array[3]); + udr_bytes(udrs, (char*)array, 4*sizeof(UDIInt32)); + printf(" string=%s\n", str_p); + udr_string(udrs, str_p); + udr_sendnow(udrs); + if(udr_errno) + { fprintf(stderr, " DFE-ipc Send ERROR\n"); + return udr_errno; + } + + udrs->udr_op = UDR_DECODE; /* receive all "out" parameters */ + printf("recv "); + udr_UDISizeT(udrs, &r_cnt); + udr_UDIInt16(udrs, &scnt); + printf(" rcnt=%d scnt=%d\n", r_cnt, scnt); + udr_bytes(udrs, (char*)array, 4*sizeof(UDIInt32)); + + printf(" array[0]=0x%x array[1]=0x%x array[2]=0x%x array[3]=0x%x\n", + array[0], array[1], array[2], array[3]); + udr_string(udrs, str_p); + printf(" string=%s\n", str_p); + + udr_UDIError(udrs, &dfe_errno); + return dfe_errno; +} + + + +UDIUInt32 UDIGetDFEIPCId() +{ + return ((company_c << 16) + (product_c << 12) + version_c); +} +#endif /* __GO32__ */ diff --git a/contrib/gdb/gdb/29k-share/udi/udiphcfg.h b/contrib/gdb/gdb/29k-share/udi/udiphcfg.h new file mode 100644 index 000000000000..e9eff0a3c867 --- /dev/null +++ b/contrib/gdb/gdb/29k-share/udi/udiphcfg.h @@ -0,0 +1,44 @@ +/* This file just picks the correct udiphxxx.h depending on the host. + The two hosts that are now defined are UNIX and MSDOS. + + Copyright 1993 Free Software Foundation, Inc. + + This file is part of GDB. + + This program 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 of the License, or + (at your option) any later version. + + This program 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; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* + * For the moment, we will default to BSD_IPC; this might change if/when + * another type of IPC (Mach? SysV?) is implemented. + */ + +#if 0 + +/* We don't seem to have a copy of udiphdos.h. Furthermore, all the + things in udiphunix.h are pretty much generic 32-bit machine defines + which don't have anything to do with IPC. */ + +#ifdef DOS_IPC +#include "udiphdos.h" +#else +/*#ifdef BSD_IPC */ +#include "udiphunix.h" +#endif + +#else + +#include "udiphunix.h" + +#endif diff --git a/contrib/gdb/gdb/29k-share/udi/udiphunix.h b/contrib/gdb/gdb/29k-share/udi/udiphunix.h new file mode 100644 index 000000000000..172fbbfd85bc --- /dev/null +++ b/contrib/gdb/gdb/29k-share/udi/udiphunix.h @@ -0,0 +1,81 @@ +/* Originally called "udiphsun.h", however it was not very + Sun-specific; now it is used for generic-unix-with-bsd-ipc. + + Copyright 1993 Free Software Foundation, Inc. + + This file is part of GDB. + + This program 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 of the License, or + (at your option) any later version. + + This program 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; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* This file is to be used to reconfigure the UDI Procedural interface + for a given host. This file should be placed so that it will be + included from udiproc.h. Everything in here may need to be changed + when you change either the host CPU or its compiler. Nothing in + here should change to support different targets. There are multiple + versions of this file, one for each of the different host/compiler + combinations in use. +*/ + +#define UDIStruct struct /* _packed not needed on unix */ +/* First, we need some types */ +/* Types with at least the specified number of bits */ +typedef double UDIReal64; /* 64-bit real value */ +typedef float UDIReal32; /* 32-bit real value */ + +typedef unsigned long UDIUInt32; /* unsigned integers */ +typedef unsigned short UDIUInt16; +typedef unsigned char UDIUInt8; + +typedef long UDIInt32; /* 32-bit integer */ +typedef short UDIInt16; /* 16-bit integer */ +typedef char UDIInt8; /* unreliable signedness */ + +/* To aid in supporting environments where the DFE and TIP use +different compilers or hosts (like DOS 386 on one side, 286 on the +other, or different Unix machines connected by sockets), we define +two abstract types - UDIInt and UDISizeT. +UDIInt should be defined to be int except for host/compiler combinations +that are intended to talk to existing UDI components that have a different +sized int. Similarly for UDISizeT. +*/ +typedef int UDIInt; +typedef unsigned int UDIUInt; + +typedef unsigned int UDISizeT; + +/* Now two void types. The first is for function return types, +the other for pointers to no particular type. Since these types +are used solely for documentational clarity, if your host/compiler +doesn't support either one, replace them with int and char * +respectively. +*/ +typedef void UDIVoid; /* void type */ +typedef void * UDIVoidPtr; /* void pointer type */ +typedef void * UDIHostMemPtr; /* Arbitrary memory pointer */ + +/* Now we want a type optimized for boolean values. Normally this + would be int, but on some machines (Z80s, 8051s, etc) it might + be better to map it onto a char +*/ +typedef int UDIBool; + +/* Now indicate whether your compiler support full ANSI style + prototypes. If so, use #if 1. If not use #if 0. +*/ +#if 0 +#define UDIParams(x) x +#else +#define UDIParams(x) () +#endif diff --git a/contrib/gdb/gdb/29k-share/udi/udiproc.h b/contrib/gdb/gdb/29k-share/udi/udiproc.h new file mode 100644 index 000000000000..0cc1c2049c1d --- /dev/null +++ b/contrib/gdb/gdb/29k-share/udi/udiproc.h @@ -0,0 +1,308 @@ +/* local type decs. and macro defs. + + Copyright 1993 Free Software Foundation, Inc. + + This file is part of GDB. + + This program 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 of the License, or + (at your option) any later version. + + This program 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; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "udiphcfg.h" /* Get host specific configuration */ +#include "udiptcfg.h" /* Get target specific configuration */ + +/* Here are all of the CPU Families for which UDI is currently defined */ +#define Am29K 1 /* AMD's Am290xx and Am292xx parts */ + +typedef UDIInt UDIError; +typedef UDIInt UDISessionId; +typedef UDIInt UDIPId; +typedef UDIInt UDIStepType; +typedef UDIInt UDIBreakType; +typedef UDIUInt UDIBreakId; +typedef UDIUInt UDIMode; + +typedef UDIStruct +{ + CPUSpace Space; + CPUOffset Offset; +} UDIResource; + +typedef UDIStruct +{ + CPUOffset Low; + CPUOffset High; +} UDIRange; + +typedef UDIStruct +{ + CPUSpace Space; + CPUOffset Offset; + CPUSizeT Size; + } UDIMemoryRange; + +/* Values for UDIStepType parameters */ +#define UDIStepNatural 0x0000 +#define UDIStepOverTraps 0x0001 +#define UDIStepOverCalls 0x0002 +#define UDIStepInRange 0x0004 +#define UDIStepNatural 0x0000 + +/* Values for UDIBreakType parameters */ +#define UDIBreakFlagExecute 0x0001 +#define UDIBreakFlagRead 0x0002 +#define UDIBreakFlagWrite 0x0004 +#define UDIBreakFlagFetch 0x0008 + +/* Special values for UDIWait MaxTime parameter */ +#define UDIWaitForever (UDIInt32) -1 /* Infinite time delay */ + +/* Special values for PId */ +#define UDIProcessProcessor -1 /* Raw Hardware, if possible */ + +/* Values for UDIWait StopReason */ +#define UDIGrossState 0xff +#define UDITrapped 0 /* Fine state - which trap */ +#define UDINotExecuting 1 +#define UDIRunning 2 +#define UDIStopped 3 +#define UDIWarned 4 +#define UDIStepped 5 +#define UDIWaiting 6 +#define UDIHalted 7 +#define UDIStdoutReady 8 /* fine state - size */ +#define UDIStderrReady 9 /* fine state - size */ +#define UDIStdinNeeded 10 /* fine state - size */ +#define UDIStdinModeX 11 /* fine state - mode */ +#define UDIBreak 12 /* Fine state - Breakpoint Id */ +#define UDIExited 13 /* Fine state - exit code */ + +/* Enumerate the return values from the callback function + for UDIEnumerateTIPs. +*/ +#define UDITerminateEnumeration 0 +#define UDIContinueEnumeration 1 + +/* Enumerate values for Terminate parameter to UDIDisconnect */ +#define UDITerminateSession 1 +#define UDIContinueSession 0 + +/* Error codes */ +#define UDINoError 0 /* No error occured */ +#define UDIErrorNoSuchConfiguration 1 +#define UDIErrorCantHappen 2 +#define UDIErrorCantConnect 3 +#define UDIErrorNoSuchConnection 4 +#define UDIErrorNoConnection 5 +#define UDIErrorCantOpenConfigFile 6 +#define UDIErrorCantStartTIP 7 +#define UDIErrorConnectionUnavailable 8 +#define UDIErrorTryAnotherTIP 9 +#define UDIErrorExecutableNotTIP 10 +#define UDIErrorInvalidTIPOption 11 +#define UDIErrorCantDisconnect 12 +#define UDIErrorUnknownError 13 +#define UDIErrorCantCreateProcess 14 +#define UDIErrorNoSuchProcess 15 +#define UDIErrorUnknownResourceSpace 16 +#define UDIErrorInvalidResource 17 +#define UDIErrorUnsupportedStepType 18 +#define UDIErrorCantSetBreakpoint 19 +#define UDIErrorTooManyBreakpoints 20 +#define UDIErrorInvalidBreakId 21 +#define UDIErrorNoMoreBreakIds 22 +#define UDIErrorUnsupportedService 23 +#define UDIErrorTryAgain 24 +#define UDIErrorIPCLimitation 25 +#define UDIErrorIncomplete 26 +#define UDIErrorAborted 27 +#define UDIErrorTransDone 28 +#define UDIErrorCantAccept 29 +#define UDIErrorTransInputNeeded 30 +#define UDIErrorTransModeX 31 +#define UDIErrorInvalidSize 32 +#define UDIErrorBadConfigFileEntry 33 +#define UDIErrorIPCInternal 34 +/* TBD */ + +/****************************************************************** PROCEDURES +*/ + +UDIError UDIConnect UDIParams(( + char *Configuration, /* In */ + UDISessionId *Session /* Out */ + )); + +UDIError UDIDisconnect UDIParams(( + UDISessionId Session, /* In */ + UDIBool Terminate /* In */ + )); + +UDIError UDISetCurrentConnection UDIParams(( + UDISessionId Session /* In */ + )); + +UDIError UDICapabilities UDIParams(( + UDIUInt32 *TIPId, /* Out */ + UDIUInt32 *TargetId, /* Out */ + UDIUInt32 DFEId, /* In */ + UDIUInt32 DFE, /* In */ + UDIUInt32 *TIP, /* Out */ + UDIUInt32 *DFEIPCId, /* Out */ + UDIUInt32 *TIPIPCId, /* Out */ + char *TIPString /* Out */ + )); + +UDIError UDIEnumerateTIPs UDIParams(( + UDIInt (*UDIETCallback) /* In */ + UDIParams(( char *Configuration )) /* In to callback() */ + )); + +UDIError UDIGetErrorMsg UDIParams(( + UDIError ErrorCode, /* In */ + UDISizeT MsgSize, /* In */ + char *Msg, /* Out */ + UDISizeT *CountDone /* Out */ + )); + +UDIError UDIGetTargetConfig UDIParams(( + UDIMemoryRange KnownMemory[], /* Out */ + UDIInt *NumberOfRanges, /* In/Out */ + UDIUInt32 ChipVersions[], /* Out */ + UDIInt *NumberOfChips /* In/Out */ + )); + +UDIError UDICreateProcess UDIParams(( + UDIPId *PId /* Out */ + )); + +UDIError UDISetCurrentProcess UDIParams(( + UDIPId PId /* In */ + )); + +UDIError UDIDestroyProcess UDIParams(( + UDIPId PId /* In */ + )); + +UDIError UDIInitializeProcess UDIParams(( + UDIMemoryRange ProcessMemory[], /* In */ + UDIInt NumberOfRanges, /* In */ + UDIResource EntryPoint, /* In */ + CPUSizeT StackSizes[], /* In */ + UDIInt NumberOfStacks, /* In */ + char *ArgString /* In */ + )); + +UDIError UDIRead UDIParams(( + UDIResource From, /* In */ + UDIHostMemPtr To, /* Out */ + UDICount Count, /* In */ + UDISizeT Size, /* In */ + UDICount *CountDone, /* Out */ + UDIBool HostEndian /* In */ + )); + +UDIError UDIWrite UDIParams(( + UDIHostMemPtr From, /* In */ + UDIResource To, /* In */ + UDICount Count, /* In */ + UDISizeT Size, /* In */ + UDICount *CountDone, /* Out */ + UDIBool HostEndian /* In */ + )); + +UDIError UDICopy UDIParams(( + UDIResource From, /* In */ + UDIResource To, /* In */ + UDICount Count, /* In */ + UDISizeT Size, /* In */ + UDICount *CountDone, /* Out */ + UDIBool Direction /* In */ + )); + +UDIError UDIExecute UDIParams(( + void + )); + +UDIError UDIStep UDIParams(( + UDIUInt32 Steps, /* In */ + UDIStepType StepType, /* In */ + UDIRange Range /* In */ + )); + +UDIVoid UDIStop UDIParams(( + void + )); + +UDIError UDIWait UDIParams(( + UDIInt32 MaxTime, /* In */ + UDIPId *PId, /* Out */ + UDIUInt32 *StopReason /* Out */ + )); + +UDIError UDISetBreakpoint UDIParams(( + UDIResource Addr, /* In */ + UDIInt32 PassCount, /* In */ + UDIBreakType Type, /* In */ + UDIBreakId *BreakId /* Out */ + )); + +UDIError UDIQueryBreakpoint UDIParams(( + UDIBreakId BreakId, /* In */ + UDIResource *Addr, /* Out */ + UDIInt32 *PassCount, /* Out */ + UDIBreakType *Type, /* Out */ + UDIInt32 *CurrentCount /* Out */ + )); + +UDIError UDIClearBreakpoint UDIParams(( + UDIBreakId BreakId /* In */ + )); + +UDIError UDIGetStdout UDIParams(( + UDIHostMemPtr Buf, /* Out */ + UDISizeT BufSize, /* In */ + UDISizeT *CountDone /* Out */ + )); + +UDIError UDIGetStderr UDIParams(( + UDIHostMemPtr Buf, /* Out */ + UDISizeT BufSize, /* In */ + UDISizeT *CountDone /* Out */ + )); + +UDIError UDIPutStdin UDIParams(( + UDIHostMemPtr Buf, /* In */ + UDISizeT Count, /* In */ + UDISizeT *CountDone /* Out */ + )); + +UDIError UDIStdinMode UDIParams(( + UDIMode *Mode /* Out */ + )); + +UDIError UDIPutTrans UDIParams(( + UDIHostMemPtr Buf, /* In */ + UDISizeT Count, /* In */ + UDISizeT *CountDone /* Out */ + )); + +UDIError UDIGetTrans UDIParams(( + UDIHostMemPtr Buf, /* Out */ + UDISizeT BufSize, /* In */ + UDISizeT *CountDone /* Out */ + )); + +UDIError UDITransMode UDIParams(( + UDIMode *Mode /* Out */ + )); diff --git a/contrib/gdb/gdb/29k-share/udi/udipt29k.h b/contrib/gdb/gdb/29k-share/udi/udipt29k.h new file mode 100644 index 000000000000..5de2f3ff860f --- /dev/null +++ b/contrib/gdb/gdb/29k-share/udi/udipt29k.h @@ -0,0 +1,87 @@ +/* This file is to be used to reconfigure the UDI Procedural interface + for a given target. + + Copyright 1993 Free Software Foundation, Inc. + + This file is part of GDB. + + This program 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 of the License, or + (at your option) any later version. + + This program 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; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* This file should be placed so that it will be + included from udiproc.h. Everything in here will probably need to + be changed when you change the target processor. Nothing in here + should need to change when you change hosts or compilers. +*/ + +/* Select a target CPU Family */ +#define TargetCPUFamily Am29K + +/* Enumerate the processor specific values for Space in a resource */ +#define UDI29KDRAMSpace 0 +#define UDI29KIOSpace 1 +#define UDI29KCPSpace0 2 +#define UDI29KCPSpace1 3 +#define UDI29KIROMSpace 4 +#define UDI29KIRAMSpace 5 +#define UDI29KLocalRegs 8 +#define UDI29KGlobalRegs 9 +#define UDI29KRealRegs 10 +#define UDI29KSpecialRegs 11 +#define UDI29KTLBRegs 12 /* Not Am29005 */ +#define UDI29KACCRegs 13 /* Am29050 only */ +#define UDI29KICacheSpace 14 /* Am2903x only */ +#define UDI29KAm29027Regs 15 /* When available */ +#define UDI29KPC 16 +#define UDI29KDCacheSpace 17 /* When available */ + +/* Enumerate the Co-processor registers */ +#define UDI29KCP_F 0 +#define UDI29KCP_Flag 8 +#define UDI29KCP_I 12 +#define UDI29KCP_ITmp 16 +#define UDI29KCP_R 20 +#define UDI29KCP_S 28 +#define UDI29KCP_RTmp 36 +#define UDI29KCP_STmp 44 +#define UDI29KCP_Stat 52 +#define UDI29KCP_Prec 56 +#define UDI29KCP_Reg0 60 +#define UDI29KCP_Reg1 68 +#define UDI29KCP_Reg2 76 +#define UDI29KCP_Reg3 84 +#define UDI29KCP_Reg4 92 +#define UDI29KCP_Reg5 100 +#define UDI29KCP_Reg6 108 +#define UDI29KCP_Reg7 116 +#define UDI29KCP_Mode 124 + +/* Enumerate the stacks in StackSizes array */ +#define UDI29KMemoryStack 0 +#define UDI29KRegisterStack 1 + +/* Enumerate the chips for ChipVersions array */ +#define UDI29K29KVersion 0 +#define UDI29K29027Version 1 + +/* Define special value for elements of ChipVersions array for + * chips not present */ +#define UDI29KChipNotPresent -1 + +typedef UDIInt32 UDICount; +typedef UDIUInt32 UDISize; + +typedef UDIInt CPUSpace; +typedef UDIUInt32 CPUOffset; +typedef UDIUInt32 CPUSizeT; diff --git a/contrib/gdb/gdb/29k-share/udi/udiptcfg.h b/contrib/gdb/gdb/29k-share/udi/udiptcfg.h new file mode 100644 index 000000000000..1641a53bcd42 --- /dev/null +++ b/contrib/gdb/gdb/29k-share/udi/udiptcfg.h @@ -0,0 +1,19 @@ +/* Copyright 1993 Free Software Foundation, Inc. + + This file is part of GDB. + + This program 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 of the License, or + (at your option) any later version. + + This program 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; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "udipt29k.h" diff --git a/contrib/gdb/gdb/29k-share/udi/udisoc.h b/contrib/gdb/gdb/29k-share/udi/udisoc.h new file mode 100644 index 000000000000..bc68b3944eca --- /dev/null +++ b/contrib/gdb/gdb/29k-share/udi/udisoc.h @@ -0,0 +1,184 @@ +/* This module defines constants used in the UDI IPC modules. + + Copyright 1993 Free Software Foundation, Inc. + + This file is part of GDB. + + This program 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 of the License, or + (at your option) any later version. + + This program 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; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +static char udisoc_h[]="@(#)udisoc.h 2.6 Daniel Mann"; +static char udisoc_h_AMD[]="@(#)udisoc.h 2.4, AMD"; + +#define LOCAL static +#define company_c 1 /* AMD Company id */ +#define product_c 1 /* socket IPC id */ + +/* Enumerate the UDI procedure services +*/ +#define UDIConnect_c 0 +#define UDIDisconnect_c 1 +#define UDISetCurrentConnection_c 2 +#define UDICapabilities_c 3 +#define UDIEnumerateTIPs_c 4 +#define UDIGetErrorMsg_c 5 +#define UDIGetTargetConfig_c 6 +#define UDICreateProcess_c 7 +#define UDISetCurrentProcess_c 8 +#define UDIDestroyProcess_c 9 +#define UDIInitializeProcess_c 10 +#define UDIRead_c 11 +#define UDIWrite_c 12 +#define UDICopy_c 13 +#define UDIExecute_c 14 +#define UDIStep_c 15 +#define UDIStop_c 16 +#define UDIWait_c 17 +#define UDISetBreakpoint_c 18 +#define UDIQueryBreakpoint_c 19 +#define UDIClearBreakpoint_c 20 +#define UDIGetStdout_c 21 +#define UDIGetStderr_c 22 +#define UDIPutStdin_c 23 +#define UDIStdinMode_c 24 +#define UDIPutTrans_c 25 +#define UDIGetTrans_c 26 +#define UDITransMode_c 27 +#define UDITest_c 28 +#define UDIKill_c 29 + +#define udr_UDIInt8(udrs, obj) udr_work(udrs, obj, 1) +#define udr_UDIInt16(udrs, obj) udr_work(udrs, obj, 2) +#define udr_UDIInt32(udrs, obj) udr_work(udrs, obj, 4) +#define udr_UDIInt(udrs, obj) udr_work(udrs, obj, 4) + +#define udr_UDIUInt8(udrs, obj) udr_work(udrs, obj, 1) +#define udr_UDIUInt16(udrs, obj) udr_work(udrs, obj, 2) +#define udr_UDIUInt32(udrs, obj) udr_work(udrs, obj, 4) +#define udr_UDIUInt(udrs, obj) udr_work(udrs, obj, 4) + +#define udr_UDIBool(udrs, obj) udr_UDIInt32(udrs, obj) +#define udr_UDICount(udrs, obj) udr_UDIInt32(udrs, obj) +#define udr_UDISize(udrs, obj) udr_UDIUInt32(udrs, obj) +#define udr_CPUSpace(udrs, obj) udr_UDIInt32(udrs, obj) +#define udr_CPUOffset(udrs, obj) udr_UDIUInt32(udrs, obj) +#define udr_CPUSizeT(udrs, obj) udr_UDIUInt32(udrs, obj) +#define udr_UDIBreakId(udrs,obj) udr_UDIUInt(udrs, obj) +#define udr_UDISizeT(udrs, obj) udr_UDIUInt(udrs, obj) +#define udr_UDIMode(udrs, obj) udr_UDIUInt(udrs, obj) + +#define udr_UDIHostMemPtr(udrs, obj) udr_UDIUInt32(udrs, obj) +#define udr_UDIVoidPtr(udrs, obj) udr_UDIUInt32(udrs, obj) +#define udr_UDIPId(udrs, obj) udr_UDIUInt(udrs, obj) +#define udr_UDISessionId(udrs, obj) udr_UDIInt32(udrs, obj) +#define udr_UDIError(udrs, obj) udr_UDIInt32(udrs, obj) +#define udr_UDIStepType(udrs, obj) udr_UDIInt32(udrs, obj) +#define udr_UDIBreakType(udrs, obj) udr_UDIInt32(udrs, obj) + + +#define UDR_ENCODE 1 +#define UDR_DECODE 2 + +typedef struct UDR_str +{ + int udr_op; /* UDR operation */ + int previous_op; + int sd; + int bufsize; + char* buff; + char* getbytes; + char* putbytes; + char* putend; + int domain; + char* soc_name; +} UDR; + +/******************************************* Declare UDR suport functions */ +int udr_create UDIParams(( + UDR* udrs, + int sd, + int size + )); + +int udr_free UDIParams(( + UDR* udrs, + )); + +int udr_signal UDIParams(( + UDR* udrs, + )); + +int udr_sendnow UDIParams(( + UDR* udrs + )); + +int udr_work UDIParams(( + UDR* udrs, + void* object_p, + int size + )); + +int udr_UDIResource UDIParams(( + UDR* udrs, + UDIResource* object_p + )); + +int udr_UDIRange UDIParams(( + UDR* udrs, + UDIRange* object_p + )); + +int udr_UDIMemoryRange UDIParams(( + UDR* udrs, + UDIMemoryRange* object_p + )); + +int udr_UDIMemoryRange UDIParams(( + UDR* udrs, + UDIMemoryRange* object_p + )); + +int udr_int UDIParams(( + UDR* udrs, + int* int_p + )); + +int udr_bytes UDIParams(( + UDR* udrs, + char* ptr, + int len + )); + +char* udr_inline UDIParams(( + UDR* udrs, + int size + )); + +char* udr_getpos UDIParams(( + UDR* udrs + )); +int udr_setpos UDIParams(( + UDR* udrs, + char* pos + )); + +int udr_readnow UDIParams(( + UDR* udrs, + int size + )); + +int udr_align UDIParams(( + UDR* udrs, + int size, + )); diff --git a/contrib/gdb/gdb/29k-share/udi/udr.c b/contrib/gdb/gdb/29k-share/udi/udr.c new file mode 100644 index 000000000000..10a9f38c6ae3 --- /dev/null +++ b/contrib/gdb/gdb/29k-share/udi/udr.c @@ -0,0 +1,427 @@ +/* This module supports sending and receiving data objects over a + socket conection. + + Copyright 1993 Free Software Foundation, Inc. + + This file is part of GDB. + + This program 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 of the License, or + (at your option) any later version. + + This program 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; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +static char udr_c[]="@(#)udr.c 2.8 Daniel Mann"; +static char udr_c_AMD[]="@(#)udr.c 2.3, AMD"; +/* +* All data is serialised into a character stream, +* and de-serialised back into the approproiate objects. +********************************************************************** HISTORY +*/ +/* This is all unneeded on DOS machines. */ +#ifndef __GO32__ + +#include <stdio.h> +#include <sys/types.h> + +/* This used to say sys/fcntl.h, but the only systems I know of that + require that are old (pre-4.3, at least) BSD systems, which we + probably don't need to worry about. */ +#include <fcntl.h> + +#include <sys/socket.h> +#include "udiproc.h" +#include "udisoc.h" + +extern int errno; +extern char* malloc(); + +/* local type decs. and macro defs. not in a .h file ************* MACRO/TYPE +*/ + +/* global dec/defs. which are not in a .h file ************* EXPORT DEC/DEFS +*/ +int udr_errno; /* error occurs during UDR service */ + +/* local dec/defs. which are not in a .h file *************** LOCAL DEC/DEFS +*/ + +/****************************************************************** UDR_CREATE +* Build UDR structure for character stream processing. +*/ +int udr_create(udrs, sd, size) +UDR* udrs; +int sd; +int size; +{ + udrs->sd = sd; + if(!udrs->buff) udrs->buff = malloc(size); + udrs->getbytes = udrs->buff; /* set the buffer to the start */ + udrs->putbytes = udrs->buff; + udrs->putend = udrs->buff; + udrs->udr_op = -1; /* don't know the direction */ + udrs->previous_op = -1; /* don't know the direction */ + udrs->bufsize = size; + return 0; +} + +/******************************************************************** UDR_FREE +* Free USR structure and close socket. +*/ +int udr_free(udrs) +UDR* udrs; +{ + close(udrs->sd); + free(udrs->buff); + return 0; +} + +/****************************************************************** UDR_SIGNAL +* Send a signal to the process at the other end of the socket, +* indicating that it should expect to recieve a new message shortly. +*/ +int udr_signal(udrs) +UDR* udrs; +{ + if(send(udrs->sd, "I", 1, MSG_OOB) == -1) + { perror("ERROR, udr_signal(), send(...MSG_OOB)"); + udr_errno = UDIErrorIPCInternal; + return -1; /* return error code */ + } + return 0; +} + +/***************************************************************** UDR_SENDNOW +* used to flush the current character stream buffer to +* the associated socket. */ +int udr_sendnow(udrs) +UDR* udrs; +{ + int size = (UDIUInt32)(udrs->putend) - (UDIUInt32)(udrs->buff); + if(udrs->previous_op == 0) + { udr_errno = UDIErrorIPCInternal; + return -1; + } + udrs->putbytes = udrs->buff; + udrs->putend = udrs->buff; + if (write(udrs->sd, udrs->buff, size) == -1) + { perror("ERROR, udr_sendnow(), write() call: "); + udr_errno = UDIErrorIPCInternal; + return -1; /* return error code */ + } + return 0; +} + +/******************************************************************** UDR_WORK +* Function to send or recieve data from the buffers supporting +* socket communication. The buffer contains serialised objects +* sent/recieved over a socket connection. +*/ +int udr_work(udrs, object_p, size) +UDR* udrs; +void* object_p; +int size; +{ + int cnt, remain; + + if(udrs->udr_op != udrs->previous_op) + { if(udrs->previous_op == 0) + { udr_errno = UDIErrorIPCInternal; + return -1; + } + udrs->previous_op= udrs->udr_op; + udrs->putbytes = udrs->buff; + udrs->getbytes = udrs->buff; + } + + if(udrs->udr_op == UDR_ENCODE) + { /* write data into character stream buffer */ + if( (UDIUInt32)(udrs->putbytes) + size > + (UDIUInt32)(udrs->buff) + (UDIUInt32)(udrs->bufsize) ) + { udr_errno = UDIErrorIPCInternal; + return -1; + } + memcpy(udrs->putbytes, (char*)object_p, size); + udrs->putbytes += size; + if(udrs->putbytes > udrs->putend) udrs->putend = udrs->putbytes; + } + else if(udrs->udr_op == UDR_DECODE) + { + if( (UDIUInt32)(udrs->putbytes)-(UDIUInt32)(udrs->getbytes) < size ) + { /* need more data in character stream buffer */ + remain = (UDIUInt32)(udrs->bufsize) - + ( (UDIUInt32)(udrs->putbytes)-(UDIUInt32)(udrs->buff) ); + if( ((UDIUInt32)(udrs->bufsize) + (UDIUInt32)(udrs->buff) + - (UDIUInt32)(udrs->getbytes)) < size) + { udr_errno = UDIErrorIPCInternal; + return -1; + } + cnt = read(udrs->sd, (char*)udrs->putbytes, remain); + if(cnt == -1) perror("ERROR udr_work(), read() failure: "); + udrs->putbytes += cnt; + if( (UDIUInt32)(udrs->putbytes)-(UDIUInt32)(udrs->getbytes) < size ) + { udr_errno = UDIErrorIPCInternal; + return -1; /* return error code */ + } + } /* read data from character stream buffer */ + memcpy((char*)object_p, udrs->getbytes, size); + udrs->getbytes += size; + } + else + { udr_errno = UDIErrorIPCInternal; + return -1; + } + return 0; +} + +/************************************************************* UDR_UDIResource +*/ +int udr_UDIResource(udrs, object_p) +UDR* udrs; +UDIResource* object_p; +{ + int retval; + + retval = udr_CPUSpace(udrs, &object_p->Space); + retval = retval | udr_CPUOffset(udrs, &object_p->Offset); + return retval; +} + +/**************************************************************** UDR_UDIRange +*/ +int udr_UDIRange(udrs, object_p) +UDR* udrs; +UDIRange* object_p; +{ + int retval; + + retval = udr_CPUOffset(udrs, &object_p->Low); + retval = retval | udr_CPUOffset(udrs, &object_p->High); + return retval; +} + +/********************************************************** UDR_UDIMemoryRange +*/ +int udr_UDIMemoryRange(udrs, object_p) +UDR* udrs; +UDIMemoryRange* object_p; +{ + int retval; + + retval = udr_CPUSpace(udrs, &object_p->Space); + retval = retval | udr_CPUOffset(udrs, &object_p->Offset); + retval = retval | udr_CPUSizeT(udrs, &object_p->Size); + return retval; +} + +/****************************************************************** UDR_string +*/ +int udr_string(udrs, sp) +UDR* udrs; +char* sp; +{ + int len, retval; + + if(udrs->udr_op == UDR_ENCODE) + { + if(sp) + { len = strlen(sp) + 1; + retval = udr_UDIInt32(udrs, &len); + retval = retval | udr_work(udrs, sp, len); + } + else /* deal with NULL pointer */ + { len = 0; + retval = udr_UDIInt32(udrs, &len); + } + } + else if(udrs->udr_op == UDR_DECODE) + { + retval = udr_UDIInt32(udrs, &len); + if(len) + retval = retval | udr_work(udrs, sp, len); + else *sp = '\0'; /* terminate string */ + } + else + { udr_errno = UDIErrorIPCInternal; + return -1; + } + return retval; +} + +/******************************************************************* UDR_BYTES +*/ +int udr_bytes(udrs, ptr, len) +UDR* udrs; +char* ptr; +int len; +{ + return udr_work(udrs, ptr, len); +} + +/********************************************************************* UDR_INT +*/ +int udr_int(udrs, int_p) +UDR* udrs; +int* int_p; +{ + int ret_val; + UDIInt32 udr_obj; /* object of know size */ + + if(udrs->udr_op == UDR_ENCODE) + { + udr_obj = *int_p; /* copy into know object size */ + return udr_UDIInt32(udrs, &udr_obj); + } + else if(udrs->udr_op == UDR_DECODE) + { + ret_val = udr_UDIInt32(udrs, &udr_obj); /* get object of known size */ + *int_p = udr_obj; + return ret_val; + } + else + { udr_errno = UDIErrorIPCInternal; + return -1; + } +} + +/****************************************************************** UDR_INLINE +*/ +char* udr_inline(udrs, size) +UDR* udrs; +int size; +{ + if(udrs->udr_op != udrs->previous_op) + { if(udrs->previous_op == 0) + { udr_errno = UDIErrorIPCInternal; + return 0; + } + udrs->previous_op= udrs->udr_op; + udrs->putbytes = udrs->buff; + udrs->getbytes = udrs->buff; + } + if(udrs->udr_op == UDR_ENCODE) + { + if(udrs->putbytes + size > udrs->bufsize + udrs->buff) + return 0; + udrs->putbytes += size; + return udrs->putbytes - size; + } + else if(udrs->udr_op == UDR_DECODE) + { + if(udrs->getbytes + size > udrs->bufsize + udrs->buff) + return 0; + udrs->getbytes += size; + return udrs->getbytes - size; + } + else + { udr_errno = UDIErrorIPCInternal; + return 0; + } +} + +/****************************************************************** UDR_GETPOS +*/ +char* udr_getpos(udrs) +UDR* udrs; +{ + if(udrs->udr_op == UDR_ENCODE) + { + return udrs->putbytes; + } + else if(udrs->udr_op == UDR_DECODE) + { + return udrs->getbytes; + } + else + { udr_errno = UDIErrorIPCInternal; + return 0; + } +} + +/****************************************************************** UDR_SETPOS +*/ +int udr_setpos(udrs, pos) +UDR* udrs; +char* pos; +{ + if( ((UDIUInt32)pos > (UDIUInt32)(udrs->buff) + (UDIUInt32)(udrs->bufsize)) + || ((UDIUInt32)pos < (UDIUInt32)(udrs->buff) ) ) + { udr_errno = UDIErrorIPCInternal; + return 0; + } + if(udrs->udr_op == UDR_ENCODE) + { + udrs->putbytes = pos; + return 1; + } + else if(udrs->udr_op == UDR_DECODE) + { + udrs->getbytes = pos; + return 1; + } + else + { udr_errno = UDIErrorIPCInternal; + return 0; + } +} + +/***************************************************************** UDR_READNOW +* Try and ensure "size" bytes are available in the +* receive buffer character stream. +*/ +int udr_readnow(udrs, size) +UDR* udrs; +int size; +{ + int cnt, remain; + + if(udrs->udr_op == UDR_ENCODE) + { + udr_errno = UDIErrorIPCInternal; + return -1; + } + else if(udrs->udr_op == UDR_DECODE) + { + if( (UDIUInt32)(udrs->putbytes)-(UDIUInt32)(udrs->getbytes) < size ) + { /* need more data in character stream buffer */ + remain = (UDIUInt32)(udrs->bufsize) - + ( (UDIUInt32)(udrs->putbytes)-(UDIUInt32)(udrs->buff) ); + cnt = read(udrs->sd, (char*)udrs->putbytes, remain); + if(cnt == -1) perror("ERROR udr_work(), read() failure: "); + udrs->putbytes += cnt; + if( (UDIUInt32)(udrs->putbytes)-(UDIUInt32)(udrs->getbytes) < size ) + { fprintf(stderr,"ERROR, udr_readnow() too few bytes in stream\n"); + return -1; /* return error code */ + } + } + } + else + { udr_errno = UDIErrorIPCInternal; + return -1; + } + return 0; +} + +/******************************************************************* UDR_ALIGN +*/ +int udr_align(udrs, size) +UDR* udrs; +int size; +{ + char* align; + int offset; + + align = udr_getpos(udrs); + offset = size - ((int)align & (size -1)); + offset = offset & (size -1); + if(offset) udr_setpos(udrs, align + offset); +} +#endif /* __GO32__ */ diff --git a/contrib/gdb/gdb/29k-share/udi_soc b/contrib/gdb/gdb/29k-share/udi_soc new file mode 100644 index 000000000000..343317f255fc --- /dev/null +++ b/contrib/gdb/gdb/29k-share/udi_soc @@ -0,0 +1,9 @@ +# @(#)udi_soc 2.1 Daniel Mann +# NOTE: the Session string must not start whith white-space characters. +# Format of string is: +# <session> <domain> <soc_name|host_name> <tip_exe> <pass to UDIconnect> +soc2cayman AF_INET cayman /bin/udi_tip ... +soc2tip AF_UNIX astring tip.exe ... +cuba AF_UNIX soc_name ../bin.68020/udi_tip stuff to pass +cayman AF_INET cayman this_entry_not_matter stuff to pass +iss AF_UNIX * sun4/isstip -r osboot diff --git a/contrib/gdb/gdb/config/mips/tm-tx39.h b/contrib/gdb/gdb/config/mips/tm-tx39.h new file mode 100644 index 000000000000..ee99a2822590 --- /dev/null +++ b/contrib/gdb/gdb/config/mips/tm-tx39.h @@ -0,0 +1,39 @@ +/* Copyright (C) 1993 Free Software Foundation, Inc. + +This file is part of GDB. + +This program 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 of the License, or +(at your option) any later version. + +This program 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; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#define TARGET_BYTE_ORDER_SELECTABLE_P 1 +#define MIPS_EABI 1 +#define MIPS_DEFAULT_FPU_TYPE MIPS_FPU_NONE + +#include "mips/tm-bigmips.h" + +#undef REGISTER_NAMES +#define REGISTER_NAMES \ + { "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", \ + "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", \ + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", \ + "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra", \ + "sr", "lo", "hi", "bad", "cause","pc", \ + "", "", "", "", "", "", "", "", \ + "", "", "", "", "", "", "", "", \ + "", "", "", "", "", "", "", "", \ + "", "", "", "", "", "", "", "", \ + "", "", "", "", \ + "", "", "", "", "", "", "", "", \ + "", "", "config", "cache", "debug", "depc", "epc", "" \ + } diff --git a/contrib/gdb/gdb/config/mips/tm-tx39l.h b/contrib/gdb/gdb/config/mips/tm-tx39l.h new file mode 100644 index 000000000000..8ceec7296eae --- /dev/null +++ b/contrib/gdb/gdb/config/mips/tm-tx39l.h @@ -0,0 +1,39 @@ +/* Copyright (C) 1993 Free Software Foundation, Inc. + +This file is part of GDB. + +This program 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 of the License, or +(at your option) any later version. + +This program 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; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#define TARGET_BYTE_ORDER_SELECTABLE_P 1 +#define MIPS_EABI 1 +#define MIPS_DEFAULT_FPU_TYPE MIPS_FPU_NONE + +#include "mips/tm-mips.h" + +#undef REGISTER_NAMES +#define REGISTER_NAMES \ + { "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", \ + "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", \ + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", \ + "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra", \ + "sr", "lo", "hi", "bad", "cause","pc", \ + "", "", "", "", "", "", "", "", \ + "", "", "", "", "", "", "", "", \ + "", "", "", "", "", "", "", "", \ + "", "", "", "", "", "", "", "", \ + "", "", "", "", \ + "", "", "", "", "", "", "", "", \ + "", "", "config", "cache", "debug", "depc", "epc", "" \ + } diff --git a/contrib/gdb/gdb/config/mips/tm-vr4100.h b/contrib/gdb/gdb/config/mips/tm-vr4100.h new file mode 100644 index 000000000000..843f49c2222d --- /dev/null +++ b/contrib/gdb/gdb/config/mips/tm-vr4100.h @@ -0,0 +1,25 @@ +/* Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GDB. + +This program 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 of the License, or +(at your option) any later version. + +This program 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; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#define TARGET_BYTE_ORDER_SELECTABLE_P 1 +#define MIPS_EABI 1 +#define MIPS_DEFAULT_FPU_TYPE MIPS_FPU_NONE +#define TARGET_MONITOR_PROMPT "<RISQ> " +#define TARGET_PTR_BIT 64 + +#include "mips/tm-bigmips64.h" diff --git a/contrib/gdb/gdb/config/mips/tm-vr5000.h b/contrib/gdb/gdb/config/mips/tm-vr5000.h new file mode 100644 index 000000000000..b834a7762fc8 --- /dev/null +++ b/contrib/gdb/gdb/config/mips/tm-vr5000.h @@ -0,0 +1,23 @@ +/* Copyright (C) 1996 Free Software Foundation, Inc. + +This file is part of GDB. + +This program 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 of the License, or +(at your option) any later version. + +This program 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; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#define TARGET_BYTE_ORDER_SELECTABLE_P 1 +#define TARGET_MONITOR_PROMPT "<RISQ> " +#define MIPS_EABI 1 + +#include "mips/tm-bigmips64.h" diff --git a/contrib/gdb/gdb/config/mips/tm-vr5000el.h b/contrib/gdb/gdb/config/mips/tm-vr5000el.h new file mode 100644 index 000000000000..c6897d12ee1d --- /dev/null +++ b/contrib/gdb/gdb/config/mips/tm-vr5000el.h @@ -0,0 +1,23 @@ +/* Copyright (C) 1996 Free Software Foundation, Inc. + +This file is part of GDB. + +This program 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 of the License, or +(at your option) any later version. + +This program 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; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#define TARGET_BYTE_ORDER_SELECTABLE_P 1 +#define TARGET_MONITOR_PROMPT "<RISQ> " +#define MIPS_EABI 1 + +#include "mips/tm-mips64.h" diff --git a/contrib/gdb/gdb/config/mips/tx39.mt b/contrib/gdb/gdb/config/mips/tx39.mt new file mode 100644 index 000000000000..8b4c1a92c244 --- /dev/null +++ b/contrib/gdb/gdb/config/mips/tx39.mt @@ -0,0 +1,5 @@ +# Target: Big-endian mips board, typically an IDT. +TDEPFILES= mips-tdep.o remote-mips.o dve3900-rom.o monitor.o dsrec.o +TM_FILE= tm-tx39.h +SIM_OBS = remote-sim.o +SIM = ../sim/mips/libsim.a diff --git a/contrib/gdb/gdb/config/mips/tx39l.mt b/contrib/gdb/gdb/config/mips/tx39l.mt new file mode 100644 index 000000000000..35083293d458 --- /dev/null +++ b/contrib/gdb/gdb/config/mips/tx39l.mt @@ -0,0 +1,5 @@ +# Target: Big-endian mips board, typically an IDT. +TDEPFILES= mips-tdep.o remote-mips.o dve3900-rom.o monitor.o dsrec.o +TM_FILE= tm-tx39l.h +SIM_OBS = remote-sim.o +SIM = ../sim/mips/libsim.a diff --git a/contrib/gdb/gdb/config/mips/vr4100.mt b/contrib/gdb/gdb/config/mips/vr4100.mt new file mode 100644 index 000000000000..c5ae4f94a6dd --- /dev/null +++ b/contrib/gdb/gdb/config/mips/vr4100.mt @@ -0,0 +1,5 @@ +# Target: Big-endian SIM monitor board. +TDEPFILES= mips-tdep.o remote-mips.o +TM_FILE= tm-vr4100.h +SIM_OBS = remote-sim.o +SIM = ../sim/mips/libsim.a diff --git a/contrib/gdb/gdb/config/mips/vr5000.mt b/contrib/gdb/gdb/config/mips/vr5000.mt new file mode 100644 index 000000000000..316c548de093 --- /dev/null +++ b/contrib/gdb/gdb/config/mips/vr5000.mt @@ -0,0 +1,7 @@ +# Target: Big-endian SIM monitor board. +TDEPFILES= mips-tdep.o remote-mips.o +TM_FILE= tm-vr5000.h +SIM_OBS = remote-sim.o +SIM = ../sim/mips/libsim.a +GDBSERVER_DEPFILES= low-sim.o +GDBSERVER_LIBS = ../../sim/mips/libsim.a ../../bfd/libbfd.a ../../libiberty/libiberty.a -lm diff --git a/contrib/gdb/gdb/config/mips/vr5000el.mt b/contrib/gdb/gdb/config/mips/vr5000el.mt new file mode 100644 index 000000000000..99687edcefc2 --- /dev/null +++ b/contrib/gdb/gdb/config/mips/vr5000el.mt @@ -0,0 +1,5 @@ +# Target: Little-endian SIM monitor board. +TDEPFILES= mips-tdep.o remote-mips.o +TM_FILE= tm-vr5000el.h +SIM_OBS = remote-sim.o +SIM = ../sim/mips/libsim.a diff --git a/contrib/gdb/gdb/d10v-tdep.c b/contrib/gdb/gdb/d10v-tdep.c new file mode 100644 index 000000000000..f4b52f62295b --- /dev/null +++ b/contrib/gdb/gdb/d10v-tdep.c @@ -0,0 +1,1028 @@ +/* Target-dependent code for Mitsubishi D10V, for GDB. + Copyright (C) 1996, 1997 Free Software Foundation, Inc. + +This file is part of GDB. + +This program 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 of the License, or +(at your option) any later version. + +This program 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; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Contributed by Martin Hunt, hunt@cygnus.com */ + +#include "defs.h" +#include "frame.h" +#include "obstack.h" +#include "symtab.h" +#include "gdbtypes.h" +#include "gdbcmd.h" +#include "gdbcore.h" +#include "gdb_string.h" +#include "value.h" +#include "inferior.h" +#include "dis-asm.h" +#include "symfile.h" +#include "objfiles.h" + +void d10v_frame_find_saved_regs PARAMS ((struct frame_info *fi, + struct frame_saved_regs *fsr)); + +int +d10v_frame_chain_valid (chain, frame) + CORE_ADDR chain; + struct frame_info *frame; /* not used here */ +{ + return ((chain) != 0 && (frame) != 0 && (frame)->pc > IMEM_START); +} + + +/* Should we use EXTRACT_STRUCT_VALUE_ADDRESS instead of + EXTRACT_RETURN_VALUE? GCC_P is true if compiled with gcc + and TYPE is the type (which is known to be struct, union or array). + + The d10v returns anything less than 8 bytes in size in + registers. */ + +int +d10v_use_struct_convention (gcc_p, type) + int gcc_p; + struct type *type; +{ + return (TYPE_LENGTH (type) > 8); +} + + +/* Discard from the stack the innermost frame, restoring all saved + registers. */ + +void +d10v_pop_frame (frame) + struct frame_info *frame; +{ + CORE_ADDR fp; + int regnum; + struct frame_saved_regs fsr; + char raw_buffer[8]; + + fp = FRAME_FP (frame); + /* fill out fsr with the address of where each */ + /* register was stored in the frame */ + get_frame_saved_regs (frame, &fsr); + + /* now update the current registers with the old values */ + for (regnum = A0_REGNUM; regnum < A0_REGNUM+2 ; regnum++) + { + if (fsr.regs[regnum]) + { + read_memory (fsr.regs[regnum], raw_buffer, REGISTER_RAW_SIZE(regnum)); + write_register_bytes (REGISTER_BYTE (regnum), raw_buffer, REGISTER_RAW_SIZE(regnum)); + } + } + for (regnum = 0; regnum < SP_REGNUM; regnum++) + { + if (fsr.regs[regnum]) + { + write_register (regnum, read_memory_unsigned_integer (fsr.regs[regnum], REGISTER_RAW_SIZE(regnum))); + } + } + if (fsr.regs[PSW_REGNUM]) + { + write_register (PSW_REGNUM, read_memory_unsigned_integer (fsr.regs[PSW_REGNUM], REGISTER_RAW_SIZE(PSW_REGNUM))); + } + + write_register (PC_REGNUM, read_register (LR_REGNUM)); + write_register (SP_REGNUM, fp + frame->size); + target_store_registers (-1); + flush_cached_frames (); +} + +static int +check_prologue (op) + unsigned short op; +{ + /* st rn, @-sp */ + if ((op & 0x7E1F) == 0x6C1F) + return 1; + + /* st2w rn, @-sp */ + if ((op & 0x7E3F) == 0x6E1F) + return 1; + + /* subi sp, n */ + if ((op & 0x7FE1) == 0x01E1) + return 1; + + /* mv r11, sp */ + if (op == 0x417E) + return 1; + + /* nop */ + if (op == 0x5E00) + return 1; + + /* st rn, @sp */ + if ((op & 0x7E1F) == 0x681E) + return 1; + + /* st2w rn, @sp */ + if ((op & 0x7E3F) == 0x3A1E) + return 1; + + return 0; +} + +CORE_ADDR +d10v_skip_prologue (pc) + CORE_ADDR pc; +{ + unsigned long op; + unsigned short op1, op2; + CORE_ADDR func_addr, func_end; + struct symtab_and_line sal; + + /* If we have line debugging information, then the end of the */ + /* prologue should the first assembly instruction of the first source line */ + if (find_pc_partial_function (pc, NULL, &func_addr, &func_end)) + { + sal = find_pc_line (func_addr, 0); + if ( sal.end && sal.end < func_end) + return sal.end; + } + + if (target_read_memory (pc, (char *)&op, 4)) + return pc; /* Can't access it -- assume no prologue. */ + + while (1) + { + op = (unsigned long)read_memory_integer (pc, 4); + if ((op & 0xC0000000) == 0xC0000000) + { + /* long instruction */ + if ( ((op & 0x3FFF0000) != 0x01FF0000) && /* add3 sp,sp,n */ + ((op & 0x3F0F0000) != 0x340F0000) && /* st rn, @(offset,sp) */ + ((op & 0x3F1F0000) != 0x350F0000)) /* st2w rn, @(offset,sp) */ + break; + } + else + { + /* short instructions */ + if ((op & 0xC0000000) == 0x80000000) + { + op2 = (op & 0x3FFF8000) >> 15; + op1 = op & 0x7FFF; + } + else + { + op1 = (op & 0x3FFF8000) >> 15; + op2 = op & 0x7FFF; + } + if (check_prologue(op1)) + { + if (!check_prologue(op2)) + { + /* if the previous opcode was really part of the prologue */ + /* and not just a NOP, then we want to break after both instructions */ + if (op1 != 0x5E00) + pc += 4; + break; + } + } + else + break; + } + pc += 4; + } + return pc; +} + +/* Given a GDB frame, determine the address of the calling function's frame. + This will be used to create a new GDB frame struct, and then + INIT_EXTRA_FRAME_INFO and INIT_FRAME_PC will be called for the new frame. +*/ + +CORE_ADDR +d10v_frame_chain (frame) + struct frame_info *frame; +{ + struct frame_saved_regs fsr; + + d10v_frame_find_saved_regs (frame, &fsr); + + if (frame->return_pc == IMEM_START || inside_entry_file(frame->return_pc)) + return (CORE_ADDR)0; + + if (!fsr.regs[FP_REGNUM]) + { + if (!fsr.regs[SP_REGNUM] || fsr.regs[SP_REGNUM] == STACK_START) + return (CORE_ADDR)0; + + return fsr.regs[SP_REGNUM]; + } + + if (!read_memory_unsigned_integer(fsr.regs[FP_REGNUM], REGISTER_RAW_SIZE(FP_REGNUM))) + return (CORE_ADDR)0; + + return D10V_MAKE_DADDR (read_memory_unsigned_integer (fsr.regs[FP_REGNUM], REGISTER_RAW_SIZE (FP_REGNUM))); +} + +static int next_addr, uses_frame; + +static int +prologue_find_regs (op, fsr, addr) + unsigned short op; + struct frame_saved_regs *fsr; + CORE_ADDR addr; +{ + int n; + + /* st rn, @-sp */ + if ((op & 0x7E1F) == 0x6C1F) + { + n = (op & 0x1E0) >> 5; + next_addr -= 2; + fsr->regs[n] = next_addr; + return 1; + } + + /* st2w rn, @-sp */ + else if ((op & 0x7E3F) == 0x6E1F) + { + n = (op & 0x1E0) >> 5; + next_addr -= 4; + fsr->regs[n] = next_addr; + fsr->regs[n+1] = next_addr+2; + return 1; + } + + /* subi sp, n */ + if ((op & 0x7FE1) == 0x01E1) + { + n = (op & 0x1E) >> 1; + if (n == 0) + n = 16; + next_addr -= n; + return 1; + } + + /* mv r11, sp */ + if (op == 0x417E) + { + uses_frame = 1; + return 1; + } + + /* nop */ + if (op == 0x5E00) + return 1; + + /* st rn, @sp */ + if ((op & 0x7E1F) == 0x681E) + { + n = (op & 0x1E0) >> 5; + fsr->regs[n] = next_addr; + return 1; + } + + /* st2w rn, @sp */ + if ((op & 0x7E3F) == 0x3A1E) + { + n = (op & 0x1E0) >> 5; + fsr->regs[n] = next_addr; + fsr->regs[n+1] = next_addr+2; + return 1; + } + + return 0; +} + +/* Put here the code to store, into a struct frame_saved_regs, the + addresses of the saved registers of frame described by FRAME_INFO. + This includes special registers such as pc and fp saved in special + ways in the stack frame. sp is even more special: the address we + return for it IS the sp for the next frame. */ +void +d10v_frame_find_saved_regs (fi, fsr) + struct frame_info *fi; + struct frame_saved_regs *fsr; +{ + CORE_ADDR fp, pc; + unsigned long op; + unsigned short op1, op2; + int i; + + fp = fi->frame; + memset (fsr, 0, sizeof (*fsr)); + next_addr = 0; + + pc = get_pc_function_start (fi->pc); + + uses_frame = 0; + while (1) + { + op = (unsigned long)read_memory_integer (pc, 4); + if ((op & 0xC0000000) == 0xC0000000) + { + /* long instruction */ + if ((op & 0x3FFF0000) == 0x01FF0000) + { + /* add3 sp,sp,n */ + short n = op & 0xFFFF; + next_addr += n; + } + else if ((op & 0x3F0F0000) == 0x340F0000) + { + /* st rn, @(offset,sp) */ + short offset = op & 0xFFFF; + short n = (op >> 20) & 0xF; + fsr->regs[n] = next_addr + offset; + } + else if ((op & 0x3F1F0000) == 0x350F0000) + { + /* st2w rn, @(offset,sp) */ + short offset = op & 0xFFFF; + short n = (op >> 20) & 0xF; + fsr->regs[n] = next_addr + offset; + fsr->regs[n+1] = next_addr + offset + 2; + } + else + break; + } + else + { + /* short instructions */ + if ((op & 0xC0000000) == 0x80000000) + { + op2 = (op & 0x3FFF8000) >> 15; + op1 = op & 0x7FFF; + } + else + { + op1 = (op & 0x3FFF8000) >> 15; + op2 = op & 0x7FFF; + } + if (!prologue_find_regs(op1,fsr,pc) || !prologue_find_regs(op2,fsr,pc)) + break; + } + pc += 4; + } + + fi->size = -next_addr; + + if (!(fp & 0xffff)) + fp = D10V_MAKE_DADDR (read_register(SP_REGNUM)); + + for (i=0; i<NUM_REGS-1; i++) + if (fsr->regs[i]) + { + fsr->regs[i] = fp - (next_addr - fsr->regs[i]); + } + + if (fsr->regs[LR_REGNUM]) + { + CORE_ADDR return_pc = read_memory_unsigned_integer (fsr->regs[LR_REGNUM], REGISTER_RAW_SIZE (LR_REGNUM)); + fi->return_pc = D10V_MAKE_IADDR (return_pc); + } + else + { + fi->return_pc = D10V_MAKE_IADDR (read_register(LR_REGNUM)); + } + + /* th SP is not normally (ever?) saved, but check anyway */ + if (!fsr->regs[SP_REGNUM]) + { + /* if the FP was saved, that means the current FP is valid, */ + /* otherwise, it isn't being used, so we use the SP instead */ + if (uses_frame) + fsr->regs[SP_REGNUM] = read_register(FP_REGNUM) + fi->size; + else + { + fsr->regs[SP_REGNUM] = fp + fi->size; + fi->frameless = 1; + fsr->regs[FP_REGNUM] = 0; + } + } +} + +void +d10v_init_extra_frame_info (fromleaf, fi) + int fromleaf; + struct frame_info *fi; +{ + fi->frameless = 0; + fi->size = 0; + fi->return_pc = 0; + + /* The call dummy doesn't save any registers on the stack, so we can + return now. */ + if (PC_IN_CALL_DUMMY (fi->pc, fi->frame, fi->frame)) + { + return; + } + else + { + struct frame_saved_regs dummy; + d10v_frame_find_saved_regs (fi, &dummy); + } +} + +static void +show_regs (args, from_tty) + char *args; + int from_tty; +{ + int a; + printf_filtered ("PC=%04x (0x%x) PSW=%04x RPT_S=%04x RPT_E=%04x RPT_C=%04x\n", + read_register (PC_REGNUM), D10V_MAKE_IADDR (read_register (PC_REGNUM)), + read_register (PSW_REGNUM), + read_register (24), + read_register (25), + read_register (23)); + printf_filtered ("R0-R7 %04x %04x %04x %04x %04x %04x %04x %04x\n", + read_register (0), + read_register (1), + read_register (2), + read_register (3), + read_register (4), + read_register (5), + read_register (6), + read_register (7)); + printf_filtered ("R8-R15 %04x %04x %04x %04x %04x %04x %04x %04x\n", + read_register (8), + read_register (9), + read_register (10), + read_register (11), + read_register (12), + read_register (13), + read_register (14), + read_register (15)); + printf_filtered ("IMAP0 %04x IMAP1 %04x DMAP %04x\n", + read_register (IMAP0_REGNUM), + read_register (IMAP1_REGNUM), + read_register (DMAP_REGNUM)); + printf_filtered ("A0-A1"); + for (a = A0_REGNUM; a <= A0_REGNUM + 1; a++) + { + char num[MAX_REGISTER_RAW_SIZE]; + int i; + printf_filtered (" "); + read_register_gen (a, (char *)&num); + for (i = 0; i < MAX_REGISTER_RAW_SIZE; i++) + { + printf_filtered ("%02x", (num[i] & 0xff)); + } + } + printf_filtered ("\n"); +} + +CORE_ADDR +d10v_read_pc (pid) + int pid; +{ + int save_pid; + CORE_ADDR pc; + CORE_ADDR retval; + + save_pid = inferior_pid; + inferior_pid = pid; + pc = (int) read_register (PC_REGNUM); + inferior_pid = save_pid; + retval = D10V_MAKE_IADDR (pc); + return retval; +} + +void +d10v_write_pc (val, pid) + CORE_ADDR val; + int pid; +{ + int save_pid; + + save_pid = inferior_pid; + inferior_pid = pid; + write_register (PC_REGNUM, D10V_CONVERT_IADDR_TO_RAW (val)); + inferior_pid = save_pid; +} + +CORE_ADDR +d10v_read_sp () +{ + return (D10V_MAKE_DADDR (read_register (SP_REGNUM))); +} + +void +d10v_write_sp (val) + CORE_ADDR val; +{ + write_register (SP_REGNUM, D10V_CONVERT_DADDR_TO_RAW (val)); +} + +void +d10v_write_fp (val) + CORE_ADDR val; +{ + write_register (FP_REGNUM, D10V_CONVERT_DADDR_TO_RAW (val)); +} + +CORE_ADDR +d10v_read_fp () +{ + return (D10V_MAKE_DADDR (read_register(FP_REGNUM))); +} + +/* Function: push_return_address (pc) + Set up the return address for the inferior function call. + Needed for targets where we don't actually execute a JSR/BSR instruction */ + +CORE_ADDR +d10v_push_return_address (pc, sp) + CORE_ADDR pc; + CORE_ADDR sp; +{ + write_register (LR_REGNUM, D10V_CONVERT_IADDR_TO_RAW (CALL_DUMMY_ADDRESS ())); + return sp; +} + + +CORE_ADDR +d10v_push_arguments (nargs, args, sp, struct_return, struct_addr) + int nargs; + value_ptr *args; + CORE_ADDR sp; + int struct_return; + CORE_ADDR struct_addr; +{ + int i; + int regnum = ARG1_REGNUM; + + /* Fill in registers and arg lists */ + for (i = 0; i < nargs; i++) + { + value_ptr arg = args[i]; + struct type *type = check_typedef (VALUE_TYPE (arg)); + char *contents = VALUE_CONTENTS (arg); + int len = TYPE_LENGTH (type); + /* printf ("push: type=%d len=%d\n", type->code, len); */ + if (TYPE_CODE (type) == TYPE_CODE_PTR) + { + /* pointers require special handling - first convert and + then store */ + long val = extract_signed_integer (contents, len); + len = 2; + if (TYPE_TARGET_TYPE (type) + && (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_FUNC)) + { + /* function pointer */ + val = D10V_CONVERT_IADDR_TO_RAW (val); + } + else if (D10V_IADDR_P (val)) + { + /* also function pointer! */ + val = D10V_CONVERT_DADDR_TO_RAW (val); + } + else + { + /* data pointer */ + val &= 0xFFFF; + } + if (regnum <= ARGN_REGNUM) + write_register (regnum++, val & 0xffff); + else + { + char ptr[2]; + sp -= 2; + store_address (ptr, val & 0xffff, 2); + write_memory (sp, ptr, 2); + } + } + else + { + int aligned_regnum = (regnum + 1) & ~1; + if (len <= 2 && regnum <= ARGN_REGNUM) + /* fits in a single register, do not align */ + { + long val = extract_unsigned_integer (contents, len); + write_register (regnum++, val); + } + else if (len <= (ARGN_REGNUM - aligned_regnum + 1) * 2) + /* value fits in remaining registers, store keeping left + aligned */ + { + int b; + regnum = aligned_regnum; + for (b = 0; b < (len & ~1); b += 2) + { + long val = extract_unsigned_integer (&contents[b], 2); + write_register (regnum++, val); + } + if (b < len) + { + long val = extract_unsigned_integer (&contents[b], 1); + write_register (regnum++, (val << 8)); + } + } + else + { + /* arg goes straight on stack */ + regnum = ARGN_REGNUM + 1; + sp = (sp - len) & ~1; + write_memory (sp, contents, len); + } + } + } + return sp; +} + + +/* Given a return value in `regbuf' with a type `valtype', + extract and copy its value into `valbuf'. */ + +void +d10v_extract_return_value (type, regbuf, valbuf) + struct type *type; + char regbuf[REGISTER_BYTES]; + char *valbuf; +{ + int len; + /* printf("RET: TYPE=%d len=%d r%d=0x%x\n",type->code, TYPE_LENGTH (type), RET1_REGNUM - R0_REGNUM, (int) extract_unsigned_integer (regbuf + REGISTER_BYTE(RET1_REGNUM), REGISTER_RAW_SIZE (RET1_REGNUM))); */ + if (TYPE_CODE (type) == TYPE_CODE_PTR + && TYPE_TARGET_TYPE (type) + && (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_FUNC)) + { + /* pointer to function */ + int num; + short snum; + snum = extract_address (regbuf + REGISTER_BYTE (RET1_REGNUM), REGISTER_RAW_SIZE (RET1_REGNUM)); + store_address ( valbuf, 4, D10V_MAKE_IADDR(snum)); + } + else if (TYPE_CODE(type) == TYPE_CODE_PTR) + { + /* pointer to data */ + int num; + short snum; + snum = extract_address (regbuf + REGISTER_BYTE (RET1_REGNUM), REGISTER_RAW_SIZE (RET1_REGNUM)); + store_address ( valbuf, 4, D10V_MAKE_DADDR(snum)); + } + else + { + len = TYPE_LENGTH (type); + if (len == 1) + { + unsigned short c = extract_unsigned_integer (regbuf + REGISTER_BYTE (RET1_REGNUM), REGISTER_RAW_SIZE (RET1_REGNUM)); + store_unsigned_integer (valbuf, 1, c); + } + else if ((len & 1) == 0) + memcpy (valbuf, regbuf + REGISTER_BYTE (RET1_REGNUM), len); + else + { + /* For return values of odd size, the first byte is in the + least significant part of the first register. The + remaining bytes in remaining registers. Interestingly, + when such values are passed in, the last byte is in the + most significant byte of that same register - wierd. */ + memcpy (valbuf, regbuf + REGISTER_BYTE (RET1_REGNUM) + 1, len); + } + } +} + +/* The following code implements access to, and display of, the D10V's + instruction trace buffer. The buffer consists of 64K or more + 4-byte words of data, of which each words includes an 8-bit count, + an 8-bit segment number, and a 16-bit instruction address. + + In theory, the trace buffer is continuously capturing instruction + data that the CPU presents on its "debug bus", but in practice, the + ROMified GDB stub only enables tracing when it continues or steps + the program, and stops tracing when the program stops; so it + actually works for GDB to read the buffer counter out of memory and + then read each trace word. The counter records where the tracing + stops, but there is no record of where it started, so we remember + the PC when we resumed and then search backwards in the trace + buffer for a word that includes that address. This is not perfect, + because you will miss trace data if the resumption PC is the target + of a branch. (The value of the buffer counter is semi-random, any + trace data from a previous program stop is gone.) */ + +/* The address of the last word recorded in the trace buffer. */ + +#define DBBC_ADDR (0xd80000) + +/* The base of the trace buffer, at least for the "Board_0". */ + +#define TRACE_BUFFER_BASE (0xf40000) + +static void trace_command PARAMS ((char *, int)); + +static void untrace_command PARAMS ((char *, int)); + +static void trace_info PARAMS ((char *, int)); + +static void tdisassemble_command PARAMS ((char *, int)); + +static void display_trace PARAMS ((int, int)); + +/* True when instruction traces are being collected. */ + +static int tracing; + +/* Remembered PC. */ + +static CORE_ADDR last_pc; + +/* True when trace output should be displayed whenever program stops. */ + +static int trace_display; + +/* True when trace listing should include source lines. */ + +static int default_trace_show_source = 1; + +struct trace_buffer { + int size; + short *counts; + CORE_ADDR *addrs; +} trace_data; + +static void +trace_command (args, from_tty) + char *args; + int from_tty; +{ + /* Clear the host-side trace buffer, allocating space if needed. */ + trace_data.size = 0; + if (trace_data.counts == NULL) + trace_data.counts = (short *) xmalloc (65536 * sizeof(short)); + if (trace_data.addrs == NULL) + trace_data.addrs = (CORE_ADDR *) xmalloc (65536 * sizeof(CORE_ADDR)); + + tracing = 1; + + printf_filtered ("Tracing is now on.\n"); +} + +static void +untrace_command (args, from_tty) + char *args; + int from_tty; +{ + tracing = 0; + + printf_filtered ("Tracing is now off.\n"); +} + +static void +trace_info (args, from_tty) + char *args; + int from_tty; +{ + int i; + + if (trace_data.size) + { + printf_filtered ("%d entries in trace buffer:\n", trace_data.size); + + for (i = 0; i < trace_data.size; ++i) + { + printf_filtered ("%d: %d instruction%s at 0x%x\n", + i, trace_data.counts[i], + (trace_data.counts[i] == 1 ? "" : "s"), + trace_data.addrs[i]); + } + } + else + printf_filtered ("No entries in trace buffer.\n"); + + printf_filtered ("Tracing is currently %s.\n", (tracing ? "on" : "off")); +} + +/* Print the instruction at address MEMADDR in debugged memory, + on STREAM. Returns length of the instruction, in bytes. */ + +static int +print_insn (memaddr, stream) + CORE_ADDR memaddr; + GDB_FILE *stream; +{ + /* If there's no disassembler, something is very wrong. */ + if (tm_print_insn == NULL) + abort (); + + if (TARGET_BYTE_ORDER == BIG_ENDIAN) + tm_print_insn_info.endian = BFD_ENDIAN_BIG; + else + tm_print_insn_info.endian = BFD_ENDIAN_LITTLE; + return (*tm_print_insn) (memaddr, &tm_print_insn_info); +} + +void +d10v_eva_prepare_to_trace () +{ + if (!tracing) + return; + + last_pc = read_register (PC_REGNUM); +} + +/* Collect trace data from the target board and format it into a form + more useful for display. */ + +void +d10v_eva_get_trace_data () +{ + int count, i, j, oldsize; + int trace_addr, trace_seg, trace_cnt, next_cnt; + unsigned int last_trace, trace_word, next_word; + unsigned int *tmpspace; + + if (!tracing) + return; + + tmpspace = xmalloc (65536 * sizeof(unsigned int)); + + last_trace = read_memory_unsigned_integer (DBBC_ADDR, 2) << 2; + + /* Collect buffer contents from the target, stopping when we reach + the word recorded when execution resumed. */ + + count = 0; + while (last_trace > 0) + { + QUIT; + trace_word = + read_memory_unsigned_integer (TRACE_BUFFER_BASE + last_trace, 4); + trace_addr = trace_word & 0xffff; + last_trace -= 4; + /* Ignore an apparently nonsensical entry. */ + if (trace_addr == 0xffd5) + continue; + tmpspace[count++] = trace_word; + if (trace_addr == last_pc) + break; + if (count > 65535) + break; + } + + /* Move the data to the host-side trace buffer, adjusting counts to + include the last instruction executed and transforming the address + into something that GDB likes. */ + + for (i = 0; i < count; ++i) + { + trace_word = tmpspace[i]; + next_word = ((i == 0) ? 0 : tmpspace[i - 1]); + trace_addr = trace_word & 0xffff; + next_cnt = (next_word >> 24) & 0xff; + j = trace_data.size + count - i - 1; + trace_data.addrs[j] = (trace_addr << 2) + 0x1000000; + trace_data.counts[j] = next_cnt + 1; + } + + oldsize = trace_data.size; + trace_data.size += count; + + free (tmpspace); + + if (trace_display) + display_trace (oldsize, trace_data.size); +} + +static void +tdisassemble_command (arg, from_tty) + char *arg; + int from_tty; +{ + int i, count; + CORE_ADDR low, high; + char *space_index; + + if (!arg) + { + low = 0; + high = trace_data.size; + } + else if (!(space_index = (char *) strchr (arg, ' '))) + { + low = parse_and_eval_address (arg); + high = low + 5; + } + else + { + /* Two arguments. */ + *space_index = '\0'; + low = parse_and_eval_address (arg); + high = parse_and_eval_address (space_index + 1); + if (high < low) + high = low; + } + + printf_filtered ("Dump of trace from %d to %d:\n", low, high); + + display_trace (low, high); + + printf_filtered ("End of trace dump.\n"); + gdb_flush (gdb_stdout); +} + +static void +display_trace (low, high) + int low, high; +{ + int i, count, trace_show_source, first, suppress; + CORE_ADDR next_address; + + trace_show_source = default_trace_show_source; + if (!have_full_symbols () && !have_partial_symbols()) + { + trace_show_source = 0; + printf_filtered ("No symbol table is loaded. Use the \"file\" command.\n"); + printf_filtered ("Trace will not display any source.\n"); + } + + first = 1; + suppress = 0; + for (i = low; i < high; ++i) + { + next_address = trace_data.addrs[i]; + count = trace_data.counts[i]; + while (count-- > 0) + { + QUIT; + if (trace_show_source) + { + struct symtab_and_line sal, sal_prev; + + sal_prev = find_pc_line (next_address - 4, 0); + sal = find_pc_line (next_address, 0); + + if (sal.symtab) + { + if (first || sal.line != sal_prev.line) + print_source_lines (sal.symtab, sal.line, sal.line + 1, 0); + suppress = 0; + } + else + { + if (!suppress) + /* FIXME-32x64--assumes sal.pc fits in long. */ + printf_filtered ("No source file for address %s.\n", + local_hex_string((unsigned long) sal.pc)); + suppress = 1; + } + } + first = 0; + print_address (next_address, gdb_stdout); + printf_filtered (":"); + printf_filtered ("\t"); + wrap_here (" "); + next_address = next_address + print_insn (next_address, gdb_stdout); + printf_filtered ("\n"); + gdb_flush (gdb_stdout); + } + } +} + +extern void (*target_resume_hook) PARAMS ((void)); +extern void (*target_wait_loop_hook) PARAMS ((void)); + +void +_initialize_d10v_tdep () +{ + tm_print_insn = print_insn_d10v; + + target_resume_hook = d10v_eva_prepare_to_trace; + target_wait_loop_hook = d10v_eva_get_trace_data; + + add_com ("regs", class_vars, show_regs, "Print all registers"); + + add_com ("trace", class_support, trace_command, + "Enable tracing of instruction execution."); + + add_com ("untrace", class_support, untrace_command, + "Disable tracing of instruction execution."); + + add_com ("tdisassemble", class_vars, tdisassemble_command, + "Disassemble the trace buffer.\n\ +Two optional arguments specify a range of trace buffer entries\n\ +as reported by info trace (NOT addresses!)."); + + add_info ("trace", trace_info, + "Display info about the trace data buffer."); + + add_show_from_set (add_set_cmd ("tracedisplay", no_class, + var_integer, (char *)&trace_display, + "Set automatic display of trace.\n", &setlist), + &showlist); + add_show_from_set (add_set_cmd ("tracesource", no_class, + var_integer, (char *)&default_trace_show_source, + "Set display of source code with trace.\n", &setlist), + &showlist); + +} diff --git a/contrib/gdb/gdb/d30v-tdep.c b/contrib/gdb/gdb/d30v-tdep.c new file mode 100644 index 000000000000..ec4646331411 --- /dev/null +++ b/contrib/gdb/gdb/d30v-tdep.c @@ -0,0 +1,1396 @@ +/* Target-dependent code for Mitsubishi D30V, for GDB. + Copyright (C) 1996, 1997 Free Software Foundation, Inc. + +This file is part of GDB. + +This program 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 of the License, or +(at your option) any later version. + +This program 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; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Contributed by Martin Hunt, hunt@cygnus.com */ + +#include "defs.h" +#include "frame.h" +#include "obstack.h" +#include "symtab.h" +#include "gdbtypes.h" +#include "gdbcmd.h" +#include "gdbcore.h" +#include "gdb_string.h" +#include "value.h" +#include "inferior.h" +#include "dis-asm.h" +#include "symfile.h" +#include "objfiles.h" + +void d30v_frame_find_saved_regs PARAMS ((struct frame_info *fi, + struct frame_saved_regs *fsr)); +void d30v_frame_find_saved_regs_offsets PARAMS ((struct frame_info *fi, + struct frame_saved_regs *fsr)); +static void d30v_pop_dummy_frame PARAMS ((struct frame_info *fi)); +static void d30v_print_flags PARAMS ((void)); +static void print_flags_command PARAMS ((char *, int)); + +/* the following defines assume: + fp is r61, lr is r62, sp is r63, and ?? is r22 + if that changes, they will need to be updated */ + +#define OP_MASK_ALL_BUT_RA 0x0ffc0fff /* throw away Ra, keep the rest */ + +#define OP_STW_SPM 0x054c0fc0 /* stw Ra, @(sp-) */ +#define OP_STW_SP_R0 0x05400fc0 /* stw Ra, @(sp,r0) */ +#define OP_STW_SP_IMM0 0x05480fc0 /* st Ra, @(sp, 0x0) */ +#define OP_STW_R22P_R0 0x05440580 /* stw Ra, @(r22+,r0) */ + +#define OP_ST2W_SPM 0x056c0fc0 /* st2w Ra, @(sp-) */ +#define OP_ST2W_SP_R0 0x05600fc0 /* st2w Ra, @(sp, r0) */ +#define OP_ST2W_SP_IMM0 0x05680fc0 /* st2w Ra, @(sp, 0x0) */ +#define OP_ST2W_R22P_R0 0x05640580 /* st2w Ra, @(r22+, r0) */ + +#define OP_MASK_OPCODE 0x0ffc0000 /* just the opcode, ign operands */ +#define OP_NOP 0x00f00000 /* nop */ + +#define OP_MASK_ALL_BUT_IMM 0x0fffffc0 /* throw away imm, keep the rest */ +#define OP_SUB_SP_IMM 0x082bffc0 /* sub sp,sp,imm */ +#define OP_ADD_SP_IMM 0x080bffc0 /* add sp,sp,imm */ +#define OP_ADD_R22_SP_IMM 0x08096fc0 /* add r22,sp,imm */ +#define OP_STW_FP_SP_IMM 0x054bdfc0 /* stw fp,@(sp,imm) */ +#define OP_OR_SP_R0_IMM 0x03abf000 /* or sp,r0,imm */ + +/* no mask */ +#define OP_OR_FP_R0_SP 0x03a3d03f /* or fp,r0,sp */ +#define OP_OR_FP_SP_R0 0x03a3dfc0 /* or fp,sp,r0 */ +#define OP_OR_FP_IMM0_SP 0x03abd03f /* or fp,0x0,sp */ +#define OP_STW_FP_R22P_R0 0x0547d580 /* stw fp,@(r22+,r0) */ +#define OP_STW_LR_R22P_R0 0x0547e580 /* stw lr,@(r22+,r0) */ + +#define OP_MASK_OP_AND_RB 0x0ff80fc0 /* keep op and rb,throw away rest */ +#define OP_STW_SP_IMM 0x05480fc0 /* stw Ra,@(sp,imm) */ +#define OP_ST2W_SP_IMM 0x05680fc0 /* st2w Ra,@(sp,imm) */ +#define OP_STW_FP_IMM 0x05480f40 /* stw Ra,@(fp,imm) */ +#define OP_STW_FP_R0 0x05400f40 /* stw Ra,@(fp,r0) */ + +#define OP_MASK_FM_BIT 0x80000000 +#define OP_MASK_CC_BITS 0x70000000 +#define OP_MASK_SUB_INST 0x0fffffff + +#define EXTRACT_RA(op) (((op) >> 12) & 0x3f) +#define EXTRACT_RB(op) (((op) >> 6) & 0x3f) +#define EXTRACT_RC(op) (((op) & 0x3f) +#define EXTRACT_UIMM6(op) ((op) & 0x3f) +#define EXTRACT_IMM6(op) ((((int)EXTRACT_UIMM6(op)) << 26) >> 26) +#define EXTRACT_IMM26(op) ((((op)&0x0ff00000) >> 2) | ((op)&0x0003ffff)) +#define EXTRACT_IMM32(opl, opr) ((EXTRACT_UIMM6(opl) << 26)|EXTRACT_IMM26(opr)) + + +int +d30v_frame_chain_valid (chain, fi) + CORE_ADDR chain; + struct frame_info *fi; /* not used here */ +{ +#if 0 + return ((chain) != 0 && (fi) != 0 && (fi)->return_pc != 0); +#else + return ((chain) != 0 && (fi) != 0 && (fi)->frame <= chain); +#endif +} + +/* Discard from the stack the innermost frame, restoring all saved + registers. */ + +void +d30v_pop_frame () +{ + struct frame_info *frame = get_current_frame (); + CORE_ADDR fp; + int regnum; + struct frame_saved_regs fsr; + char raw_buffer[8]; + + fp = FRAME_FP (frame); + if (frame->dummy) + { + d30v_pop_dummy_frame(frame); + return; + } + + /* fill out fsr with the address of where each */ + /* register was stored in the frame */ + get_frame_saved_regs (frame, &fsr); + + /* now update the current registers with the old values */ + for (regnum = A0_REGNUM; regnum < A0_REGNUM+2 ; regnum++) + { + if (fsr.regs[regnum]) + { + read_memory (fsr.regs[regnum], raw_buffer, 8); + write_register_bytes (REGISTER_BYTE (regnum), raw_buffer, 8); + } + } + for (regnum = 0; regnum < SP_REGNUM; regnum++) + { + if (fsr.regs[regnum]) + { + write_register (regnum, read_memory_unsigned_integer (fsr.regs[regnum], 4)); + } + } + if (fsr.regs[PSW_REGNUM]) + { + write_register (PSW_REGNUM, read_memory_unsigned_integer (fsr.regs[PSW_REGNUM], 4)); + } + + write_register (PC_REGNUM, read_register(LR_REGNUM)); + write_register (SP_REGNUM, fp + frame->size); + target_store_registers (-1); + flush_cached_frames (); +} + +static int +check_prologue (op) + unsigned long op; +{ + /* add sp,sp,imm -- observed */ + if ((op & OP_MASK_ALL_BUT_IMM) == OP_ADD_SP_IMM) + return 1; + + /* add r22,sp,imm -- observed */ + if ((op & OP_MASK_ALL_BUT_IMM) == OP_ADD_R22_SP_IMM) + return 1; + + /* or fp,r0,sp -- observed */ + if (op == OP_OR_FP_R0_SP) + return 1; + + /* nop */ + if ((op & OP_MASK_OPCODE) == OP_NOP) + return 1; + + /* stw Ra,@(sp,r0) */ + if ((op & OP_MASK_ALL_BUT_RA) == OP_STW_SP_R0) + return 1; + + /* stw Ra,@(sp,0x0) */ + if ((op & OP_MASK_ALL_BUT_RA) == OP_STW_SP_IMM0) + return 1; + + /* st2w Ra,@(sp,r0) */ + if ((op & OP_MASK_ALL_BUT_RA) == OP_ST2W_SP_R0) + return 1; + + /* st2w Ra,@(sp,0x0) */ + if ((op & OP_MASK_ALL_BUT_RA) == OP_ST2W_SP_IMM0) + return 1; + + /* stw fp, @(r22+,r0) -- observed */ + if (op == OP_STW_FP_R22P_R0) + return 1; + + /* stw r62, @(r22+,r0) -- observed */ + if (op == OP_STW_LR_R22P_R0) + return 1; + + /* stw Ra, @(fp,r0) -- observed */ + if ((op & OP_MASK_ALL_BUT_RA) == OP_STW_FP_R0) + return 1; /* first arg */ + + /* stw Ra, @(fp,imm) -- observed */ + if ((op & OP_MASK_OP_AND_RB) == OP_STW_FP_IMM) + return 1; /* second and subsequent args */ + + /* stw fp,@(sp,imm) -- observed */ + if ((op & OP_MASK_ALL_BUT_IMM) == OP_STW_FP_SP_IMM) + return 1; + + /* st2w Ra,@(r22+,r0) */ + if ((op & OP_MASK_ALL_BUT_RA) == OP_ST2W_R22P_R0) + return 1; + + /* stw Ra, @(sp-) */ + if ((op & OP_MASK_ALL_BUT_RA) == OP_STW_SPM) + return 1; + + /* st2w Ra, @(sp-) */ + if ((op & OP_MASK_ALL_BUT_RA) == OP_ST2W_SPM) + return 1; + + /* sub.? sp,sp,imm */ + if ((op & OP_MASK_ALL_BUT_IMM) == OP_SUB_SP_IMM) + return 1; + + return 0; +} + +CORE_ADDR +d30v_skip_prologue (pc) + CORE_ADDR pc; +{ + unsigned long op[2]; + unsigned long opl, opr; /* left / right sub operations */ + unsigned long fm0, fm1; /* left / right mode bits */ + unsigned long cc0, cc1; + unsigned long op1, op2; + CORE_ADDR func_addr, func_end; + struct symtab_and_line sal; + + /* If we have line debugging information, then the end of the */ + /* prologue should the first assembly instruction of the first source line */ + if (find_pc_partial_function (pc, NULL, &func_addr, &func_end)) + { + sal = find_pc_line (func_addr, 0); + if ( sal.end && sal.end < func_end) + return sal.end; + } + + if (target_read_memory (pc, (char *)&op[0], 8)) + return pc; /* Can't access it -- assume no prologue. */ + + while (1) + { + opl = (unsigned long)read_memory_integer (pc, 4); + opr = (unsigned long)read_memory_integer (pc+4, 4); + + fm0 = (opl & OP_MASK_FM_BIT); + fm1 = (opr & OP_MASK_FM_BIT); + + cc0 = (opl & OP_MASK_CC_BITS); + cc1 = (opr & OP_MASK_CC_BITS); + + opl = (opl & OP_MASK_SUB_INST); + opr = (opr & OP_MASK_SUB_INST); + + if (fm0 && fm1) + { + /* long instruction (opl contains the opcode) */ + if (((opl & OP_MASK_ALL_BUT_IMM) != OP_ADD_SP_IMM) && /* add sp,sp,imm */ + ((opl & OP_MASK_ALL_BUT_IMM) != OP_ADD_R22_SP_IMM) && /* add r22,sp,imm */ + ((opl & OP_MASK_OP_AND_RB) != OP_STW_SP_IMM) && /* stw Ra, @(sp,imm) */ + ((opl & OP_MASK_OP_AND_RB) != OP_ST2W_SP_IMM)) /* st2w Ra, @(sp,imm) */ + break; + } + else + { + /* short instructions */ + if (fm0 && !fm1) + { + op1 = opr; + op2 = opl; + } + else + { + op1 = opl; + op2 = opr; + } + if (check_prologue(op1)) + { + if (!check_prologue(op2)) + { + /* if the previous opcode was really part of the prologue */ + /* and not just a NOP, then we want to break after both instructions */ + if ((op1 & OP_MASK_OPCODE) != OP_NOP) + pc += 8; + break; + } + } + else + break; + } + pc += 8; + } + return pc; +} + +static int end_of_stack; + +/* Given a GDB frame, determine the address of the calling function's frame. + This will be used to create a new GDB frame struct, and then + INIT_EXTRA_FRAME_INFO and INIT_FRAME_PC will be called for the new frame. +*/ + +CORE_ADDR +d30v_frame_chain (frame) + struct frame_info *frame; +{ + struct frame_saved_regs fsr; + + d30v_frame_find_saved_regs (frame, &fsr); + + if (end_of_stack) + return (CORE_ADDR)0; + + if (frame->return_pc == IMEM_START) + return (CORE_ADDR)0; + + if (!fsr.regs[FP_REGNUM]) + { + if (!fsr.regs[SP_REGNUM] || fsr.regs[SP_REGNUM] == STACK_START) + return (CORE_ADDR)0; + + return fsr.regs[SP_REGNUM]; + } + + if (!read_memory_unsigned_integer(fsr.regs[FP_REGNUM],4)) + return (CORE_ADDR)0; + + return read_memory_unsigned_integer(fsr.regs[FP_REGNUM],4); +} + +static int next_addr, uses_frame; +static int frame_size; + +static int +prologue_find_regs (op, fsr, addr) + unsigned long op; + struct frame_saved_regs *fsr; + CORE_ADDR addr; +{ + int n; + int offset; + + /* add sp,sp,imm -- observed */ + if ((op & OP_MASK_ALL_BUT_IMM) == OP_ADD_SP_IMM) + { + offset = EXTRACT_IMM6(op); + /*next_addr += offset;*/ + frame_size += -offset; + return 1; + } + + /* add r22,sp,imm -- observed */ + if ((op & OP_MASK_ALL_BUT_IMM) == OP_ADD_R22_SP_IMM) + { + offset = EXTRACT_IMM6(op); + next_addr = (offset - frame_size); + return 1; + } + + /* stw Ra, @(fp, offset) -- observed */ + if ((op & OP_MASK_OP_AND_RB) == OP_STW_FP_IMM) + { + n = EXTRACT_RA(op); + offset = EXTRACT_IMM6(op); + fsr->regs[n] = (offset - frame_size); + return 1; + } + + /* stw Ra, @(fp, r0) -- observed */ + if ((op & OP_MASK_ALL_BUT_RA) == OP_STW_FP_R0) + { + n = EXTRACT_RA(op); + fsr->regs[n] = (- frame_size); + return 1; + } + + /* or fp,0,sp -- observed */ + if ((op == OP_OR_FP_R0_SP) || + (op == OP_OR_FP_SP_R0) || + (op == OP_OR_FP_IMM0_SP)) + { + uses_frame = 1; + return 1; + } + + /* nop */ + if ((op & OP_MASK_OPCODE) == OP_NOP) + return 1; + + /* stw Ra,@(r22+,r0) -- observed */ + if ((op & OP_MASK_ALL_BUT_RA) == OP_STW_R22P_R0) + { + n = EXTRACT_RA(op); + fsr->regs[n] = next_addr; + next_addr += 4; + return 1; + } +#if 0 /* subsumed in pattern above */ + /* stw fp,@(r22+,r0) -- observed */ + if (op == OP_STW_FP_R22P_R0) + { + fsr->regs[FP_REGNUM] = next_addr; /* XXX */ + next_addr += 4; + return 1; + } + + /* stw r62,@(r22+,r0) -- observed */ + if (op == OP_STW_LR_R22P_R0) + { + fsr->regs[LR_REGNUM] = next_addr; + next_addr += 4; + return 1; + } +#endif + /* st2w Ra,@(r22+,r0) -- observed */ + if ((op & OP_MASK_ALL_BUT_RA) == OP_ST2W_R22P_R0) + { + n = EXTRACT_RA(op); + fsr->regs[n] = next_addr; + fsr->regs[n+1] = next_addr + 4; + next_addr += 8; + return 1; + } + + /* stw rn, @(sp-) */ + if ((op & OP_MASK_ALL_BUT_RA) == OP_STW_SPM) + { + n = EXTRACT_RA(op); + fsr->regs[n] = next_addr; + next_addr -= 4; + return 1; + } + + /* st2w Ra, @(sp-) */ + else if ((op & OP_MASK_ALL_BUT_RA) == OP_ST2W_SPM) + { + n = EXTRACT_RA(op); + fsr->regs[n] = next_addr; + fsr->regs[n+1] = next_addr+4; + next_addr -= 8; + return 1; + } + + /* sub sp,sp,imm */ + if ((op & OP_MASK_ALL_BUT_IMM) == OP_SUB_SP_IMM) + { + offset = EXTRACT_IMM6(op); + frame_size += -offset; + return 1; + } + + /* st rn, @(sp,0) -- observed */ + if (((op & OP_MASK_ALL_BUT_RA) == OP_STW_SP_R0) || + ((op & OP_MASK_ALL_BUT_RA) == OP_STW_SP_IMM0)) + { + n = EXTRACT_RA(op); + fsr->regs[n] = (- frame_size); + return 1; + } + + /* st2w rn, @(sp,0) */ + if (((op & OP_MASK_ALL_BUT_RA) == OP_ST2W_SP_R0) || + ((op & OP_MASK_ALL_BUT_RA) == OP_ST2W_SP_IMM0)) + { + n = EXTRACT_RA(op); + fsr->regs[n] = (- frame_size); + fsr->regs[n+1] = (- frame_size) + 4; + return 1; + } + + /* stw fp,@(sp,imm) -- observed */ + if ((op & OP_MASK_ALL_BUT_IMM) == OP_STW_FP_SP_IMM) + { + offset = EXTRACT_IMM6(op); + fsr->regs[FP_REGNUM] = (offset - frame_size); + return 1; + } + return 0; +} + +/* Put here the code to store, into a struct frame_saved_regs, the + addresses of the saved registers of frame described by FRAME_INFO. + This includes special registers such as pc and fp saved in special + ways in the stack frame. sp is even more special: the address we + return for it IS the sp for the next frame. */ +void +d30v_frame_find_saved_regs (fi, fsr) + struct frame_info *fi; + struct frame_saved_regs *fsr; +{ + CORE_ADDR fp, pc; + unsigned long opl, opr; + unsigned long op1, op2; + unsigned long fm0, fm1; + int i; + + fp = fi->frame; + memset (fsr, 0, sizeof (*fsr)); + next_addr = 0; + frame_size = 0; + end_of_stack = 0; + + uses_frame = 0; + + d30v_frame_find_saved_regs_offsets (fi, fsr); + + fi->size = frame_size; + + if (!fp) + fp = read_register(SP_REGNUM); + + for (i=0; i<NUM_REGS-1; i++) + if (fsr->regs[i]) + { + fsr->regs[i] = fsr->regs[i] + fp + frame_size; + } + + if (fsr->regs[LR_REGNUM]) + fi->return_pc = read_memory_unsigned_integer(fsr->regs[LR_REGNUM],4); + else + fi->return_pc = read_register(LR_REGNUM); + + /* the SP is not normally (ever?) saved, but check anyway */ + if (!fsr->regs[SP_REGNUM]) + { + /* if the FP was saved, that means the current FP is valid, */ + /* otherwise, it isn't being used, so we use the SP instead */ + if (uses_frame) + fsr->regs[SP_REGNUM] = read_register(FP_REGNUM) + fi->size; + else + { + fsr->regs[SP_REGNUM] = fp + fi->size; + fi->frameless = 1; + fsr->regs[FP_REGNUM] = 0; + } + } +} + +void +d30v_frame_find_saved_regs_offsets (fi, fsr) + struct frame_info *fi; + struct frame_saved_regs *fsr; +{ + CORE_ADDR fp, pc; + unsigned long opl, opr; + unsigned long op1, op2; + unsigned long fm0, fm1; + int i; + + fp = fi->frame; + memset (fsr, 0, sizeof (*fsr)); + next_addr = 0; + frame_size = 0; + end_of_stack = 0; + + pc = get_pc_function_start (fi->pc); + + uses_frame = 0; + while (pc < fi->pc) + { + opl = (unsigned long)read_memory_integer (pc, 4); + opr = (unsigned long)read_memory_integer (pc+4, 4); + + fm0 = (opl & OP_MASK_FM_BIT); + fm1 = (opr & OP_MASK_FM_BIT); + + opl = (opl & OP_MASK_SUB_INST); + opr = (opr & OP_MASK_SUB_INST); + + if (fm0 && fm1) + { + /* long instruction */ + if ((opl & OP_MASK_ALL_BUT_IMM) == OP_ADD_SP_IMM) + { + /* add sp,sp,n */ + long offset = EXTRACT_IMM32(opl, opr); + frame_size += -offset; + } + else if ((opl & OP_MASK_ALL_BUT_IMM) == OP_ADD_R22_SP_IMM) + { + /* add r22,sp,offset */ + long offset = EXTRACT_IMM32(opl,opr); + next_addr = (offset - frame_size); + } + else if ((opl & OP_MASK_OP_AND_RB) == OP_STW_SP_IMM) + { + /* st Ra, @(sp,imm) */ + long offset = EXTRACT_IMM32(opl, opr); + short n = EXTRACT_RA(opl); + fsr->regs[n] = (offset - frame_size); + } + else if ((opl & OP_MASK_OP_AND_RB) == OP_ST2W_SP_IMM) + { + /* st2w Ra, @(sp,offset) */ + long offset = EXTRACT_IMM32(opl, opr); + short n = EXTRACT_RA(opl); + fsr->regs[n] = (offset - frame_size); + fsr->regs[n+1] = (offset - frame_size) + 4; + } + else if ((opl & OP_MASK_ALL_BUT_IMM) == OP_OR_SP_R0_IMM) + { + end_of_stack = 1; + } + else + break; + } + else + { + /* short instructions */ + if (fm0 && !fm1) + { + op2 = opl; + op1 = opr; + } + else + { + op1 = opl; + op2 = opr; + } + if (!prologue_find_regs(op1,fsr,pc) || !prologue_find_regs(op2,fsr,pc)) + break; + } + pc += 8; + } + +#if 0 + fi->size = frame_size; + + if (!fp) + fp = read_register(SP_REGNUM); + + for (i=0; i<NUM_REGS-1; i++) + if (fsr->regs[i]) + { + fsr->regs[i] = fsr->regs[i] + fp + frame_size; + } + + if (fsr->regs[LR_REGNUM]) + fi->return_pc = read_memory_unsigned_integer(fsr->regs[LR_REGNUM],4); + else + fi->return_pc = read_register(LR_REGNUM); + + /* the SP is not normally (ever?) saved, but check anyway */ + if (!fsr->regs[SP_REGNUM]) + { + /* if the FP was saved, that means the current FP is valid, */ + /* otherwise, it isn't being used, so we use the SP instead */ + if (uses_frame) + fsr->regs[SP_REGNUM] = read_register(FP_REGNUM) + fi->size; + else + { + fsr->regs[SP_REGNUM] = fp + fi->size; + fi->frameless = 1; + fsr->regs[FP_REGNUM] = 0; + } + } +#endif +} + +void +d30v_init_extra_frame_info (fromleaf, fi) + int fromleaf; + struct frame_info *fi; +{ + struct frame_saved_regs dummy; + + if (fi->next && (fi->pc == 0)) + fi->pc = fi->next->return_pc; + + d30v_frame_find_saved_regs_offsets (fi, &dummy); + + if (uses_frame == 0) + fi->frameless = 1; + else + fi->frameless = 0; + + if ((fi->next == 0) && (uses_frame == 0)) + /* innermost frame and it's "frameless", + so the fi->frame field is wrong, fix it! */ + fi->frame = read_sp (); + + if (dummy.regs[LR_REGNUM]) + { + /* it was saved, grab it! */ + dummy.regs[LR_REGNUM] += (fi->frame + frame_size); + fi->return_pc = read_memory_unsigned_integer(dummy.regs[LR_REGNUM],4); + } + else + fi->return_pc = read_register(LR_REGNUM); +} + +void +d30v_init_frame_pc (fromleaf, prev) + int fromleaf; + struct frame_info *prev; +{ + /* default value, put here so we can breakpoint on it and + see if the default value is really the right thing to use */ + prev->pc = (fromleaf ? SAVED_PC_AFTER_CALL (prev->next) : \ + prev->next ? FRAME_SAVED_PC (prev->next) : read_pc ()); +} + +static void d30v_print_register PARAMS ((int regnum, int tabular)); + +static void +d30v_print_register (regnum, tabular) + int regnum; + int tabular; +{ + if (regnum < A0_REGNUM) + { + if (tabular) + printf_filtered ("%08x", read_register (regnum)); + else + printf_filtered ("0x%x %d", read_register (regnum), + read_register (regnum)); + } + else + { + char regbuf[MAX_REGISTER_RAW_SIZE]; + + read_relative_register_raw_bytes (regnum, regbuf); + + val_print (REGISTER_VIRTUAL_TYPE (regnum), regbuf, 0, 0, + gdb_stdout, 'x', 1, 0, Val_pretty_default); + + if (!tabular) + { + printf_filtered (" "); + val_print (REGISTER_VIRTUAL_TYPE (regnum), regbuf, 0, 0, + gdb_stdout, 'd', 1, 0, Val_pretty_default); + } + } +} + +static void +d30v_print_flags () +{ + long psw = read_register (PSW_REGNUM); + printf_filtered ("flags #1"); + printf_filtered (" (sm) %d", (psw & PSW_SM) != 0); + printf_filtered (" (ea) %d", (psw & PSW_EA) != 0); + printf_filtered (" (db) %d", (psw & PSW_DB) != 0); + printf_filtered (" (ds) %d", (psw & PSW_DS) != 0); + printf_filtered (" (ie) %d", (psw & PSW_IE) != 0); + printf_filtered (" (rp) %d", (psw & PSW_RP) != 0); + printf_filtered (" (md) %d\n", (psw & PSW_MD) != 0); + + printf_filtered ("flags #2"); + printf_filtered (" (f0) %d", (psw & PSW_F0) != 0); + printf_filtered (" (f1) %d", (psw & PSW_F1) != 0); + printf_filtered (" (f2) %d", (psw & PSW_F2) != 0); + printf_filtered (" (f3) %d", (psw & PSW_F3) != 0); + printf_filtered (" (s) %d", (psw & PSW_S) != 0); + printf_filtered (" (v) %d", (psw & PSW_V) != 0); + printf_filtered (" (va) %d", (psw & PSW_VA) != 0); + printf_filtered (" (c) %d\n", (psw & PSW_C) != 0); +} + +static void +print_flags_command (args, from_tty) + char *args; + int from_tty; +{ + d30v_print_flags (); +} + +void +d30v_do_registers_info (regnum, fpregs) + int regnum; + int fpregs; +{ + long long num1, num2; + long psw; + + if (regnum != -1) + { + if (REGISTER_NAME (0) == NULL || REGISTER_NAME (0)[0] == '\000') + return; + + printf_filtered ("%s ", REGISTER_NAME (regnum)); + d30v_print_register (regnum, 0); + + printf_filtered ("\n"); + return; + } + + /* Have to print all the registers. Format them nicely. */ + + printf_filtered ("PC="); + print_address (read_pc (), gdb_stdout); + + printf_filtered (" PSW="); + d30v_print_register (PSW_REGNUM, 1); + + printf_filtered (" BPC="); + print_address (read_register (BPC_REGNUM), gdb_stdout); + + printf_filtered (" BPSW="); + d30v_print_register (BPSW_REGNUM, 1); + printf_filtered ("\n"); + + printf_filtered ("DPC="); + print_address (read_register (DPC_REGNUM), gdb_stdout); + + printf_filtered (" DPSW="); + d30v_print_register (DPSW_REGNUM, 1); + + printf_filtered (" IBA="); + print_address (read_register (IBA_REGNUM), gdb_stdout); + printf_filtered ("\n"); + + printf_filtered ("RPT_C="); + d30v_print_register (RPT_C_REGNUM, 1); + + printf_filtered (" RPT_S="); + print_address (read_register (RPT_S_REGNUM), gdb_stdout); + + printf_filtered (" RPT_E="); + print_address (read_register (RPT_E_REGNUM), gdb_stdout); + printf_filtered ("\n"); + + printf_filtered ("MOD_S="); + print_address (read_register (MOD_S_REGNUM), gdb_stdout); + + printf_filtered (" MOD_E="); + print_address (read_register (MOD_E_REGNUM), gdb_stdout); + printf_filtered ("\n"); + + printf_filtered ("EIT_VB="); + print_address (read_register (EIT_VB_REGNUM), gdb_stdout); + + printf_filtered (" INT_S="); + d30v_print_register (INT_S_REGNUM, 1); + + printf_filtered (" INT_M="); + d30v_print_register (INT_M_REGNUM, 1); + printf_filtered ("\n"); + + d30v_print_flags (); + for (regnum = 0; regnum <= 63;) + { + int i; + + printf_filtered ("R%d-R%d ", regnum, regnum + 7); + if (regnum < 10) + printf_filtered (" "); + if (regnum + 7 < 10) + printf_filtered (" "); + + for (i = 0; i < 8; i++) + { + printf_filtered (" "); + d30v_print_register (regnum++, 1); + } + + printf_filtered ("\n"); + } + + printf_filtered ("A0-A1 "); + + d30v_print_register (A0_REGNUM, 1); + printf_filtered (" "); + d30v_print_register (A1_REGNUM, 1); + printf_filtered ("\n"); +} + +CORE_ADDR +d30v_fix_call_dummy (dummyname, start_sp, fun, nargs, args, type, gcc_p) + char *dummyname; + CORE_ADDR start_sp; + CORE_ADDR fun; + int nargs; + value_ptr *args; + struct type *type; + int gcc_p; +{ + int regnum; + CORE_ADDR sp; + char buffer[MAX_REGISTER_RAW_SIZE]; + struct frame_info *frame = get_current_frame (); + frame->dummy = start_sp; + /*start_sp |= DMEM_START;*/ + + sp = start_sp; + for (regnum = 0; regnum < NUM_REGS; regnum++) + { + sp -= REGISTER_RAW_SIZE(regnum); + store_address (buffer, REGISTER_RAW_SIZE(regnum), read_register(regnum)); + write_memory (sp, buffer, REGISTER_RAW_SIZE(regnum)); + } + write_register (SP_REGNUM, (LONGEST)sp); + /* now we need to load LR with the return address */ + write_register (LR_REGNUM, (LONGEST)d30v_call_dummy_address()); + return sp; +} + +static void +d30v_pop_dummy_frame (fi) + struct frame_info *fi; +{ + CORE_ADDR sp = fi->dummy; + int regnum; + + for (regnum = 0; regnum < NUM_REGS; regnum++) + { + sp -= REGISTER_RAW_SIZE(regnum); + write_register(regnum, read_memory_unsigned_integer (sp, REGISTER_RAW_SIZE(regnum))); + } + flush_cached_frames (); /* needed? */ +} + + +CORE_ADDR +d30v_push_arguments (nargs, args, sp, struct_return, struct_addr) + int nargs; + value_ptr *args; + CORE_ADDR sp; + int struct_return; + CORE_ADDR struct_addr; +{ + int i, len, index=0, regnum=2; + char buffer[4], *contents; + LONGEST val; + CORE_ADDR ptrs[10]; + +#if 0 + /* Pass 1. Put all large args on stack */ + for (i = 0; i < nargs; i++) + { + value_ptr arg = args[i]; + struct type *arg_type = check_typedef (VALUE_TYPE (arg)); + len = TYPE_LENGTH (arg_type); + contents = VALUE_CONTENTS(arg); + val = extract_signed_integer (contents, len); + if (len > 4) + { + /* put on stack and pass pointers */ + sp -= len; + write_memory (sp, contents, len); + ptrs[index++] = sp; + } + } +#endif + index = 0; + + for (i = 0; i < nargs; i++) + { + value_ptr arg = args[i]; + struct type *arg_type = check_typedef (VALUE_TYPE (arg)); + len = TYPE_LENGTH (arg_type); + contents = VALUE_CONTENTS(arg); + if (len > 4) + { + /* we need multiple registers */ + int ndx; + + for (ndx = 0; len > 0; ndx += 8, len -= 8) + { + if (regnum & 1) + regnum++; /* all args > 4 bytes start in even register */ + + if (regnum < 18) + { + val = extract_signed_integer (&contents[ndx], 4); + write_register (regnum++, val); + + if (len >= 8) + val = extract_signed_integer (&contents[ndx+4], 4); + else + val = extract_signed_integer (&contents[ndx+4], len-4); + write_register (regnum++, val); + } + else + { + /* no more registers available. put it on the stack */ + + /* all args > 4 bytes are padded to a multiple of 8 bytes + and start on an 8 byte boundary */ + if (sp & 7) + sp -= (sp & 7); /* align it */ + + sp -= ((len + 7) & ~7); /* allocate space */ + write_memory (sp, &contents[ndx], len); + break; + } + } + } + else + { + if (regnum < 18 ) + { + val = extract_signed_integer (contents, len); + write_register (regnum++, val); + } + else + { + /* all args are padded to a multiple of 4 bytes (at least) */ + sp -= ((len + 3) & ~3); + write_memory (sp, contents, len); + } + } + } + if (sp & 7) + /* stack pointer is not on an 8 byte boundary -- align it */ + sp -= (sp & 7); + return sp; +} + + +/* pick an out-of-the-way place to set the return value */ +/* for an inferior function call. The link register is set to this */ +/* value and a momentary breakpoint is set there. When the breakpoint */ +/* is hit, the dummy frame is popped and the previous environment is */ +/* restored. */ + +CORE_ADDR +d30v_call_dummy_address () +{ + CORE_ADDR entry; + struct minimal_symbol *sym; + + entry = entry_point_address (); + + if (entry != 0) + return entry; + + sym = lookup_minimal_symbol ("_start", NULL, symfile_objfile); + + if (!sym || MSYMBOL_TYPE (sym) != mst_text) + return 0; + else + return SYMBOL_VALUE_ADDRESS (sym); +} + +/* Given a return value in `regbuf' with a type `valtype', + extract and copy its value into `valbuf'. */ + +void +d30v_extract_return_value (valtype, regbuf, valbuf) + struct type *valtype; + char regbuf[REGISTER_BYTES]; + char *valbuf; +{ + memcpy (valbuf, regbuf + REGISTER_BYTE (2), TYPE_LENGTH (valtype)); +} + +/* The following code implements access to, and display of, the D30V's + instruction trace buffer. The buffer consists of 64K or more + 4-byte words of data, of which each words includes an 8-bit count, + an 8-bit segment number, and a 16-bit instruction address. + + In theory, the trace buffer is continuously capturing instruction + data that the CPU presents on its "debug bus", but in practice, the + ROMified GDB stub only enables tracing when it continues or steps + the program, and stops tracing when the program stops; so it + actually works for GDB to read the buffer counter out of memory and + then read each trace word. The counter records where the tracing + stops, but there is no record of where it started, so we remember + the PC when we resumed and then search backwards in the trace + buffer for a word that includes that address. This is not perfect, + because you will miss trace data if the resumption PC is the target + of a branch. (The value of the buffer counter is semi-random, any + trace data from a previous program stop is gone.) */ + +/* The address of the last word recorded in the trace buffer. */ + +#define DBBC_ADDR (0xd80000) + +/* The base of the trace buffer, at least for the "Board_0". */ + +#define TRACE_BUFFER_BASE (0xf40000) + +static void trace_command PARAMS ((char *, int)); + +static void untrace_command PARAMS ((char *, int)); + +static void trace_info PARAMS ((char *, int)); + +static void tdisassemble_command PARAMS ((char *, int)); + +static void display_trace PARAMS ((int, int)); + +/* True when instruction traces are being collected. */ + +static int tracing; + +/* Remembered PC. */ + +static CORE_ADDR last_pc; + +/* True when trace output should be displayed whenever program stops. */ + +static int trace_display; + +/* True when trace listing should include source lines. */ + +static int default_trace_show_source = 1; + +struct trace_buffer { + int size; + short *counts; + CORE_ADDR *addrs; +} trace_data; + +static void +trace_command (args, from_tty) + char *args; + int from_tty; +{ + /* Clear the host-side trace buffer, allocating space if needed. */ + trace_data.size = 0; + if (trace_data.counts == NULL) + trace_data.counts = (short *) xmalloc (65536 * sizeof(short)); + if (trace_data.addrs == NULL) + trace_data.addrs = (CORE_ADDR *) xmalloc (65536 * sizeof(CORE_ADDR)); + + tracing = 1; + + printf_filtered ("Tracing is now on.\n"); +} + +static void +untrace_command (args, from_tty) + char *args; + int from_tty; +{ + tracing = 0; + + printf_filtered ("Tracing is now off.\n"); +} + +static void +trace_info (args, from_tty) + char *args; + int from_tty; +{ + int i; + + if (trace_data.size) + { + printf_filtered ("%d entries in trace buffer:\n", trace_data.size); + + for (i = 0; i < trace_data.size; ++i) + { + printf_filtered ("%d: %d instruction%s at 0x%x\n", + i, trace_data.counts[i], + (trace_data.counts[i] == 1 ? "" : "s"), + trace_data.addrs[i]); + } + } + else + printf_filtered ("No entries in trace buffer.\n"); + + printf_filtered ("Tracing is currently %s.\n", (tracing ? "on" : "off")); +} + +/* Print the instruction at address MEMADDR in debugged memory, + on STREAM. Returns length of the instruction, in bytes. */ + +static int +print_insn (memaddr, stream) + CORE_ADDR memaddr; + GDB_FILE *stream; +{ + /* If there's no disassembler, something is very wrong. */ + if (tm_print_insn == NULL) + abort (); + + if (TARGET_BYTE_ORDER == BIG_ENDIAN) + tm_print_insn_info.endian = BFD_ENDIAN_BIG; + else + tm_print_insn_info.endian = BFD_ENDIAN_LITTLE; + return (*tm_print_insn) (memaddr, &tm_print_insn_info); +} + +void +d30v_eva_prepare_to_trace () +{ + if (!tracing) + return; + + last_pc = read_register (PC_REGNUM); +} + +/* Collect trace data from the target board and format it into a form + more useful for display. */ + +void +d30v_eva_get_trace_data () +{ + int count, i, j, oldsize; + int trace_addr, trace_seg, trace_cnt, next_cnt; + unsigned int last_trace, trace_word, next_word; + unsigned int *tmpspace; + + if (!tracing) + return; + + tmpspace = xmalloc (65536 * sizeof(unsigned int)); + + last_trace = read_memory_unsigned_integer (DBBC_ADDR, 2) << 2; + + /* Collect buffer contents from the target, stopping when we reach + the word recorded when execution resumed. */ + + count = 0; + while (last_trace > 0) + { + QUIT; + trace_word = + read_memory_unsigned_integer (TRACE_BUFFER_BASE + last_trace, 4); + trace_addr = trace_word & 0xffff; + last_trace -= 4; + /* Ignore an apparently nonsensical entry. */ + if (trace_addr == 0xffd5) + continue; + tmpspace[count++] = trace_word; + if (trace_addr == last_pc) + break; + if (count > 65535) + break; + } + + /* Move the data to the host-side trace buffer, adjusting counts to + include the last instruction executed and transforming the address + into something that GDB likes. */ + + for (i = 0; i < count; ++i) + { + trace_word = tmpspace[i]; + next_word = ((i == 0) ? 0 : tmpspace[i - 1]); + trace_addr = trace_word & 0xffff; + next_cnt = (next_word >> 24) & 0xff; + j = trace_data.size + count - i - 1; + trace_data.addrs[j] = (trace_addr << 2) + 0x1000000; + trace_data.counts[j] = next_cnt + 1; + } + + oldsize = trace_data.size; + trace_data.size += count; + + free (tmpspace); + + if (trace_display) + display_trace (oldsize, trace_data.size); +} + +static void +tdisassemble_command (arg, from_tty) + char *arg; + int from_tty; +{ + int i, count; + CORE_ADDR low, high; + char *space_index; + + if (!arg) + { + low = 0; + high = trace_data.size; + } + else if (!(space_index = (char *) strchr (arg, ' '))) + { + low = parse_and_eval_address (arg); + high = low + 5; + } + else + { + /* Two arguments. */ + *space_index = '\0'; + low = parse_and_eval_address (arg); + high = parse_and_eval_address (space_index + 1); + if (high < low) + high = low; + } + + printf_filtered ("Dump of trace from %d to %d:\n", low, high); + + display_trace (low, high); + + printf_filtered ("End of trace dump.\n"); + gdb_flush (gdb_stdout); +} + +static void +display_trace (low, high) + int low, high; +{ + int i, count, trace_show_source, first, suppress; + CORE_ADDR next_address; + + trace_show_source = default_trace_show_source; + if (!have_full_symbols () && !have_partial_symbols()) + { + trace_show_source = 0; + printf_filtered ("No symbol table is loaded. Use the \"file\" command.\n"); + printf_filtered ("Trace will not display any source.\n"); + } + + first = 1; + suppress = 0; + for (i = low; i < high; ++i) + { + next_address = trace_data.addrs[i]; + count = trace_data.counts[i]; + while (count-- > 0) + { + QUIT; + if (trace_show_source) + { + struct symtab_and_line sal, sal_prev; + + sal_prev = find_pc_line (next_address - 4, 0); + sal = find_pc_line (next_address, 0); + + if (sal.symtab) + { + if (first || sal.line != sal_prev.line) + print_source_lines (sal.symtab, sal.line, sal.line + 1, 0); + suppress = 0; + } + else + { + if (!suppress) + /* FIXME-32x64--assumes sal.pc fits in long. */ + printf_filtered ("No source file for address %s.\n", + local_hex_string((unsigned long) sal.pc)); + suppress = 1; + } + } + first = 0; + print_address (next_address, gdb_stdout); + printf_filtered (":"); + printf_filtered ("\t"); + wrap_here (" "); + next_address = next_address + print_insn (next_address, gdb_stdout); + printf_filtered ("\n"); + gdb_flush (gdb_stdout); + } + } +} + +extern void (*target_resume_hook) PARAMS ((void)); +extern void (*target_wait_loop_hook) PARAMS ((void)); + +void +_initialize_d30v_tdep () +{ + tm_print_insn = print_insn_d30v; + + target_resume_hook = d30v_eva_prepare_to_trace; + target_wait_loop_hook = d30v_eva_get_trace_data; + + add_info ("flags", print_flags_command, "Print d30v flags."); + + add_com ("trace", class_support, trace_command, + "Enable tracing of instruction execution."); + + add_com ("untrace", class_support, untrace_command, + "Disable tracing of instruction execution."); + + add_com ("tdisassemble", class_vars, tdisassemble_command, + "Disassemble the trace buffer.\n\ +Two optional arguments specify a range of trace buffer entries\n\ +as reported by info trace (NOT addresses!)."); + + add_info ("trace", trace_info, + "Display info about the trace data buffer."); + + add_show_from_set (add_set_cmd ("tracedisplay", no_class, + var_integer, (char *)&trace_display, + "Set automatic display of trace.\n", &setlist), + &showlist); + add_show_from_set (add_set_cmd ("tracesource", no_class, + var_integer, (char *)&default_trace_show_source, + "Set display of source code with trace.\n", &setlist), + &showlist); + +} diff --git a/contrib/gdb/gdb/dbug-rom.c b/contrib/gdb/gdb/dbug-rom.c new file mode 100644 index 000000000000..fa9c97440ed8 --- /dev/null +++ b/contrib/gdb/gdb/dbug-rom.c @@ -0,0 +1,164 @@ +/* Remote debugging interface to dBUG ROM monitor for GDB, the GNU debugger. + Copyright 1996 Free Software Foundation, Inc. + + Written by Stan Shebs of Cygnus Support. + +This file is part of GDB. + +This program 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 of the License, or +(at your option) any later version. + +This program 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; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* dBUG is a monitor supplied on various Motorola boards, including + m68k, ColdFire, and PowerPC-based designs. The code here assumes + the ColdFire, and (as of 9/25/96) has only been tested with a + ColdFire IDP board. */ + +#include "defs.h" +#include "gdbcore.h" +#include "target.h" +#include "monitor.h" +#include "serial.h" + +static void dbug_open PARAMS ((char *args, int from_tty)); + +static void +dbug_supply_register (regname, regnamelen, val, vallen) + char *regname; + int regnamelen; + char *val; + int vallen; +{ + int regno; + + if (regnamelen != 2) + return; + + switch (regname[0]) + { + case 'S': + if (regname[1] != 'R') + return; + regno = PS_REGNUM; + break; + case 'P': + if (regname[1] != 'C') + return; + regno = PC_REGNUM; + break; + case 'D': + if (regname[1] < '0' || regname[1] > '7') + return; + regno = regname[1] - '0' + D0_REGNUM; + break; + case 'A': + if (regname[1] < '0' || regname[1] > '7') + return; + regno = regname[1] - '0' + A0_REGNUM; + break; + default: + return; + } + + monitor_supply_register (regno, val); +} + +/* This array of registers needs to match the indexes used by GDB. The + whole reason this exists is because the various ROM monitors use + different names than GDB does, and don't support all the registers + either. So, typing "info reg sp" becomes an "A7". */ + +static char *dbug_regnames[NUM_REGS] = +{ + "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", + "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", + "SR", "PC" + /* no float registers */ +}; +static struct target_ops dbug_ops; +static struct monitor_ops dbug_cmds ; + +static char *dbug_inits[] = {"\r", NULL}; + + +static void +init_dbug_cmds(void) +{ + dbug_cmds.flags = MO_CLR_BREAK_USES_ADDR | MO_GETMEM_NEEDS_RANGE | MO_FILL_USES_ADDR; + dbug_cmds.init = dbug_inits; /* Init strings */ + dbug_cmds.cont = "go\r"; /* continue command */ + dbug_cmds.step = "step\r"; /* single step */ + dbug_cmds.stop = NULL; /* interrupt command */ + dbug_cmds.set_break = "br %x\r"; /* set a breakpoint */ + dbug_cmds.clr_break = "br -c %x\r"; /* clear a breakpoint */ + dbug_cmds.clr_all_break = "br -c\r"; /* clear all breakpoints */ + dbug_cmds.fill = "bf.b %x %x %x"; /* fill (start end val) */ + dbug_cmds.setmem.cmdb = "mm.b %x %x\r"; /* setmem.cmdb (addr, value) */ + dbug_cmds.setmem.cmdw = "mm.w %x %x\r"; /* setmem.cmdw (addr, value) */ + dbug_cmds.setmem.cmdl = "mm.l %x %x\r"; /* setmem.cmdl (addr, value) */ + dbug_cmds.setmem.cmdll = NULL; /* setmem.cmdll (addr, value) */ + dbug_cmds.setmem.resp_delim = NULL; /* setmem.resp_delim */ + dbug_cmds.setmem.term = NULL; /* setmem.term */ + dbug_cmds.setmem.term_cmd = NULL; /* setmem.term_cmd */ + dbug_cmds.getmem.cmdb = "md.b %x %x\r"; /* getmem.cmdb (addr, addr2) */ + dbug_cmds.getmem.cmdw = "md.w %x %x\r"; /* getmem.cmdw (addr, addr2) */ + dbug_cmds.getmem.cmdl = "md.l %x %x\r"; /* getmem.cmdl (addr, addr2) */ + dbug_cmds.getmem.cmdll = NULL; /* getmem.cmdll (addr, addr2) */ + dbug_cmds.getmem.resp_delim = ":"; /* getmem.resp_delim */ + dbug_cmds.getmem.term = NULL; /* getmem.term */ + dbug_cmds.getmem.term_cmd = NULL; /* getmem.term_cmd */ + dbug_cmds.setreg.cmd = "rm %s %x\r"; /* setreg.cmd (name, value) */ + dbug_cmds.setreg.resp_delim = NULL; /* setreg.resp_delim */ + dbug_cmds.setreg.term = NULL; /* setreg.term */ + dbug_cmds.setreg.term_cmd = NULL; /* setreg.term_cmd */ + dbug_cmds.getreg.cmd = "rd %s\r"; /* getreg.cmd (name) */ + dbug_cmds.getreg.resp_delim = ":"; /* getreg.resp_delim */ + dbug_cmds.getreg.term = NULL; /* getreg.term */ + dbug_cmds.getreg.term_cmd = NULL; /* getreg.term_cmd */ + dbug_cmds.dump_registers = "rd\r"; /* dump_registers */ + dbug_cmds.register_pattern = "\\(\\w+\\) +:\\([0-9a-fA-F]+\\b\\)"; /* register_pattern */ + dbug_cmds.supply_register = dbug_supply_register; /* supply_register */ + dbug_cmds.load_routine = NULL; /* load_routine (defaults to SRECs) */ + dbug_cmds.load = "dl\r"; /* download command */ + dbug_cmds.loadresp = "\n"; /* load response */ + dbug_cmds.prompt = "dBUG>"; /* monitor command prompt */ + dbug_cmds.line_term = "\r"; /* end-of-line terminator */ + dbug_cmds.cmd_end = NULL; /* optional command terminator */ + dbug_cmds.target = &dbug_ops ; /* target operations */ + dbug_cmds.stopbits = SERIAL_1_STOPBITS; /* number of stop bits */ + dbug_cmds.regnames = dbug_regnames; /* registers names */ + dbug_cmds.magic = MONITOR_OPS_MAGIC ; /* magic */ +} /* init_debug_ops */ + +static void +dbug_open(args, from_tty) + char *args; + int from_tty; +{ + monitor_open (args, &dbug_cmds, from_tty); +} + +void +_initialize_dbug_rom () +{ + init_dbug_cmds() ; + init_monitor_ops (&dbug_ops); + + dbug_ops.to_shortname = "dbug"; + dbug_ops.to_longname = "dBUG monitor"; + dbug_ops.to_doc = "Debug via the dBUG monitor.\n\ +Specify the serial device it is connected to (e.g. /dev/ttya)."; + dbug_ops.to_open = dbug_open; + + add_target (&dbug_ops); +} diff --git a/contrib/gdb/gdb/debugify.c b/contrib/gdb/gdb/debugify.c new file mode 100644 index 000000000000..841b58913948 --- /dev/null +++ b/contrib/gdb/gdb/debugify.c @@ -0,0 +1,109 @@ + +/* Debug macros for developemnt. + Copyright 1997 + Free Software Foundation, Inc. + +This file is part of GDB. + +This program 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 of the License, or +(at your option) any later version. + +This program 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; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#define DEBUGIFY +#include "debugify.h" +#include "config.h" + +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#else +#include <strings.h> +#endif + +#define REDIRECT + +static FILE *fout = NULL; +static char fname[128]; +static int file_cnt = 0; /* count number of open files */ + +void +puts_dbg (data) + const char *data; +{ + FILE *fdbg; + + fdbg = fopen ("dbg.log", "a+"); + if (fdbg == NULL) + return; + fprintf (fdbg, data); + fclose (fdbg); +} + +/* Can't easily get the message back to gdb... write to a log instead. */ +void +fputs_dbg (data, fakestream) + const char *data; + FILE *fakestream; +{ +#ifdef REDIRECT + puts_dbg (data); +#else /* REDIRECT */ + + if (fakestream == stdout || fakestream == stderr || fakestream == NULL) + { + if (fout == NULL) + { + for (file_cnt = 0; file_cnt < 20; file_cnt++) + { + sprintf (fname, "dbg%d.log", file_cnt); + if ((fout = fopen (fname), "r") != NULL) + fclose (fout); + else + break; + } + fout = fopen (fname, "w"); + if (fout == NULL) + return; + } + fakestream = fout; + } + fprintf (fakestream, data); + fflush (fakestream); +#endif /* REDIRECT */ +} + +void +#ifdef ANSI_PROTOTYPES +printf_dbg (const char *format,...) +#else +printf_dbg (va_alist) + va_dcl +#endif +{ + va_list args; + char buf[256]; +#ifdef ANSI_PROTOTYPES + va_start (args, format); +#else + char *format; + + va_start (args); + format = va_arg (args, char *); +#endif + vsprintf (buf, format, args); + puts_dbg (buf); + va_end (args); +} diff --git a/contrib/gdb/gdb/debugify.h b/contrib/gdb/gdb/debugify.h new file mode 100644 index 000000000000..b4ae92bc93b7 --- /dev/null +++ b/contrib/gdb/gdb/debugify.h @@ -0,0 +1,83 @@ + +/* Debug macros for development. + Copyright 1997 + Free Software Foundation, Inc. + +This file is part of GDB. + +This program 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 of the License, or +(at your option) any later version. + +This program 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; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef _DEBUGIFY_H_ +#define _DEBUGIFY_H_ + +#include "ansidecl.h" + +#ifdef ANSI_PROTOTYPES +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#ifdef DEBUGIFY +#include <assert.h> +#ifdef TO_SCREEN +#ifdef _Win32 +#define DBG(x) OutputDebugString x +#else +#define DBG(x) printf x +#endif +#elif TO_GDB +#define DBG(x) printf_unfiltered x +#elif TO_POPUP +#ifdef _Win32 +#define DBG(x) MessageBox x +#else +#define DBG(x) printf x +#endif +#else /* default: TO_FILE "gdb.log" */ +#define DBG(x) printf_dbg x +#endif + +#define ASSERT(x) assert(x) + +#else /* DEBUGIFY */ +#define DBG(x) +#define ASSERT(x) +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifdef REDIRECT +#define printf_unfiltered printf_dbg +#define fputs_unfiltered fputs_dbg + extern void fputs_dbg PARAMS ((const char *fmt, FILE * fakestream)); +#endif /* REDIRECT */ + + extern void puts_dbg PARAMS ((const char *fmt)); +#ifdef ANSI_PROTOTYPES + extern void printf_dbg PARAMS ((const char *fmt,...)); +#else + extern void printf_dbg PARAMS ((va_alist va_dcl)); +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* _DEBUGIFY_H_ */ diff --git a/contrib/gdb/gdb/dve3900-rom.c b/contrib/gdb/gdb/dve3900-rom.c new file mode 100644 index 000000000000..c76da776d60a --- /dev/null +++ b/contrib/gdb/gdb/dve3900-rom.c @@ -0,0 +1,962 @@ +/* Remote debugging interface for Densan DVE-R3900 ROM monitor for + GDB, the GNU debugger. + Copyright 1997 Free Software Foundation, Inc. + +This file is part of GDB. + +This program 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 of the License, or +(at your option) any later version. + +This program 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; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "defs.h" +#include "gdbcore.h" +#include "target.h" +#include "monitor.h" +#include "serial.h" +#include "inferior.h" +#include "command.h" +#include "gdb_string.h" +#include <time.h> + +/* Type of function passed to bfd_map_over_sections. */ + +typedef void (*section_map_func) PARAMS ((bfd *abfd, asection *sect, PTR obj)); + +/* Packet escape character used by Densan monitor. */ + +#define PESC 0xdc + +/* Maximum packet size. This is actually smaller than necessary + just to be safe. */ + +#define MAXPSIZE 1024 + +/* External functions. */ + +extern void report_transfer_performance PARAMS ((unsigned long, + time_t, time_t)); + +/* Certain registers are "bitmapped", in that the monitor can only display + them or let the user modify them as a series of named bitfields. + This structure describes a field in a bitmapped register. */ + +struct bit_field +{ + char *prefix; /* string appearing before the value */ + char *suffix; /* string appearing after the value */ + char *user_name; /* name used by human when entering field value */ + int length; /* number of bits in the field */ + int start; /* starting (least significant) bit number of field */ +}; + +/* Local functions for register manipulation. */ + +static void r3900_supply_register PARAMS ((char *regname, int regnamelen, + char *val, int vallen)); +static void fetch_bad_vaddr PARAMS ((void)); +static unsigned long fetch_fields PARAMS ((struct bit_field *bf)); +static void fetch_bitmapped_register PARAMS ((int regno, + struct bit_field *bf)); +static void r3900_fetch_registers PARAMS ((int regno)); +static void store_bitmapped_register PARAMS ((int regno, + struct bit_field *bf)); +static void r3900_store_registers PARAMS ((int regno)); + +/* Local functions for fast binary loading. */ + +static void write_long PARAMS ((char *buf, long n)); +static void write_long_le PARAMS ((char *buf, long n)); +static int debug_readchar PARAMS ((int hex)); +static void debug_write PARAMS ((unsigned char *buf, int buflen)); +static void ignore_packet PARAMS ((void)); +static void send_packet PARAMS ((char type, unsigned char *buf, int buflen, + int seq)); +static void process_read_request PARAMS ((unsigned char *buf, int buflen)); +static void count_section PARAMS ((bfd *abfd, asection *s, + unsigned int *section_count)); +static void load_section PARAMS ((bfd *abfd, asection *s, + unsigned int *data_count)); +static void r3900_load PARAMS ((char *filename, int from_tty)); + +/* Miscellaneous local functions. */ + +static void r3900_open PARAMS ((char *args, int from_tty)); + + +/* Pointers to static functions in monitor.c for fetching and storing + registers. We can't use these function in certain cases where the Densan + monitor acts perversely: for registers that it displays in bit-map + format, and those that can't be modified at all. In those cases + we have to use our own functions to fetch and store their values. */ + +static void (*orig_monitor_fetch_registers) PARAMS ((int regno)); +static void (*orig_monitor_store_registers) PARAMS ((int regno)); + +/* Pointer to static function in monitor. for loading programs. + We use this function for loading S-records via the serial link. */ + +static void (*orig_monitor_load) PARAMS ((char *file, int from_tty)); + +/* This flag is set if a fast ethernet download should be used. */ + +static int ethernet = 0; + +/* This array of registers needs to match the indexes used by GDB. The + whole reason this exists is because the various ROM monitors use + different names than GDB does, and don't support all the registers + either. */ + +static char *r3900_regnames[NUM_REGS] = +{ + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", + + "S", /* PS_REGNUM */ + "l", /* LO_REGNUM */ + "h", /* HI_REGNUM */ + "B", /* BADVADDR_REGNUM */ + "Pcause", /* CAUSE_REGNUM */ + "p" /* PC_REGNUM */ +}; + + +/* Table of register names produced by monitor's register dump command. */ + +static struct reg_entry +{ + char *name; + int regno; +} reg_table[] = +{ + { "r0_zero", 0 }, { "r1_at", 1 }, { "r2_v0", 2 }, { "r3_v1", 3 }, + { "r4_a0", 4 }, { "r5_a1", 5 }, { "r6_a2", 6 }, { "r7_a3", 7 }, + { "r8_t0", 8 }, { "r9_t1", 9 }, { "r10_t2", 10 }, { "r11_t3", 11 }, + { "r12_t4", 12 }, { "r13_t5", 13 }, { "r14_t6", 14 }, { "r15_t7", 15 }, + { "r16_s0", 16 }, { "r17_s1", 17 }, { "r18_s2", 18 }, { "r19_s3", 19 }, + { "r20_s4", 20 }, { "r21_s5", 21 }, { "r22_s6", 22 }, { "r23_s7", 23 }, + { "r24_t8", 24 }, { "r25_t9", 25 }, { "r26_k0", 26 }, { "r27_k1", 27 }, + { "r28_gp", 28 }, { "r29_sp", 29 }, { "r30_fp", 30 }, { "r31_ra", 31 }, + { "HI", HI_REGNUM }, + { "LO", LO_REGNUM }, + { "PC", PC_REGNUM }, + { "BadV", BADVADDR_REGNUM }, + { NULL, 0 } +}; + + +/* The monitor displays the cache register along with the status register, + as if they were a single register. So when we want to fetch the + status register, parse but otherwise ignore the fields of the + cache register that the monitor displays. Register fields that should + be ignored have a length of zero in the tables below. */ + +static struct bit_field status_fields [] = +{ + /* Status register portion */ + { "SR[<CU=", " ", "cu", 4, 28 }, + { "RE=", " ", "re", 1, 25 }, + { "BEV=", " ", "bev", 1, 22 }, + { "TS=", " ", "ts", 1, 21 }, + { "Nmi=", " ", "nmi", 1, 20 }, + { "INT=", " ", "int", 6, 10 }, + { "SW=", ">]", "sw", 2, 8 }, + { "[<KUO=", " ", "kuo", 1, 5 }, + { "IEO=", " ", "ieo", 1, 4 }, + { "KUP=", " ", "kup", 1, 3 }, + { "IEP=", " ", "iep", 1, 2 }, + { "KUC=", " ", "kuc", 1, 1 }, + { "IEC=", ">]", "iec", 1, 0 }, + + /* Cache register portion (dummy for parsing only) */ + { "CR[<IalO="," ", "ialo", 0, 13 }, + { "DalO=", " ", "dalo", 0, 12 }, + { "IalP=", " ", "ialp", 0, 11 }, + { "DalP=", " ", "dalp", 0, 10 }, + { "IalC=", " ", "ialc", 0, 9 }, + { "DalC=", ">] ", "dalc", 0, 8 }, + + { NULL, NULL, 0, 0 } /* end of table marker */ +}; + + +#if 0 /* FIXME: Enable when we add support for modifying cache register. */ +static struct bit_field cache_fields [] = +{ + /* Status register portion (dummy for parsing only) */ + { "SR[<CU=", " ", "cu", 0, 28 }, + { "RE=", " ", "re", 0, 25 }, + { "BEV=", " ", "bev", 0, 22 }, + { "TS=", " ", "ts", 0, 21 }, + { "Nmi=", " ", "nmi", 0, 20 }, + { "INT=", " ", "int", 0, 10 }, + { "SW=", ">]", "sw", 0, 8 }, + { "[<KUO=", " ", "kuo", 0, 5 }, + { "IEO=", " ", "ieo", 0, 4 }, + { "KUP=", " ", "kup", 0, 3 }, + { "IEP=", " ", "iep", 0, 2 }, + { "KUC=", " ", "kuc", 0, 1 }, + { "IEC=", ">]", "iec", 0, 0 }, + + /* Cache register portion */ + { "CR[<IalO="," ", "ialo", 1, 13 }, + { "DalO=", " ", "dalo", 1, 12 }, + { "IalP=", " ", "ialp", 1, 11 }, + { "DalP=", " ", "dalp", 1, 10 }, + { "IalC=", " ", "ialc", 1, 9 }, + { "DalC=", ">] ", "dalc", 1, 8 }, + + { NULL, NULL, NULL, 0, 0 } /* end of table marker */ +}; +#endif + + +static struct bit_field cause_fields[] = +{ + { "<BD=", " ", "bd", 1, 31 }, + { "CE=", " ", "ce", 2, 28 }, + { "IP=", " ", "ip", 6, 10 }, + { "SW=", " ", "sw", 2, 8 }, + { "EC=", ">]" , "ec", 5, 2 }, + + { NULL, NULL, NULL, 0, 0 } /* end of table marker */ +}; + + +/* The monitor prints register values in the form + + regname = xxxx xxxx + + We look up the register name in a table, and remove the embedded space in + the hex value before passing it to monitor_supply_register. */ + +static void +r3900_supply_register (regname, regnamelen, val, vallen) + char *regname; + int regnamelen; + char *val; + int vallen; +{ + int regno = -1; + int i; + char valbuf[10]; + char *p; + + /* Perform some sanity checks on the register name and value. */ + if (regnamelen < 2 || regnamelen > 7 || vallen != 9) + return; + + /* Look up the register name. */ + for (i = 0; reg_table[i].name != NULL; i++) + { + int rlen = strlen (reg_table[i].name); + if (rlen == regnamelen && strncmp (regname, reg_table[i].name, rlen) == 0) + { + regno = reg_table[i].regno; + break; + } + } + if (regno == -1) + return; + + /* Copy the hex value to a buffer and eliminate the embedded space. */ + for (i = 0, p = valbuf; i < vallen; i++) + if (val[i] != ' ') + *p++ = val[i]; + *p = '\0'; + + monitor_supply_register (regno, valbuf); +} + + +/* Fetch the BadVaddr register. Unlike the other registers, this + one can't be modified, and the monitor won't even prompt to let + you modify it. */ + +static void +fetch_bad_vaddr() +{ + char buf[20]; + + monitor_printf ("xB\r"); + monitor_expect ("BadV=", NULL, 0); + monitor_expect_prompt (buf, sizeof(buf)); + monitor_supply_register (BADVADDR_REGNUM, buf); +} + + +/* Read a series of bit fields from the monitor, and return their + combined binary value. */ + +static unsigned long +fetch_fields (bf) + struct bit_field *bf; +{ + char buf[20]; + unsigned long val = 0; + unsigned long bits; + + for ( ; bf->prefix != NULL; bf++) + { + monitor_expect (bf->prefix, NULL, 0); /* get prefix */ + monitor_expect (bf->suffix, buf, sizeof (buf)); /* hex value, suffix */ + if (bf->length != 0) + { + bits = strtoul (buf, NULL, 16); /* get field value */ + bits &= ((1 << bf->length) - 1); /* mask out useless bits */ + val |= bits << bf->start; /* insert into register */ + } + + } + + return val; +} + + +static void +fetch_bitmapped_register (regno, bf) + int regno; + struct bit_field *bf; +{ + unsigned long val; + unsigned char regbuf[MAX_REGISTER_RAW_SIZE]; + + monitor_printf ("x%s\r", r3900_regnames[regno]); + val = fetch_fields (bf); + monitor_printf (".\r"); + monitor_expect_prompt (NULL, 0); + + /* supply register stores in target byte order, so swap here */ + + store_unsigned_integer (regbuf, REGISTER_RAW_SIZE (regno), val); + supply_register (regno, regbuf); + +} + + +/* Fetch all registers (if regno is -1), or one register from the + monitor. For most registers, we can use the generic monitor_ + monitor_fetch_registers function. But others are displayed in + a very unusual fashion by the monitor, and must be handled specially. */ + +static void +r3900_fetch_registers (regno) + int regno; +{ + switch (regno) + { + case BADVADDR_REGNUM: + fetch_bad_vaddr (); + return; + case PS_REGNUM: + fetch_bitmapped_register (PS_REGNUM, status_fields); + return; + case CAUSE_REGNUM: + fetch_bitmapped_register (CAUSE_REGNUM, cause_fields); + return; + default: + orig_monitor_fetch_registers (regno); + } +} + + +/* Write the new value of the bitmapped register to the monitor. */ + +static void +store_bitmapped_register (regno, bf) + int regno; + struct bit_field *bf; +{ + unsigned long oldval, newval; + + /* Fetch the current value of the register. */ + monitor_printf ("x%s\r", r3900_regnames[regno]); + oldval = fetch_fields (bf); + newval = read_register (regno); + + /* To save time, write just the fields that have changed. */ + for ( ; bf->prefix != NULL; bf++) + { + if (bf->length != 0) + { + unsigned long oldbits, newbits, mask; + + mask = (1 << bf->length) - 1; + oldbits = (oldval >> bf->start) & mask; + newbits = (newval >> bf->start) & mask; + if (oldbits != newbits) + monitor_printf ("%s %x ", bf->user_name, newbits); + } + } + + monitor_printf (".\r"); + monitor_expect_prompt (NULL, 0); +} + + +static void +r3900_store_registers (regno) + int regno; +{ + switch (regno) + { + case PS_REGNUM: + store_bitmapped_register (PS_REGNUM, status_fields); + return; + case CAUSE_REGNUM: + store_bitmapped_register (CAUSE_REGNUM, cause_fields); + return; + default: + orig_monitor_store_registers (regno); + } +} + + +/* Write a 4-byte integer to the buffer in big-endian order. */ + +static void +write_long (buf, n) + char *buf; + long n; +{ + buf[0] = (n >> 24) & 0xff; + buf[1] = (n >> 16) & 0xff; + buf[2] = (n >> 8) & 0xff; + buf[3] = n & 0xff; +} + + +/* Write a 4-byte integer to the buffer in little-endian order. */ + +static void +write_long_le (buf, n) + char *buf; + long n; +{ + buf[0] = n & 0xff; + buf[1] = (n >> 8) & 0xff; + buf[2] = (n >> 16) & 0xff; + buf[3] = (n >> 24) & 0xff; +} + + +/* Read a character from the monitor. If remote debugging is on, + print the received character. If HEX is non-zero, print the + character in hexadecimal; otherwise, print it in ASCII. */ + +static int +debug_readchar (hex) + int hex; +{ + char buf [10]; + int c = monitor_readchar (); + + if (remote_debug > 0) + { + if (hex) + sprintf (buf, "[%02x]", c & 0xff); + else if (c == '\0') + strcpy (buf, "\\0"); + else + { + buf[0] = c; + buf[1] = '\0'; + } + puts_debug ("Read -->", buf, "<--"); + } + return c; +} + + +/* Send a buffer of characters to the monitor. If remote debugging is on, + print the sent buffer in hex. */ + +static void +debug_write (buf, buflen) + unsigned char *buf; + int buflen; +{ + char s[10]; + + monitor_write (buf, buflen); + + if (remote_debug > 0) + { + while (buflen-- > 0) + { + sprintf (s, "[%02x]", *buf & 0xff); + puts_debug ("Sent -->", s, "<--"); + buf++; + } + } +} + + +/* Ignore a packet sent to us by the monitor. It send packets + when its console is in "communications interface" mode. A packet + is of this form: + + start of packet flag (one byte: 0xdc) + packet type (one byte) + length (low byte) + length (high byte) + data (length bytes) + + The last two bytes of the data field are a checksum, but we don't + bother to verify it. +*/ + +static void +ignore_packet () +{ + int c; + int len; + + /* Ignore lots of trash (messages about section addresses, for example) + until we see the start of a packet. */ + for (len = 0; len < 256; len++) + { + c = debug_readchar (0); + if (c == PESC) + break; + } + if (len == 8) + error ("Packet header byte not found; %02x seen instead.", c); + + /* Read the packet type and length. */ + c = debug_readchar (1); /* type */ + + c = debug_readchar (1); /* low byte of length */ + len = c & 0xff; + + c = debug_readchar (1); /* high byte of length */ + len += (c & 0xff) << 8; + + /* Ignore the rest of the packet. */ + while (len-- > 0) + c = debug_readchar (1); +} + + +/* Encapsulate some data into a packet and send it to the monitor. + + The 'p' packet is a special case. This is a packet we send + in response to a read ('r') packet from the monitor. This function + appends a one-byte sequence number to the data field of such a packet. +*/ + +static void +send_packet (type, buf, buflen, seq) + char type; + unsigned char *buf; + int buflen, seq; +{ + unsigned char hdr[4]; + int len = buflen; + int sum, i; + + /* If this is a 'p' packet, add one byte for a sequence number. */ + if (type == 'p') + len++; + + /* If the buffer has a non-zero length, add two bytes for a checksum. */ + if (len > 0) + len += 2; + + /* Write the packet header. */ + hdr[0] = PESC; + hdr[1] = type; + hdr[2] = len & 0xff; + hdr[3] = (len >> 8) & 0xff; + debug_write (hdr, sizeof (hdr)); + + if (len) + { + /* Write the packet data. */ + debug_write (buf, buflen); + + /* Write the sequence number if this is a 'p' packet. */ + if (type == 'p') + { + hdr[0] = seq; + debug_write (hdr, 1); + } + + /* Write the checksum. */ + sum = 0; + for (i = 0; i < buflen; i++) + { + int tmp = (buf[i] & 0xff); + if (i & 1) + sum += tmp; + else + sum += tmp << 8; + } + if (type == 'p') + { + if (buflen & 1) + sum += (seq & 0xff); + else + sum += (seq & 0xff) << 8; + } + sum = (sum & 0xffff) + ((sum >> 16) & 0xffff); + sum += (sum >> 16) & 1; + sum = ~sum; + + hdr[0] = (sum >> 8) & 0xff; + hdr[1] = sum & 0xff; + debug_write (hdr, 2); + } +} + + +/* Respond to an expected read request from the monitor by sending + data in chunks. Handle all acknowledgements and handshaking packets. + + The monitor expects a response consisting of a one or more 'p' packets, + each followed by a portion of the data requested. The 'p' packet + contains only a four-byte integer, the value of which is the number + of bytes of data we are about to send. Following the 'p' packet, + the monitor expects the data bytes themselves in raw, unpacketized, + form, without even a checksum. + */ + +static void +process_read_request (buf, buflen) + unsigned char *buf; + int buflen; +{ + unsigned char len[4]; + int i, chunk; + unsigned char seq; + + /* Discard the read request. FIXME: we have to hope it's for + the exact number of bytes we want to send; should check for this. */ + ignore_packet (); + + for (i = chunk = 0, seq = 0; i < buflen; i += chunk, seq++) + { + /* Don't send more than MAXPSIZE bytes at a time. */ + chunk = buflen - i; + if (chunk > MAXPSIZE) + chunk = MAXPSIZE; + + /* Write a packet containing the number of bytes we are sending. */ + write_long_le (len, chunk); + send_packet ('p', len, sizeof (len), seq); + + /* Write the data in raw form following the packet. */ + debug_write (&buf[i], chunk); + + /* Discard the ACK packet. */ + ignore_packet (); + } + + /* Send an "end of data" packet. */ + send_packet ('e', "", 0, 0); +} + + +/* Count loadable sections (helper function for r3900_load). */ + +static void +count_section (abfd, s, section_count) + bfd *abfd; + asection *s; + unsigned int *section_count; +{ + if (s->flags & SEC_LOAD && bfd_section_size (abfd, s) != 0) + (*section_count)++; +} + + +/* Load a single BFD section (helper function for r3900_load). + + WARNING: this code is filled with assumptions about how + the Densan monitor loads programs. The monitor issues + packets containing read requests, but rather than respond + to them in an general way, we expect them to following + a certain pattern. + + For example, we know that the monitor will start loading by + issuing an 8-byte read request for the binary file header. + We know this is coming and ignore the actual contents + of the read request packet. +*/ + +static void +load_section (abfd, s, data_count) + bfd *abfd; + asection *s; + unsigned int *data_count; +{ + if (s->flags & SEC_LOAD) + { + bfd_size_type section_size = bfd_section_size (abfd, s); + bfd_vma section_base = bfd_section_lma (abfd, s); + unsigned char *buffer; + unsigned char header[8]; + + /* Don't output zero-length sections. */ + if (section_size == 0) + return; + if (data_count) + *data_count += section_size; + + /* Print some fluff about the section being loaded. */ + printf_filtered ("Loading section %s, size 0x%lx lma ", + bfd_section_name (abfd, s), (long)section_size); + print_address_numeric (section_base, 1, gdb_stdout); + printf_filtered ("\n"); + gdb_flush (gdb_stdout); + + /* Write the section header (location and size). */ + write_long (&header[0], (long)section_base); + write_long (&header[4], (long)section_size); + process_read_request (header, sizeof (header)); + + /* Read the section contents into a buffer, write it out, + then free the buffer. */ + buffer = (unsigned char *) xmalloc (section_size); + bfd_get_section_contents (abfd, s, buffer, 0, section_size); + process_read_request (buffer, section_size); + free (buffer); + } +} + + +/* When the ethernet is used as the console port on the Densan board, + we can use the "Rm" command to do a fast binary load. The format + of the download data is: + + number of sections (4 bytes) + starting address (4 bytes) + repeat for each section: + location address (4 bytes) + section size (4 bytes) + binary data + + The 4-byte fields are all in big-endian order. + + Using this command is tricky because we have to put the monitor + into a special funky "communications interface" mode, in which + it sends and receives packets of data along with the normal prompt. + */ + +static void +r3900_load (filename, from_tty) + char *filename; + int from_tty; +{ + bfd *abfd; + unsigned int data_count = 0; + time_t start_time, end_time; /* for timing of download */ + int section_count = 0; + unsigned char buffer[8]; + + /* If we are not using the ethernet, use the normal monitor load, + which sends S-records over the serial link. */ + if (!ethernet) + { + orig_monitor_load (filename, from_tty); + return; + } + + /* Open the file. */ + if (filename == NULL || filename[0] == 0) + filename = get_exec_file (1); + abfd = bfd_openr (filename, 0); + if (!abfd) + error ("Unable to open file %s\n", filename); + if (bfd_check_format (abfd, bfd_object) == 0) + error ("File is not an object file\n"); + + /* Output the "vconsi" command to get the monitor in the communication + state where it will accept a load command. This will cause + the monitor to emit a packet before each prompt, so ignore the packet. */ + monitor_printf ("vconsi\r"); + ignore_packet (); + monitor_expect_prompt (NULL, 0); + + /* Output the "Rm" (load) command and respond to the subsequent "open" + packet by sending an ACK packet. */ + monitor_printf ("Rm\r"); + ignore_packet (); + send_packet ('a', "", 0, 0); + + /* Output the fast load header (number of sections and starting address). */ + bfd_map_over_sections ((bfd *) abfd, (section_map_func) count_section, + §ion_count); + write_long (&buffer[0], (long)section_count); + if (exec_bfd) + write_long (&buffer[4], (long)bfd_get_start_address (exec_bfd)); + else + write_long (&buffer[4], 0); + process_read_request (buffer, sizeof (buffer)); + + /* Output the section data. */ + start_time = time (NULL); + bfd_map_over_sections (abfd, (section_map_func) load_section, &data_count); + end_time = time (NULL); + + /* Acknowledge the close packet and put the monitor back into + "normal" mode so it won't send packets any more. */ + ignore_packet (); + send_packet ('a', "", 0, 0); + monitor_expect_prompt (NULL, 0); + monitor_printf ("vconsx\r"); + monitor_expect_prompt (NULL, 0); + + /* Print start address and download performance information. */ + printf_filtered ("Start address 0x%lx\n", (long)bfd_get_start_address (abfd)); + report_transfer_performance (data_count, start_time, end_time); + + /* Finally, make the PC point at the start address */ + if (exec_bfd) + write_pc (bfd_get_start_address (exec_bfd)); + + inferior_pid = 0; /* No process now */ + + /* This is necessary because many things were based on the PC at the + time that we attached to the monitor, which is no longer valid + now that we have loaded new code (and just changed the PC). + Another way to do this might be to call normal_stop, except that + the stack may not be valid, and things would get horribly + confused... */ + clear_symtab_users (); +} + + +/* Commands to send to the monitor when first connecting: + * The bare carriage return forces a prompt from the monitor + (monitor doesn't prompt immediately after a reset). + * The "vconsx" switches the monitor back to interactive mode + in case an aborted download had left it in packet mode. + * The "Xtr" command causes subsequent "t" (trace) commands to display + the general registers only. + * The "Xxr" command does the same thing for the "x" (examine + registers) command. + * The "bx" command clears all breakpoints. +*/ + +static char *r3900_inits[] = {"\r", "vconsx\r", "Xtr\r", "Xxr\r", "bx\r", NULL}; +static char *dummy_inits[] = { NULL }; + +static struct target_ops r3900_ops; +static struct monitor_ops r3900_cmds; + +static void +r3900_open (args, from_tty) + char *args; + int from_tty; +{ + char buf[64]; + int i; + + monitor_open (args, &r3900_cmds, from_tty); + + /* We have to handle sending the init strings ourselves, because + the first two strings we send (carriage returns) may not be echoed + by the monitor, but the rest will be. */ + monitor_printf_noecho ("\r\r"); + for (i = 0; r3900_inits[i] != NULL; i++) + { + monitor_printf (r3900_inits[i]); + monitor_expect_prompt (NULL, 0); + } + + /* Attempt to determine whether the console device is ethernet or serial. + This will tell us which kind of load to use (S-records over a serial + link, or the Densan fast binary multi-section format over the net). */ + + ethernet = 0; + monitor_printf ("v\r"); + if (monitor_expect ("console device :", NULL, 0) != -1) + if (monitor_expect ("\n", buf, sizeof (buf)) != -1) + if (strstr (buf, "ethernet") != NULL) + ethernet = 1; + monitor_expect_prompt (NULL, 0); +} + +void +_initialize_r3900_rom () +{ + r3900_cmds.flags = MO_NO_ECHO_ON_OPEN | + MO_ADDR_BITS_REMOVE | + MO_CLR_BREAK_USES_ADDR | + MO_GETMEM_READ_SINGLE | + MO_PRINT_PROGRAM_OUTPUT; + + r3900_cmds.init = dummy_inits; + r3900_cmds.cont = "g\r"; + r3900_cmds.step = "t\r"; + r3900_cmds.set_break = "b %A\r"; /* COREADDR */ + r3900_cmds.clr_break = "b %A,0\r"; /* COREADDR */ + r3900_cmds.fill = "fx %A s %x %x\r"; /* COREADDR, len, val */ + + r3900_cmds.setmem.cmdb = "sx %A %x\r"; /* COREADDR, val */ + r3900_cmds.setmem.cmdw = "sh %A %x\r"; /* COREADDR, val */ + r3900_cmds.setmem.cmdl = "sw %A %x\r"; /* COREADDR, val */ + + r3900_cmds.getmem.cmdb = "sx %A\r"; /* COREADDR */ + r3900_cmds.getmem.cmdw = "sh %A\r"; /* COREADDR */ + r3900_cmds.getmem.cmdl = "sw %A\r"; /* COREADDR */ + r3900_cmds.getmem.resp_delim = " : "; + r3900_cmds.getmem.term = " "; + r3900_cmds.getmem.term_cmd = ".\r"; + + r3900_cmds.setreg.cmd = "x%s %x\r"; /* regname, val */ + + r3900_cmds.getreg.cmd = "x%s\r"; /* regname */ + r3900_cmds.getreg.resp_delim = "="; + r3900_cmds.getreg.term = " "; + r3900_cmds.getreg.term_cmd = ".\r"; + + r3900_cmds.dump_registers = "x\r"; + r3900_cmds.register_pattern = + "\\([a-zA-Z0-9_]+\\) *=\\([0-9a-f]+ [0-9a-f]+\\b\\)"; + r3900_cmds.supply_register = r3900_supply_register; + /* S-record download, via "keyboard port". */ + r3900_cmds.load = "r0\r"; + r3900_cmds.prompt = "#"; + r3900_cmds.line_term = "\r"; + r3900_cmds.target = &r3900_ops; + r3900_cmds.stopbits = SERIAL_1_STOPBITS; + r3900_cmds.regnames = r3900_regnames; + r3900_cmds.magic = MONITOR_OPS_MAGIC; + + init_monitor_ops (&r3900_ops); + + r3900_ops.to_shortname = "r3900"; + r3900_ops.to_longname = "R3900 monitor"; + r3900_ops.to_doc = "Debug using the DVE R3900 monitor.\n\ +Specify the serial device it is connected to (e.g. /dev/ttya)."; + r3900_ops.to_open = r3900_open; + + /* Override the functions to fetch and store registers. But save the + addresses of the default functions, because we will use those functions + for "normal" registers. */ + + orig_monitor_fetch_registers = r3900_ops.to_fetch_registers; + orig_monitor_store_registers = r3900_ops.to_store_registers; + r3900_ops.to_fetch_registers = r3900_fetch_registers; + r3900_ops.to_store_registers = r3900_store_registers; + + /* Override the load function, but save the address of the default + function to use when loading S-records over a serial link. */ + orig_monitor_load = r3900_ops.to_load; + r3900_ops.to_load = r3900_load; + + add_target (&r3900_ops); +} diff --git a/contrib/gdb/gdb/mn10200-tdep.c b/contrib/gdb/gdb/mn10200-tdep.c new file mode 100644 index 000000000000..cb7ead093137 --- /dev/null +++ b/contrib/gdb/gdb/mn10200-tdep.c @@ -0,0 +1,919 @@ +/* Target-dependent code for the Matsushita MN10200 for GDB, the GNU debugger. + Copyright 1997 Free Software Foundation, Inc. + +This file is part of GDB. + +This program 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 of the License, or +(at your option) any later version. + +This program 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; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "defs.h" +#include "frame.h" +#include "inferior.h" +#include "obstack.h" +#include "target.h" +#include "value.h" +#include "bfd.h" +#include "gdb_string.h" +#include "gdbcore.h" +#include "symfile.h" + + +/* Should call_function allocate stack space for a struct return? */ +int +mn10200_use_struct_convention (gcc_p, type) + int gcc_p; + struct type *type; +{ + return (TYPE_NFIELDS (type) > 1 || TYPE_LENGTH (type) > 8); +} + + + +/* The main purpose of this file is dealing with prologues to extract + information about stack frames and saved registers. + + For reference here's how prologues look on the mn10200: + + With frame pointer: + mov fp,a0 + mov sp,fp + add <size>,sp + Register saves for d2, d3, a1, a2 as needed. Saves start + at fp - <size> + <outgoing_args_size> and work towards higher + addresses. Note that the saves are actually done off the stack + pointer in the prologue! This makes for smaller code and easier + prologue scanning as the displacement fields will unlikely + be more than 8 bits! + + Without frame pointer: + add <size>,sp + Register saves for d2, d3, a1, a2 as needed. Saves start + at sp + <outgoing_args_size> and work towards higher addresses. + + Out of line prologue: + add <local size>,sp -- optional + jsr __prologue + add <outgoing_size>,sp -- optional + + The stack pointer remains constant throughout the life of most + functions. As a result the compiler will usually omit the + frame pointer, so we must handle frame pointerless functions. */ + +/* Analyze the prologue to determine where registers are saved, + the end of the prologue, etc etc. Return the end of the prologue + scanned. + + We store into FI (if non-null) several tidbits of information: + + * stack_size -- size of this stack frame. Note that if we stop in + certain parts of the prologue/epilogue we may claim the size of the + current frame is zero. This happens when the current frame has + not been allocated yet or has already been deallocated. + + * fsr -- Addresses of registers saved in the stack by this frame. + + * status -- A (relatively) generic status indicator. It's a bitmask + with the following bits: + + MY_FRAME_IN_SP: The base of the current frame is actually in + the stack pointer. This can happen for frame pointerless + functions, or cases where we're stopped in the prologue/epilogue + itself. For these cases mn10200_analyze_prologue will need up + update fi->frame before returning or analyzing the register + save instructions. + + MY_FRAME_IN_FP: The base of the current frame is in the + frame pointer register ($a2). + + CALLER_A2_IN_A0: $a2 from the caller's frame is temporarily + in $a0. This can happen if we're stopped in the prologue. + + NO_MORE_FRAMES: Set this if the current frame is "start" or + if the first instruction looks like mov <imm>,sp. This tells + frame chain to not bother trying to unwind past this frame. */ + +#define MY_FRAME_IN_SP 0x1 +#define MY_FRAME_IN_FP 0x2 +#define CALLER_A2_IN_A0 0x4 +#define NO_MORE_FRAMES 0x8 + +static CORE_ADDR +mn10200_analyze_prologue (fi, pc) + struct frame_info *fi; + CORE_ADDR pc; +{ + CORE_ADDR func_addr, func_end, addr, stop; + CORE_ADDR stack_size; + unsigned char buf[4]; + int status; + char *name; + int out_of_line_prologue = 0; + + /* Use the PC in the frame if it's provided to look up the + start of this function. */ + pc = (fi ? fi->pc : pc); + + /* Find the start of this function. */ + status = find_pc_partial_function (pc, &name, &func_addr, &func_end); + + /* Do nothing if we couldn't find the start of this function or if we're + stopped at the first instruction in the prologue. */ + if (status == 0) + return pc; + + /* If we're in start, then give up. */ + if (strcmp (name, "start") == 0) + { + if (fi) + fi->status = NO_MORE_FRAMES; + return pc; + } + + /* At the start of a function our frame is in the stack pointer. */ + if (fi) + fi->status = MY_FRAME_IN_SP; + + /* If we're physically on an RTS instruction, then our frame has already + been deallocated. + + fi->frame is bogus, we need to fix it. */ + if (fi && fi->pc + 1 == func_end) + { + status = target_read_memory (fi->pc, buf, 1); + if (status != 0) + { + if (fi->next == NULL) + fi->frame = read_sp (); + return fi->pc; + } + + if (buf[0] == 0xfe) + { + if (fi->next == NULL) + fi->frame = read_sp (); + return fi->pc; + } + } + + /* Similarly if we're stopped on the first insn of a prologue as our + frame hasn't been allocated yet. */ + if (fi && fi->pc == func_addr) + { + if (fi->next == NULL) + fi->frame = read_sp (); + return fi->pc; + } + + /* Figure out where to stop scanning. */ + stop = fi ? fi->pc : func_end; + + /* Don't walk off the end of the function. */ + stop = stop > func_end ? func_end : stop; + + /* Start scanning on the first instruction of this function. */ + addr = func_addr; + + status = target_read_memory (addr, buf, 2); + if (status != 0) + { + if (fi && fi->next == NULL && fi->status & MY_FRAME_IN_SP) + fi->frame = read_sp (); + return addr; + } + + /* First see if this insn sets the stack pointer; if so, it's something + we won't understand, so quit now. */ + if (buf[0] == 0xdf + || (buf[0] == 0xf4 && buf[1] == 0x77)) + { + if (fi) + fi->status = NO_MORE_FRAMES; + return addr; + } + + /* Now see if we have a frame pointer. + + Search for mov a2,a0 (0xf278) + then mov a3,a2 (0xf27e). */ + + if (buf[0] == 0xf2 && buf[1] == 0x78) + { + /* Our caller's $a2 will be found in $a0 now. Note it for + our callers. */ + if (fi) + fi->status |= CALLER_A2_IN_A0; + addr += 2; + if (addr >= stop) + { + /* We still haven't allocated our local stack. Handle this + as if we stopped on the first or last insn of a function. */ + if (fi && fi->next == NULL) + fi->frame = read_sp (); + return addr; + } + + status = target_read_memory (addr, buf, 2); + if (status != 0) + { + if (fi && fi->next == NULL) + fi->frame = read_sp (); + return addr; + } + if (buf[0] == 0xf2 && buf[1] == 0x7e) + { + addr += 2; + + /* Our frame pointer is valid now. */ + if (fi) + { + fi->status |= MY_FRAME_IN_FP; + fi->status &= ~MY_FRAME_IN_SP; + } + if (addr >= stop) + return addr; + } + else + { + if (fi && fi->next == NULL) + fi->frame = read_sp (); + return addr; + } + } + + /* Next we should allocate the local frame. + + Search for add imm8,a3 (0xd3XX) + or add imm16,a3 (0xf70bXXXX) + or add imm24,a3 (0xf467XXXXXX). + + If none of the above was found, then this prologue has + no stack, and therefore can't have any register saves, + so quit now. */ + status = target_read_memory (addr, buf, 2); + if (status != 0) + { + if (fi && fi->next == NULL && (fi->status & MY_FRAME_IN_SP)) + fi->frame = read_sp (); + return addr; + } + if (buf[0] == 0xd3) + { + stack_size = extract_signed_integer (&buf[1], 1); + if (fi) + fi->stack_size = stack_size; + addr += 2; + if (addr >= stop) + { + if (fi && fi->next == NULL && (fi->status & MY_FRAME_IN_SP)) + fi->frame = read_sp () - stack_size; + return addr; + } + } + else if (buf[0] == 0xf7 && buf[1] == 0x0b) + { + status = target_read_memory (addr + 2, buf, 2); + if (status != 0) + { + if (fi && fi->next == NULL && (fi->status & MY_FRAME_IN_SP)) + fi->frame = read_sp (); + return addr; + } + stack_size = extract_signed_integer (buf, 2); + if (fi) + fi->stack_size = stack_size; + addr += 4; + if (addr >= stop) + { + if (fi && fi->next == NULL && (fi->status & MY_FRAME_IN_SP)) + fi->frame = read_sp () - stack_size; + return addr; + } + } + else if (buf[0] == 0xf4 && buf[1] == 0x67) + { + status = target_read_memory (addr + 2, buf, 3); + if (status != 0) + { + if (fi && fi->next == NULL && (fi->status & MY_FRAME_IN_SP)) + fi->frame = read_sp (); + return addr; + } + stack_size = extract_signed_integer (buf, 3); + if (fi) + fi->stack_size = stack_size; + addr += 5; + if (addr >= stop) + { + if (fi && fi->next == NULL && (fi->status & MY_FRAME_IN_SP)) + fi->frame = read_sp () - stack_size; + return addr; + } + } + + /* Now see if we have a call to __prologue for an out of line + prologue. */ + status = target_read_memory (addr, buf, 2); + if (status != 0) + return addr; + + /* First check for 16bit pc-relative call to __prologue. */ + if (buf[0] == 0xfd) + { + CORE_ADDR temp; + status = target_read_memory (addr + 1, buf, 2); + if (status != 0) + { + if (fi && fi->next == NULL && (fi->status & MY_FRAME_IN_SP)) + fi->frame = read_sp (); + return addr; + } + + /* Get the PC this instruction will branch to. */ + temp = (extract_signed_integer (buf, 2) + addr + 3) & 0xffffff; + + /* Get the name of the function at the target address. */ + status = find_pc_partial_function (temp, &name, NULL, NULL); + if (status == 0) + { + if (fi && fi->next == NULL && (fi->status & MY_FRAME_IN_SP)) + fi->frame = read_sp (); + return addr; + } + + /* Note if it is an out of line prologue. */ + out_of_line_prologue = (strcmp (name, "__prologue") == 0); + + /* This sucks up 3 bytes of instruction space. */ + if (out_of_line_prologue) + addr += 3; + + if (addr >= stop) + { + if (fi && fi->next == NULL) + { + fi->stack_size -= 16; + fi->frame = read_sp () - fi->stack_size; + } + return addr; + } + } + /* Now check for the 24bit pc-relative call to __prologue. */ + else if (buf[0] == 0xf4 && buf[1] == 0xe1) + { + CORE_ADDR temp; + status = target_read_memory (addr + 2, buf, 3); + if (status != 0) + { + if (fi && fi->next == NULL && (fi->status & MY_FRAME_IN_SP)) + fi->frame = read_sp (); + return addr; + } + + /* Get the PC this instruction will branch to. */ + temp = (extract_signed_integer (buf, 3) + addr + 5) & 0xffffff; + + /* Get the name of the function at the target address. */ + status = find_pc_partial_function (temp, &name, NULL, NULL); + if (status == 0) + { + if (fi && fi->next == NULL && (fi->status & MY_FRAME_IN_SP)) + fi->frame = read_sp (); + return addr; + } + + /* Note if it is an out of line prologue. */ + out_of_line_prologue = (strcmp (name, "__prologue") == 0); + + /* This sucks up 5 bytes of instruction space. */ + if (out_of_line_prologue) + addr += 5; + + if (addr >= stop) + { + if (fi && fi->next == NULL && (fi->status & MY_FRAME_IN_SP)) + { + fi->stack_size -= 16; + fi->frame = read_sp () - fi->stack_size; + } + return addr; + } + } + + /* Now actually handle the out of line prologue. */ + if (out_of_line_prologue) + { + int outgoing_args_size = 0; + + /* First adjust the stack size for this function. The out of + line prologue saves 4 registers (16bytes of data). */ + if (fi) + fi->stack_size -= 16; + + /* Update fi->frame if necessary. */ + if (fi && fi->next == NULL) + fi->frame = read_sp () - fi->stack_size; + + /* After the out of line prologue, there may be another + stack adjustment for the outgoing arguments. + + Search for add imm8,a3 (0xd3XX) + or add imm16,a3 (0xf70bXXXX) + or add imm24,a3 (0xf467XXXXXX). */ + + status = target_read_memory (addr, buf, 2); + if (status != 0) + { + if (fi) + { + fi->fsr.regs[2] = fi->frame + fi->stack_size + 4; + fi->fsr.regs[3] = fi->frame + fi->stack_size + 8; + fi->fsr.regs[5] = fi->frame + fi->stack_size + 12; + fi->fsr.regs[6] = fi->frame + fi->stack_size + 16; + } + return addr; + } + + if (buf[0] == 0xd3) + { + outgoing_args_size = extract_signed_integer (&buf[1], 1); + addr += 2; + } + else if (buf[0] == 0xf7 && buf[1] == 0x0b) + { + status = target_read_memory (addr + 2, buf, 2); + if (status != 0) + { + if (fi) + { + fi->fsr.regs[2] = fi->frame + fi->stack_size + 4; + fi->fsr.regs[3] = fi->frame + fi->stack_size + 8; + fi->fsr.regs[5] = fi->frame + fi->stack_size + 12; + fi->fsr.regs[6] = fi->frame + fi->stack_size + 16; + } + return addr; + } + outgoing_args_size = extract_signed_integer (buf, 2); + addr += 4; + } + else if (buf[0] == 0xf4 && buf[1] == 0x67) + { + status = target_read_memory (addr + 2, buf, 3); + if (status != 0) + { + if (fi && fi->next == NULL) + { + fi->fsr.regs[2] = fi->frame + fi->stack_size + 4; + fi->fsr.regs[3] = fi->frame + fi->stack_size + 8; + fi->fsr.regs[5] = fi->frame + fi->stack_size + 12; + fi->fsr.regs[6] = fi->frame + fi->stack_size + 16; + } + return addr; + } + outgoing_args_size = extract_signed_integer (buf, 3); + addr += 5; + } + else + outgoing_args_size = 0; + + /* Now that we know the size of the outgoing arguments, fix + fi->frame again if this is the innermost frame. */ + if (fi && fi->next == NULL) + fi->frame -= outgoing_args_size; + + /* Note the register save information and update the stack + size for this frame too. */ + if (fi) + { + fi->fsr.regs[2] = fi->frame + fi->stack_size + 4; + fi->fsr.regs[3] = fi->frame + fi->stack_size + 8; + fi->fsr.regs[5] = fi->frame + fi->stack_size + 12; + fi->fsr.regs[6] = fi->frame + fi->stack_size + 16; + fi->stack_size += outgoing_args_size; + } + /* There can be no more prologue insns, so return now. */ + return addr; + } + + /* At this point fi->frame needs to be correct. + + If MY_FRAME_IN_SP is set and we're the innermost frame, then we + need to fix fi->frame so that backtracing, find_frame_saved_regs, + etc work correctly. */ + if (fi && fi->next == NULL && (fi->status & MY_FRAME_IN_SP) != 0) + fi->frame = read_sp () - fi->stack_size; + + /* And last we have the register saves. These are relatively + simple because they're physically done off the stack pointer, + and thus the number of different instructions we need to + check is greatly reduced because we know the displacements + will be small. + + Search for movx d2,(X,a3) (0xf55eXX) + then movx d3,(X,a3) (0xf55fXX) + then mov a1,(X,a3) (0x5dXX) No frame pointer case + then mov a2,(X,a3) (0x5eXX) No frame pointer case + or mov a0,(X,a3) (0x5cXX) Frame pointer case. */ + + status = target_read_memory (addr, buf, 2); + if (status != 0) + return addr; + if (buf[0] == 0xf5 && buf[1] == 0x5e) + { + if (fi) + { + status = target_read_memory (addr + 2, buf, 1); + if (status != 0) + return addr; + fi->fsr.regs[2] = (fi->frame + stack_size + + extract_signed_integer (buf, 1)); + } + addr += 3; + if (addr >= stop) + return addr; + status = target_read_memory (addr, buf, 2); + if (status != 0) + return addr; + } + if (buf[0] == 0xf5 && buf[1] == 0x5f) + { + if (fi) + { + status = target_read_memory (addr + 2, buf, 1); + if (status != 0) + return addr; + fi->fsr.regs[3] = (fi->frame + stack_size + + extract_signed_integer (buf, 1)); + } + addr += 3; + if (addr >= stop) + return addr; + status = target_read_memory (addr, buf, 2); + if (status != 0) + return addr; + } + if (buf[0] == 0x5d) + { + if (fi) + { + status = target_read_memory (addr + 1, buf, 1); + if (status != 0) + return addr; + fi->fsr.regs[5] = (fi->frame + stack_size + + extract_signed_integer (buf, 1)); + } + addr += 2; + if (addr >= stop) + return addr; + status = target_read_memory (addr, buf, 2); + if (status != 0) + return addr; + } + if (buf[0] == 0x5e || buf[0] == 0x5c) + { + if (fi) + { + status = target_read_memory (addr + 1, buf, 1); + if (status != 0) + return addr; + fi->fsr.regs[6] = (fi->frame + stack_size + + extract_signed_integer (buf, 1)); + fi->status &= ~CALLER_A2_IN_A0; + } + addr += 2; + if (addr >= stop) + return addr; + return addr; + } + return addr; +} + +/* Function: frame_chain + Figure out and return the caller's frame pointer given current + frame_info struct. + + We don't handle dummy frames yet but we would probably just return the + stack pointer that was in use at the time the function call was made? */ + +CORE_ADDR +mn10200_frame_chain (fi) + struct frame_info *fi; +{ + struct frame_info dummy_frame; + + /* Walk through the prologue to determine the stack size, + location of saved registers, end of the prologue, etc. */ + if (fi->status == 0) + mn10200_analyze_prologue (fi, (CORE_ADDR)0); + + /* Quit now if mn10200_analyze_prologue set NO_MORE_FRAMES. */ + if (fi->status & NO_MORE_FRAMES) + return 0; + + /* Now that we've analyzed our prologue, determine the frame + pointer for our caller. + + If our caller has a frame pointer, then we need to + find the entry value of $a2 to our function. + + If CALLER_A2_IN_A0, then the chain is in $a0. + + If fsr.regs[6] is nonzero, then it's at the memory + location pointed to by fsr.regs[6]. + + Else it's still in $a2. + + If our caller does not have a frame pointer, then his + frame base is fi->frame + -caller's stack size + 4. */ + + /* The easiest way to get that info is to analyze our caller's frame. + + So we set up a dummy frame and call mn10200_analyze_prologue to + find stuff for us. */ + dummy_frame.pc = FRAME_SAVED_PC (fi); + dummy_frame.frame = fi->frame; + memset (dummy_frame.fsr.regs, '\000', sizeof dummy_frame.fsr.regs); + dummy_frame.status = 0; + dummy_frame.stack_size = 0; + mn10200_analyze_prologue (&dummy_frame); + + if (dummy_frame.status & MY_FRAME_IN_FP) + { + /* Our caller has a frame pointer. So find the frame in $a2, $a0, + or in the stack. */ + if (fi->fsr.regs[6]) + return (read_memory_integer (fi->fsr.regs[FP_REGNUM], REGISTER_SIZE) + & 0xffffff); + else if (fi->status & CALLER_A2_IN_A0) + return read_register (4); + else + return read_register (FP_REGNUM); + } + else + { + /* Our caller does not have a frame pointer. So his frame starts + at the base of our frame (fi->frame) + <his size> + 4 (saved pc). */ + return fi->frame + -dummy_frame.stack_size + 4; + } +} + +/* Function: skip_prologue + Return the address of the first inst past the prologue of the function. */ + +CORE_ADDR +mn10200_skip_prologue (pc) + CORE_ADDR pc; +{ + /* We used to check the debug symbols, but that can lose if + we have a null prologue. */ + return mn10200_analyze_prologue (NULL, pc); +} + +/* Function: pop_frame + This routine gets called when either the user uses the `return' + command, or the call dummy breakpoint gets hit. */ + +void +mn10200_pop_frame (frame) + struct frame_info *frame; +{ + int regnum; + + if (PC_IN_CALL_DUMMY(frame->pc, frame->frame, frame->frame)) + generic_pop_dummy_frame (); + else + { + write_register (PC_REGNUM, FRAME_SAVED_PC (frame)); + + /* Restore any saved registers. */ + for (regnum = 0; regnum < NUM_REGS; regnum++) + if (frame->fsr.regs[regnum] != 0) + { + ULONGEST value; + + value = read_memory_unsigned_integer (frame->fsr.regs[regnum], + REGISTER_RAW_SIZE (regnum)); + write_register (regnum, value); + } + + /* Actually cut back the stack. */ + write_register (SP_REGNUM, FRAME_FP (frame)); + + /* Don't we need to set the PC?!? XXX FIXME. */ + } + + /* Throw away any cached frame information. */ + flush_cached_frames (); +} + +/* Function: push_arguments + Setup arguments for a call to the target. Arguments go in + order on the stack. */ + +CORE_ADDR +mn10200_push_arguments (nargs, args, sp, struct_return, struct_addr) + int nargs; + value_ptr *args; + CORE_ADDR sp; + unsigned char struct_return; + CORE_ADDR struct_addr; +{ + int argnum = 0; + int len = 0; + int stack_offset = 0; + int regsused = struct_return ? 1 : 0; + + /* This should be a nop, but align the stack just in case something + went wrong. Stacks are two byte aligned on the mn10200. */ + sp &= ~1; + + /* Now make space on the stack for the args. + + XXX This doesn't appear to handle pass-by-invisible reference + arguments. */ + for (argnum = 0; argnum < nargs; argnum++) + { + int arg_length = (TYPE_LENGTH (VALUE_TYPE (args[argnum])) + 1) & ~1; + + /* If we've used all argument registers, then this argument is + pushed. */ + if (regsused >= 2 || arg_length > 4) + { + regsused = 2; + len += arg_length; + } + /* We know we've got some arg register space left. If this argument + will fit entirely in regs, then put it there. */ + else if (arg_length <= 2 + || TYPE_CODE (VALUE_TYPE (args[argnum])) == TYPE_CODE_PTR) + { + regsused++; + } + else if (regsused == 0) + { + regsused = 2; + } + else + { + regsused = 2; + len += arg_length; + } + } + + /* Allocate stack space. */ + sp -= len; + + regsused = struct_return ? 1 : 0; + /* Push all arguments onto the stack. */ + for (argnum = 0; argnum < nargs; argnum++) + { + int len; + char *val; + + /* XXX Check this. What about UNIONS? */ + if (TYPE_CODE (VALUE_TYPE (*args)) == TYPE_CODE_STRUCT + && TYPE_LENGTH (VALUE_TYPE (*args)) > 8) + { + /* XXX Wrong, we want a pointer to this argument. */ + len = TYPE_LENGTH (VALUE_TYPE (*args)); + val = (char *)VALUE_CONTENTS (*args); + } + else + { + len = TYPE_LENGTH (VALUE_TYPE (*args)); + val = (char *)VALUE_CONTENTS (*args); + } + + if (regsused < 2 + && (len <= 2 + || TYPE_CODE (VALUE_TYPE (*args)) == TYPE_CODE_PTR)) + { + write_register (regsused, extract_unsigned_integer (val, 4)); + regsused++; + } + else if (regsused == 0 && len == 4) + { + write_register (regsused, extract_unsigned_integer (val, 2)); + write_register (regsused + 1, extract_unsigned_integer (val + 2, 2)); + regsused = 2; + } + else + { + regsused = 2; + while (len > 0) + { + write_memory (sp + stack_offset, val, 2); + + len -= 2; + val += 2; + stack_offset += 2; + } + } + args++; + } + + return sp; +} + +/* Function: push_return_address (pc) + Set up the return address for the inferior function call. + Needed for targets where we don't actually execute a JSR/BSR instruction */ + +CORE_ADDR +mn10200_push_return_address (pc, sp) + CORE_ADDR pc; + CORE_ADDR sp; +{ + unsigned char buf[4]; + + store_unsigned_integer (buf, 4, CALL_DUMMY_ADDRESS ()); + write_memory (sp - 4, buf, 4); + return sp - 4; +} + +/* Function: store_struct_return (addr,sp) + Store the structure value return address for an inferior function + call. */ + +CORE_ADDR +mn10200_store_struct_return (addr, sp) + CORE_ADDR addr; + CORE_ADDR sp; +{ + /* The structure return address is passed as the first argument. */ + write_register (0, addr); + return sp; +} + +/* Function: frame_saved_pc + Find the caller of this frame. We do this by seeing if RP_REGNUM + is saved in the stack anywhere, otherwise we get it from the + registers. If the inner frame is a dummy frame, return its PC + instead of RP, because that's where "caller" of the dummy-frame + will be found. */ + +CORE_ADDR +mn10200_frame_saved_pc (fi) + struct frame_info *fi; +{ + /* The saved PC will always be at the base of the current frame. */ + return (read_memory_integer (fi->frame, REGISTER_SIZE) & 0xffffff); +} + +void +get_saved_register (raw_buffer, optimized, addrp, frame, regnum, lval) + char *raw_buffer; + int *optimized; + CORE_ADDR *addrp; + struct frame_info *frame; + int regnum; + enum lval_type *lval; +{ + generic_get_saved_register (raw_buffer, optimized, addrp, + frame, regnum, lval); +} + +/* Function: init_extra_frame_info + Setup the frame's frame pointer, pc, and frame addresses for saved + registers. Most of the work is done in mn10200_analyze_prologue(). + + Note that when we are called for the last frame (currently active frame), + that fi->pc and fi->frame will already be setup. However, fi->frame will + be valid only if this routine uses FP. For previous frames, fi-frame will + always be correct. mn10200_analyze_prologue will fix fi->frame if + it's not valid. + + We can be called with the PC in the call dummy under two circumstances. + First, during normal backtracing, second, while figuring out the frame + pointer just prior to calling the target function (see run_stack_dummy). */ + +void +mn10200_init_extra_frame_info (fi) + struct frame_info *fi; +{ + if (fi->next) + fi->pc = FRAME_SAVED_PC (fi->next); + + memset (fi->fsr.regs, '\000', sizeof fi->fsr.regs); + fi->status = 0; + fi->stack_size = 0; + + mn10200_analyze_prologue (fi, 0); +} + +void +_initialize_mn10200_tdep () +{ + tm_print_insn = print_insn_mn10200; +} + diff --git a/contrib/gdb/gdb/mn10300-tdep.c b/contrib/gdb/gdb/mn10300-tdep.c new file mode 100644 index 000000000000..6c43176193b8 --- /dev/null +++ b/contrib/gdb/gdb/mn10300-tdep.c @@ -0,0 +1,791 @@ +/* Target-dependent code for the Matsushita MN10300 for GDB, the GNU debugger. + Copyright 1996, 1997, 1998 Free Software Foundation, Inc. + +This file is part of GDB. + +This program 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 of the License, or +(at your option) any later version. + +This program 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; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "defs.h" +#include "frame.h" +#include "inferior.h" +#include "obstack.h" +#include "target.h" +#include "value.h" +#include "bfd.h" +#include "gdb_string.h" +#include "gdbcore.h" +#include "symfile.h" + +static char *mn10300_generic_register_names[] = +{ "d0", "d1", "d2", "d3", "a0", "a1", "a2", "a3", + "sp", "pc", "mdr", "psw", "lir", "lar", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "fp" }; + +char **mn10300_register_names = mn10300_generic_register_names; + +static CORE_ADDR mn10300_analyze_prologue PARAMS ((struct frame_info *fi, + CORE_ADDR pc)); + +/* Additional info used by the frame */ + +struct frame_extra_info +{ + int status; + int stack_size; +}; + +static struct frame_info *analyze_dummy_frame PARAMS ((CORE_ADDR, CORE_ADDR)); +static struct frame_info * +analyze_dummy_frame (pc, frame) + CORE_ADDR pc; + CORE_ADDR frame; +{ + static struct frame_info *dummy = NULL; + if (dummy == NULL) + { + dummy = xmalloc (sizeof (struct frame_info)); + dummy->saved_regs = xmalloc (SIZEOF_FRAME_SAVED_REGS); + dummy->extra_info = xmalloc (sizeof (struct frame_extra_info)); + } + dummy->next = NULL; + dummy->prev = NULL; + dummy->pc = pc; + dummy->frame = frame; + dummy->extra_info->status = 0; + dummy->extra_info->stack_size = 0; + memset (dummy->saved_regs, '\000', SIZEOF_FRAME_SAVED_REGS); + mn10300_analyze_prologue (dummy, 0); + return dummy; +} + +/* Values for frame_info.status */ + +#define MY_FRAME_IN_SP 0x1 +#define MY_FRAME_IN_FP 0x2 +#define NO_MORE_FRAMES 0x4 + + +/* Should call_function allocate stack space for a struct return? */ +int +mn10300_use_struct_convention (gcc_p, type) + int gcc_p; + struct type *type; +{ + return (TYPE_NFIELDS (type) > 1 || TYPE_LENGTH (type) > 8); +} + +/* The breakpoint instruction must be the same size as the smallest + instruction in the instruction set. + + The Matsushita mn10x00 processors have single byte instructions + so we need a single byte breakpoint. Matsushita hasn't defined + one, so we defined it ourselves. */ + +unsigned char * +mn10300_breakpoint_from_pc (bp_addr, bp_size) + CORE_ADDR *bp_addr; + int *bp_size; +{ + static char breakpoint[] = {0xff}; + *bp_size = 1; + return breakpoint; +} + + +/* Fix fi->frame if it's bogus at this point. This is a helper + function for mn10300_analyze_prologue. */ + +static void +fix_frame_pointer (fi, stack_size) + struct frame_info *fi; + int stack_size; +{ + if (fi && fi->next == NULL) + { + if (fi->extra_info->status & MY_FRAME_IN_SP) + fi->frame = read_sp () - stack_size; + else if (fi->extra_info->status & MY_FRAME_IN_FP) + fi->frame = read_register (A3_REGNUM); + } +} + + +/* Set offsets of registers saved by movm instruction. + This is a helper function for mn10300_analyze_prologue. */ + +static void +set_movm_offsets (fi, movm_args) + struct frame_info *fi; + int movm_args; +{ + int offset = 0; + + if (fi == NULL || movm_args == 0) + return; + + if (movm_args & 0x10) + { + fi->saved_regs[A3_REGNUM] = fi->frame + offset; + offset += 4; + } + if (movm_args & 0x20) + { + fi->saved_regs[A2_REGNUM] = fi->frame + offset; + offset += 4; + } + if (movm_args & 0x40) + { + fi->saved_regs[D3_REGNUM] = fi->frame + offset; + offset += 4; + } + if (movm_args & 0x80) + { + fi->saved_regs[D2_REGNUM] = fi->frame + offset; + offset += 4; + } +} + + +/* The main purpose of this file is dealing with prologues to extract + information about stack frames and saved registers. + + For reference here's how prologues look on the mn10300: + + With frame pointer: + movm [d2,d3,a2,a3],sp + mov sp,a3 + add <size>,sp + + Without frame pointer: + movm [d2,d3,a2,a3],sp (if needed) + add <size>,sp + + One day we might keep the stack pointer constant, that won't + change the code for prologues, but it will make the frame + pointerless case much more common. */ + +/* Analyze the prologue to determine where registers are saved, + the end of the prologue, etc etc. Return the end of the prologue + scanned. + + We store into FI (if non-null) several tidbits of information: + + * stack_size -- size of this stack frame. Note that if we stop in + certain parts of the prologue/epilogue we may claim the size of the + current frame is zero. This happens when the current frame has + not been allocated yet or has already been deallocated. + + * fsr -- Addresses of registers saved in the stack by this frame. + + * status -- A (relatively) generic status indicator. It's a bitmask + with the following bits: + + MY_FRAME_IN_SP: The base of the current frame is actually in + the stack pointer. This can happen for frame pointerless + functions, or cases where we're stopped in the prologue/epilogue + itself. For these cases mn10300_analyze_prologue will need up + update fi->frame before returning or analyzing the register + save instructions. + + MY_FRAME_IN_FP: The base of the current frame is in the + frame pointer register ($a2). + + NO_MORE_FRAMES: Set this if the current frame is "start" or + if the first instruction looks like mov <imm>,sp. This tells + frame chain to not bother trying to unwind past this frame. */ + +static CORE_ADDR +mn10300_analyze_prologue (fi, pc) + struct frame_info *fi; + CORE_ADDR pc; +{ + CORE_ADDR func_addr, func_end, addr, stop; + CORE_ADDR stack_size; + int imm_size; + unsigned char buf[4]; + int status, movm_args = 0; + char *name; + + /* Use the PC in the frame if it's provided to look up the + start of this function. */ + pc = (fi ? fi->pc : pc); + + /* Find the start of this function. */ + status = find_pc_partial_function (pc, &name, &func_addr, &func_end); + + /* Do nothing if we couldn't find the start of this function or if we're + stopped at the first instruction in the prologue. */ + if (status == 0) + return pc; + + /* If we're in start, then give up. */ + if (strcmp (name, "start") == 0) + { + if (fi != NULL) + fi->extra_info->status = NO_MORE_FRAMES; + return pc; + } + + /* At the start of a function our frame is in the stack pointer. */ + if (fi) + fi->extra_info->status = MY_FRAME_IN_SP; + + /* Get the next two bytes into buf, we need two because rets is a two + byte insn and the first isn't enough to uniquely identify it. */ + status = read_memory_nobpt (pc, buf, 2); + if (status != 0) + return pc; + + /* If we're physically on an "rets" instruction, then our frame has + already been deallocated. Note this can also be true for retf + and ret if they specify a size of zero. + + In this case fi->frame is bogus, we need to fix it. */ + if (fi && buf[0] == 0xf0 && buf[1] == 0xfc) + { + if (fi->next == NULL) + fi->frame = read_sp (); + return fi->pc; + } + + /* Similarly if we're stopped on the first insn of a prologue as our + frame hasn't been allocated yet. */ + if (fi && fi->pc == func_addr) + { + if (fi->next == NULL) + fi->frame = read_sp (); + return fi->pc; + } + + /* Figure out where to stop scanning. */ + stop = fi ? fi->pc : func_end; + + /* Don't walk off the end of the function. */ + stop = stop > func_end ? func_end : stop; + + /* Start scanning on the first instruction of this function. */ + addr = func_addr; + + /* Suck in two bytes. */ + status = read_memory_nobpt (addr, buf, 2); + if (status != 0) + { + fix_frame_pointer (fi, 0); + return addr; + } + + /* First see if this insn sets the stack pointer; if so, it's something + we won't understand, so quit now. */ + if (buf[0] == 0xf2 && (buf[1] & 0xf3) == 0xf0) + { + if (fi) + fi->extra_info->status = NO_MORE_FRAMES; + return addr; + } + + /* Now look for movm [regs],sp, which saves the callee saved registers. + + At this time we don't know if fi->frame is valid, so we only note + that we encountered a movm instruction. Later, we'll set the entries + in fsr.regs as needed. */ + if (buf[0] == 0xcf) + { + /* Extract the register list for the movm instruction. */ + status = read_memory_nobpt (addr + 1, buf, 1); + movm_args = *buf; + + addr += 2; + + /* Quit now if we're beyond the stop point. */ + if (addr >= stop) + { + /* Fix fi->frame since it's bogus at this point. */ + if (fi && fi->next == NULL) + fi->frame = read_sp (); + + /* Note if/where callee saved registers were saved. */ + set_movm_offsets (fi, movm_args); + return addr; + } + + /* Get the next two bytes so the prologue scan can continue. */ + status = read_memory_nobpt (addr, buf, 2); + if (status != 0) + { + /* Fix fi->frame since it's bogus at this point. */ + if (fi && fi->next == NULL) + fi->frame = read_sp (); + + /* Note if/where callee saved registers were saved. */ + set_movm_offsets (fi, movm_args); + return addr; + } + } + + /* Now see if we set up a frame pointer via "mov sp,a3" */ + if (buf[0] == 0x3f) + { + addr += 1; + + /* The frame pointer is now valid. */ + if (fi) + { + fi->extra_info->status |= MY_FRAME_IN_FP; + fi->extra_info->status &= ~MY_FRAME_IN_SP; + } + + /* Quit now if we're beyond the stop point. */ + if (addr >= stop) + { + /* Fix fi->frame if it's bogus at this point. */ + fix_frame_pointer (fi, 0); + + /* Note if/where callee saved registers were saved. */ + set_movm_offsets (fi, movm_args); + return addr; + } + + /* Get two more bytes so scanning can continue. */ + status = read_memory_nobpt (addr, buf, 2); + if (status != 0) + { + /* Fix fi->frame if it's bogus at this point. */ + fix_frame_pointer (fi, 0); + + /* Note if/where callee saved registers were saved. */ + set_movm_offsets (fi, movm_args); + return addr; + } + } + + /* Next we should allocate the local frame. No more prologue insns + are found after allocating the local frame. + + Search for add imm8,sp (0xf8feXX) + or add imm16,sp (0xfafeXXXX) + or add imm32,sp (0xfcfeXXXXXXXX). + + If none of the above was found, then this prologue has no + additional stack. */ + + status = read_memory_nobpt (addr, buf, 2); + if (status != 0) + { + /* Fix fi->frame if it's bogus at this point. */ + fix_frame_pointer (fi, 0); + + /* Note if/where callee saved registers were saved. */ + set_movm_offsets (fi, movm_args); + return addr; + } + + imm_size = 0; + if (buf[0] == 0xf8 && buf[1] == 0xfe) + imm_size = 1; + else if (buf[0] == 0xfa && buf[1] == 0xfe) + imm_size = 2; + else if (buf[0] == 0xfc && buf[1] == 0xfe) + imm_size = 4; + + if (imm_size != 0) + { + /* Suck in imm_size more bytes, they'll hold the size of the + current frame. */ + status = read_memory_nobpt (addr + 2, buf, imm_size); + if (status != 0) + { + /* Fix fi->frame if it's bogus at this point. */ + fix_frame_pointer (fi, 0); + + /* Note if/where callee saved registers were saved. */ + set_movm_offsets (fi, movm_args); + return addr; + } + + /* Note the size of the stack in the frame info structure. */ + stack_size = extract_signed_integer (buf, imm_size); + if (fi) + fi->extra_info->stack_size = stack_size; + + /* We just consumed 2 + imm_size bytes. */ + addr += 2 + imm_size; + + /* No more prologue insns follow, so begin preparation to return. */ + /* Fix fi->frame if it's bogus at this point. */ + fix_frame_pointer (fi, stack_size); + + /* Note if/where callee saved registers were saved. */ + set_movm_offsets (fi, movm_args); + return addr; + } + + /* We never found an insn which allocates local stack space, regardless + this is the end of the prologue. */ + /* Fix fi->frame if it's bogus at this point. */ + fix_frame_pointer (fi, 0); + + /* Note if/where callee saved registers were saved. */ + set_movm_offsets (fi, movm_args); + return addr; +} + +/* Function: frame_chain + Figure out and return the caller's frame pointer given current + frame_info struct. + + We don't handle dummy frames yet but we would probably just return the + stack pointer that was in use at the time the function call was made? */ + +CORE_ADDR +mn10300_frame_chain (fi) + struct frame_info *fi; +{ + struct frame_info *dummy; + /* Walk through the prologue to determine the stack size, + location of saved registers, end of the prologue, etc. */ + if (fi->extra_info->status == 0) + mn10300_analyze_prologue (fi, (CORE_ADDR)0); + + /* Quit now if mn10300_analyze_prologue set NO_MORE_FRAMES. */ + if (fi->extra_info->status & NO_MORE_FRAMES) + return 0; + + /* Now that we've analyzed our prologue, determine the frame + pointer for our caller. + + If our caller has a frame pointer, then we need to + find the entry value of $a3 to our function. + + If fsr.regs[A3_REGNUM] is nonzero, then it's at the memory + location pointed to by fsr.regs[A3_REGNUM]. + + Else it's still in $a3. + + If our caller does not have a frame pointer, then his + frame base is fi->frame + -caller's stack size. */ + + /* The easiest way to get that info is to analyze our caller's frame. + So we set up a dummy frame and call mn10300_analyze_prologue to + find stuff for us. */ + dummy = analyze_dummy_frame (FRAME_SAVED_PC (fi), fi->frame); + + if (dummy->extra_info->status & MY_FRAME_IN_FP) + { + /* Our caller has a frame pointer. So find the frame in $a3 or + in the stack. */ + if (fi->saved_regs[A3_REGNUM]) + return (read_memory_integer (fi->saved_regs[A3_REGNUM], REGISTER_SIZE)); + else + return read_register (A3_REGNUM); + } + else + { + int adjust = 0; + + adjust += (fi->saved_regs[D2_REGNUM] ? 4 : 0); + adjust += (fi->saved_regs[D3_REGNUM] ? 4 : 0); + adjust += (fi->saved_regs[A2_REGNUM] ? 4 : 0); + adjust += (fi->saved_regs[A3_REGNUM] ? 4 : 0); + + /* Our caller does not have a frame pointer. So his frame starts + at the base of our frame (fi->frame) + register save space + + <his size>. */ + return fi->frame + adjust + -dummy->extra_info->stack_size; + } +} + +/* Function: skip_prologue + Return the address of the first inst past the prologue of the function. */ + +CORE_ADDR +mn10300_skip_prologue (pc) + CORE_ADDR pc; +{ + /* We used to check the debug symbols, but that can lose if + we have a null prologue. */ + return mn10300_analyze_prologue (NULL, pc); +} + + +/* Function: pop_frame + This routine gets called when either the user uses the `return' + command, or the call dummy breakpoint gets hit. */ + +void +mn10300_pop_frame (frame) + struct frame_info *frame; +{ + int regnum; + + if (PC_IN_CALL_DUMMY(frame->pc, frame->frame, frame->frame)) + generic_pop_dummy_frame (); + else + { + write_register (PC_REGNUM, FRAME_SAVED_PC (frame)); + + /* Restore any saved registers. */ + for (regnum = 0; regnum < NUM_REGS; regnum++) + if (frame->saved_regs[regnum] != 0) + { + ULONGEST value; + + value = read_memory_unsigned_integer (frame->saved_regs[regnum], + REGISTER_RAW_SIZE (regnum)); + write_register (regnum, value); + } + + /* Actually cut back the stack. */ + write_register (SP_REGNUM, FRAME_FP (frame)); + + /* Don't we need to set the PC?!? XXX FIXME. */ + } + + /* Throw away any cached frame information. */ + flush_cached_frames (); +} + +/* Function: push_arguments + Setup arguments for a call to the target. Arguments go in + order on the stack. */ + +CORE_ADDR +mn10300_push_arguments (nargs, args, sp, struct_return, struct_addr) + int nargs; + value_ptr *args; + CORE_ADDR sp; + unsigned char struct_return; + CORE_ADDR struct_addr; +{ + int argnum = 0; + int len = 0; + int stack_offset = 0; + int regsused = struct_return ? 1 : 0; + + /* This should be a nop, but align the stack just in case something + went wrong. Stacks are four byte aligned on the mn10300. */ + sp &= ~3; + + /* Now make space on the stack for the args. + + XXX This doesn't appear to handle pass-by-invisible reference + arguments. */ + for (argnum = 0; argnum < nargs; argnum++) + { + int arg_length = (TYPE_LENGTH (VALUE_TYPE (args[argnum])) + 3) & ~3; + + while (regsused < 2 && arg_length > 0) + { + regsused++; + arg_length -= 4; + } + len += arg_length; + } + + /* Allocate stack space. */ + sp -= len; + + regsused = struct_return ? 1 : 0; + /* Push all arguments onto the stack. */ + for (argnum = 0; argnum < nargs; argnum++) + { + int len; + char *val; + + /* XXX Check this. What about UNIONS? */ + if (TYPE_CODE (VALUE_TYPE (*args)) == TYPE_CODE_STRUCT + && TYPE_LENGTH (VALUE_TYPE (*args)) > 8) + { + /* XXX Wrong, we want a pointer to this argument. */ + len = TYPE_LENGTH (VALUE_TYPE (*args)); + val = (char *)VALUE_CONTENTS (*args); + } + else + { + len = TYPE_LENGTH (VALUE_TYPE (*args)); + val = (char *)VALUE_CONTENTS (*args); + } + + while (regsused < 2 && len > 0) + { + write_register (regsused, extract_unsigned_integer (val, 4)); + val += 4; + len -= 4; + regsused++; + } + + while (len > 0) + { + write_memory (sp + stack_offset, val, 4); + len -= 4; + val += 4; + stack_offset += 4; + } + + args++; + } + + /* Make space for the flushback area. */ + sp -= 8; + return sp; +} + +/* Function: push_return_address (pc) + Set up the return address for the inferior function call. + Needed for targets where we don't actually execute a JSR/BSR instruction */ + +CORE_ADDR +mn10300_push_return_address (pc, sp) + CORE_ADDR pc; + CORE_ADDR sp; +{ + unsigned char buf[4]; + + store_unsigned_integer (buf, 4, CALL_DUMMY_ADDRESS ()); + write_memory (sp - 4, buf, 4); + return sp - 4; +} + +/* Function: store_struct_return (addr,sp) + Store the structure value return address for an inferior function + call. */ + +CORE_ADDR +mn10300_store_struct_return (addr, sp) + CORE_ADDR addr; + CORE_ADDR sp; +{ + /* The structure return address is passed as the first argument. */ + write_register (0, addr); + return sp; +} + +/* Function: frame_saved_pc + Find the caller of this frame. We do this by seeing if RP_REGNUM + is saved in the stack anywhere, otherwise we get it from the + registers. If the inner frame is a dummy frame, return its PC + instead of RP, because that's where "caller" of the dummy-frame + will be found. */ + +CORE_ADDR +mn10300_frame_saved_pc (fi) + struct frame_info *fi; +{ + int adjust = 0; + + adjust += (fi->saved_regs[D2_REGNUM] ? 4 : 0); + adjust += (fi->saved_regs[D3_REGNUM] ? 4 : 0); + adjust += (fi->saved_regs[A2_REGNUM] ? 4 : 0); + adjust += (fi->saved_regs[A3_REGNUM] ? 4 : 0); + + return (read_memory_integer (fi->frame + adjust, REGISTER_SIZE)); +} + +void +get_saved_register (raw_buffer, optimized, addrp, frame, regnum, lval) + char *raw_buffer; + int *optimized; + CORE_ADDR *addrp; + struct frame_info *frame; + int regnum; + enum lval_type *lval; +{ + generic_get_saved_register (raw_buffer, optimized, addrp, + frame, regnum, lval); +} + +/* Function: mn10300_init_extra_frame_info + Setup the frame's frame pointer, pc, and frame addresses for saved + registers. Most of the work is done in mn10300_analyze_prologue(). + + Note that when we are called for the last frame (currently active frame), + that fi->pc and fi->frame will already be setup. However, fi->frame will + be valid only if this routine uses FP. For previous frames, fi-frame will + always be correct. mn10300_analyze_prologue will fix fi->frame if + it's not valid. + + We can be called with the PC in the call dummy under two circumstances. + First, during normal backtracing, second, while figuring out the frame + pointer just prior to calling the target function (see run_stack_dummy). */ + +void +mn10300_init_extra_frame_info (fi) + struct frame_info *fi; +{ + if (fi->next) + fi->pc = FRAME_SAVED_PC (fi->next); + + frame_saved_regs_zalloc (fi); + fi->extra_info = (struct frame_extra_info *) + frame_obstack_alloc (sizeof (struct frame_extra_info)); + + fi->extra_info->status = 0; + fi->extra_info->stack_size = 0; + + mn10300_analyze_prologue (fi, 0); +} + +/* Function: mn10300_virtual_frame_pointer + Return the register that the function uses for a frame pointer, + plus any necessary offset to be applied to the register before + any frame pointer offsets. */ + +void +mn10300_virtual_frame_pointer (pc, reg, offset) + CORE_ADDR pc; + long *reg; + long *offset; +{ + struct frame_info *dummy = analyze_dummy_frame (pc, 0); + /* Set up a dummy frame_info, Analyze the prolog and fill in the + extra info. */ + /* Results will tell us which type of frame it uses. */ + if (dummy->extra_info->status & MY_FRAME_IN_SP) + { + *reg = SP_REGNUM; + *offset = -(dummy->extra_info->stack_size); + } + else + { + *reg = A3_REGNUM; + *offset = 0; + } +} + +/* This can be made more generic later. */ +static void +set_machine_hook (filename) + char *filename; +{ + int i; + + if (bfd_get_mach (exec_bfd) == bfd_mach_mn10300 + || bfd_get_mach (exec_bfd) == 0) + { + mn10300_register_names = mn10300_generic_register_names; + } + +} + +void +_initialize_mn10300_tdep () +{ +/* printf("_initialize_mn10300_tdep\n"); */ + + tm_print_insn = print_insn_mn10300; + + specify_exec_file_hook (set_machine_hook); +} + diff --git a/contrib/gdb/gdb/ser-ocd.c b/contrib/gdb/gdb/ser-ocd.c new file mode 100644 index 000000000000..971f84e049c4 --- /dev/null +++ b/contrib/gdb/gdb/ser-ocd.c @@ -0,0 +1,209 @@ +/* Remote serial interface for Macraigor Systems implementation of + On-Chip Debugging using serial target box or serial wiggler + + Copyright 1994, 1997 Free Software Foundation, Inc. + + This file is part of GDB. + + This program 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 of the License, or + (at your option) any later version. + + This program 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; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "defs.h" +#include "serial.h" + +#ifdef _WIN32 +#include <windows.h> +#endif + +static int ser_ocd_open PARAMS ((serial_t scb, const char *name)); +static void ser_ocd_raw PARAMS ((serial_t scb)); +static int ser_ocd_readchar PARAMS ((serial_t scb, int timeout)); +static int ser_ocd_setbaudrate PARAMS ((serial_t scb, int rate)); +static int ser_ocd_write PARAMS ((serial_t scb, const char *str, int len)); +static void ser_ocd_close PARAMS ((serial_t scb)); +static serial_ttystate ser_ocd_get_tty_state PARAMS ((serial_t scb)); +static int ser_ocd_set_tty_state PARAMS ((serial_t scb, serial_ttystate state)); + +#ifdef _WIN32 +/* On Windows, this function pointer is initialized to a function in + the wiggler DLL. */ +static int (*dll_do_command) PARAMS ((const char *, char *)); +#endif + +static int +ocd_open (scb, name) + serial_t scb; + const char *name; +{ +#ifdef _WIN32 + /* Find the wiggler DLL which talks to the board. */ + if (dll_do_command == NULL) + { + HINSTANCE handle; + + /* FIXME: Should the user be able to configure this? */ + handle = LoadLibrary ("Wigglers.dll"); + if (handle == NULL) + error ("Can't load Wigglers.dll"); + + dll_do_command = ((int (*) PARAMS ((const char *, char *))) + GetProcAddress (handle, "do_command")); + if (dll_do_command == NULL) + error ("Can't find do_command function in Wigglers.dll"); + } +#else + /* No wiggler DLLs on Unix yet, fail. */ + error ("Wiggler library not available for this type of host."); +#endif /* _WIN32 */ + return 0; +} + +static int +ocd_noop (scb) + serial_t scb; +{ + return 0; +} + +static void +ocd_raw (scb) + serial_t scb; +{ + /* Always in raw mode */ +} + +static void +ocd_readremote () +{ +} + +/* We need a buffer to store responses from the Wigglers.dll */ +#define WIGGLER_BUFF_SIZE 512 +unsigned char from_wiggler_buffer[WIGGLER_BUFF_SIZE]; +unsigned char * wiggler_buffer_ptr; /* curr spot in buffer */ + +static int +ocd_readchar (scb, timeout) + serial_t scb; + int timeout; +{ + /* Catch attempts at reading past the end of the buffer */ + if (wiggler_buffer_ptr > + (from_wiggler_buffer + (sizeof (char *) * WIGGLER_BUFF_SIZE))) + error ("ocd_readchar asked to read past the end of the buffer!"); + + return (int) *wiggler_buffer_ptr++; /* return curr char and increment ptr */ +} + +struct ocd_ttystate { + int dummy; +}; + +/* ocd_{get set}_tty_state() are both dummys to fill out the function + vector. Someday, they may do something real... */ + +static serial_ttystate +ocd_get_tty_state (scb) + serial_t scb; +{ + struct ocd_ttystate *state; + + state = (struct ocd_ttystate *) xmalloc (sizeof *state); + + return (serial_ttystate) state; +} + +static int +ocd_set_tty_state (scb, ttystate) + serial_t scb; + serial_ttystate ttystate; +{ + return 0; +} + +static int +ocd_noflush_set_tty_state (scb, new_ttystate, old_ttystate) + serial_t scb; + serial_ttystate new_ttystate; + serial_ttystate old_ttystate; +{ + return 0; +} + +static void +ocd_print_tty_state (scb, ttystate) + serial_t scb; + serial_ttystate ttystate; +{ + /* Nothing to print. */ + return; +} + +static int +ocd_setbaudrate (scb, rate) + serial_t scb; + int rate; +{ + return 0; +} + +static int +ocd_write (scb, str, len) + serial_t scb; + const char *str; + int len; +{ + char c; + +#ifdef _WIN32 + /* send packet to Wigglers.dll and store response so we can give it to + remote-wiggler.c when get_packet is run */ + dll_do_command (str, from_wiggler_buffer); + wiggler_buffer_ptr = from_wiggler_buffer; +#endif + + return 0; +} + +static void +ocd_close (scb) + serial_t scb; +{ +} + +static struct serial_ops ocd_ops = +{ + "ocd", + 0, + ocd_open, + ocd_close, + ocd_readchar, + ocd_write, + ocd_noop, /* flush output */ + ocd_noop, /* flush input */ + ocd_noop, /* send break -- currently used only for nindy */ + ocd_raw, + ocd_get_tty_state, + ocd_set_tty_state, + ocd_print_tty_state, + ocd_noflush_set_tty_state, + ocd_setbaudrate, + ocd_noop, /* wait for output to drain */ +}; + +void +_initialize_ser_ocd_bdm () +{ + serial_add_interface (&ocd_ops); +} diff --git a/contrib/gdb/gdb/sh-stub.c b/contrib/gdb/gdb/sh-stub.c new file mode 100644 index 000000000000..a036cff60628 --- /dev/null +++ b/contrib/gdb/gdb/sh-stub.c @@ -0,0 +1,1549 @@ +/* sh-stub.c -- debugging stub for the Hitachi-SH. + + NOTE!! This code has to be compiled with optimization, otherwise the + function inlining which generates the exception handlers won't work. + +*/ + +/* This is originally based on an m68k software stub written by Glenn + Engel at HP, but has changed quite a bit. + + Modifications for the SH by Ben Lee and Steve Chamberlain + +*/ + +/**************************************************************************** + + THIS SOFTWARE IS NOT COPYRIGHTED + + HP offers the following for use in the public domain. HP makes no + warranty with regard to the software or it's performance and the + user accepts the software "AS IS" with all faults. + + HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD + TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +****************************************************************************/ + + +/* Remote communication protocol. + + A debug packet whose contents are <data> + is encapsulated for transmission in the form: + + $ <data> # CSUM1 CSUM2 + + <data> must be ASCII alphanumeric and cannot include characters + '$' or '#'. If <data> starts with two characters followed by + ':', then the existing stubs interpret this as a sequence number. + + CSUM1 and CSUM2 are ascii hex representation of an 8-bit + checksum of <data>, the most significant nibble is sent first. + the hex digits 0-9,a-f are used. + + Receiver responds with: + + + - if CSUM is correct and ready for next packet + - - if CSUM is incorrect + + <data> is as follows: + All values are encoded in ascii hex digits. + + Request Packet + + read registers g + reply XX....X Each byte of register data + is described by two hex digits. + Registers are in the internal order + for GDB, and the bytes in a register + are in the same order the machine uses. + or ENN for an error. + + write regs GXX..XX Each byte of register data + is described by two hex digits. + reply OK for success + ENN for an error + + write reg Pn...=r... Write register n... with value r..., + which contains two hex digits for each + byte in the register (target byte + order). + reply OK for success + ENN for an error + (not supported by all stubs). + + read mem mAA..AA,LLLL AA..AA is address, LLLL is length. + reply XX..XX XX..XX is mem contents + Can be fewer bytes than requested + if able to read only part of the data. + or ENN NN is errno + + write mem MAA..AA,LLLL:XX..XX + AA..AA is address, + LLLL is number of bytes, + XX..XX is data + reply OK for success + ENN for an error (this includes the case + where only part of the data was + written). + + cont cAA..AA AA..AA is address to resume + If AA..AA is omitted, + resume at same address. + + step sAA..AA AA..AA is address to resume + If AA..AA is omitted, + resume at same address. + + last signal ? Reply the current reason for stopping. + This is the same reply as is generated + for step or cont : SAA where AA is the + signal number. + + There is no immediate reply to step or cont. + The reply comes when the machine stops. + It is SAA AA is the "signal number" + + or... TAAn...:r...;n:r...;n...:r...; + AA = signal number + n... = register number + r... = register contents + or... WAA The process exited, and AA is + the exit status. This is only + applicable for certains sorts of + targets. + kill request k + + toggle debug d toggle debug flag (see 386 & 68k stubs) + reset r reset -- see sparc stub. + reserved <other> On other requests, the stub should + ignore the request and send an empty + response ($#<checksum>). This way + we can extend the protocol and GDB + can tell whether the stub it is + talking to uses the old or the new. + search tAA:PP,MM Search backwards starting at address + AA for a match with pattern PP and + mask MM. PP and MM are 4 bytes. + Not supported by all stubs. + + general query qXXXX Request info about XXXX. + general set QXXXX=yyyy Set value of XXXX to yyyy. + query sect offs qOffsets Get section offsets. Reply is + Text=xxx;Data=yyy;Bss=zzz + console output Otext Send text to stdout. Only comes from + remote target. + + Responses can be run-length encoded to save space. A '*' means that + the next character is an ASCII encoding giving a repeat count which + stands for that many repititions of the character preceding the '*'. + The encoding is n+29, yielding a printable character where n >=3 + (which is where rle starts to win). Don't use an n > 126. + + So + "0* " means the same as "0000". */ + +#include <string.h> +#include <setjmp.h> + + + +#define COND_BR_MASK 0xff00 +#define UCOND_DBR_MASK 0xe000 +#define UCOND_RBR_MASK 0xf0df +#define TRAPA_MASK 0xff00 + +#define COND_DISP 0x00ff +#define UCOND_DISP 0x0fff +#define UCOND_REG 0x0f00 + +#define BF_INSTR 0x8b00 +#define BT_INSTR 0x8900 +#define BRA_INSTR 0xa000 +#define BSR_INSTR 0xb000 +#define JMP_INSTR 0x402b +#define JSR_INSTR 0x400b +#define RTS_INSTR 0x000b +#define RTE_INSTR 0x002b +#define TRAPA_INSTR 0xc300 + +#define SSTEP_INSTR 0xc3ff + +#define T_BIT_MASK 0x0001 +/* + * BUFMAX defines the maximum number of characters in inbound/outbound + * buffers at least NUMREGBYTES*2 are needed for register packets + */ +#define BUFMAX 1024 + +/* + * Number of bytes for registers + */ +#define NUMREGBYTES 112 /* 92 */ + +/* + * typedef + */ +typedef void (*Function) (); + +/* + * Forward declarations + */ + +static int hex (char); +static char *mem2hex (char *, char *, int); +static char *hex2mem (char *, char *, int); +static int hexToInt (char **, int *); +static void getpacket (char *); +static void putpacket (char *); +static void handle_buserror (void); +static int computeSignal (int exceptionVector); +static void handle_exception (int exceptionVector); +void init_serial(); + +void putDebugChar (char); +char getDebugChar (void); + +/* These are in the file but in asm statements so the compiler can't see them */ +void catch_exception_4 (void); +void catch_exception_6 (void); +void catch_exception_9 (void); +void catch_exception_10 (void); +void catch_exception_11 (void); +void catch_exception_32 (void); +void catch_exception_33 (void); +void catch_exception_255 (void); + + + +#define catch_exception_random catch_exception_255 /* Treat all odd ones like 255 */ + +void breakpoint (void); + + +#define init_stack_size 8*1024 /* if you change this you should also modify BINIT */ +#define stub_stack_size 8*1024 + +int init_stack[init_stack_size] __attribute__ ((section ("stack"))) = {0}; +int stub_stack[stub_stack_size] __attribute__ ((section ("stack"))) = {0}; + +typedef struct + { + void (*func_cold) (); + int *stack_cold; + void (*func_warm) (); + int *stack_warm; + void (*(handler[256 - 4])) (); + } +vec_type; + + +void INIT (); +void BINIT (); + +/* When you link take care that this is at address 0 - + or wherever your vbr points */ + +#define CPU_BUS_ERROR_VEC 9 +#define DMA_BUS_ERROR_VEC 10 +#define NMI_VEC 11 +#define INVALID_INSN_VEC 4 +#define INVALID_SLOT_VEC 6 +#define TRAP_VEC 32 +#define IO_VEC 33 +#define USER_VEC 255 + + +#define BCR (*(volatile short *)(0x05FFFFA0)) /* Bus control register */ +#define BAS (0x800) /* Byte access select */ +#define WCR1 (*(volatile short *)(0x05ffffA2)) /* Wait state control register */ + +const vec_type vectable = +{ + &BINIT, /* 0: Power-on reset PC */ + init_stack + init_stack_size, /* 1: Power-on reset SP */ + &BINIT, /* 2: Manual reset PC */ + init_stack + init_stack_size, /* 3: Manual reset SP */ +{ + &catch_exception_4, /* 4: General invalid instruction */ + &catch_exception_random, /* 5: Reserved for system */ + &catch_exception_6, /* 6: Invalid slot instruction */ + &catch_exception_random, /* 7: Reserved for system */ + &catch_exception_random, /* 8: Reserved for system */ + &catch_exception_9, /* 9: CPU bus error */ + &catch_exception_10, /* 10: DMA bus error */ + &catch_exception_11, /* 11: NMI */ + &catch_exception_random, /* 12: User break */ + &catch_exception_random, /* 13: Reserved for system */ + &catch_exception_random, /* 14: Reserved for system */ + &catch_exception_random, /* 15: Reserved for system */ + &catch_exception_random, /* 16: Reserved for system */ + &catch_exception_random, /* 17: Reserved for system */ + &catch_exception_random, /* 18: Reserved for system */ + &catch_exception_random, /* 19: Reserved for system */ + &catch_exception_random, /* 20: Reserved for system */ + &catch_exception_random, /* 21: Reserved for system */ + &catch_exception_random, /* 22: Reserved for system */ + &catch_exception_random, /* 23: Reserved for system */ + &catch_exception_random, /* 24: Reserved for system */ + &catch_exception_random, /* 25: Reserved for system */ + &catch_exception_random, /* 26: Reserved for system */ + &catch_exception_random, /* 27: Reserved for system */ + &catch_exception_random, /* 28: Reserved for system */ + &catch_exception_random, /* 29: Reserved for system */ + &catch_exception_random, /* 30: Reserved for system */ + &catch_exception_random, /* 31: Reserved for system */ + &catch_exception_32, /* 32: Trap instr (user vectors) */ + &catch_exception_33, /* 33: Trap instr (user vectors) */ + &catch_exception_random, /* 34: Trap instr (user vectors) */ + &catch_exception_random, /* 35: Trap instr (user vectors) */ + &catch_exception_random, /* 36: Trap instr (user vectors) */ + &catch_exception_random, /* 37: Trap instr (user vectors) */ + &catch_exception_random, /* 38: Trap instr (user vectors) */ + &catch_exception_random, /* 39: Trap instr (user vectors) */ + &catch_exception_random, /* 40: Trap instr (user vectors) */ + &catch_exception_random, /* 41: Trap instr (user vectors) */ + &catch_exception_random, /* 42: Trap instr (user vectors) */ + &catch_exception_random, /* 43: Trap instr (user vectors) */ + &catch_exception_random, /* 44: Trap instr (user vectors) */ + &catch_exception_random, /* 45: Trap instr (user vectors) */ + &catch_exception_random, /* 46: Trap instr (user vectors) */ + &catch_exception_random, /* 47: Trap instr (user vectors) */ + &catch_exception_random, /* 48: Trap instr (user vectors) */ + &catch_exception_random, /* 49: Trap instr (user vectors) */ + &catch_exception_random, /* 50: Trap instr (user vectors) */ + &catch_exception_random, /* 51: Trap instr (user vectors) */ + &catch_exception_random, /* 52: Trap instr (user vectors) */ + &catch_exception_random, /* 53: Trap instr (user vectors) */ + &catch_exception_random, /* 54: Trap instr (user vectors) */ + &catch_exception_random, /* 55: Trap instr (user vectors) */ + &catch_exception_random, /* 56: Trap instr (user vectors) */ + &catch_exception_random, /* 57: Trap instr (user vectors) */ + &catch_exception_random, /* 58: Trap instr (user vectors) */ + &catch_exception_random, /* 59: Trap instr (user vectors) */ + &catch_exception_random, /* 60: Trap instr (user vectors) */ + &catch_exception_random, /* 61: Trap instr (user vectors) */ + &catch_exception_random, /* 62: Trap instr (user vectors) */ + &catch_exception_random, /* 63: Trap instr (user vectors) */ + &catch_exception_random, /* 64: IRQ0 */ + &catch_exception_random, /* 65: IRQ1 */ + &catch_exception_random, /* 66: IRQ2 */ + &catch_exception_random, /* 67: IRQ3 */ + &catch_exception_random, /* 68: IRQ4 */ + &catch_exception_random, /* 69: IRQ5 */ + &catch_exception_random, /* 70: IRQ6 */ + &catch_exception_random, /* 71: IRQ7 */ + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_random, + &catch_exception_255}}; + + +char in_nmi; /* Set when handling an NMI, so we don't reenter */ +int dofault; /* Non zero, bus errors will raise exception */ + +int *stub_sp; + +/* debug > 0 prints ill-formed commands in valid packets & checksum errors */ +int remote_debug; + +/* jump buffer used for setjmp/longjmp */ +jmp_buf remcomEnv; + +enum regnames + { + R0, R1, R2, R3, R4, R5, R6, R7, + R8, R9, R10, R11, R12, R13, R14, + R15, PC, PR, GBR, VBR, MACH, MACL, SR, + TICKS, STALLS, CYCLES, INSTS, PLR + }; + +typedef struct + { + short *memAddr; + short oldInstr; + } +stepData; + +int registers[NUMREGBYTES / 4]; +stepData instrBuffer; +char stepped; +static const char hexchars[] = "0123456789abcdef"; +char remcomInBuffer[BUFMAX]; +char remcomOutBuffer[BUFMAX]; + +char highhex(int x) +{ + return hexchars[(x >> 4) & 0xf]; +} + +char lowhex(int x) +{ + return hexchars[x & 0xf]; +} + +/* + * Assembly macros + */ + +#define BREAKPOINT() asm("trapa #0x20"::); + + +/* + * Routines to handle hex data + */ + +static int +hex (char ch) +{ + if ((ch >= 'a') && (ch <= 'f')) + return (ch - 'a' + 10); + if ((ch >= '0') && (ch <= '9')) + return (ch - '0'); + if ((ch >= 'A') && (ch <= 'F')) + return (ch - 'A' + 10); + return (-1); +} + +/* convert the memory, pointed to by mem into hex, placing result in buf */ +/* return a pointer to the last char put in buf (null) */ +static char * +mem2hex (char *mem, char *buf, int count) +{ + int i; + int ch; + for (i = 0; i < count; i++) + { + ch = *mem++; + *buf++ = highhex (ch); + *buf++ = lowhex (ch); + } + *buf = 0; + return (buf); +} + +/* convert the hex array pointed to by buf into binary, to be placed in mem */ +/* return a pointer to the character after the last byte written */ + +static char * +hex2mem (char *buf, char *mem, int count) +{ + int i; + unsigned char ch; + for (i = 0; i < count; i++) + { + ch = hex (*buf++) << 4; + ch = ch + hex (*buf++); + *mem++ = ch; + } + return (mem); +} + +/**********************************************/ +/* WHILE WE FIND NICE HEX CHARS, BUILD AN INT */ +/* RETURN NUMBER OF CHARS PROCESSED */ +/**********************************************/ +static int +hexToInt (char **ptr, int *intValue) +{ + int numChars = 0; + int hexValue; + + *intValue = 0; + + while (**ptr) + { + hexValue = hex (**ptr); + if (hexValue >= 0) + { + *intValue = (*intValue << 4) | hexValue; + numChars++; + } + else + break; + + (*ptr)++; + } + + return (numChars); +} + +/* + * Routines to get and put packets + */ + +/* scan for the sequence $<data>#<checksum> */ + +static +void +getpacket (char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + char ch; + do + { + /* wait around for the start character, ignore all other characters */ + while ((ch = getDebugChar ()) != '$'); + checksum = 0; + xmitcsum = -1; + + count = 0; + + /* now, read until a # or end of buffer is found */ + while (count < BUFMAX) + { + ch = getDebugChar (); + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + buffer[count] = 0; + + if (ch == '#') + { + xmitcsum = hex (getDebugChar ()) << 4; + xmitcsum += hex (getDebugChar ()); + if (checksum != xmitcsum) + putDebugChar ('-'); /* failed checksum */ + else + { + putDebugChar ('+'); /* successful transfer */ + /* if a sequence char is present, reply the sequence ID */ + if (buffer[2] == ':') + { + putDebugChar (buffer[0]); + putDebugChar (buffer[1]); + /* remove sequence chars from buffer */ + count = strlen (buffer); + for (i = 3; i <= count; i++) + buffer[i - 3] = buffer[i]; + } + } + } + } + while (checksum != xmitcsum); + +} + + +/* send the packet in buffer. The host get's one chance to read it. + This routine does not wait for a positive acknowledge. */ + +static void +putpacket (register char *buffer) +{ + register int checksum; + register int count; + + /* $<packet info>#<checksum>. */ + do + { + char *src = buffer; + putDebugChar ('$'); + checksum = 0; + + while (*src) + { + int runlen; + + /* Do run length encoding */ + for (runlen = 0; runlen < 100; runlen ++) + { + if (src[0] != src[runlen]) + { + if (runlen > 3) + { + int encode; + /* Got a useful amount */ + putDebugChar (*src); + checksum += *src; + putDebugChar ('*'); + checksum += '*'; + checksum += (encode = runlen + ' ' - 4); + putDebugChar (encode); + src += runlen; + } + else + { + putDebugChar (*src); + checksum += *src; + src++; + } + break; + } + } + } + + + putDebugChar ('#'); + putDebugChar (highhex(checksum)); + putDebugChar (lowhex(checksum)); + } + while (getDebugChar() != '+'); + +} + + +/* a bus error has occurred, perform a longjmp + to return execution and allow handling of the error */ + +void +handle_buserror (void) +{ + longjmp (remcomEnv, 1); +} + +/* + * this function takes the SH-1 exception number and attempts to + * translate this number into a unix compatible signal value + */ +static int +computeSignal (int exceptionVector) +{ + int sigval; + switch (exceptionVector) + { + case INVALID_INSN_VEC: + sigval = 4; + break; + case INVALID_SLOT_VEC: + sigval = 4; + break; + case CPU_BUS_ERROR_VEC: + sigval = 10; + break; + case DMA_BUS_ERROR_VEC: + sigval = 10; + break; + case NMI_VEC: + sigval = 2; + break; + + case TRAP_VEC: + case USER_VEC: + sigval = 5; + break; + + default: + sigval = 7; /* "software generated"*/ + break; + } + return (sigval); +} + +void +doSStep (void) +{ + short *instrMem; + int displacement; + int reg; + unsigned short opcode; + + instrMem = (short *) registers[PC]; + + opcode = *instrMem; + stepped = 1; + + if ((opcode & COND_BR_MASK) == BT_INSTR) + { + if (registers[SR] & T_BIT_MASK) + { + displacement = (opcode & COND_DISP) << 1; + if (displacement & 0x80) + displacement |= 0xffffff00; + /* + * Remember PC points to second instr. + * after PC of branch ... so add 4 + */ + instrMem = (short *) (registers[PC] + displacement + 4); + } + else + instrMem += 1; + } + else if ((opcode & COND_BR_MASK) == BF_INSTR) + { + if (registers[SR] & T_BIT_MASK) + instrMem += 1; + else + { + displacement = (opcode & COND_DISP) << 1; + if (displacement & 0x80) + displacement |= 0xffffff00; + /* + * Remember PC points to second instr. + * after PC of branch ... so add 4 + */ + instrMem = (short *) (registers[PC] + displacement + 4); + } + } + else if ((opcode & UCOND_DBR_MASK) == BRA_INSTR) + { + displacement = (opcode & UCOND_DISP) << 1; + if (displacement & 0x0800) + displacement |= 0xfffff000; + + /* + * Remember PC points to second instr. + * after PC of branch ... so add 4 + */ + instrMem = (short *) (registers[PC] + displacement + 4); + } + else if ((opcode & UCOND_RBR_MASK) == JSR_INSTR) + { + reg = (char) ((opcode & UCOND_REG) >> 8); + + instrMem = (short *) registers[reg]; + } + else if (opcode == RTS_INSTR) + instrMem = (short *) registers[PR]; + else if (opcode == RTE_INSTR) + instrMem = (short *) registers[15]; + else if ((opcode & TRAPA_MASK) == TRAPA_INSTR) + instrMem = (short *) ((opcode & ~TRAPA_MASK) << 2); + else + instrMem += 1; + + instrBuffer.memAddr = instrMem; + instrBuffer.oldInstr = *instrMem; + *instrMem = SSTEP_INSTR; +} + + +/* Undo the effect of a previous doSStep. If we single stepped, + restore the old instruction. */ + +void +undoSStep (void) +{ + if (stepped) + { short *instrMem; + instrMem = instrBuffer.memAddr; + *instrMem = instrBuffer.oldInstr; + } + stepped = 0; +} + +/* +This function does all exception handling. It only does two things - +it figures out why it was called and tells gdb, and then it reacts +to gdb's requests. + +When in the monitor mode we talk a human on the serial line rather than gdb. + +*/ + + +void +gdb_handle_exception (int exceptionVector) +{ + int sigval; + int addr, length; + char *ptr; + + /* reply to host that an exception has occurred */ + sigval = computeSignal (exceptionVector); + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = highhex(sigval); + remcomOutBuffer[2] = lowhex (sigval); + remcomOutBuffer[3] = 0; + + putpacket (remcomOutBuffer); + + /* + * exception 255 indicates a software trap + * inserted in place of code ... so back up + * PC by one instruction, since this instruction + * will later be replaced by its original one! + */ + if (exceptionVector == 0xff + || exceptionVector == 0x20) + registers[PC] -= 2; + + /* + * Do the thangs needed to undo + * any stepping we may have done! + */ + undoSStep (); + + while (1) + { + remcomOutBuffer[0] = 0; + getpacket (remcomInBuffer); + + switch (remcomInBuffer[0]) + { + case '?': + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = highhex (sigval); + remcomOutBuffer[2] = lowhex (sigval); + remcomOutBuffer[3] = 0; + break; + case 'd': + remote_debug = !(remote_debug); /* toggle debug flag */ + break; + case 'g': /* return the value of the CPU registers */ + mem2hex ((char *) registers, remcomOutBuffer, NUMREGBYTES); + break; + case 'G': /* set the value of the CPU registers - return OK */ + hex2mem (&remcomInBuffer[1], (char *) registers, NUMREGBYTES); + strcpy (remcomOutBuffer, "OK"); + break; + + /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ + case 'm': + if (setjmp (remcomEnv) == 0) + { + dofault = 0; + /* TRY, TO READ %x,%x. IF SUCCEED, SET PTR = 0 */ + ptr = &remcomInBuffer[1]; + if (hexToInt (&ptr, &addr)) + if (*(ptr++) == ',') + if (hexToInt (&ptr, &length)) + { + ptr = 0; + mem2hex ((char *) addr, remcomOutBuffer, length); + } + if (ptr) + strcpy (remcomOutBuffer, "E01"); + } + else + strcpy (remcomOutBuffer, "E03"); + + /* restore handler for bus error */ + dofault = 1; + break; + + /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ + case 'M': + if (setjmp (remcomEnv) == 0) + { + dofault = 0; + + /* TRY, TO READ '%x,%x:'. IF SUCCEED, SET PTR = 0 */ + ptr = &remcomInBuffer[1]; + if (hexToInt (&ptr, &addr)) + if (*(ptr++) == ',') + if (hexToInt (&ptr, &length)) + if (*(ptr++) == ':') + { + hex2mem (ptr, (char *) addr, length); + ptr = 0; + strcpy (remcomOutBuffer, "OK"); + } + if (ptr) + strcpy (remcomOutBuffer, "E02"); + } + else + strcpy (remcomOutBuffer, "E03"); + + /* restore handler for bus error */ + dofault = 1; + break; + + /* cAA..AA Continue at address AA..AA(optional) */ + /* sAA..AA Step one instruction from AA..AA(optional) */ + case 'c': + case 's': + { + /* tRY, to read optional parameter, pc unchanged if no parm */ + ptr = &remcomInBuffer[1]; + if (hexToInt (&ptr, &addr)) + registers[PC] = addr; + + if (remcomInBuffer[0] == 's') + doSStep (); + } + return; + break; + + /* kill the program */ + case 'k': /* do nothing */ + break; + } /* switch */ + + /* reply to the request */ + putpacket (remcomOutBuffer); + } +} + + +#define GDBCOOKIE 0x5ac +static int ingdbmode; +/* We've had an exception - choose to go into the monitor or + the gdb stub */ +void handle_exception(int exceptionVector) +{ +#ifdef MONITOR + if (ingdbmode != GDBCOOKIE) + monitor_handle_exception (exceptionVector); + else +#endif + gdb_handle_exception (exceptionVector); + +} + +void +gdb_mode() +{ + ingdbmode = GDBCOOKIE; + breakpoint(); +} +/* This function will generate a breakpoint exception. It is used at the + beginning of a program to sync up with a debugger and can be used + otherwise as a quick means to stop program execution and "break" into + the debugger. */ + +void +breakpoint (void) +{ + BREAKPOINT (); +} + +asm ("_BINIT: mov.l L1,r15"); +asm ("bra _INIT"); +asm ("nop"); +asm ("L1: .long _init_stack + 8*1024*4"); +void +INIT (void) +{ + /* First turn on the ram */ + WCR1 = 0; /* Never sample wait */ + BCR = BAS; /* use lowbyte/high byte */ + + init_serial(); + +#ifdef MONITOR + reset_hook (); +#endif + + + in_nmi = 0; + dofault = 1; + stepped = 0; + + stub_sp = stub_stack + stub_stack_size; + breakpoint (); + + while (1) + ; +} + + +static void sr() +{ + + + /* Calling Reset does the same as pressing the button */ + asm (".global _Reset + .global _WarmReset +_Reset: +_WarmReset: + mov.l L_sp,r15 + bra _INIT + nop + .align 2 +L_sp: .long _init_stack + 8000"); + + asm("saveRegisters: + mov.l @(L_reg, pc), r0 + mov.l @r15+, r1 ! pop R0 + mov.l r2, @(0x08, r0) ! save R2 + mov.l r1, @r0 ! save R0 + mov.l @r15+, r1 ! pop R1 + mov.l r3, @(0x0c, r0) ! save R3 + mov.l r1, @(0x04, r0) ! save R1 + mov.l r4, @(0x10, r0) ! save R4 + mov.l r5, @(0x14, r0) ! save R5 + mov.l r6, @(0x18, r0) ! save R6 + mov.l r7, @(0x1c, r0) ! save R7 + mov.l r8, @(0x20, r0) ! save R8 + mov.l r9, @(0x24, r0) ! save R9 + mov.l r10, @(0x28, r0) ! save R10 + mov.l r11, @(0x2c, r0) ! save R11 + mov.l r12, @(0x30, r0) ! save R12 + mov.l r13, @(0x34, r0) ! save R13 + mov.l r14, @(0x38, r0) ! save R14 + mov.l @r15+, r4 ! save arg to handleException + add #8, r15 ! hide PC/SR values on stack + mov.l r15, @(0x3c, r0) ! save R15 + add #-8, r15 ! save still needs old SP value + add #92, r0 ! readjust register pointer + mov r15, r2 + add #4, r2 + mov.l @r2, r2 ! R2 has SR + mov.l @r15, r1 ! R1 has PC + mov.l r2, @-r0 ! save SR + sts.l macl, @-r0 ! save MACL + sts.l mach, @-r0 ! save MACH + stc.l vbr, @-r0 ! save VBR + stc.l gbr, @-r0 ! save GBR + sts.l pr, @-r0 ! save PR + mov.l @(L_stubstack, pc), r2 + mov.l @(L_hdl_except, pc), r3 + mov.l @r2, r15 + jsr @r3 + mov.l r1, @-r0 ! save PC + mov.l @(L_stubstack, pc), r0 + mov.l @(L_reg, pc), r1 + bra restoreRegisters + mov.l r15, @r0 ! save __stub_stack + + .align 2 +L_reg: + .long _registers +L_stubstack: + .long _stub_sp +L_hdl_except: + .long _handle_exception"); + +} + +static void rr() +{ +asm(" + .align 2 + .global _resume +_resume: + mov r4,r1 +restoreRegisters: + add #8, r1 ! skip to R2 + mov.l @r1+, r2 ! restore R2 + mov.l @r1+, r3 ! restore R3 + mov.l @r1+, r4 ! restore R4 + mov.l @r1+, r5 ! restore R5 + mov.l @r1+, r6 ! restore R6 + mov.l @r1+, r7 ! restore R7 + mov.l @r1+, r8 ! restore R8 + mov.l @r1+, r9 ! restore R9 + mov.l @r1+, r10 ! restore R10 + mov.l @r1+, r11 ! restore R11 + mov.l @r1+, r12 ! restore R12 + mov.l @r1+, r13 ! restore R13 + mov.l @r1+, r14 ! restore R14 + mov.l @r1+, r15 ! restore programs stack + mov.l @r1+, r0 + add #-8, r15 ! uncover PC/SR on stack + mov.l r0, @r15 ! restore PC onto stack + lds.l @r1+, pr ! restore PR + ldc.l @r1+, gbr ! restore GBR + ldc.l @r1+, vbr ! restore VBR + lds.l @r1+, mach ! restore MACH + lds.l @r1+, macl ! restore MACL + mov.l @r1, r0 + add #-88, r1 ! readjust reg pointer to R1 + mov.l r0, @(4, r15) ! restore SR onto stack+4 + mov.l r2, @-r15 + mov.l L_in_nmi, r0 + mov #0, r2 + mov.b r2, @r0 + mov.l @r15+, r2 + mov.l @r1+, r0 ! restore R0 + rte + mov.l @r1, r1 ! restore R1 + +"); +} + + +static __inline__ void code_for_catch_exception(int n) +{ + asm(" .globl _catch_exception_%O0" : : "i" (n) ); + asm(" _catch_exception_%O0:" :: "i" (n) ); + + asm(" add #-4, r15 ! reserve spot on stack "); + asm(" mov.l r1, @-r15 ! push R1 "); + + if (n == NMI_VEC) + { + /* Special case for NMI - make sure that they don't nest */ + asm(" mov.l r0, @-r15 ! push R0"); + asm(" mov.l L_in_nmi, r0"); + asm(" tas.b @r0 ! Fend off against addtnl NMIs"); + asm(" bt noNMI"); + asm(" mov.l @r15+, r0"); + asm(" mov.l @r15+, r1"); + asm(" add #4, r15"); + asm(" rte"); + asm(" nop"); + asm(".align 2"); + asm("L_in_nmi: .long _in_nmi"); + asm("noNMI:"); + } + else + { + + if (n == CPU_BUS_ERROR_VEC) + { + /* Exception 9 (bus errors) are disasbleable - so that you + can probe memory and get zero instead of a fault. + Because the vector table may be in ROM we don't revector + the interrupt like all the other stubs, we check in here + */ + asm("mov.l L_dofault,r1"); + asm("mov.l @r1,r1"); + asm("tst r1,r1"); + asm("bf faultaway"); + asm("bsr _handle_buserror"); + asm(".align 2"); + asm("L_dofault: .long _dofault"); + asm("faultaway:"); + } + asm(" mov #15<<4, r1 "); + asm(" ldc r1, sr ! disable interrupts "); + asm(" mov.l r0, @-r15 ! push R0 "); + } + + /* Prepare for saving context, we've already pushed r0 and r1, stick exception number + into the frame */ + asm(" mov r15, r0 "); + asm(" add #8, r0 "); + asm(" mov %0,r1" :: "i" (n) ); + asm(" extu.b r1,r1 "); + asm(" bra saveRegisters ! save register values "); + asm(" mov.l r1, @r0 ! save exception # "); +} + + +static void +exceptions() +{ + code_for_catch_exception (CPU_BUS_ERROR_VEC); + code_for_catch_exception (DMA_BUS_ERROR_VEC); + code_for_catch_exception (INVALID_INSN_VEC); + code_for_catch_exception (INVALID_SLOT_VEC); + code_for_catch_exception (NMI_VEC); + code_for_catch_exception (TRAP_VEC); + code_for_catch_exception (USER_VEC); + code_for_catch_exception (IO_VEC); +} + + + + + + +/* Support for Serial I/O using on chip uart */ + +#define SMR0 (*(volatile char *)(0x05FFFEC0)) /* Channel 0 serial mode register */ +#define BRR0 (*(volatile char *)(0x05FFFEC1)) /* Channel 0 bit rate register */ +#define SCR0 (*(volatile char *)(0x05FFFEC2)) /* Channel 0 serial control register */ +#define TDR0 (*(volatile char *)(0x05FFFEC3)) /* Channel 0 transmit data register */ +#define SSR0 (*(volatile char *)(0x05FFFEC4)) /* Channel 0 serial status register */ +#define RDR0 (*(volatile char *)(0x05FFFEC5)) /* Channel 0 receive data register */ + +#define SMR1 (*(volatile char *)(0x05FFFEC8)) /* Channel 1 serial mode register */ +#define BRR1 (*(volatile char *)(0x05FFFEC9)) /* Channel 1 bit rate register */ +#define SCR1 (*(volatile char *)(0x05FFFECA)) /* Channel 1 serial control register */ +#define TDR1 (*(volatile char *)(0x05FFFECB)) /* Channel 1 transmit data register */ +#define SSR1 (*(volatile char *)(0x05FFFECC)) /* Channel 1 serial status register */ +#define RDR1 (*(volatile char *)(0x05FFFECD)) /* Channel 1 receive data register */ + +/* + * Serial mode register bits + */ + +#define SYNC_MODE 0x80 +#define SEVEN_BIT_DATA 0x40 +#define PARITY_ON 0x20 +#define ODD_PARITY 0x10 +#define STOP_BITS_2 0x08 +#define ENABLE_MULTIP 0x04 +#define PHI_64 0x03 +#define PHI_16 0x02 +#define PHI_4 0x01 + +/* + * Serial control register bits + */ +#define SCI_TIE 0x80 /* Transmit interrupt enable */ +#define SCI_RIE 0x40 /* Receive interrupt enable */ +#define SCI_TE 0x20 /* Transmit enable */ +#define SCI_RE 0x10 /* Receive enable */ +#define SCI_MPIE 0x08 /* Multiprocessor interrupt enable */ +#define SCI_TEIE 0x04 /* Transmit end interrupt enable */ +#define SCI_CKE1 0x02 /* Clock enable 1 */ +#define SCI_CKE0 0x01 /* Clock enable 0 */ + +/* + * Serial status register bits + */ +#define SCI_TDRE 0x80 /* Transmit data register empty */ +#define SCI_RDRF 0x40 /* Receive data register full */ +#define SCI_ORER 0x20 /* Overrun error */ +#define SCI_FER 0x10 /* Framing error */ +#define SCI_PER 0x08 /* Parity error */ +#define SCI_TEND 0x04 /* Transmit end */ +#define SCI_MPB 0x02 /* Multiprocessor bit */ +#define SCI_MPBT 0x01 /* Multiprocessor bit transfer */ + + +/* + * Port B IO Register (PBIOR) + */ +#define PBIOR (*(volatile char *)(0x05FFFFC6)) +#define PB15IOR 0x8000 +#define PB14IOR 0x4000 +#define PB13IOR 0x2000 +#define PB12IOR 0x1000 +#define PB11IOR 0x0800 +#define PB10IOR 0x0400 +#define PB9IOR 0x0200 +#define PB8IOR 0x0100 +#define PB7IOR 0x0080 +#define PB6IOR 0x0040 +#define PB5IOR 0x0020 +#define PB4IOR 0x0010 +#define PB3IOR 0x0008 +#define PB2IOR 0x0004 +#define PB1IOR 0x0002 +#define PB0IOR 0x0001 + +/* + * Port B Control Register (PBCR1) + */ +#define PBCR1 (*(volatile short *)(0x05FFFFCC)) +#define PB15MD1 0x8000 +#define PB15MD0 0x4000 +#define PB14MD1 0x2000 +#define PB14MD0 0x1000 +#define PB13MD1 0x0800 +#define PB13MD0 0x0400 +#define PB12MD1 0x0200 +#define PB12MD0 0x0100 +#define PB11MD1 0x0080 +#define PB11MD0 0x0040 +#define PB10MD1 0x0020 +#define PB10MD0 0x0010 +#define PB9MD1 0x0008 +#define PB9MD0 0x0004 +#define PB8MD1 0x0002 +#define PB8MD0 0x0001 + +#define PB15MD PB15MD1|PB14MD0 +#define PB14MD PB14MD1|PB14MD0 +#define PB13MD PB13MD1|PB13MD0 +#define PB12MD PB12MD1|PB12MD0 +#define PB11MD PB11MD1|PB11MD0 +#define PB10MD PB10MD1|PB10MD0 +#define PB9MD PB9MD1|PB9MD0 +#define PB8MD PB8MD1|PB8MD0 + +#define PB_TXD1 PB11MD1 +#define PB_RXD1 PB10MD1 +#define PB_TXD0 PB9MD1 +#define PB_RXD0 PB8MD1 + +/* + * Port B Control Register (PBCR2) + */ +#define PBCR2 0x05FFFFCE +#define PB7MD1 0x8000 +#define PB7MD0 0x4000 +#define PB6MD1 0x2000 +#define PB6MD0 0x1000 +#define PB5MD1 0x0800 +#define PB5MD0 0x0400 +#define PB4MD1 0x0200 +#define PB4MD0 0x0100 +#define PB3MD1 0x0080 +#define PB3MD0 0x0040 +#define PB2MD1 0x0020 +#define PB2MD0 0x0010 +#define PB1MD1 0x0008 +#define PB1MD0 0x0004 +#define PB0MD1 0x0002 +#define PB0MD0 0x0001 + +#define PB7MD PB7MD1|PB7MD0 +#define PB6MD PB6MD1|PB6MD0 +#define PB5MD PB5MD1|PB5MD0 +#define PB4MD PB4MD1|PB4MD0 +#define PB3MD PB3MD1|PB3MD0 +#define PB2MD PB2MD1|PB2MD0 +#define PB1MD PB1MD1|PB1MD0 +#define PB0MD PB0MD1|PB0MD0 + + +#ifdef MHZ +#define BPS 32 * 9600 * MHZ / ( BAUD * 10) +#else +#define BPS 32 /* 9600 for 10 Mhz */ +#endif + +void handleError (char theSSR); + +void +nop () +{ + +} +void +init_serial() +{ + int i; + + /* Clear TE and RE in Channel 1's SCR */ + SCR1 &= ~(SCI_TE | SCI_RE); + + /* Set communication to be async, 8-bit data, no parity, 1 stop bit and use internal clock */ + + SMR1 = 0; + BRR1 = BPS; + + SCR1 &= ~(SCI_CKE1 | SCI_CKE0); + + /* let the hardware settle */ + + for (i = 0; i < 1000; i++) + nop (); + + /* Turn on in and out */ + SCR1 |= SCI_RE | SCI_TE; + + /* Set the PFC to make RXD1 (pin PB8) an input pin and TXD1 (pin PB9) an output pin */ + PBCR1 &= ~(PB_TXD1 | PB_RXD1); + PBCR1 |= PB_TXD1 | PB_RXD1; +} + + +int +getDebugCharReady (void) +{ + char mySSR; + mySSR = SSR1 & ( SCI_PER | SCI_FER | SCI_ORER ); + if ( mySSR ) + handleError ( mySSR ); + return SSR1 & SCI_RDRF ; +} + +char +getDebugChar (void) +{ + char ch; + char mySSR; + + while ( ! getDebugCharReady()) + ; + + ch = RDR1; + SSR1 &= ~SCI_RDRF; + + mySSR = SSR1 & (SCI_PER | SCI_FER | SCI_ORER); + + if (mySSR) + handleError (mySSR); + + return ch; +} + +int +putDebugCharReady() +{ + return (SSR1 & SCI_TDRE); +} + +void +putDebugChar (char ch) +{ + while (!putDebugCharReady()) + ; + + /* + * Write data into TDR and clear TDRE + */ + TDR1 = ch; + SSR1 &= ~SCI_TDRE; +} + +void +handleError (char theSSR) +{ + SSR1 &= ~(SCI_ORER | SCI_PER | SCI_FER); +} + diff --git a/contrib/gdb/gdb/solib.c b/contrib/gdb/gdb/solib.c index 7f602f8681dc..c82f841425c9 100644 --- a/contrib/gdb/gdb/solib.c +++ b/contrib/gdb/gdb/solib.c @@ -18,7 +18,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/gdb/gdb/solib.c,v 1.7 1999/11/22 18:04:53 peter Exp $ */ #include "defs.h" diff --git a/contrib/gdb/gdb/sparclet-rom.c b/contrib/gdb/gdb/sparclet-rom.c new file mode 100644 index 000000000000..992cca1ab51c --- /dev/null +++ b/contrib/gdb/gdb/sparclet-rom.c @@ -0,0 +1,296 @@ +/* Remote target glue for the SPARC Sparclet ROM monitor. + Copyright 1995, 1996 Free Software Foundation, Inc. + +This file is part of GDB. + +This program 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 of the License, or +(at your option) any later version. + +This program 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; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + + +#include "defs.h" +#include "gdbcore.h" +#include "target.h" +#include "monitor.h" +#include "serial.h" +#include "srec.h" +#include "symtab.h" +#include "symfile.h" /* for generic_load */ +#include <time.h> + +extern void report_transfer_performance PARAMS ((unsigned long, time_t, time_t)); + +static struct target_ops sparclet_ops; + +static void sparclet_open PARAMS ((char *args, int from_tty)); + +/* This array of registers need to match the indexes used by GDB. + This exists because the various ROM monitors use different strings + than does GDB, and don't necessarily support all the registers + either. So, typing "info reg sp" becomes a "r30". */ + +/*PSR 0x00000080 impl ver icc AW LE EE EC EF PIL S PS ET CWP WIM + 0x0 0x0 0x0 0 0 0 0 0 0x0 1 0 0 0x00 0x2 + 0000010 + INS LOCALS OUTS GLOBALS + 0 0x00000000 0x00000000 0x00000000 0x00000000 + 1 0x00000000 0x00000000 0x00000000 0x00000000 + 2 0x00000000 0x00000000 0x00000000 0x00000000 + 3 0x00000000 0x00000000 0x00000000 0x00000000 + 4 0x00000000 0x00000000 0x00000000 0x00000000 + 5 0x00000000 0x00001000 0x00000000 0x00000000 + 6 0x00000000 0x00000000 0x123f0000 0x00000000 + 7 0x00000000 0x00000000 0x00000000 0x00000000 +pc: 0x12010000 0x00000000 unimp +npc: 0x12010004 0x00001000 unimp 0x1000 +tbr: 0x00000000 +y: 0x00000000 +*/ +/* these correspond to the offsets from tm-* files from config directories */ + +/* is wim part of psr?? */ +/* monitor wants lower case */ +static char *sparclet_regnames[NUM_REGS] = REGISTER_NAMES; + + +/* Function: sparclet_supply_register + Just returns with no action. + This function is required, because parse_register_dump (monitor.c) + expects to be able to call it. If we don't supply something, it will + call a null pointer and core-dump. Since this function does not + actually do anything, GDB will request the registers individually. */ + +static void +sparclet_supply_register (regname, regnamelen, val, vallen) + char *regname; + int regnamelen; + char *val; + int vallen; +{ + return; +} + +static void +sparclet_load (desc, file, hashmark) + serial_t desc; + char *file; + int hashmark; +{ + bfd *abfd; + asection *s; + int i; + CORE_ADDR load_offset; + time_t start_time, end_time; + unsigned long data_count = 0; + + /* enable user to specify address for downloading as 2nd arg to load */ + + i = sscanf(file, "%*s 0x%lx", &load_offset); + if (i >= 1 ) + { + char *p; + + for (p = file; *p != '\000' && !isspace (*p); p++); + + *p = '\000'; + } + else + load_offset = 0; + + abfd = bfd_openr (file, 0); + if (!abfd) + { + printf_filtered ("Unable to open file %s\n", file); + return; + } + + if (bfd_check_format (abfd, bfd_object) == 0) + { + printf_filtered ("File is not an object file\n"); + return; + } + + start_time = time (NULL); + + for (s = abfd->sections; s; s = s->next) + if (s->flags & SEC_LOAD) + { + bfd_size_type section_size; + bfd_vma vma; + + vma = bfd_get_section_vma (abfd, s) + load_offset; + section_size = bfd_section_size (abfd, s); + + data_count += section_size; + + printf_filtered ("%s\t: 0x%4x .. 0x%4x ", + bfd_get_section_name (abfd, s), vma, + vma + section_size); + gdb_flush (gdb_stdout); + + monitor_printf ("load c r %x %x\r", vma, section_size); + + monitor_expect ("load: loading ", NULL, 0); + monitor_expect ("\r", NULL, 0); + + for (i = 0; i < section_size; i += 2048) + { + int numbytes; + char buf[2048]; + + numbytes = min (sizeof buf, section_size - i); + + bfd_get_section_contents (abfd, s, buf, i, numbytes); + + SERIAL_WRITE (desc, buf, numbytes); + + if (hashmark) + { + putchar_unfiltered ('#'); + gdb_flush (gdb_stdout); + } + } /* Per-packet (or S-record) loop */ + + monitor_expect_prompt (NULL, 0); + + putchar_unfiltered ('\n'); + } /* Loadable sections */ + + monitor_printf ("reg pc %x\r", bfd_get_start_address (abfd)); + monitor_expect_prompt (NULL, 0); + monitor_printf ("reg npc %x\r", bfd_get_start_address (abfd) + 4); + monitor_expect_prompt (NULL, 0); + + monitor_printf ("run\r"); + + end_time = time (NULL); + + if (hashmark) + putchar_unfiltered ('\n'); + + report_transfer_performance (data_count, start_time, end_time); + + pop_target (); + push_remote_target (monitor_get_dev_name (), 1); + + return_to_top_level (RETURN_QUIT); +} + +/* Define the monitor command strings. Since these are passed directly + through to a printf style function, we may include formatting + strings. We also need a CR or LF on the end. */ + +/* need to pause the monitor for timing reasons, so slow it down */ + +static char *sparclet_inits[] = {"\n\r\r\n", NULL}; + +static struct monitor_ops sparclet_cmds ; + +static void +init_sparclet_cmds(void) +{ + sparclet_cmds.flags = MO_CLR_BREAK_USES_ADDR | + MO_HEX_PREFIX | + MO_NO_ECHO_ON_OPEN | + MO_NO_ECHO_ON_SETMEM | + MO_RUN_FIRST_TIME | + MO_GETMEM_READ_SINGLE ; /* flags */ + sparclet_cmds.init = sparclet_inits; /* Init strings */ + sparclet_cmds.cont = "cont\r"; /* continue command */ + sparclet_cmds.step = "step\r"; /* single step */ + sparclet_cmds.stop = "\r"; /* break interrupts the program */ + sparclet_cmds.set_break = "+bp %x\r"; /* set a breakpoint */ + sparclet_cmds.clr_break = "-bp %x\r" ; /* can't use "br" because only 2 hw bps are supported */ + sparclet_cmds.clr_all_break = "-bp %x\r"; /* clear a breakpoint */ + "-bp\r" ; /* clear all breakpoints */ + sparclet_cmds.fill = "fill %x -n %x -v %x -b\r"; /* fill (start length val) */ + /* can't use "fi" because it takes words, not bytes */ + /* ex [addr] [-n count] [-b|-s|-l] default: ex cur -n 1 -b */ + sparclet_cmds.setmem.cmdb = "ex %x -b\r%x\rq\r"; /* setmem.cmdb (addr, value) */ + sparclet_cmds.setmem.cmdw = "ex %x -s\r%x\rq\r"; /* setmem.cmdw (addr, value) */ + sparclet_cmds.setmem.cmdl = "ex %x -l\r%x\rq\r"; /* setmem.cmdl (addr, value) */ + sparclet_cmds.setmem.cmdll = NULL; /* setmem.cmdll (addr, value) */ + sparclet_cmds.setmem.resp_delim = NULL; /*": " */ /* setmem.resp_delim */ + sparclet_cmds.setmem.term = NULL; /*"? " */ /* setmem.term */ + sparclet_cmds.setmem.term_cmd = NULL; /*"q\r" */ /* setmem.term_cmd */ + /* since the parsing of multiple bytes is difficult due to + interspersed addresses, we'll only read 1 value at a time, + even tho these can handle a count */ + /* we can use -n to set count to read, but may have to parse? */ + sparclet_cmds.getmem.cmdb = "ex %x -n 1 -b\r"; /* getmem.cmdb (addr, #bytes) */ + sparclet_cmds.getmem.cmdw = "ex %x -n 1 -s\r"; /* getmem.cmdw (addr, #swords) */ + sparclet_cmds.getmem.cmdl = "ex %x -n 1 -l\r"; /* getmem.cmdl (addr, #words) */ + sparclet_cmds.getmem.cmdll = NULL; /* getmem.cmdll (addr, #dwords) */ + sparclet_cmds.getmem.resp_delim = ": "; /* getmem.resp_delim */ + sparclet_cmds.getmem.term = NULL; /* getmem.term */ + sparclet_cmds.getmem.term_cmd = NULL; /* getmem.term_cmd */ + sparclet_cmds.setreg.cmd = "reg %s 0x%x\r"; /* setreg.cmd (name, value) */ + sparclet_cmds.setreg.resp_delim = NULL; /* setreg.resp_delim */ + sparclet_cmds.setreg.term = NULL; /* setreg.term */ + sparclet_cmds.setreg.term_cmd = NULL ; /* setreg.term_cmd */ + sparclet_cmds.getreg.cmd = "reg %s\r"; /* getreg.cmd (name) */ + sparclet_cmds.getreg.resp_delim = " "; /* getreg.resp_delim */ + sparclet_cmds.getreg.term = NULL; /* getreg.term */ + sparclet_cmds.getreg.term_cmd = NULL; /* getreg.term_cmd */ + sparclet_cmds.dump_registers = "reg\r"; /* dump_registers */ + sparclet_cmds.register_pattern = "\\(\\w+\\)=\\([0-9a-fA-F]+\\)"; /* register_pattern */ + sparclet_cmds.supply_register = sparclet_supply_register; /* supply_register */ + sparclet_cmds.load_routine = sparclet_load; /* load_routine */ + sparclet_cmds.load = NULL; /* download command (srecs on console) */ + sparclet_cmds.loadresp = NULL; /* load response */ + sparclet_cmds.prompt = "monitor>"; /* monitor command prompt */ + /* yikes! gdb core dumps without this delimitor!! */ + sparclet_cmds.line_term = "\r"; /* end-of-command delimitor */ + sparclet_cmds.cmd_end = NULL; /* optional command terminator */ + sparclet_cmds.target = &sparclet_ops; /* target operations */ + sparclet_cmds.stopbits = SERIAL_1_STOPBITS; /* number of stop bits */ + sparclet_cmds.regnames = sparclet_regnames; /* registers names */ + sparclet_cmds.magic = MONITOR_OPS_MAGIC ; /* magic */ +}; + +static void +sparclet_open (args, from_tty) + char *args; + int from_tty; +{ + monitor_open (args, &sparclet_cmds, from_tty); +} + +void +_initialize_sparclet () +{ + int i; + init_sparclet_cmds() ; + + for (i = 0; i < NUM_REGS; i++) + if (sparclet_regnames[i][0] == 'c' || + sparclet_regnames[i][0] == 'a') + sparclet_regnames[i] = 0; /* mon can't report c* or a* regs */ + + sparclet_regnames[0] = 0; /* mon won't report %G0 */ + + init_monitor_ops (&sparclet_ops); + sparclet_ops.to_shortname = "sparclet"; /* for the target command */ + sparclet_ops.to_longname = "SPARC Sparclet monitor"; + /* use SW breaks; target only supports 2 HW breakpoints */ + sparclet_ops.to_insert_breakpoint = memory_insert_breakpoint; + sparclet_ops.to_remove_breakpoint = memory_remove_breakpoint; + + sparclet_ops.to_doc = + "Use a board running the Sparclet debug monitor.\n\ +Specify the serial device it is connected to (e.g. /dev/ttya)."; + + sparclet_ops.to_open = sparclet_open; + add_target (&sparclet_ops); +} + diff --git a/contrib/gdb/gdb/sparclet-stub.c b/contrib/gdb/gdb/sparclet-stub.c new file mode 100644 index 000000000000..ecf670b11526 --- /dev/null +++ b/contrib/gdb/gdb/sparclet-stub.c @@ -0,0 +1,1232 @@ +/**************************************************************************** + + THIS SOFTWARE IS NOT COPYRIGHTED + + HP offers the following for use in the public domain. HP makes no + warranty with regard to the software or it's performance and the + user accepts the software "AS IS" with all faults. + + HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD + TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +****************************************************************************/ + +/**************************************************************************** + * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $ + * + * Module name: remcom.c $ + * Revision: 1.34 $ + * Date: 91/03/09 12:29:49 $ + * Contributor: Lake Stevens Instrument Division$ + * + * Description: low level support for gdb debugger. $ + * + * Considerations: only works on target hardware $ + * + * Written by: Glenn Engel $ + * ModuleState: Experimental $ + * + * NOTES: See Below $ + * + * Modified for SPARC by Stu Grossman, Cygnus Support. + * Based on sparc-stub.c, it's modified for SPARClite Debug Unit hardware + * breakpoint support to create sparclite-stub.c, by Kung Hsu, Cygnus Support. + * + * This code has been extensively tested on the Fujitsu SPARClite demo board. + * + * To enable debugger support, two things need to happen. One, a + * call to set_debug_traps() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing a trap #1. + * + ************* + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * P set the value of a single CPU register OK or P01 (???) + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * bBB..BB Set baud rate to BB..BB OK or BNN, then sets + * baud rate + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $<packet info>#<checksum>. + * + * where + * <packet info> :: <characters representing the command or response> + * <checksum> :: <two hex digits computed as modulo 256 sum of <packetinfo>> + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + ****************************************************************************/ + +#include <string.h> +#include <signal.h> + +/************************************************************************ + * + * external low-level support routines + */ + +extern void putDebugChar(); /* write a single character */ +extern int getDebugChar(); /* read and return a single char */ + +/************************************************************************/ +/* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/ +/* at least NUMREGBYTES*2 are needed for register packets */ +#define BUFMAX 2048 + +static int initialized = 0; /* !0 means we've been initialized */ +static int remote_debug = 0; /* turn on verbose debugging */ + +extern void breakinst(); +void _cprint(); +static void hw_breakpoint(); +static void set_mem_fault_trap(); +static void get_in_break_mode(); +static unsigned char *mem2hex(); + +static const char hexchars[]="0123456789abcdef"; + +#define NUMREGS 121 + +static unsigned long saved_stack_pointer; + +/* Number of bytes of registers. */ +#define NUMREGBYTES (NUMREGS * 4) +enum regnames { 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, + + Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR, + CCSR, CCPR, CCCRCR, CCOR, CCOBR, CCIBR, CCIR, UNUSED1, + + ASR1, ASR15, ASR17, ASR18, ASR19, ASR20, ASR21, ASR22, + /* the following not actually implemented */ + AWR0, AWR1, AWR2, AWR3, AWR4, AWR5, AWR6, AWR7, + AWR8, AWR9, AWR10, AWR11, AWR12, AWR13, AWR14, AWR15, + AWR16, AWR17, AWR18, AWR19, AWR20, AWR21, AWR22, AWR23, + AWR24, AWR25, AWR26, AWR27, AWR28, AWR29, AWR30, AWR31, + APSR +}; + +/*************************** ASSEMBLY CODE MACROS *************************/ +/* */ + +extern void trap_low(); + +asm(" + .reserve trapstack, 1000 * 4, \"bss\", 8 + + .data + .align 4 + +in_trap_handler: + .word 0 + + .text + .align 4 + +! This function is called when any SPARC trap (except window overflow or +! underflow) occurs. It makes sure that the invalid register window is still +! available before jumping into C code. It will also restore the world if you +! return from handle_exception. +! +! On entry, trap_low expects l1 and l2 to contain pc and npc respectivly. + + .globl _trap_low +_trap_low: + mov %psr, %l0 + mov %wim, %l3 + + srl %l3, %l0, %l4 ! wim >> cwp + and %l4, 0xff, %l4 ! Mask off windows 28, 29 + cmp %l4, 1 + bne window_fine ! Branch if not in the invalid window + nop + +! Handle window overflow + + mov %g1, %l4 ! Save g1, we use it to hold the wim + srl %l3, 1, %g1 ! Rotate wim right + and %g1, 0xff, %g1 ! Mask off windows 28, 29 + tst %g1 + bg good_wim ! Branch if new wim is non-zero + nop + +! At this point, we need to bring a 1 into the high order bit of the wim. +! Since we don't want to make any assumptions about the number of register +! windows, we figure it out dynamically so as to setup the wim correctly. + + ! The normal way doesn't work on the sparclet as register windows + ! 28 and 29 are special purpose windows. + !not %g1 ! Fill g1 with ones + !mov %g1, %wim ! Fill the wim with ones + !nop + !nop + !nop + !mov %wim, %g1 ! Read back the wim + !inc %g1 ! Now g1 has 1 just to left of wim + !srl %g1, 1, %g1 ! Now put 1 at top of wim + + mov 0x80, %g1 ! Hack for sparclet + + ! This doesn't work on the sparclet. + !mov %g0, %wim ! Clear wim so that subsequent save + ! won't trap + andn %l3, 0xff, %l5 ! Clear wim but not windows 28, 29 + mov %l5, %wim + nop + nop + nop + +good_wim: + save %g0, %g0, %g0 ! Slip into next window + mov %g1, %wim ! Install the new wim + + std %l0, [%sp + 0 * 4] ! save L & I registers + std %l2, [%sp + 2 * 4] + std %l4, [%sp + 4 * 4] + std %l6, [%sp + 6 * 4] + + std %i0, [%sp + 8 * 4] + std %i2, [%sp + 10 * 4] + std %i4, [%sp + 12 * 4] + std %i6, [%sp + 14 * 4] + + restore ! Go back to trap window. + mov %l4, %g1 ! Restore %g1 + +window_fine: + sethi %hi(in_trap_handler), %l4 + ld [%lo(in_trap_handler) + %l4], %l5 + tst %l5 + bg recursive_trap + inc %l5 + + set trapstack+1000*4, %sp ! Switch to trap stack + +recursive_trap: + st %l5, [%lo(in_trap_handler) + %l4] + sub %sp,(16+1+6+1+88)*4,%sp ! Make room for input & locals + ! + hidden arg + arg spill + ! + doubleword alignment + ! + registers[121] + + std %g0, [%sp + (24 + 0) * 4] ! registers[Gx] + std %g2, [%sp + (24 + 2) * 4] + std %g4, [%sp + (24 + 4) * 4] + std %g6, [%sp + (24 + 6) * 4] + + std %i0, [%sp + (24 + 8) * 4] ! registers[Ox] + std %i2, [%sp + (24 + 10) * 4] + std %i4, [%sp + (24 + 12) * 4] + std %i6, [%sp + (24 + 14) * 4] + + ! FP regs (sparclet doesn't have fpu) + + mov %y, %l4 + mov %tbr, %l5 + st %l4, [%sp + (24 + 64) * 4] ! Y + st %l0, [%sp + (24 + 65) * 4] ! PSR + st %l3, [%sp + (24 + 66) * 4] ! WIM + st %l5, [%sp + (24 + 67) * 4] ! TBR + st %l1, [%sp + (24 + 68) * 4] ! PC + st %l2, [%sp + (24 + 69) * 4] ! NPC + ! CPSR and FPSR not impl + or %l0, 0xf20, %l4 + mov %l4, %psr ! Turn on traps, disable interrupts + nop + nop + nop + +! Save coprocessor state. +! See SK/demo/hdlc_demo/ldc_swap_context.S. + + mov %psr, %l0 + sethi %hi(0x2000), %l5 ! EC bit in PSR + or %l5, %l0, %l5 + mov %l5, %psr ! enable coprocessor + nop ! 3 nops after write to %psr (needed?) + nop + nop + crdcxt %ccsr, %l1 ! capture CCSR + mov 0x6, %l2 + cwrcxt %l2, %ccsr ! set CCP state machine for CCFR + crdcxt %ccfr, %l2 ! capture CCOR + cwrcxt %l2, %ccfr ! tickle CCFR + crdcxt %ccfr, %l3 ! capture CCOBR + cwrcxt %l3, %ccfr ! tickle CCFR + crdcxt %ccfr, %l4 ! capture CCIBR + cwrcxt %l4, %ccfr ! tickle CCFR + crdcxt %ccfr, %l5 ! capture CCIR + cwrcxt %l5, %ccfr ! tickle CCFR + crdcxt %ccpr, %l6 ! capture CCPR + crdcxt %cccrcr, %l7 ! capture CCCRCR + st %l1, [%sp + (24 + 72) * 4] ! save CCSR + st %l2, [%sp + (24 + 75) * 4] ! save CCOR + st %l3, [%sp + (24 + 76) * 4] ! save CCOBR + st %l4, [%sp + (24 + 77) * 4] ! save CCIBR + st %l5, [%sp + (24 + 78) * 4] ! save CCIR + st %l6, [%sp + (24 + 73) * 4] ! save CCPR + st %l7, [%sp + (24 + 74) * 4] ! save CCCRCR + mov %l0, %psr ! restore original PSR + nop ! 3 nops after write to %psr (needed?) + nop + nop + +! End of saving coprocessor state. +! Save asr regs + +! Part of this is silly -- we should not display ASR15 or ASR19 at all. + + sethi %hi(0x01000000), %l6 + st %l6, [%sp + (24 + 81) * 4] ! ASR15 == NOP + sethi %hi(0xdeadc0de), %l6 + or %l6, %lo(0xdeadc0de), %l6 + st %l6, [%sp + (24 + 84) * 4] ! ASR19 == DEADC0DE + + rd %asr1, %l4 + st %l4, [%sp + (24 + 80) * 4] +! rd %asr15, %l4 ! must not read ASR15 +! st %l4, [%sp + (24 + 81) * 4] ! (illegal instr trap) + rd %asr17, %l4 + st %l4, [%sp + (24 + 82) * 4] + rd %asr18, %l4 + st %l4, [%sp + (24 + 83) * 4] +! rd %asr19, %l4 ! must not read asr19 +! st %l4, [%sp + (24 + 84) * 4] ! (halts the CPU) + rd %asr20, %l4 + st %l4, [%sp + (24 + 85) * 4] + rd %asr21, %l4 + st %l4, [%sp + (24 + 86) * 4] + rd %asr22, %l4 + st %l4, [%sp + (24 + 87) * 4] + +! End of saving asr regs + + call _handle_exception + add %sp, 24 * 4, %o0 ! Pass address of registers + +! Reload all of the registers that aren't on the stack + + ld [%sp + (24 + 1) * 4], %g1 ! registers[Gx] + ldd [%sp + (24 + 2) * 4], %g2 + ldd [%sp + (24 + 4) * 4], %g4 + ldd [%sp + (24 + 6) * 4], %g6 + + ldd [%sp + (24 + 8) * 4], %i0 ! registers[Ox] + ldd [%sp + (24 + 10) * 4], %i2 + ldd [%sp + (24 + 12) * 4], %i4 + ldd [%sp + (24 + 14) * 4], %i6 + + ! FP regs (sparclet doesn't have fpu) + +! Update the coprocessor registers. +! See SK/demo/hdlc_demo/ldc_swap_context.S. + + mov %psr, %l0 + sethi %hi(0x2000), %l5 ! EC bit in PSR + or %l5, %l0, %l5 + mov %l5, %psr ! enable coprocessor + nop ! 3 nops after write to %psr (needed?) + nop + nop + + mov 0x6, %l2 + cwrcxt %l2, %ccsr ! set CCP state machine for CCFR + + ld [%sp + (24 + 72) * 4], %l1 ! saved CCSR + ld [%sp + (24 + 75) * 4], %l2 ! saved CCOR + ld [%sp + (24 + 76) * 4], %l3 ! saved CCOBR + ld [%sp + (24 + 77) * 4], %l4 ! saved CCIBR + ld [%sp + (24 + 78) * 4], %l5 ! saved CCIR + ld [%sp + (24 + 73) * 4], %l6 ! saved CCPR + ld [%sp + (24 + 74) * 4], %l7 ! saved CCCRCR + + cwrcxt %l2, %ccfr ! restore CCOR + cwrcxt %l3, %ccfr ! restore CCOBR + cwrcxt %l4, %ccfr ! restore CCIBR + cwrcxt %l5, %ccfr ! restore CCIR + cwrcxt %l6, %ccpr ! restore CCPR + cwrcxt %l7, %cccrcr ! restore CCCRCR + cwrcxt %l1, %ccsr ! restore CCSR + + mov %l0, %psr ! restore PSR + nop ! 3 nops after write to %psr (needed?) + nop + nop + +! End of coprocessor handling stuff. +! Update asr regs + + ld [%sp + (24 + 80) * 4], %l4 + wr %l4, %asr1 +! ld [%sp + (24 + 81) * 4], %l4 ! can't write asr15 +! wr %l4, %asr15 + ld [%sp + (24 + 82) * 4], %l4 + wr %l4, %asr17 + ld [%sp + (24 + 83) * 4], %l4 + wr %l4, %asr18 +! ld [%sp + (24 + 84) * 4], %l4 ! can't write asr19 +! wr %l4, %asr19 +! ld [%sp + (24 + 85) * 4], %l4 ! can't write asr20 +! wr %l4, %asr20 +! ld [%sp + (24 + 86) * 4], %l4 ! can't write asr21 +! wr %l4, %asr21 + ld [%sp + (24 + 87) * 4], %l4 + wr %l4, %asr22 + +! End of restoring asr regs + + + ldd [%sp + (24 + 64) * 4], %l0 ! Y & PSR + ldd [%sp + (24 + 68) * 4], %l2 ! PC & NPC + + restore ! Ensure that previous window is valid + save %g0, %g0, %g0 ! by causing a window_underflow trap + + mov %l0, %y + mov %l1, %psr ! Make sure that traps are disabled + ! for rett + nop ! 3 nops after write to %psr (needed?) + nop + nop + + sethi %hi(in_trap_handler), %l4 + ld [%lo(in_trap_handler) + %l4], %l5 + dec %l5 + st %l5, [%lo(in_trap_handler) + %l4] + + jmpl %l2, %g0 ! Restore old PC + rett %l3 ! Restore old nPC +"); + +/* Convert ch from a hex digit to an int */ + +static int +hex(ch) + unsigned char ch; +{ + if (ch >= 'a' && ch <= 'f') + return ch-'a'+10; + if (ch >= '0' && ch <= '9') + return ch-'0'; + if (ch >= 'A' && ch <= 'F') + return ch-'A'+10; + return -1; +} + +/* scan for the sequence $<data>#<checksum> */ + +static void +getpacket(buffer) + char *buffer; +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + unsigned char ch; + + do + { + /* wait around for the start character, ignore all other characters */ + while ((ch = (getDebugChar() & 0x7f)) != '$') + ; + + checksum = 0; + xmitcsum = -1; + + count = 0; + + /* now, read until a # or end of buffer is found */ + while (count < BUFMAX) + { + ch = getDebugChar() & 0x7f; + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + + if (count >= BUFMAX) + continue; + + buffer[count] = 0; + + if (ch == '#') + { + xmitcsum = hex(ch = getDebugChar() & 0x7f) << 4; + xmitcsum |= hex(ch = getDebugChar() & 0x7f); + + if (checksum != xmitcsum) + putDebugChar('-'); /* failed checksum */ + else + { + putDebugChar('+'); /* successful transfer */ + /* if a sequence char is present, reply the sequence ID */ + if (buffer[2] == ':') + { + putDebugChar(buffer[0]); + putDebugChar(buffer[1]); + /* remove sequence chars from buffer */ + count = strlen(buffer); + for (i=3; i <= count; i++) + buffer[i-3] = buffer[i]; + } + } + } + } + while (checksum != xmitcsum); +} + +/* send the packet in buffer. */ + +static void +putpacket(buffer) + unsigned char *buffer; +{ + unsigned char checksum; + int count; + unsigned char ch; + + /* $<packet info>#<checksum>. */ + do + { + putDebugChar('$'); + checksum = 0; + count = 0; + + while (ch = buffer[count]) + { + putDebugChar(ch); + checksum += ch; + count += 1; + } + + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum & 0xf]); + + } + while ((getDebugChar() & 0x7f) != '+'); +} + +static char remcomInBuffer[BUFMAX]; +static char remcomOutBuffer[BUFMAX]; + +/* Indicate to caller of mem2hex or hex2mem that there has been an + error. */ +static volatile int mem_err = 0; + +/* Convert the memory pointed to by mem into hex, placing result in buf. + * Return a pointer to the last char put in buf (null), in case of mem fault, + * return 0. + * If MAY_FAULT is non-zero, then we will handle memory faults by returning + * a 0, else treat a fault like any other fault in the stub. + */ + +static unsigned char * +mem2hex(mem, buf, count, may_fault) + unsigned char *mem; + unsigned char *buf; + int count; + int may_fault; +{ + unsigned char ch; + + set_mem_fault_trap(may_fault); + + while (count-- > 0) + { + ch = *mem++; + if (mem_err) + return 0; + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch & 0xf]; + } + + *buf = 0; + + set_mem_fault_trap(0); + + return buf; +} + +/* convert the hex array pointed to by buf into binary to be placed in mem + * return a pointer to the character AFTER the last byte written */ + +static char * +hex2mem(buf, mem, count, may_fault) + unsigned char *buf; + unsigned char *mem; + int count; + int may_fault; +{ + int i; + unsigned char ch; + + set_mem_fault_trap(may_fault); + + for (i=0; i<count; i++) + { + ch = hex(*buf++) << 4; + ch |= hex(*buf++); + *mem++ = ch; + if (mem_err) + return 0; + } + + set_mem_fault_trap(0); + + return mem; +} + +/* This table contains the mapping between SPARC hardware trap types, and + signals, which are primarily what GDB understands. It also indicates + which hardware traps we need to commandeer when initializing the stub. */ + +static struct hard_trap_info +{ + unsigned char tt; /* Trap type code for SPARClite */ + unsigned char signo; /* Signal that we map this trap into */ +} hard_trap_info[] = { + {1, SIGSEGV}, /* instruction access exception */ + {0x3b, SIGSEGV}, /* instruction access error */ + {2, SIGILL}, /* illegal instruction */ + {3, SIGILL}, /* privileged instruction */ + {4, SIGEMT}, /* fp disabled */ + {0x24, SIGEMT}, /* cp disabled */ + {7, SIGBUS}, /* mem address not aligned */ + {0x29, SIGSEGV}, /* data access exception */ + {10, SIGEMT}, /* tag overflow */ + {128+1, SIGTRAP}, /* ta 1 - normal breakpoint instruction */ + {0, 0} /* Must be last */ +}; + +/* Set up exception handlers for tracing and breakpoints */ + +void +set_debug_traps() +{ + struct hard_trap_info *ht; + + for (ht = hard_trap_info; ht->tt && ht->signo; ht++) + exceptionHandler(ht->tt, trap_low); + + /* In case GDB is started before us, ack any packets (presumably + "$?#xx") sitting there. */ + putDebugChar ('+'); + + initialized = 1; +} + +asm (" +! Trap handler for memory errors. This just sets mem_err to be non-zero. It +! assumes that %l1 is non-zero. This should be safe, as it is doubtful that +! 0 would ever contain code that could mem fault. This routine will skip +! past the faulting instruction after setting mem_err. + + .text + .align 4 + +_fltr_set_mem_err: + sethi %hi(_mem_err), %l0 + st %l1, [%l0 + %lo(_mem_err)] + jmpl %l2, %g0 + rett %l2+4 +"); + +static void +set_mem_fault_trap(enable) + int enable; +{ + extern void fltr_set_mem_err(); + mem_err = 0; + + if (enable) + exceptionHandler(0x29, fltr_set_mem_err); + else + exceptionHandler(0x29, trap_low); +} + +asm (" + .text + .align 4 + +_dummy_hw_breakpoint: + jmpl %l2, %g0 + rett %l2+4 + nop + nop +"); + +static void +set_hw_breakpoint_trap(enable) + int enable; +{ + extern void dummy_hw_breakpoint(); + + if (enable) + exceptionHandler(255, dummy_hw_breakpoint); + else + exceptionHandler(255, trap_low); +} + +static void +get_in_break_mode() +{ +#if 0 + int x; + mesg("get_in_break_mode, sp = "); + phex(&x); +#endif + set_hw_breakpoint_trap(1); + + asm(" + sethi %hi(0xff10), %l4 + or %l4, %lo(0xff10), %l4 + sta %g0, [%l4]0x1 + nop + nop + nop + "); + + set_hw_breakpoint_trap(0); +} + +/* Convert the SPARC hardware trap type code to a unix signal number. */ + +static int +computeSignal(tt) + int tt; +{ + struct hard_trap_info *ht; + + for (ht = hard_trap_info; ht->tt && ht->signo; ht++) + if (ht->tt == tt) + return ht->signo; + + return SIGHUP; /* default for things we don't know about */ +} + +/* + * While we find nice hex chars, build an int. + * Return number of chars processed. + */ + +static int +hexToInt(char **ptr, int *intValue) +{ + int numChars = 0; + int hexValue; + + *intValue = 0; + + while (**ptr) + { + hexValue = hex(**ptr); + if (hexValue < 0) + break; + + *intValue = (*intValue << 4) | hexValue; + numChars ++; + + (*ptr)++; + } + + return (numChars); +} + +/* + * This function does all command procesing for interfacing to gdb. It + * returns 1 if you should skip the instruction at the trap address, 0 + * otherwise. + */ + +static void +handle_exception (registers) + unsigned long *registers; +{ + int tt; /* Trap type */ + int sigval; + int addr; + int length; + char *ptr; + unsigned long *sp; + unsigned long dsr; + +/* First, we must force all of the windows to be spilled out */ + + asm(" + ! Ugh. sparclet has broken save + !save %sp, -64, %sp + save + add %fp,-64,%sp + !save %sp, -64, %sp + save + add %fp,-64,%sp + !save %sp, -64, %sp + save + add %fp,-64,%sp + !save %sp, -64, %sp + save + add %fp,-64,%sp + !save %sp, -64, %sp + save + add %fp,-64,%sp + !save %sp, -64, %sp + save + add %fp,-64,%sp + !save %sp, -64, %sp + save + add %fp,-64,%sp + !save %sp, -64, %sp + save + add %fp,-64,%sp + restore + restore + restore + restore + restore + restore + restore + restore +"); + + if (registers[PC] == (unsigned long)breakinst) + { + registers[PC] = registers[NPC]; + registers[NPC] += 4; + } + sp = (unsigned long *)registers[SP]; + + tt = (registers[TBR] >> 4) & 0xff; + + /* reply to host that an exception has occurred */ + sigval = computeSignal(tt); + ptr = remcomOutBuffer; + + *ptr++ = 'T'; + *ptr++ = hexchars[sigval >> 4]; + *ptr++ = hexchars[sigval & 0xf]; + + *ptr++ = hexchars[PC >> 4]; + *ptr++ = hexchars[PC & 0xf]; + *ptr++ = ':'; + ptr = mem2hex((char *)®isters[PC], ptr, 4, 0); + *ptr++ = ';'; + + *ptr++ = hexchars[FP >> 4]; + *ptr++ = hexchars[FP & 0xf]; + *ptr++ = ':'; + ptr = mem2hex(sp + 8 + 6, ptr, 4, 0); /* FP */ + *ptr++ = ';'; + + *ptr++ = hexchars[SP >> 4]; + *ptr++ = hexchars[SP & 0xf]; + *ptr++ = ':'; + ptr = mem2hex((char *)&sp, ptr, 4, 0); + *ptr++ = ';'; + + *ptr++ = hexchars[NPC >> 4]; + *ptr++ = hexchars[NPC & 0xf]; + *ptr++ = ':'; + ptr = mem2hex((char *)®isters[NPC], ptr, 4, 0); + *ptr++ = ';'; + + *ptr++ = hexchars[O7 >> 4]; + *ptr++ = hexchars[O7 & 0xf]; + *ptr++ = ':'; + ptr = mem2hex((char *)®isters[O7], ptr, 4, 0); + *ptr++ = ';'; + + *ptr++ = 0; + + putpacket(remcomOutBuffer); + + while (1) + { + remcomOutBuffer[0] = 0; + + getpacket(remcomInBuffer); + switch (remcomInBuffer[0]) + { + case '?': + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[sigval >> 4]; + remcomOutBuffer[2] = hexchars[sigval & 0xf]; + remcomOutBuffer[3] = 0; + break; + + case 'd': + remote_debug = !(remote_debug); /* toggle debug flag */ + break; + + case 'g': /* return the value of the CPU registers */ + { + ptr = remcomOutBuffer; + ptr = mem2hex((char *)registers, ptr, 16 * 4, 0); /* G & O regs */ + ptr = mem2hex(sp + 0, ptr, 16 * 4, 0); /* L & I regs */ + memset(ptr, '0', 32 * 8); /* Floating point */ + ptr = mem2hex((char *)®isters[Y], + ptr + 32 * 4 * 2, + 8 * 4, + 0); /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */ + ptr = mem2hex((char *)®isters[CCSR], + ptr, + 8 * 4, + 0); /* CCSR, CCPR, CCCRCR, CCOR, CCOBR, CCIBR, CCIR */ + ptr = mem2hex((char *)®isters[ASR1], + ptr, + 8 * 4, + 0); /* ASR1,ASR15,ASR17,ASR18,ASR19,ASR20,ASR21,ASR22 */ +#if 0 /* not implemented */ + ptr = mem2hex((char *) ®isters[AWR0], + ptr, + 32 * 4, + 0); /* Alternate Window Registers */ +#endif + } + break; + + case 'G': /* set value of all the CPU registers - return OK */ + case 'P': /* set value of one CPU register - return OK */ + { + unsigned long *newsp, psr; + + psr = registers[PSR]; + + ptr = &remcomInBuffer[1]; + + if (remcomInBuffer[0] == 'P') /* do a single register */ + { + int regno; + + if (hexToInt (&ptr, ®no) + && *ptr++ == '=') + if (regno >= L0 && regno <= I7) + hex2mem (ptr, sp + regno - L0, 4, 0); + else + hex2mem (ptr, (char *)®isters[regno], 4, 0); + else + { + strcpy (remcomOutBuffer, "P01"); + break; + } + } + else + { + hex2mem(ptr, (char *)registers, 16 * 4, 0); /* G & O regs */ + hex2mem(ptr + 16 * 4 * 2, sp + 0, 16 * 4, 0); /* L & I regs */ + hex2mem(ptr + 64 * 4 * 2, (char *)®isters[Y], + 8 * 4, 0); /* Y,PSR,WIM,TBR,PC,NPC,FPSR,CPSR */ + hex2mem(ptr + 72 * 4 * 2, (char *)®isters[CCSR], + 8 * 4, 0); /* CCSR,CCPR,CCCRCR,CCOR,CCOBR,CCIBR,CCIR */ + hex2mem(ptr + 80 * 4 * 2, (char *)®isters[ASR1], + 8 * 4, 0); /* ASR1 ... ASR22 */ +#if 0 /* not implemented */ + hex2mem(ptr + 88 * 4 * 2, (char *)®isters[AWR0], + 8 * 4, 0); /* Alternate Window Registers */ +#endif + } + /* See if the stack pointer has moved. If so, then copy the saved + locals and ins to the new location. This keeps the window + overflow and underflow routines happy. */ + + newsp = (unsigned long *)registers[SP]; + if (sp != newsp) + sp = memcpy(newsp, sp, 16 * 4); + + /* Don't allow CWP to be modified. */ + + if (psr != registers[PSR]) + registers[PSR] = (psr & 0x1f) | (registers[PSR] & ~0x1f); + + strcpy(remcomOutBuffer,"OK"); + } + break; + + case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ + /* Try to read %x,%x. */ + + ptr = &remcomInBuffer[1]; + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length)) + { + if (mem2hex((char *)addr, remcomOutBuffer, length, 1)) + break; + + strcpy (remcomOutBuffer, "E03"); + } + else + strcpy(remcomOutBuffer,"E01"); + break; + + case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ + /* Try to read '%x,%x:'. */ + + ptr = &remcomInBuffer[1]; + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length) + && *ptr++ == ':') + { + if (hex2mem(ptr, (char *)addr, length, 1)) + strcpy(remcomOutBuffer, "OK"); + else + strcpy(remcomOutBuffer, "E03"); + } + else + strcpy(remcomOutBuffer, "E02"); + break; + + case 'c': /* cAA..AA Continue at address AA..AA(optional) */ + /* try to read optional parameter, pc unchanged if no parm */ + + ptr = &remcomInBuffer[1]; + if (hexToInt(&ptr, &addr)) + { + registers[PC] = addr; + registers[NPC] = addr + 4; + } + +/* Need to flush the instruction cache here, as we may have deposited a + breakpoint, and the icache probably has no way of knowing that a data ref to + some location may have changed something that is in the instruction cache. + */ + + flush_i_cache(); + return; + + /* kill the program */ + case 'k' : /* do nothing */ + break; +#if 0 + case 't': /* Test feature */ + asm (" std %f30,[%sp]"); + break; +#endif + case 'r': /* Reset */ + asm ("call 0 + nop "); + break; + +#if 0 +Disabled until we can unscrew this properly + + case 'b': /* bBB... Set baud rate to BB... */ + { + int baudrate; + extern void set_timer_3(); + + ptr = &remcomInBuffer[1]; + if (!hexToInt(&ptr, &baudrate)) + { + strcpy(remcomOutBuffer,"B01"); + break; + } + + /* Convert baud rate to uart clock divider */ + switch (baudrate) + { + case 38400: + baudrate = 16; + break; + case 19200: + baudrate = 33; + break; + case 9600: + baudrate = 65; + break; + default: + strcpy(remcomOutBuffer,"B02"); + goto x1; + } + + putpacket("OK"); /* Ack before changing speed */ + set_timer_3(baudrate); /* Set it */ + } +x1: break; +#endif + } /* switch */ + + /* reply to the request */ + putpacket(remcomOutBuffer); + } +} + +/* This function will generate a breakpoint exception. It is used at the + beginning of a program to sync up with a debugger and can be used + otherwise as a quick means to stop program execution and "break" into + the debugger. */ + +void +breakpoint() +{ + if (!initialized) + return; + + asm(" .globl _breakinst + + _breakinst: ta 1 + "); +} + +static void +hw_breakpoint() +{ + asm(" + ta 127 + "); +} + +#if 0 /* experimental and never finished, left here for reference */ +static void +splet_temp(void) +{ + asm(" sub %sp,(16+1+6+1+121)*4,%sp ! Make room for input & locals + ! + hidden arg + arg spill + ! + doubleword alignment + ! + registers[121] + +! Leave a trail of breadcrumbs! (save register save area for debugging) + mov %sp, %l0 + add %l0, 24*4, %l0 + sethi %hi(_debug_registers), %l1 + st %l0, [%lo(_debug_registers) + %l1] + +! Save the Alternate Register Set: (not implemented yet) +! To save the Alternate Register set, we must: +! 1) Save the current SP in some global location. +! 2) Swap the register sets. +! 3) Save the Alternate SP in the Y register +! 4) Fetch the SP that we saved in step 1. +! 5) Use that to save the rest of the regs (not forgetting ASP in Y) +! 6) Restore the Alternate SP from Y +! 7) Swap the registers back. + +! 1) Copy the current stack pointer to global _SAVED_STACK_POINTER: + sethi %hi(_saved_stack_pointer), %l0 + st %sp, [%lo(_saved_stack_pointer) + %l0] + +! 2) Swap the register sets: + mov %psr, %l1 + sethi %hi(0x10000), %l2 + xor %l1, %l2, %l1 + mov %l1, %psr + nop ! 3 nops after write to %psr (needed?) + nop + nop + +! 3) Save Alternate L0 in Y + wr %l0, 0, %y + +! 4) Load former SP into alternate SP, using L0 + sethi %hi(_saved_stack_pointer), %l0 + or %lo(_saved_stack_pointer), %l0, %l0 + swap [%l0], %sp + +! 4.5) Restore alternate L0 + rd %y, %l0 + +! 5) Save the Alternate Window Registers + st %r0, [%sp + (24 + 88) * 4] ! AWR0 + st %r1, [%sp + (24 + 89) * 4] ! AWR1 + st %r2, [%sp + (24 + 90) * 4] ! AWR2 + st %r3, [%sp + (24 + 91) * 4] ! AWR3 + st %r4, [%sp + (24 + 92) * 4] ! AWR4 + st %r5, [%sp + (24 + 93) * 4] ! AWR5 + st %r6, [%sp + (24 + 94) * 4] ! AWR6 + st %r7, [%sp + (24 + 95) * 4] ! AWR7 + st %r8, [%sp + (24 + 96) * 4] ! AWR8 + st %r9, [%sp + (24 + 97) * 4] ! AWR9 + st %r10, [%sp + (24 + 98) * 4] ! AWR10 + st %r11, [%sp + (24 + 99) * 4] ! AWR11 + st %r12, [%sp + (24 + 100) * 4] ! AWR12 + st %r13, [%sp + (24 + 101) * 4] ! AWR13 +! st %r14, [%sp + (24 + 102) * 4] ! AWR14 (SP) + st %r15, [%sp + (24 + 103) * 4] ! AWR15 + st %r16, [%sp + (24 + 104) * 4] ! AWR16 + st %r17, [%sp + (24 + 105) * 4] ! AWR17 + st %r18, [%sp + (24 + 106) * 4] ! AWR18 + st %r19, [%sp + (24 + 107) * 4] ! AWR19 + st %r20, [%sp + (24 + 108) * 4] ! AWR20 + st %r21, [%sp + (24 + 109) * 4] ! AWR21 + st %r22, [%sp + (24 + 110) * 4] ! AWR22 + st %r23, [%sp + (24 + 111) * 4] ! AWR23 + st %r24, [%sp + (24 + 112) * 4] ! AWR24 + st %r25, [%sp + (24 + 113) * 4] ! AWR25 + st %r26, [%sp + (24 + 114) * 4] ! AWR26 + st %r27, [%sp + (24 + 115) * 4] ! AWR27 + st %r28, [%sp + (24 + 116) * 4] ! AWR28 + st %r29, [%sp + (24 + 117) * 4] ! AWR29 + st %r30, [%sp + (24 + 118) * 4] ! AWR30 + st %r31, [%sp + (24 + 119) * 4] ! AWR21 + +! Get the Alternate PSR (I hope...) + + rd %psr, %l2 + st %l2, [%sp + (24 + 120) * 4] ! APSR + +! Don't forget the alternate stack pointer + + rd %y, %l3 + st %l3, [%sp + (24 + 102) * 4] ! AWR14 (SP) + +! 6) Restore the Alternate SP (saved in Y) + + rd %y, %o6 + + +! 7) Swap the registers back: + + mov %psr, %l1 + sethi %hi(0x10000), %l2 + xor %l1, %l2, %l1 + mov %l1, %psr + nop ! 3 nops after write to %psr (needed?) + nop + nop +"); +} + +#endif diff --git a/contrib/groff/FREEBSD-upgrade b/contrib/groff/FREEBSD-upgrade index e6f8a989234c..419f48738d62 100644 --- a/contrib/groff/FREEBSD-upgrade +++ b/contrib/groff/FREEBSD-upgrade @@ -1,4 +1,4 @@ -$FreeBSD$ +$FreeBSD: src/contrib/groff/FREEBSD-upgrade,v 1.3 2000/01/12 10:35:57 asmodai Exp $ This directory contains virgin copies of the original distribution files on a "vendor" branch. Do not, under any circumstances, attempt to upgrade diff --git a/contrib/groff/groff/groff.man b/contrib/groff/groff/groff.man index 1f95a779f2d7..ec031c8befd2 100644 --- a/contrib/groff/groff/groff.man +++ b/contrib/groff/groff/groff.man @@ -16,7 +16,7 @@ versions, except that this permission notice may be included in translations approved by the Free Software Foundation instead of in the original English. - $FreeBSD$ + $FreeBSD: src/contrib/groff/groff/groff.man,v 1.4 2000/01/12 10:26:07 asmodai Exp $ .. .de TQ diff --git a/contrib/groff/libgroff/font.cc b/contrib/groff/libgroff/font.cc index e27b705e0bac..6e7f8986d63a 100644 --- a/contrib/groff/libgroff/font.cc +++ b/contrib/groff/libgroff/font.cc @@ -18,7 +18,7 @@ You should have received a copy of the GNU General Public License along with groff; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/groff/libgroff/font.cc,v 1.3 2000/01/12 10:26:14 asmodai Exp $ */ #include <stdio.h> #include <string.h> diff --git a/contrib/groff/man/groff_out.man b/contrib/groff/man/groff_out.man index 225d8b249481..c6d4af0f23ed 100644 --- a/contrib/groff/man/groff_out.man +++ b/contrib/groff/man/groff_out.man @@ -17,7 +17,7 @@ versions, except that this permission notice may be included in translations approved by the Free Software Foundation instead of in the original English. - $FreeBSD$ + $FreeBSD: src/contrib/groff/man/groff_out.man,v 1.3 2000/01/12 10:26:19 asmodai Exp $ .. .\" This man page must be preprocessed with eqn. diff --git a/contrib/groff/nroff/nroff.man b/contrib/groff/nroff/nroff.man index d8d981626ddf..c6fa81eb1056 100644 --- a/contrib/groff/nroff/nroff.man +++ b/contrib/groff/nroff/nroff.man @@ -16,7 +16,7 @@ versions, except that this permission notice may be included in translations approved by the Free Software Foundation instead of in the original English. - $FreeBSD$ + $FreeBSD: src/contrib/groff/nroff/nroff.man,v 1.4 2000/01/12 10:26:23 asmodai Exp $ .. .TH @G@NROFF @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@" diff --git a/contrib/groff/nroff/nroff.sh b/contrib/groff/nroff/nroff.sh index 12f3fd16a2c1..42fed66f8a4f 100755 --- a/contrib/groff/nroff/nroff.sh +++ b/contrib/groff/nroff/nroff.sh @@ -1,6 +1,6 @@ #!/bin/sh # Emulate nroff with groff. -# $FreeBSD$ +# $FreeBSD: src/contrib/groff/nroff/nroff.sh,v 1.8 2000/01/12 10:26:23 asmodai Exp $ prog="$0" # Default device. diff --git a/contrib/groff/tmac/doc-common b/contrib/groff/tmac/doc-common index 00a19133f9e3..fb7492f55c00 100644 --- a/contrib/groff/tmac/doc-common +++ b/contrib/groff/tmac/doc-common @@ -30,7 +30,7 @@ .\" SUCH DAMAGE. .\" .\" @(#)doc-common 5.7 (Berkeley) 8/5/91 -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/groff/tmac/doc-common,v 1.21.2.11 2000/10/06 13:26:48 ru Exp $ .\" .\" %beginstrip% .nr %A 1 diff --git a/contrib/groff/tmac/doc-nroff b/contrib/groff/tmac/doc-nroff index faad1cb535cf..c3b1c041109e 100644 --- a/contrib/groff/tmac/doc-nroff +++ b/contrib/groff/tmac/doc-nroff @@ -30,7 +30,7 @@ .\" SUCH DAMAGE. .\" .\" @(#)doc-nroff 5.6 (Berkeley) 8/5/91 -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/groff/tmac/doc-nroff,v 1.1.1.1.8.1 2000/10/06 11:01:06 ru Exp $ .\" .\" tmac.mdoc-nroff .\" %beginstrip% diff --git a/contrib/groff/tmac/doc-syms b/contrib/groff/tmac/doc-syms index a87cf806ad2b..b8902e1b2d33 100644 --- a/contrib/groff/tmac/doc-syms +++ b/contrib/groff/tmac/doc-syms @@ -30,7 +30,7 @@ .\" SUCH DAMAGE. .\" .\" @(#)doc-syms 5.6 (Berkeley) 8/5/91 -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/groff/tmac/doc-syms,v 1.24.2.3 2000/10/30 10:33:29 obrien Exp $ .\" .\" %beginstrip% .\" NS Ux macro - UNIX diff --git a/contrib/groff/tmac/eqnrc b/contrib/groff/tmac/eqnrc index 2156c1504807..59f49909aba7 100644 --- a/contrib/groff/tmac/eqnrc +++ b/contrib/groff/tmac/eqnrc @@ -1,5 +1,5 @@ .\" Startup file for eqn. -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/groff/tmac/eqnrc,v 1.3 2000/01/12 10:26:30 asmodai Exp $ .EQ sdefine << %{ < back 20 < }% sdefine >> %{ > back 20 > }% diff --git a/contrib/groff/tmac/locale/koi8-r b/contrib/groff/tmac/locale/koi8-r index 2d5b36fe35c4..57010752fa70 100644 --- a/contrib/groff/tmac/locale/koi8-r +++ b/contrib/groff/tmac/locale/koi8-r @@ -23,7 +23,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/groff/tmac/locale/koi8-r,v 1.1.4.1 2000/05/04 18:21:53 phantom Exp $ .\" .\" This file is addition to groff package. It allows to translate Section .\" Headers (.Sh) and correctly display man page header and footer. It diff --git a/contrib/groff/tmac/locale/locale-list b/contrib/groff/tmac/locale/locale-list index 5a08168eecb1..1bc349cd38ac 100644 --- a/contrib/groff/tmac/locale/locale-list +++ b/contrib/groff/tmac/locale/locale-list @@ -23,7 +23,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/groff/tmac/locale/locale-list,v 1.1.4.1 2000/05/04 18:21:53 phantom Exp $ .\" .\" Include [tg]roff russian translated if [tg]roff invoked with -Tkoi8-r. .\" diff --git a/contrib/groff/tmac/strip.sed b/contrib/groff/tmac/strip.sed index 400dbf90f5c7..93211764f631 100644 --- a/contrib/groff/tmac/strip.sed +++ b/contrib/groff/tmac/strip.sed @@ -1,4 +1,4 @@ -# $FreeBSD$ +# $FreeBSD: src/contrib/groff/tmac/strip.sed,v 1.2 2000/01/27 17:56:41 joerg Exp $ /%beginstrip%/,$s/[ ]*\\".*// /^\.$/d /%comment%/s/%comment%/.\\"/ diff --git a/contrib/groff/tmac/tmac.doc b/contrib/groff/tmac/tmac.doc index 516584c3fd28..d7e028f0d08c 100644 --- a/contrib/groff/tmac/tmac.doc +++ b/contrib/groff/tmac/tmac.doc @@ -30,7 +30,7 @@ .\" SUCH DAMAGE. .\" .\" @(#)doc 5.8 (Berkeley) 8/5/91 -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/groff/tmac/tmac.doc,v 1.4.2.1 2000/10/06 11:01:06 ru Exp $ .\" Modified by jjc@jclark.com as follows: the doc-* files are assumed to be .\" installed as mdoc/doc-* rather than tmac.doc-* (the filename .\" `tmac.doc-common' would be too long); when using groff, the doc-* files diff --git a/contrib/groff/tmac/tmac.e b/contrib/groff/tmac/tmac.e index 6747af4f25e0..c0e5b84ddfb0 100644 --- a/contrib/groff/tmac/tmac.e +++ b/contrib/groff/tmac/tmac.e @@ -39,7 +39,7 @@ .\" to provide variant functions. .\" --- an internal macro. .\" -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/groff/tmac/tmac.e,v 1.4 2000/01/12 10:26:30 asmodai Exp $ .\" .if !\n(.g .ig .de @R \" --- initialize number register to 0, if undefined diff --git a/contrib/groff/tmac/tmac.tty b/contrib/groff/tmac/tmac.tty index 2d21af2383fc..9f5a795f037f 100644 --- a/contrib/groff/tmac/tmac.tty +++ b/contrib/groff/tmac/tmac.tty @@ -1,4 +1,4 @@ -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/groff/tmac/tmac.tty,v 1.3 2000/01/06 12:48:48 ache Exp $ .nr _C \n(.C .cp 0 .nroff diff --git a/contrib/groff/tmac/troffrc b/contrib/groff/tmac/troffrc index 858d9159e929..2ba71f49b14d 100644 --- a/contrib/groff/tmac/troffrc +++ b/contrib/groff/tmac/troffrc @@ -1,5 +1,5 @@ .\" Startup file for troff. -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/groff/tmac/troffrc,v 1.6 2000/01/12 10:26:31 asmodai Exp $ .\" This is tested by pic. .nr 0p 0 .\" Use .do here, so that it works with -C. diff --git a/contrib/groff/troff/troff.man b/contrib/groff/troff/troff.man index 4b4fdea06f04..f43be651a579 100644 --- a/contrib/groff/troff/troff.man +++ b/contrib/groff/troff/troff.man @@ -16,7 +16,7 @@ versions, except that this permission notice may be included in translations approved by the Free Software Foundation instead of in the original English. - $FreeBSD$ + $FreeBSD: src/contrib/groff/troff/troff.man,v 1.3 2000/01/12 10:26:36 asmodai Exp $ .. .\" define a string tx for the TeX logo diff --git a/contrib/ipfilter/ipmon.c b/contrib/ipfilter/ipmon.c index 2bad2514af42..f62268f6aebc 100644 --- a/contrib/ipfilter/ipmon.c +++ b/contrib/ipfilter/ipmon.c @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/ipfilter/ipmon.c,v 1.5.2.2 2000/07/19 23:00:44 darrenr Exp $ */ /* * Copyright (C) 1993-2000 by Darren Reed. * diff --git a/contrib/ipfilter/ipsend/ipsend.c b/contrib/ipfilter/ipsend/ipsend.c index 05a9b4b33c2c..271e9de42cbf 100644 --- a/contrib/ipfilter/ipsend/ipsend.c +++ b/contrib/ipfilter/ipsend/ipsend.c @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/ipfilter/ipsend/ipsend.c,v 1.4.2.1 2000/07/19 23:00:50 darrenr Exp $ */ /* * ipsend.c (C) 1995-1998 Darren Reed * diff --git a/contrib/ipfilter/ipsend/sbpf.c b/contrib/ipfilter/ipsend/sbpf.c index 2a1ce609d20d..5760cc78bfa6 100644 --- a/contrib/ipfilter/ipsend/sbpf.c +++ b/contrib/ipfilter/ipsend/sbpf.c @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/ipfilter/ipsend/sbpf.c,v 1.3 2000/02/10 03:17:46 peter Exp $ */ /* * (C)opyright 1995-1998 Darren Reed. (from tcplog) * diff --git a/contrib/ipfilter/ipsend/sock.c b/contrib/ipfilter/ipsend/sock.c index a998c90a925b..161bb989601c 100644 --- a/contrib/ipfilter/ipsend/sock.c +++ b/contrib/ipfilter/ipsend/sock.c @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/ipfilter/ipsend/sock.c,v 1.3 2000/02/10 03:17:46 peter Exp $ */ /* * sock.c (C) 1995-1998 Darren Reed * diff --git a/contrib/ipfilter/man/ipf.4 b/contrib/ipfilter/man/ipf.4 index b281c36a0fbc..a6dff62dfa18 100644 --- a/contrib/ipfilter/man/ipf.4 +++ b/contrib/ipfilter/man/ipf.4 @@ -1,4 +1,4 @@ -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/ipfilter/man/ipf.4,v 1.4 2000/02/10 03:17:48 peter Exp $ .TH IPF 4 .SH NAME ipf \- packet filtering kernel interface diff --git a/contrib/ipfilter/man/ipf.5 b/contrib/ipfilter/man/ipf.5 index d9e99923931d..54f5820ced64 100644 --- a/contrib/ipfilter/man/ipf.5 +++ b/contrib/ipfilter/man/ipf.5 @@ -1,4 +1,4 @@ -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/ipfilter/man/ipf.5,v 1.4 2000/02/10 03:17:49 peter Exp $ .TH IPF 5 .SH NAME ipf, ipf.conf \- IP packet filter rule syntax diff --git a/contrib/ipfilter/man/ipf.8 b/contrib/ipfilter/man/ipf.8 index 423ae35e7543..77966a06518c 100644 --- a/contrib/ipfilter/man/ipf.8 +++ b/contrib/ipfilter/man/ipf.8 @@ -1,4 +1,4 @@ -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/ipfilter/man/ipf.8,v 1.3.2.1 2000/07/19 23:00:50 darrenr Exp $ .TH IPF 8 .SH NAME ipf \- alters packet filtering lists for IP packet input and output diff --git a/contrib/ipfilter/man/ipfilter.5 b/contrib/ipfilter/man/ipfilter.5 index ac45146c6452..b29762479e9a 100644 --- a/contrib/ipfilter/man/ipfilter.5 +++ b/contrib/ipfilter/man/ipfilter.5 @@ -1,4 +1,4 @@ -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/ipfilter/man/ipfilter.5,v 1.3 2000/02/10 03:17:50 peter Exp $ .TH IPFILTER 1 .SH NAME IP FIlter diff --git a/contrib/ipfilter/man/ipfstat.8 b/contrib/ipfilter/man/ipfstat.8 index c0c751b16af6..2d9fd18ae10f 100644 --- a/contrib/ipfilter/man/ipfstat.8 +++ b/contrib/ipfilter/man/ipfstat.8 @@ -1,4 +1,4 @@ -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/ipfilter/man/ipfstat.8,v 1.3.2.2 2000/07/19 23:00:50 darrenr Exp $ .TH ipfstat 8 .SH NAME ipfstat \- reports on packet filter statistics and filter list diff --git a/contrib/ipfilter/man/ipmon.8 b/contrib/ipfilter/man/ipmon.8 index dbe9dc6dc0cb..ad1824c19a19 100644 --- a/contrib/ipfilter/man/ipmon.8 +++ b/contrib/ipfilter/man/ipmon.8 @@ -1,4 +1,4 @@ -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/ipfilter/man/ipmon.8,v 1.6.2.1 2000/07/19 23:00:50 darrenr Exp $ .TH ipmon 8 .SH NAME ipmon \- monitors /dev/ipl for logged packets diff --git a/contrib/ipfilter/man/ipnat.4 b/contrib/ipfilter/man/ipnat.4 index cde26396fce8..636d5f1be8d1 100644 --- a/contrib/ipfilter/man/ipnat.4 +++ b/contrib/ipfilter/man/ipnat.4 @@ -1,4 +1,4 @@ -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/ipfilter/man/ipnat.4,v 1.4 2000/02/10 03:17:51 peter Exp $ .TH IPNAT 4 .SH NAME ipnat \- Network Address Translation kernel interface diff --git a/contrib/isc-dhcp/FREEBSD-upgrade b/contrib/isc-dhcp/FREEBSD-upgrade index aa7c51724a3b..99e0cbdfdbb5 100644 --- a/contrib/isc-dhcp/FREEBSD-upgrade +++ b/contrib/isc-dhcp/FREEBSD-upgrade @@ -1,5 +1,5 @@ # ex:ts=8 -$FreeBSD$ +$FreeBSD: src/contrib/isc-dhcp/FREEBSD-upgrade,v 1.9.2.2 2000/07/20 10:26:12 obrien Exp $ ISC DHCP client 2.0 originals can be found at: ftp://ftp.isc.org/isc/dhcp/ diff --git a/contrib/isc-dhcp/TODO b/contrib/isc-dhcp/TODO new file mode 100644 index 000000000000..f1391360b56a --- /dev/null +++ b/contrib/isc-dhcp/TODO @@ -0,0 +1,31 @@ +Things to do, not in any particular order... + +- Dynamic DNS support + +- SNMP support + +- Asynchronous DNS support (look up names while serving other requests) + +- Authentication heirarchies (particularly by MAC address or by DHCP + Authentication Protocol key). + +- Authentication protocol + +- Server-to-server protocol + +- Token ring support for bpf/nit interfaces + +- FDDI support for bpf/nit interfaces (mostly done) + +- Other network hardware support for low-level interfaces? + +- Standard socket API for sending to all-ones broadcast and figuring + out which interface a packet came in on? + +- IPv6/DHCPv6 support + +- Dhcpd running on MacOS Open Transport + +- Dhcpd running on Windows/NT + +- Dhcpd running on Windows95 diff --git a/contrib/isc-dhcp/client/clparse.c b/contrib/isc-dhcp/client/clparse.c index b08e0b60c4ab..01ead3480d90 100644 --- a/contrib/isc-dhcp/client/clparse.c +++ b/contrib/isc-dhcp/client/clparse.c @@ -43,7 +43,7 @@ #ifndef lint static char copyright[] = "$Id: clparse.c,v 1.13.2.5 2000/07/20 05:06:40 mellon Exp $ Copyright (c) 1997 The Internet Software Consortium. All rights reserved.\n" -"$FreeBSD$\n"; +"$FreeBSD: src/contrib/isc-dhcp/client/clparse.c,v 1.4.2.1 2000/07/20 10:26:13 obrien Exp $\n"; #endif /* not lint */ #include "dhcpd.h" diff --git a/contrib/isc-dhcp/client/dhclient-script.8 b/contrib/isc-dhcp/client/dhclient-script.8 index 78a6db695e3e..d70a31610722 100644 --- a/contrib/isc-dhcp/client/dhclient-script.8 +++ b/contrib/isc-dhcp/client/dhclient-script.8 @@ -36,7 +36,7 @@ .\" see ``http://www.isc.org/isc''. To learn more about Vixie .\" Enterprises, see ``http://www.vix.com''. .\" -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/isc-dhcp/client/dhclient-script.8,v 1.5.2.1 2000/06/26 23:07:20 obrien Exp $ .\" .TH dhclient-script 8 .SH NAME diff --git a/contrib/isc-dhcp/client/dhclient.8 b/contrib/isc-dhcp/client/dhclient.8 index 6cdc06350b29..56cce91553ae 100644 --- a/contrib/isc-dhcp/client/dhclient.8 +++ b/contrib/isc-dhcp/client/dhclient.8 @@ -38,7 +38,7 @@ .\" .\" Portions copyright (c) 2000 David E. O'Brien. .\" All rights reserved. -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/isc-dhcp/client/dhclient.8,v 1.8.2.1 2000/07/20 10:26:13 obrien Exp $ .\" .TH dhclient 8 .SH NAME diff --git a/contrib/isc-dhcp/client/dhclient.c b/contrib/isc-dhcp/client/dhclient.c index dd66b50cedcc..c0ba8384382e 100644 --- a/contrib/isc-dhcp/client/dhclient.c +++ b/contrib/isc-dhcp/client/dhclient.c @@ -57,7 +57,7 @@ #ifndef lint static char ocopyright[] = "$Id: dhclient.c,v 1.44.2.45 2000/07/20 05:06:41 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998, 1999 The Internet Software Consortium. All rights reserved.\n" -"$FreeBSD$\n"; +"$FreeBSD: src/contrib/isc-dhcp/client/dhclient.c,v 1.9.2.4 2000/08/09 18:39:14 obrien Exp $\n"; #endif /* not lint */ #include "dhcpd.h" diff --git a/contrib/isc-dhcp/client/scripts/freebsd b/contrib/isc-dhcp/client/scripts/freebsd index 4e79e42355e2..8e1e68c89079 100755 --- a/contrib/isc-dhcp/client/scripts/freebsd +++ b/contrib/isc-dhcp/client/scripts/freebsd @@ -1,6 +1,6 @@ #!/bin/sh -# $FreeBSD$ +# $FreeBSD: src/contrib/isc-dhcp/client/scripts/freebsd,v 1.9.2.3 2000/11/05 09:34:10 obrien Exp $ if [ -x /usr/bin/logger ]; then LOGGER="/usr/bin/logger -s -p user.notice -t dhclient" diff --git a/contrib/isc-dhcp/common/tables.c b/contrib/isc-dhcp/common/tables.c index 6699d41c4622..dfc1f4677c12 100644 --- a/contrib/isc-dhcp/common/tables.c +++ b/contrib/isc-dhcp/common/tables.c @@ -44,7 +44,7 @@ #ifndef lint static char copyright[] = "$Id: tables.c,v 1.13.2.4 1999/04/24 16:46:44 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n" -"$FreeBSD$\n"; +"$FreeBSD: src/contrib/isc-dhcp/common/tables.c,v 1.5.2.1 2000/06/26 23:07:21 obrien Exp $\n"; #endif /* not lint */ #include "dhcpd.h" diff --git a/contrib/isc-dhcp/includes/version.h b/contrib/isc-dhcp/includes/version.h index 6f313903642e..ec4e8f2335d0 100644 --- a/contrib/isc-dhcp/includes/version.h +++ b/contrib/isc-dhcp/includes/version.h @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/isc-dhcp/includes/version.h,v 1.1.1.1.4.4 2000/11/05 09:37:06 obrien Exp $ */ /* Current version of ISC DHCP Distribution. */ diff --git a/contrib/less/command.c b/contrib/less/command.c index 000cf0b1e848..402c5bdc1a6c 100644 --- a/contrib/less/command.c +++ b/contrib/less/command.c @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/less/command.c,v 1.2.2.2 2000/07/19 09:24:25 ps Exp $ */ /* * Copyright (C) 1984-2000 Mark Nudelman * diff --git a/contrib/less/forwback.c b/contrib/less/forwback.c index e110507fa441..8d80ff6e0793 100644 --- a/contrib/less/forwback.c +++ b/contrib/less/forwback.c @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/less/forwback.c,v 1.3.2.2 2000/07/19 09:24:25 ps Exp $ */ /* * Copyright (C) 1984-2000 Mark Nudelman * diff --git a/contrib/less/less.h b/contrib/less/less.h index ca307d38c0c4..d513b211a75d 100644 --- a/contrib/less/less.h +++ b/contrib/less/less.h @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/less/less.h,v 1.2.2.2 2000/07/19 09:24:26 ps Exp $ */ /* * Copyright (C) 1984-2000 Mark Nudelman * diff --git a/contrib/less/main.c b/contrib/less/main.c index 9f5e1c3d491a..982ebd76c51f 100644 --- a/contrib/less/main.c +++ b/contrib/less/main.c @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/less/main.c,v 1.2.2.2 2000/07/19 09:24:26 ps Exp $ */ /* * Copyright (C) 1984-2000 Mark Nudelman * diff --git a/contrib/less/prompt.c b/contrib/less/prompt.c index 14f8ac4253d1..c27f8d75f39a 100644 --- a/contrib/less/prompt.c +++ b/contrib/less/prompt.c @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/less/prompt.c,v 1.2.2.2 2000/07/19 09:24:26 ps Exp $ */ /* * Copyright (C) 1984-2000 Mark Nudelman * diff --git a/contrib/less/screen.c b/contrib/less/screen.c index cd3d885905cb..491cbe3d9c70 100644 --- a/contrib/less/screen.c +++ b/contrib/less/screen.c @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/less/screen.c,v 1.2.2.2 2000/07/19 09:24:26 ps Exp $ */ /* * Copyright (C) 1984-2000 Mark Nudelman * diff --git a/contrib/less/search.c b/contrib/less/search.c index ff00a311d300..de7fced99188 100644 --- a/contrib/less/search.c +++ b/contrib/less/search.c @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/less/search.c,v 1.2.2.2 2000/07/19 09:24:26 ps Exp $ */ /* * Copyright (C) 1984-2000 Mark Nudelman * diff --git a/contrib/less/signal.c b/contrib/less/signal.c index 708f2120562f..ecbf92d91d95 100644 --- a/contrib/less/signal.c +++ b/contrib/less/signal.c @@ -8,7 +8,7 @@ * contact the author, see the README file. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/less/signal.c,v 1.1.1.1.2.2 2000/07/19 09:24:26 ps Exp $ */ /* * Routines dealing with signals. diff --git a/contrib/libf2c/FREEBSD-upgrade b/contrib/libf2c/FREEBSD-upgrade index e919f546f552..448db9023401 100644 --- a/contrib/libf2c/FREEBSD-upgrade +++ b/contrib/libf2c/FREEBSD-upgrade @@ -1,5 +1,5 @@ # ex:ts=8 -$FreeBSD$ +$FreeBSD: src/contrib/libf2c/FREEBSD-upgrade,v 1.4 1999/11/01 07:25:20 obrien Exp $ For the import of lib{f,g}2c, files were pruned by: diff --git a/contrib/libio/FREEBSD-upgrade b/contrib/libio/FREEBSD-upgrade index 16ce67d0adca..807bc7f62e94 100644 --- a/contrib/libio/FREEBSD-upgrade +++ b/contrib/libio/FREEBSD-upgrade @@ -1,5 +1,5 @@ # ex:ts=8 -$FreeBSD$ +$FreeBSD: src/contrib/libio/FREEBSD-upgrade,v 1.2 1999/11/01 07:35:24 obrien Exp $ For the import of libio, files were pruned by: diff --git a/contrib/libpcap/bpf/net/bpf.h b/contrib/libpcap/bpf/net/bpf.h index c6910b6d2015..a5ac4da49584 100644 --- a/contrib/libpcap/bpf/net/bpf.h +++ b/contrib/libpcap/bpf/net/bpf.h @@ -37,7 +37,7 @@ * * @(#)bpf.h 7.1 (Berkeley) 5/7/91 * - * $FreeBSD$ + * $FreeBSD: src/contrib/libpcap/bpf/net/bpf.h,v 1.2 2000/01/30 00:43:38 fenner Exp $ * @(#) $Header: bpf.h,v 1.36 97/06/12 14:29:53 leres Exp $ (LBL) */ diff --git a/contrib/libpcap/gencode.c b/contrib/libpcap/gencode.c index 4a7c107bbca6..d9792326ac55 100644 --- a/contrib/libpcap/gencode.c +++ b/contrib/libpcap/gencode.c @@ -19,7 +19,7 @@ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $FreeBSD$ + * $FreeBSD: src/contrib/libpcap/gencode.c,v 1.8 2000/03/04 23:57:39 fenner Exp $ */ #ifndef lint static const char rcsid[] = diff --git a/contrib/libpcap/gencode.h b/contrib/libpcap/gencode.h index 85fa52623321..190f1ea0e60c 100644 --- a/contrib/libpcap/gencode.h +++ b/contrib/libpcap/gencode.h @@ -18,7 +18,7 @@ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $FreeBSD$ + * $FreeBSD: src/contrib/libpcap/gencode.h,v 1.3 2000/01/30 00:43:34 fenner Exp $ * @(#) $Header: /tcpdump/master/libpcap/gencode.h,v 1.37 1999/10/19 15:18:29 itojun Exp $ (LBL) */ diff --git a/contrib/libpcap/grammar.y b/contrib/libpcap/grammar.y index cc4797f93488..0927185255e3 100644 --- a/contrib/libpcap/grammar.y +++ b/contrib/libpcap/grammar.y @@ -19,7 +19,7 @@ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $FreeBSD$ + * $FreeBSD: src/contrib/libpcap/grammar.y,v 1.5 2000/01/30 00:43:34 fenner Exp $ */ #ifndef lint static const char rcsid[] = diff --git a/contrib/libpcap/nametoaddr.c b/contrib/libpcap/nametoaddr.c index 857aac041da9..20fb219a3e50 100644 --- a/contrib/libpcap/nametoaddr.c +++ b/contrib/libpcap/nametoaddr.c @@ -21,7 +21,7 @@ * Name to id translation routines used by the scanner. * These functions are not time critical. * - * $FreeBSD$ + * $FreeBSD: src/contrib/libpcap/nametoaddr.c,v 1.6.2.1 2000/07/19 16:07:37 archie Exp $ */ #ifndef lint diff --git a/contrib/libpcap/pcap-int.h b/contrib/libpcap/pcap-int.h index fb9f913d0b39..093b35b8cbec 100644 --- a/contrib/libpcap/pcap-int.h +++ b/contrib/libpcap/pcap-int.h @@ -30,7 +30,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD$ + * $FreeBSD: src/contrib/libpcap/pcap-int.h,v 1.3 2000/01/30 00:43:34 fenner Exp $ * @(#) $Header: /tcpdump/master/libpcap/pcap-int.h,v 1.20 1999/11/21 01:10:20 assar Exp $ (LBL) */ diff --git a/contrib/libpcap/pcap-namedb.h b/contrib/libpcap/pcap-namedb.h index 57e759870a33..bda149a079f5 100644 --- a/contrib/libpcap/pcap-namedb.h +++ b/contrib/libpcap/pcap-namedb.h @@ -30,7 +30,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD$ + * $FreeBSD: src/contrib/libpcap/pcap-namedb.h,v 1.3 2000/01/30 00:43:35 fenner Exp $ * @(#) $Header: /tcpdump/master/libpcap/pcap-namedb.h,v 1.6 1999/10/19 15:18:31 itojun Exp $ (LBL) */ diff --git a/contrib/libpcap/pcap.3 b/contrib/libpcap/pcap.3 index 0fb4a82e7b66..953e1922f2fd 100644 --- a/contrib/libpcap/pcap.3 +++ b/contrib/libpcap/pcap.3 @@ -17,7 +17,7 @@ .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/libpcap/pcap.3,v 1.7 2000/01/30 00:43:35 fenner Exp $ .\" $Id$ .\" .TH PCAP 3 "24 June 1998" diff --git a/contrib/libpcap/pcap.h b/contrib/libpcap/pcap.h index c21d38c8c611..31d3a0843af2 100644 --- a/contrib/libpcap/pcap.h +++ b/contrib/libpcap/pcap.h @@ -30,7 +30,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD$ + * $FreeBSD: src/contrib/libpcap/pcap.h,v 1.4 2000/01/30 00:43:35 fenner Exp $ * @(#) $Header: /tcpdump/master/libpcap/pcap.h,v 1.22 1999/12/08 19:54:03 mcr Exp $ (LBL) */ diff --git a/contrib/libpcap/scanner.l b/contrib/libpcap/scanner.l index 54141499e01d..8a904b6040b8 100644 --- a/contrib/libpcap/scanner.l +++ b/contrib/libpcap/scanner.l @@ -19,7 +19,7 @@ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $FreeBSD$ + * $FreeBSD: src/contrib/libpcap/scanner.l,v 1.5 2000/01/30 00:43:35 fenner Exp $ */ #ifndef lint diff --git a/contrib/libreadline/complete.c b/contrib/libreadline/complete.c index b8d1f6ac0756..db0b5944be0f 100644 --- a/contrib/libreadline/complete.c +++ b/contrib/libreadline/complete.c @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/libreadline/complete.c,v 1.6.2.2 2000/07/06 23:04:23 ache Exp $ */ /* complete.c -- filename completion for readline. */ /* Copyright (C) 1987, 1989, 1992 Free Software Foundation, Inc. diff --git a/contrib/libreadline/display.c b/contrib/libreadline/display.c index a0f610579c31..544bb0b8bcda 100644 --- a/contrib/libreadline/display.c +++ b/contrib/libreadline/display.c @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/libreadline/display.c,v 1.3.2.2 2000/07/06 23:04:23 ache Exp $ */ /* display.c -- readline redisplay facility. */ /* Copyright (C) 1987, 1989, 1992 Free Software Foundation, Inc. diff --git a/contrib/libreadline/readline.3 b/contrib/libreadline/readline.3 index dce095f5c180..60f65ff69a9a 100644 --- a/contrib/libreadline/readline.3 +++ b/contrib/libreadline/readline.3 @@ -1,4 +1,4 @@ -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/libreadline/readline.3,v 1.5.2.2 2000/07/06 23:04:24 ache Exp $ .\" .\" MAN PAGE COMMENTS to .\" diff --git a/contrib/libreadline/readline.h b/contrib/libreadline/readline.h index feb54a0d8a01..574f8b5c53f7 100644 --- a/contrib/libreadline/readline.h +++ b/contrib/libreadline/readline.h @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/libreadline/readline.h,v 1.9.2.2 2000/07/06 23:04:24 ache Exp $ */ /* Readline.h -- the names of functions callable from within readline. */ /* Copyright (C) 1987, 1989, 1992 Free Software Foundation, Inc. diff --git a/contrib/libreadline/rlconf.h b/contrib/libreadline/rlconf.h index fcac3cc6cdef..32ddac2f90eb 100644 --- a/contrib/libreadline/rlconf.h +++ b/contrib/libreadline/rlconf.h @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/libreadline/rlconf.h,v 1.3.2.2 2000/07/06 23:04:24 ache Exp $ */ /* rlconf.h -- readline configuration definitions */ /* Copyright (C) 1994 Free Software Foundation, Inc. diff --git a/contrib/libreadline/shell.c b/contrib/libreadline/shell.c index 10571e34d5d1..0576e714e75d 100644 --- a/contrib/libreadline/shell.c +++ b/contrib/libreadline/shell.c @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/libreadline/shell.c,v 1.4.2.2 2000/07/06 23:04:24 ache Exp $ */ /* shell.c -- readline utility functions that are normally provided by bash when readline is linked as part of the shell. */ diff --git a/contrib/libreadline/terminal.c b/contrib/libreadline/terminal.c index 61a28f3e1a95..b240c9c1c4a5 100644 --- a/contrib/libreadline/terminal.c +++ b/contrib/libreadline/terminal.c @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/libreadline/terminal.c,v 1.2.2.2 2000/07/06 23:04:25 ache Exp $ */ /* terminal.c -- controlling the terminal with termcap. */ /* Copyright (C) 1996 Free Software Foundation, Inc. diff --git a/contrib/libreadline/util.c b/contrib/libreadline/util.c index cd6a3b0bd25e..5d472d1269ec 100644 --- a/contrib/libreadline/util.c +++ b/contrib/libreadline/util.c @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/libreadline/util.c,v 1.5.2.2 2000/07/06 23:04:25 ache Exp $ */ /* util.c -- readline utility functions */ /* Copyright (C) 1987, 1989, 1992 Free Software Foundation, Inc. diff --git a/contrib/libstdc++/FREEBSD-upgrade b/contrib/libstdc++/FREEBSD-upgrade index 137023ee0a76..3023cb602a27 100644 --- a/contrib/libstdc++/FREEBSD-upgrade +++ b/contrib/libstdc++/FREEBSD-upgrade @@ -1,5 +1,5 @@ # ex:ts=8 -$FreeBSD$ +$FreeBSD: src/contrib/libstdc++/FREEBSD-upgrade,v 1.1 1999/11/01 07:24:11 obrien Exp $ For the import of libstdc++, files were pruned by: diff --git a/contrib/ncurses/ncurses/tinfo/comp_scan.c b/contrib/ncurses/ncurses/tinfo/comp_scan.c index 5c22da9efac0..95a01ada8661 100644 --- a/contrib/ncurses/ncurses/tinfo/comp_scan.c +++ b/contrib/ncurses/ncurses/tinfo/comp_scan.c @@ -31,7 +31,7 @@ * and: Eric S. Raymond <esr@snark.thyrsus.com> * ****************************************************************************/ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/ncurses/ncurses/tinfo/comp_scan.c,v 1.2.2.2 2000/10/12 18:40:51 peter Exp $ */ /* * comp_scan.c --- Lexical scanner for terminfo compiler. diff --git a/contrib/ncurses/ncurses/tinfo/lib_raw.c b/contrib/ncurses/ncurses/tinfo/lib_raw.c index 26ee26a1243d..ae76fb4b4f3d 100644 --- a/contrib/ncurses/ncurses/tinfo/lib_raw.c +++ b/contrib/ncurses/ncurses/tinfo/lib_raw.c @@ -31,7 +31,7 @@ * and: Eric S. Raymond <esr@snark.thyrsus.com> * ****************************************************************************/ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/ncurses/ncurses/tinfo/lib_raw.c,v 1.2.2.2 2000/10/12 18:40:51 peter Exp $ */ /* * raw.c diff --git a/contrib/ncurses/ncurses/tinfo/lib_termcap.c b/contrib/ncurses/ncurses/tinfo/lib_termcap.c index 30d6b3832b5c..b522b579bda9 100644 --- a/contrib/ncurses/ncurses/tinfo/lib_termcap.c +++ b/contrib/ncurses/ncurses/tinfo/lib_termcap.c @@ -31,7 +31,7 @@ * and: Eric S. Raymond <esr@snark.thyrsus.com> * ****************************************************************************/ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/ncurses/ncurses/tinfo/lib_termcap.c,v 1.5.2.2 2000/10/12 18:40:51 peter Exp $ */ #include <curses.priv.h> diff --git a/contrib/ntp/FREEBSD-upgrade b/contrib/ntp/FREEBSD-upgrade index ea4848392f83..886ee3bd6beb 100644 --- a/contrib/ntp/FREEBSD-upgrade +++ b/contrib/ntp/FREEBSD-upgrade @@ -1,6 +1,6 @@ # ex:ts=8 # -# $FreeBSD$ +# $FreeBSD: src/contrib/ntp/FREEBSD-upgrade,v 1.2 2000/01/28 15:13:45 roberto Exp $ NTP 4.0.99b originals can be found on ftp://louie.udel.edu/pub/ntp/testing diff --git a/contrib/ntp/ntpd/ntp_proto.c b/contrib/ntp/ntpd/ntp_proto.c index fd59a6badd48..38b38e02ab01 100644 --- a/contrib/ntp/ntpd/ntp_proto.c +++ b/contrib/ntp/ntpd/ntp_proto.c @@ -1,7 +1,7 @@ /* * ntp_proto.c - NTP version 4 protocol machinery * - * $FreeBSD$ + * $FreeBSD: src/contrib/ntp/ntpd/ntp_proto.c,v 1.3 2000/01/28 15:02:35 roberto Exp $ */ #ifdef HAVE_CONFIG_H #include <config.h> diff --git a/contrib/nvi/common/exf.c b/contrib/nvi/common/exf.c index 57f5748019b0..013eb85bbb2c 100644 --- a/contrib/nvi/common/exf.c +++ b/contrib/nvi/common/exf.c @@ -15,7 +15,7 @@ static const char sccsid[] = "@(#)exf.c 10.49 (Berkeley) 10/10/96"; #endif static const char rcsid[] = - "$FreeBSD$"; + "$FreeBSD: src/contrib/nvi/common/exf.c,v 1.3 2000/01/10 09:17:46 kris Exp $"; #endif /* not lint */ #include <sys/param.h> diff --git a/contrib/nvi/ex/ex.c b/contrib/nvi/ex/ex.c index ecdae4b86934..714b23a3932a 100644 --- a/contrib/nvi/ex/ex.c +++ b/contrib/nvi/ex/ex.c @@ -7,7 +7,7 @@ * See the LICENSE file for redistribution information. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/nvi/ex/ex.c,v 1.2 1999/09/14 14:34:58 ru Exp $ */ #include "config.h" diff --git a/contrib/opie/ftpcmd.y b/contrib/opie/ftpcmd.y index ea5bb365b581..78292d77202c 100644 --- a/contrib/opie/ftpcmd.y +++ b/contrib/opie/ftpcmd.y @@ -22,7 +22,7 @@ you didn't get a copy, you may request one from <license@inner.net>. Modified at NRL for OPIE 2.0. Originally from BSD. -$FreeBSD$ +$FreeBSD: src/contrib/opie/ftpcmd.y,v 1.2.6.1 2000/06/09 07:14:56 kris Exp $ */ /* * Copyright (c) 1985, 1988 Regents of the University of California. diff --git a/contrib/opie/libopie/generator.c b/contrib/opie/libopie/generator.c index eef7cac67f92..4e88e7ef0702 100644 --- a/contrib/opie/libopie/generator.c +++ b/contrib/opie/libopie/generator.c @@ -26,7 +26,7 @@ you didn't get a copy, you may request one from <license@inner.net>. Bug fixes. Created at NRL for OPIE 2.2. -$FreeBSD$ +$FreeBSD: src/contrib/opie/libopie/generator.c,v 1.3.6.1 2000/06/09 07:15:01 kris Exp $ */ #include "opie_cfg.h" diff --git a/contrib/opie/libopie/hash.c b/contrib/opie/libopie/hash.c index eab170618bbd..84df2c2a82f0 100644 --- a/contrib/opie/libopie/hash.c +++ b/contrib/opie/libopie/hash.c @@ -13,7 +13,7 @@ you didn't get a copy, you may request one from <license@inner.net>. they killed thread-safety. Created by cmetz for OPIE 2.3 using the old hash.c as a guide. -$FreeBSD$ +$FreeBSD: src/contrib/opie/libopie/hash.c,v 1.3.6.1 2000/06/09 07:15:01 kris Exp $ */ #include "opie_cfg.h" diff --git a/contrib/opie/libopie/hashlen.c b/contrib/opie/libopie/hashlen.c index 74f1fc63b53a..7d6219b2a057 100644 --- a/contrib/opie/libopie/hashlen.c +++ b/contrib/opie/libopie/hashlen.c @@ -10,7 +10,7 @@ you didn't get a copy, you may request one from <license@inner.net>. Created by cmetz for OPIE 2.3. -$FreeBSD$ +$FreeBSD: src/contrib/opie/libopie/hashlen.c,v 1.3.6.1 2000/06/09 07:15:01 kris Exp $ */ #include "opie_cfg.h" diff --git a/contrib/opie/libopie/lock.c b/contrib/opie/libopie/lock.c index 0f666a6480b2..72e2467a7a0c 100644 --- a/contrib/opie/libopie/lock.c +++ b/contrib/opie/libopie/lock.c @@ -28,7 +28,7 @@ License Agreement applies to this software. Avoid NULL. Created at NRL for OPIE 2.2 from opiesubr2.c -$FreeBSD$ +$FreeBSD: src/contrib/opie/libopie/lock.c,v 1.1.1.2.6.1 2000/06/09 07:15:01 kris Exp $ */ #include "opie_cfg.h" #if HAVE_STRING_H diff --git a/contrib/opie/libopie/newseed.c b/contrib/opie/libopie/newseed.c index 23cdce4a936d..9afadf0f0134 100644 --- a/contrib/opie/libopie/newseed.c +++ b/contrib/opie/libopie/newseed.c @@ -12,7 +12,7 @@ you didn't get a copy, you may request one from <license@inner.net>. Modified by cmetz for OPIE 2.31. Added time.h. Created by cmetz for OPIE 2.22. -$FreeBSD$ +$FreeBSD: src/contrib/opie/libopie/newseed.c,v 1.2.6.1 2000/06/09 07:15:01 kris Exp $ */ #include "opie_cfg.h" diff --git a/contrib/opie/opie.4 b/contrib/opie/opie.4 index 58fa0d39ae6f..1fa9bbbeb21a 100644 --- a/contrib/opie/opie.4 +++ b/contrib/opie/opie.4 @@ -20,7 +20,7 @@ .\" Definition of "seed" written by Neil Haller of Bellcore .\" Written at NRL for OPIE 2.0. .\" -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/opie/opie.4,v 1.6.2.1 2000/06/09 07:14:56 kris Exp $ .\" .TH OPIE 4 "January 10, 1995" .SH NAME diff --git a/contrib/opie/opie.h b/contrib/opie/opie.h index 7ac9b2155755..f77d6f06e28f 100644 --- a/contrib/opie/opie.h +++ b/contrib/opie/opie.h @@ -34,7 +34,7 @@ License Agreement applies to this software. Written at Bellcore for the S/Key Version 1 software distribution (skey.h). -$FreeBSD$ +$FreeBSD: src/contrib/opie/opie.h,v 1.4.6.1 2000/06/09 07:14:56 kris Exp $ */ #ifndef _OPIE_H #define _OPIE_H 1 diff --git a/contrib/opie/opie_cfg.h b/contrib/opie/opie_cfg.h index be29b59380db..ffa75c68c21c 100644 --- a/contrib/opie/opie_cfg.h +++ b/contrib/opie/opie_cfg.h @@ -47,7 +47,7 @@ License Agreement applies to this software. Gutted for autoconf. Split up for autoconf. Written at NRL for OPIE 2.0. -$FreeBSD$ +$FreeBSD: src/contrib/opie/opie_cfg.h,v 1.3.6.1 2000/06/09 07:14:56 kris Exp $ */ #ifndef _OPIE_CFG_H diff --git a/contrib/opie/opieftpd.c b/contrib/opie/opieftpd.c index 1bace12bd03b..761f5f6c07a8 100644 --- a/contrib/opie/opieftpd.c +++ b/contrib/opie/opieftpd.c @@ -46,7 +46,7 @@ License Agreement applies to this software. There is some really, really ugly code in here. -$FreeBSD$ +$FreeBSD: src/contrib/opie/opieftpd.c,v 1.2.6.2 2000/07/20 10:35:06 kris Exp $ */ /* * Copyright (c) 1985, 1988, 1990 Regents of the University of California. diff --git a/contrib/opie/opieinfo.1 b/contrib/opie/opieinfo.1 index 0b094554d9c9..40ab2b71fff5 100644 --- a/contrib/opie/opieinfo.1 +++ b/contrib/opie/opieinfo.1 @@ -19,7 +19,7 @@ .\" Written at Bellcore for the S/Key Version 1 software distribution .\" (keyinfo.1). .\" -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/opie/opieinfo.1,v 1.3.6.1 2000/06/09 07:14:56 kris Exp $ .ll 6i .pl 10.5i .lt 6.0i diff --git a/contrib/opie/opiekey.1 b/contrib/opie/opiekey.1 index 92a73ea61f93..8ad19d4e0e7e 100644 --- a/contrib/opie/opiekey.1 +++ b/contrib/opie/opiekey.1 @@ -25,7 +25,7 @@ .\" Written at Bellcore for the S/Key Version 1 software distribution .\" (key.1). .\" -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/opie/opiekey.1,v 1.3.6.1 2000/06/09 07:14:56 kris Exp $ .ll 6i .pl 10.5i .lt 6.0i diff --git a/contrib/opie/opiekeys.5 b/contrib/opie/opiekeys.5 index 26f08ed06ff8..7c1ce6c60c78 100644 --- a/contrib/opie/opiekeys.5 +++ b/contrib/opie/opiekeys.5 @@ -14,7 +14,7 @@ .ll 6i .pl 10.5i .\" @(#)opiekeys.5 2.0 (NRL) 1/10/95 -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/opie/opiekeys.5,v 1.3.6.1 2000/06/09 07:14:56 kris Exp $ .\" .lt 6.0i .TH OPIEKEYS 5 "January 10, 1995" diff --git a/contrib/opie/opiepasswd.1 b/contrib/opie/opiepasswd.1 index d3fc5e32e2c4..452dbb1e75eb 100644 --- a/contrib/opie/opiepasswd.1 +++ b/contrib/opie/opiepasswd.1 @@ -21,7 +21,7 @@ .\" Written at Bellcore for the S/Key Version 1 software distribution .\" (keyinit.1). .\" -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/opie/opiepasswd.1,v 1.3.6.1 2000/06/09 07:14:57 kris Exp $ .ll 6i .pl 10.5i .lt 6.0i diff --git a/contrib/perl5/ext/POSIX/POSIX.xs b/contrib/perl5/ext/POSIX/POSIX.xs index 2066b4697236..d90be4c45609 100644 --- a/contrib/perl5/ext/POSIX/POSIX.xs +++ b/contrib/perl5/ext/POSIX/POSIX.xs @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/perl5/ext/POSIX/POSIX.xs,v 1.2.2.1 2000/05/11 23:51:52 ache Exp $ */ #ifdef WIN32 #define _POSIX_ #endif diff --git a/contrib/perl5/lib/ExtUtils/Install.pm b/contrib/perl5/lib/ExtUtils/Install.pm index bd49b9f6a21d..e39ed9adaeb5 100644 --- a/contrib/perl5/lib/ExtUtils/Install.pm +++ b/contrib/perl5/lib/ExtUtils/Install.pm @@ -2,7 +2,7 @@ package ExtUtils::Install; $VERSION = substr q$Revision: 1.28 $, 10; # $Date: 1998/01/25 07:08:24 $ -# $FreeBSD$ +# $FreeBSD: src/contrib/perl5/lib/ExtUtils/Install.pm,v 1.2 2000/01/29 17:27:17 markm Exp $ use Exporter; use Carp (); diff --git a/contrib/perl5/lib/ExtUtils/MakeMaker.pm b/contrib/perl5/lib/ExtUtils/MakeMaker.pm index a614830538ce..46b35e78e6f1 100644 --- a/contrib/perl5/lib/ExtUtils/MakeMaker.pm +++ b/contrib/perl5/lib/ExtUtils/MakeMaker.pm @@ -1,4 +1,4 @@ -# $FreeBSD$ +# $FreeBSD: src/contrib/perl5/lib/ExtUtils/MakeMaker.pm,v 1.2 1999/12/04 12:35:56 marcel Exp $ BEGIN {require 5.002;} # MakeMaker 5.17 was the last MakeMaker that was compatible with perl5.001m diff --git a/contrib/perl5/pp.c b/contrib/perl5/pp.c index 1d4dbcc83d34..89273a4974c8 100644 --- a/contrib/perl5/pp.c +++ b/contrib/perl5/pp.c @@ -5,7 +5,7 @@ * You may distribute under the terms of either the GNU General Public * License or the Artistic License, as specified in the README file. * - * $FreeBSD$ + * $FreeBSD: src/contrib/perl5/pp.c,v 1.2 1999/12/13 19:11:53 ache Exp $ */ /* diff --git a/contrib/pnpinfo/pnpinfo.8 b/contrib/pnpinfo/pnpinfo.8 index 0677640d399e..5b25da19444b 100644 --- a/contrib/pnpinfo/pnpinfo.8 +++ b/contrib/pnpinfo/pnpinfo.8 @@ -28,7 +28,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/pnpinfo/pnpinfo.8,v 1.2 1999/09/05 17:27:05 peter Exp $ .\" .Dd January 7, 1996 .Dt PNPINFO 8 diff --git a/contrib/pnpinfo/pnpinfo.c b/contrib/pnpinfo/pnpinfo.c index 1646ef239de8..958956a7b4bd 100644 --- a/contrib/pnpinfo/pnpinfo.c +++ b/contrib/pnpinfo/pnpinfo.c @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD$ + * $FreeBSD: src/contrib/pnpinfo/pnpinfo.c,v 1.7 1999/09/05 17:27:05 peter Exp $ */ #include <sys/time.h> diff --git a/contrib/pnpinfo/pnpinfo.h b/contrib/pnpinfo/pnpinfo.h index abe14271d9d9..6cb8dc3939ba 100644 --- a/contrib/pnpinfo/pnpinfo.h +++ b/contrib/pnpinfo/pnpinfo.h @@ -29,7 +29,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD$ + * $FreeBSD: src/contrib/pnpinfo/pnpinfo.h,v 1.2 1999/09/05 17:27:05 peter Exp $ */ diff --git a/contrib/sendmail/FREEBSD-upgrade b/contrib/sendmail/FREEBSD-upgrade index 72bf408975a6..be2d8737d365 100644 --- a/contrib/sendmail/FREEBSD-upgrade +++ b/contrib/sendmail/FREEBSD-upgrade @@ -1,4 +1,4 @@ -$FreeBSD$ +$FreeBSD: src/contrib/sendmail/FREEBSD-upgrade,v 1.1.2.2 2000/10/10 05:07:15 gshapiro Exp $ sendmail 8.11.1 originals can be found at: ftp://ftp.sendmail.org/pub/sendmail/ diff --git a/contrib/sendmail/cf/m4/cfhead.m4 b/contrib/sendmail/cf/m4/cfhead.m4 index 940a072aa7de..2d75e8c8774f 100644 --- a/contrib/sendmail/cf/m4/cfhead.m4 +++ b/contrib/sendmail/cf/m4/cfhead.m4 @@ -9,7 +9,7 @@ # forth in the LICENSE file which can be found at the top level of # the sendmail distribution. # -# $FreeBSD$ +# $FreeBSD: src/contrib/sendmail/cf/m4/cfhead.m4,v 1.3.6.2 2000/10/10 05:07:17 gshapiro Exp $ # ###################################################################### diff --git a/contrib/sendmail/cf/ostype/bsd4.4.m4 b/contrib/sendmail/cf/ostype/bsd4.4.m4 index 65309b98195b..60205800e5c5 100644 --- a/contrib/sendmail/cf/ostype/bsd4.4.m4 +++ b/contrib/sendmail/cf/ostype/bsd4.4.m4 @@ -10,7 +10,7 @@ divert(-1) # forth in the LICENSE file which can be found at the top level of # the sendmail distribution. # -# $FreeBSD$ +# $FreeBSD: src/contrib/sendmail/cf/ostype/bsd4.4.m4,v 1.3.6.1 2000/08/27 17:31:10 gshapiro Exp $ # divert(0) diff --git a/contrib/sendmail/cf/sh/makeinfo.sh b/contrib/sendmail/cf/sh/makeinfo.sh index 8fb371c2c66e..7e171641715a 100644 --- a/contrib/sendmail/cf/sh/makeinfo.sh +++ b/contrib/sendmail/cf/sh/makeinfo.sh @@ -13,7 +13,7 @@ # # $Id: makeinfo.sh,v 8.14 1999/02/07 07:26:25 gshapiro Exp $ # -# $FreeBSD$ +# $FreeBSD: src/contrib/sendmail/cf/sh/makeinfo.sh,v 1.3.6.1 2000/08/27 17:31:13 gshapiro Exp $ # usewhoami=0 diff --git a/contrib/sendmail/contrib/bitdomain.c b/contrib/sendmail/contrib/bitdomain.c index b864d65fe164..9435d0be2ef0 100644 --- a/contrib/sendmail/contrib/bitdomain.c +++ b/contrib/sendmail/contrib/bitdomain.c @@ -18,7 +18,7 @@ * The bitdomain table should be rebuilt monthly. */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/sendmail/contrib/bitdomain.c,v 1.2.6.1 2000/08/27 17:31:13 gshapiro Exp $ */ #include <stdio.h> #include <errno.h> diff --git a/contrib/sendmail/mail.local/mail.local.8 b/contrib/sendmail/mail.local/mail.local.8 index 2c2123934c10..01c0f63b4d0c 100644 --- a/contrib/sendmail/mail.local/mail.local.8 +++ b/contrib/sendmail/mail.local/mail.local.8 @@ -10,7 +10,7 @@ .\" .\" $Id: mail.local.8,v 8.14.14.3 2000/09/17 17:04:25 gshapiro Exp $ .\" -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/sendmail/mail.local/mail.local.8,v 1.5.6.2 2000/10/10 05:07:22 gshapiro Exp $ .\" .TH MAIL.LOCAL 8 "$Date: 2000/09/17 17:04:25 $" .SH NAME diff --git a/contrib/sendmail/mail.local/mail.local.c b/contrib/sendmail/mail.local/mail.local.c index 56e1aa2cd7fc..708c7f2b44de 100644 --- a/contrib/sendmail/mail.local/mail.local.c +++ b/contrib/sendmail/mail.local/mail.local.c @@ -22,7 +22,7 @@ static char copyright[] = static char id[] = "@(#)$Id: mail.local.c,v 8.143.4.37 2000/09/22 00:49:10 doug Exp $"; #endif /* ! lint */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/sendmail/mail.local/mail.local.c,v 1.6.6.3 2000/10/10 22:28:25 gshapiro Exp $ */ /* ** This is not intended to work on System V derived systems diff --git a/contrib/sendmail/mailstats/mailstats.c b/contrib/sendmail/mailstats/mailstats.c index cb6f190f628b..5cdb3ae78a98 100644 --- a/contrib/sendmail/mailstats/mailstats.c +++ b/contrib/sendmail/mailstats/mailstats.c @@ -24,7 +24,7 @@ static char copyright[] = static char id[] = "@(#)$Id: mailstats.c,v 8.53.16.11 2000/09/17 17:04:26 gshapiro Exp $"; #endif /* ! lint */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/sendmail/mailstats/mailstats.c,v 1.5.2.2 2000/10/10 05:07:23 gshapiro Exp $ */ #include <unistd.h> #include <stddef.h> diff --git a/contrib/sendmail/makemap/makemap.c b/contrib/sendmail/makemap/makemap.c index 0506dc4745a3..ba9a1194eeae 100644 --- a/contrib/sendmail/makemap/makemap.c +++ b/contrib/sendmail/makemap/makemap.c @@ -24,7 +24,7 @@ static char copyright[] = static char id[] = "@(#)$Id: makemap.c,v 8.135.4.11 2000/09/13 01:11:10 gshapiro Exp $"; #endif /* ! lint */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/sendmail/makemap/makemap.c,v 1.3.6.2 2000/10/10 05:07:23 gshapiro Exp $ */ #include <sys/types.h> #ifndef ISC_UNIX diff --git a/contrib/sendmail/praliases/praliases.c b/contrib/sendmail/praliases/praliases.c index 59a3c00f6380..83300ea19a5a 100644 --- a/contrib/sendmail/praliases/praliases.c +++ b/contrib/sendmail/praliases/praliases.c @@ -24,7 +24,7 @@ static char copyright[] = static char id[] = "@(#)$Id: praliases.c,v 8.59.4.10 2000/07/18 05:41:39 gshapiro Exp $"; #endif /* ! lint */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/sendmail/praliases/praliases.c,v 1.3.6.1 2000/08/27 17:31:22 gshapiro Exp $ */ #include <sys/types.h> #include <ctype.h> diff --git a/contrib/sendmail/rmail/rmail.c b/contrib/sendmail/rmail/rmail.c index 79f8b921122c..7c3ca551fe60 100644 --- a/contrib/sendmail/rmail/rmail.c +++ b/contrib/sendmail/rmail/rmail.c @@ -22,7 +22,7 @@ static char copyright[] = static char id[] = "@(#)$Id: rmail.c,v 8.39.4.8 2000/09/16 22:20:25 gshapiro Exp $"; #endif /* ! lint */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/sendmail/rmail/rmail.c,v 1.4.6.2 2000/10/10 05:07:23 gshapiro Exp $ */ /* * RMAIL -- UUCP mail server. diff --git a/contrib/sendmail/smrsh/smrsh.8 b/contrib/sendmail/smrsh/smrsh.8 index 6d58ce0c2602..e1cf8a63471b 100644 --- a/contrib/sendmail/smrsh/smrsh.8 +++ b/contrib/sendmail/smrsh/smrsh.8 @@ -11,7 +11,7 @@ .\" .\" $Id: smrsh.8,v 8.11 1999/06/09 16:51:07 ca Exp $ .\" -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/sendmail/smrsh/smrsh.8,v 1.3.6.1 2000/08/27 17:31:24 gshapiro Exp $ .\" .TH SMRSH 8 11/02/93 .SH NAME diff --git a/contrib/sendmail/smrsh/smrsh.c b/contrib/sendmail/smrsh/smrsh.c index f32e34003fed..a720fcb69b2f 100644 --- a/contrib/sendmail/smrsh/smrsh.c +++ b/contrib/sendmail/smrsh/smrsh.c @@ -24,7 +24,7 @@ static char copyright[] = static char id[] = "@(#)$Id: smrsh.c,v 8.31.4.5 2000/09/17 17:04:27 gshapiro Exp $"; #endif /* ! lint */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/sendmail/smrsh/smrsh.c,v 1.3.6.2 2000/10/10 05:07:24 gshapiro Exp $ */ /* ** SMRSH -- sendmail restricted shell diff --git a/contrib/sendmail/src/aliases.5 b/contrib/sendmail/src/aliases.5 index 7b274e7de34f..fd4b14c7ceba 100644 --- a/contrib/sendmail/src/aliases.5 +++ b/contrib/sendmail/src/aliases.5 @@ -11,7 +11,7 @@ .\" .\" $Id: aliases.5,v 8.15.4.1 2000/07/18 07:23:02 gshapiro Exp $ .\" -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/sendmail/src/aliases.5,v 1.3.6.1 2000/08/27 17:31:25 gshapiro Exp $ .\" .TH ALIASES 5 "$Date: 2000/07/18 07:23:02 $" .SH NAME diff --git a/contrib/sendmail/src/conf.c b/contrib/sendmail/src/conf.c index 6130beea4f21..f4fed650dcd8 100644 --- a/contrib/sendmail/src/conf.c +++ b/contrib/sendmail/src/conf.c @@ -15,7 +15,7 @@ static char id[] = "@(#)$Id: conf.c,v 8.646.2.2.2.32 2000/09/23 00:31:33 ca Exp $"; #endif /* ! lint */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/sendmail/src/conf.c,v 1.5.2.2 2000/10/10 05:07:24 gshapiro Exp $ */ #include <sendmail.h> #include <sendmail/pathnames.h> diff --git a/contrib/sendmail/src/conf.h b/contrib/sendmail/src/conf.h index 1ef142740419..657f03cc8b89 100644 --- a/contrib/sendmail/src/conf.h +++ b/contrib/sendmail/src/conf.h @@ -13,7 +13,7 @@ * $Id: conf.h,v 8.496.4.25 2000/08/08 23:50:40 ca Exp $ */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/sendmail/src/conf.h,v 1.6.2.2 2000/10/10 05:07:25 gshapiro Exp $ */ /* ** CONF.H -- All user-configurable parameters for sendmail diff --git a/contrib/sendmail/src/err.c b/contrib/sendmail/src/err.c index 8b65cf8bfdad..b47db3dbfbbc 100644 --- a/contrib/sendmail/src/err.c +++ b/contrib/sendmail/src/err.c @@ -15,7 +15,7 @@ static char id[] = "@(#)$Id: err.c,v 8.120.4.1 2000/05/25 18:56:15 gshapiro Exp $"; #endif /* ! lint */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/sendmail/src/err.c,v 1.3.6.1 2000/08/27 17:31:27 gshapiro Exp $ */ #include <sendmail.h> #ifdef LDAPMAP diff --git a/contrib/sendmail/src/headers.c b/contrib/sendmail/src/headers.c index 03472ac0a538..860852d10826 100644 --- a/contrib/sendmail/src/headers.c +++ b/contrib/sendmail/src/headers.c @@ -15,7 +15,7 @@ static char id[] = "@(#)$Id: headers.c,v 8.203.4.7 2000/08/22 21:50:36 gshapiro Exp $"; #endif /* ! lint */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/sendmail/src/headers.c,v 1.4.2.2 2000/10/10 05:07:26 gshapiro Exp $ */ #include <sendmail.h> diff --git a/contrib/sendmail/src/mailq.1 b/contrib/sendmail/src/mailq.1 index d3e83c51e8d3..b0a2a0f901c5 100644 --- a/contrib/sendmail/src/mailq.1 +++ b/contrib/sendmail/src/mailq.1 @@ -11,7 +11,7 @@ .\" .\" $Id: mailq.1,v 8.14.28.2 2000/09/17 17:04:27 gshapiro Exp $ .\" -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/sendmail/src/mailq.1,v 1.3.6.2 2000/10/10 05:07:26 gshapiro Exp $ .\" .TH MAILQ 1 "$Date: 2000/09/17 17:04:27 $" .SH NAME diff --git a/contrib/sendmail/src/mci.c b/contrib/sendmail/src/mci.c index 171c71ba3563..1b85a1387c6d 100644 --- a/contrib/sendmail/src/mci.c +++ b/contrib/sendmail/src/mci.c @@ -15,7 +15,7 @@ static char id[] = "@(#)$Id: mci.c,v 8.133.10.3 2000/06/23 16:17:06 ca Exp $"; #endif /* ! lint */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/sendmail/src/mci.c,v 1.3.6.1 2000/08/27 17:31:27 gshapiro Exp $ */ #include <sendmail.h> diff --git a/contrib/sendmail/src/savemail.c b/contrib/sendmail/src/savemail.c index a5173ebee95c..7f61a1cdd987 100644 --- a/contrib/sendmail/src/savemail.c +++ b/contrib/sendmail/src/savemail.c @@ -15,7 +15,7 @@ static char id[] = "@(#)$Id: savemail.c,v 8.212.4.5 2000/08/22 22:46:00 gshapiro Exp $"; #endif /* ! lint */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/sendmail/src/savemail.c,v 1.4.2.2 2000/10/10 05:07:28 gshapiro Exp $ */ #include <sendmail.h> diff --git a/contrib/sendmail/src/sendmail.8 b/contrib/sendmail/src/sendmail.8 index 2cf660077ab0..8fff8d6407b1 100644 --- a/contrib/sendmail/src/sendmail.8 +++ b/contrib/sendmail/src/sendmail.8 @@ -11,7 +11,7 @@ .\" .\" $Id: sendmail.8,v 8.36.8.2 2000/09/07 21:14:00 ca Exp $ .\" -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/sendmail/src/sendmail.8,v 1.3.6.2 2000/10/10 05:07:28 gshapiro Exp $ .\" .TH SENDMAIL 8 "$Date: 2000/09/07 21:14:00 $" .SH NAME diff --git a/contrib/tcp_wrappers/Makefile b/contrib/tcp_wrappers/Makefile index 10ede819e4de..bc3cddbcd7d7 100644 --- a/contrib/tcp_wrappers/Makefile +++ b/contrib/tcp_wrappers/Makefile @@ -1,5 +1,5 @@ # @(#) Makefile 1.23 97/03/21 19:27:20 -# $FreeBSD$ +# $FreeBSD: src/contrib/tcp_wrappers/Makefile,v 1.2 2000/02/03 10:26:57 shin Exp $ what: @echo diff --git a/contrib/tcp_wrappers/fix_options.c b/contrib/tcp_wrappers/fix_options.c index 4983a55a47a1..0a5ce2cbeaaf 100644 --- a/contrib/tcp_wrappers/fix_options.c +++ b/contrib/tcp_wrappers/fix_options.c @@ -4,7 +4,7 @@ * * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcp_wrappers/fix_options.c,v 1.2 2000/02/03 10:26:57 shin Exp $ */ #ifndef lint diff --git a/contrib/tcp_wrappers/hosts_access.5 b/contrib/tcp_wrappers/hosts_access.5 index 27ab2ae648b6..7c67a4e5208b 100644 --- a/contrib/tcp_wrappers/hosts_access.5 +++ b/contrib/tcp_wrappers/hosts_access.5 @@ -396,4 +396,4 @@ Eindhoven University of Technology Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands \" @(#) hosts_access.5 1.20 95/01/30 19:51:46 -\" $FreeBSD$ +\" $FreeBSD: src/contrib/tcp_wrappers/hosts_access.5,v 1.3 2000/02/03 10:26:57 shin Exp $ diff --git a/contrib/tcp_wrappers/hosts_access.c b/contrib/tcp_wrappers/hosts_access.c index 24f5753fa3bc..194286302414 100644 --- a/contrib/tcp_wrappers/hosts_access.c +++ b/contrib/tcp_wrappers/hosts_access.c @@ -16,7 +16,7 @@ * * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcp_wrappers/hosts_access.c,v 1.3.2.1 2000/07/18 08:34:54 ume Exp $ */ #ifndef lint diff --git a/contrib/tcp_wrappers/misc.c b/contrib/tcp_wrappers/misc.c index 8f04f870e1b4..4f80679166cb 100644 --- a/contrib/tcp_wrappers/misc.c +++ b/contrib/tcp_wrappers/misc.c @@ -3,7 +3,7 @@ * * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcp_wrappers/misc.c,v 1.2 2000/02/03 10:26:58 shin Exp $ */ #ifndef lint diff --git a/contrib/tcp_wrappers/refuse.c b/contrib/tcp_wrappers/refuse.c index fd04e08ae9bf..af5ee6d7c8b5 100644 --- a/contrib/tcp_wrappers/refuse.c +++ b/contrib/tcp_wrappers/refuse.c @@ -6,7 +6,7 @@ * * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcp_wrappers/refuse.c,v 1.2 2000/02/03 10:26:58 shin Exp $ */ #ifndef lint diff --git a/contrib/tcp_wrappers/rfc931.c b/contrib/tcp_wrappers/rfc931.c index e7fb3d1d1294..0fc8848e0464 100644 --- a/contrib/tcp_wrappers/rfc931.c +++ b/contrib/tcp_wrappers/rfc931.c @@ -8,7 +8,7 @@ * * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcp_wrappers/rfc931.c,v 1.2.2.1 2000/07/18 16:41:11 dwmalone Exp $ */ #ifndef lint diff --git a/contrib/tcp_wrappers/scaffold.c b/contrib/tcp_wrappers/scaffold.c index 8da9df0c6861..aa95efb338ba 100644 --- a/contrib/tcp_wrappers/scaffold.c +++ b/contrib/tcp_wrappers/scaffold.c @@ -3,7 +3,7 @@ * * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcp_wrappers/scaffold.c,v 1.2.2.1 2000/07/18 08:34:55 ume Exp $ */ #ifndef lint diff --git a/contrib/tcp_wrappers/scaffold.h b/contrib/tcp_wrappers/scaffold.h index ea568b722507..d376de5d3759 100644 --- a/contrib/tcp_wrappers/scaffold.h +++ b/contrib/tcp_wrappers/scaffold.h @@ -3,7 +3,7 @@ * * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcp_wrappers/scaffold.h,v 1.1.1.1.4.1 2000/07/18 08:34:55 ume Exp $ */ #ifdef INET6 diff --git a/contrib/tcp_wrappers/socket.c b/contrib/tcp_wrappers/socket.c index 47f3df6a02a7..159873c7faf9 100644 --- a/contrib/tcp_wrappers/socket.c +++ b/contrib/tcp_wrappers/socket.c @@ -14,7 +14,7 @@ * * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcp_wrappers/socket.c,v 1.2.2.2 2000/09/25 02:31:56 ume Exp $ */ #ifndef lint diff --git a/contrib/tcp_wrappers/tcpd.c b/contrib/tcp_wrappers/tcpd.c index 55c6853c1881..8d0696c947c8 100644 --- a/contrib/tcp_wrappers/tcpd.c +++ b/contrib/tcp_wrappers/tcpd.c @@ -9,7 +9,7 @@ * * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcp_wrappers/tcpd.c,v 1.2 2000/02/03 10:26:59 shin Exp $ */ #ifndef lint diff --git a/contrib/tcp_wrappers/tcpd.h b/contrib/tcp_wrappers/tcpd.h index fec20a262baf..07ef87ef1d82 100644 --- a/contrib/tcp_wrappers/tcpd.h +++ b/contrib/tcp_wrappers/tcpd.h @@ -3,7 +3,7 @@ * * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcp_wrappers/tcpd.h,v 1.2 2000/02/03 10:26:59 shin Exp $ */ /* Structure to describe one communications endpoint. */ diff --git a/contrib/tcp_wrappers/tcpdchk.c b/contrib/tcp_wrappers/tcpdchk.c index 99ec49525300..6b9417b6bc34 100644 --- a/contrib/tcp_wrappers/tcpdchk.c +++ b/contrib/tcp_wrappers/tcpdchk.c @@ -13,7 +13,7 @@ * * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcp_wrappers/tcpdchk.c,v 1.3.2.1 2000/07/18 08:34:55 ume Exp $ */ #ifndef lint diff --git a/contrib/tcp_wrappers/tcpdmatch.c b/contrib/tcp_wrappers/tcpdmatch.c index 94f3e7009606..a9bb3a32e694 100644 --- a/contrib/tcp_wrappers/tcpdmatch.c +++ b/contrib/tcp_wrappers/tcpdmatch.c @@ -12,7 +12,7 @@ * * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcp_wrappers/tcpdmatch.c,v 1.2.2.1 2000/07/18 08:34:55 ume Exp $ */ #ifndef lint diff --git a/contrib/tcp_wrappers/tli.c b/contrib/tcp_wrappers/tli.c index 36d6f7eff398..1efd807c8f71 100644 --- a/contrib/tcp_wrappers/tli.c +++ b/contrib/tcp_wrappers/tli.c @@ -13,7 +13,7 @@ * * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcp_wrappers/tli.c,v 1.2 2000/02/03 10:27:00 shin Exp $ */ #ifndef lint diff --git a/contrib/tcp_wrappers/update.c b/contrib/tcp_wrappers/update.c index b612d5e02466..8ba12653c2cc 100644 --- a/contrib/tcp_wrappers/update.c +++ b/contrib/tcp_wrappers/update.c @@ -12,7 +12,7 @@ * * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcp_wrappers/update.c,v 1.2 2000/02/03 10:27:00 shin Exp $ */ #ifndef lint diff --git a/contrib/tcp_wrappers/workarounds.c b/contrib/tcp_wrappers/workarounds.c index 1ad2c6471ef3..e1aa5c504c45 100644 --- a/contrib/tcp_wrappers/workarounds.c +++ b/contrib/tcp_wrappers/workarounds.c @@ -6,7 +6,7 @@ * * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcp_wrappers/workarounds.c,v 1.2 2000/02/03 10:27:01 shin Exp $ */ #ifndef lint diff --git a/contrib/tcpdump/addrtoname.c b/contrib/tcpdump/addrtoname.c index c1f220821c08..01cccb5f8f7d 100644 --- a/contrib/tcpdump/addrtoname.c +++ b/contrib/tcpdump/addrtoname.c @@ -21,7 +21,7 @@ * Internet, ethernet, port, and protocol string to address * and address to string conversion routines * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/addrtoname.c,v 1.7.2.1 2000/10/05 02:56:31 kris Exp $ */ #ifndef lint static const char rcsid[] = diff --git a/contrib/tcpdump/ethertype.h b/contrib/tcpdump/ethertype.h index 56bfa3c69ed1..b15753c9476c 100644 --- a/contrib/tcpdump/ethertype.h +++ b/contrib/tcpdump/ethertype.h @@ -19,7 +19,7 @@ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * @(#) $Header: /tcpdump/master/tcpdump/ethertype.h,v 1.7.2.1 2000/01/29 22:00:12 fenner Exp $ (LBL) - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/ethertype.h,v 1.4 2000/01/30 01:00:50 fenner Exp $ */ /* Types missing from some systems */ diff --git a/contrib/tcpdump/interface.h b/contrib/tcpdump/interface.h index f9081bb7f896..1a181ef218ce 100644 --- a/contrib/tcpdump/interface.h +++ b/contrib/tcpdump/interface.h @@ -19,7 +19,7 @@ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * @(#) $Header: /tcpdump/master/tcpdump/interface.h,v 1.118 1999/12/22 15:44:09 itojun Exp $ (LBL) - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/interface.h,v 1.4 2000/01/30 01:00:50 fenner Exp $ */ #ifndef tcpdump_interface_h diff --git a/contrib/tcpdump/nfs.h b/contrib/tcpdump/nfs.h index 0887148dad13..cd43a3ea4e79 100644 --- a/contrib/tcpdump/nfs.h +++ b/contrib/tcpdump/nfs.h @@ -35,7 +35,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/nfs.h,v 1.2 2000/01/30 01:00:50 fenner Exp $ * @(#)nfsproto.h 8.2 (Berkeley) 3/30/95 */ diff --git a/contrib/tcpdump/nfsfh.h b/contrib/tcpdump/nfsfh.h index 8a4541ecdc6f..ac9a9dab0cdc 100644 --- a/contrib/tcpdump/nfsfh.h +++ b/contrib/tcpdump/nfsfh.h @@ -6,7 +6,7 @@ * Jeffrey C. Mogul * Digital Equipment Corporation * Western Research Laboratory - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/nfsfh.h,v 1.5 2000/01/30 01:00:50 fenner Exp $ * $NetBSD: nfsfh.h,v 1.1.1.2 1997/10/03 17:25:13 christos Exp $ */ /* diff --git a/contrib/tcpdump/parsenfsfh.c b/contrib/tcpdump/parsenfsfh.c index c59ad6aa317a..f545e2e8aa96 100644 --- a/contrib/tcpdump/parsenfsfh.c +++ b/contrib/tcpdump/parsenfsfh.c @@ -6,7 +6,7 @@ * Digital Equipment Corporation * Western Research Laboratory * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/parsenfsfh.c,v 1.5 2000/01/30 01:00:50 fenner Exp $ */ #ifndef lint diff --git a/contrib/tcpdump/ppp.h b/contrib/tcpdump/ppp.h index da79154c7dc7..122da6d65db8 100644 --- a/contrib/tcpdump/ppp.h +++ b/contrib/tcpdump/ppp.h @@ -15,7 +15,7 @@ * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/ppp.h,v 1.3 2000/01/30 01:00:50 fenner Exp $ */ #undef PPP_ADDRESS #define PPP_ADDRESS 0xff /* The address byte value */ diff --git a/contrib/tcpdump/print-arp.c b/contrib/tcpdump/print-arp.c index 2af4bc1093e7..a3f0cf73f132 100644 --- a/contrib/tcpdump/print-arp.c +++ b/contrib/tcpdump/print-arp.c @@ -18,7 +18,7 @@ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/print-arp.c,v 1.5 2000/01/30 01:00:51 fenner Exp $ */ #ifndef lint diff --git a/contrib/tcpdump/print-atalk.c b/contrib/tcpdump/print-atalk.c index 2b77ecd35579..b383d27a71ea 100644 --- a/contrib/tcpdump/print-atalk.c +++ b/contrib/tcpdump/print-atalk.c @@ -20,7 +20,7 @@ * * Format and print AppleTalk packets. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/print-atalk.c,v 1.7.2.1 2000/10/05 02:56:31 kris Exp $ */ #ifndef lint diff --git a/contrib/tcpdump/print-atm.c b/contrib/tcpdump/print-atm.c index 8fb95e786dc0..703f0850ae7d 100644 --- a/contrib/tcpdump/print-atm.c +++ b/contrib/tcpdump/print-atm.c @@ -18,7 +18,7 @@ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/print-atm.c,v 1.6 2000/01/30 01:00:51 fenner Exp $ */ #ifndef lint static const char rcsid[] = diff --git a/contrib/tcpdump/print-bgp.c b/contrib/tcpdump/print-bgp.c index 89ef0477372f..20a3fc178e78 100644 --- a/contrib/tcpdump/print-bgp.c +++ b/contrib/tcpdump/print-bgp.c @@ -26,7 +26,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/print-bgp.c,v 1.1.1.1.2.1 2000/10/05 02:56:32 kris Exp $ */ #ifdef HAVE_CONFIG_H diff --git a/contrib/tcpdump/print-bootp.c b/contrib/tcpdump/print-bootp.c index 7e15815dfc45..182fbc51b568 100644 --- a/contrib/tcpdump/print-bootp.c +++ b/contrib/tcpdump/print-bootp.c @@ -20,7 +20,7 @@ * * Format and print bootp packets. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/print-bootp.c,v 1.5 2000/01/30 01:00:51 fenner Exp $ */ #ifndef lint static const char rcsid[] = diff --git a/contrib/tcpdump/print-domain.c b/contrib/tcpdump/print-domain.c index 50f2ce2b63b5..07d57feeb40b 100644 --- a/contrib/tcpdump/print-domain.c +++ b/contrib/tcpdump/print-domain.c @@ -18,7 +18,7 @@ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/print-domain.c,v 1.6 2000/01/30 01:00:51 fenner Exp $ */ #ifndef lint diff --git a/contrib/tcpdump/print-ether.c b/contrib/tcpdump/print-ether.c index d33028d6fecf..7845abecc49f 100644 --- a/contrib/tcpdump/print-ether.c +++ b/contrib/tcpdump/print-ether.c @@ -18,7 +18,7 @@ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/print-ether.c,v 1.9 2000/01/30 01:00:52 fenner Exp $ */ #ifndef lint static const char rcsid[] = diff --git a/contrib/tcpdump/print-fddi.c b/contrib/tcpdump/print-fddi.c index d2526fa7814d..d0a8a10e8cd9 100644 --- a/contrib/tcpdump/print-fddi.c +++ b/contrib/tcpdump/print-fddi.c @@ -18,7 +18,7 @@ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/print-fddi.c,v 1.5 2000/01/30 01:00:52 fenner Exp $ */ #ifndef lint diff --git a/contrib/tcpdump/print-fr.c b/contrib/tcpdump/print-fr.c index 70393387f210..20e60cdf71da 100644 --- a/contrib/tcpdump/print-fr.c +++ b/contrib/tcpdump/print-fr.c @@ -18,7 +18,7 @@ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/print-fr.c,v 1.2.6.1 2000/10/05 02:56:32 kris Exp $ */ #ifndef lint diff --git a/contrib/tcpdump/print-icmp.c b/contrib/tcpdump/print-icmp.c index a4b3dd6206df..b4c41be54fa3 100644 --- a/contrib/tcpdump/print-icmp.c +++ b/contrib/tcpdump/print-icmp.c @@ -18,7 +18,7 @@ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/print-icmp.c,v 1.4.2.1 2000/10/05 02:56:32 kris Exp $ */ #ifndef lint diff --git a/contrib/tcpdump/print-ip.c b/contrib/tcpdump/print-ip.c index fe06efd48ee9..36fa2b8a966c 100644 --- a/contrib/tcpdump/print-ip.c +++ b/contrib/tcpdump/print-ip.c @@ -18,7 +18,7 @@ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/print-ip.c,v 1.7 2000/01/30 01:00:53 fenner Exp $ */ #ifndef lint diff --git a/contrib/tcpdump/print-ip6.c b/contrib/tcpdump/print-ip6.c index 04dee43a350b..d4a27fea1b1d 100644 --- a/contrib/tcpdump/print-ip6.c +++ b/contrib/tcpdump/print-ip6.c @@ -18,7 +18,7 @@ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/print-ip6.c,v 1.2 2000/02/17 03:30:03 fenner Exp $ */ #ifndef lint diff --git a/contrib/tcpdump/print-ipx.c b/contrib/tcpdump/print-ipx.c index c17812387848..e0ae66018207 100644 --- a/contrib/tcpdump/print-ipx.c +++ b/contrib/tcpdump/print-ipx.c @@ -21,7 +21,7 @@ * Format and print Novell IPX packets. * Contributed by Brad Parker (brad@fcr.com). * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/print-ipx.c,v 1.4 2000/01/30 01:00:53 fenner Exp $ */ #ifndef lint diff --git a/contrib/tcpdump/print-isoclns.c b/contrib/tcpdump/print-isoclns.c index 399ec8b81d99..ccde2bec1b6e 100644 --- a/contrib/tcpdump/print-isoclns.c +++ b/contrib/tcpdump/print-isoclns.c @@ -20,7 +20,7 @@ * * Original code by Matt Thomas, Digital Equipment Corporation * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/print-isoclns.c,v 1.8 2000/01/30 01:00:53 fenner Exp $ */ #ifndef lint diff --git a/contrib/tcpdump/print-llc.c b/contrib/tcpdump/print-llc.c index 3216f4d6c148..78ebbb88c743 100644 --- a/contrib/tcpdump/print-llc.c +++ b/contrib/tcpdump/print-llc.c @@ -21,7 +21,7 @@ * Code by Matt Thomas, Digital Equipment Corporation * with an awful lot of hacking by Jeffrey Mogul, DECWRL * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/print-llc.c,v 1.6 2000/01/30 01:00:53 fenner Exp $ */ #ifndef lint diff --git a/contrib/tcpdump/print-ntp.c b/contrib/tcpdump/print-ntp.c index 1009872ed4b0..d3bddabf0a28 100644 --- a/contrib/tcpdump/print-ntp.c +++ b/contrib/tcpdump/print-ntp.c @@ -22,7 +22,7 @@ * By Jeffrey Mogul/DECWRL * loosely based on print-bootp.c * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/print-ntp.c,v 1.5 2000/01/30 01:00:53 fenner Exp $ */ #ifndef lint diff --git a/contrib/tcpdump/print-null.c b/contrib/tcpdump/print-null.c index 64c7df340c05..8ed571532a01 100644 --- a/contrib/tcpdump/print-null.c +++ b/contrib/tcpdump/print-null.c @@ -18,7 +18,7 @@ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/print-null.c,v 1.5 2000/01/30 01:00:53 fenner Exp $ */ #ifndef lint diff --git a/contrib/tcpdump/print-pim.c b/contrib/tcpdump/print-pim.c index 96bf6830ed76..6488a0972089 100644 --- a/contrib/tcpdump/print-pim.c +++ b/contrib/tcpdump/print-pim.c @@ -18,7 +18,7 @@ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/print-pim.c,v 1.2 2000/02/17 03:30:04 fenner Exp $ */ #ifndef lint diff --git a/contrib/tcpdump/print-ppp.c b/contrib/tcpdump/print-ppp.c index 3c831d372e1b..d9827e4f00f7 100644 --- a/contrib/tcpdump/print-ppp.c +++ b/contrib/tcpdump/print-ppp.c @@ -18,7 +18,7 @@ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/print-ppp.c,v 1.10 2000/01/30 01:00:54 fenner Exp $ */ #ifndef lint diff --git a/contrib/tcpdump/print-rx.c b/contrib/tcpdump/print-rx.c index ed5cca90709b..82983bf02df7 100644 --- a/contrib/tcpdump/print-rx.c +++ b/contrib/tcpdump/print-rx.c @@ -9,7 +9,7 @@ * * Ken Hornstein <kenh@cmf.nrl.navy.mil> * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/print-rx.c,v 1.1.1.1.2.1 2000/10/05 02:56:32 kris Exp $ */ #ifndef lint diff --git a/contrib/tcpdump/print-sl.c b/contrib/tcpdump/print-sl.c index c2f622bde0aa..dff2ac99d760 100644 --- a/contrib/tcpdump/print-sl.c +++ b/contrib/tcpdump/print-sl.c @@ -18,7 +18,7 @@ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/print-sl.c,v 1.5 2000/01/30 01:00:54 fenner Exp $ */ #ifndef lint diff --git a/contrib/tcpdump/print-sunrpc.c b/contrib/tcpdump/print-sunrpc.c index ddac9138321e..b4a2ecd1b309 100644 --- a/contrib/tcpdump/print-sunrpc.c +++ b/contrib/tcpdump/print-sunrpc.c @@ -18,7 +18,7 @@ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/print-sunrpc.c,v 1.5.2.1 2000/10/05 02:56:32 kris Exp $ */ #ifndef lint diff --git a/contrib/tcpdump/print-telnet.c b/contrib/tcpdump/print-telnet.c index b37486f9a62c..312b614f4086 100644 --- a/contrib/tcpdump/print-telnet.c +++ b/contrib/tcpdump/print-telnet.c @@ -44,7 +44,7 @@ * 1/ that the above copyright notice and this notice * are preserved in all copies. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/print-telnet.c,v 1.1.1.1.2.1 2000/10/05 02:56:32 kris Exp $ */ #ifdef HAVE_CONFIG_H diff --git a/contrib/tcpdump/print-token.c b/contrib/tcpdump/print-token.c index 0edbf2f0f75a..2eaa9a503d44 100644 --- a/contrib/tcpdump/print-token.c +++ b/contrib/tcpdump/print-token.c @@ -20,7 +20,7 @@ * * Hacked version of print-ether.c Larry Lile <lile@stdio.com> * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/print-token.c,v 1.3 2000/01/30 01:00:54 fenner Exp $ */ #ifndef lint static const char rcsid[] = diff --git a/contrib/tcpdump/print-udp.c b/contrib/tcpdump/print-udp.c index 9a64fb3da376..a72431271188 100644 --- a/contrib/tcpdump/print-udp.c +++ b/contrib/tcpdump/print-udp.c @@ -18,7 +18,7 @@ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/print-udp.c,v 1.5 2000/01/30 01:00:54 fenner Exp $ */ #ifndef lint diff --git a/contrib/tcpdump/smbutil.c b/contrib/tcpdump/smbutil.c index ebe627d0369f..8f5b24c7cace 100644 --- a/contrib/tcpdump/smbutil.c +++ b/contrib/tcpdump/smbutil.c @@ -5,7 +5,7 @@ BSD-style license that accompanies tcpdump or the GNU GPL version 2 or later */ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/tcpdump/smbutil.c,v 1.1.1.1.2.1 2000/10/05 02:56:32 kris Exp $ */ #ifdef HAVE_CONFIG_H #include "config.h" diff --git a/contrib/tcpdump/tcpdump.1 b/contrib/tcpdump/tcpdump.1 index 500bb000c206..f98562248c8f 100644 --- a/contrib/tcpdump/tcpdump.1 +++ b/contrib/tcpdump/tcpdump.1 @@ -20,7 +20,7 @@ .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/tcpdump/tcpdump.1,v 1.8 2000/01/30 01:00:55 fenner Exp $ .\" .TH TCPDUMP 1 "30 June 1997" .SH NAME diff --git a/contrib/tcpdump/tcpdump.c b/contrib/tcpdump/tcpdump.c index daf95fe28950..81c0c3ada3e5 100644 --- a/contrib/tcpdump/tcpdump.c +++ b/contrib/tcpdump/tcpdump.c @@ -27,7 +27,7 @@ static const char rcsid[] = "@(#) $Header: /tcpdump/master/tcpdump/tcpdump.c,v 1.138.2.1 2000/01/11 07:34:00 fenner Exp $ (LBL)"; #endif -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/tcpdump/tcpdump.c,v 1.4 2000/01/30 01:00:55 fenner Exp $ */ /* * tcpdump - monitor tcp/ip traffic on an ethernet. diff --git a/contrib/tcpdump/util.c b/contrib/tcpdump/util.c index e2d883ef8418..063f1e5f2ba4 100644 --- a/contrib/tcpdump/util.c +++ b/contrib/tcpdump/util.c @@ -18,7 +18,7 @@ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $FreeBSD$ + * $FreeBSD: src/contrib/tcpdump/util.c,v 1.1.1.4.2.1 2000/10/05 02:56:32 kris Exp $ */ #ifndef lint diff --git a/contrib/texinfo/FREEBSD-upgrade b/contrib/texinfo/FREEBSD-upgrade index 54254e397a6c..b12195c6bb01 100644 --- a/contrib/texinfo/FREEBSD-upgrade +++ b/contrib/texinfo/FREEBSD-upgrade @@ -1,5 +1,5 @@ # ex:ts=8 -$FreeBSD$ +$FreeBSD: src/contrib/texinfo/FREEBSD-upgrade,v 1.1 2000/01/17 11:53:54 ru Exp $ GNU Texinfo 4.0 originals can be found at: ftp://ftp.gnu.org/gnu/texinfo/ diff --git a/contrib/texinfo/config.h b/contrib/texinfo/config.h index d5a968232a02..38b9c7c7c044 100644 --- a/contrib/texinfo/config.h +++ b/contrib/texinfo/config.h @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: src/contrib/texinfo/config.h,v 1.2 2000/01/17 10:52:37 ru Exp $ */ /* config.h. Generated automatically by configure. */ /* config.in. Generated automatically from configure.in by autoheader. */ diff --git a/contrib/texinfo/info/signals.c b/contrib/texinfo/info/signals.c index 954a73945159..2c01d5724e0b 100644 --- a/contrib/texinfo/info/signals.c +++ b/contrib/texinfo/info/signals.c @@ -1,6 +1,6 @@ /* signals.c -- install and maintain Info signal handlers. $Id: signals.c,v 1.6 1998/12/06 22:00:04 karl Exp $ - $FreeBSD$ + $FreeBSD: src/contrib/texinfo/info/signals.c,v 1.6 2000/01/17 10:46:55 ru Exp $ Copyright (C) 1993, 94, 95, 98 Free Software Foundation, Inc. diff --git a/contrib/texinfo/util/install-info.c b/contrib/texinfo/util/install-info.c index e69bbeadb2f6..6f9f8505a72a 100644 --- a/contrib/texinfo/util/install-info.c +++ b/contrib/texinfo/util/install-info.c @@ -1,6 +1,6 @@ /* install-info -- create Info directory entry(ies) for an Info file. $Id: install-info.c,v 1.48 1999/08/06 18:13:32 karl Exp $ - $FreeBSD$ + $FreeBSD: src/contrib/texinfo/util/install-info.c,v 1.11 2000/01/24 16:05:17 ru Exp $ Copyright (C) 1996, 97, 98, 99 Free Software Foundation, Inc. diff --git a/contrib/top/display.c b/contrib/top/display.c index 2a39072e2cb6..212787c5aad3 100644 --- a/contrib/top/display.c +++ b/contrib/top/display.c @@ -8,7 +8,7 @@ * Copyright (c) 1984, 1989, William LeFebvre, Rice University * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University * - * $FreeBSD$ + * $FreeBSD: src/contrib/top/display.c,v 1.4.6.1 2000/10/04 23:40:04 imp Exp $ */ /* diff --git a/contrib/top/screen.c b/contrib/top/screen.c index 348304cc2836..03a2a3b99bd9 100644 --- a/contrib/top/screen.c +++ b/contrib/top/screen.c @@ -8,7 +8,7 @@ * Copyright (c) 1984, 1989, William LeFebvre, Rice University * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University * - * $FreeBSD$ + * $FreeBSD: src/contrib/top/screen.c,v 1.2.6.1 2000/09/20 02:27:57 jkh Exp $ */ /* This file contains the routines that interface to termcap and stty/gtty. diff --git a/contrib/top/top.X b/contrib/top/top.X index 97649c37f26e..0712535dc666 100644 --- a/contrib/top/top.X +++ b/contrib/top/top.X @@ -1,6 +1,6 @@ .\" NOTE: changes to the manual page for "top" should be made in the .\" file "top.X" and NOT in the file "top.1". -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/top/top.X,v 1.6.6.1 2000/07/20 13:29:20 phantom Exp $ .nr N %topn% .nr D %delay% .TH TOP 1 Local diff --git a/contrib/top/top.c b/contrib/top/top.c index 94a02f2c424a..389ea12d15d5 100644 --- a/contrib/top/top.c +++ b/contrib/top/top.c @@ -11,7 +11,7 @@ char *copyright = * Copyright (c) 1984, 1989, William LeFebvre, Rice University * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University * - * $FreeBSD$ + * $FreeBSD: src/contrib/top/top.c,v 1.4.6.2 2000/11/04 23:51:42 imp Exp $ */ /* diff --git a/contrib/traceroute/FREEBSD-upgrade b/contrib/traceroute/FREEBSD-upgrade index 749dd672ca03..7d9fe84223f8 100644 --- a/contrib/traceroute/FREEBSD-upgrade +++ b/contrib/traceroute/FREEBSD-upgrade @@ -1,4 +1,4 @@ -$FreeBSD$ +$FreeBSD: src/contrib/traceroute/FREEBSD-upgrade,v 1.2 1999/12/20 16:07:53 phantom Exp $ This directory contains virgin copies of the original distribution files on a "vendor" branch. Do not, under any circumstances, attempt to upgrade diff --git a/contrib/traceroute/traceroute.8 b/contrib/traceroute/traceroute.8 index 784935b0e82c..8bc012cc00d2 100644 --- a/contrib/traceroute/traceroute.8 +++ b/contrib/traceroute/traceroute.8 @@ -14,7 +14,7 @@ .\" WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" .\" $Header: traceroute.8,v 1.7 96/09/27 20:02:41 leres Exp $ -.\" $FreeBSD$ +.\" $FreeBSD: src/contrib/traceroute/traceroute.8,v 1.6 1999/10/05 15:20:45 obrien Exp $ .\" .TH TRACEROUTE 8 "27 September 1996" .UC 6 diff --git a/contrib/traceroute/traceroute.c b/contrib/traceroute/traceroute.c index a733a8a463a1..0c021817440c 100644 --- a/contrib/traceroute/traceroute.c +++ b/contrib/traceroute/traceroute.c @@ -28,7 +28,7 @@ static const char rcsid[] = "@(#)$Header: traceroute.c,v 1.43 96/09/27 20:08:10 leres Exp $ (LBL)"; #endif static const char rcsid[] = - "$FreeBSD$"; + "$FreeBSD: src/contrib/traceroute/traceroute.c,v 1.11.2.3 2000/10/29 03:46:24 kris Exp $"; #endif /* |
