--- gcc/config/sh/sh.c 2018/04/24 18:10:26 1.1.1.1 +++ gcc/config/sh/sh.c 2018/04/24 18:22:38 1.1.1.3 @@ -1,21 +1,21 @@ /* Output routines for GCC for Hitachi Super-H - Copyright (C) 1993 Free Software Foundation, Inc. + Copyright (C) 1993, 1994 Free Software Foundation, Inc. -This file is part of GNU CC. + 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. */ + 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) */ @@ -32,22 +32,30 @@ the Free Software Foundation, 675 Mass A #include "insn-flags.h" #include "tree.h" #include "output.h" + #include "insn-attr.h" #include "flags.h" #include "obstack.h" #include "expr.h" +static rtx add_constant (); -static int add_constant (); -static int dump_constants (); +int pragma_interrupt; +int pragma_trapa; int current_function_anonymous_args; extern int current_function_pretend_args_size; +extern char *version_string; +extern int flag_traditional; + +static rtx shiftsyms[32]; +struct rtx_def *table_lab; +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. */ + or bcc insn. */ rtx sh_compare_op0; rtx sh_compare_op1; @@ -61,8 +69,8 @@ int regno_reg_class[FIRST_PSEUDO_REGISTE 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, + GENERAL_REGS, PR_REGS, T_REGS, NO_REGS, + MAC_REGS, MAC_REGS, }; /* Provide reg_class from a letter such as appears in the machine @@ -79,393 +87,188 @@ enum reg_class reg_class_from_letter[] = /* y */ NO_REGS, /* z */ R0_REGS }; +/* Value is 1 if register/mode pair is acceptable on SH. Even + registers can hold DIs and DF values. The rest can only hold + SI's efficiently */ -/* Local label counter, used for constants in the pool and inside - pattern branches. */ -static int lf = 100; +#define REG_ODD \ + ( (1 << (int) QImode) | (1 << (int) HImode) | (1 << (int) SImode) \ + | (1 << (int) QFmode) | (1 << (int) HFmode) | (1 << (int) SFmode) \ + | (1 << (int) CQImode) | (1 << (int) CHImode)| (1<< (int)DFmode) | (1<<(int)DImode)) -/* 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; - -/* Functions for generating procedure prologue and epilogue code */ +#define REG_EVEN \ + (REG_ODD | (1 << (int) CSImode) | (1 << (int) SCmode)) -/* Adjust the stack and return the number of bytes taken to do it */ +#define SI_ONLY (1<<(int)SImode) -static int -output_stack_adjust (file, direction, size) - FILE *file; - int direction; - int size; +int hard_regno_mode_ok[] = { - int code_size; - - if (size > 127) - { - fprintf (file, "\tmov.l LK%d,r13\n", - add_constant (GEN_INT (size * direction), SImode)); - - fprintf (file, "\tadd r13,r15\n"); - code_size += 4; - } - else if (size) - { - fprintf (file, "\tadd #%d,r15\n", direction * size); - code_size += 2; - } - return code_size; -} - -/* Generate code to push the regs specified in the mask, and return - the number of bytes the insns take. */ - -static int -push_regs (f, mask) - FILE *f; - int mask; -{ - int i; - int size = 0; - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - { - if (mask & (1 << i)) - { - fprintf (f, "\tmov.l r%d,@-r15\n", i); - size += 2; - } - } - return size; -} - - -/* Working out the right code to use for an epilogue can get quite - hairy, since there are only certain insns which can go in the delay - slot, and there may or may not be a delay insn provided already. - - We generate a canonical list of the instructions to use to perform - the exit, massage that and output from that list */ + REG_EVEN, REG_ODD, REG_EVEN, REG_ODD, + REG_EVEN, REG_ODD, REG_EVEN, REG_ODD, + REG_EVEN, REG_ODD, REG_EVEN, REG_ODD, + REG_EVEN, REG_ODD, REG_EVEN, REG_ODD, + REG, 0, SI_ONLY, SI_ONLY, + SI_ONLY, SI_ONLY +}; +/* Local label counter, used for constants in the pool and inside + pattern branches. */ +static int lf = 100; -/* The structure of a canonical element. */ -typedef struct -{ - enum epi_type - { - STACK_ADJUST, /* add i to stack pointer */ - POP, /* pop into register i */ - RTS, /* rts instruction */ - DELAY, /* delay slot instruction */ - NOP, /* a nop */ - DELETED, - } type; - int i; -} - -epilogue_insn; +/* Number of bytes pushed for anonymous args, used to pass information + between expand_prologue and expand_epilogue. */ +static int extra_push; + -static epilogue_insn epilogue_vec[20]; -static int epilogue_vec_len; -static void -set_epilogue_insn (type, l) - enum epi_type type; - int l; +void +push (rn) + int rn; { - epilogue_vec[epilogue_vec_len].type = type; - epilogue_vec[epilogue_vec_len].i = l; - epilogue_vec_len++; + rtx x ; + x= emit_insn (gen_push (gen_rtx (REG, SImode, rn))); + REG_NOTES (x) = gen_rtx (EXPR_LIST, REG_INC, + gen_rtx(REG, SImode, STACK_POINTER_REGNUM), 0); } -/* Delete an insn from the epilogue list. */ - -static void -delete_epilogue_insn (n) - int n; +void +pop (rn) + int rn; { - int j; - - for (j = n; j < epilogue_vec_len; j++) - epilogue_vec[j] = epilogue_vec[j + 1]; - - epilogue_vec_len--; + rtx x; + x = emit_insn (gen_pop (gen_rtx (REG, SImode, rn))); + REG_NOTES (x) = gen_rtx (EXPR_LIST, REG_INC, + gen_rtx(REG, SImode, STACK_POINTER_REGNUM), 0); } -/* Run through the epilogue list and optimize it. */ +/* Adjust the stack and return the number of bytes taken to do it */ +static rtx lastreg; +int lastval; static void -optimize_epilogue_vec () +output_stack_adjust (size) + int size; { - int i; - - /* Turn two adds in a row into one add and kill empty adds */ - for (i = 0; i < epilogue_vec_len - 1; i++) - { - if (epilogue_vec[i].type == STACK_ADJUST - && epilogue_vec[i + 1].type == STACK_ADJUST) - { - epilogue_vec[i].i += epilogue_vec[i + 1].i; - delete_epilogue_insn (i + 1); - } - if (epilogue_vec[i].type == STACK_ADJUST - && epilogue_vec[i].i == 0) - delete_epilogue_insn (i); - } - - /* If the instruction after the RTS is a nop, see if it can be - changed */ - - for (i = 1; i < epilogue_vec_len - 1; i++) + if (size) { - if (epilogue_vec[i].type == RTS - && epilogue_vec[i + 1].type == NOP) - { - epilogue_vec[i + 1] = epilogue_vec[i - 1]; - delete_epilogue_insn (i - 1); - } - } + rtx val = GEN_INT (size); + rtx insn; - /* Delete all the instructions after the rts's delay slot */ - for (i = 0; i < epilogue_vec_len; i++) - { - if (epilogue_vec[i].type == RTS) + if (!CONST_OK_FOR_I (size)) { - int j; + lastreg = gen_rtx (REG, SImode, 3); + lastval = size; + emit_insn (gen_movsi (lastreg, val)); + val = lastreg; - for (j = i + 2; j < epilogue_vec_len; j++) - epilogue_vec[j].type = DELETED; - return; } + + insn = gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, val); + emit_insn (insn); } } -/* Dump out the insns in epilogue vector. */ + +/* Generate code to push the regs specified in the mask, and return + the number of bytes the insns take. */ static void -output_epilogue_vec () +push_regs (mask) + int mask; { int i; - for (i = 0; i < epilogue_vec_len; i++) + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) { - switch (epilogue_vec[i].type) + if (mask & (1 << i)) { - case STACK_ADJUST: - fprintf (asm_out_file, "\tadd #%d,r15\n", epilogue_vec[i].i); - break; + push (i); + } + } +} - case NOP: - fprintf (asm_out_file, "\tor r0,r0\n"); - break; - case DELAY: - final_scan_insn (XEXP (current_function_epilogue_delay_list, 0), - asm_out_file, 1, 0, 1); - break; +/* 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. */ - case DELETED: - fprintf (asm_out_file, "\t!delete_epilogue_insnd\n"); - break; - - case RTS: - fprintf (asm_out_file, "\trts\n"); - break; +static void +print_slot (insn) + rtx insn; +{ + final_scan_insn (XVECEXP (insn, 0, 1), asm_out_file, optimize, 0, 1); - case POP: - fprintf (asm_out_file, "\tmov.l @r15+,r%d\n", - epilogue_vec[i].i); - break; - } - } - epilogue_vec_len = 0; + 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 */ + count. -int -calc_live_regs (count) - int *count; + If doing a pragma interrupt function, then push all regs used by the function, + and if we call another function (we can tell by looking at PR), make sure that all the + regs it clobbers are safe too. + */ +static int +calc_live_regs (count_ptr) + int *count_ptr; { int reg; int live_regs_mask = 0; - *count = 0; - + int 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); + if (reg == ARG_POINTER_REGNUM) + continue; + if (reg == T_REG) + continue; + if (reg == GBR_REG) + continue; + + if (pragma_interrupt && !pragma_trapa) + { + /* Need to save all the regs ever live */ + if ((regs_ever_live[reg] + || (call_used_regs[reg] && regs_ever_live[PR_REG])) + && reg != 15) + { + live_regs_mask |= 1 << reg; + count++; + } } - } - return live_regs_mask; -} - -/* Generate a procedure prologue. */ - -void -output_prologue (f, frame_size) - FILE *f; - int frame_size; -{ - int live_regs_mask; - int d; - - pc = 0; - - /* This only happens when an arg has been split, part in - registers, part in memory. Allocate the stack space so there is - somewhere to put the value */ - - output_stack_adjust (f, -1, current_function_pretend_args_size); - - live_regs_mask = calc_live_regs (&d); - - extra_push = 0; - - 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++) + else if (TARGET_SMALLCALL) { - int rn = NPARM_REGS + FIRST_PARM_REG - i - 1; - if (i > NPARM_REGS - current_function_args_info) - break; - fprintf (f, "\tmov.l r%d,@-r15\n", rn); - extra_push += 4; - pc += 2; + /* Don't need to push anthing, but count the regs which have + been pushed by the wrapper */ + if (call_used_regs[reg]) + count++; } - } - - if (frame_pointer_needed) - { - /* Don't need to push the fp with the rest of the registers. */ - live_regs_mask &= ~(1 << FRAME_POINTER_REGNUM); - pc += push_regs (f, live_regs_mask); - if (regs_ever_live[PR_REG]) + else { - - fprintf (f, "\tsts.l pr,@-r15\n"); - pc += 2; + /* Only push those regs which are used and need to be saved */ + if (regs_ever_live[reg] && !call_used_regs[reg]) + { + count++; + live_regs_mask |= (1 << reg); + } } - - fprintf (f, "\tmov.l r14,@-r15\n"); - fprintf (f, "\tmov r15,r14\n"); - pc += 4; - pc += output_stack_adjust (f, -1, frame_size); } - else - { - pc += push_regs (f, live_regs_mask); - if (regs_ever_live[PR_REG]) - { - fprintf (f, "\tsts.l pr,@-r15\n"); - pc += 2; - } - pc += output_stack_adjust (f, -1, frame_size); - } + *count_ptr = count; + return live_regs_mask; } + - -/* Generate a procedure epilogue. */ - -void -output_epilogue (f, frame_size) - FILE *f; - int frame_size; +static int +need_slot (insn) + rtx insn; { - int live_regs_mask = 0; - int d; - int i; - rtx delay_insn; - - live_regs_mask = calc_live_regs (&d); - - - /* See if the delay insn is really ok for the slot. */ - if (current_function_epilogue_delay_list) { - delay_insn = PATTERN (XEXP (current_function_epilogue_delay_list, 0)); - - if (GET_CODE (delay_insn) == SET - && SET_DEST (delay_insn) == stack_pointer_rtx) - { - /* Can not use this instruction in the delay slot because - it changes the stack pointer, so emit it now. */ - final_scan_insn (XEXP (current_function_epilogue_delay_list, 0), - asm_out_file, 1, 0, 1); - current_function_epilogue_delay_list = 0; - } - } - - - /* Reclaim the room for the automatics. */ - - output_stack_adjust (f, 1, frame_size); - - /* Make the frame pointer. */ - - if (frame_pointer_needed) - { - fprintf (f, "\tmov r14,r15\n"); - fprintf (f, "\tmov.l @r15+,r14\n"); - live_regs_mask &= ~(1 << FRAME_POINTER_REGNUM); - } - - /* Get the PR register if it was clobbered in the function. */ - - if (regs_ever_live[PR_REG]) - fprintf (f, "\tlds.l @r15+,pr\n"); - - /* 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)) - { - set_epilogue_insn (POP, j); - } - } - - /* Need to adjust the stack by some amount of bytes since we've pushed - some of the args which normally come in registers */ - - set_epilogue_insn (STACK_ADJUST, extra_push); - - /* Need to adjust the stack by some amount of bytes if there - an arg has been split part register and part stack */ - - set_epilogue_insn (STACK_ADJUST, current_function_pretend_args_size); - - set_epilogue_insn (RTS, 0); - - /* Got here without dumping a register pop into the delay slot */ - if (current_function_epilogue_delay_list) - { - set_epilogue_insn (DELAY, 0); - } - set_epilogue_insn (NOP, 0); - - optimize_epilogue_vec (); - - output_epilogue_vec (); - - dump_constants (0); - current_function_anonymous_args = 0; + return (insn && !INSN_ANNULLED_BRANCH_P (XVECEXP (insn, 0, 0))); } - + /* Print the operand address in x to the stream */ void @@ -478,7 +281,6 @@ print_operand_address (stream, x) case REG: fprintf (stream, "@%s", reg_names[REGNO (x)]); break; - case PLUS: { rtx base = XEXP (x, 0); @@ -501,12 +303,13 @@ print_operand_address (stream, x) break; case REG: - fprintf (stream, "@(%s,%s)", - reg_names[REGNO (base)], - reg_names[REGNO (index)]); + fprintf (stream, "@(r0,%s)", + reg_names[MAX (REGNO (base), REGNO (index))]); + break; default: + debug_rtx (x); abort (); } } @@ -529,13 +332,17 @@ print_operand_address (stream, x) /* Print operand x (an rtx) in assembler syntax to file stream according to modifier code. - '*' 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 - 'I' put something into the constant pool and print its label */ + '.' 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 + '@' print rte or rts depending upon pragma interruptness + '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 + 'N' print insides of a @++ or @-- o */ void print_operand (stream, x, code) @@ -545,42 +352,50 @@ print_operand (stream, x, 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 '@': + if (pragma_interrupt) + fprintf (stream, "rte"); + else + fprintf (stream, "rts"); + 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"); + fprintf (stream, "\n\tnop"); } break; - - case 'I': - fprintf (asm_out_file, "LK%d", add_constant (x, SImode)); + case 'O': + output_addr_const (stream, x); + break; + case 'M': + fprintf (asm_out_file, "#%d", -INTVAL (x)); + break; + case 'N': + fputs (reg_names[REGNO (XEXP (XEXP (x, 0), 0))], (stream)); break; - case 'R': - /* Next location along in memory or register*/ + /* 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); + print_operand_address (stream, XEXP (adj_offsettable_operand (x, 4), 0)); break; } break; - default: switch (GET_CODE (x)) { @@ -594,165 +409,585 @@ print_operand (stream, x, code) 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. */ +static int +sextb (x) + int x; +{ + x &= 0xff; + if (x > 127) + { + x = -256 + x; + } + return x; +} -int -initial_elimination_offset (from, to) + + +/* Take a move with integer constant source in OPERANDS, see if it can be generated by + devious shifting. If so, generate the instruction sequence and return 1, otherwise + return 0. + + OPERANDS[0] Destination register + OPERANDS[1] Source constant + + 00000000 00000000 00000000 0NNNNNNNN simple load + 00000000 00000000 00000000 NNNNNNNN0 load and shift by 1 + 00000000 00000000 0000000N NNNNNNN00 load and shift by 2 + 00000000 00000000 0NNNNNNN 000000000 load and shift by 8 + 00000000 0NNNNNNN 00000000 000000000 load and shift by 16 + N0000000 00000000 00000000 00NNNNNNN load and rotate right + + 11111111 11111111 11111111 1NNNNNNNN simple load + 11111111 11111111 11111111 NNNNNNNN0 load and shift by 1 + 11111111 11111111 1111111N NNNNNNN00 load and shift by 2 + 11111111 11111111 1NNNNNNN 000000000 load and shift by 8 + 11111111 1NNNNNNN 00000000 000000000 load and shift by 16 + N1111111 11111111 11111111 11NNNNNNN load and rotate right + + 00000000 00000000 00000000 1NNNNNNNN load and zero extend byte + 00000000 00000000 11111111 1NNNNNNNN load and zero extend word + + +*/ + +static int +synth_constant (operands, mode) + rtx operands[]; + enum machine_mode mode; { - int regs_saved; - int d = calc_live_regs (®s_saved); - int total_saved_regs_space = (regs_saved + regs_ever_live[PR_REG]) * 4; - int total_auto_space = get_frame_size (); + rtx dst; + int i = INTVAL (operands[1]) & 0xffffffff; + + if (CONST_OK_FOR_I (i)) + return 0; + if (TARGET_CLEN0 && mode != QImode) + return 0; - if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM) + if (mode != SImode) + { + if (reload_in_progress) + return 0; + dst = gen_reg_rtx (SImode); + } + else { - return total_saved_regs_space; + dst = operands[0]; } - if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM) + + /* 00000000 00000000 11111111 1NNNNNNNN load and zero extend word */ + if ((i & 0xffffff80) == 0x0000ff80) { - return total_saved_regs_space + total_auto_space; + emit_move_insn (dst, GEN_INT (sextb (i))); + emit_insn (gen_and_ffff (dst, dst)); + } + /* 00000000 00000000 00000000 1NNNNNNNN load and zero extend byte */ + else if ((i & 0xffffff80) == 0x00000080) + { + emit_move_insn (dst, GEN_INT (sextb (i))); + emit_insn (gen_and_ff (dst, dst)); + } + /* 00000000 00000000 00000000 NNNNNNNN0 load and shift by 1 + 11111111 11111111 11111111 NNNNNNNN0 load and shift by 1 */ + else if ((i & 0xffffff01) == 0 + || (i & 0xffffff01) == 0xffffff00) + { + emit_move_insn (dst, GEN_INT (sextb (i >> 1))); + emit_insn (gen_ashlsi3_n (dst, dst, GEN_INT (1))); } + /* 00000000 00000000 0000000N NNNNNNN00 load and shift by 2 + 11111111 11111111 1111111N NNNNNNN00 load and shift by 2*/ + else if ((i & 0xfffffe03) == 0 + || (i & 0xfffffe03) == 0xfffffe00) + { + emit_move_insn (dst, GEN_INT (sextb (i >> 2))); + emit_insn (gen_ashlsi3_n (dst, dst, GEN_INT (2))); + } + /* 00000000 00000000 0NNNNNNN 000000000 load and shift by 8 + 11111111 11111111 1NNNNNNN 000000000 load and shift by 8 */ - if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM) + else if ((i & 0xffff80ff) == 0 + || (i & 0xffff80ff) == 0xffff8000) + { + emit_move_insn (dst, GEN_INT (sextb (i >> 8))); + emit_insn (gen_ashlsi3_n (dst, dst, GEN_INT (8))); + } + /* 00000000 0NNNNNNN 00000000 000000000 load and shift by 16 + 11111111 1NNNNNNN 00000000 000000000 load and shift by 16 */ + else if ((i & 0xff80ffff) == 0x00000000 + || (i & 0xff80ffff) == 0xff800000) + { + emit_move_insn (dst, GEN_INT (sextb (i >> 16))); + emit_insn (gen_ashlsi3_n (dst, dst, GEN_INT (16))); + } + /* 00000000 00000000 0NNNNNNN 0NNNNNNNN load shift 8 and add */ + else if ((i & 0xffff8080) == 0 && TARGET_CLEN3) { - return total_auto_space; + emit_move_insn (dst, GEN_INT (sextb (i >> 8))); + emit_insn (gen_ashlsi3_n (dst, dst, GEN_INT (8))); + emit_insn (gen_addsi3 (dst, dst, GEN_INT (i & 0x7f))); } + else + return 0; + + if (mode == DImode) + { + /* Moving from SI to DI, we've got to zero out the high part */ + + emit_insn (gen_rtx (SET, VOIDmode, + gen_rtx (SUBREG, SImode, operands[0], 0), + dst)); + emit_insn (gen_rtx (SET, VOIDmode, + gen_rtx (SUBREG, SImode, operands[0], 1), + const0_rtx)); + + } + else if (mode != SImode) + { + emit_insn (gen_rtx (SET, VOIDmode, operands[0], + gen_rtx (SUBREG, mode, dst, 0))); + + } + return 1; } -delay_slots_for_epilogue () + +/* 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. */ + + +int +expand_block_move (operands) + rtx *operands; { - /* We need to find something to fill the epilogue if there won't be - any instructions to make the stack or pop registers which can be - moved into the slot */ + int align = INTVAL (operands[3]); + int constp = (GET_CODE (operands[2]) == CONST_INT); + int bytes = (constp ? INTVAL (operands[2]) : 0); + enum machine_mode mode; - int d; - calc_live_regs (&d); - return !(get_frame_size () + d); + /* IF odd then fail */ + if (!constp || bytes <= 0) + return 0; + + /* Don't expand if we'd make the code bigger and we don't want big code */ + + if (bytes > 8 && TARGET_SMALLCODE) + return 0; + + switch (align) + { + case 1: + mode = QImode; + break; + case 2: + mode = HImode; + break; + default: + mode = SImode; + align = 4; + } + + if (mode == SImode && constp && bytes < 64 && (bytes % 4 == 0)) + { + char entry[30]; + tree entry_name; + rtx func_addr_rtx; + rtx r4 = gen_rtx (REG, SImode, 4); + rtx r5 = gen_rtx (REG, SImode, 5); + sprintf (entry, "__movstr%s%d", GET_MODE_NAME (mode), bytes); + entry_name = get_identifier (entry); + + func_addr_rtx = copy_to_mode_reg (Pmode, + gen_rtx (SYMBOL_REF, Pmode, IDENTIFIER_POINTER (entry_name))); + emit_insn (gen_move_insn (r4, XEXP (operands[0], 0))); + emit_insn (gen_move_insn (r5, XEXP (operands[1], 0))); + emit_insn (gen_block_move_real (func_addr_rtx)); + return 1; + } + if (mode == SImode && constp && (bytes % 4 == 0)) + { + tree entry_name; + rtx func_addr_rtx; + rtx r4 = gen_rtx (REG, SImode, 4); + rtx r5 = gen_rtx (REG, SImode, 5); + rtx r6 = gen_rtx (REG, SImode, 6); + entry_name = get_identifier ("__movstr"); + + func_addr_rtx = copy_to_mode_reg (Pmode, + gen_rtx (SYMBOL_REF, Pmode, + IDENTIFIER_POINTER (entry_name))); + emit_insn (gen_move_insn (r4, XEXP (operands[0], 0))); + emit_insn (gen_move_insn (r5, XEXP (operands[1], 0))); + + /* r6 controls the size of the move, 16 is decremented from it + for each 64 bytes moved, then the -ve bit is used as an index into a + list of move instructions like this: + + { + do { + *dst++ = *src++; + *dst++ = *src++; + *dst++ = *src++; + ..etc.. 16 in all + *dst++ = *src++; + *dst++ = *src++; + size -= 16; + } while (size > 0); + + switch (size) + { + case -15: + *dst++ = *src++; + case -14: + *dst++ = *src++; + .. etc.. ; + case -2: + *dst++ = *src++; + case -1: + *dst++ = *src++; + case 0: + ; + } + } + + eg, a 72 byte move would be set up with size(r6) = 14, for one + iteration through the big while loop, and a switch of -2 for the last part */ + + { + int final_switch = 16 - ((bytes / 4) % 16); + int while_loop = ((bytes / 4) / 16 - 1) * 16; + emit_insn (gen_move_insn (r6, GEN_INT (while_loop + final_switch))); + emit_insn (gen_block_lump_real (func_addr_rtx)); + return 1; + } + } + + return 0; } - /* Prepare operands for a move define_expand; specifically, one of the - operands must be in a register */ + operands must be in a register. Take this chance to remove + addressing modes which can't be coped with very well. */ -void +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) + if (!(reload_in_progress || reload_completed) + && ((!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 ((mode == SImode || mode == HImode || mode == QImode) + && GET_CODE (operands[1]) == CONST_INT) + { + return synth_constant (operands, mode); + } + if (mode == DFmode || mode == DImode) + { + rtx src = operands[1]; + rtx dst = operands[0]; + rtx insns; + if (src == dst) + { + emit_insn (gen_rtx (SET, VOIDmode, dst, src)); + return 1; + } + + if (GET_CODE (src) == REG && + REGNO (src) >= FIRST_PSEUDO_REGISTER) + return 0; + + if (GET_CODE (dst) == REG && + REGNO (dst) >= FIRST_PSEUDO_REGISTER) + return 0; + + if (push_operand (dst, mode)) + return 0; + + if (GET_CODE (src) == CONST_DOUBLE) + src = force_const_mem (DFmode, src); + + if (reload_in_progress) + { + if (!(offsettable_memref_p (src) || register_operand (src, mode))) + return 0; + if (!(offsettable_memref_p (dst) || register_operand (dst, + mode))) + return 0; + } + start_sequence (); + if (GET_CODE (operands[0]) != REG + || !refers_to_regno_p (REGNO (operands[0]), REGNO (operands[0]) + 1, operands[1], 0)) + { + emit_move_insn (operand_subword (dst, 0, 1, mode), + operand_subword_force (src, 0, mode)); + emit_move_insn (operand_subword (dst, 1, 1, mode), + operand_subword_force (src, 1, mode)); + } + else + { + emit_move_insn (operand_subword (dst, 1, 1, mode), + operand_subword_force (src, 1, mode)); + emit_move_insn (operand_subword (dst, 0, 1, mode), + operand_subword_force (src, 0, mode)); + } + + insns = get_insns (); + end_sequence (); + + emit_no_conflict_block (insns, dst, src, 0, src); + return 1; + } + + return 0; +} /* Prepare the operands for an scc instruction; make sure that the compare has been done. */ rtx prepare_scc_operands (code) + int code; { - if (GET_CODE(sh_compare_op0) != REG - || REGNO(sh_compare_op0) != T_REG) + if (GET_CODE (sh_compare_op0) != REG + || REGNO (sh_compare_op0) != T_REG) { + int newcode = code; /* First need a compare insn */ - emit_insn (gen_rtx (SET, SImode, + switch (code) + { + case NE: + newcode = EQ; + break; + case LT: + newcode = GT; + break; + case LE: + newcode = GE; + break; + case LTU: + newcode = GTU; + break; + case LEU: + newcode = GEU; + break; + } + if (newcode != code) + { + rtx tmp = sh_compare_op0; + sh_compare_op0 = sh_compare_op1; + sh_compare_op1 = tmp; + code = newcode; + } + + sh_compare_op0 = force_reg (SImode, sh_compare_op0); + emit_insn (gen_rtx (SET, VOIDmode, gen_rtx (REG, SImode, T_REG), - gen_rtx (code, SImode, sh_compare_op0, - sh_compare_op1))); + gen_rtx (code, SImode, sh_compare_op0, sh_compare_op1))); } - return gen_rtx(REG, SImode, T_REG); + return gen_rtx (REG, SImode, T_REG); } -/* Functions to output assembly */ -/* Return a sequence of instructions to perform DI move, taking into - account overlapping source and dest registers */ +/* Functions to output assembly code. */ + +/* 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) +output_movedouble (insn, operands, mode) + rtx insn; rtx operands[]; enum machine_mode mode; { - if (register_operand (operands[0], mode) - && register_operand (operands[1], mode)) + rtx dst = operands[0]; + rtx src = operands[1]; + +/* fprintf (asm_out_file, "! move double \n"); + fprintf (asm_out_file, "! pc %04x\n", insn_addresses[INSN_UID (insn)]);*/ + if (GET_CODE (dst) == MEM + && GET_CODE (XEXP (dst, 0)) == POST_INC) + { + operands[0] = XEXP (XEXP (dst, 0), 0); + return "mov.l %R1,@(4,%0)\n\tmov.l %1,@%0\n\tadd #8,%0"; + } + if (register_operand (dst, mode) + && register_operand (src, mode)) { - if (REGNO (operands[1]) == MACH_REG) + if (REGNO (src) == MACH_REG) return "sts mach,%0\n\tsts macl,%R0"; - if (REGNO (operands[1]) > REGNO (operands[0])) + + /* + 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 %R1,%R0\n\tmov %1,%0 ! cra"; + else + return "mov %1,%0\n\tmov %R1,%R0 ! crb"; + } + else if (GET_CODE (src) == CONST_INT) + { + HOST_WIDE_INT val = INTVAL (src); + int rn = REGNO (operands[0]); + if (val < 0) { - return "mov %1,%0\n\tmov %R1,%R0"; + fprintf (asm_out_file, "\tmov #-1,r%d\n", rn); } - else + else { - return "mov %R1,%R0\n\tmov %1,%0"; + fprintf (asm_out_file, "\tmov #0,r%d\n", rn); } - } - if (GET_CODE (operands[1]) == CONST_INT) - { - if (INTVAL (operands[1]) < 0) - return "mov #-1,%0\n\tmov %1,%R0"; - else - return "mov #0,%0\n\tmov %1,%R0"; + fprintf (asm_out_file, "\tmov #%d,r%d\n", val, rn + 1); + return ""; } - - if (GET_CODE (operands[1]) == MEM) + else if (GET_CODE (src) == MEM) { - int idxreg = -1; - rtx inside = XEXP (operands[1], 0); + int ptrreg1 = -1; + int ptrreg2 = -1; + int dreg = REGNO (dst); + rtx inside = XEXP (src, 0); if (GET_CODE (inside) == REG) - idxreg = REGNO (inside); + { + ptrreg1 = REGNO (inside); + } else if (GET_CODE (inside) == PLUS) { rtx lhs = XEXP (inside, 0); rtx rhs = XEXP (inside, 1); if (GET_CODE (lhs) == REG) - idxreg = REGNO (lhs); - else if (GET_CODE (rhs) == REG) - idxreg = REGNO (rhs); - else - abort (); + ptrreg1 = REGNO (lhs); + if (GET_CODE (rhs) == REG) + ptrreg2 = REGNO (rhs); + } + else if (GET_CODE (inside) == LABEL_REF) + { + return "mov.l %1,%0\n\tmov.l %1+4,%R0"; + } + else if (GET_CODE (inside) == POST_INC) + { + return "mov.l %1,%0\n\tmov.l %1,%R0 !mdi\n"; } else abort (); - if (REGNO (operands[0]) != idxreg) + 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) { - /* The dest register is mentioned in the addressing mode, - so print them the other way around */ - return "mov.l %1,%0\n\tmov.l %R1,%R0 ! one way"; + /* Copy into the second half first */ + return "mov.l %R1,%R0\n\tmov.l %1,%0 ! cr"; } - return "mov.l %R1,%R0\n\tmov.l %1,%0 ! other way"; } - return "mov.l %R1,%R0\n\tmov.l %1,%0"; + return "mov.l %1,%0\n\tmov.l %R1,%R0"; } /* Emit assembly to shift reg by k bits */ char * -output_shift (string, reg, k) +output_shift (string, reg, k, code) char *string; rtx reg; rtx k; + int code; + { int s = INTVAL (k); + if (s < 0) + { + s = -s; + switch (code) + { + case LSHIFTRT: + case ASHIFTRT: + code = ASHIFT; + break; + case ASHIFT: + code = ASHIFTRT; + break; + default: + abort (); + } + } + 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; @@ -784,143 +1019,146 @@ output_shift (string, reg, k) return ""; } + +void +function_epilogue (stream, size) + FILE *stream; + int size; +{ + pragma_interrupt = pragma_trapa = 0; +} + + /* Return the text of the branch instruction which matches its length - attribute. */ + 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. */ + +int pending_const_table; + + /* We can't tell if we need a register as a scratch for the jump + until after branch shortening, and then it's too late to allocate a + register the 'proper' way. These instruction sequences are rare + anyway, so to avoid always using a reg up from our limited set, we'll + grab one when we need one on output. */ char * -output_branch (logic, insn) - int logic; - rtx *insn; +output_far_jump (insn, op) + rtx insn; + rtx op; { - extern rtx recog_operand[]; - int label = lf++; - - switch (get_attr_length (insn)) + rtx thislab = gen_label_rtx (); + + if (dbr_sequence_length ()) { - case 2: - /* Simple branch in range -200..+200 bytes */ - return logic ? "bt %l0" : "bf %l0"; + /* Something to go in what would have been the delay + slot if this had been a short branch. Make sure the + reg we use to generate the branch target address + doesn't conflict */ - case 6: - /* Branch in range -4000..+4000 bytes */ - fprintf (asm_out_file, "\tb%c\tLF%d\n", logic ? 'f' : 't', label); - 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 ""; + int i; + rtx vec[2]; + vec[0] = thislab; - case 8: - /* Branches a long way away */ - - fprintf (asm_out_file, "\tb%c\tLF%d\n", logic ? 'f' : 't', label); - output_asm_insn ("mov.l %I0,r13", recog_operand); - fprintf (asm_out_file, "\tjmp @r13 ! 32 cond \n"); - fprintf (asm_out_file, "\tor r0,r0\n"); - fprintf (asm_out_file, "LF%d:\n", label); - return ""; - } - return "bad"; + for (i = 0; i < 8; i++) + { + vec[1] = gen_rtx (REG, SImode, i); + if (!reg_referenced_p (vec[1], + PATTERN (XVECEXP (final_sequence, 0, 1)))) + break; + } -} - -/* Predicates used by the templates */ -/* Nonzero if OP is a normal arithmetic register. */ + print_slot (final_sequence); + output_asm_insn ("mov.l %1,@-r15", vec); + output_asm_insn ("mov.l %O0,%1", vec); -int -arith_reg_operand(op, mode) - rtx op; - enum machine_mode mode; -{ - if (register_operand (op, mode)) + output_asm_insn ("jmp @%1 ! 32 xcond", vec); + output_asm_insn ("mov.l @r15+,%1", vec); + } + else { - if (GET_CODE (op) == REG) - return REGNO (op) != T_REG; - return 1; + output_asm_insn ("mov.l r13,@-r15", 0); + output_asm_insn ("mov.l %O0,r13", &thislab); + output_asm_insn ("jmp @r13 ! 32 zcond", 0); + output_asm_insn ("mov.l @r15+,r13", 0); } - return 0; -} - -/* Nonzero if OP is a valid source operand for an arithmetic insn. */ + output_asm_insn (".align 2", 0); + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (thislab)); + output_asm_insn (".long %O0", &op); + return ""; +} -int -arith_operand (op, mode) - rtx op; - enum machine_mode mode; +char * +output_branch (logic, insn) + int logic; + rtx insn; { - if (register_operand (op, mode)) - return 1; + extern rtx recog_operand[]; + int label = lf++; - if (GET_CODE (op) == CONST_INT) + /* fprintf (asm_out_file, "! pc %04x\n", insn_addresses[INSN_UID (insn)]);*/ + + switch (get_attr_length (insn)) { - if (CONST_OK_FOR_I (INTVAL (op))) - return 1; - } - return 0; -} + 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]; -/* 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 (need_slot (final_sequence)) + { + fprintf (asm_out_file, "\tb%c.s\tLF%d\n", logic ? 'f' : 't', + label); - if (GET_CODE (op) == CONST_INT) - { - if (CONST_OK_FOR_L (INTVAL (op))) - return 1; - } - return 0; -} + print_slot (final_sequence); + } -/* Nonzero if p is a valid shift operand for lshr and ashl */ + else + { + fprintf (asm_out_file, "\tb%c\tLF%d\n", logic ? 'f' : 't', + label); + } + recog_operand[0] = oldop; -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; -} + output_asm_insn ("bra %l0 ! 12 bit cond ", recog_operand); + fprintf (asm_out_file, "\tor r0,r0\n"); + fprintf (asm_out_file, "LF%d:\n", label); + } + return ""; -/* Nonzero if the arg is an immediate which has to be loaded from - memory */ + case 16: + /* Branches a long way away */ + { + rtx oldop = recog_operand[0]; -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; + 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); + } + + output_far_jump (insn, oldop); + fprintf (asm_out_file, "LF%d:\n", label); + return ""; + } } - return 0; + return "bad"; } + /* 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 @@ -930,57 +1168,61 @@ hard_immediate_operand (op, mode) It is important to minimize this, since the branches will slow things down and make things bigger. - Worst case code looks like: + 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. + mov.l L1,rn + bra L2 + nop + align + L1: .long value + L2: + .. + + mov.l L3,rn + bra L4 + nop + align + L3: .long value + L4: + .. + + We fix this by performing a scan before scheduling, which notices which + instructions need to have their operands fetched from the constant table + and builds the table. + + + The algorithm is: + + scan, find an instruction which needs a pcrel move. Look forward, find the + last barrier which is within MAX_COUNT bytes of the requirement. + If there isn't one, make one. Process all the instructions between + the find and the barrier. 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 L1,rn .. - mov.l L3,rn - bra L4 + mov.l L3,rn + bra L4 nop align -L3:.long value -L4:.long value + 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; + rtx value; /* Value in table */ + rtx label; /* Label of value */ + enum machine_mode mode; /* Mode of value */ +} + +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 @@ -990,28 +1232,19 @@ typedef struct static pool_node pool_vector[MAX_POOL_SIZE]; static int pool_size; +/* Add a constant to the pool and return its label. */ -/* Add a constant to the pool and return its label number. */ - -static int +static rtx 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; - } - + rtx lab; /* 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) { @@ -1020,250 +1253,1273 @@ add_constant (x, mode) if (XINT (x, 3) != XINT (pool_vector[i].value, 3)) continue; } + if (rtx_equal_p (x, pool_vector[i].value)) + return pool_vector[i].label; } - - if (rtx_equal_p (x, pool_vector[i].value)) - return pool_vector[i].number; } - + + /* Need a new one */ pool_vector[pool_size].value = x; + lab = gen_label_rtx (); pool_vector[pool_size].mode = mode; - pool_vector[pool_size].number = lf; + pool_vector[pool_size].label = lab; pool_size++; + return lab; +} + +/* Dump out interesting debug info */ - return lf++; +void +final_prescan_insn (insn, opvec, noperands) + rtx insn; + rtx *opvec; + int noperands; +{ + if (target_flags & ISIZE_BIT) + { + extern int *insn_addresses; + fprintf (asm_out_file, "\n! at %04x\n", + insn_addresses[INSN_UID (insn)]); + } } + + -/* Nonzero if the insn could take a constant table. */ + +/* 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 -has_constant_table (insn) - rtx insn; +output_option (file, sep, type, name, indent, pos, max) + FILE *file; + char *sep; + char *type; + char *name; + char *indent; + int pos; + int max; { - rtx body; + 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); +} - if (GET_CODE (insn) == NOTE - || GET_CODE (insn) == BARRIER - || GET_CODE (insn) == CODE_LABEL) - return 0; +static struct +{ + char *name; + int value; +} - 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) +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); + fprintf (file, "! %d %d\n", max_count_si, max_count_hi); +} + +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) (release I-1) arguments:", version_string); + output_options (file, f_options, f_len, W_options, W_len, + pos, 75, " ", "\n! ", "\n\n"); +} + + + +/* 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; - if (GET_CODE (body) == UNSPEC_VOLATILE) + /* otherwise it will be several insns, but we pretend that it will be more than + just the components, so that combine doesn't glue together a load of shifts into + one shift which has to be emitted as a bunch anyway - breaking scheduling */ + return 1; +} + +int +andcosts (RTX) + rtx RTX; +{ + int i; + if (GET_CODE (XEXP (RTX, 1)) != CONST_INT) + return 2; + i = INTVAL (XEXP (RTX, 1)); + /* And can use the extend insns cheaply */ + if (i == 0xff || i == 0xffff) + return 2; + /* Any small constant is reasonably cheap - but requires r0 */ + if (CONST_OK_FOR_I (i)) + return 3; + return 5; +} + +int +howshift (i) + int i; +{ + int total = 0; + while (i > 0) { - return INTVAL (XVECEXP (body, 0, 0)) == 1; + if (i >= 16) + { + total++; + i -= 16; + } + else if (i >= 8) + { + total++; + i -= 8; + } + else if (i >= 2) + { + total++; + i -= 2; + } + else if (i >= 1) + { + total++; + i--; + } } - return 0; + return total; } -/* Adjust the length of an instruction. +/* Return the cost of a multiply */ +int +multcosts (RTX) + rtx RTX; +{ + /* If mult by a power of 2 then work out how we'd shift to make it */ + int insn_cost = 0; - We'll look at the previous instruction which holds a constant - table and see if we can move the table to here instead. */ + if (GET_CODE (XEXP (RTX, 1)) == CONST_INT) + { + int i = exact_log2 (INTVAL (XEXP (RTX, 1))); + if (i >= 0) + insn_cost = howshift (i); + else + insn_cost = 100000; + } + if (TARGET_SH2) + { + /* We have a mul insn, so we can never take more than the mul and the + read of the mac reg, but count more because of the latency and extra reg + usage */ + if (TARGET_SMALLCODE) + return 2; + if (insn_cost > 5) + return 5; + return insn_cost; + } -int target_insn_uid; -int target_insn_smallest_size; + /* If we we're aiming at small code, then just count the number of + insns in a multiply call sequence */ -int target_pc; -int target_insn_range; -int current_pc; -int table_size; + if (TARGET_SMALLCODE) + { + if (insn_cost > 6) + return 6; + return insn_cost; + } + + /* Otherwise count all the insns in the routine we'd be calling too */ + return 20; +} + +/* Code to expand a shift */ void -adjust_insn_length (insn, insn_lengths) - rtx insn; - short *insn_lengths; +gen_ashift (type, n, reg) + int type; + int n; + rtx reg; +{ + switch (type) + { + case ASHIFTRT: + emit_insn (gen_ashrsi3_k (reg, reg, GEN_INT (n))); + break; + case LSHIFTRT: + emit_insn (gen_lshrsi3_k (reg, reg, GEN_INT (n))); + break; + case ASHIFT: + if (n == 1) + emit_insn (gen_addsi3 (reg, reg, reg)); + else + emit_insn (gen_ashlsi3_k (reg, reg, GEN_INT (n))); + break; + } +} + +int +gen_shifty_op (code, operands) + int code; + rtx *operands; +{ + rtx wrk = gen_reg_rtx (SImode); + rtx t; + char *func; + if (GET_CODE (operands[2]) == CONST_INT) + { + int value = INTVAL (operands[2]); + top: + switch (code) + { + case ASHIFTRT: + if (value < 0) + { + code = ASHIFT; + value = -value; + goto top; + } + + /* Expand a short sequence inline, longer call a magic routine */ + if (value <= 5) + { + emit_move_insn (wrk, operands[1]); + while (value--) + { + gen_ashift (ASHIFTRT, 1, wrk); + } + emit_move_insn (operands[0], wrk); + return 1; + } + t = gen_reg_rtx (Pmode); + /* Load the value into an arg reg and call a helper */ + emit_move_insn (gen_rtx (REG, SImode, 4), operands[1]); + if (!shiftsyms[value]) + { + func = xmalloc (18); + sprintf (func, "__ashiftrt_r4_%d", value); + shiftsyms[value] = gen_rtx (SYMBOL_REF, Pmode, func); + } + emit_move_insn (t, shiftsyms[value]); + emit_insn (gen_ashrsi3_n (GEN_INT (value), t)); + emit_move_insn (operands[0], gen_rtx (REG, SImode, 4)); + return 1; + + case ASHIFT: + if (value < 0) + { + code = LSHIFTRT; + value = -value; + goto top; + } + /* Fall through */ + case LSHIFTRT: + + if (value < 0) + { + code = ASHIFT; + value = -value; + goto top; + } + + emit_move_insn (wrk, operands[1]); + while (value) + { + if (value >= 16) + { + gen_ashift (code, 16, wrk); + value -= 16; + } + else if (value >= 8) + { + gen_ashift (code, 8, wrk); + value -= 8; + } + else if (value >= 2) + { + gen_ashift (code, 2, wrk); + value -= 2; + } + else + { + gen_ashift (code, 1, wrk); + value--; + } + } + emit_move_insn (operands[0], wrk); + return 1; + + } + } + return 0; +} + +/* Dump out any constants accumulated in the final pass - + which will only be labels */ +char * +output_jump_label_table () +{ + int i; + if (pool_size) + { + fprintf (asm_out_file, "\t.align 2\n"); + for (i = 0; i < pool_size; i++) + { + pool_node *p = pool_vector + i; + + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (p->label)); + output_asm_insn (".long %O0", &p->value); + } + pool_size = 0; + } + + return ""; +} +/* Output the literal table */ + +static void +dump_table (scan) + rtx scan; { - int uid = INSN_UID (insn); + int i; + int need_align = 1; + - current_pc += insn_lengths[uid]; + /* Do two passes, first time dump out the HI sized constants */ - if (has_constant_table (insn)) + for (i = 0; i < pool_size; i++) { - if (current_pc >= target_insn_range) + pool_node *p = pool_vector + i; + if (p->mode == HImode) { - /* 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; + if (need_align) + { + scan = emit_insn_after (gen_align_2 (), scan); + need_align = 0; + } + scan = emit_label_after (p->label, scan); + scan = emit_insn_after (gen_consttable_2 (p->value), scan); + } + } + need_align = 1; + + for (i = 0; i < pool_size; i++) + { + pool_node *p = pool_vector + i; + + switch (p->mode) + { + case HImode: + break; + case SImode: + if (need_align) + { + need_align = 0; + scan = emit_label_after (gen_label_rtx (), scan); + scan = emit_insn_after (gen_align_4 (), scan); + } + scan = emit_label_after (p->label, scan); + scan = emit_insn_after (gen_consttable_4 (p->value), scan); + break; + case DImode: + if (need_align) + { + need_align = 0; + scan = emit_label_after (gen_label_rtx (), scan); + scan = emit_insn_after (gen_align_4 (), scan); + } + scan = emit_label_after (p->label, scan); + scan = emit_insn_after (gen_consttable_8 (p->value), scan); + break; + default: + abort (); + break; + } + } + + scan = emit_insn_after (gen_consttable_end (), scan); + scan = emit_barrier_after (scan); + pool_size = 0; +} + + + +/* Non zero if the src operand needs to be fixed up */ +static +int +fixit (src, mode) + rtx src; + enum machine_mode mode; +{ + if (mode == QImode) + return 0; /* QIs never need to be fixed */ + if (GET_CODE (src) == CONST) + return 1; + + if (GET_CODE (src) == SYMBOL_REF) + { + return 1; + } + if (GET_CODE (src) == LABEL_REF) + { + return 1; + } + if (GET_CODE (src) == CONST_INT) + { + /* All QI insns are ok */ + if (mode == QImode) + return 1; + /* The rest may need to be fixed */ + return !CONST_OK_FOR_I (INTVAL (src)); + } + return 0; +} + +/* Return Non-zero if constant would be an ok source for a + mov.w instead of a mov.l */ +int +hi_const (src) + rtx src; +{ + if (GET_CODE (src) == CONST + && GET_CODE (XEXP (src, 0)) == SIGN_EXTEND + && GET_CODE (XEXP (XEXP (src, 0), 0)) == SYMBOL_REF) + return 1; + + if (TARGET_SHORTADDR + && GET_CODE (src) == SYMBOL_REF) + return 1; + + return (GET_CODE (src) == CONST_INT + && INTVAL (src) >= -32768 + && INTVAL (src) <= 32767); +} + +/* Find the last barrier less than MAX_COUNT bytes from FROM, or create one. + If an HI move is found, then make sure that MAX_COUNT_HI isn't broken from that one. */ + +static +rtx +find_barrier (from) + rtx from; +{ + int count_si = 0; + int count_hi = 0; + int found_hi = 0; + int found_si = 0; + rtx found_barrier = 0; + while (from + && count_si < max_count_si + && count_hi < max_count_hi) + { + int inc; + if (GET_CODE (from) == BARRIER) + { + found_barrier = from; + } + /* Count the length of this insn - we assume that all moves will + be 2 bytes long, except the DIs */ + + if (GET_CODE (from) == INSN && + GET_CODE (PATTERN (from)) == SET) + { + rtx src = SET_SRC (PATTERN (from)); + if (hi_const (src)) + found_hi = 1; + else + found_si = 1; + inc = (GET_MODE_SIZE (GET_MODE (src)) > 4) ? 4 : 2; } 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. */ + inc = get_attr_length (from); + } + if (found_si) + count_si += inc; + if (found_hi) + count_hi += inc; + from = NEXT_INSN (from); + } + if (!found_barrier) + { + /* We didn't find a barrier in time to + dump our stuff, so we'll make one */ + rtx label = gen_label_rtx (); + /* Walk back to be just before any jump */ + from = PREV_INSN (from); + while (GET_CODE (from) == JUMP_INSN + || GET_CODE (from) == NOTE + || GET_CODE (from) == CODE_LABEL) + { + from = PREV_INSN (from); + } + from = emit_jump_insn_after (gen_jump (label), from); + JUMP_LABEL (from) = label; + found_barrier = emit_barrier_after (from); + emit_label_after (label, found_barrier); + return found_barrier; + } + return found_barrier; +} - /* Add the stuff from this insn to what will go in the - growing table. */ +/* Non zero if the insn is a move instruction which needs to be fixed. */ - table_size += get_attr_constantsize (insn); +static +int +broken_move (insn) + rtx insn; +{ + if (!INSN_DELETED_P (insn) + && GET_CODE (insn) == INSN + && GET_CODE (PATTERN (insn)) == SET) + { + rtx pat = PATTERN (insn); + rtx src = SET_SRC (pat); + rtx dst = SET_DEST (pat); + enum machine_mode mode = GET_MODE (dst); + if (dst == pc_rtx) + return 0; + return fixit (src, mode); + } + return 0; +} - /* 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. */ +/* Exported to toplev.c - insn_lengths[uid] = get_attr_largestsize (insn) + table_size; + Scan the function looking for move instructions which have to be changed to + pcrel loads and insert the literal tables. */ +void +machine_dependent_reorg (first) + rtx first; +{ + rtx insn; + for (insn = first; insn; insn = NEXT_INSN (insn)) + { + if (broken_move (insn)) + { + /* This is a broken move instruction, scan ahead looking for + a barrier to stick the constant table behind */ + rtx scan; + rtx barrier = find_barrier (insn); + + /* Now find all the moves between the points and modify them */ + for (scan = insn; scan != barrier; scan = NEXT_INSN (scan)) + { + if (broken_move (scan)) + { + rtx pat = PATTERN (scan); + rtx src = SET_SRC (pat); + rtx dst = SET_DEST (pat); + enum machine_mode mode = GET_MODE (dst); + rtx lab; + rtx newinsn; + rtx newsrc; + /* This is a broken move instruction, add it to the pool */ + + if (mode == SImode && hi_const (src)) + { + /* This is an HI source, clobber the dest to get the mode right too */ + mode = HImode; + while (GET_CODE (dst) == SUBREG) + dst = SUBREG_REG (dst); + dst = gen_rtx (REG, HImode, REGNO (dst)); + } + lab = add_constant (src, mode); + newsrc = gen_rtx (MEM, mode, + gen_rtx (LABEL_REF, VOIDmode, lab)); + + /* Build a jump insn wrapper around the move instead + of an ordinary insn, because we want to have room for + the target label rtx in fld[7], which an ordinary + insn doesn't have. */ + newinsn = emit_jump_insn_after (gen_rtx (SET, VOIDmode, + dst, newsrc), scan); + JUMP_LABEL (newinsn) = lab; + + /* But it's still an ordinary insn */ + PUT_CODE (newinsn, INSN); + + /* Kill old insn */ + delete_insn (scan); + scan = newinsn; + } + } + dump_table (barrier); } - /* Current insn becomes the target. */ - target_insn_uid = uid; - target_insn_smallest_size = get_attr_smallestsize (insn); + } +} + +/* Called from the md file, set up the operands of a compare instruction */ +void +from_compare (operands, code) + rtx *operands; + int code; +{ + if (code != EQ && code != NE) + { + /* Force args into regs, since we can't use constants here */ + sh_compare_op0 = force_reg (SImode, sh_compare_op0); + if (sh_compare_op1 != const0_rtx) + sh_compare_op1 = force_reg (SImode, sh_compare_op1); } + operands[1] = sh_compare_op0; + operands[2] = sh_compare_op1; +} + +/* Non-zero if x is EQ or NE */ +int +equality_operator (x, mode) + rtx x; + enum machine_mode mode; +{ + enum rtx_code code = GET_CODE (x); + return (code == EQ || code == NE); } -/* Dump out the pending constant pool. - If label provided then insert an branch in the middle of the table - */ +/* Add this function to the list of ones seen - temporary + gross hack to try out bsrs. */ +struct flist +{ + char *name; + struct flist *next; +}; +struct flist *head; + +static void +add_function (name) + char *name; +{ + struct flist *n = (struct flist *) xmalloc (sizeof (struct flist)); + int l = strlen (name) + 1; + n->name = xmalloc (l); + memcpy (n->name, name, l); + n->next = head; + head = n; +} static int -dump_constants (label) +seen_function (name) + char *name; +{ + struct flist *p = head; + for (p = head; p; p = p->next) + { + if (strcmp (p->name, name) == 0) + return 1; + } + return 0; +} + + /* Framefull frame looks like: + + arg-5 + arg-4 + [ if current_function_anonymous_args + arg-3 + arg-2 + arg-1 + arg-0 ] + saved-fp + saved-r10 + saved-r11 + saved-r12 + saved-pr + local-n + .. + local-1 + local-0 <- fp points here + + + If TARGET_SMALLCALL, then the preserved registers are pushed by a + wrapper before the routine is entered, so the regs are always pushed + and there are two pr's on the stack - the caller and the wrapper. + */ + + + /* Code to generate prologue and epilogue sequences */ + + +void +sh_expand_prologue () +{ + int live_regs_mask; + int d; + extern tree current_function_decl; + live_regs_mask = calc_live_regs (&d); + + /* We have pretend args if we had an object sent partially in registers + and partially on the stack - eg a large structure */ + output_stack_adjust (-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; + } + } + push_regs (live_regs_mask); + output_stack_adjust (-get_frame_size ()); + + if (frame_pointer_needed) + { + emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx)); + } + if (TARGET_BSR) + { + add_function (IDENTIFIER_POINTER (DECL_NAME (current_function_decl))); + } + + +} + +void +sh_expand_epilogue () { + int live_regs_mask; + int d; int i; - int rlabel = label; - int size = 0; + + live_regs_mask = calc_live_regs (&d); + - for (i = 0; i < pool_size; i++) + if (frame_pointer_needed) { - pool_node *p = pool_vector + i; - fprintf (asm_out_file, "\n\t! constants - waited %d\n", pc - first_pc); - fprintf (asm_out_file, "\t.align\t2\n"); - fprintf (asm_out_file, "LK%d:", p->number); - size += GET_MODE_SIZE (p->mode); - - switch (GET_MODE_CLASS (p->mode)) + emit_insn (gen_movsi (stack_pointer_rtx, frame_pointer_rtx)); + } + output_stack_adjust (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)) { - 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); - } + pop (j); } - - /* After 200 bytes of table, stick in another branch */ - if (label && size > 200) + } + + output_stack_adjust (extra_push + current_function_pretend_args_size); + + extra_push = 0; + current_function_pretend_args_size = 0; + current_function_anonymous_args = 0; + for (i = 0; i < 32; i++) + shiftsyms[i] = 0; + +} + +/* 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 from; + int to; +{ + int regs_saved; + int total_saved_regs_space; + int total_auto_space = get_frame_size (); + + calc_live_regs (®s_saved); + total_saved_regs_space = (regs_saved) * 4; + + if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM) + { + return total_saved_regs_space + total_auto_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) + { + /* Initial gap between fp and sp is 0 */ + return 0; + } + abort (); +} + +/* Handle machine specific pragmas to be semi-compatible with Hitachi + compiler */ + +int +handle_pragma (file) + FILE *file; +{ + int c; + char pbuf[200]; + int psize = 0; + + c = getc (file); + while (c == ' ' || c == '\t') + c = getc (file); + + if (c == '\n' || c == EOF) + return c; + + while (psize < sizeof (pbuf) - 1 && c != '\n') + { + pbuf[psize++] = c; + if (psize == 9 && strncmp (pbuf, "interrupt", 9) == 0) + { + pragma_interrupt = 1; + return ' '; + } + if (psize == 5 && strncmp (pbuf, "trapa", 5) == 0) { - rlabel = lf ++; - fprintf (asm_out_file,"LF%d:\tbra LF%d\n", label, rlabel); - fprintf (asm_out_file,"\tor r0,r0\n"); - label = 0; + pragma_interrupt = pragma_trapa = 1; + return ' '; } + c = getc (file); + } + return c; +} + +/* insn expand helpers */ + +/* Emit insns to perform a call. + If TARGET_SHORTADDR then use a bsr. If TARGET_SMALLCALL, then load the + target address into r1 and call __saveargs, otherwise + perform the standard call sequence */ + +void +expand_acall (isa_retval, operands) + int isa_retval; + rtx *operands; +{ + rtx call; + rtx ret = operands[0]; + rtx call_target = operands[isa_retval + 0]; + rtx numargs = operands[isa_retval + 1]; + + if (TARGET_BSR && bsr_operand (call_target, VOIDmode)) + { + call = gen_rtx (CALL, VOIDmode, call_target, numargs); + } + else + { + if (GET_CODE (call_target) == MEM) + { + call_target = force_reg (Pmode, + XEXP (call_target, 0)); + } + if (TARGET_SMALLCALL) + { + rtx tmp = gen_reg_rtx (SImode); + rtx r1 = gen_rtx (REG, SImode, 1); + emit_move_insn (tmp, gen_rtx (SYMBOL_REF, SImode, "__saveargs")); + emit_move_insn (r1, call_target); + emit_insn (gen_rtx (USE, VOIDmode, r1)); + call_target = tmp; + } + + call = gen_rtx (CALL, VOIDmode, gen_rtx (MEM, SImode, call_target), numargs); + } + if (isa_retval) + { + call = gen_rtx (SET, VOIDmode, ret, call); + } + + emit_call_insn (gen_rtx (PARALLEL, VOIDmode, + gen_rtvec (2, + call, + gen_rtx (CLOBBER, VOIDmode, gen_rtx (REG, SImode, 17))))); + +} + + +/* Predicates used by the templates */ + + +/* Returns 1 if OP can be source of a simple move operation. + Same as general_operand, but a LABEL_REF is valid, PRE_DEC is + invalid as are subregs of system registers. */ + +int +general_movsrc_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + /* Any MEM(label_ref) is ok, that's a pcrel load */ + if (GET_CODE (op) == MEM + && GET_CODE (XEXP (op, 0)) == LABEL_REF) + return 1; + + if (GET_CODE (op) == MEM) + { + rtx inside = XEXP (op, 0); + if (GET_CODE (inside) == CONST) + inside = XEXP (inside, 0); + + if (GET_CODE (inside) == LABEL_REF) + return 1; + + if (GET_CODE (inside) == PLUS + && GET_CODE (XEXP (inside,0)) == LABEL_REF + && GET_CODE (XEXP (inside,1)) == CONST_INT) + return 1; - fprintf (asm_out_file, "\n"); + /* No post inc allowed */ + if (GET_CODE (inside) == POST_DEC + || GET_CODE (inside) == PRE_INC + || GET_CODE (inside) == PRE_DEC) + return 0; + + /* Can't do that with large modes */ + if (GET_CODE (inside) == POST_INC + && GET_MODE_SIZE (mode) > 4) + return 0; } - pool_size = 0; - current_pc = 0; - target_insn_range = 0; - return rlabel; - + + if ((mode == QImode || mode == HImode) + && (GET_CODE (op) == SUBREG + && GET_CODE (XEXP (op, 0)) == REG + && system_reg_operand (XEXP (op, 0), mode))) + return 0; + + if (GET_CODE (op) == CONST_INT) + { + int i = INTVAL (op); + return CONST_OK_FOR_I (i); + } + return general_operand (op, mode); } -/* Emit the text to load a value from a constant table. */ +/* Returns 1 if OP can be a destination of a move. + Same as general_operand, but no preinc allowed. */ -char * -output_movepcrel (insn, operands, mode) - rtx insn; - rtx operands[]; +int +general_movdst_operand (op, mode) + rtx op; enum machine_mode mode; { - int len = GET_MODE_SIZE (mode); - int rn = REGNO (operands[0]); + /* No pre dec allowed */ + if (GET_CODE (op) == MEM + && (GET_CODE (XEXP (op, 0)) == PRE_INC + || GET_CODE (XEXP (op, 0)) == POST_INC + || GET_CODE (XEXP (op, 0)) == POST_DEC)) + return 0; + + if (GET_CODE (op) == MEM + && GET_CODE (XEXP (op, 0)) == PRE_DEC + && GET_MODE_SIZE (mode) > 4) + return 0; - fprintf (asm_out_file, "\tmov.l LK%d,r%d\n", - add_constant (operands[1], mode), rn); + return general_operand (op, mode); +} - if (GET_MODE_SIZE(mode) > 4) + + +/* Returns 1 if OP is valid destination for a bsr. */ + +int +bsr_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (TARGET_BSR) { - fprintf (asm_out_file, - "\tmov.l LK%d+4,r%d\n", - add_constant (operands[1], mode), - rn + 1); - - } - /* 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"); + if (GET_CODE (op) == SYMBOL_REF) + { + if (!strcmp (XSTR (op, 0), + IDENTIFIER_POINTER (DECL_NAME (current_function_decl)))) + return 1; + return (seen_function (XSTR (op, 0))); + } } - return ""; + return 0; } - -/* Dump out interesting debug info */ +/* Returns 1 if OP is an immediate ok for a byte index. */ -void -final_prescan_insn (insn, opvec, noperands) - rtx insn; - rtx *opvec; - int noperands; +int +byte_index_operand (op, mode) + rtx op; + enum machine_mode mode; { - register rtx body = PATTERN (insn); + return (GET_CODE (op) == CONST_INT + && INTVAL (op) >= 0 + && INTVAL (op) <= 15); +} - if (target_flags & ISIZE_BIT) +/* Returns 1 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; +} + + +/* Returns 1 if OP is a normal arithmetic register. */ + +int +arith_reg_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (register_operand (op, mode)) { - extern int *insn_addresses; + if (GET_CODE (op) == REG) + return (REGNO (op) != T_REG + && REGNO (op) != PR_REG); + return 1; + } + return 0; +} + +/* Returns 1 if OP is MACL, MACH or PR. */ + +int +system_reg_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (GET_CODE (op) == REG) + { + switch (REGNO (op)) + { + case PR_REG: + case MACL_REG: + case MACH_REG: + return 1; + } + } + return 0; +} - 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)); +/* Returns 1 if OP is a valid source operand for an arithmetic insn. */ - if (TARGET_DUMP_RTL) - print_rtl (asm_out_file, body); +int +arith_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (arith_reg_operand (op, mode)) + return 1; + if (GET_CODE (op) == CONST_INT) + { + if (CONST_OK_FOR_I (INTVAL (op))) + return 1; + } + return 0; +} + + +/* Returns 1 if OP is a valid source operand for a logical operation. */ + +int +logical_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (arith_reg_operand (op, mode)) + return 1; + if (GET_CODE (op) == CONST_INT) + { + if (CONST_OK_FOR_L (INTVAL (op))) + return 1; } - - pc += get_attr_length (insn); + return 0; +} + +/* Returns 1 if OP is a valid operand for a MAC instruction, + either a register or indirect memory. For now we don't + try and recognise a mac insn */ - if (pool_size && pc - first_pc > MUST_DUMP_LEVEL) +int +mac_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (arith_reg_operand (op, mode)) + return 1; +#if 0 + Turned off till mac is understood + if (GET_CODE (op) == MEM) + return 1; +#endif + return 0; +} + +/* Determine where to put an argument to a function. + Value is zero to push the argument on the stack, + or a hard register in which to store the argument. + + MODE is the argument's machine mode. + TYPE is the data type of the argument (as a tree). + This is null for libcalls where that information may + not be available. + CUM is a variable of type CUMULATIVE_ARGS which gives info about + the preceding args and about the function being called. + NAMED is nonzero if this argument is a named parameter + (otherwise it is an extra parameter matching an ellipsis). */ + +rtx +sh_function_arg (cum, mode, type, named) + CUMULATIVE_ARGS cum; + enum machine_mode mode; + tree type; + int named; +{ + if (named) { - /* 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"); + int rr = (ROUND_REG ((cum), (mode))); + + if (rr < NPARM_REGS) + { + return (((type) == 0 || !TREE_ADDRESSABLE ((tree) (type))) + && ((type) == 0 || (mode) != BLKmode + || (TYPE_ALIGN ((type)) % PARM_BOUNDARY == 0)) + ? gen_rtx (REG, (mode), + (FIRST_PARM_REG + rr)) + : 0); + + } } - + return 0; +} + +/* For an arg passed partly in registers and partly in memory, + this is the number of registers used. + For args passed entirely in registers or entirely in memory, zero. + Any arg that starts in the first 4 regs but won't entirely fit in them + needs partial registers on the SH. */ + +int +sh_function_arg_partial_nregs (CUM, MODE, TYPE, NAMED) + CUMULATIVE_ARGS CUM; + enum machine_mode MODE; + tree TYPE; + int NAMED; +{ + if ((CUM) < NPARM_REGS) + { + if (((TYPE) == 0 || !TREE_ADDRESSABLE ((tree) (TYPE))) + && ((TYPE) == 0 || (TYPE_ALIGN ((TYPE)) % PARM_BOUNDARY == 0)) + && ((CUM) + ((MODE) == BLKmode + ? ROUND_ADVANCE (int_size_in_bytes (TYPE)) + : ROUND_ADVANCE (GET_MODE_SIZE (MODE))) - NPARM_REGS > 0)) + { + return NPARM_REGS - CUM; + } + } + return 0; } +/* Turn this on to recognise shift insns which aren't supported in the + hardware. This will allow the combiner to notice more patterns, + but the down side is that the asm outputter will have to emit + several instructions for each shift which isn't possible in the + hardware, this makes scheduling perform badly .*/ +int fake_shift() +{ + return 0; +}