159692Selan /* Subroutines for insn-output.c for Motorola 68000 family.
259692Selan    Copyright (C) 1987 Free Software Foundation, Inc.
359692Selan 
459692Selan This file is part of GNU CC.
559692Selan 
659692Selan GNU CC is free software; you can redistribute it and/or modify
759692Selan it under the terms of the GNU General Public License as published by
859692Selan the Free Software Foundation; either version 2, or (at your option)
959692Selan any later version.
1059692Selan 
1159692Selan GNU CC is distributed in the hope that it will be useful,
1259692Selan but WITHOUT ANY WARRANTY; without even the implied warranty of
1359692Selan MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1459692Selan GNU General Public License for more details.
1559692Selan 
1659692Selan You should have received a copy of the GNU General Public License
1759692Selan along with GNU CC; see the file COPYING.  If not, write to
1859692Selan the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
1959692Selan 
2059692Selan 
2159692Selan /* Some output-actions in m68k.md need these.  */
2259692Selan #include <stdio.h>
2359692Selan #include "config.h"
2459692Selan #include "rtl.h"
2559692Selan #include "regs.h"
2659692Selan #include "hard-reg-set.h"
2759692Selan #include "real.h"
2859692Selan #include "insn-config.h"
2959692Selan #include "conditions.h"
3059692Selan #include "insn-flags.h"
3159692Selan #include "output.h"
3259692Selan #include "insn-attr.h"
3359692Selan 
3459692Selan /* Needed for use_return_insn.  */
3559692Selan #include "flags.h"
3659692Selan 
3759692Selan #ifdef SUPPORT_SUN_FPA
3859692Selan 
3959692Selan /* Index into this array by (register number >> 3) to find the
4059692Selan    smallest class which contains that register.  */
4159692Selan enum reg_class regno_reg_class[]
4259692Selan   = { DATA_REGS, ADDR_REGS, FP_REGS,
4359692Selan       LO_FPA_REGS, LO_FPA_REGS, FPA_REGS, FPA_REGS };
4459692Selan 
4559692Selan #endif /* defined SUPPORT_SUN_FPA */
4659692Selan 
4759692Selan /* This flag is used to communicate between movhi and ASM_OUTPUT_CASE_END,
4859692Selan    if SGS_SWITCH_TABLE.  */
4959692Selan int switch_table_difference_label_flag;
5059692Selan 
5159692Selan static rtx find_addr_reg ();
5259692Selan rtx legitimize_pic_address ();
5359692Selan 
5459692Selan 
5559692Selan /* Emit a (use pic_offset_table_rtx) if we used PIC relocation in the
5659692Selan    function at any time during the compilation process.  In the future
5759692Selan    we should try and eliminate the USE if we can easily determine that
5859692Selan    all PIC references were deleted from the current function.  That would
5959692Selan    save an address register */
6059692Selan 
finalize_pic()6159692Selan finalize_pic ()
6259692Selan {
6359692Selan   if (flag_pic && current_function_uses_pic_offset_table)
6459692Selan     emit_insn (gen_rtx (USE, VOIDmode, pic_offset_table_rtx));
6559692Selan }
6659692Selan 
6759692Selan 
6859692Selan /* This function generates the assembly code for function entry.
6959692Selan    STREAM is a stdio stream to output the code to.
7059692Selan    SIZE is an int: how many units of temporary storage to allocate.
7159692Selan    Refer to the array `regs_ever_live' to determine which registers
7259692Selan    to save; `regs_ever_live[I]' is nonzero if register number I
7359692Selan    is ever used in the function.  This function is responsible for
7459692Selan    knowing which registers should not be saved even if used.  */
7559692Selan 
7659692Selan 
7759692Selan /* Note that the order of the bit mask for fmovem is the opposite
7859692Selan    of the order for movem!  */
7959692Selan 
8059692Selan 
8159692Selan void
output_function_prologue(stream,size)8259692Selan output_function_prologue (stream, size)
8359692Selan      FILE *stream;
8459692Selan      int size;
8559692Selan {
8659692Selan   register int regno;
8759692Selan   register int mask = 0;
8859692Selan   int num_saved_regs = 0;
8959692Selan   extern char call_used_regs[];
9059692Selan   int fsize = (size + 3) & -4;
9159692Selan 
9259692Selan 
9359692Selan   if (frame_pointer_needed)
9459692Selan     {
9559692Selan       /* Adding negative number is faster on the 68040.  */
9659692Selan       if (fsize < 0x8000 && !TARGET_68040)
9759692Selan 	{
9859692Selan #ifdef MOTOROLA
9959692Selan 	  asm_fprintf (stream, "\tlink.w %s,%0I%d\n",
10059692Selan 		       reg_names[FRAME_POINTER_REGNUM], -fsize);
10159692Selan #else
10259692Selan 	  asm_fprintf (stream, "\tlink %s,%0I%d\n",
10359692Selan 		       reg_names[FRAME_POINTER_REGNUM], -fsize);
10459692Selan #endif
10559692Selan 	}
10659692Selan       else if (TARGET_68020)
10759692Selan 	{
10859692Selan #ifdef MOTOROLA
10959692Selan 	  asm_fprintf (stream, "\tlink.l %s,%0I%d\n",
11059692Selan 		       reg_names[FRAME_POINTER_REGNUM], -fsize);
11159692Selan #else
11259692Selan 	  asm_fprintf (stream, "\tlink %s,%0I%d\n",
11359692Selan 		       reg_names[FRAME_POINTER_REGNUM], -fsize);
11459692Selan #endif
11559692Selan 	}
11659692Selan       else
11759692Selan 	{
11859692Selan #ifdef MOTOROLA
11959692Selan 	  asm_fprintf (stream, "\tlink.w %s,%0I0\n\tadd.l %0I%d,%Rsp\n",
12059692Selan 		       reg_names[FRAME_POINTER_REGNUM], -fsize);
12159692Selan #else
12259692Selan 	  asm_fprintf (stream, "\tlink %s,%0I0\n\taddl %0I%d,%Rsp\n",
12359692Selan 		       reg_names[FRAME_POINTER_REGNUM], -fsize);
12459692Selan #endif
12559692Selan 	}
12659692Selan     }
12759692Selan   else if (fsize)
12859692Selan     {
12959692Selan       /* Adding negative number is faster on the 68040.  */
13059692Selan       if (fsize + 4 < 0x8000)
13159692Selan 	{
13259692Selan #ifdef MOTOROLA
13359692Selan 	  asm_fprintf (stream, "\tadd.w %0I%d,%Rsp\n", - (fsize + 4));
13459692Selan #else
13559692Selan 	  asm_fprintf (stream, "\taddw %0I%d,%Rsp\n", - (fsize + 4));
13659692Selan #endif
13759692Selan 	}
13859692Selan       else
13959692Selan 	{
14059692Selan #ifdef MOTOROLA
14159692Selan 	  asm_fprintf (stream, "\tadd.l %0I%d,%Rsp\n", - (fsize + 4));
14259692Selan #else
14359692Selan 	  asm_fprintf (stream, "\taddl %0I%d,%Rsp\n", - (fsize + 4));
14459692Selan #endif
14559692Selan 	}
14659692Selan     }
14759692Selan #ifdef SUPPORT_SUN_FPA
14859692Selan   for (regno = 24; regno < 56; regno++)
14959692Selan     if (regs_ever_live[regno] && ! call_used_regs[regno])
15059692Selan       {
15159692Selan #ifdef MOTOROLA
15259692Selan 	asm_fprintf (stream, "\tfpmovd %s,-(%Rsp)\n",
15359692Selan 		     reg_names[regno]);
15459692Selan #else
15559692Selan 	asm_fprintf (stream, "\tfpmoved %s,%Rsp@-\n",
15659692Selan 		     reg_names[regno]);
15759692Selan #endif
15859692Selan       }
15959692Selan #endif
16059692Selan   for (regno = 16; regno < 24; regno++)
16159692Selan     if (regs_ever_live[regno] && ! call_used_regs[regno])
16259692Selan        mask |= 1 << (regno - 16);
16359692Selan   if ((mask & 0xff) != 0)
16459692Selan     {
16559692Selan #ifdef MOTOROLA
16659692Selan       asm_fprintf (stream, "\tfmovm %0I0x%x,-(%Rsp)\n", mask & 0xff);
16759692Selan #else
16859692Selan       asm_fprintf (stream, "\tfmovem %0I0x%x,%Rsp@-\n", mask & 0xff);
16959692Selan #endif
17059692Selan     }
17159692Selan   mask = 0;
17259692Selan   for (regno = 0; regno < 16; regno++)
17359692Selan     if (regs_ever_live[regno] && ! call_used_regs[regno])
17459692Selan       {
17559692Selan         mask |= 1 << (15 - regno);
17659692Selan         num_saved_regs++;
17759692Selan       }
17859692Selan   if (frame_pointer_needed)
17959692Selan     {
18059692Selan       mask &= ~ (1 << (15 - FRAME_POINTER_REGNUM));
18159692Selan       num_saved_regs--;
18259692Selan     }
18359692Selan 
18459692Selan #if NEED_PROBE
18559692Selan   fprintf (stream, "\ttstl sp@(%d)\n", NEED_PROBE - num_saved_regs * 4);
18659692Selan #endif
18759692Selan 
18859692Selan   if (num_saved_regs <= 2)
18959692Selan     {
19059692Selan       /* Store each separately in the same order moveml uses.
19159692Selan          Using two movel instructions instead of a single moveml
19259692Selan          is about 15% faster for the 68020 and 68030 at no expense
19359692Selan          in code size */
19459692Selan 
19559692Selan       int i;
19659692Selan 
19759692Selan       /* Undo the work from above. */
19859692Selan       for (i = 0; i< 16; i++)
19959692Selan         if (mask & (1 << i))
20059692Selan           asm_fprintf (stream,
20159692Selan #ifdef MOTOROLA
20259692Selan 		       "\t%Omove.l %s,-(%Rsp)\n",
20359692Selan #else
20459692Selan 		       "\tmovel %s,%Rsp@-\n",
20559692Selan #endif
20659692Selan 		       reg_names[15 - i]);
20759692Selan     }
20859692Selan   else if (mask)
20959692Selan     {
21059692Selan #ifdef MOTOROLA
21159692Selan       asm_fprintf (stream, "\tmovm.l %0I0x%x,-(%Rsp)\n", mask);
21259692Selan #else
21359692Selan       asm_fprintf (stream, "\tmoveml %0I0x%x,%Rsp@-\n", mask);
21459692Selan #endif
21559692Selan     }
21659692Selan   if (flag_pic && current_function_uses_pic_offset_table)
21759692Selan     {
21859692Selan #ifdef MOTOROLA
21959692Selan       asm_fprintf (stream, "\t%Omove.l %0I__GLOBAL_OFFSET_TABLE_, %s\n",
22059692Selan 		   reg_names[PIC_OFFSET_TABLE_REGNUM]);
22159692Selan       asm_fprintf (stream, "\tlea.l (%Rpc,%s.l),%s\n",
22259692Selan 		   reg_names[PIC_OFFSET_TABLE_REGNUM],
22359692Selan 		   reg_names[PIC_OFFSET_TABLE_REGNUM]);
22459692Selan #else
22559692Selan       asm_fprintf (stream, "\tmovel %0I__GLOBAL_OFFSET_TABLE_, %s\n",
22659692Selan 		   reg_names[PIC_OFFSET_TABLE_REGNUM]);
22759692Selan       asm_fprintf (stream, "\tlea %Rpc@(0,%s:l),%s\n",
22859692Selan 		   reg_names[PIC_OFFSET_TABLE_REGNUM],
22959692Selan 		   reg_names[PIC_OFFSET_TABLE_REGNUM]);
23059692Selan #endif
23159692Selan     }
23259692Selan }
23359692Selan 
23459692Selan /* Return true if this function's epilogue can be output as RTL.  */
23559692Selan 
23659692Selan int
use_return_insn()23759692Selan use_return_insn ()
23859692Selan {
23959692Selan   int regno;
24059692Selan 
24159692Selan   if (!reload_completed || frame_pointer_needed || get_frame_size () != 0)
24259692Selan     return 0;
24359692Selan 
24459692Selan   /* Copied from output_function_epilogue ().  We should probably create a
24559692Selan      separate layout routine to perform the common work.  */
24659692Selan 
24759692Selan   for (regno = 0 ; regno < FIRST_PSEUDO_REGISTER ; regno++)
24859692Selan     if (regs_ever_live[regno] && ! call_used_regs[regno])
24959692Selan       return 0;
25059692Selan 
25159692Selan   return 1;
25259692Selan }
25359692Selan 
25459692Selan /* This function generates the assembly code for function exit,
25559692Selan    on machines that need it.  Args are same as for FUNCTION_PROLOGUE.
25659692Selan 
25759692Selan    The function epilogue should not depend on the current stack pointer!
25859692Selan    It should use the frame pointer only, if there is a frame pointer.
25959692Selan    This is mandatory because of alloca; we also take advantage of it to
26059692Selan    omit stack adjustments before returning.  */
26159692Selan 
26259692Selan void
output_function_epilogue(stream,size)26359692Selan output_function_epilogue (stream, size)
26459692Selan      FILE *stream;
26559692Selan      int size;
26659692Selan {
26759692Selan   register int regno;
26859692Selan   register int mask, fmask;
26959692Selan   register int nregs;
27059692Selan   int offset, foffset, fpoffset;
27159692Selan   extern char call_used_regs[];
27259692Selan   int fsize = (size + 3) & -4;
27359692Selan   int big = 0;
27459692Selan   rtx insn = get_last_insn ();
27559692Selan 
27659692Selan   /* If the last insn was a BARRIER, we don't have to write any code.  */
27759692Selan   if (GET_CODE (insn) == NOTE)
27859692Selan     insn = prev_nonnote_insn (insn);
27959692Selan   if (insn && GET_CODE (insn) == BARRIER)
28059692Selan     {
28159692Selan       /* Output just a no-op so that debuggers don't get confused
28259692Selan 	 about which function the pc is in at this address.  */
28359692Selan       asm_fprintf (stream, "\tnop\n");
28459692Selan       return;
28559692Selan     }
28659692Selan 
28759692Selan #ifdef FUNCTION_EXTRA_EPILOGUE
28859692Selan   FUNCTION_EXTRA_EPILOGUE (stream, size);
28959692Selan #endif
29059692Selan   nregs = 0;  fmask = 0; fpoffset = 0;
29159692Selan #ifdef SUPPORT_SUN_FPA
29259692Selan   for (regno = 24 ; regno < 56 ; regno++)
29359692Selan     if (regs_ever_live[regno] && ! call_used_regs[regno])
29459692Selan       nregs++;
29559692Selan   fpoffset = nregs * 8;
29659692Selan #endif
29759692Selan   nregs = 0;
29859692Selan   for (regno = 16; regno < 24; regno++)
29959692Selan     if (regs_ever_live[regno] && ! call_used_regs[regno])
30059692Selan       {
30159692Selan         nregs++;
30259692Selan 	fmask |= 1 << (23 - regno);
30359692Selan       }
30459692Selan   foffset = fpoffset + nregs * 12;
30559692Selan   nregs = 0;  mask = 0;
30659692Selan   if (frame_pointer_needed)
30759692Selan     regs_ever_live[FRAME_POINTER_REGNUM] = 0;
30859692Selan   for (regno = 0; regno < 16; regno++)
30959692Selan     if (regs_ever_live[regno] && ! call_used_regs[regno])
31059692Selan       {
31159692Selan         nregs++;
31259692Selan 	mask |= 1 << regno;
31359692Selan       }
31459692Selan   offset = foffset + nregs * 4;
31559692Selan   if (offset + fsize >= 0x8000
31659692Selan       && frame_pointer_needed
31759692Selan       && (mask || fmask || fpoffset))
31859692Selan     {
31959692Selan #ifdef MOTOROLA
32059692Selan       asm_fprintf (stream, "\t%Omove.l %0I%d,%Ra0\n", -fsize);
32159692Selan #else
32259692Selan       asm_fprintf (stream, "\tmovel %0I%d,%Ra0\n", -fsize);
32359692Selan #endif
32459692Selan       fsize = 0, big = 1;
32559692Selan     }
32659692Selan   if (nregs <= 2)
32759692Selan     {
32859692Selan       /* Restore each separately in the same order moveml does.
32959692Selan          Using two movel instructions instead of a single moveml
33059692Selan          is about 15% faster for the 68020 and 68030 at no expense
33159692Selan          in code size. */
33259692Selan 
33359692Selan       int i;
33459692Selan 
33559692Selan       /* Undo the work from above. */
33659692Selan       for (i = 0; i< 16; i++)
33759692Selan         if (mask & (1 << i))
33859692Selan           {
33959692Selan             if (big)
34059692Selan 	      {
34159692Selan #ifdef MOTOROLA
34259692Selan 		asm_fprintf (stream, "\t%Omove.l -%d(%s,%Ra0.l),%s\n",
34359692Selan 			     offset + fsize,
34459692Selan 			     reg_names[FRAME_POINTER_REGNUM],
34559692Selan 			     reg_names[i]);
34659692Selan #else
34759692Selan 		asm_fprintf (stream, "\tmovel %s@(-%d,%Ra0:l),%s\n",
34859692Selan 			     reg_names[FRAME_POINTER_REGNUM],
34959692Selan 			     offset + fsize, reg_names[i]);
35059692Selan #endif
35159692Selan 	      }
35259692Selan             else if (! frame_pointer_needed)
35359692Selan 	      {
35459692Selan #ifdef MOTOROLA
35559692Selan 		asm_fprintf (stream, "\t%Omove.l (%Rsp)+,%s\n",
35659692Selan 			     reg_names[i]);
35759692Selan #else
35859692Selan 		asm_fprintf (stream, "\tmovel %Rsp@+,%s\n",
35959692Selan 			     reg_names[i]);
36059692Selan #endif
36159692Selan 	      }
36259692Selan             else
36359692Selan 	      {
36459692Selan #ifdef MOTOROLA
36559692Selan 		asm_fprintf (stream, "\t%Omove.l -%d(%s),%s\n",
36659692Selan 			     offset + fsize,
36759692Selan 			     reg_names[FRAME_POINTER_REGNUM],
36859692Selan 			     reg_names[i]);
36959692Selan #else
37059692Selan 		asm_fprintf (stream, "\tmovel %s@(-%d),%s\n",
37159692Selan 			     reg_names[FRAME_POINTER_REGNUM],
37259692Selan 			     offset + fsize, reg_names[i]);
37359692Selan #endif
37459692Selan 	      }
37559692Selan             offset = offset - 4;
37659692Selan           }
37759692Selan     }
37859692Selan   else if (mask)
37959692Selan     {
38059692Selan       if (big)
38159692Selan 	{
38259692Selan #ifdef MOTOROLA
38359692Selan 	  asm_fprintf (stream, "\tmovm.l -%d(%s,%Ra0.l),%0I0x%x\n",
38459692Selan 		       offset + fsize,
38559692Selan 		       reg_names[FRAME_POINTER_REGNUM],
38659692Selan 		       mask);
38759692Selan #else
38859692Selan 	  asm_fprintf (stream, "\tmoveml %s@(-%d,%Ra0:l),%0I0x%x\n",
38959692Selan 		       reg_names[FRAME_POINTER_REGNUM],
39059692Selan 		       offset + fsize, mask);
39159692Selan #endif
39259692Selan 	}
39359692Selan       else if (! frame_pointer_needed)
39459692Selan 	{
39559692Selan #ifdef MOTOROLA
39659692Selan 	  asm_fprintf (stream, "\tmovm.l (%Rsp)+,%0I0x%x\n", mask);
39759692Selan #else
39859692Selan 	  asm_fprintf (stream, "\tmoveml %Rsp@+,%0I0x%x\n", mask);
39959692Selan #endif
40059692Selan 	}
40159692Selan       else
40259692Selan 	{
40359692Selan #ifdef MOTOROLA
40459692Selan 	  asm_fprintf (stream, "\tmovm.l -%d(%s),%0I0x%x\n",
40559692Selan 		       offset + fsize,
40659692Selan 		       reg_names[FRAME_POINTER_REGNUM],
40759692Selan 		       mask);
40859692Selan #else
40959692Selan 	  asm_fprintf (stream, "\tmoveml %s@(-%d),%0I0x%x\n",
41059692Selan 		       reg_names[FRAME_POINTER_REGNUM],
41159692Selan 		       offset + fsize, mask);
41259692Selan #endif
41359692Selan 	}
41459692Selan     }
41559692Selan   if (fmask)
41659692Selan     {
41759692Selan       if (big)
41859692Selan 	{
41959692Selan #ifdef MOTOROLA
42059692Selan 	  asm_fprintf (stream, "\tfmovm -%d(%s,%Ra0.l),%0I0x%x\n",
42159692Selan 		       foffset + fsize,
42259692Selan 		       reg_names[FRAME_POINTER_REGNUM],
42359692Selan 		       fmask);
42459692Selan #else
42559692Selan 	  asm_fprintf (stream, "\tfmovem %s@(-%d,%Ra0:l),%0I0x%x\n",
42659692Selan 		       reg_names[FRAME_POINTER_REGNUM],
42759692Selan 		       foffset + fsize, fmask);
42859692Selan #endif
42959692Selan 	}
43059692Selan       else if (! frame_pointer_needed)
43159692Selan 	{
43259692Selan #ifdef MOTOROLA
43359692Selan 	  asm_fprintf (stream, "\tfmovm (%Rsp)+,%0I0x%x\n", fmask);
43459692Selan #else
43559692Selan 	  asm_fprintf (stream, "\tfmovem %Rsp@+,%0I0x%x\n", fmask);
43659692Selan #endif
43759692Selan 	}
43859692Selan       else
43959692Selan 	{
44059692Selan #ifdef MOTOROLA
44159692Selan 	  asm_fprintf (stream, "\tfmovm -%d(%s),%0I0x%x\n",
44259692Selan 		       foffset + fsize,
44359692Selan 		       reg_names[FRAME_POINTER_REGNUM],
44459692Selan 		       fmask);
44559692Selan #else
44659692Selan 	  asm_fprintf (stream, "\tfmovem %s@(-%d),%0I0x%x\n",
44759692Selan 		       reg_names[FRAME_POINTER_REGNUM],
44859692Selan 		       foffset + fsize, fmask);
44959692Selan #endif
45059692Selan 	}
45159692Selan     }
45259692Selan   if (fpoffset != 0)
45359692Selan     for (regno = 55; regno >= 24; regno--)
45459692Selan       if (regs_ever_live[regno] && ! call_used_regs[regno])
45559692Selan         {
45659692Selan 	  if (big)
45759692Selan 	    {
45859692Selan #ifdef MOTOROLA
45959692Selan 	      asm_fprintf (stream, "\tfpmovd -%d(%s,%Ra0.l), %s\n",
46059692Selan 			   fpoffset + fsize,
46159692Selan 			   reg_names[FRAME_POINTER_REGNUM],
46259692Selan 			   reg_names[regno]);
46359692Selan #else
46459692Selan 	      asm_fprintf (stream, "\tfpmoved %s@(-%d,%Ra0:l), %s\n",
46559692Selan 			   reg_names[FRAME_POINTER_REGNUM],
46659692Selan 			   fpoffset + fsize, reg_names[regno]);
46759692Selan #endif
46859692Selan 	    }
46959692Selan 	  else if (! frame_pointer_needed)
47059692Selan 	    {
47159692Selan #ifdef MOTOROLA
47259692Selan 	      asm_fprintf (stream, "\tfpmovd (%Rsp)+,%s\n",
47359692Selan 			   reg_names[regno]);
47459692Selan #else
47559692Selan 	      asm_fprintf (stream, "\tfpmoved %Rsp@+, %s\n",
47659692Selan 			   reg_names[regno]);
47759692Selan #endif
47859692Selan 	    }
47959692Selan 	  else
48059692Selan 	    {
48159692Selan #ifdef MOTOROLA
48259692Selan 	      asm_fprintf (stream, "\tfpmovd -%d(%s), %s\n",
48359692Selan 			   fpoffset + fsize,
48459692Selan 			   reg_names[FRAME_POINTER_REGNUM],
48559692Selan 			   reg_names[regno]);
48659692Selan #else
48759692Selan 	      asm_fprintf (stream, "\tfpmoved %s@(-%d), %s\n",
48859692Selan 			   reg_names[FRAME_POINTER_REGNUM],
48959692Selan 			   fpoffset + fsize, reg_names[regno]);
49059692Selan #endif
49159692Selan 	    }
49259692Selan 	  fpoffset -= 8;
49359692Selan 	}
49459692Selan   if (frame_pointer_needed)
49559692Selan     fprintf (stream, "\tunlk %s\n",
49659692Selan 	     reg_names[FRAME_POINTER_REGNUM]);
49759692Selan   else if (fsize)
49859692Selan     {
49959692Selan       if (fsize + 4 < 0x8000)
50059692Selan 	{
50159692Selan #ifdef MOTOROLA
50259692Selan 	  asm_fprintf (stream, "\tadd.w %0I%d,%Rsp\n", fsize + 4);
50359692Selan #else
50459692Selan 	  asm_fprintf (stream, "\taddw %0I%d,%Rsp\n", fsize + 4);
50559692Selan #endif
50659692Selan 	}
50759692Selan       else
50859692Selan 	{
50959692Selan #ifdef MOTOROLA
51059692Selan 	  asm_fprintf (stream, "\tadd.l %0I%d,%Rsp\n", fsize + 4);
51159692Selan #else
51259692Selan 	  asm_fprintf (stream, "\taddl %0I%d,%Rsp\n", fsize + 4);
51359692Selan #endif
51459692Selan 	}
51559692Selan     }
51659692Selan   if (current_function_pops_args)
51759692Selan     asm_fprintf (stream, "\trtd %0I%d\n", current_function_pops_args);
51859692Selan   else
51959692Selan     fprintf (stream, "\trts\n");
52059692Selan }
52159692Selan 
52259692Selan /* Similar to general_operand, but exclude stack_pointer_rtx.  */
52359692Selan 
52459692Selan int
not_sp_operand(op,mode)52559692Selan not_sp_operand (op, mode)
52659692Selan      register rtx op;
52759692Selan      enum machine_mode mode;
52859692Selan {
52959692Selan   return op != stack_pointer_rtx && general_operand (op, mode);
53059692Selan }
53159692Selan 
53259692Selan /* Return TRUE if X is a valid comparison operator for the dbcc
53359692Selan    instruction.
53459692Selan 
53559692Selan    Note it rejects floating point comparison operators.
53659692Selan    (In the future we could use Fdbcc).
53759692Selan 
53859692Selan    It also rejects some comparisons when CC_NO_OVERFLOW is set.  */
53959692Selan 
54059692Selan int
valid_dbcc_comparison_p(x,mode)54159692Selan valid_dbcc_comparison_p (x, mode)
54259692Selan      rtx x;
54359692Selan      enum machine_mode mode;
54459692Selan {
54559692Selan   /* We could add support for these in the future */
54659692Selan   if (cc_prev_status.flags & CC_IN_68881)
54759692Selan     return 0;
54859692Selan 
54959692Selan   switch (GET_CODE (x))
55059692Selan     {
55159692Selan 
55259692Selan       case EQ: case NE: case GTU: case LTU:
55359692Selan       case GEU: case LEU:
55459692Selan         return 1;
55559692Selan 
55659692Selan       /* Reject some when CC_NO_OVERFLOW is set.  This may be over
55759692Selan          conservative */
55859692Selan       case GT: case LT: case GE: case LE:
55959692Selan         return ! (cc_prev_status.flags & CC_NO_OVERFLOW);
56059692Selan       default:
56159692Selan         return 0;
56259692Selan     }
56359692Selan }
56459692Selan 
56559692Selan /* Output a dbCC; jCC sequence.  Note we do not handle the
56659692Selan    floating point version of this sequence (Fdbcc).  We also
56759692Selan    do not handle alternative conditions when CC_NO_OVERFLOW is
56859692Selan    set.  It is assumed that valid_dbcc_comparison_p will kick
56959692Selan    those out before we get here.  */
57059692Selan 
output_dbcc_and_branch(operands)57159692Selan output_dbcc_and_branch (operands)
57259692Selan      rtx *operands;
57359692Selan {
57459692Selan 
57559692Selan   switch (GET_CODE (operands[3]))
57659692Selan     {
57759692Selan       case EQ:
57859692Selan #ifdef MOTOROLA
57959692Selan         output_asm_insn ("dbeq %0,%l1\n\tjbeq %l2", operands);
58059692Selan #else
58159692Selan         output_asm_insn ("dbeq %0,%l1\n\tjeq %l2", operands);
58259692Selan #endif
58359692Selan         break;
58459692Selan 
58559692Selan       case NE:
58659692Selan #ifdef MOTOROLA
58759692Selan         output_asm_insn ("dbne %0,%l1\n\tjbne %l2", operands);
58859692Selan #else
58959692Selan         output_asm_insn ("dbne %0,%l1\n\tjne %l2", operands);
59059692Selan #endif
59159692Selan         break;
59259692Selan 
59359692Selan       case GT:
59459692Selan #ifdef MOTOROLA
59559692Selan         output_asm_insn ("dbgt %0,%l1\n\tjbgt %l2", operands);
59659692Selan #else
59759692Selan         output_asm_insn ("dbgt %0,%l1\n\tjgt %l2", operands);
59859692Selan #endif
59959692Selan         break;
60059692Selan 
60159692Selan       case GTU:
60259692Selan #ifdef MOTOROLA
60359692Selan         output_asm_insn ("dbhi %0,%l1\n\tjbhi %l2", operands);
60459692Selan #else
60559692Selan         output_asm_insn ("dbhi %0,%l1\n\tjhi %l2", operands);
60659692Selan #endif
60759692Selan         break;
60859692Selan 
60959692Selan       case LT:
61059692Selan #ifdef MOTOROLA
61159692Selan         output_asm_insn ("dblt %0,%l1\n\tjblt %l2", operands);
61259692Selan #else
61359692Selan         output_asm_insn ("dblt %0,%l1\n\tjlt %l2", operands);
61459692Selan #endif
61559692Selan         break;
61659692Selan 
61759692Selan       case LTU:
61859692Selan #ifdef MOTOROLA
61959692Selan         output_asm_insn ("dbcs %0,%l1\n\tjbcs %l2", operands);
62059692Selan #else
62159692Selan         output_asm_insn ("dbcs %0,%l1\n\tjcs %l2", operands);
62259692Selan #endif
62359692Selan         break;
62459692Selan 
62559692Selan       case GE:
62659692Selan #ifdef MOTOROLA
62759692Selan         output_asm_insn ("dbge %0,%l1\n\tjbge %l2", operands);
62859692Selan #else
62959692Selan         output_asm_insn ("dbge %0,%l1\n\tjge %l2", operands);
63059692Selan #endif
63159692Selan         break;
63259692Selan 
63359692Selan       case GEU:
63459692Selan #ifdef MOTOROLA
63559692Selan         output_asm_insn ("dbcc %0,%l1\n\tjbcc %l2", operands);
63659692Selan #else
63759692Selan         output_asm_insn ("dbcc %0,%l1\n\tjcc %l2", operands);
63859692Selan #endif
63959692Selan         break;
64059692Selan 
64159692Selan       case LE:
64259692Selan #ifdef MOTOROLA
64359692Selan         output_asm_insn ("dble %0,%l1\n\tjble %l2", operands);
64459692Selan #else
64559692Selan         output_asm_insn ("dble %0,%l1\n\tjle %l2", operands);
64659692Selan #endif
64759692Selan         break;
64859692Selan 
64959692Selan       case LEU:
65059692Selan #ifdef MOTOROLA
65159692Selan         output_asm_insn ("dbls %0,%l1\n\tjbls %l2", operands);
65259692Selan #else
65359692Selan         output_asm_insn ("dbls %0,%l1\n\tjls %l2", operands);
65459692Selan #endif
65559692Selan         break;
65659692Selan 
65759692Selan       default:
65859692Selan 	abort ();
65959692Selan     }
66059692Selan 
66159692Selan   /* If the decrement is to be done in SImode, then we have
66259692Selan      to compensate for the fact that dbcc decrements in HImode. */
66359692Selan   switch (GET_MODE (operands[0]))
66459692Selan     {
66559692Selan       case SImode:
66659692Selan #ifdef MOTOROLA
66759692Selan         output_asm_insn ("clr%.w %0\n\tsubq%.l %#1,%0\n\tjbpl %l1", operands);
66859692Selan #else
66959692Selan         output_asm_insn ("clr%.w %0\n\tsubq%.l %#1,%0\n\tjpl %l1", operands);
67059692Selan #endif
67159692Selan         break;
67259692Selan 
67359692Selan       case HImode:
67459692Selan         break;
67559692Selan 
67659692Selan       default:
67759692Selan         abort ();
67859692Selan     }
67959692Selan }
68059692Selan 
68159692Selan char *
output_btst(operands,countop,dataop,insn,signpos)68259692Selan output_btst (operands, countop, dataop, insn, signpos)
68359692Selan      rtx *operands;
68459692Selan      rtx countop, dataop;
68559692Selan      rtx insn;
68659692Selan      int signpos;
68759692Selan {
68859692Selan   operands[0] = countop;
68959692Selan   operands[1] = dataop;
69059692Selan 
69159692Selan   if (GET_CODE (countop) == CONST_INT)
69259692Selan     {
69359692Selan       register int count = INTVAL (countop);
69459692Selan       /* If COUNT is bigger than size of storage unit in use,
69559692Selan 	 advance to the containing unit of same size.  */
69659692Selan       if (count > signpos)
69759692Selan 	{
69859692Selan 	  int offset = (count & ~signpos) / 8;
69959692Selan 	  count = count & signpos;
70059692Selan 	  operands[1] = dataop = adj_offsettable_operand (dataop, offset);
70159692Selan 	}
70259692Selan       if (count == signpos)
70359692Selan 	cc_status.flags = CC_NOT_POSITIVE | CC_Z_IN_NOT_N;
70459692Selan       else
70559692Selan 	cc_status.flags = CC_NOT_NEGATIVE | CC_Z_IN_NOT_N;
70659692Selan 
70759692Selan       /* These three statements used to use next_insns_test_no...
70859692Selan 	 but it appears that this should do the same job.  */
70959692Selan       if (count == 31
71059692Selan 	  && next_insn_tests_no_inequality (insn))
71159692Selan 	return "tst%.l %1";
71259692Selan       if (count == 15
71359692Selan 	  && next_insn_tests_no_inequality (insn))
71459692Selan 	return "tst%.w %1";
71559692Selan       if (count == 7
71659692Selan 	  && next_insn_tests_no_inequality (insn))
71759692Selan 	return "tst%.b %1";
71859692Selan 
71959692Selan       cc_status.flags = CC_NOT_NEGATIVE;
72059692Selan     }
72159692Selan   return "btst %0,%1";
72259692Selan }
72359692Selan 
72459692Selan /* Returns 1 if OP is either a symbol reference or a sum of a symbol
72559692Selan    reference and a constant.  */
72659692Selan 
72759692Selan int
symbolic_operand(op,mode)72859692Selan symbolic_operand (op, mode)
72959692Selan      register rtx op;
73059692Selan      enum machine_mode mode;
73159692Selan {
73259692Selan   switch (GET_CODE (op))
73359692Selan     {
73459692Selan     case SYMBOL_REF:
73559692Selan     case LABEL_REF:
73659692Selan       return 1;
73759692Selan 
73859692Selan     case CONST:
73959692Selan       op = XEXP (op, 0);
74059692Selan       return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
74159692Selan 	       || GET_CODE (XEXP (op, 0)) == LABEL_REF)
74259692Selan 	      && GET_CODE (XEXP (op, 1)) == CONST_INT);
74359692Selan 
74459692Selan #if 0 /* Deleted, with corresponding change in m68k.h,
74559692Selan 	 so as to fit the specs.  No CONST_DOUBLE is ever symbolic.  */
74659692Selan     case CONST_DOUBLE:
74759692Selan       return GET_MODE (op) == mode;
74859692Selan #endif
74959692Selan 
75059692Selan     default:
75159692Selan       return 0;
75259692Selan     }
75359692Selan }
75459692Selan 
75559692Selan 
75659692Selan /* Legitimize PIC addresses.  If the address is already
75759692Selan    position-independent, we return ORIG.  Newly generated
75859692Selan    position-independent addresses go to REG.  If we need more
75959692Selan    than one register, we lose.
76059692Selan 
76159692Selan    An address is legitimized by making an indirect reference
76259692Selan    through the Global Offset Table with the name of the symbol
76359692Selan    used as an offset.
76459692Selan 
76559692Selan    The assembler and linker are responsible for placing the
76659692Selan    address of the symbol in the GOT.  The function prologue
76759692Selan    is responsible for initializing a5 to the starting address
76859692Selan    of the GOT.
76959692Selan 
77059692Selan    The assembler is also responsible for translating a symbol name
77159692Selan    into a constant displacement from the start of the GOT.
77259692Selan 
77359692Selan    A quick example may make things a little clearer:
77459692Selan 
77559692Selan    When not generating PIC code to store the value 12345 into _foo
77659692Selan    we would generate the following code:
77759692Selan 
77859692Selan 	movel #12345, _foo
77959692Selan 
78059692Selan    When generating PIC two transformations are made.  First, the compiler
78159692Selan    loads the address of foo into a register.  So the first transformation makes:
78259692Selan 
78359692Selan 	lea	_foo, a0
78459692Selan 	movel   #12345, a0@
78559692Selan 
78659692Selan    The code in movsi will intercept the lea instruction and call this
78759692Selan    routine which will transform the instructions into:
78859692Selan 
78959692Selan 	movel   a5@(_foo:w), a0
79059692Selan 	movel   #12345, a0@
79159692Selan 
79259692Selan 
79359692Selan    That (in a nutshell) is how *all* symbol and label references are
79459692Selan    handled.  */
79559692Selan 
79659692Selan rtx
legitimize_pic_address(orig,mode,reg)79759692Selan legitimize_pic_address (orig, mode, reg)
79859692Selan      rtx orig, reg;
79959692Selan      enum machine_mode mode;
80059692Selan {
80159692Selan   rtx pic_ref = orig;
80259692Selan 
80359692Selan   /* First handle a simple SYMBOL_REF or LABEL_REF */
80459692Selan   if (GET_CODE (orig) == SYMBOL_REF || GET_CODE (orig) == LABEL_REF)
80559692Selan     {
80659692Selan       if (reg == 0)
80759692Selan 	abort ();
80859692Selan 
80959692Selan       pic_ref = gen_rtx (MEM, Pmode,
81059692Selan 			 gen_rtx (PLUS, Pmode,
81159692Selan 				  pic_offset_table_rtx, orig));
81259692Selan       current_function_uses_pic_offset_table = 1;
81359692Selan       RTX_UNCHANGING_P (pic_ref) = 1;
81459692Selan       emit_move_insn (reg, pic_ref);
81559692Selan       return reg;
81659692Selan     }
81759692Selan   else if (GET_CODE (orig) == CONST)
81859692Selan     {
81959692Selan       rtx base, offset;
82059692Selan 
82159692Selan       /* Make sure this is CONST has not already been legitimized */
82259692Selan       if (GET_CODE (XEXP (orig, 0)) == PLUS
82359692Selan 	  && XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx)
82459692Selan 	return orig;
82559692Selan 
82659692Selan       if (reg == 0)
82759692Selan 	abort ();
82859692Selan 
82959692Selan       /* legitimize both operands of the PLUS */
83059692Selan       if (GET_CODE (XEXP (orig, 0)) == PLUS)
83159692Selan 	{
83259692Selan 	  base = legitimize_pic_address (XEXP (XEXP (orig, 0), 0), Pmode, reg);
83359692Selan 	  orig = legitimize_pic_address (XEXP (XEXP (orig, 0), 1), Pmode,
83459692Selan 					 base == reg ? 0 : reg);
83559692Selan 	}
83659692Selan       else abort ();
83759692Selan 
83859692Selan       if (GET_CODE (orig) == CONST_INT)
83959692Selan 	return plus_constant_for_output (base, INTVAL (orig));
84059692Selan       pic_ref = gen_rtx (PLUS, Pmode, base, orig);
84159692Selan       /* Likewise, should we set special REG_NOTEs here?  */
84259692Selan     }
84359692Selan   return pic_ref;
84459692Selan }
84559692Selan 
84659692Selan 
84759692Selan /* Return the best assembler insn template
84859692Selan    for moving operands[1] into operands[0] as a fullword.  */
84959692Selan 
85059692Selan static char *
singlemove_string(operands)85159692Selan singlemove_string (operands)
85259692Selan      rtx *operands;
85359692Selan {
85459692Selan #ifdef SUPPORT_SUN_FPA
85559692Selan   if (FPA_REG_P (operands[0]) || FPA_REG_P (operands[1]))
85659692Selan     return "fpmoves %1,%0";
85759692Selan #endif
85859692Selan   if (DATA_REG_P (operands[0])
85959692Selan       && GET_CODE (operands[1]) == CONST_INT
86059692Selan       && INTVAL (operands[1]) < 128
86159692Selan       && INTVAL (operands[1]) >= -128)
86259692Selan     {
86359692Selan #if defined (MOTOROLA) && !defined (CRDS)
86459692Selan       return "moveq%.l %1,%0";
86559692Selan #else
86659692Selan       return "moveq %1,%0";
86759692Selan #endif
86859692Selan     }
86959692Selan   if (operands[1] != const0_rtx)
87059692Selan     return "move%.l %1,%0";
87159692Selan   if (! ADDRESS_REG_P (operands[0]))
87259692Selan     return "clr%.l %0";
87359692Selan   return "sub%.l %0,%0";
87459692Selan }
87559692Selan 
87659692Selan /* Output assembler code to perform a doubleword move insn
87759692Selan    with operands OPERANDS.  */
87859692Selan 
87959692Selan char *
output_move_double(operands)88059692Selan output_move_double (operands)
88159692Selan      rtx *operands;
88259692Selan {
88359692Selan   enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1;
88459692Selan   rtx latehalf[2];
88559692Selan   rtx addreg0 = 0, addreg1 = 0;
88659692Selan 
88759692Selan   /* First classify both operands.  */
88859692Selan 
88959692Selan   if (REG_P (operands[0]))
89059692Selan     optype0 = REGOP;
89159692Selan   else if (offsettable_memref_p (operands[0]))
89259692Selan     optype0 = OFFSOP;
89359692Selan   else if (GET_CODE (XEXP (operands[0], 0)) == POST_INC)
89459692Selan     optype0 = POPOP;
89559692Selan   else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
89659692Selan     optype0 = PUSHOP;
89759692Selan   else if (GET_CODE (operands[0]) == MEM)
89859692Selan     optype0 = MEMOP;
89959692Selan   else
90059692Selan     optype0 = RNDOP;
90159692Selan 
90259692Selan   if (REG_P (operands[1]))
90359692Selan     optype1 = REGOP;
90459692Selan   else if (CONSTANT_P (operands[1]))
90559692Selan     optype1 = CNSTOP;
90659692Selan   else if (offsettable_memref_p (operands[1]))
90759692Selan     optype1 = OFFSOP;
90859692Selan   else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC)
90959692Selan     optype1 = POPOP;
91059692Selan   else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC)
91159692Selan     optype1 = PUSHOP;
91259692Selan   else if (GET_CODE (operands[1]) == MEM)
91359692Selan     optype1 = MEMOP;
91459692Selan   else
91559692Selan     optype1 = RNDOP;
91659692Selan 
91759692Selan   /* Check for the cases that the operand constraints are not
91859692Selan      supposed to allow to happen.  Abort if we get one,
91959692Selan      because generating code for these cases is painful.  */
92059692Selan 
92159692Selan   if (optype0 == RNDOP || optype1 == RNDOP)
92259692Selan     abort ();
92359692Selan 
92459692Selan   /* If one operand is decrementing and one is incrementing
92559692Selan      decrement the former register explicitly
92659692Selan      and change that operand into ordinary indexing.  */
92759692Selan 
92859692Selan   if (optype0 == PUSHOP && optype1 == POPOP)
92959692Selan     {
93059692Selan       operands[0] = XEXP (XEXP (operands[0], 0), 0);
93159692Selan       output_asm_insn ("subq%.l %#8,%0", operands);
93259692Selan       operands[0] = gen_rtx (MEM, DImode, operands[0]);
93359692Selan       optype0 = OFFSOP;
93459692Selan     }
93559692Selan   if (optype0 == POPOP && optype1 == PUSHOP)
93659692Selan     {
93759692Selan       operands[1] = XEXP (XEXP (operands[1], 0), 0);
93859692Selan       output_asm_insn ("subq%.l %#8,%1", operands);
93959692Selan       operands[1] = gen_rtx (MEM, DImode, operands[1]);
94059692Selan       optype1 = OFFSOP;
94159692Selan     }
94259692Selan 
94359692Selan   /* If an operand is an unoffsettable memory ref, find a register
94459692Selan      we can increment temporarily to make it refer to the second word.  */
94559692Selan 
94659692Selan   if (optype0 == MEMOP)
94759692Selan     addreg0 = find_addr_reg (XEXP (operands[0], 0));
94859692Selan 
94959692Selan   if (optype1 == MEMOP)
95059692Selan     addreg1 = find_addr_reg (XEXP (operands[1], 0));
95159692Selan 
95259692Selan   /* Ok, we can do one word at a time.
95359692Selan      Normally we do the low-numbered word first,
95459692Selan      but if either operand is autodecrementing then we
95559692Selan      do the high-numbered word first.
95659692Selan 
95759692Selan      In either case, set up in LATEHALF the operands to use
95859692Selan      for the high-numbered word and in some cases alter the
95959692Selan      operands in OPERANDS to be suitable for the low-numbered word.  */
96059692Selan 
96159692Selan   if (optype0 == REGOP)
96259692Selan     latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
96359692Selan   else if (optype0 == OFFSOP)
96459692Selan     latehalf[0] = adj_offsettable_operand (operands[0], 4);
96559692Selan   else
96659692Selan     latehalf[0] = operands[0];
96759692Selan 
96859692Selan   if (optype1 == REGOP)
96959692Selan     latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1);
97059692Selan   else if (optype1 == OFFSOP)
97159692Selan     latehalf[1] = adj_offsettable_operand (operands[1], 4);
97259692Selan   else if (optype1 == CNSTOP)
97359692Selan     split_double (operands[1], &operands[1], &latehalf[1]);
97459692Selan   else
97559692Selan     latehalf[1] = operands[1];
97659692Selan 
97759692Selan   /* If insn is effectively movd N(sp),-(sp) then we will do the
97859692Selan      high word first.  We should use the adjusted operand 1 (which is N+4(sp))
97959692Selan      for the low word as well, to compensate for the first decrement of sp.  */
98059692Selan   if (optype0 == PUSHOP
98159692Selan       && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM
98259692Selan       && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1]))
98359692Selan     operands[1] = latehalf[1];
98459692Selan 
98559692Selan   /* If one or both operands autodecrementing,
98659692Selan      do the two words, high-numbered first.  */
98759692Selan 
98859692Selan   /* Likewise,  the first move would clobber the source of the second one,
98959692Selan      do them in the other order.  This happens only for registers;
99059692Selan      such overlap can't happen in memory unless the user explicitly
99159692Selan      sets it up, and that is an undefined circumstance.  */
99259692Selan 
99359692Selan   if (optype0 == PUSHOP || optype1 == PUSHOP
99459692Selan       || (optype0 == REGOP && optype1 == REGOP
99559692Selan 	  && REGNO (operands[0]) == REGNO (latehalf[1])))
99659692Selan     {
99759692Selan       /* Make any unoffsettable addresses point at high-numbered word.  */
99859692Selan       if (addreg0)
99959692Selan 	output_asm_insn ("addql %#4,%0", &addreg0);
100059692Selan       if (addreg1)
100159692Selan 	output_asm_insn ("addql %#4,%0", &addreg1);
100259692Selan 
100359692Selan       /* Do that word.  */
100459692Selan       output_asm_insn (singlemove_string (latehalf), latehalf);
100559692Selan 
100659692Selan       /* Undo the adds we just did.  */
100759692Selan       if (addreg0)
100859692Selan 	output_asm_insn ("subql %#4,%0", &addreg0);
100959692Selan       if (addreg1)
101059692Selan 	output_asm_insn ("subql %#4,%0", &addreg1);
101159692Selan 
101259692Selan       /* Do low-numbered word.  */
101359692Selan       return singlemove_string (operands);
101459692Selan     }
101559692Selan 
101659692Selan   /* Normal case: do the two words, low-numbered first.  */
101759692Selan 
101859692Selan   output_asm_insn (singlemove_string (operands), operands);
101959692Selan 
102059692Selan   /* Make any unoffsettable addresses point at high-numbered word.  */
102159692Selan   if (addreg0)
102259692Selan     output_asm_insn ("addql %#4,%0", &addreg0);
102359692Selan   if (addreg1)
102459692Selan     output_asm_insn ("addql %#4,%0", &addreg1);
102559692Selan 
102659692Selan   /* Do that word.  */
102759692Selan   output_asm_insn (singlemove_string (latehalf), latehalf);
102859692Selan 
102959692Selan   /* Undo the adds we just did.  */
103059692Selan   if (addreg0)
103159692Selan     output_asm_insn ("subql %#4,%0", &addreg0);
103259692Selan   if (addreg1)
103359692Selan     output_asm_insn ("subql %#4,%0", &addreg1);
103459692Selan 
103559692Selan   return "";
103659692Selan }
103759692Selan 
103859692Selan /* Return a REG that occurs in ADDR with coefficient 1.
103959692Selan    ADDR can be effectively incremented by incrementing REG.  */
104059692Selan 
104159692Selan static rtx
find_addr_reg(addr)104259692Selan find_addr_reg (addr)
104359692Selan      rtx addr;
104459692Selan {
104559692Selan   while (GET_CODE (addr) == PLUS)
104659692Selan     {
104759692Selan       if (GET_CODE (XEXP (addr, 0)) == REG)
104859692Selan 	addr = XEXP (addr, 0);
104959692Selan       else if (GET_CODE (XEXP (addr, 1)) == REG)
105059692Selan 	addr = XEXP (addr, 1);
105159692Selan       else if (CONSTANT_P (XEXP (addr, 0)))
105259692Selan 	addr = XEXP (addr, 1);
105359692Selan       else if (CONSTANT_P (XEXP (addr, 1)))
105459692Selan 	addr = XEXP (addr, 0);
105559692Selan       else
105659692Selan 	abort ();
105759692Selan     }
105859692Selan   if (GET_CODE (addr) == REG)
105959692Selan     return addr;
106059692Selan   abort ();
106159692Selan }
106259692Selan 
106359692Selan /* Store in cc_status the expressions that the condition codes will
106459692Selan    describe after execution of an instruction whose pattern is EXP.
106559692Selan    Do not alter them if the instruction would not alter the cc's.  */
106659692Selan 
106759692Selan /* On the 68000, all the insns to store in an address register fail to
106859692Selan    set the cc's.  However, in some cases these instructions can make it
106959692Selan    possibly invalid to use the saved cc's.  In those cases we clear out
107059692Selan    some or all of the saved cc's so they won't be used.  */
107159692Selan 
notice_update_cc(exp,insn)107259692Selan notice_update_cc (exp, insn)
107359692Selan      rtx exp;
107459692Selan      rtx insn;
107559692Selan {
107659692Selan   /* If the cc is being set from the fpa and the expression is not an
107759692Selan      explicit floating point test instruction (which has code to deal with
107859692Selan      this), reinit the CC.  */
107959692Selan   if (((cc_status.value1 && FPA_REG_P (cc_status.value1))
108059692Selan        || (cc_status.value2 && FPA_REG_P (cc_status.value2)))
108159692Selan       && !(GET_CODE (exp) == PARALLEL
108259692Selan 	   && GET_CODE (XVECEXP (exp, 0, 0)) == SET
108359692Selan 	   && XEXP (XVECEXP (exp, 0, 0), 0) == cc0_rtx))
108459692Selan     {
108559692Selan       CC_STATUS_INIT;
108659692Selan     }
108759692Selan   else if (GET_CODE (exp) == SET)
108859692Selan     {
108959692Selan       if (GET_CODE (SET_SRC (exp)) == CALL)
109059692Selan 	{
109159692Selan 	  CC_STATUS_INIT;
109259692Selan 	}
109359692Selan       else if (ADDRESS_REG_P (SET_DEST (exp)))
109459692Selan 	{
109559692Selan 	  if (cc_status.value1
109659692Selan 	      && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value1))
109759692Selan 	    cc_status.value1 = 0;
109859692Selan 	  if (cc_status.value2
109959692Selan 	      && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value2))
110059692Selan 	    cc_status.value2 = 0;
110159692Selan 	}
110259692Selan       else if (!FP_REG_P (SET_DEST (exp))
110359692Selan 	       && SET_DEST (exp) != cc0_rtx
110459692Selan 	       && (FP_REG_P (SET_SRC (exp))
110559692Selan 		   || GET_CODE (SET_SRC (exp)) == FIX
110659692Selan 		   || GET_CODE (SET_SRC (exp)) == FLOAT_TRUNCATE
110759692Selan 		   || GET_CODE (SET_SRC (exp)) == FLOAT_EXTEND))
110859692Selan 	{
110959692Selan 	  CC_STATUS_INIT;
111059692Selan 	}
111159692Selan       /* A pair of move insns doesn't produce a useful overall cc.  */
111259692Selan       else if (!FP_REG_P (SET_DEST (exp))
111359692Selan 	       && !FP_REG_P (SET_SRC (exp))
111459692Selan 	       && GET_MODE_SIZE (GET_MODE (SET_SRC (exp))) > 4
111559692Selan 	       && (GET_CODE (SET_SRC (exp)) == REG
111659692Selan 		   || GET_CODE (SET_SRC (exp)) == MEM
111759692Selan 		   || GET_CODE (SET_SRC (exp)) == CONST_DOUBLE))
111859692Selan 	{
111959692Selan 	  CC_STATUS_INIT;
112059692Selan 	}
112159692Selan       else if (GET_CODE (SET_SRC (exp)) == CALL)
112259692Selan 	{
112359692Selan 	  CC_STATUS_INIT;
112459692Selan 	}
112559692Selan       else if (XEXP (exp, 0) != pc_rtx)
112659692Selan 	{
112759692Selan 	  cc_status.flags = 0;
112859692Selan 	  cc_status.value1 = XEXP (exp, 0);
112959692Selan 	  cc_status.value2 = XEXP (exp, 1);
113059692Selan 	}
113159692Selan     }
113259692Selan   else if (GET_CODE (exp) == PARALLEL
113359692Selan 	   && GET_CODE (XVECEXP (exp, 0, 0)) == SET)
113459692Selan     {
113559692Selan       if (ADDRESS_REG_P (XEXP (XVECEXP (exp, 0, 0), 0)))
113659692Selan 	CC_STATUS_INIT;
113759692Selan       else if (XEXP (XVECEXP (exp, 0, 0), 0) != pc_rtx)
113859692Selan 	{
113959692Selan 	  cc_status.flags = 0;
114059692Selan 	  cc_status.value1 = XEXP (XVECEXP (exp, 0, 0), 0);
114159692Selan 	  cc_status.value2 = XEXP (XVECEXP (exp, 0, 0), 1);
114259692Selan 	}
114359692Selan     }
114459692Selan   else
114559692Selan     CC_STATUS_INIT;
114659692Selan   if (cc_status.value2 != 0
114759692Selan       && ADDRESS_REG_P (cc_status.value2)
114859692Selan       && GET_MODE (cc_status.value2) == QImode)
114959692Selan     CC_STATUS_INIT;
115059692Selan   if (cc_status.value2 != 0
115159692Selan       && !(cc_status.value1 && FPA_REG_P (cc_status.value1)))
115259692Selan     switch (GET_CODE (cc_status.value2))
115359692Selan       {
115459692Selan       case PLUS: case MINUS: case MULT:
115559692Selan       case DIV: case UDIV: case MOD: case UMOD: case NEG:
115659692Selan       case ASHIFT: case LSHIFT: case ASHIFTRT: case LSHIFTRT:
115759692Selan       case ROTATE: case ROTATERT:
115859692Selan 	if (GET_MODE (cc_status.value2) != VOIDmode)
115959692Selan 	  cc_status.flags |= CC_NO_OVERFLOW;
116059692Selan 	break;
116159692Selan       case ZERO_EXTEND:
116259692Selan 	/* (SET r1 (ZERO_EXTEND r2)) on this machine
116359692Selan 	   ends with a move insn moving r2 in r2's mode.
116459692Selan 	   Thus, the cc's are set for r2.
116559692Selan 	   This can set N bit spuriously. */
116659692Selan 	cc_status.flags |= CC_NOT_NEGATIVE;
116759692Selan       }
116859692Selan   if (cc_status.value1 && GET_CODE (cc_status.value1) == REG
116959692Selan       && cc_status.value2
117059692Selan       && reg_overlap_mentioned_p (cc_status.value1, cc_status.value2))
117159692Selan     cc_status.value2 = 0;
117259692Selan   if (((cc_status.value1 && FP_REG_P (cc_status.value1))
117359692Selan        || (cc_status.value2 && FP_REG_P (cc_status.value2)))
117459692Selan       && !((cc_status.value1 && FPA_REG_P (cc_status.value1))
117559692Selan 	   || (cc_status.value2 && FPA_REG_P (cc_status.value2))))
117659692Selan     cc_status.flags = CC_IN_68881;
117759692Selan }
117859692Selan 
117959692Selan char *
output_move_const_double(operands)118059692Selan output_move_const_double (operands)
118159692Selan      rtx *operands;
118259692Selan {
118359692Selan #ifdef SUPPORT_SUN_FPA
118459692Selan   if (TARGET_FPA && FPA_REG_P (operands[0]))
118559692Selan     {
118659692Selan       int code = standard_sun_fpa_constant_p (operands[1]);
118759692Selan 
118859692Selan       if (code != 0)
118959692Selan 	{
119059692Selan 	  static char buf[40];
119159692Selan 
119259692Selan 	  sprintf (buf, "fpmove%%.d %%%%%d,%%0", code & 0x1ff);
119359692Selan 	  return buf;
119459692Selan 	}
119559692Selan       return "fpmove%.d %1,%0";
119659692Selan     }
119759692Selan   else
119859692Selan #endif
119959692Selan     {
120059692Selan       int code = standard_68881_constant_p (operands[1]);
120159692Selan 
120259692Selan       if (code != 0)
120359692Selan 	{
120459692Selan 	  static char buf[40];
120559692Selan 
120659692Selan 	  sprintf (buf, "fmovecr %%#0x%x,%%0", code & 0xff);
120759692Selan 	  return buf;
120859692Selan 	}
120959692Selan       return "fmove%.d %1,%0";
121059692Selan     }
121159692Selan }
121259692Selan 
121359692Selan char *
output_move_const_single(operands)121459692Selan output_move_const_single (operands)
121559692Selan      rtx *operands;
121659692Selan {
121759692Selan #ifdef SUPPORT_SUN_FPA
121859692Selan   if (TARGET_FPA)
121959692Selan     {
122059692Selan       int code = standard_sun_fpa_constant_p (operands[1]);
122159692Selan 
122259692Selan       if (code != 0)
122359692Selan 	{
122459692Selan 	  static char buf[40];
122559692Selan 
122659692Selan 	  sprintf (buf, "fpmove%%.s %%%%%d,%%0", code & 0x1ff);
122759692Selan 	  return buf;
122859692Selan 	}
122959692Selan       return "fpmove%.s %1,%0";
123059692Selan     }
123159692Selan   else
123259692Selan #endif /* defined SUPPORT_SUN_FPA */
123359692Selan     {
123459692Selan       int code = standard_68881_constant_p (operands[1]);
123559692Selan 
123659692Selan       if (code != 0)
123759692Selan 	{
123859692Selan 	  static char buf[40];
123959692Selan 
124059692Selan 	  sprintf (buf, "fmovecr %%#0x%x,%%0", code & 0xff);
124159692Selan 	  return buf;
124259692Selan 	}
124359692Selan       return "fmove%.s %f1,%0";
124459692Selan     }
124559692Selan }
124659692Selan 
124759692Selan /* Return nonzero if X, a CONST_DOUBLE, has a value that we can get
124859692Selan    from the "fmovecr" instruction.
124959692Selan    The value, anded with 0xff, gives the code to use in fmovecr
125059692Selan    to get the desired constant.  */
125159692Selan 
125259692Selan /* ??? This code should be fixed for cross-compilation. */
125359692Selan 
125459692Selan int
standard_68881_constant_p(x)125559692Selan standard_68881_constant_p (x)
125659692Selan      rtx x;
125759692Selan {
125859692Selan   register double d;
125959692Selan 
126059692Selan   /* fmovecr must be emulated on the 68040, so it shouldn't be used at all. */
126159692Selan   if (TARGET_68040)
126259692Selan     return 0;
126359692Selan 
126459692Selan #if HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT
126559692Selan   if (! flag_pretend_float)
126659692Selan     return 0;
126759692Selan #endif
126859692Selan 
126959692Selan   REAL_VALUE_FROM_CONST_DOUBLE (d, x);
127059692Selan 
127159692Selan   if (d == 0)
127259692Selan     return 0x0f;
127359692Selan   /* Note: there are various other constants available
127459692Selan      but it is a nuisance to put in their values here.  */
127559692Selan   if (d == 1)
127659692Selan     return 0x32;
127759692Selan   if (d == 10)
127859692Selan     return 0x33;
127959692Selan   if (d == 100)
128059692Selan     return 0x34;
128159692Selan   if (d == 10000)
128259692Selan     return 0x35;
128359692Selan   if (d == 1e8)
128459692Selan     return 0x36;
128559692Selan   if (GET_MODE (x) == SFmode)
128659692Selan     return 0;
128759692Selan   if (d == 1e16)
128859692Selan     return 0x37;
128959692Selan   /* larger powers of ten in the constants ram are not used
129059692Selan      because they are not equal to a `double' C constant.  */
129159692Selan   return 0;
129259692Selan }
129359692Selan 
129459692Selan /* If X is a floating-point constant, return the logarithm of X base 2,
129559692Selan    or 0 if X is not a power of 2.  */
129659692Selan 
129759692Selan int
floating_exact_log2(x)129859692Selan floating_exact_log2 (x)
129959692Selan      rtx x;
130059692Selan {
130159692Selan   register double d, d1;
130259692Selan   int i;
130359692Selan 
130459692Selan #if HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT
130559692Selan   if (! flag_pretend_float)
130659692Selan     return 0;
130759692Selan #endif
130859692Selan 
130959692Selan   REAL_VALUE_FROM_CONST_DOUBLE (d, x);
131059692Selan 
131159692Selan   if (! (d > 0))
131259692Selan     return 0;
131359692Selan 
131459692Selan   for (d1 = 1.0, i = 0; d1 < d; d1 *= 2.0, i++)
131559692Selan     ;
131659692Selan 
131759692Selan   if (d == d1)
131859692Selan     return i;
131959692Selan 
132059692Selan   return 0;
132159692Selan }
132259692Selan 
132359692Selan #ifdef SUPPORT_SUN_FPA
132459692Selan /* Return nonzero if X, a CONST_DOUBLE, has a value that we can get
132559692Selan    from the Sun FPA's constant RAM.
132659692Selan    The value returned, anded with 0x1ff, gives the code to use in fpmove
132759692Selan    to get the desired constant. */
132859692Selan #define S_E (2.718281745910644531)
132959692Selan #define D_E (2.718281828459045091)
133059692Selan #define S_PI (3.141592741012573242)
133159692Selan #define D_PI (3.141592653589793116)
133259692Selan #define S_SQRT2 (1.414213538169860840)
133359692Selan #define D_SQRT2 (1.414213562373095145)
133459692Selan #define S_LOG2ofE (1.442695021629333496)
133559692Selan #define D_LOG2ofE (1.442695040888963387)
133659692Selan #define S_LOG2of10 (3.321928024291992188)
133759692Selan #define D_LOG2of10 (3.321928024887362182)
133859692Selan #define S_LOGEof2 (0.6931471824645996094)
133959692Selan #define D_LOGEof2 (0.6931471805599452862)
134059692Selan #define S_LOGEof10 (2.302585124969482442)
134159692Selan #define D_LOGEof10 (2.302585092994045901)
134259692Selan #define S_LOG10of2 (0.3010300099849700928)
134359692Selan #define D_LOG10of2 (0.3010299956639811980)
134459692Selan #define S_LOG10ofE (0.4342944920063018799)
134559692Selan #define D_LOG10ofE (0.4342944819032518167)
134659692Selan 
134759692Selan /* This code should be fixed for cross-compilation. */
134859692Selan 
134959692Selan int
standard_sun_fpa_constant_p(x)135059692Selan standard_sun_fpa_constant_p (x)
135159692Selan      rtx x;
135259692Selan {
135359692Selan   register double d;
135459692Selan 
135559692Selan #if HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT
135659692Selan   if (! flag_pretend_float)
135759692Selan     return 0;
135859692Selan #endif
135959692Selan 
136059692Selan   REAL_VALUE_FROM_CONST_DOUBLE (d, x);
136159692Selan 
136259692Selan   if (d == 0.0)
136359692Selan     return 0x200;		/* 0 once 0x1ff is anded with it */
136459692Selan   if (d == 1.0)
136559692Selan     return 0xe;
136659692Selan   if (d == 0.5)
136759692Selan     return 0xf;
136859692Selan   if (d == -1.0)
136959692Selan     return 0x10;
137059692Selan   if (d == 2.0)
137159692Selan     return 0x11;
137259692Selan   if (d == 3.0)
137359692Selan     return 0xB1;
137459692Selan   if (d == 4.0)
137559692Selan     return 0x12;
137659692Selan   if (d == 8.0)
137759692Selan     return 0x13;
137859692Selan   if (d == 0.25)
137959692Selan     return 0x15;
138059692Selan   if (d == 0.125)
138159692Selan     return 0x16;
138259692Selan   if (d == 10.0)
138359692Selan     return 0x17;
138459692Selan   if (d == -(1.0/2.0))
138559692Selan     return 0x2E;
138659692Selan 
138759692Selan /*
138859692Selan  * Stuff that looks different if it's single or double
138959692Selan  */
139059692Selan   if (GET_MODE (x) == SFmode)
139159692Selan     {
139259692Selan       if (d == S_E)
139359692Selan 	return 0x8;
139459692Selan       if (d == (2*S_PI))
139559692Selan 	return 0x9;
139659692Selan       if (d == S_PI)
139759692Selan 	return 0xA;
139859692Selan       if (d == (S_PI / 2.0))
139959692Selan 	return 0xB;
140059692Selan       if (d == S_SQRT2)
140159692Selan 	return 0xC;
140259692Selan       if (d == (1.0 / S_SQRT2))
140359692Selan 	return 0xD;
140459692Selan       /* Large powers of 10 in the constant
140559692Selan 	 ram are not used because they are
140659692Selan 	 not equal to a C double constant  */
140759692Selan       if (d == -(S_PI / 2.0))
140859692Selan 	return 0x27;
140959692Selan       if (d == S_LOG2ofE)
141059692Selan 	return 0x28;
141159692Selan       if (d == S_LOG2of10)
141259692Selan 	return 0x29;
141359692Selan       if (d == S_LOGEof2)
141459692Selan 	return 0x2A;
141559692Selan       if (d == S_LOGEof10)
141659692Selan 	return 0x2B;
141759692Selan       if (d == S_LOG10of2)
141859692Selan 	return 0x2C;
141959692Selan       if (d == S_LOG10ofE)
142059692Selan 	return 0x2D;
142159692Selan     }
142259692Selan   else
142359692Selan     {
142459692Selan       if (d == D_E)
142559692Selan 	return 0x8;
142659692Selan       if (d == (2*D_PI))
142759692Selan 	return 0x9;
142859692Selan       if (d == D_PI)
142959692Selan 	return 0xA;
143059692Selan       if (d == (D_PI / 2.0))
143159692Selan 	return 0xB;
143259692Selan       if (d == D_SQRT2)
143359692Selan 	return 0xC;
143459692Selan       if (d == (1.0 / D_SQRT2))
143559692Selan 	return 0xD;
143659692Selan       /* Large powers of 10 in the constant
143759692Selan 	 ram are not used because they are
143859692Selan 	 not equal to a C double constant  */
143959692Selan       if (d == -(D_PI / 2.0))
144059692Selan 	return 0x27;
144159692Selan       if (d == D_LOG2ofE)
144259692Selan 	return 0x28;
144359692Selan       if (d == D_LOG2of10)
144459692Selan 	return 0x29;
144559692Selan       if (d == D_LOGEof2)
144659692Selan 	return 0x2A;
144759692Selan       if (d == D_LOGEof10)
144859692Selan 	return 0x2B;
144959692Selan       if (d == D_LOG10of2)
145059692Selan 	return 0x2C;
145159692Selan       if (d == D_LOG10ofE)
145259692Selan 	return 0x2D;
145359692Selan     }
145459692Selan   return 0x0;
145559692Selan }
145659692Selan #endif /* define SUPPORT_SUN_FPA */
145759692Selan 
145859692Selan /* A C compound statement to output to stdio stream STREAM the
145959692Selan    assembler syntax for an instruction operand X.  X is an RTL
146059692Selan    expression.
146159692Selan 
146259692Selan    CODE is a value that can be used to specify one of several ways
146359692Selan    of printing the operand.  It is used when identical operands
146459692Selan    must be printed differently depending on the context.  CODE
146559692Selan    comes from the `%' specification that was used to request
146659692Selan    printing of the operand.  If the specification was just `%DIGIT'
146759692Selan    then CODE is 0; if the specification was `%LTR DIGIT' then CODE
146859692Selan    is the ASCII code for LTR.
146959692Selan 
147059692Selan    If X is a register, this macro should print the register's name.
147159692Selan    The names can be found in an array `reg_names' whose type is
147259692Selan    `char *[]'.  `reg_names' is initialized from `REGISTER_NAMES'.
147359692Selan 
147459692Selan    When the machine description has a specification `%PUNCT' (a `%'
147559692Selan    followed by a punctuation character), this macro is called with
147659692Selan    a null pointer for X and the punctuation character for CODE.
147759692Selan 
147859692Selan    The m68k specific codes are:
147959692Selan 
148059692Selan    '.' for dot needed in Motorola-style opcode names.
148159692Selan    '-' for an operand pushing on the stack:
148259692Selan        sp@-, -(sp) or -(%sp) depending on the style of syntax.
148359692Selan    '+' for an operand pushing on the stack:
148459692Selan        sp@+, (sp)+ or (%sp)+ depending on the style of syntax.
148559692Selan    '@' for a reference to the top word on the stack:
148659692Selan        sp@, (sp) or (%sp) depending on the style of syntax.
148759692Selan    '#' for an immediate operand prefix (# in MIT and Motorola syntax
148859692Selan        but & in SGS syntax).
148959692Selan    '!' for the cc register (used in an `and to cc' insn).
149059692Selan    '$' for the letter `s' in an op code, but only on the 68040.
149159692Selan    '&' for the letter `d' in an op code, but only on the 68040.
1492*60382Selan    '/' for register prefix needed by longlong.h.
149359692Selan 
149459692Selan    'b' for byte insn (no effect, on the Sun; this is for the ISI).
149559692Selan    'd' to force memory addressing to be absolute, not relative.
149659692Selan    'f' for float insn (print a CONST_DOUBLE as a float rather than in hex)
149759692Selan    'w' for FPA insn (print a CONST_DOUBLE as a SunFPA constant rather
149859692Selan        than directly).  Second part of 'y' below.
149959692Selan    'x' for float insn (print a CONST_DOUBLE as a float rather than in hex),
150059692Selan        or print pair of registers as rx:ry.
150159692Selan    'y' for a FPA insn (print pair of registers as rx:ry).  This also outputs
150259692Selan        CONST_DOUBLE's as SunFPA constant RAM registers if
150359692Selan        possible, so it should not be used except for the SunFPA.
150459692Selan 
150559692Selan    */
150659692Selan 
150759692Selan void
print_operand(file,op,letter)150859692Selan print_operand (file, op, letter)
150959692Selan      FILE *file;		/* file to write to */
151059692Selan      rtx op;			/* operand to print */
151159692Selan      int letter;		/* %<letter> or 0 */
151259692Selan {
151359692Selan   int i;
151459692Selan 
151559692Selan   if (letter == '.')
151659692Selan     {
151759692Selan #ifdef MOTOROLA
151859692Selan       asm_fprintf (file, ".");
151959692Selan #endif
152059692Selan     }
152159692Selan   else if (letter == '#')
152259692Selan     {
152359692Selan       asm_fprintf (file, "%0I");
152459692Selan     }
152559692Selan   else if (letter == '-')
152659692Selan     {
152759692Selan #ifdef MOTOROLA
152859692Selan       asm_fprintf (file, "-(%Rsp)");
152959692Selan #else
153059692Selan       asm_fprintf (file, "%Rsp@-");
153159692Selan #endif
153259692Selan     }
153359692Selan   else if (letter == '+')
153459692Selan     {
153559692Selan #ifdef MOTOROLA
153659692Selan       asm_fprintf (file, "(%Rsp)+");
153759692Selan #else
153859692Selan       asm_fprintf (file, "%Rsp@+");
153959692Selan #endif
154059692Selan     }
154159692Selan   else if (letter == '@')
154259692Selan     {
154359692Selan #ifdef MOTOROLA
154459692Selan       asm_fprintf (file, "(%Rsp)");
154559692Selan #else
154659692Selan       asm_fprintf (file, "%Rsp@");
154759692Selan #endif
154859692Selan     }
154959692Selan   else if (letter == '!')
155059692Selan     {
155159692Selan       asm_fprintf (file, "%Rfpcr");
155259692Selan     }
155359692Selan   else if (letter == '$')
155459692Selan     {
155559692Selan       if (TARGET_68040_ONLY)
155659692Selan 	{
155759692Selan 	  fprintf (file, "s");
155859692Selan 	}
155959692Selan     }
156059692Selan   else if (letter == '&')
156159692Selan     {
156259692Selan       if (TARGET_68040_ONLY)
156359692Selan 	{
156459692Selan 	  fprintf (file, "d");
156559692Selan 	}
156659692Selan     }
1567*60382Selan   else if (letter == '/')
1568*60382Selan     {
1569*60382Selan       asm_fprintf (file, "%R");
1570*60382Selan     }
157159692Selan   else if (GET_CODE (op) == REG)
157259692Selan     {
157359692Selan       if (REGNO (op) < 16
157459692Selan 	  && (letter == 'y' || letter == 'x')
157559692Selan 	  && GET_MODE (op) == DFmode)
157659692Selan 	{
157759692Selan 	  fprintf (file, "%s:%s", reg_names[REGNO (op)],
157859692Selan 		   reg_names[REGNO (op)+1]);
157959692Selan 	}
158059692Selan       else
158159692Selan 	{
158259692Selan 	  fprintf (file, "%s", reg_names[REGNO (op)]);
158359692Selan 	}
158459692Selan     }
158559692Selan   else if (GET_CODE (op) == MEM)
158659692Selan     {
158759692Selan       output_address (XEXP (op, 0));
158859692Selan       if (letter == 'd' && ! TARGET_68020
158959692Selan 	  && CONSTANT_ADDRESS_P (XEXP (op, 0))
159059692Selan 	  && !(GET_CODE (XEXP (op, 0)) == CONST_INT
159159692Selan 	       && INTVAL (XEXP (op, 0)) < 0x8000
159259692Selan 	       && INTVAL (XEXP (op, 0)) >= -0x8000))
159359692Selan 	{
159459692Selan 	  fprintf (file, ":l");
159559692Selan 	}
159659692Selan     }
159759692Selan #ifdef SUPPORT_SUN_FPA
159859692Selan   else if ((letter == 'y' || letter == 'w')
159959692Selan 	   && GET_CODE (op) == CONST_DOUBLE
160059692Selan 	   && (i = standard_sun_fpa_constant_p (op)))
160159692Selan     {
160259692Selan       fprintf (file, "%%%d", i & 0x1ff);
160359692Selan     }
160459692Selan #endif
160559692Selan   else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == SFmode)
160659692Selan     {
160759692Selan       double d;
160859692Selan       union { float f; int i; } u1;
160959692Selan       REAL_VALUE_FROM_CONST_DOUBLE (d, op);
161059692Selan       u1.f = d;
161159692Selan       PRINT_OPERAND_PRINT_FLOAT (letter, file);
161259692Selan     }
161359692Selan   else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) != DImode)
161459692Selan     {
161559692Selan       double d;
161659692Selan       REAL_VALUE_FROM_CONST_DOUBLE (d, op);
161759692Selan       ASM_OUTPUT_DOUBLE_OPERAND (file, d);
161859692Selan     }
161959692Selan   else
162059692Selan     {
162159692Selan       asm_fprintf (file, "%0I"); output_addr_const (file, op);
162259692Selan     }
162359692Selan }
162459692Selan 
162559692Selan 
162659692Selan /* A C compound statement to output to stdio stream STREAM the
162759692Selan    assembler syntax for an instruction operand that is a memory
162859692Selan    reference whose address is ADDR.  ADDR is an RTL expression.
162959692Selan 
163059692Selan    Note that this contains a kludge that knows that the only reason
163159692Selan    we have an address (plus (label_ref...) (reg...)) when not generating
163259692Selan    PIC code is in the insn before a tablejump, and we know that m68k.md
163359692Selan    generates a label LInnn: on such an insn.
163459692Selan 
163559692Selan    It is possible for PIC to generate a (plus (label_ref...) (reg...))
163659692Selan    and we handle that just like we would a (plus (symbol_ref...) (reg...)).
163759692Selan 
163859692Selan    Some SGS assemblers have a bug such that "Lnnn-LInnn-2.b(pc,d0.l*2)"
163959692Selan    fails to assemble.  Luckily "Lnnn(pc,d0.l*2)" produces the results
164059692Selan    we want.  This difference can be accommodated by using an assembler
164159692Selan    define such "LDnnn" to be either "Lnnn-LInnn-2.b", "Lnnn", or any other
164259692Selan    string, as necessary.  This is accomplished via the ASM_OUTPUT_CASE_END
164359692Selan    macro.  See m68ksgs.h for an example; for versions without the bug.
164459692Selan 
164559692Selan    They also do not like things like "pea 1.w", so we simple leave off
164659692Selan    the .w on small constants.
164759692Selan 
164859692Selan    This routine is responsible for distinguishing between -fpic and -fPIC
164959692Selan    style relocations in an address.  When generating -fpic code the
165059692Selan    offset is output in word mode (eg movel a5@(_foo:w), a0).  When generating
165159692Selan    -fPIC code the offset is output in long mode (eg movel a5@(_foo:l), a0) */
165259692Selan 
165359692Selan void
print_operand_address(file,addr)165459692Selan print_operand_address (file, addr)
165559692Selan      FILE *file;
165659692Selan      rtx addr;
165759692Selan {
165859692Selan   register rtx reg1, reg2, breg, ireg;
165959692Selan   rtx offset;
166059692Selan 
166159692Selan   switch (GET_CODE (addr))
166259692Selan     {
166359692Selan       case REG:
166459692Selan #ifdef MOTOROLA
166559692Selan 	fprintf (file, "(%s)", reg_names[REGNO (addr)]);
166659692Selan #else
166759692Selan 	fprintf (file, "%s@", reg_names[REGNO (addr)]);
166859692Selan #endif
166959692Selan 	break;
167059692Selan       case PRE_DEC:
167159692Selan #ifdef MOTOROLA
167259692Selan 	fprintf (file, "-(%s)", reg_names[REGNO (XEXP (addr, 0))]);
167359692Selan #else
167459692Selan 	fprintf (file, "%s@-", reg_names[REGNO (XEXP (addr, 0))]);
167559692Selan #endif
167659692Selan 	break;
167759692Selan       case POST_INC:
167859692Selan #ifdef MOTOROLA
167959692Selan 	fprintf (file, "(%s)+", reg_names[REGNO (XEXP (addr, 0))]);
168059692Selan #else
168159692Selan 	fprintf (file, "%s@+", reg_names[REGNO (XEXP (addr, 0))]);
168259692Selan #endif
168359692Selan 	break;
168459692Selan       case PLUS:
168559692Selan 	reg1 = reg2 = ireg = breg = offset = 0;
168659692Selan 	if (CONSTANT_ADDRESS_P (XEXP (addr, 0)))
168759692Selan 	  {
168859692Selan 	    offset = XEXP (addr, 0);
168959692Selan 	    addr = XEXP (addr, 1);
169059692Selan 	  }
169159692Selan 	else if (CONSTANT_ADDRESS_P (XEXP (addr, 1)))
169259692Selan 	  {
169359692Selan 	    offset = XEXP (addr, 1);
169459692Selan 	    addr = XEXP (addr, 0);
169559692Selan 	  }
169659692Selan 	if (GET_CODE (addr) != PLUS)
169759692Selan 	  {
169859692Selan 	    ;
169959692Selan 	  }
170059692Selan 	else if (GET_CODE (XEXP (addr, 0)) == SIGN_EXTEND)
170159692Selan 	  {
170259692Selan 	    reg1 = XEXP (addr, 0);
170359692Selan 	    addr = XEXP (addr, 1);
170459692Selan 	  }
170559692Selan 	else if (GET_CODE (XEXP (addr, 1)) == SIGN_EXTEND)
170659692Selan 	  {
170759692Selan 	    reg1 = XEXP (addr, 1);
170859692Selan 	    addr = XEXP (addr, 0);
170959692Selan 	  }
171059692Selan 	else if (GET_CODE (XEXP (addr, 0)) == MULT)
171159692Selan 	  {
171259692Selan 	    reg1 = XEXP (addr, 0);
171359692Selan 	    addr = XEXP (addr, 1);
171459692Selan 	  }
171559692Selan 	else if (GET_CODE (XEXP (addr, 1)) == MULT)
171659692Selan 	  {
171759692Selan 	    reg1 = XEXP (addr, 1);
171859692Selan 	    addr = XEXP (addr, 0);
171959692Selan 	  }
172059692Selan 	else if (GET_CODE (XEXP (addr, 0)) == REG)
172159692Selan 	  {
172259692Selan 	    reg1 = XEXP (addr, 0);
172359692Selan 	    addr = XEXP (addr, 1);
172459692Selan 	  }
172559692Selan 	else if (GET_CODE (XEXP (addr, 1)) == REG)
172659692Selan 	  {
172759692Selan 	    reg1 = XEXP (addr, 1);
172859692Selan 	    addr = XEXP (addr, 0);
172959692Selan 	  }
173059692Selan 	if (GET_CODE (addr) == REG || GET_CODE (addr) == MULT
173159692Selan 	    || GET_CODE (addr) == SIGN_EXTEND)
173259692Selan 	  {
173359692Selan 	    if (reg1 == 0)
173459692Selan 	      {
173559692Selan 		reg1 = addr;
173659692Selan 	      }
173759692Selan 	    else
173859692Selan 	      {
173959692Selan 		reg2 = addr;
174059692Selan 	      }
174159692Selan 	    addr = 0;
174259692Selan 	  }
174359692Selan #if 0	/* for OLD_INDEXING */
174459692Selan 	else if (GET_CODE (addr) == PLUS)
174559692Selan 	  {
174659692Selan 	    if (GET_CODE (XEXP (addr, 0)) == REG)
174759692Selan 	      {
174859692Selan 		reg2 = XEXP (addr, 0);
174959692Selan 		addr = XEXP (addr, 1);
175059692Selan 	      }
175159692Selan 	    else if (GET_CODE (XEXP (addr, 1)) == REG)
175259692Selan 	      {
175359692Selan 		reg2 = XEXP (addr, 1);
175459692Selan 		addr = XEXP (addr, 0);
175559692Selan 	      }
175659692Selan 	  }
175759692Selan #endif
175859692Selan 	if (offset != 0)
175959692Selan 	  {
176059692Selan 	    if (addr != 0)
176159692Selan 	      {
176259692Selan 		abort ();
176359692Selan 	      }
176459692Selan 	    addr = offset;
176559692Selan 	  }
176659692Selan 	if ((reg1 && (GET_CODE (reg1) == SIGN_EXTEND
176759692Selan 		      || GET_CODE (reg1) == MULT))
176859692Selan 	    || (reg2 != 0 && REGNO_OK_FOR_BASE_P (REGNO (reg2))))
176959692Selan 	  {
177059692Selan 	    breg = reg2;
177159692Selan 	    ireg = reg1;
177259692Selan 	  }
177359692Selan 	else if (reg1 != 0 && REGNO_OK_FOR_BASE_P (REGNO (reg1)))
177459692Selan 	  {
177559692Selan 	    breg = reg1;
177659692Selan 	    ireg = reg2;
177759692Selan 	  }
177859692Selan 	if (ireg != 0 && breg == 0 && GET_CODE (addr) == LABEL_REF
177959692Selan 	    && ! (flag_pic && ireg == pic_offset_table_rtx))
178059692Selan 	  {
178159692Selan 	    int scale = 1;
178259692Selan 	    if (GET_CODE (ireg) == MULT)
178359692Selan 	      {
178459692Selan 		scale = INTVAL (XEXP (ireg, 1));
178559692Selan 		ireg = XEXP (ireg, 0);
178659692Selan 	      }
178759692Selan 	    if (GET_CODE (ireg) == SIGN_EXTEND)
178859692Selan 	      {
178959692Selan #ifdef MOTOROLA
179059692Selan #ifdef SGS
179159692Selan 		asm_fprintf (file, "%LLD%d(%Rpc,%s.w",
179259692Selan 			     CODE_LABEL_NUMBER (XEXP (addr, 0)),
179359692Selan 			     reg_names[REGNO (XEXP (ireg, 0))]);
179459692Selan #else
179559692Selan 		asm_fprintf (file, "%LL%d-%LLI%d.b(%Rpc,%s.w",
179659692Selan 			     CODE_LABEL_NUMBER (XEXP (addr, 0)),
179759692Selan 			     CODE_LABEL_NUMBER (XEXP (addr, 0)),
179859692Selan 			     reg_names[REGNO (XEXP (ireg, 0))]);
179959692Selan #endif
180059692Selan #else
180159692Selan 		asm_fprintf (file, "%Rpc@(%LL%d-%LLI%d-2:b,%s:w",
180259692Selan 			     CODE_LABEL_NUMBER (XEXP (addr, 0)),
180359692Selan 			     CODE_LABEL_NUMBER (XEXP (addr, 0)),
180459692Selan 			     reg_names[REGNO (XEXP (ireg, 0))]);
180559692Selan #endif
180659692Selan 	      }
180759692Selan 	    else
180859692Selan 	      {
180959692Selan #ifdef MOTOROLA
181059692Selan #ifdef SGS
181159692Selan 		asm_fprintf (file, "%LLD%d(%Rpc,%s.l",
181259692Selan 			     CODE_LABEL_NUMBER (XEXP (addr, 0)),
181359692Selan 			     reg_names[REGNO (ireg)]);
181459692Selan #else
181559692Selan 		asm_fprintf (file, "%LL%d-%LLI%d.b(%Rpc,%s.l",
181659692Selan 			     CODE_LABEL_NUMBER (XEXP (addr, 0)),
181759692Selan 			     CODE_LABEL_NUMBER (XEXP (addr, 0)),
181859692Selan 			     reg_names[REGNO (ireg)]);
181959692Selan #endif
182059692Selan #else
182159692Selan 		asm_fprintf (file, "%Rpc@(%LL%d-%LLI%d-2:b,%s:l",
182259692Selan 			     CODE_LABEL_NUMBER (XEXP (addr, 0)),
182359692Selan 			     CODE_LABEL_NUMBER (XEXP (addr, 0)),
182459692Selan 			     reg_names[REGNO (ireg)]);
182559692Selan #endif
182659692Selan 	      }
182759692Selan 	    if (scale != 1)
182859692Selan 	      {
182959692Selan #ifdef MOTOROLA
183059692Selan 		fprintf (file, "*%d", scale);
183159692Selan #else
183259692Selan 		fprintf (file, ":%d", scale);
183359692Selan #endif
183459692Selan 	      }
183559692Selan 	    putc (')', file);
183659692Selan 	    break;
183759692Selan 	  }
183859692Selan 	if (breg != 0 && ireg == 0 && GET_CODE (addr) == LABEL_REF
183959692Selan 	    && ! (flag_pic && breg == pic_offset_table_rtx))
184059692Selan 	  {
184159692Selan #ifdef MOTOROLA
184259692Selan #ifdef SGS
184359692Selan 	    asm_fprintf (file, "%LLD%d(%Rpc,%s.l",
184459692Selan 			 CODE_LABEL_NUMBER (XEXP (addr, 0)),
184559692Selan 			 reg_names[REGNO (breg)]);
184659692Selan #else
184759692Selan 	    asm_fprintf (file, "%LL%d-%LLI%d.b(%Rpc,%s.l",
184859692Selan 			 CODE_LABEL_NUMBER (XEXP (addr, 0)),
184959692Selan 			 CODE_LABEL_NUMBER (XEXP (addr, 0)),
185059692Selan 			 reg_names[REGNO (breg)]);
185159692Selan #endif
185259692Selan #else
185359692Selan 	    asm_fprintf (file, "%Rpc@(%LL%d-%LLI%d-2:b,%s:l",
185459692Selan 			 CODE_LABEL_NUMBER (XEXP (addr, 0)),
185559692Selan 			 CODE_LABEL_NUMBER (XEXP (addr, 0)),
185659692Selan 			 reg_names[REGNO (breg)]);
185759692Selan #endif
185859692Selan 	    putc (')', file);
185959692Selan 	    break;
186059692Selan 	  }
186159692Selan 	if (ireg != 0 || breg != 0)
186259692Selan 	  {
186359692Selan 	    int scale = 1;
186459692Selan 	    if (breg == 0)
186559692Selan 	      {
186659692Selan 		abort ();
186759692Selan 	      }
186859692Selan 	    if (! flag_pic && addr && GET_CODE (addr) == LABEL_REF)
186959692Selan 	      {
187059692Selan 		abort ();
187159692Selan 	      }
187259692Selan #ifdef MOTOROLA
187359692Selan 	    if (addr != 0)
187459692Selan 	      {
187559692Selan 		output_addr_const (file, addr);
187659692Selan 	        if ((flag_pic == 1) && (breg == pic_offset_table_rtx))
187759692Selan 	          fprintf (file, ".w");
187859692Selan 	        if ((flag_pic == 2) && (breg == pic_offset_table_rtx))
187959692Selan 	          fprintf (file, ".l");
188059692Selan 	      }
188159692Selan 	    fprintf (file, "(%s", reg_names[REGNO (breg)]);
188259692Selan 	    if (ireg != 0)
188359692Selan 	      {
188459692Selan 		putc (',', file);
188559692Selan 	      }
188659692Selan #else
188759692Selan 	    fprintf (file, "%s@(", reg_names[REGNO (breg)]);
188859692Selan 	    if (addr != 0)
188959692Selan 	      {
189059692Selan 		output_addr_const (file, addr);
189159692Selan 	        if ((flag_pic == 1) && (breg == pic_offset_table_rtx))
189259692Selan 	          fprintf (file, ":w");
189359692Selan 	        if ((flag_pic == 2) && (breg == pic_offset_table_rtx))
189459692Selan 	          fprintf (file, ":l");
189559692Selan 	      }
189659692Selan 	    if (addr != 0 && ireg != 0)
189759692Selan 	      {
189859692Selan 		putc (',', file);
189959692Selan 	      }
190059692Selan #endif
190159692Selan 	    if (ireg != 0 && GET_CODE (ireg) == MULT)
190259692Selan 	      {
190359692Selan 		scale = INTVAL (XEXP (ireg, 1));
190459692Selan 		ireg = XEXP (ireg, 0);
190559692Selan 	      }
190659692Selan 	    if (ireg != 0 && GET_CODE (ireg) == SIGN_EXTEND)
190759692Selan 	      {
190859692Selan #ifdef MOTOROLA
190959692Selan 		fprintf (file, "%s.w", reg_names[REGNO (XEXP (ireg, 0))]);
191059692Selan #else
191159692Selan 		fprintf (file, "%s:w", reg_names[REGNO (XEXP (ireg, 0))]);
191259692Selan #endif
191359692Selan 	      }
191459692Selan 	    else if (ireg != 0)
191559692Selan 	      {
191659692Selan #ifdef MOTOROLA
191759692Selan 		fprintf (file, "%s.l", reg_names[REGNO (ireg)]);
191859692Selan #else
191959692Selan 		fprintf (file, "%s:l", reg_names[REGNO (ireg)]);
192059692Selan #endif
192159692Selan 	      }
192259692Selan 	    if (scale != 1)
192359692Selan 	      {
192459692Selan #ifdef MOTOROLA
192559692Selan 		fprintf (file, "*%d", scale);
192659692Selan #else
192759692Selan 		fprintf (file, ":%d", scale);
192859692Selan #endif
192959692Selan 	      }
193059692Selan 	    putc (')', file);
193159692Selan 	    break;
193259692Selan 	  }
193359692Selan 	else if (reg1 != 0 && GET_CODE (addr) == LABEL_REF
193459692Selan 		 && ! (flag_pic && reg1 == pic_offset_table_rtx))
193559692Selan 	  {
193659692Selan #ifdef MOTOROLA
193759692Selan #ifdef SGS
193859692Selan 	    asm_fprintf (file, "%LLD%d(%Rpc,%s.l)",
193959692Selan 			 CODE_LABEL_NUMBER (XEXP (addr, 0)),
194059692Selan 			 reg_names[REGNO (reg1)]);
194159692Selan #else
194259692Selan 	    asm_fprintf (file, "%LL%d-%LLI%d.b(%Rpc,%s.l)",
194359692Selan 			 CODE_LABEL_NUMBER (XEXP (addr, 0)),
194459692Selan 			 CODE_LABEL_NUMBER (XEXP (addr, 0)),
194559692Selan 			 reg_names[REGNO (reg1)]);
194659692Selan #endif
194759692Selan #else
194859692Selan 	    asm_fprintf (file, "%Rpc@(%LL%d-%LLI%d-2:b,%s:l)",
194959692Selan 			 CODE_LABEL_NUMBER (XEXP (addr, 0)),
195059692Selan 			 CODE_LABEL_NUMBER (XEXP (addr, 0)),
195159692Selan 			 reg_names[REGNO (reg1)]);
195259692Selan #endif
195359692Selan 	    break;
195459692Selan 	  }
195559692Selan 	/* FALL-THROUGH (is this really what we want? */
195659692Selan       default:
195759692Selan         if (GET_CODE (addr) == CONST_INT
195859692Selan 	    && INTVAL (addr) < 0x8000
195959692Selan 	    && INTVAL (addr) >= -0x8000)
196059692Selan 	  {
196159692Selan #ifdef MOTOROLA
196259692Selan #ifdef SGS
196359692Selan 	    /* Many SGS assemblers croak on size specifiers for constants. */
196459692Selan 	    fprintf (file, "%d", INTVAL (addr));
196559692Selan #else
196659692Selan 	    fprintf (file, "%d.w", INTVAL (addr));
196759692Selan #endif
196859692Selan #else
196959692Selan 	    fprintf (file, "%d:w", INTVAL (addr));
197059692Selan #endif
197159692Selan 	  }
197259692Selan 	else
197359692Selan 	  {
197459692Selan 	    output_addr_const (file, addr);
197559692Selan 	  }
197659692Selan 	break;
197759692Selan     }
197859692Selan }
1979