diff options
Diffstat (limited to 'gcc/config/rl78/rl78.c')
-rw-r--r-- | gcc/config/rl78/rl78.c | 1280 |
1 files changed, 1042 insertions, 238 deletions
diff --git a/gcc/config/rl78/rl78.c b/gcc/config/rl78/rl78.c index d7cacc16352..cd9010ab190 100644 --- a/gcc/config/rl78/rl78.c +++ b/gcc/config/rl78/rl78.c @@ -50,6 +50,8 @@ #include "dumpfile.h" #include "tree-pass.h" #include "context.h" +#include "tm-constrs.h" /* for satisfies_constraint_*(). */ +#include "insn-flags.h" /* for gen_*(). */ static inline bool is_interrupt_func (const_tree decl); static inline bool is_brk_interrupt_func (const_tree decl); @@ -168,6 +170,86 @@ make_pass_rl78_devirt (gcc::context *ctxt) return new pass_rl78_devirt (ctxt); } +static unsigned int +move_elim_pass (void) +{ + rtx insn, ninsn, prev = NULL_RTX; + + for (insn = get_insns (); insn; insn = ninsn) + { + rtx set; + + ninsn = next_nonnote_nondebug_insn (insn); + + if ((set = single_set (insn)) == NULL_RTX) + { + prev = NULL_RTX; + continue; + } + + /* If we have two SET insns in a row (without anything + between them) and the source of the second one is the + destination of the first one, and vice versa, then we + can eliminate the second SET. */ + if (prev + && rtx_equal_p (SET_DEST (prev), SET_SRC (set)) + && rtx_equal_p (SET_DEST (set), SET_SRC (prev)) + ) + { + if (dump_file) + fprintf (dump_file, " Delete insn %d because it is redundant\n", + INSN_UID (insn)); + + delete_insn (insn); + prev = NULL_RTX; + } + else + prev = set; + } + + if (dump_file) + print_rtl_with_bb (dump_file, get_insns (), 0); + + return 0; +} + +namespace { + +const pass_data pass_data_rl78_move_elim = +{ + RTL_PASS, /* type */ + "move_elim", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + true, /* has_gate */ + true, /* has_execute */ + TV_MACH_DEP, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ +}; + +class pass_rl78_move_elim : public rtl_opt_pass +{ +public: + pass_rl78_move_elim(gcc::context *ctxt) + : rtl_opt_pass(pass_data_rl78_move_elim, ctxt) + { + } + + /* opt_pass methods: */ + bool gate () { return devirt_gate (); } + unsigned int execute () { return move_elim_pass (); } +}; + +} // anon namespace + +rtl_opt_pass * +make_pass_rl78_move_elim (gcc::context *ctxt) +{ + return new pass_rl78_move_elim (ctxt); +} #undef TARGET_ASM_FILE_START #define TARGET_ASM_FILE_START rl78_asm_file_start @@ -184,15 +266,25 @@ rl78_asm_file_start (void) } opt_pass *rl78_devirt_pass = make_pass_rl78_devirt (g); - struct register_pass_info rl78_devirt_info = + static struct register_pass_info rl78_devirt_info = { rl78_devirt_pass, - "vartrack", + "pro_and_epilogue", 1, PASS_POS_INSERT_BEFORE }; + opt_pass *rl78_move_elim_pass = make_pass_rl78_move_elim (g); + static struct register_pass_info rl78_move_elim_info = + { + rl78_move_elim_pass, + "bbro", + 1, + PASS_POS_INSERT_AFTER + }; + register_pass (& rl78_devirt_info); + register_pass (& rl78_move_elim_info); } @@ -332,6 +424,46 @@ rl78_expand_movsi (rtx *operands) } } +void +rl78_split_movsi (rtx *operands) +{ + rtx op00, op02, op10, op12; + + op00 = rl78_subreg (HImode, operands[0], SImode, 0); + op02 = rl78_subreg (HImode, operands[0], SImode, 2); + if (GET_CODE (operands[1]) == CONST + || GET_CODE (operands[1]) == SYMBOL_REF) + { + op10 = gen_rtx_ZERO_EXTRACT (HImode, operands[1], GEN_INT (16), GEN_INT (0)); + op10 = gen_rtx_CONST (HImode, op10); + op12 = gen_rtx_ZERO_EXTRACT (HImode, operands[1], GEN_INT (16), GEN_INT (16)); + op12 = gen_rtx_CONST (HImode, op12); + } + else + { + op10 = rl78_subreg (HImode, operands[1], SImode, 0); + op12 = rl78_subreg (HImode, operands[1], SImode, 2); + } + + if (rtx_equal_p (operands[0], operands[1])) + ; + else if (rtx_equal_p (op00, op12)) + { + operands[2] = op02; + operands[4] = op12; + operands[3] = op00; + operands[5] = op10; + } + else + { + operands[2] = op00; + operands[4] = op10; + operands[3] = op02; + operands[5] = op12; + } +} + + /* Used by various two-operand expanders which cannot accept all operands in the "far" namespace. Force some such operands into registers so that each pattern has at most one far operand. */ @@ -646,11 +778,11 @@ rl78_hl_b_c_addr_p (rtx op) int rl78_far_p (rtx x) { - if (GET_CODE (x) != MEM) + if (! MEM_P (x)) return 0; #if DEBUG0 - fprintf(stderr, "\033[35mrl78_far_p: "); debug_rtx(x); - fprintf(stderr, " = %d\033[0m\n", MEM_ADDR_SPACE (x) == ADDR_SPACE_FAR); + fprintf (stderr, "\033[35mrl78_far_p: "); debug_rtx(x); + fprintf (stderr, " = %d\033[0m\n", MEM_ADDR_SPACE (x) == ADDR_SPACE_FAR); #endif return MEM_ADDR_SPACE (x) == ADDR_SPACE_FAR; } @@ -744,6 +876,10 @@ rl78_as_legitimate_address (enum machine_mode mode ATTRIBUTE_UNUSED, rtx x, if (strict && base && GET_CODE (base) == REG && REGNO (base) >= FIRST_PSEUDO_REGISTER) return false; + if (! cfun->machine->virt_insns_ok && base && GET_CODE (base) == REG + && REGNO (base) >= 8 && REGNO (base) <= 31) + return false; + return true; } @@ -789,8 +925,6 @@ rl78_addr_space_convert (rtx op, tree from_type, tree to_type) { /* This always works. */ result = gen_reg_rtx (SImode); - debug_rtx(result); - debug_rtx(op); emit_move_insn (rl78_subreg (HImode, result, SImode, 0), op); emit_move_insn (rl78_subreg (HImode, result, SImode, 2), const0_rtx); return result; @@ -998,7 +1132,7 @@ rl78_start_function (FILE *file, HOST_WIDE_INT hwi_local ATTRIBUTE_UNUSED) for (i = 0; i < 16; i ++) if (cfun->machine->need_to_push[i]) fprintf (file, " %s", word_regnames[i*2]); - fprintf(file, "\n"); + fprintf (file, "\n"); } if (frame_pointer_needed) @@ -1094,12 +1228,17 @@ rl78_function_arg_boundary (enum machine_mode mode ATTRIBUTE_UNUSED, v - real register corresponding to a virtual register m - minus - negative of CONST_INT value. c - inverse of a conditional (NE vs EQ for example) + z - collapsed conditional + s - shift count mod 8 + S - shift count mod 16 + r - reverse shift count (8-(count mod 8)) h - bottom HI of an SI H - top HI of an SI q - bottom QI of an HI Q - top QI of an HI e - third QI of an SI (i.e. where the ES register gets values from) + E - fourth QI of an SI (i.e. MSB) */ @@ -1120,7 +1259,7 @@ rl78_print_operand_1 (FILE * file, rtx op, int letter) else { if (rl78_far_p (op)) - fprintf(file, "es:"); + fprintf (file, "es:"); if (letter == 'H') { op = adjust_address (op, HImode, 2); @@ -1146,15 +1285,20 @@ rl78_print_operand_1 (FILE * file, rtx op, int letter) op = adjust_address (op, QImode, 2); letter = 0; } + if (letter == 'E') + { + op = adjust_address (op, QImode, 3); + letter = 0; + } if (CONSTANT_P (XEXP (op, 0))) { - fprintf(file, "!"); + fprintf (file, "!"); rl78_print_operand_1 (file, XEXP (op, 0), letter); } else if (GET_CODE (XEXP (op, 0)) == PLUS && GET_CODE (XEXP (XEXP (op, 0), 0)) == SYMBOL_REF) { - fprintf(file, "!"); + fprintf (file, "!"); rl78_print_operand_1 (file, XEXP (op, 0), letter); } else if (GET_CODE (XEXP (op, 0)) == PLUS @@ -1162,15 +1306,15 @@ rl78_print_operand_1 (FILE * file, rtx op, int letter) && REGNO (XEXP (XEXP (op, 0), 0)) == 2) { rl78_print_operand_1 (file, XEXP (XEXP (op, 0), 1), 'u'); - fprintf(file, "["); + fprintf (file, "["); rl78_print_operand_1 (file, XEXP (XEXP (op, 0), 0), 0); - fprintf(file, "]"); + fprintf (file, "]"); } else { - fprintf(file, "["); + fprintf (file, "["); rl78_print_operand_1 (file, XEXP (op, 0), letter); - fprintf(file, "]"); + fprintf (file, "]"); } } break; @@ -1184,6 +1328,8 @@ rl78_print_operand_1 (FILE * file, rtx op, int letter) fprintf (file, "%s", reg_names [REGNO (op) & ~1]); else if (letter == 'e') fprintf (file, "%s", reg_names [REGNO (op) + 2]); + else if (letter == 'E') + fprintf (file, "%s", reg_names [REGNO (op) + 3]); else if (letter == 'S') fprintf (file, "0x%x", 0xffef8 + REGNO (op)); else if (GET_MODE (op) == HImode @@ -1209,10 +1355,20 @@ rl78_print_operand_1 (FILE * file, rtx op, int letter) fprintf (file, "%ld", INTVAL (op) & 0xffff); else if (letter == 'e') fprintf (file, "%ld", (INTVAL (op) >> 16) & 0xff); + else if (letter == 'E') + fprintf (file, "%ld", (INTVAL (op) >> 24) & 0xff); else if (letter == 'm') fprintf (file, "%ld", - INTVAL (op)); + else if (letter == 's') + fprintf (file, "%ld", INTVAL (op) % 8); + else if (letter == 'S') + fprintf (file, "%ld", INTVAL (op) % 16); + else if (letter == 'r') + fprintf (file, "%ld", 8 - (INTVAL (op) % 8)); + else if (letter == 'C') + fprintf (file, "%ld", (INTVAL (op) ^ 0x8000) & 0xffff); else - fprintf(file, "%ld", INTVAL (op)); + fprintf (file, "%ld", INTVAL (op)); break; case CONST: @@ -1316,22 +1472,68 @@ rl78_print_operand_1 (FILE * file, rtx op, int letter) break; case LTU: - fprintf (file, letter == 'c' ? "nc" : "c"); + if (letter == 'z') + fprintf (file, "#comparison eliminated"); + else + fprintf (file, letter == 'c' ? "nc" : "c"); break; case LEU: - fprintf (file, letter == 'c' ? "h" : "nh"); + if (letter == 'z') + fprintf (file, "br"); + else + fprintf (file, letter == 'c' ? "h" : "nh"); break; case GEU: - fprintf (file, letter == 'c' ? "c" : "nc"); + if (letter == 'z') + fprintf (file, "br"); + else + fprintf (file, letter == 'c' ? "c" : "nc"); break; case GTU: - fprintf (file, letter == 'c' ? "nh" : "h"); + if (letter == 'z') + fprintf (file, "#comparison eliminated"); + else + fprintf (file, letter == 'c' ? "nh" : "h"); break; case EQ: - fprintf (file, letter == 'c' ? "nz" : "z"); + if (letter == 'z') + fprintf (file, "br"); + else + fprintf (file, letter == 'c' ? "nz" : "z"); break; case NE: - fprintf (file, letter == 'c' ? "z" : "nz"); + if (letter == 'z') + fprintf (file, "#comparison eliminated"); + else + fprintf (file, letter == 'c' ? "z" : "nz"); + break; + + /* Note: these assume appropriate adjustments were made so that + unsigned comparisons, which is all this chip has, will + work. */ + case LT: + if (letter == 'z') + fprintf (file, "#comparison eliminated"); + else + fprintf (file, letter == 'c' ? "nc" : "c"); + break; + case LE: + if (letter == 'z') + fprintf (file, "br"); + else + fprintf (file, letter == 'c' ? "h" : "nh"); + break; + case GE: + if (letter == 'z') + fprintf (file, "br"); + else + fprintf (file, letter == 'c' ? "c" : "nc"); + break; + case GT: + if (letter == 'z') + fprintf (file, "#comparison eliminated"); + else + fprintf (file, letter == 'c' ? "nh" : "h"); break; default: @@ -1346,7 +1548,7 @@ rl78_print_operand_1 (FILE * file, rtx op, int letter) static void rl78_print_operand (FILE * file, rtx op, int letter) { - if (CONSTANT_P (op) && letter != 'u') + if (CONSTANT_P (op) && letter != 'u' && letter != 's' && letter != 'r' && letter != 'S') fprintf (file, "#"); rl78_print_operand_1 (file, op, letter); } @@ -1391,66 +1593,8 @@ rl78_trampoline_adjust_address (rtx m_tramp) void rl78_expand_compare (rtx *operands) { - /* RL78 does not have signed comparisons. We must modify the - operands to be in the unsigned range, and emit an unsigned - comparison. */ - - enum machine_mode mode; - rtx high_bit; - int i; - RTX_CODE new_cond; - - switch (GET_CODE (operands[0])) - { - case GE: - new_cond = GEU; - break; - case LE: - new_cond = LEU; - break; - case GT: - new_cond = GTU; - break; - case LT: - new_cond = LTU; - break; - default: - return; - } - -#if DEBUG0 - fprintf (stderr, "\033[38;5;129mrl78_expand_compare\n"); - debug_rtx (operands[0]); - fprintf (stderr, "\033[0m"); -#endif - - mode = GET_MODE (operands[1]); - if (mode == VOIDmode) - mode = GET_MODE (operands[2]); - high_bit = GEN_INT (~0 << (GET_MODE_BITSIZE (mode) - 1)); - - /* 0: conditional 1,2: operands */ - for (i = 1; i <= 2; i ++) - { - rtx r = operands[i]; - - if (GET_CODE (r) == CONST_INT) - r = GEN_INT (INTVAL (r) ^ INTVAL (high_bit)); - else - { - r = gen_rtx_PLUS (mode, operands[i], high_bit); - r = copy_to_mode_reg (mode, r); - } - operands[i] = r; - } - - operands[0] = gen_rtx_fmt_ee (new_cond, GET_MODE (operands[0]), operands[1], operands[2]); - -#if DEBUG0 - fprintf (stderr, "\033[38;5;142mrl78_expand_compare\n"); - debug_rtx (operands[0]); - fprintf (stderr, "\033[0m"); -#endif + if (GET_CODE (operands[2]) == MEM) + operands[2] = copy_to_mode_reg (GET_MODE (operands[2]), operands[2]); } @@ -1473,10 +1617,10 @@ rl78_peep_movhi_p (rtx *operands) #if DEBUG_PEEP fprintf (stderr, "\033[33m"); - debug_rtx(operands[0]); - debug_rtx(operands[1]); - debug_rtx(operands[2]); - debug_rtx(operands[3]); + debug_rtx (operands[0]); + debug_rtx (operands[1]); + debug_rtx (operands[2]); + debug_rtx (operands[3]); fprintf (stderr, "\033[0m"); #endif @@ -1662,14 +1806,242 @@ re-run regmove, but that has not yet been attempted. */ #define DEBUG_ALLOC 0 +/* This array is used to hold knowledge about the contents of the + real registers (A ... H), the memory-based registers (r8 ... r31) + and the first NUM_STACK_LOCS words on the stack. We use this to + avoid generating redundant move instructions. + + A value in the range 0 .. 31 indicates register A .. r31. + A value in the range 32 .. 63 indicates stack slot (value - 32). + A value of NOT_KNOWN indicates that the contents of that location + are not known. */ + +#define NUM_STACK_LOCS 32 +#define NOT_KNOWN 127 + +static unsigned char content_memory [32 + NUM_STACK_LOCS]; + +static unsigned char saved_update_index = NOT_KNOWN; +static unsigned char saved_update_value; +static enum machine_mode saved_update_mode; + + +static inline void +clear_content_memory (void) +{ + memset (content_memory, NOT_KNOWN, sizeof content_memory); + if (dump_file) + fprintf (dump_file, " clear content memory\n"); + saved_update_index = NOT_KNOWN; +} + +/* Convert LOC into an index into the content_memory array. + If LOC cannot be converted, return NOT_KNOWN. */ + +static unsigned char +get_content_index (rtx loc) +{ + enum machine_mode mode; + + if (loc == NULL_RTX) + return NOT_KNOWN; + + if (REG_P (loc)) + { + if (REGNO (loc) < 32) + return REGNO (loc); + return NOT_KNOWN; + } + + mode = GET_MODE (loc); + + if (! rl78_stack_based_mem (loc, mode)) + return NOT_KNOWN; + + loc = XEXP (loc, 0); + + if (REG_P (loc)) + /* loc = MEM (SP) */ + return 32; + + /* loc = MEM (PLUS (SP, INT)). */ + loc = XEXP (loc, 1); + + if (INTVAL (loc) < NUM_STACK_LOCS) + return 32 + INTVAL (loc); + + return NOT_KNOWN; +} + +/* Return a string describing content INDEX in mode MODE. + WARNING: Can return a pointer to a static buffer. */ + +static const char * +get_content_name (unsigned char index, enum machine_mode mode) +{ + static char buffer [128]; + + if (index == NOT_KNOWN) + return "Unknown"; + + if (index > 31) + sprintf (buffer, "stack slot %d", index - 32); + else if (mode == HImode) + sprintf (buffer, "%s%s", + reg_names [index + 1], reg_names [index]); + else + return reg_names [index]; + + return buffer; +} + +#if DEBUG_ALLOC + +static void +display_content_memory (FILE * file) +{ + unsigned int i; + + fprintf (file, " Known memory contents:\n"); + + for (i = 0; i < sizeof content_memory; i++) + if (content_memory[i] != NOT_KNOWN) + { + fprintf (file, " %s contains a copy of ", get_content_name (i, QImode)); + fprintf (file, "%s\n", get_content_name (content_memory [i], QImode)); + } +} +#endif + +static void +update_content (unsigned char index, unsigned char val, enum machine_mode mode) +{ + unsigned int i; + + gcc_assert (index < sizeof content_memory); + + content_memory [index] = val; + if (val != NOT_KNOWN) + content_memory [val] = index; + + /* Make the entry in dump_file *before* VAL is increased below. */ + if (dump_file) + { + fprintf (dump_file, " %s now contains ", get_content_name (index, mode)); + if (val == NOT_KNOWN) + fprintf (dump_file, "Unknown\n"); + else + fprintf (dump_file, "%s and vice versa\n", get_content_name (val, mode)); + } + + if (mode == HImode) + { + val = val == NOT_KNOWN ? val : val + 1; + + content_memory [index + 1] = val; + if (val != NOT_KNOWN) + { + content_memory [val] = index + 1; + -- val; + } + } + + /* Any other places that had INDEX recorded as their contents are now invalid. */ + for (i = 0; i < sizeof content_memory; i++) + { + if (i == index + || (val != NOT_KNOWN && i == val)) + { + if (mode == HImode) + ++ i; + continue; + } + + if (content_memory[i] == index + || (val != NOT_KNOWN && content_memory[i] == val)) + { + content_memory[i] = NOT_KNOWN; + + if (dump_file) + fprintf (dump_file, " %s cleared\n", get_content_name (i, mode)); + + if (mode == HImode) + content_memory[++ i] = NOT_KNOWN; + } + } +} + +/* Record that LOC contains VALUE. + For HImode locations record that LOC+1 contains VALUE+1. + If LOC is not a register or stack slot, do nothing. + If VALUE is not a register or stack slot, clear the recorded content. */ + +static void +record_content (rtx loc, rtx value) +{ + enum machine_mode mode; + unsigned char index; + unsigned char val; + + if ((index = get_content_index (loc)) == NOT_KNOWN) + return; + + val = get_content_index (value); + + mode = GET_MODE (loc); + + if (val == index) + { + if (! optimize) + return; + + /* This should not happen when optimizing. */ +#if 1 + fprintf (stderr, "ASSIGNMENT of location to itself detected! [%s]\n", + get_content_name (val, mode)); + return; +#else + gcc_unreachable (); +#endif + } + + update_content (index, val, mode); +} + +/* Returns TRUE if LOC already contains a copy of VALUE. */ + +static bool +already_contains (rtx loc, rtx value) +{ + unsigned char index; + unsigned char val; + + if ((index = get_content_index (loc)) == NOT_KNOWN) + return false; + + if ((val = get_content_index (value)) == NOT_KNOWN) + return false; + + if (content_memory [index] != val) + return false; + + if (GET_MODE (loc) == HImode) + return content_memory [index + 1] == val + 1; + + return true; +} + /* Rescans an insn to see if it's recognized again. This is done carefully to ensure that all the constraint information is accurate for the newly matched insn. */ static bool insn_ok_now (rtx insn) { + rtx pattern = PATTERN (insn); + INSN_CODE (insn) = -1; - if (recog (PATTERN (insn), insn, 0) > -1) + + if (recog (pattern, insn, 0) > -1) { extract_insn (insn); if (constrain_operands (1)) @@ -1679,15 +2051,34 @@ insn_ok_now (rtx insn) debug_rtx (insn); fprintf (stderr, "\033[0m"); #endif + if (SET_P (pattern)) + record_content (SET_DEST (pattern), SET_SRC (pattern)); + return true; } } else { - fprintf (stderr, "\033[41;30m Unrecognized insn \033[0m\n"); + /* We need to re-recog the insn with virtual registers to get + the operands */ + cfun->machine->virt_insns_ok = 1; + if (recog (pattern, insn, 0) > -1) + { + extract_insn (insn); + if (constrain_operands (0)) + { + cfun->machine->virt_insns_ok = 0; + return false; + } + } + +#if DEBUG_ALLOC + fprintf (stderr, "\033[41;30m Unrecognized *virtual* insn \033[0m\n"); debug_rtx (insn); +#endif gcc_unreachable (); } + #if DEBUG_ALLOC fprintf (stderr, "\033[31m"); debug_rtx (insn); @@ -1697,15 +2088,15 @@ insn_ok_now (rtx insn) } #if DEBUG_ALLOC -#define WORKED fprintf (stderr, "\033[48;5;22m Worked at line %d \033[0m\n", __LINE__) +#define WORKED fprintf (stderr, "\033[48;5;22m Worked at line %d \033[0m\n", __LINE__) #define FAILEDSOFAR fprintf (stderr, "\033[48;5;52m FAILED at line %d \033[0m\n", __LINE__) -#define FAILED fprintf (stderr, "\033[48;5;52m FAILED at line %d \033[0m\n", __LINE__), gcc_unreachable() +#define FAILED fprintf (stderr, "\033[48;5;52m FAILED at line %d \033[0m\n", __LINE__), gcc_unreachable() #define MAYBE_OK(insn) if (insn_ok_now (insn)) { WORKED; return; } else { FAILEDSOFAR; } +#define MUST_BE_OK(insn) if (insn_ok_now (insn)) { WORKED; return; } FAILED #else -#define WORKED -#define FAILEDSOFAR #define FAILED gcc_unreachable () #define MAYBE_OK(insn) if (insn_ok_now (insn)) return; +#define MUST_BE_OK(insn) if (insn_ok_now (insn)) return; FAILED #endif /* Registers into which we move the contents of virtual registers. */ @@ -1787,16 +2178,95 @@ rl78_hi8 (rtx addr) return rl78_subreg (QImode, addr, SImode, 2); } -/* Copy any register values into real registers and return an RTX for - the same memory, now addressed by real registers. Any needed insns - are emitted before BEFORE. */ +static void +add_postponed_content_update (rtx to, rtx value) +{ + unsigned char index; + + if ((index = get_content_index (to)) == NOT_KNOWN) + return; + + gcc_assert (saved_update_index == NOT_KNOWN); + saved_update_index = index; + saved_update_value = get_content_index (value); + saved_update_mode = GET_MODE (to); +} + +static void +process_postponed_content_update (void) +{ + if (saved_update_index != NOT_KNOWN) + { + update_content (saved_update_index, saved_update_value, saved_update_mode); + saved_update_index = NOT_KNOWN; + } +} + +/* Generate and emit a move of (register) FROM into TO. if WHERE is not NULL + then if BEFORE is true then emit the insn before WHERE, otherwise emit it + after WHERE. If TO already contains FROM then do nothing. Returns TO if + BEFORE is true, FROM otherwise. */ +static rtx +gen_and_emit_move (rtx to, rtx from, rtx where, bool before) +{ + enum machine_mode mode = GET_MODE (to); + + if (optimize && before && already_contains (to, from)) + { +#if DEBUG_ALLOC + display_content_memory (stderr); +#endif + if (dump_file) + { + fprintf (dump_file, " Omit move of %s into ", + get_content_name (get_content_index (from), mode)); + fprintf (dump_file, "%s as it already contains this value\n", + get_content_name (get_content_index (to), mode)); + } + } + else + { + rtx move = mode == QImode ? gen_movqi (to, from) : gen_movhi (to, from); + + EM (move); + + if (where == NULL_RTX) + emit_insn (move); + else if (before) + emit_insn_before (move, where); + else + { + rtx note = find_reg_note (where, REG_EH_REGION, NULL_RTX); + + /* If necessary move REG_EH_REGION notes forward. + cf. compiling gcc.dg/pr44545.c. */ + if (note != NULL_RTX) + { + add_reg_note (move, REG_EH_REGION, XEXP (note, 0)); + remove_note (where, note); + } + + emit_insn_after (move, where); + } + + if (before) + record_content (to, from); + else + add_postponed_content_update (to, from); + } + return before ? to : from; +} + +/* If M is MEM(REG) or MEM(PLUS(REG,INT)) and REG is virtual then + copy it into NEWBASE and return the updated MEM. Otherwise just + return M. Any needed insns are emitted before BEFORE. */ static rtx transcode_memory_rtx (rtx m, rtx newbase, rtx before) { rtx base, index, addendr; int addend = 0; - if (GET_CODE (m) != MEM) + if (! MEM_P (m)) return m; if (GET_MODE (XEXP (m, 0)) == SImode) @@ -1806,16 +2276,18 @@ transcode_memory_rtx (rtx m, rtx newbase, rtx before) fprintf (stderr, "setting ES:\n"); debug_rtx(seg); #endif - emit_insn_before (EM(gen_movqi (A, seg)), before); - emit_insn_before (EM(gen_movqi_es (A)), before); + emit_insn_before (EM (gen_movqi (A, seg)), before); + emit_insn_before (EM (gen_movqi_es (A)), before); + record_content (A, NULL_RTX); + m = change_address (m, GET_MODE (m), rl78_lo16 (XEXP (m, 0))); } - characterize_address (XEXP (m, 0), &base, &index, &addendr); + characterize_address (XEXP (m, 0), & base, & index, & addendr); gcc_assert (index == NULL_RTX); #if DEBUG_ALLOC - fprintf (stderr, "\033[33m"); debug_rtx(m); fprintf (stderr, "\033[0m"); + fprintf (stderr, "\033[33m"); debug_rtx (m); fprintf (stderr, "\033[0m"); debug_rtx (base); #endif if (base == NULL_RTX) @@ -1824,6 +2296,9 @@ transcode_memory_rtx (rtx m, rtx newbase, rtx before) if (addendr && GET_CODE (addendr) == CONST_INT) addend = INTVAL (addendr); + gcc_assert (REG_P (base)); + gcc_assert (REG_P (newbase)); + if (REGNO (base) == SP_REG) { if (addend >= 0 && addend <= 255) @@ -1844,17 +2319,22 @@ transcode_memory_rtx (rtx m, rtx newbase, rtx before) EM (emit_insn_before (gen_movhi (AX, base), before)); EM (emit_insn_before (gen_addhi3 (AX, AX, addendr), before)); EM (emit_insn_before (gen_movhi (newbase, AX), before)); + record_content (AX, NULL_RTX); + record_content (newbase, NULL_RTX); + base = newbase; addend = 0; } else { - EM (emit_insn_before (gen_movhi (newbase, base), before)); - base = newbase; + base = gen_and_emit_move (newbase, base, before, true); } if (addend) - base = gen_rtx_PLUS (HImode, base, GEN_INT (addend)); + { + record_content (base, NULL_RTX); + base = gen_rtx_PLUS (HImode, base, GEN_INT (addend)); + } #if DEBUG_ALLOC fprintf (stderr, "\033[33m"); @@ -1874,49 +2354,48 @@ transcode_memory_rtx (rtx m, rtx newbase, rtx before) static rtx move_to_acc (int opno, rtx before) { - rtx src = OP(opno); + rtx src = OP (opno); enum machine_mode mode = GET_MODE (src); - if (GET_CODE (src) == REG - && REGNO (src) < 2) + if (REG_P (src) && REGNO (src) < 2) return src; if (mode == VOIDmode) mode = recog_data.operand_mode[opno]; - if (mode == QImode) - { - EM (emit_insn_before (gen_movqi (A, src), before)); - return A; - } - else - { - EM (emit_insn_before (gen_movhi (AX, src), before)); - return AX; - } + return gen_and_emit_move (mode == QImode ? A : AX, src, before, true); +} + +static void +force_into_acc (rtx src, rtx before) +{ + enum machine_mode mode = GET_MODE (src); + rtx move; + + if (REG_P (src) && REGNO (src) < 2) + return; + + move = mode == QImode ? gen_movqi (A, src) : gen_movhi (AX, src); + + EM (move); + + emit_insn_before (move, before); + record_content (AX, NULL_RTX); } /* Copy accumulator (A or AX) to DEST, placing any generated insns after AFTER. Returns accumulator RTX. */ static rtx -move_from_acc (rtx dest, rtx after) +move_from_acc (unsigned int opno, rtx after) { + rtx dest = OP (opno); enum machine_mode mode = GET_MODE (dest); if (REG_P (dest) && REGNO (dest) < 2) return dest; - if (mode == QImode) - { - EM (emit_insn_after (gen_movqi (dest, A), after)); - return A; - } - else - { - EM (emit_insn_after (gen_movhi (dest, AX), after)); - return AX; - } + return gen_and_emit_move (dest, mode == QImode ? A : AX, after, false); } /* Copy accumulator (A or AX) to REGNO, placing any generated insns @@ -1930,16 +2409,7 @@ move_acc_to_reg (rtx acc, int regno, rtx before) reg = gen_rtx_REG (mode, regno); - if (mode == QImode) - { - EM (emit_insn_before (gen_movqi (reg, A), before)); - return reg; - } - else - { - EM (emit_insn_before (gen_movhi (reg, AX), before)); - return reg; - } + return gen_and_emit_move (reg, acc, before, true); } /* Copy SRC to X, placing any generated insns before BEFORE. @@ -1948,7 +2418,7 @@ move_acc_to_reg (rtx acc, int regno, rtx before) static rtx move_to_x (int opno, rtx before) { - rtx src = OP(opno); + rtx src = OP (opno); enum machine_mode mode = GET_MODE (src); rtx reg; @@ -1958,17 +2428,12 @@ move_to_x (int opno, rtx before) if (mode == QImode || ! is_virtual_register (OP (opno))) { - OP(opno) = move_to_acc (opno, before); - OP(opno) = move_acc_to_reg (OP(opno), X_REG, before); + OP (opno) = move_to_acc (opno, before); + OP (opno) = move_acc_to_reg (OP(opno), X_REG, before); return reg; } - if (mode == QImode) - EM (emit_insn_before (gen_movqi (reg, src), before)); - else - EM (emit_insn_before (gen_movhi (reg, src), before)); - - return reg; + return gen_and_emit_move (reg, src, before, true); } /* Copy OP(opno) to H or HL, placing any generated insns before BEFORE. @@ -1992,12 +2457,7 @@ move_to_hl (int opno, rtx before) return reg; } - if (mode == QImode) - EM (emit_insn_before (gen_movqi (reg, src), before)); - else - EM (emit_insn_before (gen_movhi (reg, src), before)); - - return reg; + return gen_and_emit_move (reg, src, before, true); } /* Copy OP(opno) to E or DE, placing any generated insns before BEFORE. @@ -2022,9 +2482,7 @@ move_to_de (int opno, rtx before) } else { - rtx move = mode == QImode ? gen_movqi (reg, src) : gen_movhi (reg, src); - - EM (emit_insn_before (move, before)); + gen_and_emit_move (reg, src, before, true); } return reg; @@ -2038,63 +2496,104 @@ rl78_alloc_physical_registers_op1 (rtx insn) /* We first try using A as the destination, then copying it back. */ - if (rtx_equal_p (OP(0), OP(1))) + if (rtx_equal_p (OP (0), OP (1))) { - OP(0) = - OP(1) = transcode_memory_rtx (OP(1), DE, insn); + OP (0) = + OP (1) = transcode_memory_rtx (OP (1), DE, insn); } else { - OP(0) = transcode_memory_rtx (OP(0), BC, insn); - OP(1) = transcode_memory_rtx (OP(1), HL, insn); + /* If necessary, load the operands into BC and HL. + Check to see if we already have OP (0) in HL + and if so, swap the order. */ + if (MEM_P (OP (0)) + && already_contains (HL, XEXP (OP (0), 0))) + { + OP (0) = transcode_memory_rtx (OP (0), HL, insn); + OP (1) = transcode_memory_rtx (OP (1), BC, insn); + } + else + { + OP (0) = transcode_memory_rtx (OP (0), BC, insn); + OP (1) = transcode_memory_rtx (OP (1), HL, insn); + } } MAYBE_OK (insn); - OP(0) = move_from_acc (OP(0), insn); + OP (0) = move_from_acc (0, insn); MAYBE_OK (insn); /* Try copying the src to acc first, then. This is for, for example, ZERO_EXTEND or NOT. */ - OP(1) = move_to_acc (1, insn); + OP (1) = move_to_acc (1, insn); - MAYBE_OK (insn); + MUST_BE_OK (insn); +} + +/* Returns true if operand OPNUM contains a constraint of type CONSTRAINT. + Assumes that the current insn has already been recognised and hence the + constraint data has been filled in. */ +static bool +has_constraint (unsigned int opnum, enum constraint_num constraint) +{ + const char * p = recog_data.constraints[opnum]; + + /* No constraints means anything is accepted. */ + if (p == NULL || *p == 0 || *p == ',') + return true; + + do + { + char c; + unsigned int len; - FAILED; + c = *p; + len = CONSTRAINT_LEN (c, p); + gcc_assert (len > 0); + + switch (c) + { + case 0: + case ',': + return false; + default: + if (lookup_constraint (p) == constraint) + return true; + } + p += len; + } + while (1); } -/* Devirtualize an insn of the form (SET (op) (unop (op) (op))). */ +/* Devirtualize an insn of the form (SET (op) (binop (op) (op))). */ static void rl78_alloc_physical_registers_op2 (rtx insn) { - /* op[0] = op[1] func op[2] */ - rtx prev = prev_nonnote_nondebug_insn (insn); + rtx prev; rtx first; bool hl_used; + int tmp_id; + rtx saved_op1; - if (rtx_equal_p (OP(0), OP(1))) + if (rtx_equal_p (OP (0), OP (1))) { - OP(0) = - OP(1) = transcode_memory_rtx (OP(1), DE, insn); - prev = next_nonnote_nondebug_insn (prev); - OP(2) = transcode_memory_rtx (OP(2), HL, insn); - prev = prev_nonnote_nondebug_insn (prev); + OP (0) = + OP (1) = transcode_memory_rtx (OP (1), DE, insn); + OP (2) = transcode_memory_rtx (OP (2), HL, insn); } - else if (rtx_equal_p (OP(0), OP(2))) + else if (rtx_equal_p (OP (0), OP (2))) { - OP(1) = transcode_memory_rtx (OP(1), DE, insn); - prev = next_nonnote_nondebug_insn (prev); - OP(0) = - OP(2) = transcode_memory_rtx (OP(2), HL, insn); - prev = prev_nonnote_nondebug_insn (prev); + OP (1) = transcode_memory_rtx (OP (1), DE, insn); + OP (0) = + OP (2) = transcode_memory_rtx (OP (2), HL, insn); } else { - OP(0) = transcode_memory_rtx (OP(0), BC, insn); - OP(1) = transcode_memory_rtx (OP(1), DE, insn); - prev = next_nonnote_nondebug_insn (prev); - OP(2) = transcode_memory_rtx (OP(2), HL, insn); + OP (0) = transcode_memory_rtx (OP (0), BC, insn); + OP (1) = transcode_memory_rtx (OP (1), DE, insn); + OP (2) = transcode_memory_rtx (OP (2), HL, insn); } MAYBE_OK (insn); @@ -2110,17 +2609,106 @@ rl78_alloc_physical_registers_op2 (rtx insn) OP (2) = tmp; } - /* Make a note of wether (H)L is being used. It matters - because if OP(2) alsoneeds reloading, then we must take + /* Make a note of whether (H)L is being used. It matters + because if OP (2) alsoneeds reloading, then we must take care not to corrupt HL. */ hl_used = reg_mentioned_p (L, OP (0)) || reg_mentioned_p (L, OP (1)); - OP(0) = move_from_acc (OP (0), insn); - OP(1) = move_to_acc (1, insn); + /* If HL is not currently being used and dest == op1 then there are + some possible optimizations available by reloading one of the + operands into HL, before trying to use the accumulator. */ + if (optimize + && ! hl_used + && rtx_equal_p (OP (0), OP (1))) + { + /* If op0 is a Ws1 type memory address then switching the base + address register to HL might allow us to perform an in-memory + operation. (eg for the INCW instruction). + + FIXME: Adding the move into HL is costly if this optimization is not + going to work, so for now, make sure that we know that the new insn will + match the requirements of the addhi3_real pattern. Really we ought to + generate a candidate sequence, test that, and then install it if the + results are good. */ + if (satisfies_constraint_Ws1 (OP (0)) + && has_constraint (0, CONSTRAINT_Wh1) + && (satisfies_constraint_K (OP (2)) || satisfies_constraint_L (OP (2)))) + { + rtx base, index, addend, newbase; + + characterize_address (XEXP (OP (0), 0), & base, & index, & addend); + gcc_assert (index == NULL_RTX); + gcc_assert (REG_P (base) && REGNO (base) == SP_REG); + + /* Ws1 addressing allows an offset of 0, Wh1 addressing requires a non-zero offset. */ + if (addend != NULL_RTX) + { + newbase = gen_and_emit_move (HL, base, insn, true); + record_content (newbase, NULL_RTX); + newbase = gen_rtx_PLUS (HImode, newbase, addend); + + OP (0) = OP (1) = change_address (OP (0), VOIDmode, newbase); + + /* We do not want to fail here as this means that + we have inserted useless insns into the stream. */ + MUST_BE_OK (insn); + } + } + else if (REG_P (OP (0)) + && satisfies_constraint_Ws1 (OP (2)) + && has_constraint (2, CONSTRAINT_Wh1)) + { + rtx base, index, addend, newbase; + + characterize_address (XEXP (OP (2), 0), & base, & index, & addend); + gcc_assert (index == NULL_RTX); + gcc_assert (REG_P (base) && REGNO (base) == SP_REG); + + /* Ws1 addressing allows an offset of 0, Wh1 addressing requires a non-zero offset. */ + if (addend != NULL_RTX) + { + gen_and_emit_move (HL, base, insn, true); + + if (REGNO (OP (0)) != X_REG) + { + OP (1) = move_to_acc (1, insn); + OP (0) = move_from_acc (0, insn); + } + + record_content (HL, NULL_RTX); + newbase = gen_rtx_PLUS (HImode, HL, addend); + + OP (2) = change_address (OP (2), VOIDmode, newbase); + + /* We do not want to fail here as this means that + we have inserted useless insns into the stream. */ + MUST_BE_OK (insn); + } + } + } + + + OP (0) = move_from_acc (0, insn); + + tmp_id = get_max_insn_count (); + saved_op1 = OP (1); + + if (rtx_equal_p (OP (1), OP (2))) + OP (2) = OP (1) = move_to_acc (1, insn); + else + OP (1) = move_to_acc (1, insn); MAYBE_OK (insn); - /* We have to copy op2 to HL, but that involves AX, which + /* If we omitted the move of OP1 into the accumulator (because + it was already there from a previous insn), then force the + generation of the move instruction now. We know that we + are about to emit a move into HL (or DE) via AX, and hence + our optimization to remove the load of OP1 is no longer valid. */ + if (tmp_id == get_max_insn_count ()) + force_into_acc (saved_op1, insn); + + /* We have to copy op2 to HL (or DE), but that involves AX, which already has a live value. Emit it before those insns. */ if (prev) @@ -2131,89 +2719,160 @@ rl78_alloc_physical_registers_op2 (rtx insn) OP (2) = hl_used ? move_to_de (2, first) : move_to_hl (2, first); - MAYBE_OK (insn); - - FAILED; + MUST_BE_OK (insn); } -/* Devirtualize an insn of the form (SET () (unop (op))). */ +/* Devirtualize an insn of the form SET (PC) (MEM/REG). */ static void rl78_alloc_physical_registers_ro1 (rtx insn) { - /* (void) op[0] */ - OP(0) = transcode_memory_rtx (OP(0), BC, insn); + OP (0) = transcode_memory_rtx (OP (0), BC, insn); MAYBE_OK (insn); - OP(0) = move_to_acc (0, insn); - - MAYBE_OK (insn); + OP (0) = move_to_acc (0, insn); - FAILED; + MUST_BE_OK (insn); } /* Devirtualize a compare insn. */ + static void rl78_alloc_physical_registers_cmp (rtx insn) { - /* op[1] cmp_op[0] op[2] */ + int tmp_id; + rtx saved_op1; rtx prev = prev_nonnote_nondebug_insn (insn); rtx first; - OP(1) = transcode_memory_rtx (OP(1), DE, insn); - OP(2) = transcode_memory_rtx (OP(2), HL, insn); + OP (1) = transcode_memory_rtx (OP (1), DE, insn); + OP (2) = transcode_memory_rtx (OP (2), HL, insn); + /* HI compares have to have OP(1) in AX, but QI + compares do not, so it is worth checking here. */ MAYBE_OK (insn); - OP(1) = move_to_acc (1, insn); + /* For an HImode compare, OP(1) must always be in AX. + But if OP(1) is a REG (and not AX), then we can avoid + a reload of OP(1) if we reload OP(2) into AX and invert + the comparison. */ + if (REG_P (OP (1)) + && REGNO (OP (1)) != AX_REG + && GET_MODE (OP (1)) == HImode + && MEM_P (OP (2))) + { + rtx cmp = XEXP (SET_SRC (PATTERN (insn)), 0); + + OP (2) = move_to_acc (2, insn); + + switch (GET_CODE (cmp)) + { + case EQ: + case NE: + break; + case LTU: cmp = gen_rtx_GTU (HImode, OP (2), OP (1)); break; + case GTU: cmp = gen_rtx_LTU (HImode, OP (2), OP (1)); break; + case LEU: cmp = gen_rtx_GEU (HImode, OP (2), OP (1)); break; + case GEU: cmp = gen_rtx_LEU (HImode, OP (2), OP (1)); break; + + case LT: + case GT: + case LE: + case GE: +#if DEBUG_ALLOC + debug_rtx (insn); +#endif + default: + gcc_unreachable (); + } + + if (GET_CODE (cmp) == EQ || GET_CODE (cmp) == NE) + PATTERN (insn) = gen_cbranchhi4_real (cmp, OP (2), OP (1), OP (3)); + else + PATTERN (insn) = gen_cbranchhi4_real_inverted (cmp, OP (2), OP (1), OP (3)); + + MUST_BE_OK (insn); + } + + /* Surprisingly, gcc can generate a comparison of a register with itself, but this + should be handled by the second alternative of the cbranchhi_real pattern. */ + if (rtx_equal_p (OP (1), OP (2))) + { + OP (1) = OP (2) = BC; + MUST_BE_OK (insn); + } + + tmp_id = get_max_insn_count (); + saved_op1 = OP (1); + + OP (1) = move_to_acc (1, insn); MAYBE_OK (insn); + /* If we omitted the move of OP1 into the accumulator (because + it was already there from a previous insn), then force the + generation of the move instruction now. We know that we + are about to emit a move into HL via AX, and hence our + optimization to remove the load of OP1 is no longer valid. */ + if (tmp_id == get_max_insn_count ()) + force_into_acc (saved_op1, insn); + /* We have to copy op2 to HL, but that involves the acc, which already has a live value. Emit it before those insns. */ - if (prev) first = next_nonnote_nondebug_insn (prev); else for (first = insn; prev_nonnote_nondebug_insn (first); first = prev_nonnote_nondebug_insn (first)) ; - OP(2) = move_to_hl (2, first); + OP (2) = move_to_hl (2, first); - MAYBE_OK (insn); - - FAILED; + MUST_BE_OK (insn); } /* Like op2, but AX = A op X. */ + static void rl78_alloc_physical_registers_umul (rtx insn) { - /* op[0] = op[1] func op[2] */ rtx prev = prev_nonnote_nondebug_insn (insn); rtx first; + int tmp_id; + rtx saved_op1; - OP(0) = transcode_memory_rtx (OP(0), BC, insn); - OP(1) = transcode_memory_rtx (OP(1), DE, insn); - OP(2) = transcode_memory_rtx (OP(2), HL, insn); + OP (0) = transcode_memory_rtx (OP (0), BC, insn); + OP (1) = transcode_memory_rtx (OP (1), DE, insn); + OP (2) = transcode_memory_rtx (OP (2), HL, insn); MAYBE_OK (insn); if (recog_data.constraints[1][0] == '%' - && is_virtual_register (OP(1)) - && !is_virtual_register (OP(2)) - && !CONSTANT_P (OP(2))) + && is_virtual_register (OP (1)) + && !is_virtual_register (OP (2)) + && !CONSTANT_P (OP (2))) { - rtx tmp = OP(1); - OP(1) = OP(2); - OP(2) = tmp; + rtx tmp = OP (1); + OP (1) = OP (2); + OP (2) = tmp; } - OP(0) = move_from_acc (OP(0), insn); - OP(1) = move_to_acc (1, insn); + OP (0) = move_from_acc (0, insn); + + tmp_id = get_max_insn_count (); + saved_op1 = OP (1); + + OP (1) = move_to_acc (1, insn); MAYBE_OK (insn); + /* If we omitted the move of OP1 into the accumulator (because + it was already there from a previous insn), then force the + generation of the move instruction now. We know that we + are about to emit a move into HL (or DE) via AX, and hence + our optimization to remove the load of OP1 is no longer valid. */ + if (tmp_id == get_max_insn_count ()) + force_into_acc (saved_op1, insn); + /* We have to copy op2 to X, but that involves the acc, which already has a live value. Emit it before those insns. */ @@ -2222,11 +2881,65 @@ rl78_alloc_physical_registers_umul (rtx insn) else for (first = insn; prev_nonnote_nondebug_insn (first); first = prev_nonnote_nondebug_insn (first)) ; - OP(2) = move_to_x (2, first); + OP (2) = move_to_x (2, first); + + MUST_BE_OK (insn); +} + +static void +rl78_alloc_address_registers_macax (rtx insn) +{ + int which, op; + bool replace_in_op0 = false; + bool replace_in_op1 = false; MAYBE_OK (insn); - FAILED; + /* Two different MEMs are not allowed. */ + which = 0; + for (op = 2; op >= 0; op --) + { + if (MEM_P (OP (op))) + { + if (op == 0 && replace_in_op0) + continue; + if (op == 1 && replace_in_op1) + continue; + + switch (which) + { + case 0: + /* If we replace a MEM, make sure that we replace it for all + occurrences of the same MEM in the insn. */ + replace_in_op0 = (op > 0 && rtx_equal_p (OP (op), OP (0))); + replace_in_op1 = (op > 1 && rtx_equal_p (OP (op), OP (1))); + + OP (op) = transcode_memory_rtx (OP (op), HL, insn); + if (op == 2 + && MEM_P (OP (op)) + && (REGNO (XEXP (OP (op), 0)) == SP_REG + || (GET_CODE (XEXP (OP (op), 0)) == PLUS + && REGNO (XEXP (XEXP (OP (op), 0), 0)) == SP_REG))) + { + emit_insn_before (gen_movhi (HL, gen_rtx_REG (HImode, SP_REG)), insn); + OP (op) = replace_rtx (OP (op), gen_rtx_REG (HImode, SP_REG), HL); + } + if (replace_in_op0) + OP (0) = OP (op); + if (replace_in_op1) + OP (1) = OP (op); + break; + case 1: + OP (op) = transcode_memory_rtx (OP (op), DE, insn); + break; + case 2: + OP (op) = transcode_memory_rtx (OP (op), BC, insn); + break; + } + which ++; + } + } + MUST_BE_OK (insn); } /* Scan all insns and devirtualize them. */ @@ -2266,23 +2979,46 @@ rl78_alloc_physical_registers (void) cfun->machine->virt_insns_ok = 0; cfun->machine->real_insns_ok = 1; + clear_content_memory (); + for (insn = get_insns (); insn; insn = curr) { + rtx pattern; + curr = insn ? next_nonnote_nondebug_insn (insn) : NULL; if (!INSN_P (insn)) - continue; - if (GET_CODE (PATTERN (insn)) != SET - && GET_CODE (PATTERN (insn)) != CALL) - continue; + { + if (LABEL_P (insn)) + clear_content_memory (); + + continue; + } - if (GET_CODE (PATTERN (insn)) == SET - && GET_CODE (SET_SRC (PATTERN (insn))) == ASM_OPERANDS) + if (dump_file) + fprintf (dump_file, "Converting insn %d\n", INSN_UID (insn)); + + pattern = PATTERN (insn); + if (GET_CODE (pattern) == PARALLEL) + pattern = XVECEXP (pattern, 0, 0); + if (JUMP_P (insn) || CALL_P (insn) || GET_CODE (pattern) == CALL) + clear_content_memory (); + if (GET_CODE (pattern) != SET + && GET_CODE (pattern) != CALL) + continue; + if (GET_CODE (SET_SRC (pattern)) == ASM_OPERANDS) continue; valloc_method = get_attr_valloc (insn); - PATTERN (insn)= copy_rtx_if_shared (PATTERN (insn)); + PATTERN (insn) = copy_rtx_if_shared (PATTERN (insn)); + + if (valloc_method == VALLOC_MACAX) + { + record_content (AX, NULL_RTX); + record_content (BC, NULL_RTX); + record_content (DE, NULL_RTX); + } if (insn_ok_now (insn)) continue; @@ -2312,9 +3048,18 @@ rl78_alloc_physical_registers (void) rl78_alloc_physical_registers_umul (insn); break; case VALLOC_MACAX: - /* Macro that clobbers AX */ + /* Macro that clobbers AX. */ + rl78_alloc_address_registers_macax (insn); + record_content (AX, NULL_RTX); + record_content (BC, NULL_RTX); + record_content (DE, NULL_RTX); break; } + + if (JUMP_P (insn) || CALL_P (insn) || GET_CODE (pattern) == CALL) + clear_content_memory (); + else + process_postponed_content_update (); } #if DEBUG_ALLOC fprintf (stderr, "\033[0m"); @@ -2426,7 +3171,7 @@ rl78_calculate_death_notes (void) fprintf (dump_file, "\nDead:"); for (i = 0; i < FIRST_PSEUDO_REGISTER; i ++) if (dead[i]) - fprintf(dump_file, " %s", reg_names[i]); + fprintf (dump_file, " %s", reg_names[i]); fprintf (dump_file, "\n"); print_rtl_single (dump_file, insn); } @@ -2651,13 +3396,13 @@ rl78_propogate_register_origins (void) } } - /* Special case - our ADDSI3 macro uses AX */ + /* Special case - our ADDSI3 macro uses AX and sometimes BC. */ if (get_attr_valloc (insn) == VALLOC_MACAX) { if (dump_file) - fprintf (dump_file, "Resetting origin of AX for macro.\n"); + fprintf (dump_file, "Resetting origin of AX/BC for macro.\n"); for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (i <= 1 || origins[i] <= 1) + if (i <= 3 || origins[i] <= 3) { origins[i] = i; age[i] = 0; @@ -2712,13 +3457,15 @@ rl78_remove_unused_sets (void) } } -#undef xTARGET_MACHINE_DEPENDENT_REORG -#define xTARGET_MACHINE_DEPENDENT_REORG rl78_reorg - /* This is the top of the devritualization pass. */ static void rl78_reorg (void) { + /* split2 only happens when optimizing, but we need all movSIs to be + split now. */ + if (optimize <= 0) + split_all_insns (); + rl78_alloc_physical_registers (); if (dump_file) @@ -2753,7 +3500,7 @@ rl78_reorg (void) df_analyze (); } -#undef TARGET_RETURN_IN_MEMORY +#undef TARGET_RETURN_IN_MEMORY #define TARGET_RETURN_IN_MEMORY rl78_return_in_memory static bool @@ -2764,6 +3511,63 @@ rl78_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED) } +#undef TARGET_RTX_COSTS +#define TARGET_RTX_COSTS rl78_rtx_costs + +static bool rl78_rtx_costs (rtx x, + int code, + int outer_code ATTRIBUTE_UNUSED, + int opno ATTRIBUTE_UNUSED, + int * total, + bool speed ATTRIBUTE_UNUSED) +{ + if (code == IF_THEN_ELSE) + return COSTS_N_INSNS (10); + if (GET_MODE (x) == SImode) + { + switch (code) + { + case MULT: + if (RL78_MUL_RL78) + *total = COSTS_N_INSNS (14); + else if (RL78_MUL_G13) + *total = COSTS_N_INSNS (29); + else + *total = COSTS_N_INSNS (500); + return true; + case PLUS: + *total = COSTS_N_INSNS (8); + return true; + case ASHIFT: + case ASHIFTRT: + case LSHIFTRT: + if (GET_CODE (XEXP (x, 1)) == CONST_INT) + { + switch (INTVAL (XEXP (x, 1))) + { + case 0: *total = COSTS_N_INSNS (0); break; + case 1: *total = COSTS_N_INSNS (6); break; + case 2: case 3: case 4: case 5: case 6: case 7: + *total = COSTS_N_INSNS (10); break; + case 8: *total = COSTS_N_INSNS (6); break; + case 9: case 10: case 11: case 12: case 13: case 14: case 15: + *total = COSTS_N_INSNS (10); break; + case 16: *total = COSTS_N_INSNS (3); break; + case 17: case 18: case 19: case 20: case 21: case 22: case 23: + *total = COSTS_N_INSNS (4); break; + case 24: *total = COSTS_N_INSNS (4); break; + case 25: case 26: case 27: case 28: case 29: case 30: case 31: + *total = COSTS_N_INSNS (5); break; + } + } + else + *total = COSTS_N_INSNS (10+4*16); + return true; + } + } + return false; +} + #undef TARGET_UNWIND_WORD_MODE #define TARGET_UNWIND_WORD_MODE rl78_unwind_word_mode |