X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Freload1.c;h=61b8d0113dcd1698cc1cb69f0bbbeabb093e25ab;hb=f605ca4f4e769d22c244dcf0ad9ebdb08311632d;hp=a102ccbaa0c881b5cc157b2d604645116b8c8e78;hpb=2fcd559a9b8e9cc7b27ac005ccf49f3c52aec5cb;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/reload1.c b/gcc/reload1.c index a102ccbaa0c..61b8d0113dc 100644 --- a/gcc/reload1.c +++ b/gcc/reload1.c @@ -1,5 +1,5 @@ /* Reload pseudo regs into hard regs for insns that require hard regs. - Copyright (C) 1987, 88, 89, 92-5, 1996 Free Software Foundation, Inc. + Copyright (C) 1987, 88, 89, 92-6, 1997 Free Software Foundation, Inc. This file is part of GNU CC. @@ -253,6 +253,18 @@ int reload_first_uid; int caller_save_needed; +/* The register class to use for a base register when reloading an + address. This is normally BASE_REG_CLASS, but it may be different + when using SMALL_REGISTER_CLASSES and passing parameters in + registers. */ +enum reg_class reload_address_base_reg_class; + +/* The register class to use for an index register when reloading an + address. This is normally INDEX_REG_CLASS, but it may be different + when using SMALL_REGISTER_CLASSES and passing parameters in + registers. */ +enum reg_class reload_address_index_reg_class; + /* Set to 1 while reload_as_needed is operating. Required by some machines to handle any generated moves differently. */ @@ -381,6 +393,17 @@ static void delete_output_reload PROTO((rtx, int, rtx)); static void inc_for_reload PROTO((rtx, rtx, int)); static int constraint_accepts_reg_p PROTO((char *, rtx)); static int count_occurrences PROTO((rtx, rtx)); +static void reload_cse_invalidate_regno PROTO((int, enum machine_mode, int)); +static int reload_cse_mem_conflict_p PROTO((rtx, rtx, enum machine_mode, + rtx)); +static void reload_cse_invalidate_mem PROTO((rtx)); +static void reload_cse_invalidate_rtx PROTO((rtx, rtx)); +static void reload_cse_regs PROTO((rtx)); +static int reload_cse_regno_equal_p PROTO((int, rtx, enum machine_mode)); +static int reload_cse_noop_set_p PROTO((rtx, rtx)); +static void reload_cse_simplify_set PROTO((rtx, rtx)); +static void reload_cse_check_clobber PROTO((rtx, rtx)); +static void reload_cse_record_set PROTO((rtx, rtx)); /* Initialize the reload pass once per compilation. */ @@ -431,6 +454,66 @@ init_reload () /* Initialize obstack for our rtl allocation. */ gcc_obstack_init (&reload_obstack); reload_firstobj = (char *) obstack_alloc (&reload_obstack, 0); + + /* Decide which register class should be used when reloading + addresses. If we are using SMALL_REGISTER_CLASSES, and any + parameters are passed in registers, then we do not want to use + those registers when reloading an address. Otherwise, if a + function argument needs a reload, we may wind up clobbering + another argument to the function which was already computed. If + we find a subset class which simply avoids those registers, we + use it instead. ??? It would be better to only use the + restricted class when we actually are loading function arguments, + but that is hard to determine. */ + reload_address_base_reg_class = BASE_REG_CLASS; + reload_address_index_reg_class = INDEX_REG_CLASS; +#ifdef SMALL_REGISTER_CLASSES + if (SMALL_REGISTER_CLASSES) + { + int regno; + HARD_REG_SET base, index; + enum reg_class *p; + + COPY_HARD_REG_SET (base, reg_class_contents[BASE_REG_CLASS]); + COPY_HARD_REG_SET (index, reg_class_contents[INDEX_REG_CLASS]); + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + { + if (FUNCTION_ARG_REGNO_P (regno)) + { + CLEAR_HARD_REG_BIT (base, regno); + CLEAR_HARD_REG_BIT (index, regno); + } + } + + GO_IF_HARD_REG_EQUAL (base, reg_class_contents[BASE_REG_CLASS], + baseok); + for (p = reg_class_subclasses[BASE_REG_CLASS]; + *p != LIM_REG_CLASSES; + p++) + { + GO_IF_HARD_REG_EQUAL (base, reg_class_contents[*p], usebase); + continue; + usebase: + reload_address_base_reg_class = *p; + break; + } + baseok:; + + GO_IF_HARD_REG_EQUAL (index, reg_class_contents[INDEX_REG_CLASS], + indexok); + for (p = reg_class_subclasses[INDEX_REG_CLASS]; + *p != LIM_REG_CLASSES; + p++) + { + GO_IF_HARD_REG_EQUAL (index, reg_class_contents[*p], useindex); + continue; + useindex: + reload_address_index_reg_class = *p; + break; + } + indexok:; + } +#endif /* SMALL_REGISTER_CLASSES */ } /* Main entry point for the reload pass. @@ -506,10 +589,18 @@ reload (first, global, dumpfile) as homes for pseudo registers. This is done here rather than (eg) in global_alloc because this point is reached even if not optimizing. */ - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) mark_home_live (i); + /* A function that receives a nonlocal goto must save all call-saved + registers. */ + if (current_function_has_nonlocal_label) + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + { + if (! call_used_regs[i] && ! fixed_regs[i]) + regs_ever_live[i] = 1; + } + for (i = 0; i < scratch_list_length; i++) if (scratch_list[i]) mark_scratch_live (scratch_list[i]); @@ -675,11 +766,6 @@ reload (first, global, dumpfile) for (i = LAST_VIRTUAL_REGISTER + 1; i < max_regno; i++) alter_reg (i, -1); - /* Round size of stack frame to BIGGEST_ALIGNMENT. This must be done here - because the stack size may be a part of the offset computation for - register elimination. */ - assign_stack_local (BLKmode, 0, 0); - /* If we have some registers we think can be eliminated, scan all insns to see if there is an insn that sets one of these registers to something other than itself plus a constant. If so, the register cannot be @@ -789,7 +875,7 @@ reload (first, global, dumpfile) rtx max_groups_insn[N_REG_CLASSES]; rtx max_nongroups_insn[N_REG_CLASSES]; rtx x; - HOST_WIDE_INT starting_frame_size = get_frame_size (); + HOST_WIDE_INT starting_frame_size; int previous_frame_pointer_needed = frame_pointer_needed; static char *reg_class_names[] = REG_CLASS_NAMES; @@ -811,6 +897,14 @@ reload (first, global, dumpfile) changes from 0 to 1 in this pass. */ new_basic_block_needs = 0; + /* Round size of stack frame to BIGGEST_ALIGNMENT. This must be done + here because the stack size may be a part of the offset computation + for register elimination, and there might have been new stack slots + created in the last iteration of this loop. */ + assign_stack_local (BLKmode, 0, 0); + + starting_frame_size = get_frame_size (); + /* Reset all offsets on eliminable registers to their initial values. */ #ifdef ELIMINABLE_REGS for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) @@ -871,7 +965,7 @@ reload (first, global, dumpfile) for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) if (reg_renumber[i] < 0 && reg_equiv_memory_loc[i]) { - rtx x = eliminate_regs (reg_equiv_memory_loc[i], 0, NULL_RTX); + rtx x = eliminate_regs (reg_equiv_memory_loc[i], 0, NULL_RTX, 0); if (strict_memory_address_p (GET_MODE (regno_reg_rtx[i]), XEXP (x, 0))) @@ -978,7 +1072,9 @@ reload (first, global, dumpfile) struct needs op_addr; struct needs op_addr_reload; struct needs in_addr[MAX_RECOG_OPERANDS]; + struct needs in_addr_addr[MAX_RECOG_OPERANDS]; struct needs out_addr[MAX_RECOG_OPERANDS]; + struct needs out_addr_addr[MAX_RECOG_OPERANDS]; } insn_needs; /* If needed, eliminate any eliminable registers. */ @@ -1138,9 +1234,15 @@ reload (first, global, dumpfile) case RELOAD_FOR_INPUT_ADDRESS: this_needs = &insn_needs.in_addr[reload_opnum[i]]; break; + case RELOAD_FOR_INPADDR_ADDRESS: + this_needs = &insn_needs.in_addr_addr[reload_opnum[i]]; + break; case RELOAD_FOR_OUTPUT_ADDRESS: this_needs = &insn_needs.out_addr[reload_opnum[i]]; break; + case RELOAD_FOR_OUTADDR_ADDRESS: + this_needs = &insn_needs.out_addr_addr[reload_opnum[i]]; + break; case RELOAD_FOR_OPERAND_ADDRESS: this_needs = &insn_needs.op_addr; break; @@ -1214,8 +1316,14 @@ reload (first, global, dumpfile) { in_max = MAX (in_max, insn_needs.in_addr[k].regs[j][i]); + in_max + = MAX (in_max, + insn_needs.in_addr_addr[k].regs[j][i]); out_max = MAX (out_max, insn_needs.out_addr[k].regs[j][i]); + out_max + = MAX (out_max, + insn_needs.out_addr_addr[k].regs[j][i]); } /* RELOAD_FOR_INSN reloads conflict with inputs, outputs, @@ -1249,8 +1357,12 @@ reload (first, global, dumpfile) j < reload_n_operands; j++) { in_max = MAX (in_max, insn_needs.in_addr[j].groups[i]); + in_max = MAX (in_max, + insn_needs.in_addr_addr[j].groups[i]); out_max = MAX (out_max, insn_needs.out_addr[j].groups[i]); + out_max + = MAX (out_max, insn_needs.out_addr_addr[j].groups[i]); } in_max = MAX (MAX (insn_needs.op_addr.groups[i], @@ -2013,6 +2125,10 @@ reload (first, global, dumpfile) } } + /* Do a very simple CSE pass over just the hard registers. */ + if (optimize > 0) + reload_cse_regs (first); + #ifdef PRESERVE_DEATH_INFO_REGNO_P /* Make a pass over all the insns and remove death notes for things that are no longer registers or no longer die in the insn (e.g., an input @@ -2375,7 +2491,8 @@ alter_reg (i, from_reg) if (from_reg == -1) { /* No known place to spill from => no slot to reuse. */ - x = assign_stack_local (GET_MODE (regno_reg_rtx[i]), total_size, -1); + x = assign_stack_local (GET_MODE (regno_reg_rtx[i]), total_size, + inherent_size == total_size ? 0 : -1); if (BYTES_BIG_ENDIAN) /* Cancel the big-endian correction done in assign_stack_local. Get the address of the beginning of the slot. @@ -2407,7 +2524,8 @@ alter_reg (i, from_reg) total_size = spill_stack_slot_width[from_reg]; } /* Make a slot with that size. */ - x = assign_stack_local (mode, total_size, -1); + x = assign_stack_local (mode, total_size, + inherent_size == total_size ? 0 : -1); stack_slot = x; if (BYTES_BIG_ENDIAN) { @@ -2664,10 +2782,11 @@ static struct rtvec_def *old_asm_operands_vec, *new_asm_operands_vec; the proper thing. */ rtx -eliminate_regs (x, mem_mode, insn) +eliminate_regs (x, mem_mode, insn, storing) rtx x; enum machine_mode mem_mode; rtx insn; + int storing; { enum rtx_code code = GET_CODE (x); struct elim_table *ep; @@ -2724,7 +2843,7 @@ eliminate_regs (x, mem_mode, insn) reference to the pseudo. Ensure we make a copy of the address in case it is shared. */ new = eliminate_regs (reg_equiv_memory_loc[regno], - mem_mode, insn); + mem_mode, insn, 0); if (new != reg_equiv_memory_loc[regno]) { cannot_omit_stores[regno] = 1; @@ -2786,8 +2905,8 @@ eliminate_regs (x, mem_mode, insn) reload. This is the desired action. */ { - rtx new0 = eliminate_regs (XEXP (x, 0), mem_mode, insn); - rtx new1 = eliminate_regs (XEXP (x, 1), mem_mode, insn); + rtx new0 = eliminate_regs (XEXP (x, 0), mem_mode, insn, 0); + rtx new1 = eliminate_regs (XEXP (x, 1), mem_mode, insn, 0); if (new0 != XEXP (x, 0) || new1 != XEXP (x, 1)) { @@ -2858,9 +2977,9 @@ eliminate_regs (x, mem_mode, insn) case GE: case GT: case GEU: case GTU: case LE: case LT: case LEU: case LTU: { - rtx new0 = eliminate_regs (XEXP (x, 0), mem_mode, insn); + rtx new0 = eliminate_regs (XEXP (x, 0), mem_mode, insn, 0); rtx new1 - = XEXP (x, 1) ? eliminate_regs (XEXP (x, 1), mem_mode, insn) : 0; + = XEXP (x, 1) ? eliminate_regs (XEXP (x, 1), mem_mode, insn, 0) : 0; if (new0 != XEXP (x, 0) || new1 != XEXP (x, 1)) return gen_rtx (code, GET_MODE (x), new0, new1); @@ -2871,7 +2990,7 @@ eliminate_regs (x, mem_mode, insn) /* If we have something in XEXP (x, 0), the usual case, eliminate it. */ if (XEXP (x, 0)) { - new = eliminate_regs (XEXP (x, 0), mem_mode, insn); + new = eliminate_regs (XEXP (x, 0), mem_mode, insn, 0); if (new != XEXP (x, 0)) x = gen_rtx (EXPR_LIST, REG_NOTE_KIND (x), new, XEXP (x, 1)); } @@ -2884,7 +3003,7 @@ eliminate_regs (x, mem_mode, insn) strictly needed, but it simplifies the code. */ if (XEXP (x, 1)) { - new = eliminate_regs (XEXP (x, 1), mem_mode, insn); + new = eliminate_regs (XEXP (x, 1), mem_mode, insn, 0); if (new != XEXP (x, 1)) return gen_rtx (GET_CODE (x), GET_MODE (x), XEXP (x, 0), new); } @@ -2920,7 +3039,7 @@ eliminate_regs (x, mem_mode, insn) case ABS: case SQRT: case FFS: - new = eliminate_regs (XEXP (x, 0), mem_mode, insn); + new = eliminate_regs (XEXP (x, 0), mem_mode, insn, 0); if (new != XEXP (x, 0)) return gen_rtx (code, GET_MODE (x), new); return x; @@ -2939,7 +3058,7 @@ eliminate_regs (x, mem_mode, insn) && reg_equiv_memory_loc[REGNO (SUBREG_REG (x))] != 0) { new = eliminate_regs (reg_equiv_memory_loc[REGNO (SUBREG_REG (x))], - mem_mode, insn); + mem_mode, insn, 0); /* If we didn't change anything, we must retain the pseudo. */ if (new == reg_equiv_memory_loc[REGNO (SUBREG_REG (x))]) @@ -2959,27 +3078,37 @@ eliminate_regs (x, mem_mode, insn) } } else - new = eliminate_regs (SUBREG_REG (x), mem_mode, insn); + new = eliminate_regs (SUBREG_REG (x), mem_mode, insn, 0); if (new != XEXP (x, 0)) { - if (GET_CODE (new) == MEM - && (GET_MODE_SIZE (GET_MODE (x)) - <= GET_MODE_SIZE (GET_MODE (new))) + int x_size = GET_MODE_SIZE (GET_MODE (x)); + int new_size = GET_MODE_SIZE (GET_MODE (new)); + + /* When asked to spill a partial word subreg, we need to go + ahead and spill the whole thing against the possibility + that we reload the whole reg and find garbage at the top. */ + if (storing + && GET_CODE (new) == MEM + && x_size < new_size + && ((x_size + UNITS_PER_WORD-1) / UNITS_PER_WORD + == (new_size + UNITS_PER_WORD-1) / UNITS_PER_WORD)) + return new; + else if (GET_CODE (new) == MEM + && x_size <= new_size #ifdef LOAD_EXTEND_OP - /* On these machines we will be reloading what is - inside the SUBREG if it originally was a pseudo and - the inner and outer modes are both a word or - smaller. So leave the SUBREG then. */ - && ! (GET_CODE (SUBREG_REG (x)) == REG - && GET_MODE_SIZE (GET_MODE (x)) <= UNITS_PER_WORD - && GET_MODE_SIZE (GET_MODE (new)) <= UNITS_PER_WORD - && (GET_MODE_SIZE (GET_MODE (x)) - > GET_MODE_SIZE (GET_MODE (new))) - && INTEGRAL_MODE_P (GET_MODE (new)) - && LOAD_EXTEND_OP (GET_MODE (new)) != NIL) + /* On these machines we will be reloading what is + inside the SUBREG if it originally was a pseudo and + the inner and outer modes are both a word or + smaller. So leave the SUBREG then. */ + && ! (GET_CODE (SUBREG_REG (x)) == REG + && x_size <= UNITS_PER_WORD + && new_size <= UNITS_PER_WORD + && x_size > new_size + && INTEGRAL_MODE_P (GET_MODE (new)) + && LOAD_EXTEND_OP (GET_MODE (new)) != NIL) #endif - ) + ) { int offset = SUBREG_WORD (x) * UNITS_PER_WORD; enum machine_mode mode = GET_MODE (x); @@ -3007,7 +3136,7 @@ eliminate_regs (x, mem_mode, insn) if (ep->from_rtx == XEXP (x, 0)) ep->can_eliminate = 0; - new = eliminate_regs (XEXP (x, 0), mem_mode, insn); + new = eliminate_regs (XEXP (x, 0), mem_mode, insn, 0); if (new != XEXP (x, 0)) return gen_rtx (code, GET_MODE (x), new); return x; @@ -3020,7 +3149,7 @@ eliminate_regs (x, mem_mode, insn) if (ep->to_rtx == XEXP (x, 0)) ep->can_eliminate = 0; - new = eliminate_regs (XEXP (x, 0), mem_mode, insn); + new = eliminate_regs (XEXP (x, 0), mem_mode, insn, 0); if (new != XEXP (x, 0)) return gen_rtx (code, GET_MODE (x), new); return x; @@ -3038,7 +3167,7 @@ eliminate_regs (x, mem_mode, insn) temp_vec = (rtx *) alloca (XVECLEN (x, 3) * sizeof (rtx)); for (i = 0; i < ASM_OPERANDS_INPUT_LENGTH (x); i++) temp_vec[i] = eliminate_regs (ASM_OPERANDS_INPUT (x, i), - mem_mode, insn); + mem_mode, insn, 0); for (i = 0; i < ASM_OPERANDS_INPUT_LENGTH (x); i++) if (temp_vec[i] != ASM_OPERANDS_INPUT (x, i)) @@ -3109,8 +3238,8 @@ eliminate_regs (x, mem_mode, insn) /* Now avoid the loop below in this common case. */ { - rtx new0 = eliminate_regs (SET_DEST (x), 0, insn); - rtx new1 = eliminate_regs (SET_SRC (x), 0, insn); + rtx new0 = eliminate_regs (SET_DEST (x), 0, insn, 1); + rtx new1 = eliminate_regs (SET_SRC (x), 0, insn, 0); /* If SET_DEST changed from a REG to a MEM and INSN is an insn, write a CLOBBER insn. */ @@ -3129,7 +3258,7 @@ eliminate_regs (x, mem_mode, insn) /* Our only special processing is to pass the mode of the MEM to our recursive call and copy the flags. While we are here, handle this case more efficiently. */ - new = eliminate_regs (XEXP (x, 0), GET_MODE (x), insn); + new = eliminate_regs (XEXP (x, 0), GET_MODE (x), insn, 0); if (new != XEXP (x, 0)) { new = gen_rtx (MEM, GET_MODE (x), new); @@ -3149,7 +3278,7 @@ eliminate_regs (x, mem_mode, insn) { if (*fmt == 'e') { - new = eliminate_regs (XEXP (x, i), mem_mode, insn); + new = eliminate_regs (XEXP (x, i), mem_mode, insn, 0); if (new != XEXP (x, i) && ! copied) { rtx new_x = rtx_alloc (code); @@ -3166,7 +3295,7 @@ eliminate_regs (x, mem_mode, insn) int copied_vec = 0; for (j = 0; j < XVECLEN (x, i); j++) { - new = eliminate_regs (XVECEXP (x, i, j), mem_mode, insn); + new = eliminate_regs (XVECEXP (x, i, j), mem_mode, insn, 0); if (new != XVECEXP (x, i, j) && ! copied_vec) { rtvec new_v = gen_rtvec_vv (XVECLEN (x, i), @@ -3237,12 +3366,29 @@ eliminate_regs_in_insn (insn, replace) { rtx src = SET_SRC (old_set); int offset, ok = 0; + rtx prev_insn, prev_set; if (src == ep->to_rtx) offset = 0, ok = 1; else if (GET_CODE (src) == PLUS && GET_CODE (XEXP (src, 0)) == CONST_INT) offset = INTVAL (XEXP (src, 0)), ok = 1; + else if ((prev_insn = prev_nonnote_insn (insn)) != 0 + && (prev_set = single_set (prev_insn)) != 0 + && rtx_equal_p (SET_DEST (prev_set), src)) + { + src = SET_SRC (prev_set); + if (src == ep->to_rtx) + offset = 0, ok = 1; + else if (GET_CODE (src) == PLUS + && GET_CODE (XEXP (src, 0)) == CONST_INT + && XEXP (src, 1) == ep->to_rtx) + offset = INTVAL (XEXP (src, 0)), ok = 1; + else if (GET_CODE (src) == PLUS + && GET_CODE (XEXP (src, 1)) == CONST_INT + && XEXP (src, 0) == ep->to_rtx) + offset = INTVAL (XEXP (src, 1)), ok = 1; + } if (ok) { @@ -3327,7 +3473,7 @@ eliminate_regs_in_insn (insn, replace) but now can do this as a load-address. This saves an insn in this common case. */ - new_body = eliminate_regs (old_body, 0, replace ? insn : NULL_RTX); + new_body = eliminate_regs (old_body, 0, replace ? insn : NULL_RTX, 0); if (new_body != old_body) { /* If we aren't replacing things permanently and we changed something, @@ -3416,7 +3562,7 @@ eliminate_regs_in_insn (insn, replace) of spill registers to be needed in the final reload pass than in the pre-passes. */ if (val && REG_NOTES (insn) != 0) - REG_NOTES (insn) = eliminate_regs (REG_NOTES (insn), 0, REG_NOTES (insn)); + REG_NOTES (insn) = eliminate_regs (REG_NOTES (insn), 0, REG_NOTES (insn), 0); if (! replace) pop_obstacks (); @@ -3894,7 +4040,8 @@ reload_as_needed (first, live_known) && GET_CODE (XEXP (PATTERN (insn), 0)) == MEM) XEXP (XEXP (PATTERN (insn), 0), 0) = eliminate_regs (XEXP (XEXP (PATTERN (insn), 0), 0), - GET_MODE (XEXP (PATTERN (insn), 0)), NULL_RTX); + GET_MODE (XEXP (PATTERN (insn), 0)), + NULL_RTX, 0); /* If we need to do register elimination processing, do so. This might delete the insn, in which case we are done. */ @@ -4167,8 +4314,12 @@ reload_reg_class_lower (r1p, r2p) static HARD_REG_SET reload_reg_used; /* If reg is in use for a RELOAD_FOR_INPUT_ADDRESS reload for operand I. */ static HARD_REG_SET reload_reg_used_in_input_addr[MAX_RECOG_OPERANDS]; +/* If reg is in use for a RELOAD_FOR_INPADDR_ADDRESS reload for operand I. */ +static HARD_REG_SET reload_reg_used_in_inpaddr_addr[MAX_RECOG_OPERANDS]; /* If reg is in use for a RELOAD_FOR_OUTPUT_ADDRESS reload for operand I. */ static HARD_REG_SET reload_reg_used_in_output_addr[MAX_RECOG_OPERANDS]; +/* If reg is in use for a RELOAD_FOR_OUTADDR_ADDRESS reload for operand I. */ +static HARD_REG_SET reload_reg_used_in_outaddr_addr[MAX_RECOG_OPERANDS]; /* If reg is in use for a RELOAD_FOR_INPUT reload for operand I. */ static HARD_REG_SET reload_reg_used_in_input[MAX_RECOG_OPERANDS]; /* If reg is in use for a RELOAD_FOR_OUTPUT reload for operand I. */ @@ -4215,10 +4366,18 @@ mark_reload_reg_in_use (regno, opnum, type, mode) SET_HARD_REG_BIT (reload_reg_used_in_input_addr[opnum], i); break; + case RELOAD_FOR_INPADDR_ADDRESS: + SET_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[opnum], i); + break; + case RELOAD_FOR_OUTPUT_ADDRESS: SET_HARD_REG_BIT (reload_reg_used_in_output_addr[opnum], i); break; + case RELOAD_FOR_OUTADDR_ADDRESS: + SET_HARD_REG_BIT (reload_reg_used_in_outaddr_addr[opnum], i); + break; + case RELOAD_FOR_OPERAND_ADDRESS: SET_HARD_REG_BIT (reload_reg_used_in_op_addr, i); break; @@ -4272,10 +4431,18 @@ clear_reload_reg_in_use (regno, opnum, type, mode) CLEAR_HARD_REG_BIT (reload_reg_used_in_input_addr[opnum], i); break; + case RELOAD_FOR_INPADDR_ADDRESS: + CLEAR_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[opnum], i); + break; + case RELOAD_FOR_OUTPUT_ADDRESS: CLEAR_HARD_REG_BIT (reload_reg_used_in_output_addr[opnum], i); break; + case RELOAD_FOR_OUTADDR_ADDRESS: + CLEAR_HARD_REG_BIT (reload_reg_used_in_outaddr_addr[opnum], i); + break; + case RELOAD_FOR_OPERAND_ADDRESS: CLEAR_HARD_REG_BIT (reload_reg_used_in_op_addr, i); break; @@ -4329,7 +4496,9 @@ reload_reg_free_p (regno, opnum, type) for (i = 0; i < reload_n_operands; i++) if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[i], regno) || TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_outaddr_addr[i], regno) || TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno) || TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno)) return 0; @@ -4351,7 +4520,8 @@ reload_reg_free_p (regno, opnum, type) /* If it is used in a later operand's address, can't use it. */ for (i = opnum + 1; i < reload_n_operands; i++) - if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)) + if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[i], regno)) return 0; return 1; @@ -4359,7 +4529,21 @@ reload_reg_free_p (regno, opnum, type) case RELOAD_FOR_INPUT_ADDRESS: /* Can't use a register if it is used for an input address for this operand or used as an input in an earlier one. */ - if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[opnum], regno)) + if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[opnum], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[opnum], regno)) + return 0; + + for (i = 0; i < opnum; i++) + if (TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno)) + return 0; + + return 1; + + case RELOAD_FOR_INPADDR_ADDRESS: + /* Can't use a register if it is used for an input address + address for this operand or used as an input in an earlier + one. */ + if (TEST_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[opnum], regno)) return 0; for (i = 0; i < opnum; i++) @@ -4380,6 +4564,19 @@ reload_reg_free_p (regno, opnum, type) return 1; + case RELOAD_FOR_OUTADDR_ADDRESS: + /* Can't use a register if it is used for an output address + address for this operand or used as an output in this or a + later operand. */ + if (TEST_HARD_REG_BIT (reload_reg_used_in_outaddr_addr[opnum], regno)) + return 0; + + for (i = opnum; i < reload_n_operands; i++) + if (TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno)) + return 0; + + return 1; + case RELOAD_FOR_OPERAND_ADDRESS: for (i = 0; i < reload_n_operands; i++) if (TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno)) @@ -4406,7 +4603,8 @@ reload_reg_free_p (regno, opnum, type) return 0; for (i = 0; i <= opnum; i++) - if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno)) + if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_outaddr_addr[i], regno)) return 0; return 1; @@ -4460,12 +4658,14 @@ reload_reg_free_before_p (regno, opnum, type) the first place, since we know that it was allocated. */ case RELOAD_FOR_OUTPUT_ADDRESS: + case RELOAD_FOR_OUTADDR_ADDRESS: /* Earlier reloads are for earlier outputs or their addresses, any RELOAD_FOR_INSN reloads, any inputs or their addresses, or any RELOAD_FOR_OTHER_ADDRESS reloads (we know it can't conflict with RELOAD_OTHER).. */ for (i = 0; i < opnum; i++) if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_outaddr_addr[i], regno) || TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno)) return 0; @@ -4474,6 +4674,7 @@ reload_reg_free_before_p (regno, opnum, type) for (i = 0; i < reload_n_operands; i++) if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[i], regno) || TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno)) return 0; @@ -4486,16 +4687,19 @@ reload_reg_free_before_p (regno, opnum, type) anything that can't be used for it, except that we've already tested for RELOAD_FOR_INSN objects. */ - if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[opnum], regno)) + if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[opnum], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_outaddr_addr[opnum], regno)) return 0; for (i = 0; i < opnum; i++) if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_outaddr_addr[i], regno) || TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno)) return 0; for (i = 0; i < reload_n_operands; i++) if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[i], regno) || TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno) || TEST_HARD_REG_BIT (reload_reg_used_in_op_addr, regno)) return 0; @@ -4503,13 +4707,20 @@ reload_reg_free_before_p (regno, opnum, type) return ! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno); case RELOAD_FOR_OPERAND_ADDRESS: + /* Earlier reloads include RELOAD_FOR_OPADDR_ADDR reloads. */ + if (TEST_HARD_REG_BIT (reload_reg_used_in_op_addr_reload, regno)) + return 0; + + /* ... fall through ... */ + case RELOAD_FOR_OPADDR_ADDR: case RELOAD_FOR_INSN: /* These can't conflict with inputs, or each other, so all we have to test is input addresses and the addresses of OTHER items. */ for (i = 0; i < reload_n_operands; i++) - if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)) + if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[i], regno)) return 0; return ! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno); @@ -4520,16 +4731,19 @@ reload_reg_free_before_p (regno, opnum, type) with), and addresses of RELOAD_OTHER objects. */ for (i = 0; i <= opnum; i++) - if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)) + if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[i], regno)) return 0; return ! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno); case RELOAD_FOR_INPUT_ADDRESS: + case RELOAD_FOR_INPADDR_ADDRESS: /* Similarly, all we have to check is for use in earlier inputs' addresses. */ for (i = 0; i < opnum; i++) - if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)) + if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[i], regno)) return 0; return ! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno); @@ -4571,8 +4785,10 @@ reload_reg_reaches_end_p (regno, opnum, type) for (i = 0; i < reload_n_operands; i++) if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_outaddr_addr[i], regno) || TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno) || TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[i], regno) || TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno)) return 0; @@ -4581,6 +4797,7 @@ reload_reg_reaches_end_p (regno, opnum, type) && ! TEST_HARD_REG_BIT (reload_reg_used, regno)); case RELOAD_FOR_INPUT_ADDRESS: + case RELOAD_FOR_INPADDR_ADDRESS: /* Similar, except that we check only for this and subsequent inputs and the address of only subsequent inputs and we do not need to check for RELOAD_OTHER objects since they are known not to @@ -4591,11 +4808,13 @@ reload_reg_reaches_end_p (regno, opnum, type) return 0; for (i = opnum + 1; i < reload_n_operands; i++) - if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)) + if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[i], regno)) return 0; for (i = 0; i < reload_n_operands; i++) if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_outaddr_addr[i], regno) || TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno)) return 0; @@ -4613,6 +4832,7 @@ reload_reg_reaches_end_p (regno, opnum, type) for (i = opnum + 1; i < reload_n_operands; i++) if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[i], regno) || TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno)) return 0; @@ -4623,6 +4843,7 @@ reload_reg_reaches_end_p (regno, opnum, type) for (i = 0; i < reload_n_operands; i++) if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_outaddr_addr[i], regno) || TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno)) return 0; @@ -4631,6 +4852,7 @@ reload_reg_reaches_end_p (regno, opnum, type) case RELOAD_FOR_OPADDR_ADDR: for (i = 0; i < reload_n_operands; i++) if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_outaddr_addr[i], regno) || TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno)) return 0; @@ -4647,10 +4869,12 @@ reload_reg_reaches_end_p (regno, opnum, type) case RELOAD_FOR_OUTPUT: case RELOAD_FOR_OUTPUT_ADDRESS: + case RELOAD_FOR_OUTADDR_ADDRESS: /* We already know these can't conflict with a later output. So the only thing to check are later output addresses. */ for (i = opnum + 1; i < reload_n_operands; i++) - if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno)) + if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_outaddr_addr[i], regno)) return 0; return 1; @@ -4686,16 +4910,26 @@ reloads_conflict (r1, r2) || r2_type == RELOAD_FOR_OPERAND_ADDRESS || r2_type == RELOAD_FOR_OPADDR_ADDR || r2_type == RELOAD_FOR_INPUT - || (r2_type == RELOAD_FOR_INPUT_ADDRESS && r2_opnum > r1_opnum)); + || ((r2_type == RELOAD_FOR_INPUT_ADDRESS + || r2_type == RELOAD_FOR_INPADDR_ADDRESS) + && r2_opnum > r1_opnum)); case RELOAD_FOR_INPUT_ADDRESS: return ((r2_type == RELOAD_FOR_INPUT_ADDRESS && r1_opnum == r2_opnum) || (r2_type == RELOAD_FOR_INPUT && r2_opnum < r1_opnum)); + case RELOAD_FOR_INPADDR_ADDRESS: + return ((r2_type == RELOAD_FOR_INPADDR_ADDRESS && r1_opnum == r2_opnum) + || (r2_type == RELOAD_FOR_INPUT && r2_opnum < r1_opnum)); + case RELOAD_FOR_OUTPUT_ADDRESS: return ((r2_type == RELOAD_FOR_OUTPUT_ADDRESS && r2_opnum == r1_opnum) || (r2_type == RELOAD_FOR_OUTPUT && r2_opnum >= r1_opnum)); + case RELOAD_FOR_OUTADDR_ADDRESS: + return ((r2_type == RELOAD_FOR_OUTADDR_ADDRESS && r2_opnum == r1_opnum) + || (r2_type == RELOAD_FOR_OUTPUT && r2_opnum >= r1_opnum)); + case RELOAD_FOR_OPERAND_ADDRESS: return (r2_type == RELOAD_FOR_INPUT || r2_type == RELOAD_FOR_INSN || r2_type == RELOAD_FOR_OPERAND_ADDRESS); @@ -4706,7 +4940,8 @@ reloads_conflict (r1, r2) case RELOAD_FOR_OUTPUT: return (r2_type == RELOAD_FOR_INSN || r2_type == RELOAD_FOR_OUTPUT - || (r2_type == RELOAD_FOR_OUTPUT_ADDRESS + || ((r2_type == RELOAD_FOR_OUTPUT_ADDRESS + || r2_type == RELOAD_FOR_OUTADDR_ADDRESS) && r2_opnum >= r1_opnum)); case RELOAD_FOR_INSN: @@ -4968,7 +5203,9 @@ choose_reload_regs (insn, avoid_return_reg) int save_reload_spill_index[MAX_RELOADS]; HARD_REG_SET save_reload_reg_used; HARD_REG_SET save_reload_reg_used_in_input_addr[MAX_RECOG_OPERANDS]; + HARD_REG_SET save_reload_reg_used_in_inpaddr_addr[MAX_RECOG_OPERANDS]; HARD_REG_SET save_reload_reg_used_in_output_addr[MAX_RECOG_OPERANDS]; + HARD_REG_SET save_reload_reg_used_in_outaddr_addr[MAX_RECOG_OPERANDS]; HARD_REG_SET save_reload_reg_used_in_input[MAX_RECOG_OPERANDS]; HARD_REG_SET save_reload_reg_used_in_output[MAX_RECOG_OPERANDS]; HARD_REG_SET save_reload_reg_used_in_op_addr; @@ -4993,7 +5230,9 @@ choose_reload_regs (insn, avoid_return_reg) CLEAR_HARD_REG_SET (reload_reg_used_in_output[i]); CLEAR_HARD_REG_SET (reload_reg_used_in_input[i]); CLEAR_HARD_REG_SET (reload_reg_used_in_input_addr[i]); + CLEAR_HARD_REG_SET (reload_reg_used_in_inpaddr_addr[i]); CLEAR_HARD_REG_SET (reload_reg_used_in_output_addr[i]); + CLEAR_HARD_REG_SET (reload_reg_used_in_outaddr_addr[i]); } #ifdef SMALL_REGISTER_CLASSES @@ -5132,8 +5371,12 @@ choose_reload_regs (insn, avoid_return_reg) reload_reg_used_in_input[i]); COPY_HARD_REG_SET (save_reload_reg_used_in_input_addr[i], reload_reg_used_in_input_addr[i]); + COPY_HARD_REG_SET (save_reload_reg_used_in_inpaddr_addr[i], + reload_reg_used_in_inpaddr_addr[i]); COPY_HARD_REG_SET (save_reload_reg_used_in_output_addr[i], reload_reg_used_in_output_addr[i]); + COPY_HARD_REG_SET (save_reload_reg_used_in_outaddr_addr[i], + reload_reg_used_in_outaddr_addr[i]); } /* If -O, try first with inheritance, then turning it off. @@ -5511,8 +5754,12 @@ choose_reload_regs (insn, avoid_return_reg) save_reload_reg_used_in_output[i]); COPY_HARD_REG_SET (reload_reg_used_in_input_addr[i], save_reload_reg_used_in_input_addr[i]); + COPY_HARD_REG_SET (reload_reg_used_in_inpaddr_addr[i], + save_reload_reg_used_in_inpaddr_addr[i]); COPY_HARD_REG_SET (reload_reg_used_in_output_addr[i], save_reload_reg_used_in_output_addr[i]); + COPY_HARD_REG_SET (reload_reg_used_in_outaddr_addr[i], + save_reload_reg_used_in_outaddr_addr[i]); } } @@ -5686,8 +5933,9 @@ merge_assigned_reloads (insn) && reg_overlap_mentioned_for_reload_p (reload_in[j], reload_in[i])) reload_when_needed[j] - = reload_when_needed[i] == RELOAD_FOR_INPUT_ADDRESS - ? RELOAD_FOR_OTHER_ADDRESS : RELOAD_OTHER; + = ((reload_when_needed[i] == RELOAD_FOR_INPUT_ADDRESS + || reload_when_needed[i] == RELOAD_FOR_INPADDR_ADDRESS) + ? RELOAD_FOR_OTHER_ADDRESS : RELOAD_OTHER); } } } @@ -5704,8 +5952,10 @@ emit_reload_insns (insn) rtx other_input_address_reload_insns = 0; rtx other_input_reload_insns = 0; rtx input_address_reload_insns[MAX_RECOG_OPERANDS]; + rtx inpaddr_address_reload_insns[MAX_RECOG_OPERANDS]; rtx output_reload_insns[MAX_RECOG_OPERANDS]; rtx output_address_reload_insns[MAX_RECOG_OPERANDS]; + rtx outaddr_address_reload_insns[MAX_RECOG_OPERANDS]; rtx operand_reload_insns = 0; rtx other_operand_reload_insns = 0; rtx other_output_reload_insns[MAX_RECOG_OPERANDS]; @@ -5717,7 +5967,9 @@ emit_reload_insns (insn) for (j = 0; j < reload_n_operands; j++) input_reload_insns[j] = input_address_reload_insns[j] + = inpaddr_address_reload_insns[j] = output_reload_insns[j] = output_address_reload_insns[j] + = outaddr_address_reload_insns[j] = other_output_reload_insns[j] = 0; /* Now output the instructions to copy the data into and out of the @@ -5912,9 +6164,15 @@ emit_reload_insns (insn) case RELOAD_FOR_INPUT_ADDRESS: where = &input_address_reload_insns[reload_opnum[j]]; break; + case RELOAD_FOR_INPADDR_ADDRESS: + where = &inpaddr_address_reload_insns[reload_opnum[j]]; + break; case RELOAD_FOR_OUTPUT_ADDRESS: where = &output_address_reload_insns[reload_opnum[j]]; break; + case RELOAD_FOR_OUTADDR_ADDRESS: + where = &outaddr_address_reload_insns[reload_opnum[j]]; + break; case RELOAD_FOR_OPERAND_ADDRESS: where = &operand_reload_insns; break; @@ -6517,8 +6775,9 @@ emit_reload_insns (insn) RELOAD_OTHER reloads. - For each operand, any RELOAD_FOR_INPUT_ADDRESS reloads followed by - the RELOAD_FOR_INPUT reload for the operand. + For each operand, any RELOAD_FOR_INPADDR_ADDRESS reloads followed + by any RELOAD_FOR_INPUT_ADDRESS reloads followed by the + RELOAD_FOR_INPUT reload for the operand. RELOAD_FOR_OPADDR_ADDRS reloads. @@ -6526,16 +6785,18 @@ emit_reload_insns (insn) After the insn being reloaded, we write the following: - For each operand, any RELOAD_FOR_OUTPUT_ADDRESS reload followed by - the RELOAD_FOR_OUTPUT reload, followed by any RELOAD_OTHER output - reloads for the operand. The RELOAD_OTHER output reloads are output - in descending order by reload number. */ + For each operand, any RELOAD_FOR_OUTADDR_ADDRESS reloads followed + by any RELOAD_FOR_OUTPUT_ADDRESS reload followed by the + RELOAD_FOR_OUTPUT reload, followed by any RELOAD_OTHER output + reloads for the operand. The RELOAD_OTHER output reloads are + output in descending order by reload number. */ emit_insns_before (other_input_address_reload_insns, before_insn); emit_insns_before (other_input_reload_insns, before_insn); for (j = 0; j < reload_n_operands; j++) { + emit_insns_before (inpaddr_address_reload_insns[j], before_insn); emit_insns_before (input_address_reload_insns[j], before_insn); emit_insns_before (input_reload_insns[j], before_insn); } @@ -6545,6 +6806,7 @@ emit_reload_insns (insn) for (j = 0; j < reload_n_operands; j++) { + emit_insns_before (outaddr_address_reload_insns[j], following_insn); emit_insns_before (output_address_reload_insns[j], following_insn); emit_insns_before (output_reload_insns[j], following_insn); emit_insns_before (other_output_reload_insns[j], following_insn); @@ -7272,3 +7534,772 @@ count_occurrences (x, find) } return count; } + +/* This array holds values which are equivalent to a hard register + during reload_cse_regs. Each array element is an EXPR_LIST of + values. Each time a hard register is set, we set the corresponding + array element to the value. Each time a hard register is copied + into memory, we add the memory location to the corresponding array + element. We don't store values or memory addresses with side + effects in this array. + + If the value is a CONST_INT, then the mode of the containing + EXPR_LIST is the mode in which that CONST_INT was referenced. + + We sometimes clobber a specific entry in a list. In that case, we + just set XEXP (list-entry, 0) to 0. */ + +static rtx *reg_values; + +/* This is a preallocated REG rtx which we use as a temporary in + reload_cse_invalidate_regno, so that we don't need to allocate a + new one each time through a loop in that function. */ + +static rtx invalidate_regno_rtx; + +/* Invalidate any entries in reg_values which depend on REGNO, + including those for REGNO itself. This is called if REGNO is + changing. If CLOBBER is true, then always forget anything we + currently know about REGNO. MODE is the mode of the assignment to + REGNO, which is used to determine how many hard registers are being + changed. If MODE is VOIDmode, then only REGNO is being changed; + this is used when invalidating call clobbered registers across a + call. */ + +static void +reload_cse_invalidate_regno (regno, mode, clobber) + int regno; + enum machine_mode mode; + int clobber; +{ + int endregno; + register int i; + + /* Our callers don't always go through true_regnum; we may see a + pseudo-register here from a CLOBBER or the like. We probably + won't ever see a pseudo-register that has a real register number, + for we check anyhow for safety. */ + if (regno >= FIRST_PSEUDO_REGISTER) + regno = reg_renumber[regno]; + if (regno < 0) + return; + + if (mode == VOIDmode) + endregno = regno + 1; + else + endregno = regno + HARD_REGNO_NREGS (regno, mode); + + if (clobber) + for (i = regno; i < endregno; i++) + reg_values[i] = 0; + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + { + rtx x; + + for (x = reg_values[i]; x; x = XEXP (x, 1)) + { + if (XEXP (x, 0) != 0 + && refers_to_regno_p (regno, endregno, XEXP (x, 0), NULL_RTX)) + { + /* If this is the only entry on the list, clear + reg_values[i]. Otherwise, just clear this entry on + the list. */ + if (XEXP (x, 1) == 0 && x == reg_values[i]) + { + reg_values[i] = 0; + break; + } + XEXP (x, 0) = 0; + } + } + } + + /* We must look at earlier registers, in case REGNO is part of a + multi word value but is not the first register. If an earlier + register has a value in a mode which overlaps REGNO, then we must + invalidate that earlier register. Note that we do not need to + check REGNO or later registers (we must not check REGNO itself, + because we would incorrectly conclude that there was a conflict). */ + + for (i = 0; i < regno; i++) + { + rtx x; + + for (x = reg_values[i]; x; x = XEXP (x, 1)) + { + if (XEXP (x, 0) != 0) + { + PUT_MODE (invalidate_regno_rtx, GET_MODE (XEXP (x, 0))); + REGNO (invalidate_regno_rtx) = i; + if (refers_to_regno_p (regno, endregno, invalidate_regno_rtx, + NULL_PTR)) + { + reload_cse_invalidate_regno (i, VOIDmode, 1); + break; + } + } + } + } +} + +/* The memory at address (plus MEM_BASE MEM_OFFSET), where MEM_OFFSET + is a CONST_INT, is being changed. MEM_MODE is the mode of the + memory reference. Return whether this change will invalidate VAL. */ + +static int +reload_cse_mem_conflict_p (mem_base, mem_offset, mem_mode, val) + rtx mem_base; + rtx mem_offset; + enum machine_mode mem_mode; + rtx val; +{ + enum rtx_code code; + char *fmt; + int i; + + code = GET_CODE (val); + switch (code) + { + /* Get rid of a few simple cases quickly. */ + case REG: + case PC: + case CC0: + case SCRATCH: + case CONST: + case CONST_INT: + case CONST_DOUBLE: + case SYMBOL_REF: + case LABEL_REF: + return 0; + + case MEM: + { + rtx val_base, val_offset; + + if (mem_mode == BLKmode || GET_MODE (val) == BLKmode) + return 1; + + val_offset = const0_rtx; + val_base = eliminate_constant_term (XEXP (val, 0), &val_offset); + + /* If MEM_BASE and VAL_BASE are the same, but the offsets do + not overlap, then we do not have a conflict on this MEM. + For complete safety, we still need to check that VAL_BASE + itself does not contain an overlapping MEM. + + We can't simplify the check to just OFFSET + SIZE <= + OTHER_OFFSET, because SIZE might cause OFFSET to wrap from + positive to negative. If we used unsigned arithmetic, we + would have the same problem wrapping around zero. */ + + if (rtx_equal_p (mem_base, val_base) + && ((INTVAL (mem_offset) < INTVAL (val_offset) + && (INTVAL (mem_offset) + GET_MODE_SIZE (mem_mode) + <= INTVAL (val_offset))) + || (INTVAL (val_offset) < INTVAL (mem_offset) + && (INTVAL (val_offset) + GET_MODE_SIZE (GET_MODE (val)) + <= INTVAL (mem_offset))))) + return reload_cse_mem_conflict_p (mem_base, mem_offset, mem_mode, + val_base); + + return 1; + } + + default: + break; + } + + fmt = GET_RTX_FORMAT (code); + + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + { + if (reload_cse_mem_conflict_p (mem_base, mem_offset, mem_mode, + XEXP (val, i))) + return 1; + } + else if (fmt[i] == 'E') + { + int j; + + for (j = 0; j < XVECLEN (val, i); j++) + if (reload_cse_mem_conflict_p (mem_base, mem_offset, mem_mode, + XVECEXP (val, i, j))) + return 1; + } + } + + return 0; +} + +/* Invalidate any entries in reg_values which are changed because of a + store to MEM_RTX. If this is called because of a non-const call + instruction, MEM_RTX is (mem:BLK const0_rtx). */ + +static void +reload_cse_invalidate_mem (mem_rtx) + rtx mem_rtx; +{ + register int i; + rtx mem_base, mem_offset; + enum machine_mode mem_mode; + + /* We detect certain cases where memory addresses can not conflict: + if they use the same register, and the offsets do not overlap. */ + + mem_offset = const0_rtx; + mem_base = eliminate_constant_term (XEXP (mem_rtx, 0), &mem_offset); + mem_mode = GET_MODE (mem_rtx); + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + { + rtx x; + + for (x = reg_values[i]; x; x = XEXP (x, 1)) + { + if (XEXP (x, 0) != 0 + && reload_cse_mem_conflict_p (mem_base, mem_offset, mem_mode, + XEXP (x, 0))) + { + /* If this is the only entry on the list, clear + reg_values[i]. Otherwise, just clear this entry on + the list. */ + if (XEXP (x, 1) == 0 && x == reg_values[i]) + { + reg_values[i] = 0; + break; + } + XEXP (x, 0) = 0; + } + } + } +} + +/* Invalidate DEST, which is being assigned to or clobbered. The + second parameter exists so that this function can be passed to + note_stores; it is ignored. */ + +static void +reload_cse_invalidate_rtx (dest, ignore) + rtx dest; + rtx ignore; +{ + while (GET_CODE (dest) == STRICT_LOW_PART + || GET_CODE (dest) == SIGN_EXTRACT + || GET_CODE (dest) == ZERO_EXTRACT + || GET_CODE (dest) == SUBREG) + dest = XEXP (dest, 0); + + if (GET_CODE (dest) == REG) + reload_cse_invalidate_regno (REGNO (dest), GET_MODE (dest), 1); + else if (GET_CODE (dest) == MEM) + reload_cse_invalidate_mem (dest); +} + +/* Do a very simple CSE pass over the hard registers. + + This function detects no-op moves where we happened to assign two + different pseudo-registers to the same hard register, and then + copied one to the other. Reload will generate a useless + instruction copying a register to itself. + + This function also detects cases where we load a value from memory + into two different registers, and (if memory is more expensive than + registers) changes it to simply copy the first register into the + second register. */ + +static void +reload_cse_regs (first) + rtx first; +{ + char *firstobj; + rtx callmem; + register int i; + rtx insn; + + reg_values = (rtx *) alloca (FIRST_PSEUDO_REGISTER * sizeof (rtx)); + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + reg_values[i] = 0; + + /* Create our EXPR_LIST structures on reload_obstack, so that we can + free them when we are done. */ + push_obstacks (&reload_obstack, &reload_obstack); + firstobj = (char *) obstack_alloc (&reload_obstack, 0); + + /* We pass this to reload_cse_invalidate_mem to invalidate all of + memory for a non-const call instruction. */ + callmem = gen_rtx (MEM, BLKmode, const0_rtx); + + /* This is used in reload_cse_invalidate_regno to avoid consing a + new REG in a loop in that function. */ + invalidate_regno_rtx = gen_rtx (REG, VOIDmode, 0); + + for (insn = first; insn; insn = NEXT_INSN (insn)) + { + rtx body; + + if (GET_CODE (insn) == CODE_LABEL) + { + /* Forget all the register values at a code label. We don't + try to do anything clever around jumps. */ + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + reg_values[i] = 0; + + continue; + } + +#ifdef NON_SAVING_SETJMP + if (NON_SAVING_SETJMP && GET_CODE (insn) == NOTE + && NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP) + { + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + reg_values[i] = 0; + + continue; + } +#endif + + if (GET_RTX_CLASS (GET_CODE (insn)) != 'i') + continue; + + /* If this is a call instruction, forget anything stored in a + call clobbered register, or, if this is not a const call, in + memory. */ + if (GET_CODE (insn) == CALL_INSN) + { + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (call_used_regs[i]) + reload_cse_invalidate_regno (i, VOIDmode, 1); + + if (! CONST_CALL_P (insn)) + reload_cse_invalidate_mem (callmem); + } + + body = PATTERN (insn); + if (GET_CODE (body) == SET) + { + if (reload_cse_noop_set_p (body, insn)) + { + PUT_CODE (insn, NOTE); + NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (insn) = 0; + + /* We're done with this insn. */ + continue; + } + + reload_cse_simplify_set (body, insn); + reload_cse_record_set (body, body); + } + else if (GET_CODE (body) == PARALLEL) + { + int delete; + + /* If every action in a PARALLEL is a noop, we can delete + the entire PARALLEL. */ + for (i = XVECLEN (body, 0) - 1; i >= 0; --i) + if (GET_CODE (XVECEXP (body, 0, i)) != SET + || ! reload_cse_noop_set_p (XVECEXP (body, 0, i), insn)) + break; + if (i < 0) + { + PUT_CODE (insn, NOTE); + NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (insn) = 0; + + /* We're done with this insn. */ + continue; + } + + /* Look through the PARALLEL and record the values being + set, if possible. Also handle any CLOBBERs. */ + for (i = XVECLEN (body, 0) - 1; i >= 0; --i) + { + rtx x = XVECEXP (body, 0, i); + + if (GET_CODE (x) == SET) + reload_cse_record_set (x, body); + else + note_stores (x, reload_cse_invalidate_rtx); + } + } + else + note_stores (body, reload_cse_invalidate_rtx); + +#ifdef AUTO_INC_DEC + /* Clobber any registers which appear in REG_INC notes. We + could keep track of the changes to their values, but it is + unlikely to help. */ + { + rtx x; + + for (x = REG_NOTES (insn); x; x = XEXP (x, 1)) + if (REG_NOTE_KIND (x) == REG_INC) + reload_cse_invalidate_rtx (XEXP (x, 0), NULL_RTX); + } +#endif + + /* Look for any CLOBBERs in CALL_INSN_FUNCTION_USAGE, but only + after we have processed the insn. */ + if (GET_CODE (insn) == CALL_INSN) + { + rtx x; + + for (x = CALL_INSN_FUNCTION_USAGE (insn); x; x = XEXP (x, 1)) + if (GET_CODE (XEXP (x, 0)) == CLOBBER) + reload_cse_invalidate_rtx (XEXP (XEXP (x, 0), 0), NULL_RTX); + } + } + + /* Free all the temporary structures we created, and go back to the + regular obstacks. */ + obstack_free (&reload_obstack, firstobj); + pop_obstacks (); +} + +/* Return whether the values known for REGNO are equal to VAL. MODE + is the mode of the object that VAL is being copied to; this matters + if VAL is a CONST_INT. */ + +static int +reload_cse_regno_equal_p (regno, val, mode) + int regno; + rtx val; + enum machine_mode mode; +{ + rtx x; + + if (val == 0) + return 0; + + for (x = reg_values[regno]; x; x = XEXP (x, 1)) + if (XEXP (x, 0) != 0 + && rtx_equal_p (XEXP (x, 0), val) + && (GET_CODE (val) != CONST_INT + || mode == GET_MODE (x) + || (GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (x)) + && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode), + GET_MODE_BITSIZE (GET_MODE (x)))))) + return 1; + + return 0; +} + +/* See whether a single set is a noop. SET is the set instruction we + are should check, and INSN is the instruction from which it came. */ + +static int +reload_cse_noop_set_p (set, insn) + rtx set; + rtx insn; +{ + rtx src, dest; + enum machine_mode dest_mode; + int dreg, sreg; + int ret; + + src = SET_SRC (set); + dest = SET_DEST (set); + dest_mode = GET_MODE (dest); + + if (side_effects_p (src)) + return 0; + + dreg = true_regnum (dest); + sreg = true_regnum (src); + + /* Check for setting a register to itself. In this case, we don't + have to worry about REG_DEAD notes. */ + if (dreg >= 0 && dreg == sreg) + return 1; + + ret = 0; + if (dreg >= 0) + { + /* Check for setting a register to itself. */ + if (dreg == sreg) + ret = 1; + + /* Check for setting a register to a value which we already know + is in the register. */ + else if (reload_cse_regno_equal_p (dreg, src, dest_mode)) + ret = 1; + + /* Check for setting a register DREG to another register SREG + where SREG is equal to a value which is already in DREG. */ + else if (sreg >= 0) + { + rtx x; + + for (x = reg_values[sreg]; x; x = XEXP (x, 1)) + { + if (XEXP (x, 0) != 0 + && reload_cse_regno_equal_p (dreg, XEXP (x, 0), dest_mode)) + { + ret = 1; + break; + } + } + } + } + else if (GET_CODE (dest) == MEM) + { + /* Check for storing a register to memory when we know that the + register is equivalent to the memory location. */ + if (sreg >= 0 + && reload_cse_regno_equal_p (sreg, dest, dest_mode) + && ! side_effects_p (dest)) + ret = 1; + } + + /* If we can delete this SET, then we need to look for an earlier + REG_DEAD note on DREG, and remove it if it exists. */ + if (ret) + { + if (! find_regno_note (insn, REG_UNUSED, dreg)) + { + rtx trial; + + for (trial = prev_nonnote_insn (insn); + (trial + && GET_CODE (trial) != CODE_LABEL + && GET_CODE (trial) != BARRIER); + trial = prev_nonnote_insn (trial)) + { + if (find_regno_note (trial, REG_DEAD, dreg)) + { + remove_death (dreg, trial); + break; + } + } + } + } + + return ret; +} + +/* Try to simplify a single SET instruction. SET is the set pattern. + INSN is the instruction it came from. */ + +static void +reload_cse_simplify_set (set, insn) + rtx set; + rtx insn; +{ + int dreg; + rtx src; + enum machine_mode dest_mode; + enum reg_class dclass; + register int i; + + /* We only handle one case: if we set a register to a value which is + not a register, we try to find that value in some other register + and change the set into a register copy. */ + + dreg = true_regnum (SET_DEST (set)); + if (dreg < 0) + return; + + src = SET_SRC (set); + if (side_effects_p (src) || true_regnum (src) >= 0) + return; + + /* If memory loads are cheaper than register copies, don't change + them. */ + if (GET_CODE (src) == MEM && MEMORY_MOVE_COST (GET_MODE (src)) < 2) + return; + + dest_mode = GET_MODE (SET_DEST (set)); + dclass = REGNO_REG_CLASS (dreg); + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + { + if (i != dreg + && REGISTER_MOVE_COST (REGNO_REG_CLASS (i), dclass) == 2 + && reload_cse_regno_equal_p (i, src, dest_mode)) + { + int validated; + + /* Pop back to the real obstacks while changing the insn. */ + pop_obstacks (); + + validated = validate_change (insn, &SET_SRC (set), + gen_rtx (REG, dest_mode, i), 0); + + /* Go back to the obstack we are using for temporary + storage. */ + push_obstacks (&reload_obstack, &reload_obstack); + + if (validated) + { + /* We need to look for an earlier REG_DEAD note on I, + and remove it if it exists. */ + if (! find_regno_note (insn, REG_UNUSED, i)) + { + rtx trial; + + for (trial = prev_nonnote_insn (insn); + (trial + && GET_CODE (trial) != CODE_LABEL + && GET_CODE (trial) != BARRIER); + trial = prev_nonnote_insn (trial)) + { + if (find_regno_note (trial, REG_DEAD, i)) + { + remove_death (i, trial); + break; + } + } + } + + return; + } + } + } +} + +/* These two variables are used to pass information from + reload_cse_record_set to reload_cse_check_clobber. */ + +static int reload_cse_check_clobbered; +static rtx reload_cse_check_src; + +/* See if DEST overlaps with RELOAD_CSE_CHECK_SRC. If it does, set + RELOAD_CSE_CHECK_CLOBBERED. This is called via note_stores. The + second argument, which is passed by note_stores, is ignored. */ + +static void +reload_cse_check_clobber (dest, ignore) + rtx dest; + rtx ignore; +{ + if (reg_overlap_mentioned_p (dest, reload_cse_check_src)) + reload_cse_check_clobbered = 1; +} + +/* Record the result of a SET instruction. SET is the set pattern. + BODY is the pattern of the insn that it came from. */ + +static void +reload_cse_record_set (set, body) + rtx set; + rtx body; +{ + rtx dest, src; + int dreg, sreg; + enum machine_mode dest_mode; + + dest = SET_DEST (set); + src = SET_SRC (set); + dreg = true_regnum (dest); + sreg = true_regnum (src); + dest_mode = GET_MODE (dest); + + /* We can only handle an assignment to a register, or a store of a + register to a memory location. For other cases, we just clobber + the destination. We also have to just clobber if there are side + effects in SRC or DEST. */ + if ((dreg < 0 && GET_CODE (dest) != MEM) + || side_effects_p (src) + || side_effects_p (dest)) + { + reload_cse_invalidate_rtx (dest, NULL_RTX); + return; + } + +#ifdef HAVE_cc0 + /* We don't try to handle values involving CC, because it's a pain + to keep track of when they have to be invalidated. */ + if (reg_mentioned_p (cc0_rtx, src) + || reg_mentioned_p (cc0_rtx, dest)) + { + reload_cse_invalidate_rtx (dest, NULL_RTX); + return; + } +#endif + + /* If BODY is a PARALLEL, then we need to see whether the source of + SET is clobbered by some other instruction in the PARALLEL. */ + if (GET_CODE (body) == PARALLEL) + { + int i; + + for (i = XVECLEN (body, 0) - 1; i >= 0; --i) + { + rtx x; + + x = XVECEXP (body, 0, i); + if (x == set) + continue; + + reload_cse_check_clobbered = 0; + reload_cse_check_src = src; + note_stores (x, reload_cse_check_clobber); + if (reload_cse_check_clobbered) + { + reload_cse_invalidate_rtx (dest, NULL_RTX); + return; + } + } + } + + if (dreg >= 0) + { + int i; + + /* This is an assignment to a register. Update the value we + have stored for the register. */ + if (sreg >= 0) + { + rtx x; + + /* This is a copy from one register to another. Any values + which were valid for SREG are now valid for DREG. If the + mode changes, we use gen_lowpart_common to extract only + the part of the value that is copied. */ + reg_values[dreg] = 0; + for (x = reg_values[sreg]; x; x = XEXP (x, 1)) + { + rtx tmp; + + if (XEXP (x, 0) == 0) + continue; + if (dest_mode == GET_MODE (XEXP (x, 0))) + tmp = XEXP (x, 0); + else + tmp = gen_lowpart_common (dest_mode, XEXP (x, 0)); + if (tmp) + reg_values[dreg] = gen_rtx (EXPR_LIST, dest_mode, tmp, + reg_values[dreg]); + } + } + else + reg_values[dreg] = gen_rtx (EXPR_LIST, dest_mode, src, NULL_RTX); + + /* We've changed DREG, so invalidate any values held by other + registers that depend upon it. */ + reload_cse_invalidate_regno (dreg, dest_mode, 0); + + /* If this assignment changes more than one hard register, + forget anything we know about the others. */ + for (i = 1; i < HARD_REGNO_NREGS (dreg, dest_mode); i++) + reg_values[dreg + i] = 0; + } + else if (GET_CODE (dest) == MEM) + { + /* Invalidate conflicting memory locations. */ + reload_cse_invalidate_mem (dest); + + /* If we're storing a register to memory, add DEST to the list + in REG_VALUES. */ + if (sreg >= 0 && ! side_effects_p (dest)) + reg_values[sreg] = gen_rtx (EXPR_LIST, dest_mode, dest, + reg_values[sreg]); + } + else + { + /* We should have bailed out earlier. */ + abort (); + } +}