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