X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;ds=sidebyside;f=gcc%2Freload1.c;h=ed6a07d000bf279d908cbbdedf5212efab2e5edb;hb=9238bd6d3140e42e78f067948d12c288054a5cd3;hp=ad0f8f80588121d8bbac897ee52082556bd6995c;hpb=e87a940156e52427dd37528ad9555d4820406fce;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/reload1.c b/gcc/reload1.c index ad0f8f80588..ed6a07d000b 100644 --- a/gcc/reload1.c +++ b/gcc/reload1.c @@ -393,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. */ @@ -534,6 +545,11 @@ reload (first, global, dumpfile) register rtx insn; register struct elim_table *ep; + /* The two pointers used to track the true location of the memory used + for label offsets. */ + char *real_known_ptr = NULL_PTR; + int (*real_at_ptr)[NUM_ELIMINABLE_REGS]; + int something_changed; int something_needs_reloads; int something_needs_elimination; @@ -578,10 +594,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]); @@ -732,13 +756,17 @@ reload (first, global, dumpfile) num_labels = max_label_num () - get_first_label_num (); /* Allocate the tables used to store offset information at labels. */ - offsets_known_at = (char *) alloca (num_labels); - offsets_at + /* We used to use alloca here, but the size of what it would try to + allocate would occasionally cause it to exceed the stack limit and + cause a core dump. */ + real_known_ptr = xmalloc (num_labels); + real_at_ptr = (int (*)[NUM_ELIMINABLE_REGS]) - alloca (num_labels * NUM_ELIMINABLE_REGS * sizeof (int)); + xmalloc (num_labels * NUM_ELIMINABLE_REGS * sizeof (int)); - offsets_known_at -= get_first_label_num (); - offsets_at -= get_first_label_num (); + offsets_known_at = real_known_ptr - get_first_label_num (); + offsets_at + = (int (*)[NUM_ELIMINABLE_REGS]) (real_at_ptr - get_first_label_num ()); /* Alter each pseudo-reg rtx to contain its hard reg number. Assign stack slots to the pseudos that lack hard regs or equivalents. @@ -766,11 +794,15 @@ reload (first, global, dumpfile) in that case some pseudos might be in the wrong kind of hard reg. */ for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - if (reg_renumber[i] == -1 && reg_n_refs[i] != 0) + if (reg_renumber[i] == -1 && REG_N_REFS (i) != 0) break; if (i == max_regno && num_eliminable == 0 && ! caller_save_needed) - return; + { + free (real_known_ptr); + free (real_at_ptr); + return; + } #endif /* Compute the order of preference for hard registers to spill. @@ -2106,6 +2138,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 @@ -2131,6 +2167,11 @@ reload (first, global, dumpfile) reg_equiv_constant = 0; reg_equiv_memory_loc = 0; + if (real_known_ptr) + free (real_known_ptr); + if (real_at_ptr) + free (real_at_ptr); + if (scratch_list) free (scratch_list); scratch_list = 0; @@ -2448,7 +2489,7 @@ alter_reg (i, from_reg) allocate a stack slot for it. */ if (reg_renumber[i] < 0 - && reg_n_refs[i] > 0 + && REG_N_REFS (i) > 0 && reg_equiv_constant[i] == 0 && reg_equiv_memory_loc[i] == 0) { @@ -3636,14 +3677,14 @@ spill_hard_reg (regno, global, dumpfile, cant_eliminate) if (! cant_eliminate && basic_block_needs[0] - && reg_basic_block[i] >= 0 - && basic_block_needs[(int) class][reg_basic_block[i]] == 0) + && REG_BASIC_BLOCK (i) >= 0 + && basic_block_needs[(int) class][REG_BASIC_BLOCK (i)] == 0) { enum reg_class *p; for (p = reg_class_superclasses[(int) class]; *p != LIM_REG_CLASSES; p++) - if (basic_block_needs[(int) *p][reg_basic_block[i]] > 0) + if (basic_block_needs[(int) *p][REG_BASIC_BLOCK (i)] > 0) break; if (*p == LIM_REG_CLASSES) @@ -3804,12 +3845,12 @@ order_regs_for_reload (global) we're not going to be able to reallocate it, but we might if allocated by global alloc. */ if (global && reg_allocno[i] < 0) - hard_reg_n_uses[regno].uses += (reg_n_refs[i] + 1) / 2; + hard_reg_n_uses[regno].uses += (REG_N_REFS (i) + 1) / 2; - hard_reg_n_uses[regno++].uses += reg_n_refs[i]; + hard_reg_n_uses[regno++].uses += REG_N_REFS (i); } } - large += reg_n_refs[i]; + large += REG_N_REFS (i); } /* Now fixed registers (which cannot safely be used for reloading) @@ -4684,6 +4725,12 @@ 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 @@ -6212,8 +6259,8 @@ emit_reload_insns (insn) SET_DEST (PATTERN (temp)) = reloadreg; /* If these are the only uses of the pseudo reg, pretend for GDB it lives in the reload reg we used. */ - if (reg_n_deaths[REGNO (old)] == 1 - && reg_n_sets[REGNO (old)] == 1) + if (REG_N_DEATHS (REGNO (old)) == 1 + && REG_N_SETS (REGNO (old)) == 1) { reg_renumber[REGNO (old)] = REGNO (reload_reg_rtx[j]); alter_reg (REGNO (old), -1); @@ -7251,8 +7298,8 @@ delete_output_reload (insn, j, output_reload_insn) /* See if the pseudo reg has been completely replaced with reload regs. If so, delete the store insn and forget we had a stack slot for the pseudo. */ - else if (reg_n_deaths[REGNO (reg)] == 1 - && reg_basic_block[REGNO (reg)] >= 0 + else if (REG_N_DEATHS (REGNO (reg)) == 1 + && REG_BASIC_BLOCK (REGNO (reg)) >= 0 && find_regno_note (insn, REG_DEAD, REGNO (reg))) { rtx i2; @@ -7505,3 +7552,813 @@ 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_PTR)) + { + /* 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 (x)); + 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)) + /* On a big endian machine if the value spans more than + one register then this register holds the high part of + it and we can't use it. + + ??? We should also compare with the high part of the + value. */ + && !(WORDS_BIG_ENDIAN + && HARD_REGNO_NREGS (regno, GET_MODE (x)) > 1) + && 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)) + { + rtx tmp; + + if (XEXP (x, 0) == 0) + continue; + + if (dest_mode == GET_MODE (x)) + tmp = XEXP (x, 0); + else if (GET_MODE_BITSIZE (dest_mode) + < GET_MODE_BITSIZE (GET_MODE (x))) + tmp = gen_lowpart_common (dest_mode, XEXP (x, 0)); + else + continue; + + if (tmp + && reload_cse_regno_equal_p (dreg, tmp, 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, x; + 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); + + /* Some machines don't define AUTO_INC_DEC, but they still use push + instructions. We need to catch that case here in order to + invalidate the stack pointer correctly. Note that invalidating + the stack pointer is different from invalidating DEST. */ + x = dest; + while (GET_CODE (x) == SUBREG + || GET_CODE (x) == ZERO_EXTRACT + || GET_CODE (x) == SIGN_EXTRACT + || GET_CODE (x) == STRICT_LOW_PART) + x = XEXP (x, 0); + if (push_operand (x, GET_MODE (x))) + { + reload_cse_invalidate_rtx (stack_pointer_rtx, NULL_RTX); + reload_cse_invalidate_rtx (dest, NULL_RTX); + return; + } + + /* 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 if (GET_MODE_BITSIZE (dest_mode) + > GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0)))) + continue; + 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 (); + } +}