home *** CD-ROM | disk | FTP | other *** search
- /* Output routines for GCC for Hitachi Super-H
- Copyright (C) 1993 Free Software Foundation, Inc.
-
- This file is part of GNU CC.
-
- GNU CC is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- GNU CC is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with GNU CC; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-
-
- /* Contributed by Steve Chamberlain (sac@cygnus.com) */
-
- #include <stdio.h>
- #include "assert.h"
- #include "config.h"
- #include "rtl.h"
- #include "regs.h"
- #include "hard-reg-set.h"
- #include "real.h"
- #include "insn-config.h"
- #include "conditions.h"
- #include "insn-flags.h"
- #include "tree.h"
- #include "output.h"
- #include "insn-attr.h"
- #include "flags.h"
- #include "obstack.h"
- #include "expr.h"
-
-
- static int add_constant ();
- int dump_constants ();
-
- int current_function_anonymous_args;
- extern int current_function_pretend_args_size;
- extern char *version_string;
- extern int flag_traditional;
-
-
- enum attr_cpu sh_cpu; /* target cpu */
-
- /* Global variables for machine-dependent things. */
-
- /* Saved operands from the last compare to use when we generate an scc
- or bcc insn. */
-
- rtx sh_compare_op0;
- rtx sh_compare_op1;
-
- /* Provides the class number of the smallest class containing
- reg number */
-
- int regno_reg_class[FIRST_PSEUDO_REGISTER] =
- {
- R0_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
- GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
- GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
- GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
- GENERAL_REGS, PR_REGS, T_REGS, NO_REGS, MAC_REGS,
- MAC_REGS,
- };
-
- /* Provide reg_class from a letter such as appears in the machine
- description. */
-
- enum reg_class reg_class_from_letter[] =
- {
- /* a */ NO_REGS, /* b */ NO_REGS, /* c */ NO_REGS, /* d */ NO_REGS,
- /* e */ NO_REGS, /* f */ NO_REGS, /* g */ NO_REGS, /* h */ NO_REGS,
- /* i */ NO_REGS, /* j */ NO_REGS, /* k */ NO_REGS, /* l */ PR_REGS,
- /* m */ NO_REGS, /* n */ NO_REGS, /* o */ NO_REGS, /* p */ NO_REGS,
- /* q */ NO_REGS, /* r */ NO_REGS, /* s */ NO_REGS, /* t */ T_REGS,
- /* u */ NO_REGS, /* v */ NO_REGS, /* w */ NO_REGS, /* x */ MAC_REGS,
- /* y */ NO_REGS, /* z */ R0_REGS
- };
-
-
-
-
- /* Local label counter, used for constants in the pool and inside
- pattern branches. */
-
- static int lf = 100;
-
- /* Used to work out sizes of instructions */
- static int first_pc;
- static int pc;
- #define MAYBE_DUMP_LEVEL 900
- #define MUST_DUMP_LEVEL 1000
- static int dumpnext;
-
-
- void
- push (rn)
- {
- emit_insn (gen_push (gen_rtx (REG, SImode, rn)));
- }
-
- void
- pop (rn)
- {
- emit_insn (gen_pop (gen_rtx (REG, SImode, rn)));
- }
-
-
- /* Adjust the stack and return the number of bytes taken to do it */
-
- static void
- output_stack_adjust (direction, size)
- int direction;
- int size;
- {
- if (size)
- {
- rtx val = GEN_INT (size);
- rtx insn;
-
- if (size > 120)
- {
- rtx nval = gen_rtx (REG, SImode, 13);
- emit_insn (gen_movsi (nval, val));
- val = nval;
- }
-
- if (direction > 0)
- insn = gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, val);
- else
- insn = gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx, val);
-
- emit_insn (insn);
- }
- }
-
-
-
- /* Generate code to push the regs specified in the mask, and return
- the number of bytes the insns take. */
-
- static void
- push_regs (mask)
- int mask;
- {
- int i;
- int size = 0;
-
- for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- {
- if (mask & (1 << i))
- {
- push (i);
- }
- }
- }
-
-
- /*
- Print an instruction which would have gone into a delay slot
- after an instructiuon, but couldn't because the instruction expanded
- into a sequence where putting the slot insn at the end wouldn't work.
- */
-
- void
- print_slot (insn)
- rtx insn;
- {
- final_scan_insn (XVECEXP (insn, 0, 1), asm_out_file, optimize, 0, 1);
-
- INSN_DELETED_P (XVECEXP (insn, 0, 1)) = 1;
- }
-
- /* Number of bytes pushed for anonymous args */
-
- static int extra_push;
-
- /* Work out the registers which need to be saved, both as a mask and a
- count */
-
- int
- calc_live_regs (count)
- int *count;
- {
- int reg;
- int live_regs_mask = 0;
- *count = 0;
-
- for (reg = 0; reg < FIRST_PSEUDO_REGISTER; reg++)
- {
- if (regs_ever_live[reg] && !call_used_regs[reg])
- {
- (*count)++;
- live_regs_mask |= (1 << reg);
- }
- }
- return live_regs_mask;
- }
-
-
-
-
- static int
- need_slot (insn)
- rtx insn;
- {
- return (insn && !INSN_ANNULLED_BRANCH_P (XVECEXP (insn, 0, 0)));
- }
-
- /* Print the operand address in x to the stream */
-
- void
- print_operand_address (stream, x)
- FILE *stream;
- rtx x;
- {
- switch (GET_CODE (x))
- {
- case REG:
- fprintf (stream, "@%s", reg_names[REGNO (x)]);
- break;
- case PLUS:
- {
- rtx base = XEXP (x, 0);
- rtx index = XEXP (x, 1);
-
- if (GET_CODE (base) != REG)
- {
- /* Ensure that BASE is a register (one of them must be). */
- rtx temp = base;
- base = index;
- index = temp;
- }
-
- switch (GET_CODE (index))
- {
- case CONST_INT:
- fprintf (stream, "@(%d,%s)",
- INTVAL (index),
- reg_names[REGNO (base)]);
- break;
-
- case REG:
- fprintf (stream, "@(r0,%s)",
- reg_names[MAX (REGNO (base), REGNO (index))]);
-
- break;
-
- default:
- debug_rtx (x);
-
- abort ();
- }
- }
-
- break;
- case PRE_DEC:
- fprintf (stream, "@-%s", reg_names[REGNO (XEXP (x, 0))]);
- break;
-
- case POST_INC:
- fprintf (stream, "@%s+", reg_names[REGNO (XEXP (x, 0))]);
- break;
-
- default:
- output_addr_const (stream, x);
- break;
- }
- }
-
- /* Print operand x (an rtx) in assembler syntax to file stream
- according to modifier code.
-
- '.' print a .s if insn needs delay slot
- '*' print a local label
- '^' increment the local label number
- '!' dump the constant table
- '#' output a nop if there is nothing to put in the delay slot
- 'R' print the next register or memory location along, ie the lsw in
- a double word value
- 'O' print a constant without the #
- 'M' print a constant as its negative
- 'I' put something into the constant pool and print its label */
-
- void
- print_operand (stream, x, code)
- FILE *stream;
- rtx x;
- int code;
- {
- switch (code)
- {
-
-
- case '.':
- if (need_slot (final_sequence))
- fprintf (stream, ".s");
- break;
- case '*':
- fprintf (stream, "LF%d", lf);
- break;
- case '!':
- dump_constants (0);
- break;
- case '^':
- lf++;
- break;
-
- case '#':
- /* Output a nop if there's nothing in the delay slot */
- if (dbr_sequence_length () == 0)
- {
- fprintf (stream, "\n\tor r0,r0\t!wasted slot");
- }
- break;
- case 'O':
- fprintf (asm_out_file, "%d", INTVAL (x));
- break;
-
- case 'I':
- fprintf (asm_out_file, "LK%d", add_constant (x, SImode));
- break;
-
- case 'M':
- fprintf (asm_out_file, "#%d", -INTVAL (x));
- break;
-
- case 'R':
- /* Next location along in memory or register*/
- switch (GET_CODE (x))
- {
- case REG:
- fputs (reg_names[REGNO (x) + 1], (stream));
- break;
- case MEM:
- print_operand_address (stream,
- XEXP (adj_offsettable_operand (x, 4), 0), 0);
- break;
- }
- break;
-
- default:
- switch (GET_CODE (x))
- {
- case REG:
- fputs (reg_names[REGNO (x)], (stream));
- break;
- case MEM:
- output_address (XEXP (x, 0));
- break;
- default:
- fputc ('#', stream);
- output_addr_const (stream, x);
- break;
-
- }
- break;
- }
- }
-
-
-
- /* Define the offset between two registers, one to be eliminated, and
- the other its replacement, at the start of a routine. */
-
- int
- initial_elimination_offset (from, to)
- {
- int regs_saved;
- int d = calc_live_regs (®s_saved);
- int total_saved_regs_space = (regs_saved) * 4;
- int total_auto_space = get_frame_size ();
-
-
- if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
- {
- return total_saved_regs_space;
- }
-
- if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
- {
- return total_saved_regs_space + total_auto_space;
- }
-
- if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
- {
- return total_auto_space;
- }
- }
-
-
- /* Prepare operands for a move define_expand; specifically, one of the
- operands must be in a register. Take this chance to remove
- addressing modes which can't be coped with very well. */
-
- int
- prepare_move_operands (operands, mode)
- rtx operands[];
- enum machine_mode mode;
- {
- /* One of the operands has to be a register */
- if ((!register_operand (operands[0], mode)
- && !register_operand (operands[1], mode))
- || GET_CODE (operands[1]) == PLUS)
- {
- /* copy the source to a register */
- operands[1] = copy_to_mode_reg (mode, operands[1]);
- }
-
- /* If we've got a negative index, break it down */
-
- if (GET_CODE (operands[0]) == MEM && !reload_in_progress)
- {
-
- rtx inside = XEXP (operands[0], 0);
- if (GET_CODE (inside) == PLUS)
- {
- rtx inside1 = XEXP (inside, 1);
- if (GET_CODE (inside1) == CONST_INT
- && INTVAL (inside1) < 0)
- {
- /* Catch this now and break it into bits, it will only cause
- problems later */
-
- rtx sub = copy_to_mode_reg (SImode, inside);
- XEXP (operands[0], 0) = sub;
- }
- }
- }
- return 0;
- }
-
-
- /* Prepare the operands for an scc instruction; make sure that the
- compare has been done. */
- rtx
- prepare_scc_operands (code)
- {
- if (GET_CODE (sh_compare_op0) != REG
- || REGNO (sh_compare_op0) != T_REG)
- {
- /* First need a compare insn */
- emit_insn (gen_rtx (SET, SImode,
- gen_rtx (REG, SImode, T_REG),
- gen_rtx (code, SImode, sh_compare_op0,
- sh_compare_op1)));
- }
- return gen_rtx (REG, SImode, T_REG);
- }
-
-
- /* Functions to output assembly */
-
- /* Return a sequence of instructions to perform DI or DF move.
-
- Since the SH cannot move a DI or DF in one instruction, we have
- to take care when we see overlapping source and dest registers.
-
- */
- char *
- output_movedouble (operands, mode)
- rtx operands[];
- enum machine_mode mode;
- {
- rtx dst = operands[0];
- rtx src = operands[1];
- int lowfirst;
-
- if (register_operand (dst, mode)
- && register_operand (src, mode))
- {
- if (REGNO (src) == MACH_REG)
- return "sts mach,%0\n\tsts macl,%R0";
-
- /*
- when mov.d r1,r2 do r2->r3 then r1->r2
- when mov.d r1,r0 do r1->r0 then r2->r1
- */
-
- if (REGNO (src) + 1 == REGNO (dst))
- return "mov %1,%0\n\tmov %R1,%R0 ! cr";
- else
- return "mov %R1,%R0\n\tmov %1,%0 ";
-
- }
- else if (GET_CODE (src) == CONST_INT)
- {
- if (INTVAL (src) < 0)
- return "mov #-1,%0\n\tmov %1,%R0";
- else
- return "mov #0,%0\n\tmov %1,%R0";
- }
-
- else if (GET_CODE (src) == MEM)
- {
- int ptrreg1 = -1;
- int ptrreg2 = -1;
- int dreg = REGNO (dst);
- rtx inside = XEXP (src, 0);
-
- if (GET_CODE (inside) == REG)
- {
- ptrreg1 = REGNO (inside);
- }
- else if (GET_CODE (inside) == PLUS)
- {
- rtx lhs = XEXP (inside, 0);
- rtx rhs = XEXP (inside, 1);
- if (GET_CODE (lhs) == REG)
- ptrreg1 = REGNO (lhs);
- if (GET_CODE (rhs) == REG)
- ptrreg2 = REGNO (rhs);
- }
- else
- abort ();
-
-
- if ((ptrreg1 >= 0 && ptrreg2 >= 0)
- && (dreg == ptrreg1
- || dreg == ptrreg2
- || dreg + 1 == ptrreg1
- || dreg + 1 == ptrreg2))
- {
- /* This move clobbers both index registers,
- calculate the sum in one register. */
- fprintf (asm_out_file, " add %s,%s ! special fix\n",
- reg_names[ptrreg2], reg_names[ptrreg1]);
-
- if (dreg == ptrreg1)
- {
- /* Copy into dreg+1 first. */
- fprintf (asm_out_file, " mov.l @(4,%s),%s\n",
- reg_names[ptrreg1],
- reg_names[dreg + 1]);
-
- fprintf (asm_out_file, " mov.l @(%s),%s\n",
- reg_names[ptrreg1],
- reg_names[dreg]);
- }
- else
- {
- /* Copy into dreg first. */
- fprintf (asm_out_file, " mov.l @(%s),%s\n",
- reg_names[ptrreg1],
- reg_names[dreg]);
-
- fprintf (asm_out_file, " mov.l @(4,%s),%s\n",
- reg_names[ptrreg1],
- reg_names[dreg + 1]);
-
- }
- warning ("generated complex amode");
- return "";
- }
-
- /* Work out the safe way to copy */
- if (dreg == ptrreg1)
- {
- /* Copy into the second half first */
- return "mov.l %R1,%R0\n\tmov.l %1,%0 ! cr";
- }
- }
-
- return "mov.l %1,%0\n\tmov.l %R1,%R0";
- }
-
- /* Emit assembly to shift reg by k bits */
-
- char *
- output_shift (string, reg, k, code)
- char *string;
- rtx reg;
- rtx k;
- int code;
-
- {
- int s = INTVAL (k);
-
- if (code == ASHIFT && s == 31)
- {
- /* Shift left by 31 moving into the t bit, clearing and rotating the other way */
-
- fprintf (asm_out_file, "\trotr r%d\n", REGNO (reg));
- fprintf (asm_out_file, "\tmov #0,r%d\n", REGNO (reg));
- fprintf (asm_out_file, "\trotcr r%d\n", REGNO (reg));
- s = 0;
- }
-
- if (code == LSHIFTRT && s == 31)
- {
- fprintf (asm_out_file, "\trotl r%d\n", REGNO (reg));
- fprintf (asm_out_file, "\tmov #0,r%d\n", REGNO (reg));
- fprintf (asm_out_file, "\trotcl r%d\n", REGNO (reg));
- s = 0;
- }
-
- while (s)
- {
- char *out;
- int d;
-
- if (s >= 16)
- {
- d = 16;
- out = "16";
- }
- else if (s >= 8)
- {
- d = 8;
- out = "8";
- }
- else if (s >= 2)
- {
- d = 2;
- out = "2";
- }
- else
- {
- d = 1;
- out = "";
- }
- fprintf (asm_out_file, "\t%s%s\tr%d\n", string, out, REGNO (reg));
- s -= d;
- }
- return "";
- }
-
- /* Return the text of the branch instruction which matches its length
- attribute.
-
- This gets tricky if we have an insn in the delay slot of a branch
- and the branch needs more than 1 insn to complete.*/
-
-
-
- char *
- output_branch (logic, insn)
- int logic;
- rtx insn;
- {
- extern rtx recog_operand[];
- int label = lf++;
- int rn = -1;
- int need_save;
-
- switch (get_attr_length (insn))
- {
- case 2:
- /* Simple branch in range -200..+200 bytes */
- return logic ? "bt%. %l0" : "bf%. %l0";
-
- case 6:
- /* Branch in range -4000..+4000 bytes */
- {
- rtx oldop = recog_operand[0];
-
-
- if (need_slot (final_sequence))
- {
- fprintf (asm_out_file, "\tb%c.s\tLF%d\n", logic ? 'f' : 't',
- label);
-
- print_slot (final_sequence);
- }
-
- else
- {
- fprintf (asm_out_file, "\tb%c\tLF%d\n", logic ? 'f' : 't',
- label);
- }
- recog_operand[0] = oldop;
-
- output_asm_insn ("bra %l0 ! 12 bit cond ", recog_operand);
- fprintf (asm_out_file, "\tor r0,r0\n");
- label = dump_constants (label);
- fprintf (asm_out_file, "LF%d:\n", label);
- }
-
- return "";
-
- case 8:
- /* Branches a long way away */
- {
-
- rtx oldop = recog_operand[0];
-
- if (need_slot (final_sequence))
- {
- fprintf (asm_out_file, "\tb%c.s\tLF%d\n", logic ? 'f' : 't', label);
- print_slot (final_sequence);
-
- }
- else
- {
- fprintf (asm_out_file, "\tb%c\tLF%d\n", logic ? 'f' : 't', label);
- }
-
- recog_operand[0] = oldop;
-
- /* We use r13 as a scratch */
- need_save = 0;
- rn = 13;
-
- if (need_save)
- fprintf (asm_out_file, "\tpush r%d\n", rn);
- fprintf (asm_out_file, "\tmov.l LK%d,r%d\n", add_constant (oldop, SImode), rn);
- fprintf (asm_out_file, "\tjmp @r%d ! 32 cond \n", rn);
- if (need_save)
- fprintf (asm_out_file, "\tpop r%d\n", rn);
- else
- fprintf (asm_out_file, "\tor r0,r0\n");
- fprintf (asm_out_file, "LF%d:\n", label);
- return "";
- }
- }
- return "bad";
- }
-
-
- /* Predicates used by the templates */
-
- /* Non zero if op is an immediate ok for a byte index */
-
- int
- byte_index_operand (op, mode)
- rtx op;
- enum machine_mode mode;
- {
- return (GET_CODE (op) == CONST_INT
- && INTVAL (op) >= 0 && INTVAL (op) <= 15);
- }
-
- /* Non zero if OP is a pop operand */
-
- int
- pop_operand (op, mode)
- rtx op;
- enum machine_mode mode;
- {
- if (GET_CODE (op) != MEM)
- return 0;
-
- if (GET_MODE (op) != mode)
- return 0;
-
- op = XEXP (op, 0);
-
- if (GET_CODE (op) != POST_INC)
- return 0;
-
- return XEXP (op, 0) == stack_pointer_rtx;
- }
-
- /* Non zero if OP is an immediate which can be made from two insns. */
-
- int
- painful_immediate_operand (op, mode)
- rtx op;
- enum machine_mode mode;
- {
- if (GET_CODE (op) == CONST_INT)
- {
- int i = INTVAL (op);
-
- if (i > 127 && i < 255)
- return 1; /* two adds */
- }
- return 0;
- }
-
-
- /* Non zero if OP can be source of a simple move operation. */
-
- int
- general_movsrc_operand (op, mode)
- rtx op;
- enum machine_mode mode;
- {
- if (GET_CODE (op) == REG
- || GET_CODE (op) == SUBREG
- || (GET_CODE (op) == CONST_INT &&
- CONST_OK_FOR_I (INTVAL (op)))
- || GET_CODE (op) == MEM)
- return general_operand (op, mode);
- return 0;
- }
-
-
-
- /* Nonzero if OP is a normal arithmetic register. */
-
- int
- arith_reg_operand (op, mode)
- rtx op;
- enum machine_mode mode;
- {
- if (register_operand (op, mode))
- {
- if (GET_CODE (op) == REG)
- return REGNO (op) != T_REG;
- return 1;
- }
- return 0;
- }
-
-
- /* Nonzero if OP is a valid source operand for an arithmetic insn. */
-
- int
- arith_operand (op, mode)
- rtx op;
- enum machine_mode mode;
- {
- if (register_operand (op, mode))
- return 1;
-
- if (GET_CODE (op) == CONST_INT)
- {
- if (CONST_OK_FOR_I (INTVAL (op)))
- return 1;
- }
- return 0;
- }
-
-
- /* Nonzero if OP is a valid source operand for a logical operation */
-
- int
- logical_operand (op, mode)
- rtx op;
- enum machine_mode mode;
- {
- if (register_operand (op, mode))
- return 1;
-
- if (GET_CODE (op) == CONST_INT)
- {
- if (CONST_OK_FOR_L (INTVAL (op)))
- return 1;
- }
- return 0;
- }
-
- /* Nonzero if p is a valid shift operand for lshr and ashl */
-
- int
- ok_shift_value (p)
- rtx p;
- {
- if (GET_CODE (p) == CONST_INT)
- {
- switch (INTVAL (p))
- {
- case 1:
- case 2:
- case 8:
- case 16:
- return 1;
- default:
- if (TARGET_FASTCODE)
- return INTVAL (p) >= 0;
- }
- }
- return 0;
- }
-
- /* Nonzero if the arg is an immediate which has to be loaded from
- memory */
-
- int
- hard_immediate_operand (op, mode)
- rtx op;
- enum machine_mode mode;
- {
- if (immediate_operand (op, mode))
- {
- if (GET_CODE (op) == CONST_INT
- && INTVAL (op) >= -128 && INTVAL (op) < 127)
- return 0;
- return 1;
- }
- return 0;
- }
-
- /* The SH cannot load a large constant into a register, constants have to
- come from a pc relative load. The reference of a pc relative load
- instruction must be less than 1k infront of the instruction. This
- means that we often have to dump a constant inside a function, and
- generate code to branch around it.
-
- It is important to minimize this, since the branches will slow things
- down and make things bigger.
-
- Worst case code looks like:
-
- mov.l L1,rn
- bra L2
- nop
- align
- L1: .long value
- L2:
- ..
-
- mov.l L3,rn
- bra L4
- nop
- align
- L3: .long value
- L4:
- ..
-
- During shorten_branches we notice the instructions which can have a
- constant table in them, if we see two that are close enough
- together, we move the constants from the first table to the second
- table and continue. This process can happen again and again, and
- in the best case, moves the constant table outside of the function.
-
- In the above example, we can tell that L3 is within 1k of L1, so
- the first move can be shrunk from the 3 insn+constant sequence into
- just 1 insn, and the constant moved to L3 to make:
-
- mov.l L1,rn
- ..
- mov.l L3,rn
- bra L4
- nop
- align
- L3:.long value
- L4:.long value
-
- Then the second move becomes the target for the shortening process.
-
- We keep a simple list of all the constants accumulated in the
- current pool so there are no duplicates in a single table, but
- they are not factored into the size estimates.
-
- */
-
- typedef struct
- {
- rtx value;
- int number;
- enum machine_mode mode;
- } pool_node;
-
- /* The maximum number of constants that can fit into one pool, since
- the pc relative range is 0...1020 bytes and constants are at least 4
- bytes long */
-
- #define MAX_POOL_SIZE (1020/4)
- static pool_node pool_vector[MAX_POOL_SIZE];
- static int pool_size;
-
-
- /* Add a constant to the pool and return its label number. */
-
- static int
- add_constant (x, mode)
- rtx x;
- enum machine_mode mode;
- {
- int i;
-
- /* Start the countdown on the first constant */
-
- if (!pool_size)
- {
- first_pc = pc;
- }
-
- /* First see if we've already got it */
-
- for (i = 0; i < pool_size; i++)
- {
-
- if (x->code == pool_vector[i].value->code
- && mode == pool_vector[i].mode)
- {
- if (x->code == CODE_LABEL)
- {
- if (XINT (x, 3) != XINT (pool_vector[i].value, 3))
- continue;
- }
- }
-
- if (rtx_equal_p (x, pool_vector[i].value))
- return pool_vector[i].number;
- }
-
-
- pool_vector[pool_size].value = x;
- pool_vector[pool_size].mode = mode;
- pool_vector[pool_size].number = lf;
- pool_size++;
-
- return lf++;
- }
-
- /* Nonzero if the insn could take a constant table. */
-
- static int
- has_constant_table (insn)
- rtx insn;
- {
- rtx body;
-
- if (GET_CODE (insn) == NOTE
- || GET_CODE (insn) == BARRIER
- || GET_CODE (insn) == CODE_LABEL)
- return 0;
-
- body = PATTERN (insn);
- if (GET_CODE (body) == SEQUENCE)
- return 0;
- if (GET_CODE (body) == ADDR_VEC)
- return 0;
- if (GET_CODE (body) == USE)
- return 0;
- if (GET_CODE (body) == CLOBBER)
- return 0;
- if (get_attr_constneed (insn) == CONSTNEED_YES)
- return 1;
-
- if (GET_CODE (body) == UNSPEC_VOLATILE)
- {
- return INTVAL (XVECEXP (body, 0, 0)) == 1;
- }
- return 0;
- }
-
- /* Adjust the length of an instruction.
-
- We'll look at the previous instruction which holds a constant
- table and see if we can move the table to here instead. */
-
- int target_insn_uid;
- int target_insn_smallest_size;
-
- int target_pc;
- int target_insn_range;
- int current_pc;
- int pool_bytes;
-
- int last_uid;
- int last_pc;
-
- void
- adjust_insn_length (insn, insn_lengths)
- rtx insn;
- short *insn_lengths;
- {
- int uid = INSN_UID (insn);
- rtx body = PATTERN (insn);
-
- current_pc += insn_lengths[uid];
-
-
- if (GET_CODE (body) == SEQUENCE)
- {
- int i;
-
- for (i = 0; i < XVECLEN (body, 0); i++)
- {
- adjust_insn_length (XVECEXP (body, 0, i), insn_lengths);
- }
- }
- else
- {
- if (has_constant_table (insn))
- {
- if (current_pc >= target_insn_range)
- {
- /* This instruction is further away from the referencing
- instruction than it can reach, so we'll stop accumulating
- from that one and start fresh. */
- target_pc = current_pc;
- target_insn_range = current_pc + MAYBE_DUMP_LEVEL;
- }
- else
- {
- /* This instruction is within the reach of the target,
- remove the constant table from the target by adjusting
- downwards, and increase the size of this one to
- compensate. */
-
-
- /* Add the stuff from this insn to what will go in the
- growing table. */
-
- pool_bytes += get_attr_constantsize (insn);
-
- /* The target shinks to its smallest natural size */
- insn_lengths[target_insn_uid] = target_insn_smallest_size;
-
- /* The current insn grows to be its larger size plust the
- table size. */
-
- insn_lengths[uid] = get_attr_largestsize (insn) + pool_bytes;
-
- }
- /* Current insn becomes the target. */
- target_insn_uid = uid;
- target_insn_smallest_size = get_attr_smallestsize (insn);
-
- }
- }
- }
-
-
-
- /* Dump out the pending constant pool.
- If label provided then insert an branch in the middle of the table
- */
-
- int
- dump_constants (label)
- {
- int i;
- int rlabel = label;
- int size = 0;
-
- if (pool_size)
- {
- fprintf (asm_out_file, "\n\t! constants - waited %d\n", pc - first_pc);
- fprintf (asm_out_file, "\t.align\t2\n");
-
- for (i = 0; i < pool_size; i++)
- {
- pool_node *p = pool_vector + i;
-
- fprintf (asm_out_file, "LK%d:", p->number);
- size += GET_MODE_SIZE (p->mode);
-
- switch (GET_MODE_CLASS (p->mode))
- {
- case MODE_INT:
- case MODE_PARTIAL_INT:
- assemble_integer (p->value, GET_MODE_SIZE (p->mode), 1);
- break;
- case MODE_FLOAT:
- {
- union real_extract u;
- bcopy (&CONST_DOUBLE_LOW (p->value), &u, sizeof u);
- assemble_real (u.d, p->mode);
- }
- }
-
- /* After 200 bytes of table, stick in another branch */
- if (label && size > 200)
- {
- rlabel = lf++;
- fprintf (asm_out_file, "LF%d:\tbra LF%d\n", label, rlabel);
- fprintf (asm_out_file, "\tor r0,r0\n");
- label = 0;
- }
-
- }
- }
-
- pool_size = 0;
- current_pc = 0;
- pc = 0;
- pool_bytes = 0;
-
- target_insn_range = 0;
- return rlabel;
-
- }
-
-
- /* Emit the text to load a value from a constant table. */
-
- char *
- output_movepcrel (insn, operands, mode)
- rtx insn;
- rtx operands[];
- enum machine_mode mode;
- {
- int len = GET_MODE_SIZE (mode);
- int rn = REGNO (operands[0]);
-
- fprintf (asm_out_file, "\tmov.l LK%d,r%d\n",
- add_constant (operands[1], mode), rn);
-
- if (GET_MODE_SIZE (mode) > 4)
- {
- fprintf (asm_out_file,
- "\tmov.l LK%d+4,r%d\n",
- add_constant (operands[1], mode),
- rn + 1);
-
- }
-
- /* This may have been the last move in the function, so nothing
- took its constant table, we may be able to move it past the end
- of the function (after the rts) if we are careful */
-
- if (target_insn_uid == INSN_UID (insn)
- && current_pc < target_insn_range)
- return "";
-
-
- /* If this instruction is as small as it can be, there can be no
- constant table attached to it. */
- if (get_attr_length (insn) != get_attr_smallestsize (insn))
- {
- /* This needs a constant table */
- fprintf (asm_out_file, "\t!constant table start\n");
- fprintf (asm_out_file, "\tbra LF%d\n", lf);
- fprintf (asm_out_file, "\tor r0,r0 ! wasted slot\n");
- dump_constants (0);
- fprintf (asm_out_file, "LF%d:\n", lf++);
- fprintf (asm_out_file, "\t!constant table end\n");
- }
- return "";
- }
-
-
- /* Dump out interesting debug info */
-
- rtx
- final_prescan_insn (insn, opvec, noperands)
- rtx insn;
- rtx *opvec;
- int noperands;
- {
- register rtx body = PATTERN (insn);
-
- if (target_flags & ISIZE_BIT)
- {
- extern int *insn_addresses;
-
- fprintf (asm_out_file, "\n!%04x*\n",
- insn_addresses[INSN_UID (insn)] + 0x10);
-
- fprintf (asm_out_file, "\n!%04x %d %04x len=%d\n",
- pc, pool_size, first_pc, get_attr_length (insn));
-
- if (TARGET_DUMP_RTL)
- print_rtl (asm_out_file, body);
-
-
- }
-
- pc += get_attr_length (insn);
- if (pool_size && pc - first_pc > MUST_DUMP_LEVEL)
- {
- /* For some reason we have not dumped out a constant table, and
- we have emitted a lot of code. This can happen if the think
- which wants the table is a long conditional branch (which has no
- room for a constant table), and there has not been a move
- constant anywhere. */
- int label = lf++;
- fprintf (asm_out_file, "\t!forced constant table\n");
- fprintf (asm_out_file, "\tbra LF%d\n", label);
- fprintf (asm_out_file, "\tor r0,r0 ! wasted slot\n");
- label = dump_constants (label);
- fprintf (asm_out_file, "LF%d:\n", label);
- fprintf (asm_out_file, "\t!constant table end\n");
- }
- }
-
-
-
- /* Block move stuff stolen from m88k*/
-
- /* Emit code to perform a block move. Choose the best method.
-
- OPERANDS[0] is the destination.
- OPERANDS[1] is the source.
- OPERANDS[2] is the size.
- OPERANDS[3] is the alignment safe to use. */
-
- /* Emit code to perform a block move with an offset sequence of ld/st
- instructions (..., ld 0, st 1, ld 1, st 0, ...). SIZE and ALIGN are
- known constants. DEST and SRC are registers. OFFSET is the known
- starting point for the output pattern. */
-
- static enum machine_mode mode_from_align[] =
- {VOIDmode, QImode, HImode, VOIDmode, SImode,
- VOIDmode, VOIDmode, VOIDmode, DImode};
- static void
-
- block_move_sequence (dest, dest_mem, src, src_mem, size, align, offset)
- rtx dest, dest_mem;
- rtx src, src_mem;
- int size;
- int align;
- int offset;
- {
- rtx temp[2];
- enum machine_mode mode[2];
- int amount[2];
- int active[2];
- int phase = 0;
- int next;
- int offset_ld = offset;
- int offset_st = offset;
-
- active[0] = active[1] = FALSE;
-
- /* Establish parameters for the first load and for the second load if
- it is known to be the same mode as the first. */
- amount[0] = amount[1] = align;
-
-
- mode[0] = mode_from_align[align];
-
- temp[0] = gen_reg_rtx (mode[0]);
- if (size >= 2 * align)
- {
- mode[1] = mode[0];
- temp[1] = gen_reg_rtx (mode[1]);
- }
-
- do
- {
- rtx srcp, dstp;
- next = phase;
- phase = !phase;
-
- if (size > 0)
- {
- /* Change modes as the sequence tails off. */
- if (size < amount[next])
- {
- amount[next] = (size >= 4 ? 4 : (size >= 2 ? 2 : 1));
- mode[next] = mode_from_align[amount[next]];
- temp[next] = gen_reg_rtx (mode[next]);
- }
- size -= amount[next];
- srcp = gen_rtx (MEM,
- MEM_IN_STRUCT_P (src_mem) ? mode[next] : BLKmode,
- gen_rtx (PLUS, Pmode, src,
- gen_rtx (CONST_INT, SImode, offset_ld)));
- RTX_UNCHANGING_P (srcp) = RTX_UNCHANGING_P (src_mem);
- MEM_VOLATILE_P (srcp) = MEM_VOLATILE_P (src_mem);
- MEM_IN_STRUCT_P (srcp) = 1;
- emit_insn (gen_rtx (SET, VOIDmode, temp[next], srcp));
- offset_ld += amount[next];
- active[next] = TRUE;
- }
-
- if (active[phase])
- {
- active[phase] = FALSE;
- dstp = gen_rtx (MEM,
- MEM_IN_STRUCT_P (dest_mem) ? mode[phase] : BLKmode,
- gen_rtx (PLUS, Pmode, dest,
- gen_rtx (CONST_INT, SImode, offset_st)));
- RTX_UNCHANGING_P (dstp) = RTX_UNCHANGING_P (dest_mem);
- MEM_VOLATILE_P (dstp) = MEM_VOLATILE_P (dest_mem);
- MEM_IN_STRUCT_P (dstp) = 1;
- emit_insn (gen_rtx (SET, VOIDmode, dstp, temp[phase]));
- offset_st += amount[phase];
- }
- }
- while (active[next]);
- }
-
- void
- expand_block_move (dest_mem, src_mem, operands)
- rtx dest_mem;
- rtx src_mem;
- rtx *operands;
- {
- int align = INTVAL (operands[3]);
- int constp = (GET_CODE (operands[2]) == CONST_INT);
- int bytes = (constp ? INTVAL (operands[2]) : 0);
-
- #if 0
- if (constp && bytes <= 0)
- return;
-
- if (align > 4)
- align = 4;
-
- if (constp && bytes <= 3 * align)
- block_move_sequence (operands[0], dest_mem, operands[1], src_mem,
- bytes, align, 0);
-
- #if 0
- else if (constp && bytes <= best_from_align[target][align])
- block_move_no_loop (operands[0], dest_mem, operands[1], src_mem,
- bytes, align);
-
- else if (constp && align == 4 && TARGET_88100)
- block_move_loop (operands[0], dest_mem, operands[1], src_mem,
- bytes, align);
- #endif
- else
- #endif
- {
- emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "memcpy"), 0,
- VOIDmode, 3,
- operands[0], Pmode,
- operands[1], Pmode,
- operands[2], SImode);
- }
- }
-
-
- override_options ()
- {
- sh_cpu = CPU_SH0;
- if (TARGET_SH1)
- sh_cpu = CPU_SH1;
- if (TARGET_SH2)
- sh_cpu = CPU_SH2;
- if (TARGET_SH3)
- sh_cpu = CPU_SH3;
- }
-
-
- /* Stuff taken from m88k.c */
-
- /* Output to FILE the start of the assembler file. */
-
- struct option
- {
- char *string;
- int *variable;
- int on_value;
- };
-
- static int
- output_option (file, sep, type, name, indent, pos, max)
- FILE *file;
- char *sep;
- char *type;
- char *name;
- char *indent;
- int pos;
- int max;
- {
- if (strlen (sep) + strlen (type) + strlen (name) + pos > max)
- {
- fprintf (file, indent);
- return fprintf (file, "%s%s", type, name);
- }
- return pos + fprintf (file, "%s%s%s", sep, type, name);
- }
-
- static struct
- {
- char *name;
- int value;
- }
-
- m_options[] = TARGET_SWITCHES;
-
- static void
- output_options (file, f_options, f_len, W_options, W_len,
- pos, max, sep, indent, term)
- FILE *file;
- struct option *f_options;
- struct option *W_options;
- int f_len, W_len;
- int pos;
- int max;
- char *sep;
- char *indent;
- char *term;
- {
- register int j;
-
-
- if (optimize)
- pos = output_option (file, sep, "-O", "", indent, pos, max);
- if (write_symbols != NO_DEBUG)
- pos = output_option (file, sep, "-g", "", indent, pos, max);
- if (flag_traditional)
- pos = output_option (file, sep, "-traditional", "", indent, pos, max);
- if (profile_flag)
- pos = output_option (file, sep, "-p", "", indent, pos, max);
- if (profile_block_flag)
- pos = output_option (file, sep, "-a", "", indent, pos, max);
-
- for (j = 0; j < f_len; j++)
- if (*f_options[j].variable == f_options[j].on_value)
- pos = output_option (file, sep, "-f", f_options[j].string,
- indent, pos, max);
-
- for (j = 0; j < W_len; j++)
- if (*W_options[j].variable == W_options[j].on_value)
- pos = output_option (file, sep, "-W", W_options[j].string,
- indent, pos, max);
-
- for (j = 0; j < sizeof m_options / sizeof m_options[0]; j++)
- if (m_options[j].name[0] != '\0'
- && m_options[j].value > 0
- && ((m_options[j].value & target_flags)
- == m_options[j].value))
- pos = output_option (file, sep, "-m", m_options[j].name,
- indent, pos, max);
-
-
- fprintf (file, term);
- }
-
- void
- output_file_start (file, f_options, f_len, W_options, W_len)
- FILE *file;
- struct option *f_options;
- struct option *W_options;
- int f_len, W_len;
- {
- register int pos;
-
- output_file_directive (file, main_input_filename);
-
- /* Switch to the data section so that the coffsem symbol and the
- gcc2_compiled. symbol aren't in the text section. */
- data_section ();
-
-
- pos = fprintf (file, "\n! Hitachi SH cc1 (%s) arguments:", version_string);
- output_options (file, f_options, f_len, W_options, W_len,
- pos, 75, " ", "\n! ", "\n\n");
- }
-
-
- /* Code to generate prologue and epilogue sequences */
-
- void
- sh_expand_prologue ()
- {
- int live_regs_mask;
- int d;
-
- live_regs_mask = calc_live_regs (&d);
-
- output_stack_adjust (-1, current_function_pretend_args_size);
-
- if (current_function_anonymous_args)
- {
- /* Push arg regs as if they'd been provided by caller in stack */
- int i;
- for (i = 0; i < NPARM_REGS; i++)
- {
- int rn = NPARM_REGS + FIRST_PARM_REG - i - 1;
- if (i > NPARM_REGS - current_function_args_info)
- break;
- push (rn);
-
- extra_push += 4;
- }
- }
-
- if (frame_pointer_needed)
- {
- push_regs (live_regs_mask);
- emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx));
- }
- else
- {
- push_regs (live_regs_mask);
- }
-
- output_stack_adjust (-1, get_frame_size ());
- }
-
- void
- sh_expand_epilogue ()
- {
- int live_regs_mask;
- int d;
- int i;
-
- live_regs_mask = calc_live_regs (&d);
-
- if (frame_pointer_needed)
- {
- emit_insn (gen_movsi (stack_pointer_rtx, frame_pointer_rtx));
- }
- else
- {
- output_stack_adjust (1, get_frame_size ());
- }
-
-
- /* Pop all the registers */
- for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- {
- int j = (FIRST_PSEUDO_REGISTER - 1) - i;
- if (live_regs_mask & (1 << j))
- {
- pop (j);
- }
- }
- output_stack_adjust (1, extra_push +
- current_function_pretend_args_size);
-
- extra_push = 0;
-
- current_function_anonymous_args = 0;
- }
-
-
- /* Return the cost of a shift */
-
- int
- shiftcosts (RTX)
- rtx RTX;
- {
- /* If shift by a non constant, then this will be expensive. */
- if (GET_CODE (XEXP (RTX, 1)) != CONST_INT)
- return 20;
-
- /* otherwise, it will be very cheap if by one of the constants
- we can cope with. */
- if (CONST_OK_FOR_K (INTVAL (XEXP (RTX, 1))))
- return 1;
-
- /* otherwise it will be several insns. */
- return 4;
- }
-
- /* Return the cost of a multiply */
- int
- multcosts (RTX)
- rtx RTX;
- {
- /* If we we're aiming at small code, then just count the number of
- insns in a multiply call sequence, otherwise, count all the insnsn
- inside the call. */
- if (TARGET_SMALLCODE)
- return 3;
- return 30;
- }
-