From 0d7e008e87f916fe7ff510696ec4fabbcad43925 Mon Sep 17 00:00:00 2001 From: Steve Chamberlain Date: Sat, 5 Feb 1994 21:03:19 +0000 Subject: [PATCH] *** empty log message *** From-SVN: r6486 --- gcc/config/sh/sh.c | 2258 +++++++++++++++++++++++++++---------------- gcc/config/sh/sh.h | 494 +++++++--- gcc/config/sh/sh.md | 1535 +++++++++++++++++++---------- 3 files changed, 2821 insertions(+), 1466 deletions(-) diff --git a/gcc/config/sh/sh.c b/gcc/config/sh/sh.c index ca8f8f029d4..d8d73ba610e 100644 --- a/gcc/config/sh/sh.c +++ b/gcc/config/sh/sh.c @@ -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 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. + 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. */ + 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,27 +32,30 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #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 (); -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; @@ -62,12 +65,12 @@ rtx sh_compare_op1; int regno_reg_class[FIRST_PSEUDO_REGISTER] = { - R0_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, + 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, + GENERAL_REGS, PR_REGS, T_REGS, NO_REGS, + MAC_REGS, MAC_REGS, }; /* Provide reg_class from a letter such as appears in the machine @@ -84,24 +87,45 @@ 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 */ + + +#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)) +#define REG_EVEN \ + (REG_ODD | (1 << (int) DImode) | (1 << (int) DFmode) \ + | (1 << (int) CSImode) | (1 << (int) SCmode)) +#define SI_ONLY (1<<(int)SImode) +int hard_regno_mode_ok[] = +{ + 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; -/* 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; + +/* Number of bytes pushed for anonymous args, used to pass information + between expand_prologue and expand_epilogue. */ +static int extra_push; + void push (rn) + int rn; { emit_insn (gen_push (gen_rtx (REG, SImode, rn))); } @@ -116,8 +140,7 @@ pop (rn) /* Adjust the stack and return the number of bytes taken to do it */ static void -output_stack_adjust (direction, size) - int direction; +output_stack_adjust (size) int size; { if (size) @@ -125,24 +148,18 @@ output_stack_adjust (direction, size) rtx val = GEN_INT (size); rtx insn; - if (size > 120) + if (!CONST_OK_FOR_I (size)) { - rtx nval = gen_rtx (REG, SImode, 13); + rtx nval = gen_rtx (REG, SImode, 3); 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); - + insn = gen_addsi3 (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. */ @@ -151,7 +168,6 @@ push_regs (mask) int mask; { int i; - int size = 0; for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) { @@ -163,13 +179,11 @@ push_regs (mask) } -/* - 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. - */ +/* 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 +static void print_slot (insn) rtx insn; { @@ -178,35 +192,65 @@ print_slot (insn) 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]) + 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++; + } + } + else if (TARGET_SMALLCALL) + { + /* Don't need to push anthing, but count the regs which have + been pushed by the wrapper */ + if (call_used_regs[reg]) + count++; + } + else { - (*count)++; - live_regs_mask |= (1 << reg); + /* 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); + } } } + + + *count_ptr = count; return live_regs_mask; } - - static int need_slot (insn) rtx insn; @@ -287,7 +331,7 @@ print_operand_address (stream, x) 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 */ + 'N' print insides of a @++ or @-- o */ void print_operand (stream, x, code) @@ -297,8 +341,6 @@ print_operand (stream, x, code) { switch (code) { - - case '.': if (need_slot (final_sequence)) fprintf (stream, ".s"); @@ -306,13 +348,9 @@ print_operand (stream, x, code) 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) @@ -321,31 +359,26 @@ print_operand (stream, x, code) } break; case 'O': - fprintf (asm_out_file, "%d", INTVAL (x)); - break; - - case 'I': - fprintf (asm_out_file, "LK%d", add_constant (x, SImode)); + 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)) { @@ -359,43 +392,241 @@ print_operand (stream, x, code) fputc ('#', stream); output_addr_const (stream, x); break; - } break; } } +sextb (x) +{ + x &= 0xff; + if (x > 127) + { + x = -256 + x; + } + return x; +} + -/* 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) +/* 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) * 4; - int total_auto_space = get_frame_size (); + rtx dst; + int i = INTVAL (operands[1]) & 0xffffffff; + + if (CONST_OK_FOR_I (i)) + return 0; + dst = mode == SImode ? operands[0] : gen_reg_rtx (SImode); - if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM) + /* 00000000 00000000 11111111 1NNNNNNNN load and zero extend word */ + if ((i & 0xffffff80) == 0x0000ff80) + { + 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) { - return total_saved_regs_space; + 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 == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM) + else if ((i & 0xffff80ff) == 0 + || (i & 0xffff80ff) == 0xffff8000) { - return total_saved_regs_space + total_auto_space; + 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) == 0 + || (i & 0xff80ffff) == 0xff80ffff) + { + 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) + { + 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 (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM) + if (mode != SImode) { - return total_auto_space; + emit_insn (gen_rtx (SET, VOIDmode, operands[0], + gen_rtx (SUBREG, mode, dst, 0))); + } + return 1; } +/* 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; +{ + int align = INTVAL (operands[3]); + int constp = (GET_CODE (operands[2]) == CONST_INT); + int bytes = (constp ? INTVAL (operands[2]) : 0); + enum machine_mode mode; + /* IF odd then fail */ + if (!constp || bytes <= 0) + 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)) + { + char entry[30]; + tree entry_name; + rtx func_addr_rtx; + int groups; + 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. Take this chance to remove addressing modes which can't be coped with very well. */ @@ -405,38 +636,130 @@ 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 we've got a negative index, break it down */ - - if (GET_CODE (operands[0]) == MEM && !reload_in_progress) + if ((mode == DImode || 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; - rtx inside = XEXP (operands[0], 0); - if (GET_CODE (inside) == PLUS) + if (src == dst) { - 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 */ + emit_insn (gen_rtx (SET, VOIDmode, dst, src)); + return 1; + } - rtx sub = copy_to_mode_reg (SImode, inside); - XEXP (operands[0], 0) = sub; - } + 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; } +/* Work out the subword parts to split up a double move + into two SI moves - take care to do it in the right order + */ + +int +prepare_split_double_ops (operands, mode) + rtx operands[]; + enum machine_mode mode; +{ + if (GET_CODE (operands[1]) == REG + && REGNO (operands[1]) > FIRST_PSEUDO_REGISTER) + return 0; + + if (GET_CODE (operands[0]) == REG + && REGNO (operands[0]) > FIRST_PSEUDO_REGISTER) + return 0; + + /* If we split move insns from memory, it confuses scheduling + later on. */ + if (GET_CODE (operands[1]) == MEM) + return 0; + if (GET_CODE (operands[0]) == MEM) + return 0; + + if (GET_CODE (operands[0]) != REG + || !refers_to_regno_p (REGNO (operands[0]), + REGNO (operands[0]) + 1, operands[1], 0)) + { + operands[2] = operand_subword (operands[0], 0, 0, mode); + operands[3] = operand_subword (operands[1], 0, 0, mode); + operands[4] = operand_subword (operands[0], 1, 0, mode); + operands[5] = operand_subword (operands[1], 1, 0, mode); + } + else + { + operands[2] = operand_subword (operands[0], 1, 0, mode); + operands[3] = operand_subword (operands[1], 1, 0, mode); + operands[4] = operand_subword (operands[0], 0, 0, mode); + operands[5] = operand_subword (operands[1], 0, 0, mode); + } + + if (operands[2] == 0 || operands[3] == 0 + || operands[4] == 0 || operands[5] == 0) + return 0; + + emit_move_insn (operands[2], operands[3]); + emit_move_insn (operands[4], operands[5]); + return 1; +} /* Prepare the operands for an scc instruction; make sure that the compare has been done. */ @@ -446,17 +769,44 @@ prepare_scc_operands (code) 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); } -/* Functions to output assembly */ +/* Functions to output assembly code. */ /* Return a sequence of instructions to perform DI or DF move. @@ -464,15 +814,24 @@ prepare_scc_operands (code) 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; { rtx dst = operands[0]; rtx src = operands[1]; - int lowfirst; + 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)) { @@ -480,24 +839,31 @@ output_movedouble (operands, mode) 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 - */ + 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"; + return "mov %R1,%R0\n\tmov %1,%0 ! cra"; else - return "mov %R1,%R0\n\tmov %1,%0 "; - + return "mov %1,%0\n\tmov %R1,%R0 ! crb"; } else if (GET_CODE (src) == CONST_INT) { - if (INTVAL (src) < 0) - return "mov #-1,%0\n\tmov %1,%R0"; + HOST_WIDE_INT val = INTVAL (src); + int rn = REGNO (operands[0]); + if (val < 0) + { + fprintf (asm_out_file, "\tmov #-1,r%d\n", rn); + } else - return "mov #0,%0\n\tmov %1,%R0"; - } + { + fprintf (asm_out_file, "\tmov #0,r%d\n", rn); + } + fprintf (asm_out_file, "\tmov #%d,r%d\n", val, rn + 1); + return ""; + } else if (GET_CODE (src) == MEM) { int ptrreg1 = -1; @@ -518,10 +884,13 @@ output_movedouble (operands, mode) 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 abort (); - if ((ptrreg1 >= 0 && ptrreg2 >= 0) && (dreg == ptrreg1 || dreg == ptrreg2 @@ -582,7 +951,24 @@ output_shift (string, reg, k, code) { int s = INTVAL (k); - + if (s < 0) + { + s = -s; + switch (code) + { + case LSHIFTRT: + case ASHIFTRT: + code = LSHIFT; + break; + case ASHIFT: + code = ASHIFTRT; + break; + case LSHIFT: + code = LSHIFTRT; + default: + abort (); + } + } if (code == ASHIFT && s == 31) { /* Shift left by 31 moving into the t bit, clearing and rotating the other way */ @@ -632,13 +1018,80 @@ output_shift (string, reg, k, code) return ""; } + +void +function_epilogue (stream, size) + FILE *stream; + int size; +{ + fprintf (stream, "\trts\n"); + fprintf (stream, "\tor r0,r0\n"); +} + + /* 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.*/ + 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_far_jump (insn, op) + rtx insn; + rtx op; +{ + rtx thislab = gen_label_rtx (); + + /* See if we can grab a reg from the prev insn */ + rtx gotone = 0; + rtx prev = PREV_INSN (insn); + rtx link; + + if (dbr_sequence_length ()) + { + /* 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 */ + + int i; + rtx vec[2]; + vec[0] = thislab; + + 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; + } + output_asm_insn ("mov.l %1,@-r15", vec); + output_asm_insn ("mov.l %O0,%1", vec); + print_slot (final_sequence); + output_asm_insn ("jmp @%1 ! 32 xcond", vec); + output_asm_insn ("mov.l @r15+,%1", vec); + } + else + { + 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); + } + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (thislab)); + output_asm_insn (".align 2", 0); + output_asm_insn (".long %O0", &op); + return ""; +} char * output_branch (logic, insn) @@ -649,6 +1102,7 @@ output_branch (logic, insn) int label = lf++; int rn = -1; int need_save; + fprintf (asm_out_file, "! pc %04x\n", insn_addresses[INSN_UID (insn)]); switch (get_attr_length (insn)) { @@ -679,16 +1133,13 @@ output_branch (logic, insn) 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: + case 16: /* Branches a long way away */ { - rtx oldop = recog_operand[0]; if (need_slot (final_sequence)) @@ -702,20 +1153,7 @@ output_branch (logic, insn) 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"); + output_far_jump (insn, oldop); fprintf (asm_out_file, "LF%d:\n", label); return ""; } @@ -724,231 +1162,70 @@ output_branch (logic, insn) } -/* Predicates used by the templates */ - -/* Non zero if op is an immediate ok for a byte index */ +/* 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. -int -byte_index_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return (GET_CODE (op) == CONST_INT - && INTVAL (op) >= 0 && INTVAL (op) <= 15); -} + It is important to minimize this, since the branches will slow things + down and make things bigger. -/* Non zero if OP is a pop operand */ + Worst case code looks like: -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 */ + mov.l L1,rn + bra L2 + nop + align + L1: .long value + L2: + .. -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; -} + mov.l L3,rn + bra L4 + nop + align + L3: .long value + L4: + .. -/* Nonzero if the arg is an immediate which has to be loaded from - memory */ + 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. -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. + The algorithm is: - 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. + 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 @@ -958,28 +1235,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) { @@ -989,546 +1257,657 @@ add_constant (x, mode) continue; } } - if (rtx_equal_p (x, pool_vector[i].value)) - return pool_vector[i].number; + return pool_vector[i].label; } + /* 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 lf++; + return lab; } -/* Nonzero if the insn could take a constant table. */ +/* Dump out interesting debug info */ -static int -has_constant_table (insn) +rtx +final_prescan_insn (insn, opvec, noperands) rtx insn; + rtx *opvec; + int noperands; { - 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) + if (target_flags & ISIZE_BIT) { - return INTVAL (XVECEXP (body, 0, 0)) == 1; + extern int *insn_addresses; + fprintf (asm_out_file, "\n! at %04x\n", + insn_addresses[INSN_UID (insn)]); } - 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; +/* Stuff taken from m88k.c */ -int last_uid; -int last_pc; +/* Output to FILE the start of the assembler file. */ -void -adjust_insn_length (insn, insn_lengths) - rtx insn; - short *insn_lengths; +struct option { - int uid = INSN_UID (insn); - rtx body = PATTERN (insn); - - current_pc += insn_lengths[uid]; - + char *string; + int *variable; + int on_value; +}; - if (GET_CODE (body) == SEQUENCE) +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) { - int i; - - for (i = 0; i < XVECLEN (body, 0); i++) - { - adjust_insn_length (XVECEXP (body, 0, i), insn_lengths); - } + fprintf (file, indent); + return fprintf (file, "%s%s", type, name); } - 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. */ - + return pos + fprintf (file, "%s%s%s", sep, type, name); +} - /* Add the stuff from this insn to what will go in the - growing table. */ +static struct +{ + char *name; + int value; +} - pool_bytes += get_attr_constantsize (insn); +m_options[] = TARGET_SWITCHES; - /* The target shinks to its smallest natural size */ - insn_lengths[target_insn_uid] = target_insn_smallest_size; +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; - /* The current insn grows to be its larger size plust the - table size. */ - insn_lengths[uid] = get_attr_largestsize (insn) + pool_bytes; + 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); - } - /* Current insn becomes the target. */ - target_insn_uid = uid; - target_insn_smallest_size = get_attr_smallestsize (insn); + 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); -/* Dump out the pending constant pool. - If label provided then insert an branch in the middle of the table - */ + fprintf (file, term); + fprintf (file, "! %d %d\n", max_count_si, max_count_hi); +} -int -dump_constants (label) +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; { - 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; - } + register int pos; - } - } + output_file_directive (file, main_input_filename); - pool_size = 0; - current_pc = 0; - pc = 0; - pool_bytes = 0; + /* Switch to the data section so that the coffsem symbol and the + gcc2_compiled. symbol aren't in the text section. */ + data_section (); - target_insn_range = 0; - return rlabel; + 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"); } + -/* Emit the text to load a value from a constant table. */ +/* Return the cost of a shift */ -char * -output_movepcrel (insn, operands, mode) - rtx insn; - rtx operands[]; - enum machine_mode mode; +int +shiftcosts (RTX) + rtx RTX; { - 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); + /* 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; - /* 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 */ + /* 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 100; +} - if (target_insn_uid == INSN_UID (insn) - && current_pc < target_insn_range) - return ""; +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; +} +/* Return the cost of a multiply */ +int +multcosts (RTX) + rtx RTX; +{ + if (TARGET_SH2) + return 2; + /* 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; +} +/* Code to expand a shift */ - /* 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)) +void +gen_ashift (type, n, reg) + int type; + int n; + rtx reg; +{ + switch (type) { - /* 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"); + 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; } - return ""; } - - -/* Dump out interesting debug info */ -rtx -final_prescan_insn (insn, opvec, noperands) - rtx insn; - rtx *opvec; - int noperands; +int +gen_shifty_op (code, operands) + int code; + rtx *operands; { - register rtx body = PATTERN (insn); - - if (target_flags & ISIZE_BIT) + rtx wrk = gen_reg_rtx (SImode); + rtx t; + char *func; + if (GET_CODE (operands[2]) == CONST_INT) { - extern int *insn_addresses; - - fprintf (asm_out_file, "\n!%04x*\n", - insn_addresses[INSN_UID (insn)] + 0x10); + int value = INTVAL (operands[2]); + top: + switch (code) + { + case ASHIFTRT: + if (value < 0) + { + code = ASHIFT; + value = -value; + goto top; + } - fprintf (asm_out_file, "\n!%04x %d %04x len=%d\n", - pc, pool_size, first_pc, get_attr_length (insn)); + /* Expand a short sequence inline, longer call a magic routine */ + if (value < 4) + { + 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; - if (TARGET_DUMP_RTL) - print_rtl (asm_out_file, body); + 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; - 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"); + } } + 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; + } -/* 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. */ + return ""; +} +/* Output the literal table */ -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; +dump_table (scan) + rtx scan; { - 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; + int i; + int pass; + int need_align = 1; - mode[0] = mode_from_align[align]; + /* Do two passes, first time dump out the HI sized constants */ - temp[0] = gen_reg_rtx (mode[0]); - if (size >= 2 * align) + for (i = 0; i < pool_size; i++) { - mode[1] = mode[0]; - temp[1] = gen_reg_rtx (mode[1]); + pool_node *p = pool_vector + i; + if (p->mode == HImode) + { + 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; - do + + for (i = 0; i < pool_size; i++) { - rtx srcp, dstp; - next = phase; - phase = !phase; + pool_node *p = pool_vector + i; - if (size > 0) + switch (p->mode) { - /* Change modes as the sequence tails off. */ - if (size < amount[next]) + case HImode: + break; + case SImode: + if (need_align) { - amount[next] = (size >= 4 ? 4 : (size >= 2 ? 2 : 1)); - mode[next] = mode_from_align[amount[next]]; - temp[next] = gen_reg_rtx (mode[next]); + need_align = 0; + scan = emit_insn_after (gen_align_4 (), scan); } - 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]; + 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_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; } } - 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; + scan = emit_insn_after (gen_consttable_end (), scan); + scan = emit_barrier_after (scan); + pool_size = 0; +} - 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); +/* 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; - else if (constp && align == 4 && TARGET_88100) - block_move_loop (operands[0], dest_mem, operands[1], src_mem, - bytes, align); -#endif - else -#endif + if (GET_CODE (src) == SYMBOL_REF) { - emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "memcpy"), 0, - VOIDmode, 3, - operands[0], Pmode, - operands[1], Pmode, - operands[2], SImode); + 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; } - -override_options () +/* 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; { - 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; + return (GET_CODE (src) == CONST_INT + && INTVAL (src) >= -32768 + && INTVAL (src) <= 32767); } - - -/* Stuff taken from m88k.c */ -/* Output to FILE the start of the assembler file. */ +/* 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. */ -struct option +static +rtx +find_barrier (from) + rtx from; { - char *string; - int *variable; - int on_value; -}; + 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 the pcrelloads + will work out to be only 2 bytes long */ -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) + 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 = 2; + } + else + { + inc = get_attr_length (from); + } + if (found_si) + count_si += inc; + if (found_hi) + count_hi += inc; + from = NEXT_INSN (from); + } + + if (!found_barrier) { - fprintf (file, indent); - return fprintf (file, "%s%s", type, name); + /* Insert a jump around the barrier here */ + rtx label = gen_label_rtx (); + /* Walk back to be just before any jump */ + while (GET_CODE (from) == JUMP_INSN + || GET_CODE (from) == NOTE) + { + 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 pos + fprintf (file, "%s%s%s", sep, type, name); + return found_barrier; } -static struct - { - char *name; - int value; - } - -m_options[] = TARGET_SWITCHES; +/* Non zero if the insn is a move instruction which needs to be fixed. */ -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; +static +int +broken_move (insn) + rtx insn; { - 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); + 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; +} - 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); +/* Exported to toplev.c - 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); + 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; + int limit; + 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); - fprintf (file, term); + /* 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; + 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); + } + } } -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; +/* Called from the md file, set up the operands of a compare instruction */ + +int +from_compare (operands, code) + rtx *operands; + int code; { - register int pos; + operands[1] = sh_compare_op0; + operands[2] = force_reg (SImode, sh_compare_op1); + operands[1] = force_reg (SImode, operands[1]); +} - output_file_directive (file, main_input_filename); +/* Non-zero if x is EQ or NE */ - /* Switch to the data section so that the coffsem symbol and the - gcc2_compiled. symbol aren't in the text section. */ - data_section (); +int +equality_operator (x, mode) + rtx x; + enum machine_mode mode; +{ + enum rtx_code code = GET_CODE (x); + return (code == EQ || code == NE); +} - 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"); -} - +/* 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 () { @@ -1537,7 +1916,9 @@ sh_expand_prologue () live_regs_mask = calc_live_regs (&d); - output_stack_adjust (-1, current_function_pretend_args_size); + /* 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) { @@ -1549,22 +1930,16 @@ sh_expand_prologue () 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) { - 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 @@ -1580,13 +1955,10 @@ sh_expand_epilogue () { emit_insn (gen_movsi (stack_pointer_rtx, frame_pointer_rtx)); } - else - { - output_stack_adjust (1, get_frame_size ()); - } - + 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; @@ -1595,43 +1967,285 @@ sh_expand_epilogue () pop (j); } } - output_stack_adjust (1, extra_push + - current_function_pretend_args_size); - extra_push = 0; + 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 regs_saved; + int regs_saved_mask = calc_live_regs (®s_saved); + int total_saved_regs_space; + int total_auto_space = get_frame_size (); + total_saved_regs_space = (regs_saved) * 4; -/* Return the cost of a shift */ + 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 -shiftcosts (RTX) - rtx RTX; +handle_pragma (file) + FILE *file; { - /* If shift by a non constant, then this will be expensive. */ - if (GET_CODE (XEXP (RTX, 1)) != CONST_INT) - return 20; + int c; + char pbuf[200]; + int psize = 0; - /* 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)))) + 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) + { + pragma_interrupt = pragma_trapa = 1; + return; + } + c = getc (file); + } + return c; +} + +/* insn expand helpers */ + +/* Emit insns to perform a call. 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 (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; - /* otherwise it will be several insns. */ - return 4; + /* No predec allowed */ + + if (GET_CODE (op) == MEM + && GET_CODE (XEXP (op, 0)) == PRE_DEC) + return 0; + + 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); } -/* Return the cost of a multiply */ + +/* Returns 1 if OP can be a destination of a move. + Same as general_operand, but no preinc allowed. */ + int -multcosts (RTX) - rtx RTX; +general_movdst_operand (op, mode) + rtx op; + enum machine_mode mode; { - /* 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; + if (GET_CODE (op) == MEM + && GET_CODE (XEXP (op, 0)) == PRE_INC) + return 0; + return general_operand (op, mode); +} + + +/* Returns 1 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); +} + +/* 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)) + { + 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; +} + + +/* Returns 1 if OP is a valid source operand for an arithmetic insn. */ + +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; + } + return 0; } + diff --git a/gcc/config/sh/sh.h b/gcc/config/sh/sh.h index d59e8ec46ce..74094f1418b 100644 --- a/gcc/config/sh/sh.h +++ b/gcc/config/sh/sh.h @@ -1,5 +1,5 @@ /* Definitions of target machine for GNU compiler, for Hitachi Super-H. - Copyright (C) 1993 Free Software Foundation, Inc. + Copyright (C) 1993, 1994 Free Software Foundation, Inc. Contributed by Steve Chamberlain (sac@cygnus.com) @@ -36,24 +36,48 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Show we can debug even without a frame pointer. */ #define CAN_DEBUG_WITHOUT_FP + + +#define CONDITIONAL_REGISTER_USAGE \ + /* Experimental calling convention with fewer saved registers */ \ + if (TARGET_NOSAVE) \ + { \ + call_used_regs[8] = 1; \ + call_used_regs[9] = 1; \ + call_used_regs[10] = 1; \ + call_used_regs[11] = 1; \ + } \ + /* Hitachi saves and restores mac registers on call */ \ + if (TARGET_HITACHI) \ + { \ + call_used_regs[MACH_REG] = 0; \ + call_used_regs[MACL_REG] = 0; \ + } + /* Run-time compilation parameters selecting different hardware subsets. */ extern int target_flags; -#define ISIZE_BIT 1 -#define FAST_BIT 2 - -#define MAC_BIT 8 -#define RTL_BIT 16 -#define DT_BIT 32 -#define DALIGN_BIT 64 -#define SH0_BIT 128 -#define SH1_BIT 256 -#define SH2_BIT 512 -#define SH3_BIT 1024 -#define C_BIT 2048 -#define R_BIT (1<<12) -#define SPACE_BIT (1<<13) +#define ISIZE_BIT (1<<1) +#define FAST_BIT (1<<2) +#define MAC_BIT (1<<3) +#define RTL_BIT (1<<4) +#define DT_BIT (1<<5) +#define DALIGN_BIT (1<<6) +#define SH0_BIT (1<<7) +#define SH1_BIT (1<<8) +#define SH2_BIT (1<<9) +#define SH3_BIT (1<<10) +#define C_BIT (1<<11) +#define R_BIT (1<<12) +#define SPACE_BIT (1<<13) +#define BIGTABLE_BIT (1<<14) +#define TRYR0_BIT (1<<15) +#define NOSAVE_BIT (1<<16) +#define SMALLCALL_BIT (1<<17) +#define CONSTLEN_2_BIT (1<<20) +#define CONSTLEN_3_BIT (1<<21) +#define HITACHI_BIT (1<<22) /* Nonzero if we should generate code using type 0 insns */ #define TARGET_SH0 (target_flags & SH0_BIT) @@ -88,28 +112,81 @@ extern int target_flags; /* Nonzero to align doubles on 64 bit boundaries */ #define TARGET_ALIGN_DOUBLE (target_flags & DALIGN_BIT) +/* Nonzero to use long jump tables */ +#define TARGET_BIGTABLE (target_flags & BIGTABLE_BIT) -/* Nonzero if Combine dumping wanted */ +/* Nonzero if combine dumping wanted */ #define TARGET_CDUMP (target_flags & C_BIT) -#define TARGET_SWITCHES \ -{ {"isize", ( ISIZE_BIT) },\ - {"space", ( SPACE_BIT) },\ - {"0", ( SH0_BIT) },\ - {"1", ( SH1_BIT) },\ - {"2", ( SH2_BIT) },\ - {"3", ( SH3_BIT) },\ - {"ac", ( MAC_BIT) },\ - {"dalign", ( DALIGN_BIT) },\ - {"c", ( C_BIT) },\ - {"r", ( RTL_BIT) },\ - {"R", ( R_BIT) },\ - {"", TARGET_DEFAULT} \ +/* Nonzero if trying to use reg+disp for QIs and HIs. This + doesn't work yet.*/ +#define TARGET_TRYR0 (target_flags & TRYR0_BIT) + +/* Nonzero if using no save calling convention */ +#define TARGET_NOSAVE (target_flags & NOSAVE_BIT) + +/* Nonzero if using no save calling convention */ +#define TARGET_SMALLCALL (target_flags & SMALLCALL_BIT) + +/* Select max size of computed constant code sequences to be 3 insns */ +#define TARGET_CLEN3 (target_flags & CONSTLEN_3_BIT) + +/* Nonzero if using Hitachi's calling convention */ +#define TARGET_HITACHI (target_flags & HITACHI_BIT) + +#define TARGET_SWITCHES \ +{ {"isize", ( ISIZE_BIT) }, \ + {"space", ( SPACE_BIT) }, \ + {"0", ( SH0_BIT) }, \ + {"1", ( SH1_BIT) }, \ + {"2", ( SH2_BIT) }, \ + {"3", ( SH3_BIT) }, \ + {"ac", ( MAC_BIT) }, \ + {"dalign", ( DALIGN_BIT) }, \ + {"c", ( C_BIT) }, \ + {"r", ( RTL_BIT) }, \ + {"bigtable", ( BIGTABLE_BIT)}, \ + {"try-r0", ( TRYR0_BIT)}, \ + {"R", ( R_BIT) }, \ + {"nosave", ( NOSAVE_BIT) }, \ + {"clen3", ( CONSTLEN_3_BIT) }, \ + {"smallcall", ( SMALLCALL_BIT) }, \ + {"hitachi", ( HITACHI_BIT) }, \ + {"", TARGET_DEFAULT} \ } -#define TARGET_DEFAULT FAST_BIT - -#define OVERRIDE_OPTIONS override_options(); +#define TARGET_DEFAULT (FAST_BIT) + +/* Macro to define table for command options with values. */ +#define TARGET_OPTIONS \ + { { "maxsi-", &max_si}, \ + { "maxhi-", &max_hi} } + +#define OVERRIDE_OPTIONS \ +do { \ + 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; \ + \ + /* We *MUST* always define optimize since we *HAVE* to run \ + shorten branches to get correct code. */ \ + \ + optimize = 1; \ + flag_delayed_branch = 1; \ + \ + if (max_si) \ + max_count_si = atoi (max_si); \ + else \ + max_count_si = 1010; \ + if (max_hi) \ + max_count_hi = atoi (max_hi); \ + else \ + max_count_hi = 505; \ +} while (0) /* Target machine storage Layout. */ @@ -171,7 +248,7 @@ extern int target_flags; #define CONSTANT_ALIGNMENT(EXP, ALIGN) \ ((TREE_CODE (EXP) == STRING_CST \ && (ALIGN) < FASTEST_ALIGNMENT) \ - ? FASTEST_ALIGNMENT : (ALIGN)) + ? FASTEST_ALIGNMENT : (ALIGN)) /* Make arrays of chars word-aligned for the same reasons. */ #define DATA_ALIGNMENT(TYPE, ALIGN) \ @@ -186,13 +263,13 @@ extern int target_flags; /* Standard register usage. */ -/* Register allocation for our first guess +/* Register allocation for the Hitachi calling convention: - r0-r3 scratch - r4-r7 args in and out - r8-r12 call saved - r13 assembler temp - r14 frame pointer + r0 arg return + r1..r3 scratch + r4-r7 args in + r8..r13 call saved + r14 frame pointer/call saved r15 stack pointer ap arg pointer (doesn't really exist, always eliminated) pr subroutine return address @@ -207,23 +284,33 @@ extern int target_flags; All registers that the compiler knows about must be given numbers, even those that are not normally considered general registers. - SH has 16 integer registers and 4 control registers + the arg - pointer */ - -#define FIRST_PSEUDO_REGISTER 22 +*/ +#define AP_REG 16 #define PR_REG 17 #define T_REG 18 #define GBR_REG 19 #define MACH_REG 20 #define MACL_REG 21 +#define FIRST_PSEUDO_REGISTER 22 /* 1 for registers that have pervasive standard uses and are not available for the register allocator. */ - /* r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 r15 ap pr t gbr mh ml */ -#define FIXED_REGISTERS \ - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1} + /* r0 r1 r2 r3 + r4 r5 r6 r7 + r8 r9 r10 r11 + r12 r13 r14 r15 + ap pr t gbr + mh ml */ + +#define FIXED_REGISTERS \ + { 0, 0, 0, 0, \ + 0, 0, 0, 0, \ + 0, 0, 0, 0, \ + 0, 0, 0, 1, \ + 1, 1, 1, 1, \ + 1, 1} /* 1 for registers not available across function calls. These must include the FIXED_REGISTERS and also any @@ -232,9 +319,20 @@ extern int target_flags; and the register where structure-value addresses are passed. Aside from that, you can include as many other registers as you like. */ - /* r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 r15 ap pr t gbr mh ml */ -#define CALL_USED_REGISTERS \ - { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1} + /* r0 r1 r2 r3 + r4 r5 r6 r7 + r8 r9 r10 r11 + r12 r13 r14 r15 + ap pr t gbr + mh ml */ + +#define CALL_USED_REGISTERS \ + { 1, 1, 1, 1, \ + 1, 1, 1, 1, \ + 0, 0, 0, 0, \ + 0, 0, 0, 1, \ + 1, 0, 1, 1, \ + 1, 1} /* Return number of consecutive hard regs needed starting at reg REGNO to hold something of mode MODE. @@ -242,14 +340,16 @@ extern int target_flags; but can be less for certain modes in special long registers. On the SH regs are UNITS_PER_WORD bits wide; */ + #define HARD_REGNO_NREGS(REGNO, MODE) \ (((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)) /* Value is 1 if hard register REGNO can hold a value of machine-mode MODE. We may keep double values in even registers */ +extern int hard_regno_mode_ok[]; #define HARD_REGNO_MODE_OK(REGNO, MODE) \ - ((TARGET_ALIGN_DOUBLE && GET_MODE_SIZE(MODE) > 4) ? (((REGNO)&1)==0) : 1) + (hard_regno_mode_ok[REGNO] & (1<<(int)MODE)) /* Value is 1 if it is a good idea to tie two pseudo registers when one has mode MODE1 and one has mode MODE2. @@ -275,8 +375,7 @@ extern int target_flags; Zero means the frame pointer need not be set up (and parms may be accessed via the stack pointer) in functions that seem suitable. */ - -#define FRAME_POINTER_REQUIRED (get_frame_size() > 1000) +#define FRAME_POINTER_REQUIRED 0 /* Definitions for register eliminations. @@ -290,11 +389,17 @@ extern int target_flags; followed by "to". Eliminations of the same "from" register are listed in order of preference. */ +/* This is an array of structures. Each structure initializes one pair + of eliminable registers. The "from" register number is given first, + followed by "to". Eliminations of the same "from" register are listed + in order of preference. */ + #define ELIMINABLE_REGS \ {{ FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ { ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ { ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM},} + /* Given FROM and TO register numbers, say whether this elimination is allowed. */ #define CAN_ELIMINATE(FROM, TO) \ @@ -314,9 +419,21 @@ extern int target_flags; /* The register in which a struct value address is passed */ -#define STRUCT_VALUE_REGNUM 3 +#define STRUCT_VALUE_REGNUM 2 + +/* If the structure value address is not passed in a register, define + `STRUCT_VALUE' as an expression returning an RTX for the place + where the address is passed. If it returns 0, the address is + passed as an "invisible" first argument. */ + +/*#define STRUCT_VALUE ((rtx)0)*/ +/* Don't default to pcc-struct-return, because we have already specified + exactly how to return structures in the RETURN_IN_MEMORY macro. */ + +#define DEFAULT_PCC_STRUCT_RETURN 0 + /* Define the classes of registers for register constraints in the machine description. Also define ranges of constants. @@ -347,10 +464,10 @@ enum reg_class { NO_REGS, R0_REGS, - GENERAL_REGS, PR_REGS, T_REGS, MAC_REGS, + GENERAL_REGS, ALL_REGS, LIM_REG_CLASSES }; @@ -362,10 +479,10 @@ enum reg_class { \ "NO_REGS", \ "R0_REGS", \ - "GENERAL_REGS", \ "PR_REGS", \ "T_REGS", \ "MAC_REGS", \ + "GENERAL_REGS", \ "ALL_REGS", \ } @@ -377,10 +494,10 @@ enum reg_class { \ 0x000000, /* NO_REGS */ \ 0x000001, /* R0_REGS */ \ - 0x01FFFF, /* GENERAL_REGS */ \ 0x020000, /* PR_REGS */ \ 0x040000, /* T_REGS */ \ 0x300000, /* MAC_REGS */ \ + 0x01FFFF, /* GENERAL_REGS */ \ 0x37FFFF /* ALL_REGS */ \ } @@ -392,9 +509,15 @@ enum reg_class extern int regno_reg_class[]; #define REGNO_REG_CLASS(REGNO) regno_reg_class[REGNO] +/* When defined, the compiler allows registers explicitly used in the + rtl to be used as spill registers but prevents the compiler from + extending the lifetime of these registers. */ + +#define SMALL_REGISTER_CLASSES + /* The order in which register should be allocated. */ #define REG_ALLOC_ORDER \ - { 1,2,3,7,4,5,6,0,8,9,10,11,12,13,14,15,16,17,18,19,20,21} + { 1,2,3,7,6,5,4,0,8,9,10,11,12,13,14,15,16,17,18,19,20,21 } /* The class value for index registers, and the one for base regs. */ #define INDEX_REG_CLASS R0_REGS @@ -415,20 +538,22 @@ extern enum reg_class reg_class_from_letter[]; Return 1 if VALUE is in the range specified by C. I: arithmetic operand -127..128, as used in add, sub, etc L: logical operand 0..255, as used in and, or, etc. + J: something ok as a move source - so it must be easy to make M: constant 1 + N: constant 0 K: shift operand 1,2,8 or 16 */ #define CONST_OK_FOR_I(VALUE) (((int)(VALUE))>= -128 && ((int)(VALUE)) <= 127) #define CONST_OK_FOR_L(VALUE) (((int)(VALUE))>= 0 && ((int)(VALUE)) <= 255) #define CONST_OK_FOR_M(VALUE) ((VALUE)==1) +#define CONST_OK_FOR_N(VALUE) ((VALUE)==0) #define CONST_OK_FOR_K(VALUE) ((VALUE)==1||(VALUE)==2||(VALUE)==8||(VALUE)==16) - #define CONST_OK_FOR_LETTER_P(VALUE, C) \ ((C) == 'I' ? CONST_OK_FOR_I (VALUE) \ + : (C) == 'K' ? CONST_OK_FOR_K (VALUE) \ : (C) == 'L' ? CONST_OK_FOR_L (VALUE) \ : (C) == 'M' ? CONST_OK_FOR_M (VALUE) \ - : (C) == 'K' ? CONST_OK_FOR_K (VALUE) \ : 0) /* Similar, but for floating constants, and defining letters G and H. @@ -444,13 +569,12 @@ extern enum reg_class reg_class_from_letter[]; In general this is just CLASS; but on some machines in some cases it is preferable to use a more restrictive class. */ -#define PREFERRED_RELOAD_CLASS(X, CLASS) CLASS +#define PREFERRED_RELOAD_CLASS(X, CLASS) CLASS /* Return the register class of a scratch register needed to copy IN into or out of a register in CLASS in MODE. If it can be done directly, NO_REGS is returned. */ -#define SECONDARY_RELOAD_CLASS(CLASS, MODE, X) NO_REGS /* Return the maximum number of consecutive registers needed to represent mode MODE in a register of class CLASS. @@ -459,6 +583,7 @@ extern enum reg_class reg_class_from_letter[]; #define CLASS_MAX_NREGS(CLASS, MODE) \ ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) + /* Stack layout; function entry, exit and calling. */ @@ -466,22 +591,21 @@ extern enum reg_class reg_class_from_letter[]; These two macros are used only in other macro definitions below. */ #define NPARM_REGS 4 #define FIRST_PARM_REG 4 -#define FIRST_RET_REG 4 +#define FIRST_RET_REG 0 /* Define this if pushing a word on the stack makes the stack pointer a smaller address. */ #define STACK_GROWS_DOWNWARD -/* Define this if the nominal address of the stack frame - is at the high-address end of the local variables; - that is, each additional local variable allocated - goes at a more negative offset in the frame. */ -#define FRAME_GROWS_DOWNWARD +/* Define this macro if the addresses of local variable slots are at + negative offsets from the frame pointer. -/* Offset within stack frame to start allocating local variables at. - If FRAME_GROWS_DOWNWARD, this is the offset to the END of the - first local allocated. Otherwise, it is the offset to the BEGINNING - of the first local allocated. */ + The SH only has positive indexes, so grow the frame up +*/ +/* #define FRAME_GROWS_DOWNWARD */ + +/* Offset from the frame pointer to the first local variable slot to + be allocated. */ #define STARTING_FRAME_OFFSET 0 /* If we generate an insn to push BYTES bytes, @@ -505,8 +629,11 @@ extern enum reg_class reg_class_from_letter[]; VALTYPE is the data type of the value (as a tree). If the precise function being called is known, FUNC is its FUNCTION_DECL; otherwise, FUNC is 0. */ -#define FUNCTION_VALUE(VALTYPE, FUNC) \ - gen_rtx (REG, TYPE_MODE (VALTYPE), FIRST_RET_REG) + +#define FUNCTION_VALUE(VALTYPE, FUNC) \ + gen_rtx (REG, \ + TYPE_MODE (VALTYPE) == BLKmode ? SImode : TYPE_MODE (VALTYPE), \ + FIRST_RET_REG) /* Define how to find the value returned by a library function assuming the value has mode MODE. */ @@ -514,7 +641,7 @@ extern enum reg_class reg_class_from_letter[]; gen_rtx (REG, MODE, FIRST_RET_REG) /* 1 if N is a possible register number for a function value. - On the SH, only r4 can return results. */ + On the SH, only r0 can return results. */ #define FUNCTION_VALUE_REGNO_P(REGNO) \ ((REGNO) == FIRST_RET_REG) @@ -593,6 +720,7 @@ extern enum reg_class reg_class_from_letter[]; #define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \ (NAMED && ROUND_REG ((CUM), (MODE)) < NPARM_REGS \ + && (MODE) != BLKmode \ && ((TYPE)==0 || ! TREE_ADDRESSABLE ((tree)(TYPE))) \ && ((TYPE)==0 || (MODE) != BLKmode \ || (TYPE_ALIGN ((TYPE)) % PARM_BOUNDARY == 0)) \ @@ -603,20 +731,10 @@ extern enum reg_class reg_class_from_letter[]; /* 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 NPARM_REGS regs but won't entirely - fit in them needs partial registers on the SH. */ - -#define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) \ - ((ROUND_REG ((CUM), (MODE)) < NPARM_REGS \ - && ((TYPE)==0 || ! TREE_ADDRESSABLE ((tree)(TYPE))) \ - && ((TYPE)==0 || (MODE) != BLKmode \ - || (TYPE_ALIGN ((TYPE)) % PARM_BOUNDARY == 0)) \ - && (ROUND_REG ((CUM), (MODE)) \ - + ((MODE) == BLKmode \ - ? ROUND_ADVANCE (int_size_in_bytes (TYPE)) \ - : ROUND_ADVANCE (GET_MODE_SIZE (MODE)))) - NPARM_REGS > 0) \ - ? (NPARM_REGS - ROUND_REG ((CUM), (MODE))) \ - : 0) + + We never split args */ + +#define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) 0 extern int current_function_anonymous_args; @@ -647,8 +765,8 @@ extern int current_function_anonymous_args; /* Generate the assembly code for function exit Just dump out any accumulated constant table.*/ -#define FUNCTION_EPILOGUE(STREAM, SIZE) \ - dump_constants(0); +#define FUNCTION_EPILOGUE(STREAM, SIZE) function_epilogue (STREAM, SIZE) + /* Output assembler code for a block containing the constant parts of a trampoline, leaving space for the variable parts. @@ -691,7 +809,6 @@ extern int current_function_anonymous_args; /* Addressing modes, and classification of registers for them. */ - /*#define HAVE_POST_INCREMENT 1*/ /*#define HAVE_PRE_INCREMENT 1*/ /*#define HAVE_POST_DECREMENT 1*/ @@ -706,27 +823,27 @@ extern int current_function_anonymous_args; has been allocated, which happens in local-alloc.c. */ + #define REGNO_OK_FOR_BASE_P(REGNO) \ ((REGNO) < PR_REG || (unsigned) reg_renumber[(REGNO)] < PR_REG) - -#define REGNO_OK_FOR_INDEX_P(REGNO) ((REGNO)==0) +#define REGNO_OK_FOR_INDEX_P(REGNO) \ + ((REGNO) == 0 || (unsigned) reg_renumber[(REGNO)] == 0) /* Maximum number of registers that can appear in a valid memory address. */ -#define MAX_REGS_PER_ADDRESS 4 +#define MAX_REGS_PER_ADDRESS 2 /* Recognize any constant value that is a valid address. */ #define CONSTANT_ADDRESS_P(X) \ (GET_CODE (X) == LABEL_REF) -/* Nonzero if the constant value X is a legitimate general operand. - It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE. +/* Nonzero if the constant value X is a legitimate general operand. */ - On the SH, allow anything but a double */ +#define LEGITIMATE_CONSTANT_P(X) \ + (GET_CODE(X) != CONST_DOUBLE && GET_CODE(X) != LABEL_REF) -#define LEGITIMATE_CONSTANT_P(X) (GET_CODE(X) != CONST_DOUBLE) /* The macros REG_OK_FOR..._P assume that the arg is a REG rtx and check its validity for a certain class. @@ -735,23 +852,27 @@ extern int current_function_anonymous_args; them unless they have been allocated suitable hard regs. The symbol REG_OK_STRICT causes the latter definition to be used. */ +#define MODE_DISP_OK_4(X,MODE) ((GET_MODE_SIZE(MODE)==4) && ((unsigned)INTVAL(X)<64)) +#define MODE_DISP_OK_2(X,MODE) ((GET_MODE_SIZE(MODE)==2) && ((unsigned)INTVAL(X)<32) && TARGET_TRYR0) +#define MODE_DISP_OK_1(X,MODE) ((GET_MODE_SIZE(MODE)==1) && ((unsigned)INTVAL(X)<16) && TARGET_TRYR0) + #ifndef REG_OK_STRICT + /* Nonzero if X is a hard reg that can be used as a base reg or if it is a pseudo reg. */ #define REG_OK_FOR_BASE_P(X) \ - (REGNO (X) <= 16 || REGNO (X) >= FIRST_PSEUDO_REGISTER) - + (REGNO (X) <= 16 || REGNO(X) >= FIRST_PSEUDO_REGISTER) /* Nonzero if X is a hard reg that can be used as an index or if it is a pseudo reg. */ + #define REG_OK_FOR_INDEX_P(X) \ - (REGNO (X) == 0 || REGNO (X) >= FIRST_PSEUDO_REGISTER) + (REGNO (X) == 0 || REGNO(X) >= FIRST_PSEUDO_REGISTER) #define REG_OK_FOR_PRE_POST_P(X) \ (REGNO (X) <= 16) #else - /* Nonzero if X is a hard reg that can be used as a base reg. */ #define REG_OK_FOR_BASE_P(X) \ REGNO_OK_FOR_BASE_P (REGNO (X)) @@ -761,8 +882,29 @@ extern int current_function_anonymous_args; REGNO_OK_FOR_INDEX_P (REGNO (X)) #define REG_OK_FOR_PRE_POST_P(X) \ - (REGNO (X) <= 16 || (unsigned) reg_renumber[REGNO (X)] <=16) + (REGNO (X) <= 16) #endif + +/* The Q is a pc relative load operand */ +#define EXTRA_CONSTRAINT_Q(OP) \ + (GET_CODE (OP) == MEM && GET_CODE (XEXP (OP,0)) == LABEL_REF) + +/* The U is a label ref */ +#define EXTRA_CONSTRAINT_U(OP) \ + (GET_CODE (OP) == LABEL_REF) + +#define IS_INDEX(OP) \ + ((GET_CODE(OP) == PLUS && \ + (INDEX_REGISTER_RTX_P(XEXP(OP,0)) && BASE_REGISTER_RTX_P(XEXP(OP,1))) || \ + (INDEX_REGISTER_RTX_P(XEXP(OP,1)) && BASE_REGISTER_RTX_P(XEXP(OP,0))))) + + + +#define EXTRA_CONSTRAINT(OP, C) \ + ((C) == 'Q' ? EXTRA_CONSTRAINT_Q (OP) \ + : (C) == 'U' ? EXTRA_CONSTRAINT_U (OP) \ + : 0) + /* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression that is a valid memory address for an instruction. @@ -796,18 +938,14 @@ extern int current_function_anonymous_args; A legitimate index for a QI or HI is 0, SI and above can be any number 0..63 */ -#define GO_IF_LEGITIMATE_INDEX(MODE, REGNO, OP, LABEL) \ - do { \ - if (GET_CODE (OP) == CONST_INT) \ - { \ - if (0&&GET_MODE_SIZE (MODE) == 2 && ((unsigned)INTVAL(OP)) <=30)\ - goto LABEL; \ - if (0&&GET_MODE_SIZE (MODE) == 1 && ((unsigned)INTVAL(OP)) <=15)\ - goto LABEL; \ - if (GET_MODE_SIZE (MODE) >=4 \ - && ((unsigned)INTVAL(OP)) < 64) \ - goto LABEL; \ - } \ +#define GO_IF_LEGITIMATE_INDEX(MODE, REGNO, OP, LABEL) \ + do { \ + if (GET_CODE (OP) == CONST_INT) \ + { \ + if (MODE_DISP_OK_4 (OP, MODE)) goto LABEL; \ + if (MODE_DISP_OK_2 (OP, MODE)) goto LABEL; \ + if (MODE_DISP_OK_1 (OP, MODE)) goto LABEL; \ + } \ } while(0) @@ -819,13 +957,13 @@ extern int current_function_anonymous_args; && GET_CODE (XEXP (X, 0)) == REG \ && REG_OK_FOR_PRE_POST_P (XEXP (X, 0))) \ goto LABEL; \ - else if (GET_CODE (X) == PLUS || GET_CODE(X) == LO_SUM) \ + else if (GET_CODE (X) == PLUS) \ { \ rtx xop0 = XEXP(X,0); \ rtx xop1 = XEXP(X,1); \ - if (GET_MODE_SIZE(MODE) >= 4 && BASE_REGISTER_RTX_P (xop0)) \ + if (GET_MODE_SIZE(MODE) <= 4 && BASE_REGISTER_RTX_P (xop0)) \ GO_IF_LEGITIMATE_INDEX (MODE, REGNO (xop0), xop1, LABEL); \ - if (GET_MODE_SIZE(MODE) >= 4 && BASE_REGISTER_RTX_P (xop1)) \ + if (GET_MODE_SIZE(MODE) <= 4 && BASE_REGISTER_RTX_P (xop1)) \ GO_IF_LEGITIMATE_INDEX (MODE, REGNO (xop1), xop0, LABEL); \ if (GET_MODE_SIZE(MODE)<=4) { \ if(BASE_REGISTER_RTX_P(xop1) && \ @@ -869,12 +1007,12 @@ extern int current_function_anonymous_args; /* Specify the machine mode that this machine uses for the index in the tablejump instruction. */ -#define CASE_VECTOR_MODE SImode +#define CASE_VECTOR_MODE (TARGET_BIGTABLE ? SImode : HImode) /* Define this if the tablejump instruction expects the table to contain offsets from the address of the table. Do not define this if the table should contain absolute addresses. */ -/* #define CASE_VECTOR_PC_RELATIVE */ +#define CASE_VECTOR_PC_RELATIVE /* Specify the tree operation to be used to convert reals to integers. */ #define IMPLICIT_FIX_EXPR FIX_ROUND_EXPR @@ -889,7 +1027,7 @@ extern int current_function_anonymous_args; #define SIZE_TYPE "unsigned int" /* Don't cse the address of the function being compiled. */ -#define NO_RECURSIVE_FUNCTION_CSE 1 +/*#define NO_RECURSIVE_FUNCTION_CSE 1*/ /* Max number of bytes we can move from memory to memory in one reasonably fast instruction. */ @@ -940,15 +1078,12 @@ extern int current_function_anonymous_args; #define Pmode SImode #define FUNCTION_MODE Pmode -/* The structure type of the machine dependent info field of insns - No uses for this yet. */ -/* #define INSN_MACHINE_INFO struct machine_info */ - /* The relative costs of various types of constants. Note that cse.c defines REG = 1, SUBREG = 2, any node = (2 + sum of subnodes). */ #define CONST_COSTS(RTX, CODE, OUTER_CODE) \ case CONST_INT: \ + if (INTVAL(RTX)==0) return 0; \ if (CONST_OK_FOR_I (INTVAL(RTX))) \ return 1; \ else \ @@ -961,6 +1096,8 @@ extern int current_function_anonymous_args; return 10; #define RTX_COSTS(X, CODE, OUTER_CODE) \ + case AND: \ + return COSTS_N_INSNS (andcosts (X)); \ case MULT: \ return COSTS_N_INSNS (multcosts (X)); \ case ASHIFT: \ @@ -976,6 +1113,34 @@ extern int current_function_anonymous_args; case FIX: \ return 100; + +/* The multiply and divide insns on the SH are actually function calls + with some special constraints on arguments and register usage. + + These macros tell reorg that the references to arguments and + register clobbers for insns of type sfunc do not appear to happen + until after the millicode call. This allows reorg to put insns + which set the argument registers into the delay slot of the millicode + call -- thus they act more like traditional CALL_INSNs. + + get_attr_type will try to recognize the given insn, so make sure to + filter out things it will not accept -- SEQUENCE, USE and CLOBBER insns + in particular. */ + +#define INSN_SETS_ARE_DELAYED(X) \ + ((GET_CODE (X) == INSN \ + && GET_CODE (PATTERN (X)) != SEQUENCE \ + && GET_CODE (PATTERN (X)) != USE \ + && GET_CODE (PATTERN (X)) != CLOBBER \ + && get_attr_type (X) == TYPE_SFUNC)) + +#define INSN_REFERENCES_ARE_DELAYED(X) \ + ((GET_CODE (X) == INSN \ + && GET_CODE (PATTERN (X)) != SEQUENCE \ + && GET_CODE (PATTERN (X)) != USE \ + && GET_CODE (PATTERN (X)) != CLOBBER \ + && get_attr_type (X) == TYPE_SFUNC)) + /* Compute extra cost of moving data between one register class and another. @@ -984,7 +1149,7 @@ extern int current_function_anonymous_args; */ #define REGISTER_MOVE_COST(SRCCLASS, DSTCLASS) \ - ((DSTCLASS == T_REGS) ? 10 : 1) + (((DSTCLASS == T_REGS) || (DSTCLASS == PR_REG)) ? 10 : 1) /* Assembler output control */ @@ -994,23 +1159,23 @@ extern int current_function_anonymous_args; W_options, sizeof W_options / sizeof W_options[0]); -#define ASM_FILE_END(STREAM) \ - dump_constants(0); +#define ASM_FILE_END(STREAM) -#define ASM_APP_ON "" -#define ASM_APP_OFF "" -#define FILE_ASM_OP "\t.file\n" -#define IDENT_ASM_OP "\t.ident\n" +#define ASM_APP_ON "" +#define ASM_APP_OFF "" +#define FILE_ASM_OP "\t.file\n" +#define IDENT_ASM_OP "\t.ident\n" +/* How to change between sections. */ -/* Switch to the text or data segment. */ -#define TEXT_SECTION_ASM_OP "\t.text" -#define DATA_SECTION_ASM_OP "\t.data" -#define CTORS_SECTION_ASM_OP "\t.section\t.ctors\n" -#define DTORS_SECTION_ASM_OP "\t.section\t.dtors\n" +#define TEXT_SECTION_ASM_OP "\t.text" +#define DATA_SECTION_ASM_OP "\t.data" +#define CTORS_SECTION_ASM_OP "\t.section\t.ctors\n" +#define DTORS_SECTION_ASM_OP "\t.section\t.dtors\n" +#define INIT_SECTION_ASM_OP "\t.section\t.init\n" +#define EXTRA_SECTIONS in_ctors, in_dtors -#define EXTRA_SECTIONS in_ctors, in_dtors #define EXTRA_SECTION_FUNCTIONS \ void \ ctors_section() \ @@ -1040,8 +1205,8 @@ dtors_section() \ #define ASM_OUTPUT_DESTRUCTOR(FILE,NAME) \ do { dtors_section(); fprintf(FILE,"\t.long\t_%s\n", NAME); } while (0) - #undef DO_GLOBAL_CTORS_BODY + #define DO_GLOBAL_CTORS_BODY \ { \ typedef (*pfunc)(); \ @@ -1068,14 +1233,12 @@ dtors_section() \ } - #define ASM_OUTPUT_REG_PUSH(file, v) \ fprintf (file, "\tmov.l r%s,-@r15\n", v); #define ASM_OUTPUT_REG_POP(file, v) \ fprintf (file, "\tmov.l @r15+,r%s\n", v); - /* The assembler's names for the registers. RFP need not always be used as the Real framepointer; it can also be used as a normal general register. @@ -1133,18 +1296,25 @@ dtors_section() \ ((OUTVAR) = (char *) alloca (strlen (NAME) + 10), \ sprintf ((OUTVAR), "%s.%d", (NAME), (NUMBER))) -/* Jump tables must be 32 bit aligned. */ +/* Jump tables must be 32 bit aligned, no matter the size of the element */ #define ASM_OUTPUT_CASE_LABEL(STREAM,PREFIX,NUM,TABLE) \ - fprintf (STREAM, "\t.align 2\n%s%d:\n", PREFIX, NUM); + fprintf (STREAM, "\t.align 2\n%s%d:\n", PREFIX, NUM); + +/* Output a relative address table. */ -/* Output a relative address. Not needed since jump tables are absolute - but we must define it anyway. */ -#define ASM_OUTPUT_ADDR_DIFF_ELT(STREAM,VALUE,REL) \ - fputs ("- - - ASM_OUTPUT_ADDR_DIFF_ELT called!\n", STREAM) +#define ASM_OUTPUT_ADDR_DIFF_ELT(STREAM,VALUE,REL) \ + if (TARGET_BIGTABLE) \ + fprintf (STREAM, "\t.long L%d-L%d\n", VALUE,REL); \ + else \ + fprintf (STREAM, "\t.word L%d-L%d\n", VALUE,REL); \ -/* Output an element of a dispatch table. */ -#define ASM_OUTPUT_ADDR_VEC_ELT(STREAM,VALUE) \ - fprintf (STREAM, "\t.long\tL%d\n", VALUE) +/* Output an absolute table element */ + +#define ASM_OUTPUT_ADDR_VEC_ELT(STREAM,VALUE) \ + if (TARGET_BIGTABLE) \ + fprintf (STREAM, "\t.long L%d\n", VALUE); \ + else \ + fprintf (STREAM, "\t.word L%d\n", VALUE); \ /* Output various types of constants. */ @@ -1165,7 +1335,6 @@ do { char dstr[30]; \ fprintf (FILE, "\t.float %s\n", dstr); \ } while (0) - #define ASM_OUTPUT_INT(STREAM, EXP) \ (fprintf (STREAM, "\t.long\t"), \ output_addr_const (STREAM, (EXP)), \ @@ -1237,16 +1406,13 @@ do { char dstr[30]; \ #define PRINT_OPERAND_ADDRESS(STREAM,X) print_operand_address (STREAM, X) #define PRINT_OPERAND_PUNCT_VALID_P(CHAR) \ - ((CHAR)=='.' || (CHAR) == '#' || (CHAR) == '*' || (CHAR) == '^' || (CHAR) == '!') + ((CHAR)=='.' || (CHAR) == '#' || (CHAR) == '*' || (CHAR) == '^' || (CHAR)=='!') -/* Define the information needed to generate branch insns. This is stored - from the compare operation. Note that we can't use "rtx" here since it - hasn't been defined! */ - extern struct rtx_def *sh_compare_op0; extern struct rtx_def *sh_compare_op1; extern struct rtx_def *prepare_scc_operands(); +extern struct rtx_def *table_lab; extern enum attr_cpu sh_cpu; /* target cpu */ @@ -1256,12 +1422,24 @@ extern char *output_branch(); extern char *output_shift(); extern char *output_movedouble(); extern char *output_movepcrel(); +extern char *output_jump_label_table(); +extern char *output_far_jump(); + +#define MACHINE_DEPENDENT_REORG(X) machine_dependent_reorg(X) -#define ADJUST_INSN_LENGTH(insn, length) \ - adjust_insn_length (insn, insn_lengths) +/* Generate calls to memcpy, memcmp and memset. */ +#define TARGET_MEM_FUNCTIONS +#define HANDLE_PRAGMA(finput) handle_pragma (finput) +/* Set when processing a function with pragma interrupt turned on. */ +extern int pragma_interrupt; +#define MOVE_RATIO 16 +char *max_si; +char *max_hi; +int max_count_si; +int max_count_hi; diff --git a/gcc/config/sh/sh.md b/gcc/config/sh/sh.md index 5ba25734a1d..3bdb8010fa1 100644 --- a/gcc/config/sh/sh.md +++ b/gcc/config/sh/sh.md @@ -18,10 +18,37 @@ ;; along with GNU CC; see the file COPYING. If not, write to ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. -;;- See file "rtl.def" for documentation on define_insn, match_*, et. al. - +;; Special constraints for SH machine description: +;; +;; t -- T +;; x -- mac +;; l -- pr +;; z -- r0 +;; +;; Special formats used for outputting SH instructions: +;; +;; %. -- print a .s if insn needs delay slot +;; %* -- print a local label +;; %^ -- increment the local label number +;; %# -- 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 +;; +;; +;; Special predicates: +;; +;; arith_operand -- operand is valid source for arithmetic op +;; arith_reg_operand -- operand is valid register for arithmetic op +;; byte_index_operand -- operand is ok as an index in a mov.b +;; general_movdst_operand -- operand is valid move destination +;; general_movsrc_operand -- operand is valid move source +;; logical_operand -- operand is valid source for logical op +;; pop_operand -- operand is a pop from the stack +;; system_reg_operand -- operand is MACL, MACH, or PR ;; ------------------------------------------------------------------------- ;; Attributes ;; ------------------------------------------------------------------------- @@ -30,66 +57,98 @@ (define_attr "cpu" "sh0,sh1,sh2,sh3" (const (symbol_ref "sh_cpu"))) - -(define_attr "type" "cbranch,ctable,jump,arith,other,load,store,move,smpy,dmpy,return,pload" +;; +;; cbranch conditional branch instructions +;; jump unconditional jumps +;; arith ordinary arithmetic +;; load from memory +;; store to memory +;; move register to register +;; smpy single precision integer multiply +;; dmpy double precision integer multiply +;; return rts +;; pload load of pr reg (can't be put into delay slot of rts) +;; pcloadsi pc relative load of SI value +;; pcloadhi pc relative load of HI value +;; rte return from exception +;; sfunc special function call with known used registers + +(define_attr "type" + "cbranch,jump,arith,other,load,store,move,smpy,dmpy,return,pload,pcloadsi,pcloadhi,rte,sfunc" (const_string "other")) -; If a conditional branch destination is within -100..100 bytes away +; If a conditional branch destination is within -120..120 bytes away ; from the instruction it can be 2 bytes long. Something in the -; range -4000..4000 bytes can be 6 bytes long, all other conditional +; range -4090..4090 bytes can be 6 bytes long, all other conditional ; branches are 8 bytes long. ; An unconditional jump which can reach forward or back 4k can be ; 6 bytes long (including the delay slot). If it is too big, it -; must be 8 bytes long. +; must be 10 bytes long. +; If a pcrel instruction is within 500 bytes of the constant, then the insn is +; 2 bytes long, otherwise 12 bytes ; All other instructions are two bytes long by default. (define_attr "length" "" (cond [(eq_attr "type" "cbranch") - (if_then_else (and (ge (minus (pc) (match_dup 0)) - (const_int -100)) - (le (minus (pc) (match_dup 0)) - (const_int 100))) - (const_int 2) - (if_then_else (and (ge (minus (pc) (match_dup 0)) - (const_int -4000)) - (le (minus (pc) (match_dup 0)) - (const_int 4000))) - (const_int 6) - (const_int 8))) + (if_then_else (and (ge (minus (pc) (match_dup 0)) + (const_int -122)) + (le (minus (pc) (match_dup 0)) + (const_int 122))) + (const_int 2) + (if_then_else (and (ge (minus (pc) (match_dup 0)) + (const_int -4090)) + (le (minus (pc) (match_dup 0)) + (const_int 4090))) + (const_int 6) + (const_int 16))) (eq_attr "type" "jump") - (if_then_else (and (ge (minus (pc) (match_dup 0)) - (const_int -4000)) - (le (minus (pc) (match_dup 0)) - (const_int 4000))) - (const_int 4) - (const_int 6)) + (if_then_else (and (ge (minus (pc) (match_dup 0)) + (const_int -4090)) + (le (minus (pc) (match_dup 0)) + (const_int 4090))) + (const_int 4) + (const_int 10)) + (eq_attr "type" "pcloadsi") + (if_then_else (gt (pc) (minus (match_dup 0) (const_int 1000))) + (const_int 2) + (const_int 12)) + (eq_attr "type" "pcloadhi") + (if_then_else (gt (pc) (minus (match_dup 0) (const_int 500))) + (const_int 2) + (const_int 12)) + ] (const_int 2))) ;; (define_function_unit {name} {num-units} {n-users} {test} ;; {ready-delay} {issue-delay} [{conflict-list}]) -(define_function_unit "memory" 1 1 (eq_attr "type" "load") 1 0) -(define_function_unit "mpy" 1 1 (eq_attr "type" "smpy") 3 0) -(define_function_unit "mpy" 1 1 (eq_attr "type" "dmpy") 5 0) +(define_function_unit "memory" 1 0 (eq_attr "type" "load,pcloadsi,pcloadhi") 2 0) +(define_function_unit "mpy" 1 0 (eq_attr "type" "smpy") 3 0) +(define_function_unit "mpy" 1 0 (eq_attr "type" "dmpy") 5 0) (define_attr "needs_delay_slot" "yes,no" - (cond [(eq_attr "type" "jump") (const_string "yes") + (cond [(eq_attr "type" "jump") (const_string "yes") (eq_attr "type" "return") (const_string "yes")] (const_string "no"))) (define_delay - (eq_attr "needs_delay_slot" "yes") + (eq_attr "needs_delay_slot" "yes") [(eq_attr "in_delay_slot" "yes") (nil) (nil)]) +(define_delay + (eq_attr "type" "rte") + [(and (eq_attr "in_delay_slot" "yes") + (eq_attr "hit_stack" "no")) (nil) (nil)]) + +(define_attr "hit_stack" "yes,no" (const_string "no")) -(define_attr "dump" "yes,no,must" (const_string "no")) -(define_attr "constneed" "yes,no" (const_string "no")) -(define_attr "smallestsize" "" (const_int 2)) -(define_attr "largestsize" "" (const_int 8)) -(define_attr "constantsize" "" (const_int 4)) +(define_delay + (and (eq_attr "type" "cbranch") + (eq_attr "cpu" "sh2")) + [(eq_attr "in_delay_slot" "yes") (nil) (nil)]) (define_attr "in_delay_slot" "maybe,yes,no" (cond [(eq_attr "type" "cbranch") (const_string "no") @@ -100,7 +159,6 @@ (eq_attr "length" "4,6,8,10,12") (const_string "no") ] (const_string "yes"))) - ;; ------------------------------------------------------------------------- ;; SImode signed integer comparisons @@ -112,49 +170,50 @@ (const_int 1)))] "" "movt %0 !movt1") - + (define_insn "" - [(set (reg:SI 18) (gt (match_operand:SI 0 "arith_reg_operand" "r") - (const_int 0)))] + [(set (reg:SI 18) (gt:SI (match_operand:SI 0 "arith_reg_operand" "r") + (const_int 0)))] "" "cmp/pl %0") (define_insn "" - [(set (reg:SI 18) (ge (match_operand:SI 0 "arith_reg_operand" "r") - (const_int 0)))] + [(set (reg:SI 18) (ge:SI (match_operand:SI 0 "arith_reg_operand" "r") + (const_int 0)))] "" "cmp/pz %0") -(define_insn "cmpeqsi_t" - [(set (reg:SI 18) (eq (match_operand:SI 0 "arith_reg_operand" "r,z") - (match_operand:SI 1 "arith_operand" "r,I")))] +(define_insn "cmpeq_0" + [(set (reg:SI 18) (eq:SI (match_operand:SI 0 "arith_reg_operand" "r") + (const_int 0)))] "" - "cmp/eq %1,%0") + "tst %0,%0 ! t0") +(define_insn "cmpeqsi_t" + [(set (reg:SI 18) (eq:SI (match_operand:SI 0 "arith_operand" "r,N,r,z") + (match_operand:SI 1 "arith_operand" "N,r,r,I")))] + "" + "@ + tst %0,%0 !t1 + tst %1,%1 !t2 + cmp/eq %1,%0 + cmp/eq %1,%0") (define_insn "cmpgtsi_t" - [(set (reg:SI 18) (gt (match_operand:SI 0 "arith_reg_operand" "r") - (match_operand:SI 1 "arith_reg_operand" "r")))] + [(set (reg:SI 18) (gt:SI (match_operand:SI 0 "arith_reg_operand" "r,r") + (match_operand:SI 1 "arith_operand" "N,r")))] "" - "cmp/gt %1,%0") + "@ + cmp/pl %0 + cmp/gt %1,%0") (define_insn "cmpgesi_t" - [(set (reg:SI 18) (ge (match_operand:SI 0 "arith_reg_operand" "r") - (match_operand:SI 1 "arith_reg_operand" "r")))] + [(set (reg:SI 18) (ge:SI (match_operand:SI 0 "arith_reg_operand" "r,r") + (match_operand:SI 1 "arith_operand" "N,r")))] "" - "cmp/ge %1,%0") - -(define_insn "cmpltsi_t" - [(set (reg:SI 18) (lt (match_operand:SI 0 "arith_reg_operand" "r") - (match_operand:SI 1 "arith_reg_operand" "r")))] - "" - "cmp/gt %0,%1") - -(define_insn "cmplesi_t" - [(set (reg:SI 18) (le (match_operand:SI 0 "arith_reg_operand" "r") - (match_operand:SI 1 "arith_reg_operand" "r")))] - "" - "cmp/ge %0,%1") + "@ + cmp/pz %0 + cmp/ge %1,%0") ;; ------------------------------------------------------------------------- @@ -162,38 +221,31 @@ ;; ------------------------------------------------------------------------- (define_insn "cmpgeusi_t" - [(set (reg:SI 18) (geu (match_operand:SI 0 "arith_reg_operand" "r") - (match_operand:SI 1 "arith_reg_operand" "r")))] + [(set (reg:SI 18) (geu:SI (match_operand:SI 0 "arith_reg_operand" "r,r") + (match_operand:SI 1 "arith_operand" "N,r")))] "" - "cmp/hs %1,%0") + "@ + cmp/pz %1 + cmp/hs %1,%0") (define_insn "cmpgtusi_t" - [(set (reg:SI 18) (gtu (match_operand:SI 0 "arith_reg_operand" "r") - (match_operand:SI 1 "arith_reg_operand" "r")))] - "" - "cmp/hi %1,%0") - -(define_insn "cmpleusi_t" - [(set (reg:SI 18) (leu (match_operand:SI 0 "arith_reg_operand" "r") - (match_operand:SI 1 "arith_reg_operand" "r")))] - "" - "cmp/hs %0,%1") - -(define_insn "cmpltusi_t" - [(set (reg:SI 18) (ltu (match_operand:SI 0 "arith_reg_operand" "r") - (match_operand:SI 1 "arith_reg_operand" "r")))] + [(set (reg:SI 18) (gtu:SI (match_operand:SI 0 "arith_operand" "r,r") + (match_operand:SI 1 "arith_operand" "N,r")))] "" - "cmp/hi %0,%1") + "@ + cmp/pl %1 + cmp/hi %1,%0") ;; We save the compare operands in the cmpxx patterns and use them when ;; we generate the branch. (define_expand "cmpsi" - [(set (reg:SI 18) (compare (match_operand:SI 0 "arith_reg_operand" "") - (match_operand:SI 1 "arith_reg_operand" "")))] + [(set (reg:SI 18) (compare (match_operand:SI 0 "arith_operand" "") + (match_operand:SI 1 "arith_operand" "")))] "" " -{ sh_compare_op0 = operands[0]; +{ + sh_compare_op0 = operands[0]; sh_compare_op1 = operands[1]; DONE; }") @@ -203,19 +255,36 @@ ;; Addition instructions ;; ------------------------------------------------------------------------- -(define_insn "adddi3" - [(set (match_operand:DI 0 "arith_reg_operand" "=&r") - (plus:DI (match_operand:DI 1 "arith_reg_operand" "%0") - (match_operand:DI 2 "arith_reg_operand" "r"))) +(define_insn "addc" + [(set (match_operand:SI 0 "arith_reg_operand" "=r") + (plus:SI (reg:SI 18) + (plus:SI (match_operand:SI 1 "arith_reg_operand" "%0") + (match_operand:SI 2 "arith_reg_operand" "r")))) (clobber (reg:SI 18))] "" - "clrt\;addc %R2,%R0\;addc %2,%0" - [(set_attr "length" "6") - (set_attr "in_delay_slot" "no") - (set_attr "type" "arith")]) + "addc %2,%0") + +;; this should be a define split. + +(define_expand "adddi3" + [(set (reg:SI 18) (const_int 0)) + (parallel + [(set (subreg:SI (match_operand:DI 0 "arith_reg_operand" "=r") 1) + (plus:SI (reg:SI 18) + (plus:SI (subreg:SI (match_operand:DI 1 "arith_reg_operand" "r") 1) + (subreg:SI (match_operand:DI 2 "arith_reg_operand" "r") 1)))) + (clobber (reg:SI 18))]) + (parallel + [(set (subreg:SI (match_dup 0) 0) + (plus:SI (reg:SI 18) + (plus:SI (subreg:SI (match_dup 1) 0) + (subreg:SI (match_dup 2) 0)))) + (clobber (reg:SI 18))])] + "" + "") -(define_insn "addsi3_i" +(define_insn "addsi3_real" [(set (match_operand:SI 0 "arith_reg_operand" "=r") (plus:SI (match_operand:SI 1 "arith_reg_operand" "%0") (match_operand:SI 2 "arith_operand" "rI")))] @@ -226,7 +295,7 @@ (define_expand "addsi3" [(set (match_operand:SI 0 "arith_reg_operand" "=r") - (plus:SI (match_operand:SI 1 "arith_reg_operand" "%0") + (plus:SI (match_operand:SI 1 "arith_operand" "%0") (match_operand:SI 2 "arith_operand" "rI")))] "" "") @@ -237,7 +306,7 @@ ;; ------------------------------------------------------------------------- (define_insn "subdi3" - [(set (match_operand:DI 0 "arith_reg_operand" "=&r") + [(set (match_operand:DI 0 "arith_reg_operand" "=r") (minus:DI (match_operand:DI 1 "arith_reg_operand" "0") (match_operand:DI 2 "arith_reg_operand" "r"))) (clobber (reg:SI 18))] @@ -248,27 +317,95 @@ (set_attr "type" "arith")]) (define_insn "subsi3" - [(set (match_operand:SI 0 "arith_reg_operand" "=r,r") - (minus:SI (match_operand:SI 1 "arith_reg_operand" "0,0") - (match_operand:SI 2 "arith_operand" "r,I")))] + [(set (match_operand:SI 0 "arith_reg_operand" "=r") + (minus:SI (match_operand:SI 1 "arith_reg_operand" "0") + (match_operand:SI 2 "arith_operand" "r")))] "" - "@ - sub %2,%0 - add %M2,%0" + "sub %2,%0" [(set_attr "type" "arith")]) ;; ------------------------------------------------------------------------- -;; Multiplication instructions +;; Division instructions ;; ------------------------------------------------------------------------- +;; we take advantage of the library routines which don't clobber as many +;; registers as a normal function call would. + + +(define_insn "" + [(set (reg:SI 0) + (udiv:SI (reg:SI 4) (reg:SI 5))) + (clobber (reg:SI 18)) + (clobber (reg:SI 17)) + (clobber (reg:SI 6)) + (clobber (reg:SI 4)) + (use (match_operand:SI 0 "register_operand" "r"))] + "" + "jsr @%0%#" + [(set_attr "type" "sfunc") + (set_attr "length" "4") + (set_attr "needs_delay_slot" "yes")]) + +(define_expand "udivsi3" + [(set (reg:SI 4) (match_operand:SI 1 "general_operand" "g")) + (set (reg:SI 5) (match_operand:SI 2 "general_operand" "g")) + (set (match_dup 3) (symbol_ref:SI "__udivsi3")) + (parallel[(set (reg:SI 0) + (udiv:SI (reg:SI 4) + (reg:SI 5))) + (clobber (reg:SI 18)) + (clobber (reg:SI 17)) + (clobber (reg:SI 6)) + (clobber (reg:SI 4)) + (use (match_dup 3))]) + (set (match_operand:SI 0 "general_operand" "=g") + (reg:SI 0))] + "" + "operands[3] = gen_reg_rtx(SImode);") + + +(define_insn "" + [(set (reg:SI 0) + (div:SI (reg:SI 4) (reg:SI 5))) + (clobber (reg:SI 18)) + (clobber (reg:SI 17)) + (clobber (reg:SI 4)) + (clobber (reg:SI 3)) + (use (match_operand:SI 0 "register_operand" "r"))] + "" + "jsr @%0%#" + [(set_attr "type" "sfunc") + (set_attr "length" "4") + (set_attr "needs_delay_slot" "yes")]) + +(define_expand "divsi3" + [(set (reg:SI 4) (match_operand:SI 1 "general_operand" "g")) + (set (reg:SI 5) (match_operand:SI 2 "general_operand" "g")) + (set (match_dup 3) (symbol_ref:SI "__sdivsi3")) + (parallel[(set (reg:SI 0) + (div:SI (reg:SI 4) + (reg:SI 5))) + (clobber (reg:SI 18)) + (clobber (reg:SI 17)) + (clobber (reg:SI 4)) + (clobber (reg:SI 3)) + (use (match_dup 3))]) + (set (match_operand:SI 0 "general_operand" "=g") + (reg:SI 0))] + "" + "operands[3] = gen_reg_rtx(SImode);") + + +;; ------------------------------------------------------------------------- +;; Multiplication instructions +;; ------------------------------------------------------------------------- + (define_insn "" [(set (reg:SI 21) - (mult:SI (zero_extend:SI - (match_operand:HI 1 "arith_reg_operand" "r")) - (zero_extend:SI - (match_operand:HI 2 "arith_reg_operand" "r"))))] + (mult:SI (zero_extend:SI (match_operand:HI 1 "arith_reg_operand" "r")) + (zero_extend:SI (match_operand:HI 2 "arith_reg_operand" "r"))))] "" "mulu %2,%1" [(set_attr "type" "smpy")]) @@ -305,12 +442,124 @@ "" "") +;; mulsi3 on the SH2 can be done in one instruction, on the SH1 we generate +;; a call to a routine which clobbers known registers. + +(define_insn "" + [(set (reg:SI 0) + (mult:SI (reg:SI 4) (reg:SI 5))) + (clobber (reg:SI 21)) + (clobber (reg:SI 18)) + (clobber (reg:SI 17)) + (clobber (reg:SI 3)) + (clobber (reg:SI 2)) + (clobber (reg:SI 1)) + (use (match_operand:SI 0 "register_operand" "r"))] + "" + "jsr @%0%#" + [(set_attr "type" "sfunc") + (set_attr "length" "4") + (set_attr "needs_delay_slot" "yes")]) + +(define_expand "mulsi3_call" + [(set (reg:SI 4) (match_operand:SI 1 "general_operand" "g")) + (set (reg:SI 5) (match_operand:SI 2 "general_operand" "g")) + (set (match_dup 3) (symbol_ref:SI "__mulsi3")) + (parallel[(set (reg:SI 0) + (mult:SI (reg:SI 4) + (reg:SI 5))) + (clobber (reg:SI 21)) + (clobber (reg:SI 18)) + (clobber (reg:SI 17)) + (clobber (reg:SI 3)) + (clobber (reg:SI 2)) + (clobber (reg:SI 1)) + (use (match_dup 3))]) + (set (match_operand:SI 0 "general_operand" "=g") + (reg:SI 0))] + "" + "operands[3] = gen_reg_rtx(SImode);") + +(define_insn "mul_l" + [(set (reg:SI 21) + (mult:SI (match_operand:SI 0 "arith_reg_operand" "r") + (match_operand:SI 1 "arith_reg_operand" "r")))] + "TARGET_SH2" + "mul.l %1,%0" + [(set_attr "type" "smpy")]) + +(define_expand "mulsi3" + [(set (reg:SI 21) + (mult:SI (match_operand:SI 1 "arith_reg_operand" "r") + (match_operand:SI 2 "arith_reg_operand" "r"))) + (set (match_operand:SI 0 "arith_reg_operand" "=r") + (reg:SI 21))] + "" + " +{ + if (!TARGET_SH2) + { + emit_insn (gen_mulsi3_call (operands[0], operands[1], operands[2])); + DONE; + } +}") + +(define_insn "" + [(set (reg:DI 20) + (mult:DI (sign_extend:DI (match_operand:SI 1 "arith_reg_operand" "r")) + (sign_extend:DI (match_operand:SI 2 "arith_reg_operand" "r"))))] + "TARGET_SH2" + "dmuls.l %2,%1" + [(set_attr "type" "dmpy")]) + +(define_expand "mulsidi3" + [(set (reg:DI 20) + (mult:DI (sign_extend:DI (match_operand:SI 1 "arith_reg_operand" "r")) + (sign_extend:DI (match_operand:SI 2 "arith_reg_operand" "r")))) + (set (match_operand:DI 0 "arith_reg_operand" "=r") + (reg:DI 20))] + "TARGET_SH2" + "") + +(define_insn "" + [(set (reg:DI 20) + (mult:DI (zero_extend:DI (match_operand:SI 1 "arith_reg_operand" "r")) + (zero_extend:DI (match_operand:SI 2 "arith_reg_operand" "r"))))] + "TARGET_SH2" + "dmulu.l %2,%1" + [(set_attr "type" "dmpy")]) + +(define_expand "umulsidi3" + [(set (reg:DI 20) + (mult:DI (zero_extend:DI (match_operand:SI 1 "arith_reg_operand" "r")) + (zero_extend:DI (match_operand:SI 2 "arith_reg_operand" "r")))) + (set (match_operand:DI 0 "arith_reg_operand" "=r") + (reg:DI 20))] + "TARGET_SH2" + "") + ;; ------------------------------------------------------------------------- ;; Logical operations ;; ------------------------------------------------------------------------- -(define_insn "andsi3" +(define_insn "and_ffff" + [(set (match_operand:SI 0 "arith_reg_operand" "=r") + (and:SI (match_operand:SI 1 "arith_reg_operand" "r") + (const_int 65535)))] + "" + "extu.w %1,%0" + [(set_attr "type" "arith")]) + +(define_insn "and_ff" + [(set (match_operand:SI 0 "arith_reg_operand" "=r") + (and:SI (match_operand:SI 1 "arith_reg_operand" "r") + (const_int 255)))] + "" + "extu.b %1,%0" + [(set_attr "type" "arith")]) + +(define_insn "" [(set (match_operand:SI 0 "arith_reg_operand" "=r,z") (and:SI (match_operand:SI 1 "arith_reg_operand" "%0,0") (match_operand:SI 2 "logical_operand" "r,L")))] @@ -318,6 +567,13 @@ "and %2,%0" [(set_attr "type" "arith")]) +(define_expand "andsi3" + [(set (match_operand:SI 0 "arith_reg_operand" "") + (and:SI (match_operand:SI 1 "arith_reg_operand" "") + (match_operand:SI 2 "logical_operand" "")))] + "" + "") + (define_insn "iorsi3" [(set (match_operand:SI 0 "arith_reg_operand" "=r,z") (ior:SI (match_operand:SI 1 "arith_reg_operand" "%0,0") @@ -326,9 +582,9 @@ "or %2,%0") (define_insn "xorsi3" - [(set (match_operand:SI 0 "arith_reg_operand" "=r,z") + [(set (match_operand:SI 0 "arith_reg_operand" "=z,r") (xor:SI (match_operand:SI 1 "arith_reg_operand" "%0,0") - (match_operand:SI 2 "logical_operand" "r,L")))] + (match_operand:SI 2 "logical_operand" "L,r")))] "" "xor %2,%0" [(set_attr "type" "arith")]) @@ -370,15 +626,34 @@ "" "{ if (GET_CODE(operands[2]) != CONST_INT || INTVAL(operands[2]) != 1) FAIL;}") + +;; +;; shift left + (define_insn "ashlsi3_k" [(set (match_operand:SI 0 "arith_reg_operand" "=r,r") (ashift:SI (match_operand:SI 1 "arith_reg_operand" "0,0") - (match_operand:SI 2 "immediate_operand" "K,n"))) + (match_operand:SI 2 "immediate_operand" "M,K"))) (clobber (reg:SI 18))] - "" + "CONST_OK_FOR_K (INTVAL (operands[2]))" + "@ + shll %0 + shll%O2 %0") + +; seperate pattern for shifts by any N. Look at pnum_clobbers +; to see if this is being recognised inside combine. If so, dont +; match, since combine will try and merge shifts, which will break +; scheduling + +(define_insn "ashlsi3_n" + [(set (match_operand:SI 0 "arith_reg_operand" "=r") + (ashift:SI (match_operand:SI 1 "arith_reg_operand" "0") + (match_operand:SI 2 "immediate_operand" "n"))) + (clobber (reg:SI 18))] + "(! pnum_clobbers)" "*return output_shift(\"shll\", operands[0], operands[2], ASHIFT);" - [(set_attr "length" "2,12") - (set_attr "in_delay_slot" "yes,no") + [(set_attr "length" "12") + (set_attr "in_delay_slot" "no") (set_attr "type" "arith")]) (define_expand "ashlsi3" @@ -387,48 +662,104 @@ (match_operand:SI 2 "immediate_operand" ""))) (clobber (reg:SI 18))])] "" - "if (!ok_shift_value(operands[2], ASHIFT)) FAIL;") + "if (gen_shifty_op (ASHIFT, operands)) DONE; else FAIL;") + +; +; arithmetic shift right +; (define_insn "ashrsi3_k" [(set (match_operand:SI 0 "arith_reg_operand" "=r") (ashiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0") - (const_int 1))) + (match_operand:SI 2 "immediate_operand" "M"))) (clobber (reg:SI 18))] - "" + "INTVAL(operands[2]) == 1" "shar %0" [(set_attr "type" "arith")]) +(define_insn "ashrsi3_16" + [(set (match_operand:SI 0 "arith_reg_operand" "=r") + (ashiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0") + (match_operand:SI 2 "immediate_operand" "i"))) + (clobber (reg:SI 18))] + "INTVAL(operands[2]) == 16" + "shlr16 %0\;exts.w %0,%0" + [(set_attr "type" "arith") + (set_attr "length" "4")]) + +; an arithmetic shift right by 16 is better as a logical shift and a +; sign extend + +;(define_split +; [(set (match_operand:SI 0 "arith_reg_operand" "=r") +; (ashiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0") +; (const_int 16))) +; (clobber (reg:SI 18))] +; "" +; [(set (match_dup 3) (match_dup 0)) +; (set (match_dup 3) (lshiftrt:SI (match_dup 3) (const_int 16))) +; (set (match_dup 0) (sign_extend:SI (subreg:HI (match_dup 3) 0)))] +; "operands[3] = gen_reg_rtx (SImode);") + +(define_insn "ashrsi3_n" + [(set (reg:SI 4) + (ashiftrt:SI (reg:SI 4) + (match_operand:SI 0 "immediate_operand" "i"))) + (clobber (reg:SI 18)) + (clobber (reg:SI 17)) + (use (match_operand:SI 1 "arith_reg_operand" "r"))] + "" + "jsr @%1%#" + [(set_attr "type" "sfunc") + (set_attr "in_delay_slot" "no") + (set_attr "length" "4") + (set_attr "needs_delay_slot" "yes")]) + (define_expand "ashrsi3" [(parallel[(set (match_operand:SI 0 "arith_reg_operand" "=r") (ashiftrt:SI (match_operand:SI 1 "arith_reg_operand" "r") (match_operand:SI 2 "nonmemory_operand" "M"))) (clobber (reg:SI 18))])] "" - " -{ - if (GET_CODE (operands[2]) != CONST_INT || - INTVAL (operands[2]) != 1) FAIL; -} -") + "if (gen_shifty_op (ASHIFTRT, operands)) DONE; else FAIL;") + +; logical shift right +; (define_insn "lshrsi3_k" [(set (match_operand:SI 0 "arith_reg_operand" "=r,r") (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0,0") - (match_operand:SI 2 "immediate_operand" "K,n"))) + (match_operand:SI 2 "immediate_operand" "M,K"))) (clobber (reg:SI 18))] - "" + "CONST_OK_FOR_K (INTVAL (operands[2]))" + "@ + shlr %0 + shlr%O2 %0") + +; seperate pattern for shifts by any N. Look at pnum_clobbers +; to see if this is being recognised inside combine. If so, dont +; match, since combine will try and merge shifts, which will break +; scheduling - this could be handled with a large number of +; define_splits + +(define_insn "lshrsi3_n" + [(set (match_operand:SI 0 "arith_reg_operand" "=r") + (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0") + (match_operand:SI 2 "immediate_operand" "n"))) + (clobber (reg:SI 18))] + "!pnum_clobbers" "* return output_shift (\"shlr\", operands[0], operands[2], LSHIFTRT);" - [(set_attr "length" "2,12") - (set_attr "in_delay_slot" "yes,no") + [(set_attr "length" "12") + (set_attr "in_delay_slot" "no") (set_attr "type" "arith")]) (define_expand "lshrsi3" [(parallel[(set (match_operand:SI 0 "arith_reg_operand" "") (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "") - (match_operand:SI 2 "nonmemory_operand" ""))) + (match_operand:SI 2 "immediate_operand" ""))) (clobber (reg:SI 18))])] "" - "if (!ok_shift_value (operands[2])) FAIL; ") + "if (gen_shifty_op (LSHIFTRT, operands)) DONE; else FAIL;") (define_insn "ashldi3_k" [(set (match_operand:DI 0 "arith_reg_operand" "=r") @@ -485,7 +816,6 @@ "{ if (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != 1) FAIL; } ") - ;; ------------------------------------------------------------------------- ;; Unary arithmetic @@ -512,7 +842,7 @@ (not:SI (match_operand:SI 1 "arith_reg_operand" "r")))] "" "not %1,%0" -[ (set_attr "type" "arith")]) + [(set_attr "type" "arith")]) ;; ------------------------------------------------------------------------- @@ -546,170 +876,187 @@ ;; ------------------------------------------------------------------------- (define_insn "extendsidi2" - [(set (match_operand:DI 0 "arith_reg_operand" "=r") - (sign_extend:DI (match_operand:SI 1 "arith_reg_operand" "0")))] + [(set (match_operand:DI 0 "arith_reg_operand" "=r,r") + (sign_extend:DI (match_operand:SI 1 "arith_reg_operand" "0,r"))) + (clobber (reg:SI 18))] "" - "mov %1,%0\;shll %0\;subc %0,%0" - [(set_attr "length" "6")]) + "@ + mov %1,%0\;shll %0\;subc %0,%0 ! b sidi2 + mov %1,%0\;mov %1,%R0\;shll %0\;subc %0,%0 ! a sidi2" + [(set_attr "length" "6,8")]) (define_insn "extendhisi2" - [(set (match_operand:SI 0 "arith_reg_operand" "=r") - (sign_extend:SI (match_operand:HI 1 "arith_reg_operand" "r")))] + [(set (match_operand:SI 0 "arith_reg_operand" "=r,z,r") + (sign_extend:SI (match_operand:HI 1 "arith_operand" "r,u,m")))] "" - "exts.w %1,%0") + "@ + exts.w %1,%0 + mov.w %1,%0 + mov.w %1,%0" + [(set_attr "type" "arith,load,load")]) (define_insn "extendqisi2" - [(set (match_operand:SI 0 "arith_reg_operand" "=r") - (sign_extend:SI (match_operand:QI 1 "arith_reg_operand" "r")))] + [(set (match_operand:SI 0 "arith_reg_operand" "=r,z,r") + (sign_extend:SI (match_operand:QI 1 "general_movsrc_operand" "r,U,m")))] "" - "exts.b %1,%0") + "@ + exts.b %1,%0 + mov.b %1,%0 !p9 + mov.b %1,%0 !p8" + [(set_attr "type" "arith,load,load")]) (define_insn "extendqihi2" - [(set (match_operand:HI 0 "arith_reg_operand" "=r") - (sign_extend:HI (match_operand:QI 1 "arith_reg_operand" "r")))] + [(set (match_operand:HI 0 "arith_reg_operand" "=r,z,r") + (sign_extend:HI (match_operand:QI 1 "general_movsrc_operand" "r,U,m")))] "" - "exts.b %1,%0") + "@ + exts.b %1,%0 + mov.b %1,%0 !p7 + mov.b %1,%0 ! p6" + [(set_attr "type" "arith,load,load")]) ;; ------------------------------------------------------------------------- ;; Move instructions ;; ------------------------------------------------------------------------- -(define_insn "" - [(set (match_operand:SI 0 "push_operand" "=<,<") - (match_operand:SI 1 "arith_reg_operand" "r,l"))] - "" - "@ - mov.l %1,%0 - sts.l pr,%0" - [(set_attr "type" "store")]) - -(define_insn "" - [(set (match_operand:SI 0 "arith_reg_operand" "=r,l") - (match_operand:SI 1 "pop_operand" "=>,>"))] - "" - "@ - mov.l %1,%0 - lds.l %1,pr" - [(set_attr "type" "load,pload")]) +;; define push and pop so it is easy for sh.c (define_insn "push" [(set (mem:SI (pre_dec:SI (reg:SI 15))) - (match_operand:SI 0 "register_operand" "r,l"))] + (match_operand:SI 0 "register_operand" "r,lx"))] "" "@ mov.l %0,@-r15 - sts.l pr,@-r15") + sts.l %0,@-r15 ! push" + [(set_attr "type" "store") + (set_attr "hit_stack" "yes")]) (define_insn "pop" - [(set (match_operand:SI 0 "register_operand" "=r,l") + [(set (match_operand:SI 0 "register_operand" "=r,lx") (mem:SI (post_inc:SI (reg:SI 15))))] "" "@ mov.l @r15+,%0 - lds.l @r15+,pr" - [(set_attr "type" "load,pload")]) - -; some constants are easier to generate with alu operations -; rather than loading from memory + lds.l @r15+,%0" + [(set_attr "type" "load,pload") + (set_attr "hit_stack" "yes")]) -(define_split - [(set (match_operand:SI 0 "register_operand" "=r") - (match_operand:SI 1 "painful_immediate_operand" "i"))] +(define_insn "" + [(set (match_operand:SI 0 "push_operand" "=<,<") + (match_operand:SI 1 "arith_reg_operand" "r,xl"))] "" - [(set (match_dup 0) (const_int 127)) - (set (match_dup 0) (plus:SI (match_dup 0) - (match_dup 2)))] - "operands[2] = GEN_INT (INTVAL(operands[1]) - 127);" ) + "@ + mov.l %1,%0 + sts.l %1,%0" + [(set_attr "type" "store")]) -(define_insn "movsi_i" - [(set (match_operand:SI 0 "general_operand" "=r,r,r,m,l,r,r,r,t,x") - (match_operand:SI 1 "general_movsrc_operand" "r,I,m,r,r,l,t,x,r,r"))] +(define_insn "" + [(set (match_operand:SI 0 "arith_reg_operand" "=r,xl") + (match_operand:SI 1 "pop_operand" "=>,>"))] "" "@ - mov %1,%0 - mov %1,%0 mov.l %1,%0 - mov.l %1,%0 - lds %1,%0 - sts %1,%0 - movt %0 - sts %1,%0 - tst %1,%1\;bt T%*\;bra F%*\;sett\;T%*:clrt\;F%*:%^ - lds %1,%0" - [(set_attr "length" "2,2,2,2,2,2,2,2,10,2") - (set_attr "type" "move,move,load,pload,move,move,move,move,move,move")]) + lds.l %1,%0" + [(set_attr "type" "load")]) -(define_insn "movsi_pcrel" - [(set (match_operand:SI 0 "arith_reg_operand" "=r") - (match_operand:SI 1 "hard_immediate_operand" "i"))] +(define_insn "clrt" + [(set (reg:SI 18) (const_int 0))] "" - "* return output_movepcrel (insn, operands, SImode);" - [(set_attr "length" "2") - (set_attr "in_delay_slot" "no") - (set_attr "constneed" "yes") - (set_attr "smallestsize" "2") - (set_attr "largestsize" "8") - (set_attr "type" "load")]) + "clrt") +(define_insn "movsi_i" + [(set (match_operand:SI 0 "general_movdst_operand" "=r,r,r,r,r,m,xl,t,r,xl,r,>,r,i"))] + "" + "* +{ + switch (which_alternative) + { + case 0: + switch (get_attr_length(insn)) + { + case 2: + return \"mov.l %1,%0 ! 2 byte\"; + case 12: + return \"mov.l TA%*,%0\;bra TB%*\;mov.l @%0,%0\;.align 2\;TA%*: .long %1\;TB%*:%^\"; + } + case 1: return \"mov %1,%0\"; + case 2: return \"mov.l %1,%0\"; + case 3: return \"sts %1,%0\"; + case 4: return \"movt %0\"; + case 5: return \"mov.l %1,%0\"; + case 6: return \"sts.l %1,%0\"; + case 7: return \"lds %1,%0\"; + case 8: return \"lds.l %1,%0\"; + case 9: return \"tst %1,%1\;bt T%*\;bra F%*\;sett\;T%*:clrt\;F%*:%^\"; + case 10: return \"fake %1,%0\"; + } +}" + [(set_attr "length" "*,2,2,2,2,2,2,2,2,6,2") + (set_attr "type" "pcloadsi,move,load,move,store,store,move,load,move,move,move")]) + (define_expand "movsi" - [(set (match_operand:SI 0 "general_operand" "") - (match_operand:SI 1 "general_operand" ""))] + [(set (match_operand:SI 0 "general_movdst_operand" "") + (match_operand:SI 1 "general_movsrc_operand" ""))] "" - "{ if(prepare_move_operands(operands, SImode)) DONE; } ") + "{ if (prepare_move_operands(operands, SImode)) DONE; } ") (define_insn "movqi_i" - [(set (match_operand:QI 0 "general_operand" "=r,r,r,m,r,m,r,r") - (match_operand:QI 1 "general_operand" "r,n,m,r,m,r,x,t"))] - "" + [(set (match_operand:QI 0 "general_movdst_operand" "=r,r,>m,r,r,l") + (match_operand:QI 1 "general_movsrc_operand" "ri,m,t,r,i,l,r"))] "" - "@ - mov %1,%0 - mov.w %1,%0 - mov.w %1,%0 - mov.w %1,%0 - mov.w %1,%0 - sts %1,%0 - movt %0" - [(set_attr "type" "move,load,store,load,store,move,move")]) + "* +{ + switch (which_alternative) + { + case 0: + switch (get_attr_length(insn)) + { + case 2: + return \"mov.w %1,%0 ! 2 byte\"; + case 12: + return \"mov.l TA%*,%0\;bra TB%*\;mov.w @%0,%0\;.align 2\;TA%*: .long %1\;TB%*:%^\"; + } + case 1: return \"mov %1,%0\"; + case 2: return \"mov.w %1,%0\"; + case 3: return \"movt %0\"; + case 4: return \"mov.w %1,%0\"; + case 5: return \"fake %1,%0\"; + case 6: return \"sts %1,%0\"; + case 7: return \"lds %1,%0\"; + } +}" + [(set_attr "length" "*,2,2,2,2,2,2,2") + (set_attr "type" "pcloadhi,move,load,move,store,move,move,move")]) (define_expand "movhi" - [(set (match_operand:HI 0 "general_operand" "") - (match_operand:HI 1 "general_operand" ""))] + [(set (match_operand:HI 0 "general_movdst_operand" "") + (match_operand:HI 1 "general_movsrc_operand" ""))] "" - "prepare_move_operands (operands, HImode);") - + "if (prepare_move_operands (operands, HImode)) DONE;") + (define_insn "" [(set (match_operand:DI 0 "push_operand" "=<") (match_operand:DI 1 "arith_reg_operand" "r"))] @@ -718,68 +1065,59 @@ [(set_attr "length" "4") (set_attr "type" "store")]) -(define_insn "movdi_pcrel" - [(set (match_operand:DI 0 "arith_reg_operand" "=r") - (match_operand:DI 1 "hard_immediate_operand" "i"))] - "" - "* return output_movepcrel (insn, operands, DImode);" - [(set_attr "length" "4") - (set_attr "in_delay_slot" "no") - (set_attr "constneed" "yes") - (set_attr "smallestsize" "4") - (set_attr "constantsize" "8") - (set_attr "largestsize" "18") - (set_attr "type" "load")]) - -(define_insn "movdi_k" - [(set (match_operand:DI 0 "general_operand" "=r,r,m,r,r,m,r") - (match_operand:DI 1 "general_operand" "r,m,r,I,m,r,x"))] +(define_split + [(set (match_operand:DI 0 "general_movdst_operand" "") + (match_operand:DI 1 "nonimmediate_operand" ""))] "" - "* return output_movedouble(operands, DImode);" - [(set_attr "length" "4") - (set_attr "type" "move,load,store,move,load,store,load")]) + [(set (match_dup 2) (match_dup 3)) + (set (match_dup 4) (match_dup 5))] + "if (prepare_split_double_ops (operands, DImode)) DONE; else FAIL;") +(define_insn "" + [(set (match_operand:DI 0 "general_movdst_operand" "=r,r,r,m,r") + (match_operand:DI 1 "general_movsrc_operand" "Q,r,m,r,i"))] + "register_operand (operands[0], DImode) + || register_operand (operands[1], DImode)" + "* return output_movedouble (insn, operands, DImode);" + [(set_attr "length" "*,4,4,4,4") + (set_attr "type" "pcloadsi,move,load,store,move")]) (define_expand "movdi" - [(set (match_operand:DI 0 "general_operand" "") - (match_operand:DI 1 "general_operand" ""))] + [(set (match_operand:DI 0 "general_movdst_operand" "=r,m,r") + (match_operand:DI 1 "general_movsrc_operand" "m,r,i"))] "" - "prepare_move_operands (operands, DImode);") + "if (prepare_move_operands(operands, DImode)) DONE;") (define_insn "" [(set (match_operand:DF 0 "push_operand" "=<") (match_operand:DF 1 "arith_reg_operand" "r"))] "" - "mov.l %R1,%0\;mov.l %1,%0" + "mov.l %R1,%0\;mov.l %1,%0" [(set_attr "length" "4") (set_attr "type" "store")]) -(define_insn "movdf_pcrel" - [(set (match_operand:DF 0 "arith_reg_operand" "=r") - (match_operand:DF 1 "hard_immediate_operand" "i"))] +(define_split + [(set (match_operand:DF 0 "general_movdst_operand" "") + (match_operand:DF 1 "nonimmediate_operand" ""))] "" - "* return output_movepcrel (insn, operands, DFmode);" - [(set_attr "length" "4") - (set_attr "in_delay_slot" "no") - (set_attr "constneed" "yes") - (set_attr "smallestsize" "4") - (set_attr "constantsize" "8") - (set_attr "largestsize" "18") - (set_attr "type" "load")]) + [(set (match_dup 2) (match_dup 3)) + (set (match_dup 4) (match_dup 5))] + "if (prepare_split_double_ops (operands, DFmode)) DONE; else FAIL;") (define_insn "movdf_k" - [(set (match_operand:DF 0 "general_operand" "=r,r,m") - (match_operand:DF 1 "general_operand" "r,m,r"))] - "" - "* return output_movedouble(operands, DFmode);" + [(set (match_operand:DF 0 "general_movdst_operand" "=r,r,m") + (match_operand:DF 1 "general_movsrc_operand" "r,m,r"))] + "register_operand (operands[0], DFmode) + || register_operand (operands[1], DFmode)" + "* return output_movedouble (insn, operands, DFmode);" [(set_attr "length" "4") (set_attr "type" "move,load,store")]) (define_expand "movdf" - [(set (match_operand:DF 0 "general_operand" "") - (match_operand:DF 1 "general_operand" ""))] + [(set (match_operand:DF 0 "general_movdst_operand" "") + (match_operand:DF 1 "general_movsrc_operand" ""))] "" - "prepare_move_operands(operands, DFmode);") + "if (prepare_move_operands (operands, DFmode)) DONE;") (define_insn "" [(set (match_operand:SF 0 "push_operand" "=<") @@ -787,39 +1125,28 @@ "" "mov.l %1,%0" [(set_attr "type" "store")]) - -(define_insn "movsf_pcrel" - [(set (match_operand:SF 0 "arith_reg_operand" "=r") - (match_operand:SF 1 "hard_immediate_operand" "i"))] - "" - "* return output_movepcrel (insn, operands, SFmode);" - [(set_attr "length" "2") - (set_attr "in_delay_slot" "no") - (set_attr "constneed" "yes") - (set_attr "smallestsize" "2") - (set_attr "largestsize" "8") - (set_attr "type" "load")]) (define_insn "movsf_i" - [(set (match_operand:SF 0 "general_operand" "=r,r,r,m,l,r,m,r") - (match_operand:SF 1 "general_operand" "r,I,m,r,r,l,r,m"))] + [(set (match_operand:SF 0 "general_movdst_operand" "=>,r,r,r,r,m,l,r") + (match_operand:SF 1 "general_movsrc_operand" "r,<,r,I,m,r,r,l"))] "" "@ + mov.l %1,@%N0\;add #4,%N0 + add #-4,%1\;mov.l @%N1,%0 mov %1,%0 mov %1,%0 mov.l %1,%0 mov.l %1,%0 lds %1,%0 - sts %1,%0 - mov %1,%0 - mov %1,%0" - [(set_attr "type" "move,move,load,store,move,move,move,move")]) + sts %1,%0" + [(set_attr "type" "store,load,move,move,load,store,move,move") + (set_attr "length" "4,4,*,*,*,*,*,*")]) (define_expand "movsf" - [(set (match_operand:SF 0 "general_operand" "") - (match_operand:SF 1 "general_operand" ""))] + [(set (match_operand:SF 0 "general_movdst_operand" "") + (match_operand:SF 1 "general_movsrc_operand" ""))] "" - "prepare_move_operands(operands, SFmode);") + "if (prepare_move_operands (operands, SFmode)) DONE;") ;; ------------------------------------------------------------------------ @@ -839,7 +1166,7 @@ (label_ref (match_operand 0 "" "")) (pc)))] "" - "* return output_branch (0, insn);" + "* return output_branch (0, insn, operands[1]);" [(set_attr "type" "cbranch")]) (define_insn "inverse_branch_true" @@ -869,12 +1196,7 @@ (label_ref (match_operand 0 "" "")) (pc)))] "" - " -{ - operands[1] = sh_compare_op0; - operands[2] = sh_compare_op1; -}") - + "from_compare (operands, EQ);") ; There is no bne compare, so we reverse the branch arms. @@ -886,11 +1208,7 @@ (pc) (label_ref (match_operand 0 "" ""))))] "" - " -{ - operands[1] = sh_compare_op0; - operands[2] = sh_compare_op1; -}") + "from_compare (operands, NE);") (define_expand "bgt" [(set (reg:SI 18) (gt:SI (match_dup 1) (match_dup 2))) @@ -898,41 +1216,29 @@ (if_then_else (eq (reg:SI 18) (const_int 1)) (label_ref (match_operand 0 "" "")) - (pc)))] + (pc))) ] "" - " -{ - operands[1] = sh_compare_op0; - operands[2] = sh_compare_op1; -}") + "from_compare (operands, GT);") (define_expand "blt" - [(set (reg:SI 18) (lt:SI (match_dup 1) (match_dup 2))) + [(set (reg:SI 18) (ge:SI (match_dup 1) (match_dup 2))) (set (pc) - (if_then_else (eq (reg:SI 18) - (const_int 1)) - (label_ref (match_operand 0 "" "")) - (pc)))] + (if_then_else (eq (reg:SI 18)(const_int 1)) + (pc) + (label_ref (match_operand 0 "" ""))))] "" - " -{ - operands[1] = sh_compare_op0; - operands[2] = sh_compare_op1; -}") + "from_compare (operands, LT);") (define_expand "ble" - [(set (reg:SI 18) (le:SI (match_dup 1) (match_dup 2))) - (set (pc) + [(set (reg:SI 18) (gt:SI (match_dup 1) (match_dup 2))) + (set (pc) (if_then_else (eq (reg:SI 18) (const_int 1)) - (label_ref (match_operand 0 "" "")) - (pc)))] + (pc) + (label_ref (match_operand 0 "" "")))) + ] "" - " -{ - operands[1] = sh_compare_op0; - operands[2] = sh_compare_op1; -}") + "from_compare (operands, LE);") (define_expand "bge" [(set (reg:SI 18) (ge:SI (match_dup 1) (match_dup 2))) @@ -940,84 +1246,65 @@ (if_then_else (eq (reg:SI 18) (const_int 1)) (label_ref (match_operand 0 "" "")) - (pc)))] + (pc))) ] "" - " -{ - operands[1] = sh_compare_op0; - operands[2] = sh_compare_op1; -}") + "from_compare (operands, GE);") (define_expand "bgtu" [(set (reg:SI 18) (gtu:SI (match_dup 1) (match_dup 2))) (set (pc) (if_then_else (eq (reg:SI 18) - (const_int 1)) + (const_int 1)) (label_ref (match_operand 0 "" "")) (pc)))] "" - " -{ - operands[1] = sh_compare_op0; - operands[2] = sh_compare_op1; -}") + "from_compare (operands, GTU); ") (define_expand "bltu" - [(set (reg:SI 18) (ltu:SI (match_dup 1) (match_dup 2))) + [(set (reg:SI 18) (geu:SI (match_dup 1) (match_dup 2))) (set (pc) - (if_then_else (eq (reg:SI 18) - (const_int 1)) - (label_ref (match_operand 0 "" "")) - (pc)))] + (if_then_else (eq (reg:SI 18) + (const_int 1)) + (pc) + (label_ref (match_operand 0 "" ""))))] "" - " -{ - operands[1] = sh_compare_op0; - operands[2] = sh_compare_op1; -}") + "from_compare (operands, LTU);") (define_expand "bgeu" [(set (reg:SI 18) (geu:SI (match_dup 1) (match_dup 2))) (set (pc) (if_then_else (eq (reg:SI 18) - (const_int 1)) + (const_int 1)) (label_ref (match_operand 0 "" "")) - (pc)))] + (pc))) + ] "" - " -{ - operands[1] = sh_compare_op0; - operands[2] = sh_compare_op1; -}") + "from_compare (operands, GEU);") (define_expand "bleu" - [(set (reg:SI 18) (leu:SI (match_dup 1) (match_dup 2))) + [(set (reg:SI 18) (gtu:SI (match_dup 1) (match_dup 2))) (set (pc) (if_then_else (eq (reg:SI 18) - (const_int 1)) - (label_ref (match_operand 0 "" "")) - (pc)))] + (const_int 1)) + (pc) + (label_ref (match_operand 0 "" ""))))] "" - " -{ - operands[1] = sh_compare_op0; - operands[2] = sh_compare_op1; -}") + "from_compare (operands, LEU);") ;; ------------------------------------------------------------------------ ;; Jump and linkage insns ;; ------------------------------------------------------------------------ -(define_insn "jump_real" +(define_insn "jump" [(set (pc) (label_ref (match_operand 0 "" "")))] "" "* { - if (get_attr_length(insn) == 6) + if (get_attr_length(insn) == 10) { - return \"mov.l %I0,r13\;jmp @r13%#\"; + return output_far_jump(insn, operands[0]); } else { @@ -1027,15 +1314,6 @@ [(set_attr "type" "jump") (set_attr "needs_delay_slot" "yes")]) -(define_expand "jump" - [(set (pc) (label_ref (match_operand 0 "" "")))] - "" - " -{ - emit_insn(gen_jump_real(operand0)); - DONE; -} -") (define_insn "calli" [(call (mem:SI (match_operand:SI 0 "arith_reg_operand" "r")) @@ -1063,16 +1341,7 @@ (match_operand 1 "" "")) (clobber (reg:SI 17))])] "" - " -{ - if (GET_CODE (operands[0]) == MEM) - { - operands[0] - = gen_rtx(MEM,GET_MODE (operands[0]), - force_reg (Pmode, - XEXP (operands[0], 0))); - } -}") + "expand_acall(0, operands); DONE;") (define_expand "call_value" [(parallel[(set (match_operand 0 "" "=rf") @@ -1080,16 +1349,7 @@ (match_operand 2 "" ""))) (clobber (reg:SI 17))])] "" - " -{ - if (GET_CODE (operands[1]) == MEM) - { - operands[1] - = gen_rtx (MEM, GET_MODE (operands[1]), - force_reg (Pmode, - XEXP (operands[1], 0))); - } -}") + "expand_acall(1, operands); DONE; ") (define_insn "indirect_jump" [(set (pc) @@ -1100,36 +1360,107 @@ (set_attr "in_delay_slot" "no") (set_attr "length" "4")]) - ;; ------------------------------------------------------------------------ ;; Misc insns ;; ------------------------------------------------------------------------ +(define_insn "dect" + [(parallel[ + (set (reg:SI 18) + (eq:SI (match_operand:SI 0 "register_operand" "=r") + (const_int 1))) + + (set (match_dup 0) + (plus:SI (match_dup 0) + (const_int -1)))])] + "TARGET_SH2" + "dt %0") (define_insn "nop" [(const_int 0)] "" "or r0,r0") -(define_insn "tablejump" - [(set (pc) - (match_operand:SI 0 "arith_reg_operand" "r")) - (use (label_ref (match_operand 1 "" "")))] +; experimental use of auto inc and dec made these... +; can be deleted + +(define_insn "fake" + [(set (match_operand:QI 0 "register_operand" "=r") + (mem:QI (pre_dec:SI (match_operand:SI 1 "register_operand" "r"))))] "" - "!table jump\;jmp @%0\;or r0,r0\;%!" + "add #-1,%1\;mov.b @%1,%0" + [(set_attr "length" "4")]) + +;; Load address of a label. This is only generated by the casesi expand. + +(define_insn "mova" + [(set (reg:SI 0) + (label_ref (match_operand 0 "" "")))] + "" + "mova %O0,r0" + [(set_attr "in_delay_slot" "no")]) + +;; case instruction for switch statements. + +;; Operand 0 is index +;; operand 1 is the minimum bound +;; operand 2 is the maximum bound - minimum bound + 1 +;; operand 3 is CODE_LABEL for the table; +;; operand 4 is the CODE_LABEL to go to if index out of range. + +(define_expand "casesi" + [(set (match_dup 5) (match_operand:SI 0 "arith_reg_operand" "")) + (set (match_dup 5) (minus:SI (match_dup 5) + (match_operand:SI 1 "arith_operand" ""))) + (set (reg:SI 18) + (gtu:SI (match_dup 5) + (match_operand:SI 2 "arith_operand" ""))) + (set (pc) + (if_then_else (eq (reg:SI 18) + (const_int 1)) + (label_ref (match_operand 4 "" "")) + (pc))) + (set (match_dup 6) (plus:SI (match_dup 5) (match_dup 5))) + (set (reg:SI 0) (label_ref (match_operand 3 "" ""))) + (parallel[(set (reg:SI 0) (plus:SI (reg:SI 0) + (mem:HI (plus:SI (reg:SI 0) + (match_dup 6))))) + (set (match_dup 6) (mem:HI (plus:SI (reg:SI 0) (match_dup 6))))]) + (set (pc) (reg:SI 0))] + "" + " +{ + operands[1] = copy_to_mode_reg (SImode, operands[1]); + operands[2] = copy_to_mode_reg (SImode, operands[2]); + operands[5] = gen_reg_rtx (SImode); + operands[6] = gen_reg_rtx (SImode); +}") + +(define_insn "casesi_worker" + [(set (reg:SI 0) + (plus:SI (reg:SI 0) + (mem:HI (plus:SI (reg:SI 0) + (match_operand:SI 0 "register_operand" "=r"))))) + (set (match_dup 0) (mem:HI (plus:SI (reg:SI 0) + (match_dup 0))))] + "" + "mov.w @(r0,%0),%0\;add %0,r0" [(set_attr "needs_delay_slot" "no") (set_attr "in_delay_slot" "no") - (set_attr "type" "jump") - (set_attr "dump" "no")]) + (set_attr "length" "6")]) -(define_insn "return" +(define_insn "" [(return)] - "reload_completed" - "rts %#" - [(set_attr "type" "return") - (set_attr "needs_delay_slot" "yes") - (set_attr "dump" "yes")]) + "!pragma_interrupt && reload_completed" + "rts%#" + [(set_attr "type" "return")]) + +(define_insn "" + [(return)] + "pragma_interrupt && reload_completed" + "rte%#" + [(set_attr "type" "rte")]) (define_expand "prologue" [(const_int 0)] @@ -1152,7 +1483,7 @@ ;; Scc instructions ;; ------------------------------------------------------------------------ -(define_insn "" +(define_insn "movt" [(set (match_operand:SI 0 "arith_reg_operand" "=r") (eq (reg:SI 18) (const_int 1)))] "" @@ -1219,59 +1550,6 @@ "" "operands[1] = prepare_scc_operands (EQ);") -; these patterns give better code then gcc invents if -; left to its own devices - -(define_insn "anddi3" - [(set (match_operand:DI 0 "arith_reg_operand" "=r") - (and:DI (match_operand:DI 1 "arith_reg_operand" "%0") - (match_operand:DI 2 "arith_reg_operand" "r")))] - "" - "and %2,%0\;and %R2,%R0" - [(set_attr "length" "4")]) - -(define_insn "iordi3" - [(set (match_operand:DI 0 "arith_reg_operand" "=r") - (ior:DI (match_operand:DI 1 "arith_reg_operand" "%0") - (match_operand:DI 2 "arith_reg_operand" "r")))] - "" - "or %2,%0\;or %R2,%R0" - [(set_attr "length" "4")]) - -(define_insn "xordi3" - [(set (match_operand:DI 0 "arith_reg_operand" "=r") - (xor:DI (match_operand:DI 1 "arith_reg_operand" "%0") - (match_operand:DI 2 "arith_reg_operand" "r")))] - "" - "xor %2,%0\;xor %R2,%R0" - [(set_attr "length" "4")]) - - -;; ------------------------------------------------------------------------ -;; Block move -;; ------------------------------------------------------------------------ - -(define_expand "movstrsi" - [(parallel [(set (mem:BLK (match_operand:BLK 0 "general_operand" "")) - (mem:BLK (match_operand:BLK 1 "general_operand" ""))) - (use (match_operand:SI 2 "general_operand" "")) - (use (match_operand:SI 3 "immediate_operand" "")) - ])] - "" - " -{ - rtx dest_mem = operands[0]; - rtx src_mem = operands[1]; - operands[0] = copy_to_mode_reg (SImode, XEXP (operands[0], 0)); - operands[1] = copy_to_mode_reg (SImode, XEXP (operands[1], 0)); - expand_block_move (dest_mem, src_mem, operands); - DONE; -}") - - - - - ;; ------------------------------------------------------------------------- ;; Peepholes ;; ------------------------------------------------------------------------- @@ -1281,39 +1559,39 @@ [(set (match_operand:QI 0 "arith_reg_operand" "") (mem:QI (match_operand:SI 1 "arith_reg_operand" ""))) (set (match_dup 1) (plus:SI (match_dup 1) (const_int 1)))] - "REGNO(operands[1]) != REGNO(operands[0])" + "REGNO (operands[1]) != REGNO (operands[0])" "mov.b @%1+,%0") (define_peephole [(set (match_operand:HI 0 "arith_reg_operand" "") (mem:HI (match_operand:SI 1 "arith_reg_operand" ""))) (set (match_dup 1) (plus:SI (match_dup 1) (const_int 2)))] - "REGNO(operands[1]) != REGNO(operands[0])" + "REGNO (operands[1]) != REGNO (operands[0])" "mov.w @%1+,%0") (define_peephole [(set (match_operand:SI 0 "arith_reg_operand" "") (mem:SI (match_operand:SI 1 "arith_reg_operand" ""))) (set (match_dup 1) (plus:SI (match_dup 1) (const_int 4)))] - "REGNO(operands[1]) != REGNO(operands[0])" + "REGNO (operands[1]) != REGNO (operands[0])" "mov.l @%1+,%0") (define_peephole [(set (match_operand:QI 0 "register_operand" "=r") - (match_operand:QI 1 "general_operand" "g")) + (match_operand:QI 1 "memory_operand" "g")) (set (match_operand:SI 2 "register_operand" "=r") (sign_extend:SI (match_dup 0)))] - "REGNO(operands[0]) == REGNO(operands[2])" - "mov.b %1,%0") + "REGNO (operands[0]) == REGNO (operands[2])" + "mov.b %1,%0 !p 5") (define_peephole [(set (match_operand:QI 0 "register_operand" "=r") - (match_operand:QI 1 "general_operand" "g")) + (match_operand:QI 1 "general_movsrc_operand" "g")) (set (match_operand:SI 2 "register_operand" "=r") (sign_extend:SI (match_dup 0)))] - "REGNO(operands[0]) != REGNO(operands[2]) + "REGNO (operands[0]) != REGNO (operands[2]) && dead_or_set_p (insn, operands[0])" - "mov.b %1,%2") + "mov.b %1,%2 ! p4") ; notice when a mov.b could be done with a displacement @@ -1322,7 +1600,7 @@ (plus:SI (match_dup 0) (match_operand:SI 1 "byte_index_operand" "i"))) (set (mem:QI (match_dup 0)) (reg:QI 0))] - "dead_or_set_p(insn, operands[0])" + "dead_or_set_p (insn, operands[0])" "mov.b r0,@(%O1,%0)") (define_peephole @@ -1330,9 +1608,294 @@ (plus:SI (match_dup 0) (match_operand:SI 1 "byte_index_operand" "i"))) (set (reg:QI 0) (mem:QI (match_dup 0)))] - "dead_or_set_p(insn, operands[0])" + "dead_or_set_p (insn, operands[0])" "mov.b @(%O1,%0),r0") +;; ------------------------------------------------------------------------- +;; Peepholes +;; ------------------------------------------------------------------------- + +(define_peephole + [(set (reg:SI 0) (label_ref (match_operand 0 "" ""))) + (set (match_operand:SI 1 "register_operand" "=r") + (reg:SI 0)) + (set (reg:SI 0) (label_ref (match_dup 0))) + (set (match_operand:SI 2 "register_operand" "=r") + (reg:SI 0))] + "" + "mova %O0,r0\;mov r0,%1\;mov r0,%2" + [(set_attr "length" "6") + (set_attr "in_delay_slot" "no")]) + + +; mov r4,r3 +; shll r3 +; mov r3,r0 +;-> +; mov r4,r0 +; shll r0 + +(define_peephole + [(set (match_operand:SI 0 "register_operand" "=r") + (match_operand:SI 1 "register_operand" "r")) + (set (match_dup 0) + (plus:SI (match_dup 0) (match_dup 1))) + (set (match_operand:SI 2 "register_operand" "=r") + (match_dup 0))] + "dead_or_set_p (NEXT_INSN (insn), operands[0])" + "mov %1,%2\;add %2,%2" + [(set_attr "length" "4")]) + +(define_peephole + [(set (match_operand:SI 0 "arith_reg_operand" "=r") + (and:SI (match_dup 0) + (const_int 1))) + (set (match_operand:SI 1 "arith_reg_operand" "=r") + (const_int 0)) + (set (reg:SI 18) + (eq:SI (match_dup 0) (match_dup 1)))] + "dead_or_set_p (insn, operands[0]) + && dead_or_set_p (insn, operands[1])" + "rotr %0") + +(define_peephole + [(set (match_operand:SI 0 "arith_reg_operand" "z,r") + (and:SI (match_dup 0) + (match_operand:SI 1 "arith_operand" "L,r"))) + (set (reg:SI 18) + (eq:SI (match_dup 0) (const_int 0)))] + "dead_or_set_p (insn, operands[0])" + "tst %1,%0 !t5") + +(define_peephole + [(set (match_operand:SI 0 "arith_reg_operand" "z,r") + (and:SI (match_dup 0) + (match_operand:SI 1 "arith_operand" "L,r"))) + (set (reg:SI 18) + (eq:SI (match_dup 0) (const_int 0)))] + "dead_or_set_p (insn, operands[0])" + "tst %1,%0 !t4") + +;; ------------------------------------------------------------------------- +;; Combine patterns +;; ------------------------------------------------------------------------- + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (match_operand:HI 1 "register_operand" "%0") + (match_operand:SI 2 "register_operand" "r")))] + "" + "add %2,%0 ! why") + +(define_insn "addc_2" + [(set (match_operand:SI 0 "arith_reg_operand" "=&r") + (plus:SI (reg:SI 18) + (match_operand:SI 1 "arith_reg_operand" "r"))) + (clobber (reg:SI 18))] + "" + "mov #0,%0\;addc %1,%0 ! addc1" + [(set_attr "length" "4")]) + +(define_insn "combine_1" + [(set (match_operand:SI 0 "arith_reg_operand" "=r") + (sign_extend:SI (mem:QI (match_operand:SI 1 "arith_reg_operand" "r"))))] + "" + "mov.b @%1,%0 ! why" + [(set_attr "type" "load")]) + +(define_insn "combine_2" + [(set (reg:SI 18) + (eq (and:SI (match_operand:SI 0 "arith_reg_operand" "z,r") + (match_operand:SI 1 "arith_operand" "L,r")) + (const_int 0)))] + "" + "tst %1,%0 !t2") + +(define_split + [(set (pc) + (if_then_else + (match_operator 2 "equality_operator" [(match_operand:SI 0 "arith_reg_operand" "r") + (const_int 0)]) + (label_ref (match_operand 1 "" "")) + (pc))) + (clobber (reg:SI 18))] + "" + [(set (reg:SI 18) (eq (and:SI (match_dup 0) (match_dup 0)) + (const_int 0))) + (set (pc) + (if_then_else (match_op_dup 2 [(reg:SI 18) (const_int 1)]) + (label_ref (match_dup 1)) + (pc)))] + "") + +;; ------------------------------------------------------------------------- +;; Instructions to cope with inline literal tables +;; ------------------------------------------------------------------------- + +; 2 byte integer in line +(define_insn "consttable_2" + [(unspec_volatile [(match_operand:SI 0 "general_operand" "=g")] 2)] + "" + "* +{ + assemble_integer (operands[0], 2, 1); + return \"\"; +}" + [(set_attr "length" "2") + (set_attr "in_delay_slot" "no")]) + +; 4 byte integer in line + +(define_insn "consttable_4" + [(unspec_volatile [(match_operand:SI 0 "general_operand" "=g")] 4)] + "" + "* +{ + assemble_integer (operands[0], 4, 1); + return \"\"; +}" + [(set_attr "length" "4") + (set_attr "in_delay_slot" "no")]) + +; 8 byte integer in line + +(define_insn "consttable_8" + [(unspec_volatile [(match_operand:SI 0 "general_operand" "=g")] 6)] + "" + "* +{ + assemble_integer (operands[0], 8, 1); + return \"\"; +}" + [(set_attr "length" "8") + (set_attr "in_delay_slot" "no")]) + +; align to a two byte boundary + +(define_insn "align_2" + [(unspec_volatile [(const_int 0)] 10)] + "" + ".align 1" + [(set_attr "length" "0") + (set_attr "in_delay_slot" "no")]) + +; align to a four byte boundary + +(define_insn "align_4" + [(unspec_volatile [(const_int 0)] 5)] + "" + ".align 2" + [(set_attr "in_delay_slot" "no")]) + +; emitted at the end of the literal table, used to emit the +; 32bit branch labels if needed. + +(define_insn "consttable_end" + [(unspec_volatile [(const_int 0)] 11)] + "" + "* return output_jump_label_table ();" + [(set_attr "in_delay_slot" "no")]) + + +;(define_split +; [(set (subreg:SI (match_operand:QI 0 "register_operand" "=r") 0) +; (plus:SI (subreg:SI (match_operand:QI 1 "general_operand" "g") 0) +; (subreg:SI (match_operand:QI 2 "general_operand" "g") 0)))] +; "" +; [(set (match_dup 3) (plus:SI (match_dup 1) (match_dup 2))) +; (set (match_dup 1) (subreg:SI (match_dup 3) 0))] +; "operands[3] = gen_reg_rtx(SImode);") + + +; byte arithmetic involving constants which need to be sign extended can be +; fixed up... + + +(define_split + [(set (subreg:SI (match_operand:QI 0 "register_operand" "=r") 0) + (plus:SI (subreg:SI (match_operand:QI 1 "register_operand" "0") 0) + (subreg:SI (match_operand 2 "immediate_operand" "n") 0)))] + "" + [(set (match_dup 4) (plus:SI (match_dup 2) (match_dup 3))) + (set (match_dup 0) (and:SI (match_dup 0) (const_int 255)))] + "{ int i = INTVAL(operands[2]) & 0xff; + if (i > 127) i = i - 256; + operands[3] = GEN_INT(i); + operands[4] = gen_reg_rtx(SImode);} ") + + +(define_insn "movsi_k" + [(set (match_operand:SI 0 "register_operand" "=r") + (match_operand:SI 1 "immediate_operand" ""))] + "!pnum_clobbers" + "! this is a fake") + + +(define_insn "movhi_k" + [(set (match_operand:HI 0 "register_operand" "=r") + (match_operand:HI 1 "immediate_operand" ""))] + "!pnum_clobbers" + "! this is a fake") + +(define_insn "movdi_k" + [(set (match_operand:DI 0 "register_operand" "=r") + (match_operand:DI 1 "immediate_operand" ""))] + "!pnum_clobbers" + "! this is a fake") + +;; ------------------------------------------------------------------------- +;; Misc +;; ------------------------------------------------------------------------- + + +;; String/block move insn. + +(define_expand "movstrsi" + [(parallel [(set (mem:BLK (match_operand:BLK 0 "" "")) + (mem:BLK (match_operand:BLK 1 "" ""))) + (use (match_operand:SI 2 "nonmemory_operand" "")) + (use (match_operand:SI 3 "immediate_operand" "")) + (clobber (reg:SI 17)) + (clobber (reg:SI 4)) + (clobber (reg:SI 5)) + (clobber (reg:SI 0))])] + "" + " +{ + if(expand_block_move (operands)) + DONE; + else FAIL; +}") + +(define_insn "block_move_real" + [(parallel [(set (mem:BLK (reg:SI 4)) + (mem:BLK (reg:SI 5))) + (use (match_operand:SI 0 "arith_reg_operand" "r")) + (clobber (reg:SI 17)) + (clobber (reg:SI 4)) + (clobber (reg:SI 5)) + (clobber (reg:SI 0))])] + "" + "jsr @%0%#" + [(set_attr "length" "4") + (set_attr "type" "sfunc") + (set_attr "needs_delay_slot" "yes")]) + +(define_insn "block_lump_real" + [(parallel [(set (mem:BLK (reg:SI 4)) + (mem:BLK (reg:SI 5))) + (use (match_operand:SI 0 "arith_reg_operand" "r")) + (use (reg:SI 6)) + (clobber (reg:SI 17)) + (clobber (reg:SI 4)) + (clobber (reg:SI 5)) + (clobber (reg:SI 6)) + (clobber (reg:SI 0))])] + "" + "jsr @%0%#" + [(set_attr "length" "4") + (set_attr "type" "sfunc") + (set_attr "needs_delay_slot" "yes")]) -- 2.30.2