diff options
Diffstat (limited to 'toolchain-layer/recipes-devtools/gcc/gcc-4.5/linaro/gcc-4.5-linaro-r99439.patch')
-rw-r--r-- | toolchain-layer/recipes-devtools/gcc/gcc-4.5/linaro/gcc-4.5-linaro-r99439.patch | 1500 |
1 files changed, 1500 insertions, 0 deletions
diff --git a/toolchain-layer/recipes-devtools/gcc/gcc-4.5/linaro/gcc-4.5-linaro-r99439.patch b/toolchain-layer/recipes-devtools/gcc/gcc-4.5/linaro/gcc-4.5-linaro-r99439.patch new file mode 100644 index 0000000000..cec7f57d47 --- /dev/null +++ b/toolchain-layer/recipes-devtools/gcc/gcc-4.5/linaro/gcc-4.5-linaro-r99439.patch @@ -0,0 +1,1500 @@ +2010-11-16 Chung-Lin Tang <cltang@codesourcery.com> + + 2010-07-21 Richard Henderson <rth@redhat.com> + + gcc/ + * config/i386/i386.c (setup_incoming_varargs_64): Emit a simple + comparison for avoiding xmm register saves. Emit the xmm register + saves explicitly. + * config/i386/i386.md (UNSPEC_SSE_PROLOGUE_SAVE): Remove. + (UNSPEC_SSE_PROLOGUE_SAVE_LOW): Remove. + (sse_prologue_save, sse_prologue_save_insn1, sse_prologue_save_insn): + Remove patterns and the associated splitters. + + 2010-07-22 Richard Henderson <rth@redhat.com> + + gcc/ + PR target/45027 + * config/i386/i386.c (setup_incoming_varargs_64): Force the use + of V4SFmode for the SSE saves; increase stack alignment if needed. + +2010-11-16 Chung-Lin Tang <cltang@codesourcery.com> + + Re-merge, backport from mainline: + + 2010-07-15 Bernd Schmidt <bernds@codesourcery.com> + + gcc/ + * postreload.c (last_label_ruid, first_index_reg, last_index_reg): + New static variables. + (reload_combine_recognize_pattern): New static function, broken out + of reload_combine. + (reload_combine): Use it. Only initialize first_index_reg and + last_index_reg once. + + 2010-07-17 Bernd Schmidt <bernds@codesourcery.com> + + PR target/42235 + gcc/ + * postreload.c (reload_cse_move2add): Return bool, true if anything. + changed. All callers changed. + (move2add_use_add2_insn): Likewise. + (move2add_use_add3_insn): Likewise. + (reload_cse_regs): If reload_cse_move2add changed anything, rerun + reload_combine. + (RELOAD_COMBINE_MAX_USES): Bump to 16. + (last_jump_ruid): New static variable. + (struct reg_use): New members CONTAINING_MEM and RUID. + (reg_state): New members ALL_OFFSETS_MATCH and REAL_STORE_RUID. + (reload_combine_split_one_ruid, reload_combine_split_ruids, + reload_combine_purge_insn_uses, reload_combine_closest_single_use + reload_combine_purge_reg_uses_after_ruid, + reload_combine_recognize_const_pattern): New static functions. + (reload_combine_recognize_pattern): Verify that ALL_OFFSETS_MATCH + is true for our reg and that we have available index regs. + (reload_combine_note_use): New args RUID and CONTAINING_MEM. All + callers changed. Use them to initialize fields in struct reg_use. + (reload_combine): Initialize last_jump_ruid. Be careful when to + take PREV_INSN of the scanned insn. Update REAL_STORE_RUID fields. + Call reload_combine_recognize_const_pattern. + (reload_combine_note_store): Update REAL_STORE_RUID field. + + gcc/testsuite/ + * gcc.target/arm/pr42235.c: New test. + + 2010-07-19 Bernd Schmidt <bernds@codesourcery.com> + + gcc/ + * postreload.c (reload_combine_closest_single_use): Ignore the + number of uses for DEBUG_INSNs. + (fixup_debug_insns): New static function. + (reload_combine_recognize_const_pattern): Use it. Don't let the + main loop be affected by DEBUG_INSNs. + Really disallow moving adds past a jump insn. + (reload_combine_recognize_pattern): Don't update use_ruid here. + (reload_combine_note_use): Do it here. + (reload_combine): Use control_flow_insn_p rather than JUMP_P. + + 2010-07-20 Bernd Schmidt <bernds@codesourcery.com> + + gcc/ + * postreload.c (fixup_debug_insns): Remove arg REGNO. New args + FROM and TO. All callers changed. Don't look for tracked uses, + just scan the RTL for DEBUG_INSNs and substitute. + (reload_combine_recognize_pattern): Call fixup_debug_insns. + (reload_combine): Ignore DEBUG_INSNs. + + 2010-07-22 Bernd Schmidt <bernds@codesourcery.com> + + PR bootstrap/44970 + PR middle-end/45009 + gcc/ + * postreload.c: Include "target.h". + (reload_combine_closest_single_use): Don't take DEBUG_INSNs + into account. + (fixup_debug_insns): Don't copy the rtx. + (reload_combine_recognize_const_pattern): DEBUG_INSNs can't have uses. + Don't copy when replacing. Call fixup_debug_insns in the case where + we merged one add with another. + (reload_combine_recognize_pattern): Fail if there aren't any uses. + Try harder to determine whether we're picking a valid index register. + Don't set store_ruid for an insn we're going to scan in the + next iteration. + (reload_combine): Remove unused code. + (reload_combine_note_use): When updating use information for + an old insn, ignore a use that occurs after store_ruid. + * Makefile.in (postreload.o): Update dependencies. + + 2010-07-27 Bernd Schmidt <bernds@codesourcery.com> + + gcc/ + * postreload.c (reload_combine_recognize_const_pattern): Move test + for limiting the insn movement to the right scope. + + 2010-07-27 Bernd Schmidt <bernds@codesourcery.com> + + gcc/ + * postreload.c (try_replace_in_use): New static function. + (reload_combine_recognize_const_pattern): Use it here. Allow + substituting into a final add insn, and substituting into a memory + reference in an insn that sets the reg. + +=== modified file 'gcc/Makefile.in' +--- old/gcc/Makefile.in 2010-11-11 11:34:59 +0000 ++++ new/gcc/Makefile.in 2010-11-16 18:05:53 +0000 +@@ -3157,7 +3157,7 @@ + $(RTL_H) $(REAL_H) $(FLAGS_H) $(EXPR_H) $(OPTABS_H) reload.h $(REGS_H) \ + hard-reg-set.h insn-config.h $(BASIC_BLOCK_H) $(RECOG_H) output.h \ + $(FUNCTION_H) $(TOPLEV_H) cselib.h $(TM_P_H) $(EXCEPT_H) $(TREE_H) $(MACHMODE_H) \ +- $(OBSTACK_H) $(TIMEVAR_H) $(TREE_PASS_H) $(DF_H) $(DBGCNT_H) ++ $(OBSTACK_H) $(TARGET_H) $(TIMEVAR_H) $(TREE_PASS_H) $(DF_H) $(DBGCNT_H) + postreload-gcse.o : postreload-gcse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ + $(TM_H) $(RTL_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) insn-config.h \ + $(RECOG_H) $(EXPR_H) $(BASIC_BLOCK_H) $(FUNCTION_H) output.h $(TOPLEV_H) \ + +=== modified file 'gcc/config/i386/i386.c' +--- old/gcc/config/i386/i386.c 2010-09-30 20:24:54 +0000 ++++ new/gcc/config/i386/i386.c 2010-11-16 18:05:53 +0000 +@@ -6737,12 +6737,8 @@ + setup_incoming_varargs_64 (CUMULATIVE_ARGS *cum) + { + rtx save_area, mem; +- rtx label; +- rtx label_ref; +- rtx tmp_reg; +- rtx nsse_reg; + alias_set_type set; +- int i; ++ int i, max; + + /* GPR size of varargs save area. */ + if (cfun->va_list_gpr_size) +@@ -6752,7 +6748,7 @@ + + /* FPR size of varargs save area. We don't need it if we don't pass + anything in SSE registers. */ +- if (cum->sse_nregs && cfun->va_list_fpr_size) ++ if (TARGET_SSE && cfun->va_list_fpr_size) + ix86_varargs_fpr_size = X86_64_SSE_REGPARM_MAX * 16; + else + ix86_varargs_fpr_size = 0; +@@ -6763,10 +6759,11 @@ + save_area = frame_pointer_rtx; + set = get_varargs_alias_set (); + +- for (i = cum->regno; +- i < X86_64_REGPARM_MAX +- && i < cum->regno + cfun->va_list_gpr_size / UNITS_PER_WORD; +- i++) ++ max = cum->regno + cfun->va_list_gpr_size / UNITS_PER_WORD; ++ if (max > X86_64_REGPARM_MAX) ++ max = X86_64_REGPARM_MAX; ++ ++ for (i = cum->regno; i < max; i++) + { + mem = gen_rtx_MEM (Pmode, + plus_constant (save_area, i * UNITS_PER_WORD)); +@@ -6778,62 +6775,42 @@ + + if (ix86_varargs_fpr_size) + { +- /* Stack must be aligned to 16byte for FP register save area. */ +- if (crtl->stack_alignment_needed < 128) +- crtl->stack_alignment_needed = 128; ++ enum machine_mode smode; ++ rtx label, test; + + /* Now emit code to save SSE registers. The AX parameter contains number +- of SSE parameter registers used to call this function. We use +- sse_prologue_save insn template that produces computed jump across +- SSE saves. We need some preparation work to get this working. */ ++ of SSE parameter registers used to call this function, though all we ++ actually check here is the zero/non-zero status. */ + + label = gen_label_rtx (); +- label_ref = gen_rtx_LABEL_REF (Pmode, label); +- +- /* Compute address to jump to : +- label - eax*4 + nnamed_sse_arguments*4 Or +- label - eax*5 + nnamed_sse_arguments*5 for AVX. */ +- tmp_reg = gen_reg_rtx (Pmode); +- nsse_reg = gen_reg_rtx (Pmode); +- emit_insn (gen_zero_extendqidi2 (nsse_reg, gen_rtx_REG (QImode, AX_REG))); +- emit_insn (gen_rtx_SET (VOIDmode, tmp_reg, +- gen_rtx_MULT (Pmode, nsse_reg, +- GEN_INT (4)))); +- +- /* vmovaps is one byte longer than movaps. */ +- if (TARGET_AVX) +- emit_insn (gen_rtx_SET (VOIDmode, tmp_reg, +- gen_rtx_PLUS (Pmode, tmp_reg, +- nsse_reg))); +- +- if (cum->sse_regno) +- emit_move_insn +- (nsse_reg, +- gen_rtx_CONST (DImode, +- gen_rtx_PLUS (DImode, +- label_ref, +- GEN_INT (cum->sse_regno +- * (TARGET_AVX ? 5 : 4))))); +- else +- emit_move_insn (nsse_reg, label_ref); +- emit_insn (gen_subdi3 (nsse_reg, nsse_reg, tmp_reg)); +- +- /* Compute address of memory block we save into. We always use pointer +- pointing 127 bytes after first byte to store - this is needed to keep +- instruction size limited by 4 bytes (5 bytes for AVX) with one +- byte displacement. */ +- tmp_reg = gen_reg_rtx (Pmode); +- emit_insn (gen_rtx_SET (VOIDmode, tmp_reg, +- plus_constant (save_area, +- ix86_varargs_gpr_size + 127))); +- mem = gen_rtx_MEM (BLKmode, plus_constant (tmp_reg, -127)); +- MEM_NOTRAP_P (mem) = 1; +- set_mem_alias_set (mem, set); +- set_mem_align (mem, BITS_PER_WORD); +- +- /* And finally do the dirty job! */ +- emit_insn (gen_sse_prologue_save (mem, nsse_reg, +- GEN_INT (cum->sse_regno), label)); ++ test = gen_rtx_EQ (VOIDmode, gen_rtx_REG (QImode, AX_REG), const0_rtx); ++ emit_jump_insn (gen_cbranchqi4 (test, XEXP (test, 0), XEXP (test, 1), ++ label)); ++ ++ /* ??? If !TARGET_SSE_TYPELESS_STORES, would we perform better if ++ we used movdqa (i.e. TImode) instead? Perhaps even better would ++ be if we could determine the real mode of the data, via a hook ++ into pass_stdarg. Ignore all that for now. */ ++ smode = V4SFmode; ++ if (crtl->stack_alignment_needed < GET_MODE_ALIGNMENT (smode)) ++ crtl->stack_alignment_needed = GET_MODE_ALIGNMENT (smode); ++ ++ max = cum->sse_regno + cfun->va_list_fpr_size / 16; ++ if (max > X86_64_SSE_REGPARM_MAX) ++ max = X86_64_SSE_REGPARM_MAX; ++ ++ for (i = cum->sse_regno; i < max; ++i) ++ { ++ mem = plus_constant (save_area, i * 16 + ix86_varargs_gpr_size); ++ mem = gen_rtx_MEM (smode, mem); ++ MEM_NOTRAP_P (mem) = 1; ++ set_mem_alias_set (mem, set); ++ set_mem_align (mem, GET_MODE_ALIGNMENT (smode)); ++ ++ emit_move_insn (mem, gen_rtx_REG (smode, SSE_REGNO (i))); ++ } ++ ++ emit_label (label); + } + } + + +=== modified file 'gcc/config/i386/i386.md' +--- old/gcc/config/i386/i386.md 2010-10-22 04:56:41 +0000 ++++ new/gcc/config/i386/i386.md 2010-11-27 15:24:12 +0000 +@@ -80,7 +80,6 @@ + ; Prologue support + (UNSPEC_STACK_ALLOC 11) + (UNSPEC_SET_GOT 12) +- (UNSPEC_SSE_PROLOGUE_SAVE 13) + (UNSPEC_REG_SAVE 14) + (UNSPEC_DEF_CFA 15) + (UNSPEC_SET_RIP 16) +@@ -20252,74 +20251,6 @@ + { return ASM_SHORT "0x0b0f"; } + [(set_attr "length" "2")]) + +-(define_expand "sse_prologue_save" +- [(parallel [(set (match_operand:BLK 0 "" "") +- (unspec:BLK [(reg:DI XMM0_REG) +- (reg:DI XMM1_REG) +- (reg:DI XMM2_REG) +- (reg:DI XMM3_REG) +- (reg:DI XMM4_REG) +- (reg:DI XMM5_REG) +- (reg:DI XMM6_REG) +- (reg:DI XMM7_REG)] UNSPEC_SSE_PROLOGUE_SAVE)) +- (use (match_operand:DI 1 "register_operand" "")) +- (use (match_operand:DI 2 "immediate_operand" "")) +- (use (label_ref:DI (match_operand 3 "" "")))])] +- "TARGET_64BIT" +- "") +- +-(define_insn "*sse_prologue_save_insn" +- [(set (mem:BLK (plus:DI (match_operand:DI 0 "register_operand" "R") +- (match_operand:DI 4 "const_int_operand" "n"))) +- (unspec:BLK [(reg:DI XMM0_REG) +- (reg:DI XMM1_REG) +- (reg:DI XMM2_REG) +- (reg:DI XMM3_REG) +- (reg:DI XMM4_REG) +- (reg:DI XMM5_REG) +- (reg:DI XMM6_REG) +- (reg:DI XMM7_REG)] UNSPEC_SSE_PROLOGUE_SAVE)) +- (use (match_operand:DI 1 "register_operand" "r")) +- (use (match_operand:DI 2 "const_int_operand" "i")) +- (use (label_ref:DI (match_operand 3 "" "X")))] +- "TARGET_64BIT +- && INTVAL (operands[4]) + X86_64_SSE_REGPARM_MAX * 16 - 16 < 128 +- && INTVAL (operands[4]) + INTVAL (operands[2]) * 16 >= -128" +-{ +- int i; +- operands[0] = gen_rtx_MEM (Pmode, +- gen_rtx_PLUS (Pmode, operands[0], operands[4])); +- /* VEX instruction with a REX prefix will #UD. */ +- if (TARGET_AVX && GET_CODE (XEXP (operands[0], 0)) != PLUS) +- gcc_unreachable (); +- +- output_asm_insn ("jmp\t%A1", operands); +- for (i = X86_64_SSE_REGPARM_MAX - 1; i >= INTVAL (operands[2]); i--) +- { +- operands[4] = adjust_address (operands[0], DImode, i*16); +- operands[5] = gen_rtx_REG (TImode, SSE_REGNO (i)); +- PUT_MODE (operands[4], TImode); +- if (GET_CODE (XEXP (operands[0], 0)) != PLUS) +- output_asm_insn ("rex", operands); +- output_asm_insn ("%vmovaps\t{%5, %4|%4, %5}", operands); +- } +- (*targetm.asm_out.internal_label) (asm_out_file, "L", +- CODE_LABEL_NUMBER (operands[3])); +- return ""; +-} +- [(set_attr "type" "other") +- (set_attr "length_immediate" "0") +- (set_attr "length_address" "0") +- (set (attr "length") +- (if_then_else +- (eq (symbol_ref "TARGET_AVX") (const_int 0)) +- (const_string "34") +- (const_string "42"))) +- (set_attr "memory" "store") +- (set_attr "modrm" "0") +- (set_attr "prefix" "maybe_vex") +- (set_attr "mode" "DI")]) +- + (define_expand "prefetch" + [(prefetch (match_operand 0 "address_operand" "") + (match_operand:SI 1 "const_int_operand" "") + +=== modified file 'gcc/postreload.c' +--- old/gcc/postreload.c 2010-11-08 22:08:43 +0000 ++++ new/gcc/postreload.c 2010-11-16 18:05:53 +0000 +@@ -44,6 +44,7 @@ + #include "toplev.h" + #include "except.h" + #include "tree.h" ++#include "target.h" + #include "timevar.h" + #include "tree-pass.h" + #include "df.h" +@@ -56,10 +57,10 @@ + static int reload_cse_simplify_operands (rtx, rtx); + + static void reload_combine (void); +-static void reload_combine_note_use (rtx *, rtx); ++static void reload_combine_note_use (rtx *, rtx, int, rtx); + static void reload_combine_note_store (rtx, const_rtx, void *); + +-static void reload_cse_move2add (rtx); ++static bool reload_cse_move2add (rtx); + static void move2add_note_store (rtx, const_rtx, void *); + + /* Call cse / combine like post-reload optimization phases. +@@ -67,11 +68,16 @@ + void + reload_cse_regs (rtx first ATTRIBUTE_UNUSED) + { ++ bool moves_converted; + reload_cse_regs_1 (first); + reload_combine (); +- reload_cse_move2add (first); ++ moves_converted = reload_cse_move2add (first); + if (flag_expensive_optimizations) +- reload_cse_regs_1 (first); ++ { ++ if (moves_converted) ++ reload_combine (); ++ reload_cse_regs_1 (first); ++ } + } + + /* See whether a single set SET is a noop. */ +@@ -660,30 +666,43 @@ + + /* The maximum number of uses of a register we can keep track of to + replace them with reg+reg addressing. */ +-#define RELOAD_COMBINE_MAX_USES 6 ++#define RELOAD_COMBINE_MAX_USES 16 + +-/* INSN is the insn where a register has been used, and USEP points to the +- location of the register within the rtl. */ +-struct reg_use { rtx insn, *usep; }; ++/* Describes a recorded use of a register. */ ++struct reg_use ++{ ++ /* The insn where a register has been used. */ ++ rtx insn; ++ /* Points to the memory reference enclosing the use, if any, NULL_RTX ++ otherwise. */ ++ rtx containing_mem; ++ /* Location of the register withing INSN. */ ++ rtx *usep; ++ /* The reverse uid of the insn. */ ++ int ruid; ++}; + + /* If the register is used in some unknown fashion, USE_INDEX is negative. + If it is dead, USE_INDEX is RELOAD_COMBINE_MAX_USES, and STORE_RUID +- indicates where it becomes live again. ++ indicates where it is first set or clobbered. + Otherwise, USE_INDEX is the index of the last encountered use of the +- register (which is first among these we have seen since we scan backwards), +- OFFSET contains the constant offset that is added to the register in +- all encountered uses, and USE_RUID indicates the first encountered, i.e. +- last, of these uses. ++ register (which is first among these we have seen since we scan backwards). ++ USE_RUID indicates the first encountered, i.e. last, of these uses. ++ If ALL_OFFSETS_MATCH is true, all encountered uses were inside a PLUS ++ with a constant offset; OFFSET contains this constant in that case. + STORE_RUID is always meaningful if we only want to use a value in a + register in a different place: it denotes the next insn in the insn +- stream (i.e. the last encountered) that sets or clobbers the register. */ ++ stream (i.e. the last encountered) that sets or clobbers the register. ++ REAL_STORE_RUID is similar, but clobbers are ignored when updating it. */ + static struct + { + struct reg_use reg_use[RELOAD_COMBINE_MAX_USES]; ++ rtx offset; + int use_index; +- rtx offset; + int store_ruid; ++ int real_store_ruid; + int use_ruid; ++ bool all_offsets_match; + } reg_state[FIRST_PSEUDO_REGISTER]; + + /* Reverse linear uid. This is increased in reload_combine while scanning +@@ -691,42 +710,548 @@ + and the store_ruid / use_ruid fields in reg_state. */ + static int reload_combine_ruid; + ++/* The RUID of the last label we encountered in reload_combine. */ ++static int last_label_ruid; ++ ++/* The RUID of the last jump we encountered in reload_combine. */ ++static int last_jump_ruid; ++ ++/* The register numbers of the first and last index register. A value of ++ -1 in LAST_INDEX_REG indicates that we've previously computed these ++ values and found no suitable index registers. */ ++static int first_index_reg = -1; ++static int last_index_reg; ++ + #define LABEL_LIVE(LABEL) \ + (label_live[CODE_LABEL_NUMBER (LABEL) - min_labelno]) + ++/* Subroutine of reload_combine_split_ruids, called to fix up a single ++ ruid pointed to by *PRUID if it is higher than SPLIT_RUID. */ ++ ++static inline void ++reload_combine_split_one_ruid (int *pruid, int split_ruid) ++{ ++ if (*pruid > split_ruid) ++ (*pruid)++; ++} ++ ++/* Called when we insert a new insn in a position we've already passed in ++ the scan. Examine all our state, increasing all ruids that are higher ++ than SPLIT_RUID by one in order to make room for a new insn. */ ++ ++static void ++reload_combine_split_ruids (int split_ruid) ++{ ++ unsigned i; ++ ++ reload_combine_split_one_ruid (&reload_combine_ruid, split_ruid); ++ reload_combine_split_one_ruid (&last_label_ruid, split_ruid); ++ reload_combine_split_one_ruid (&last_jump_ruid, split_ruid); ++ ++ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) ++ { ++ int j, idx = reg_state[i].use_index; ++ reload_combine_split_one_ruid (®_state[i].use_ruid, split_ruid); ++ reload_combine_split_one_ruid (®_state[i].store_ruid, split_ruid); ++ reload_combine_split_one_ruid (®_state[i].real_store_ruid, ++ split_ruid); ++ if (idx < 0) ++ continue; ++ for (j = idx; j < RELOAD_COMBINE_MAX_USES; j++) ++ { ++ reload_combine_split_one_ruid (®_state[i].reg_use[j].ruid, ++ split_ruid); ++ } ++ } ++} ++ ++/* Called when we are about to rescan a previously encountered insn with ++ reload_combine_note_use after modifying some part of it. This clears all ++ information about uses in that particular insn. */ ++ ++static void ++reload_combine_purge_insn_uses (rtx insn) ++{ ++ unsigned i; ++ ++ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) ++ { ++ int j, k, idx = reg_state[i].use_index; ++ if (idx < 0) ++ continue; ++ j = k = RELOAD_COMBINE_MAX_USES; ++ while (j-- > idx) ++ { ++ if (reg_state[i].reg_use[j].insn != insn) ++ { ++ k--; ++ if (k != j) ++ reg_state[i].reg_use[k] = reg_state[i].reg_use[j]; ++ } ++ } ++ reg_state[i].use_index = k; ++ } ++} ++ ++/* Called when we need to forget about all uses of REGNO after an insn ++ which is identified by RUID. */ ++ ++static void ++reload_combine_purge_reg_uses_after_ruid (unsigned regno, int ruid) ++{ ++ int j, k, idx = reg_state[regno].use_index; ++ if (idx < 0) ++ return; ++ j = k = RELOAD_COMBINE_MAX_USES; ++ while (j-- > idx) ++ { ++ if (reg_state[regno].reg_use[j].ruid >= ruid) ++ { ++ k--; ++ if (k != j) ++ reg_state[regno].reg_use[k] = reg_state[regno].reg_use[j]; ++ } ++ } ++ reg_state[regno].use_index = k; ++} ++ ++/* Find the use of REGNO with the ruid that is highest among those ++ lower than RUID_LIMIT, and return it if it is the only use of this ++ reg in the insn. Return NULL otherwise. */ ++ ++static struct reg_use * ++reload_combine_closest_single_use (unsigned regno, int ruid_limit) ++{ ++ int i, best_ruid = 0; ++ int use_idx = reg_state[regno].use_index; ++ struct reg_use *retval; ++ ++ if (use_idx < 0) ++ return NULL; ++ retval = NULL; ++ for (i = use_idx; i < RELOAD_COMBINE_MAX_USES; i++) ++ { ++ struct reg_use *use = reg_state[regno].reg_use + i; ++ int this_ruid = use->ruid; ++ if (this_ruid >= ruid_limit) ++ continue; ++ if (this_ruid > best_ruid) ++ { ++ best_ruid = this_ruid; ++ retval = use; ++ } ++ else if (this_ruid == best_ruid) ++ retval = NULL; ++ } ++ if (last_label_ruid >= best_ruid) ++ return NULL; ++ return retval; ++} ++ ++/* After we've moved an add insn, fix up any debug insns that occur ++ between the old location of the add and the new location. REG is ++ the destination register of the add insn; REPLACEMENT is the ++ SET_SRC of the add. FROM and TO specify the range in which we ++ should make this change on debug insns. */ ++ ++static void ++fixup_debug_insns (rtx reg, rtx replacement, rtx from, rtx to) ++{ ++ rtx insn; ++ for (insn = from; insn != to; insn = NEXT_INSN (insn)) ++ { ++ rtx t; ++ ++ if (!DEBUG_INSN_P (insn)) ++ continue; ++ ++ t = INSN_VAR_LOCATION_LOC (insn); ++ t = simplify_replace_rtx (t, reg, replacement); ++ validate_change (insn, &INSN_VAR_LOCATION_LOC (insn), t, 0); ++ } ++} ++ ++/* Subroutine of reload_combine_recognize_const_pattern. Try to replace REG ++ with SRC in the insn described by USE, taking costs into account. Return ++ true if we made the replacement. */ ++ ++static bool ++try_replace_in_use (struct reg_use *use, rtx reg, rtx src) ++{ ++ rtx use_insn = use->insn; ++ rtx mem = use->containing_mem; ++ bool speed = optimize_bb_for_speed_p (BLOCK_FOR_INSN (use_insn)); ++ ++ if (mem != NULL_RTX) ++ { ++ addr_space_t as = MEM_ADDR_SPACE (mem); ++ rtx oldaddr = XEXP (mem, 0); ++ rtx newaddr = NULL_RTX; ++ int old_cost = address_cost (oldaddr, GET_MODE (mem), as, speed); ++ int new_cost; ++ ++ newaddr = simplify_replace_rtx (oldaddr, reg, src); ++ if (memory_address_addr_space_p (GET_MODE (mem), newaddr, as)) ++ { ++ XEXP (mem, 0) = newaddr; ++ new_cost = address_cost (newaddr, GET_MODE (mem), as, speed); ++ XEXP (mem, 0) = oldaddr; ++ if (new_cost <= old_cost ++ && validate_change (use_insn, ++ &XEXP (mem, 0), newaddr, 0)) ++ return true; ++ } ++ } ++ else ++ { ++ rtx new_set = single_set (use_insn); ++ if (new_set ++ && REG_P (SET_DEST (new_set)) ++ && GET_CODE (SET_SRC (new_set)) == PLUS ++ && REG_P (XEXP (SET_SRC (new_set), 0)) ++ && CONSTANT_P (XEXP (SET_SRC (new_set), 1))) ++ { ++ rtx new_src; ++ int old_cost = rtx_cost (SET_SRC (new_set), SET, speed); ++ ++ gcc_assert (rtx_equal_p (XEXP (SET_SRC (new_set), 0), reg)); ++ new_src = simplify_replace_rtx (SET_SRC (new_set), reg, src); ++ ++ if (rtx_cost (new_src, SET, speed) <= old_cost ++ && validate_change (use_insn, &SET_SRC (new_set), ++ new_src, 0)) ++ return true; ++ } ++ } ++ return false; ++} ++ ++/* Called by reload_combine when scanning INSN. This function tries to detect ++ patterns where a constant is added to a register, and the result is used ++ in an address. ++ Return true if no further processing is needed on INSN; false if it wasn't ++ recognized and should be handled normally. */ ++ ++static bool ++reload_combine_recognize_const_pattern (rtx insn) ++{ ++ int from_ruid = reload_combine_ruid; ++ rtx set, pat, reg, src, addreg; ++ unsigned int regno; ++ struct reg_use *use; ++ bool must_move_add; ++ rtx add_moved_after_insn = NULL_RTX; ++ int add_moved_after_ruid = 0; ++ int clobbered_regno = -1; ++ ++ set = single_set (insn); ++ if (set == NULL_RTX) ++ return false; ++ ++ reg = SET_DEST (set); ++ src = SET_SRC (set); ++ if (!REG_P (reg) ++ || hard_regno_nregs[REGNO (reg)][GET_MODE (reg)] != 1 ++ || GET_MODE (reg) != Pmode ++ || reg == stack_pointer_rtx) ++ return false; ++ ++ regno = REGNO (reg); ++ ++ /* We look for a REG1 = REG2 + CONSTANT insn, followed by either ++ uses of REG1 inside an address, or inside another add insn. If ++ possible and profitable, merge the addition into subsequent ++ uses. */ ++ if (GET_CODE (src) != PLUS ++ || !REG_P (XEXP (src, 0)) ++ || !CONSTANT_P (XEXP (src, 1))) ++ return false; ++ ++ addreg = XEXP (src, 0); ++ must_move_add = rtx_equal_p (reg, addreg); ++ ++ pat = PATTERN (insn); ++ if (must_move_add && set != pat) ++ { ++ /* We have to be careful when moving the add; apart from the ++ single_set there may also be clobbers. Recognize one special ++ case, that of one clobber alongside the set (likely a clobber ++ of the CC register). */ ++ gcc_assert (GET_CODE (PATTERN (insn)) == PARALLEL); ++ if (XVECLEN (pat, 0) != 2 || XVECEXP (pat, 0, 0) != set ++ || GET_CODE (XVECEXP (pat, 0, 1)) != CLOBBER ++ || !REG_P (XEXP (XVECEXP (pat, 0, 1), 0))) ++ return false; ++ clobbered_regno = REGNO (XEXP (XVECEXP (pat, 0, 1), 0)); ++ } ++ ++ do ++ { ++ use = reload_combine_closest_single_use (regno, from_ruid); ++ ++ if (use) ++ /* Start the search for the next use from here. */ ++ from_ruid = use->ruid; ++ ++ if (use && GET_MODE (*use->usep) == Pmode) ++ { ++ bool delete_add = false; ++ rtx use_insn = use->insn; ++ int use_ruid = use->ruid; ++ ++ /* Avoid moving the add insn past a jump. */ ++ if (must_move_add && use_ruid <= last_jump_ruid) ++ break; ++ ++ /* If the add clobbers another hard reg in parallel, don't move ++ it past a real set of this hard reg. */ ++ if (must_move_add && clobbered_regno >= 0 ++ && reg_state[clobbered_regno].real_store_ruid >= use_ruid) ++ break; ++ ++ gcc_assert (reg_state[regno].store_ruid <= use_ruid); ++ /* Avoid moving a use of ADDREG past a point where it is stored. */ ++ if (reg_state[REGNO (addreg)].store_ruid > use_ruid) ++ break; ++ ++ /* We also must not move the addition past an insn that sets ++ the same register, unless we can combine two add insns. */ ++ if (must_move_add && reg_state[regno].store_ruid == use_ruid) ++ { ++ if (use->containing_mem == NULL_RTX) ++ delete_add = true; ++ else ++ break; ++ } ++ ++ if (try_replace_in_use (use, reg, src)) ++ { ++ reload_combine_purge_insn_uses (use_insn); ++ reload_combine_note_use (&PATTERN (use_insn), use_insn, ++ use_ruid, NULL_RTX); ++ ++ if (delete_add) ++ { ++ fixup_debug_insns (reg, src, insn, use_insn); ++ delete_insn (insn); ++ return true; ++ } ++ if (must_move_add) ++ { ++ add_moved_after_insn = use_insn; ++ add_moved_after_ruid = use_ruid; ++ } ++ continue; ++ } ++ } ++ /* If we get here, we couldn't handle this use. */ ++ if (must_move_add) ++ break; ++ } ++ while (use); ++ ++ if (!must_move_add || add_moved_after_insn == NULL_RTX) ++ /* Process the add normally. */ ++ return false; ++ ++ fixup_debug_insns (reg, src, insn, add_moved_after_insn); ++ ++ reorder_insns (insn, insn, add_moved_after_insn); ++ reload_combine_purge_reg_uses_after_ruid (regno, add_moved_after_ruid); ++ reload_combine_split_ruids (add_moved_after_ruid - 1); ++ reload_combine_note_use (&PATTERN (insn), insn, ++ add_moved_after_ruid, NULL_RTX); ++ reg_state[regno].store_ruid = add_moved_after_ruid; ++ ++ return true; ++} ++ ++/* Called by reload_combine when scanning INSN. Try to detect a pattern we ++ can handle and improve. Return true if no further processing is needed on ++ INSN; false if it wasn't recognized and should be handled normally. */ ++ ++static bool ++reload_combine_recognize_pattern (rtx insn) ++{ ++ rtx set, reg, src; ++ unsigned int regno; ++ ++ set = single_set (insn); ++ if (set == NULL_RTX) ++ return false; ++ ++ reg = SET_DEST (set); ++ src = SET_SRC (set); ++ if (!REG_P (reg) ++ || hard_regno_nregs[REGNO (reg)][GET_MODE (reg)] != 1) ++ return false; ++ ++ regno = REGNO (reg); ++ ++ /* Look for (set (REGX) (CONST_INT)) ++ (set (REGX) (PLUS (REGX) (REGY))) ++ ... ++ ... (MEM (REGX)) ... ++ and convert it to ++ (set (REGZ) (CONST_INT)) ++ ... ++ ... (MEM (PLUS (REGZ) (REGY)))... . ++ ++ First, check that we have (set (REGX) (PLUS (REGX) (REGY))) ++ and that we know all uses of REGX before it dies. ++ Also, explicitly check that REGX != REGY; our life information ++ does not yet show whether REGY changes in this insn. */ ++ ++ if (GET_CODE (src) == PLUS ++ && reg_state[regno].all_offsets_match ++ && last_index_reg != -1 ++ && REG_P (XEXP (src, 1)) ++ && rtx_equal_p (XEXP (src, 0), reg) ++ && !rtx_equal_p (XEXP (src, 1), reg) ++ && reg_state[regno].use_index >= 0 ++ && reg_state[regno].use_index < RELOAD_COMBINE_MAX_USES ++ && last_label_ruid < reg_state[regno].use_ruid) ++ { ++ rtx base = XEXP (src, 1); ++ rtx prev = prev_nonnote_insn (insn); ++ rtx prev_set = prev ? single_set (prev) : NULL_RTX; ++ rtx index_reg = NULL_RTX; ++ rtx reg_sum = NULL_RTX; ++ int i; ++ ++ /* Now we need to set INDEX_REG to an index register (denoted as ++ REGZ in the illustration above) and REG_SUM to the expression ++ register+register that we want to use to substitute uses of REG ++ (typically in MEMs) with. First check REG and BASE for being ++ index registers; we can use them even if they are not dead. */ ++ if (TEST_HARD_REG_BIT (reg_class_contents[INDEX_REG_CLASS], regno) ++ || TEST_HARD_REG_BIT (reg_class_contents[INDEX_REG_CLASS], ++ REGNO (base))) ++ { ++ index_reg = reg; ++ reg_sum = src; ++ } ++ else ++ { ++ /* Otherwise, look for a free index register. Since we have ++ checked above that neither REG nor BASE are index registers, ++ if we find anything at all, it will be different from these ++ two registers. */ ++ for (i = first_index_reg; i <= last_index_reg; i++) ++ { ++ if (TEST_HARD_REG_BIT (reg_class_contents[INDEX_REG_CLASS], i) ++ && reg_state[i].use_index == RELOAD_COMBINE_MAX_USES ++ && reg_state[i].store_ruid <= reg_state[regno].use_ruid ++ && (call_used_regs[i] || df_regs_ever_live_p (i)) ++ && (!frame_pointer_needed || i != HARD_FRAME_POINTER_REGNUM) ++ && !fixed_regs[i] && !global_regs[i] ++ && hard_regno_nregs[i][GET_MODE (reg)] == 1 ++ && targetm.hard_regno_scratch_ok (i)) ++ { ++ index_reg = gen_rtx_REG (GET_MODE (reg), i); ++ reg_sum = gen_rtx_PLUS (GET_MODE (reg), index_reg, base); ++ break; ++ } ++ } ++ } ++ ++ /* Check that PREV_SET is indeed (set (REGX) (CONST_INT)) and that ++ (REGY), i.e. BASE, is not clobbered before the last use we'll ++ create. */ ++ if (reg_sum ++ && prev_set ++ && CONST_INT_P (SET_SRC (prev_set)) ++ && rtx_equal_p (SET_DEST (prev_set), reg) ++ && (reg_state[REGNO (base)].store_ruid ++ <= reg_state[regno].use_ruid)) ++ { ++ /* Change destination register and, if necessary, the constant ++ value in PREV, the constant loading instruction. */ ++ validate_change (prev, &SET_DEST (prev_set), index_reg, 1); ++ if (reg_state[regno].offset != const0_rtx) ++ validate_change (prev, ++ &SET_SRC (prev_set), ++ GEN_INT (INTVAL (SET_SRC (prev_set)) ++ + INTVAL (reg_state[regno].offset)), ++ 1); ++ ++ /* Now for every use of REG that we have recorded, replace REG ++ with REG_SUM. */ ++ for (i = reg_state[regno].use_index; ++ i < RELOAD_COMBINE_MAX_USES; i++) ++ validate_unshare_change (reg_state[regno].reg_use[i].insn, ++ reg_state[regno].reg_use[i].usep, ++ /* Each change must have its own ++ replacement. */ ++ reg_sum, 1); ++ ++ if (apply_change_group ()) ++ { ++ struct reg_use *lowest_ruid = NULL; ++ ++ /* For every new use of REG_SUM, we have to record the use ++ of BASE therein, i.e. operand 1. */ ++ for (i = reg_state[regno].use_index; ++ i < RELOAD_COMBINE_MAX_USES; i++) ++ { ++ struct reg_use *use = reg_state[regno].reg_use + i; ++ reload_combine_note_use (&XEXP (*use->usep, 1), use->insn, ++ use->ruid, use->containing_mem); ++ if (lowest_ruid == NULL || use->ruid < lowest_ruid->ruid) ++ lowest_ruid = use; ++ } ++ ++ fixup_debug_insns (reg, reg_sum, insn, lowest_ruid->insn); ++ ++ /* Delete the reg-reg addition. */ ++ delete_insn (insn); ++ ++ if (reg_state[regno].offset != const0_rtx) ++ /* Previous REG_EQUIV / REG_EQUAL notes for PREV ++ are now invalid. */ ++ remove_reg_equal_equiv_notes (prev); ++ ++ reg_state[regno].use_index = RELOAD_COMBINE_MAX_USES; ++ return true; ++ } ++ } ++ } ++ return false; ++} ++ + static void + reload_combine (void) + { +- rtx insn, set; +- int first_index_reg = -1; +- int last_index_reg = 0; ++ rtx insn, prev; + int i; + basic_block bb; + unsigned int r; +- int last_label_ruid; + int min_labelno, n_labels; + HARD_REG_SET ever_live_at_start, *label_live; + +- /* If reg+reg can be used in offsetable memory addresses, the main chunk of +- reload has already used it where appropriate, so there is no use in +- trying to generate it now. */ +- if (double_reg_address_ok && INDEX_REG_CLASS != NO_REGS) +- return; +- + /* To avoid wasting too much time later searching for an index register, + determine the minimum and maximum index register numbers. */ +- for (r = 0; r < FIRST_PSEUDO_REGISTER; r++) +- if (TEST_HARD_REG_BIT (reg_class_contents[INDEX_REG_CLASS], r)) +- { +- if (first_index_reg == -1) +- first_index_reg = r; +- +- last_index_reg = r; +- } +- +- /* If no index register is available, we can quit now. */ +- if (first_index_reg == -1) +- return; ++ if (INDEX_REG_CLASS == NO_REGS) ++ last_index_reg = -1; ++ else if (first_index_reg == -1 && last_index_reg == 0) ++ { ++ for (r = 0; r < FIRST_PSEUDO_REGISTER; r++) ++ if (TEST_HARD_REG_BIT (reg_class_contents[INDEX_REG_CLASS], r)) ++ { ++ if (first_index_reg == -1) ++ first_index_reg = r; ++ ++ last_index_reg = r; ++ } ++ ++ /* If no index register is available, we can quit now. Set LAST_INDEX_REG ++ to -1 so we'll know to quit early the next time we get here. */ ++ if (first_index_reg == -1) ++ { ++ last_index_reg = -1; ++ return; ++ } ++ } + + /* Set up LABEL_LIVE and EVER_LIVE_AT_START. The register lifetime + information is a bit fuzzy immediately after reload, but it's +@@ -753,20 +1278,23 @@ + } + + /* Initialize last_label_ruid, reload_combine_ruid and reg_state. */ +- last_label_ruid = reload_combine_ruid = 0; ++ last_label_ruid = last_jump_ruid = reload_combine_ruid = 0; + for (r = 0; r < FIRST_PSEUDO_REGISTER; r++) + { +- reg_state[r].store_ruid = reload_combine_ruid; ++ reg_state[r].store_ruid = 0; ++ reg_state[r].real_store_ruid = 0; + if (fixed_regs[r]) + reg_state[r].use_index = -1; + else + reg_state[r].use_index = RELOAD_COMBINE_MAX_USES; + } + +- for (insn = get_last_insn (); insn; insn = PREV_INSN (insn)) ++ for (insn = get_last_insn (); insn; insn = prev) + { + rtx note; + ++ prev = PREV_INSN (insn); ++ + /* We cannot do our optimization across labels. Invalidating all the use + information we have would be costly, so we just note where the label + is and then later disable any optimization that would cross it. */ +@@ -777,141 +1305,17 @@ + if (! fixed_regs[r]) + reg_state[r].use_index = RELOAD_COMBINE_MAX_USES; + +- if (! INSN_P (insn)) ++ if (! NONDEBUG_INSN_P (insn)) + continue; + + reload_combine_ruid++; + +- /* Look for (set (REGX) (CONST_INT)) +- (set (REGX) (PLUS (REGX) (REGY))) +- ... +- ... (MEM (REGX)) ... +- and convert it to +- (set (REGZ) (CONST_INT)) +- ... +- ... (MEM (PLUS (REGZ) (REGY)))... . +- +- First, check that we have (set (REGX) (PLUS (REGX) (REGY))) +- and that we know all uses of REGX before it dies. +- Also, explicitly check that REGX != REGY; our life information +- does not yet show whether REGY changes in this insn. */ +- set = single_set (insn); +- if (set != NULL_RTX +- && REG_P (SET_DEST (set)) +- && (hard_regno_nregs[REGNO (SET_DEST (set))] +- [GET_MODE (SET_DEST (set))] +- == 1) +- && GET_CODE (SET_SRC (set)) == PLUS +- && REG_P (XEXP (SET_SRC (set), 1)) +- && rtx_equal_p (XEXP (SET_SRC (set), 0), SET_DEST (set)) +- && !rtx_equal_p (XEXP (SET_SRC (set), 1), SET_DEST (set)) +- && last_label_ruid < reg_state[REGNO (SET_DEST (set))].use_ruid) +- { +- rtx reg = SET_DEST (set); +- rtx plus = SET_SRC (set); +- rtx base = XEXP (plus, 1); +- rtx prev = prev_nonnote_nondebug_insn (insn); +- rtx prev_set = prev ? single_set (prev) : NULL_RTX; +- unsigned int regno = REGNO (reg); +- rtx index_reg = NULL_RTX; +- rtx reg_sum = NULL_RTX; +- +- /* Now we need to set INDEX_REG to an index register (denoted as +- REGZ in the illustration above) and REG_SUM to the expression +- register+register that we want to use to substitute uses of REG +- (typically in MEMs) with. First check REG and BASE for being +- index registers; we can use them even if they are not dead. */ +- if (TEST_HARD_REG_BIT (reg_class_contents[INDEX_REG_CLASS], regno) +- || TEST_HARD_REG_BIT (reg_class_contents[INDEX_REG_CLASS], +- REGNO (base))) +- { +- index_reg = reg; +- reg_sum = plus; +- } +- else +- { +- /* Otherwise, look for a free index register. Since we have +- checked above that neither REG nor BASE are index registers, +- if we find anything at all, it will be different from these +- two registers. */ +- for (i = first_index_reg; i <= last_index_reg; i++) +- { +- if (TEST_HARD_REG_BIT (reg_class_contents[INDEX_REG_CLASS], +- i) +- && reg_state[i].use_index == RELOAD_COMBINE_MAX_USES +- && reg_state[i].store_ruid <= reg_state[regno].use_ruid +- && hard_regno_nregs[i][GET_MODE (reg)] == 1) +- { +- index_reg = gen_rtx_REG (GET_MODE (reg), i); +- reg_sum = gen_rtx_PLUS (GET_MODE (reg), index_reg, base); +- break; +- } +- } +- } +- +- /* Check that PREV_SET is indeed (set (REGX) (CONST_INT)) and that +- (REGY), i.e. BASE, is not clobbered before the last use we'll +- create. */ +- if (reg_sum +- && prev_set +- && CONST_INT_P (SET_SRC (prev_set)) +- && rtx_equal_p (SET_DEST (prev_set), reg) +- && reg_state[regno].use_index >= 0 +- && (reg_state[REGNO (base)].store_ruid +- <= reg_state[regno].use_ruid)) +- { +- int i; +- +- /* Change destination register and, if necessary, the constant +- value in PREV, the constant loading instruction. */ +- validate_change (prev, &SET_DEST (prev_set), index_reg, 1); +- if (reg_state[regno].offset != const0_rtx) +- validate_change (prev, +- &SET_SRC (prev_set), +- GEN_INT (INTVAL (SET_SRC (prev_set)) +- + INTVAL (reg_state[regno].offset)), +- 1); +- +- /* Now for every use of REG that we have recorded, replace REG +- with REG_SUM. */ +- for (i = reg_state[regno].use_index; +- i < RELOAD_COMBINE_MAX_USES; i++) +- validate_unshare_change (reg_state[regno].reg_use[i].insn, +- reg_state[regno].reg_use[i].usep, +- /* Each change must have its own +- replacement. */ +- reg_sum, 1); +- +- if (apply_change_group ()) +- { +- /* For every new use of REG_SUM, we have to record the use +- of BASE therein, i.e. operand 1. */ +- for (i = reg_state[regno].use_index; +- i < RELOAD_COMBINE_MAX_USES; i++) +- reload_combine_note_use +- (&XEXP (*reg_state[regno].reg_use[i].usep, 1), +- reg_state[regno].reg_use[i].insn); +- +- if (reg_state[REGNO (base)].use_ruid +- > reg_state[regno].use_ruid) +- reg_state[REGNO (base)].use_ruid +- = reg_state[regno].use_ruid; +- +- /* Delete the reg-reg addition. */ +- delete_insn (insn); +- +- if (reg_state[regno].offset != const0_rtx) +- /* Previous REG_EQUIV / REG_EQUAL notes for PREV +- are now invalid. */ +- remove_reg_equal_equiv_notes (prev); +- +- reg_state[regno].use_index = RELOAD_COMBINE_MAX_USES; +- reg_state[REGNO (index_reg)].store_ruid +- = reload_combine_ruid; +- continue; +- } +- } +- } ++ if (control_flow_insn_p (insn)) ++ last_jump_ruid = reload_combine_ruid; ++ ++ if (reload_combine_recognize_const_pattern (insn) ++ || reload_combine_recognize_pattern (insn)) ++ continue; + + note_stores (PATTERN (insn), reload_combine_note_store, NULL); + +@@ -967,7 +1371,8 @@ + reg_state[i].use_index = -1; + } + +- reload_combine_note_use (&PATTERN (insn), insn); ++ reload_combine_note_use (&PATTERN (insn), insn, ++ reload_combine_ruid, NULL_RTX); + for (note = REG_NOTES (insn); note; note = XEXP (note, 1)) + { + if (REG_NOTE_KIND (note) == REG_INC +@@ -976,6 +1381,7 @@ + int regno = REGNO (XEXP (note, 0)); + + reg_state[regno].store_ruid = reload_combine_ruid; ++ reg_state[regno].real_store_ruid = reload_combine_ruid; + reg_state[regno].use_index = -1; + } + } +@@ -985,8 +1391,8 @@ + } + + /* Check if DST is a register or a subreg of a register; if it is, +- update reg_state[regno].store_ruid and reg_state[regno].use_index +- accordingly. Called via note_stores from reload_combine. */ ++ update store_ruid, real_store_ruid and use_index in the reg_state ++ structure accordingly. Called via note_stores from reload_combine. */ + + static void + reload_combine_note_store (rtx dst, const_rtx set, void *data ATTRIBUTE_UNUSED) +@@ -1010,14 +1416,14 @@ + /* note_stores might have stripped a STRICT_LOW_PART, so we have to be + careful with registers / register parts that are not full words. + Similarly for ZERO_EXTRACT. */ +- if (GET_CODE (set) != SET +- || GET_CODE (SET_DEST (set)) == ZERO_EXTRACT ++ if (GET_CODE (SET_DEST (set)) == ZERO_EXTRACT + || GET_CODE (SET_DEST (set)) == STRICT_LOW_PART) + { + for (i = hard_regno_nregs[regno][mode] - 1 + regno; i >= regno; i--) + { + reg_state[i].use_index = -1; + reg_state[i].store_ruid = reload_combine_ruid; ++ reg_state[i].real_store_ruid = reload_combine_ruid; + } + } + else +@@ -1025,6 +1431,8 @@ + for (i = hard_regno_nregs[regno][mode] - 1 + regno; i >= regno; i--) + { + reg_state[i].store_ruid = reload_combine_ruid; ++ if (GET_CODE (set) == SET) ++ reg_state[i].real_store_ruid = reload_combine_ruid; + reg_state[i].use_index = RELOAD_COMBINE_MAX_USES; + } + } +@@ -1035,7 +1443,7 @@ + *XP is the pattern of INSN, or a part of it. + Called from reload_combine, and recursively by itself. */ + static void +-reload_combine_note_use (rtx *xp, rtx insn) ++reload_combine_note_use (rtx *xp, rtx insn, int ruid, rtx containing_mem) + { + rtx x = *xp; + enum rtx_code code = x->code; +@@ -1048,7 +1456,7 @@ + case SET: + if (REG_P (SET_DEST (x))) + { +- reload_combine_note_use (&SET_SRC (x), insn); ++ reload_combine_note_use (&SET_SRC (x), insn, ruid, NULL_RTX); + return; + } + break; +@@ -1104,6 +1512,11 @@ + return; + } + ++ /* We may be called to update uses in previously seen insns. ++ Don't add uses beyond the last store we saw. */ ++ if (ruid < reg_state[regno].store_ruid) ++ return; ++ + /* If this register is already used in some unknown fashion, we + can't do anything. + If we decrement the index from zero to -1, we can't store more +@@ -1112,29 +1525,34 @@ + if (use_index < 0) + return; + +- if (use_index != RELOAD_COMBINE_MAX_USES - 1) +- { +- /* We have found another use for a register that is already +- used later. Check if the offsets match; if not, mark the +- register as used in an unknown fashion. */ +- if (! rtx_equal_p (offset, reg_state[regno].offset)) +- { +- reg_state[regno].use_index = -1; +- return; +- } +- } +- else ++ if (use_index == RELOAD_COMBINE_MAX_USES - 1) + { + /* This is the first use of this register we have seen since we + marked it as dead. */ + reg_state[regno].offset = offset; +- reg_state[regno].use_ruid = reload_combine_ruid; +- } ++ reg_state[regno].all_offsets_match = true; ++ reg_state[regno].use_ruid = ruid; ++ } ++ else ++ { ++ if (reg_state[regno].use_ruid > ruid) ++ reg_state[regno].use_ruid = ruid; ++ ++ if (! rtx_equal_p (offset, reg_state[regno].offset)) ++ reg_state[regno].all_offsets_match = false; ++ } ++ + reg_state[regno].reg_use[use_index].insn = insn; ++ reg_state[regno].reg_use[use_index].ruid = ruid; ++ reg_state[regno].reg_use[use_index].containing_mem = containing_mem; + reg_state[regno].reg_use[use_index].usep = xp; + return; + } + ++ case MEM: ++ containing_mem = x; ++ break; ++ + default: + break; + } +@@ -1144,11 +1562,12 @@ + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') +- reload_combine_note_use (&XEXP (x, i), insn); ++ reload_combine_note_use (&XEXP (x, i), insn, ruid, containing_mem); + else if (fmt[i] == 'E') + { + for (j = XVECLEN (x, i) - 1; j >= 0; j--) +- reload_combine_note_use (&XVECEXP (x, i, j), insn); ++ reload_combine_note_use (&XVECEXP (x, i, j), insn, ruid, ++ containing_mem); + } + } + } +@@ -1196,9 +1615,10 @@ + while REG is known to already have value (SYM + offset). + This function tries to change INSN into an add instruction + (set (REG) (plus (REG) (OFF - offset))) using the known value. +- It also updates the information about REG's known value. */ ++ It also updates the information about REG's known value. ++ Return true if we made a change. */ + +-static void ++static bool + move2add_use_add2_insn (rtx reg, rtx sym, rtx off, rtx insn) + { + rtx pat = PATTERN (insn); +@@ -1207,6 +1627,7 @@ + rtx new_src = gen_int_mode (INTVAL (off) - reg_offset[regno], + GET_MODE (reg)); + bool speed = optimize_bb_for_speed_p (BLOCK_FOR_INSN (insn)); ++ bool changed = false; + + /* (set (reg) (plus (reg) (const_int 0))) is not canonical; + use (set (reg) (reg)) instead. +@@ -1221,13 +1642,13 @@ + (reg)), would be discarded. Maybe we should + try a truncMN pattern? */ + if (INTVAL (off) == reg_offset [regno]) +- validate_change (insn, &SET_SRC (pat), reg, 0); ++ changed = validate_change (insn, &SET_SRC (pat), reg, 0); + } + else if (rtx_cost (new_src, PLUS, speed) < rtx_cost (src, SET, speed) + && have_add2_insn (reg, new_src)) + { + rtx tem = gen_rtx_PLUS (GET_MODE (reg), reg, new_src); +- validate_change (insn, &SET_SRC (pat), tem, 0); ++ changed = validate_change (insn, &SET_SRC (pat), tem, 0); + } + else if (sym == NULL_RTX && GET_MODE (reg) != BImode) + { +@@ -1252,8 +1673,9 @@ + gen_rtx_STRICT_LOW_PART (VOIDmode, + narrow_reg), + narrow_src); +- if (validate_change (insn, &PATTERN (insn), +- new_set, 0)) ++ changed = validate_change (insn, &PATTERN (insn), ++ new_set, 0); ++ if (changed) + break; + } + } +@@ -1263,6 +1685,7 @@ + reg_mode[regno] = GET_MODE (reg); + reg_symbol_ref[regno] = sym; + reg_offset[regno] = INTVAL (off); ++ return changed; + } + + +@@ -1272,9 +1695,10 @@ + value (SYM + offset) and change INSN into an add instruction + (set (REG) (plus (the found register) (OFF - offset))) if such + a register is found. It also updates the information about +- REG's known value. */ ++ REG's known value. ++ Return true iff we made a change. */ + +-static void ++static bool + move2add_use_add3_insn (rtx reg, rtx sym, rtx off, rtx insn) + { + rtx pat = PATTERN (insn); +@@ -1284,6 +1708,7 @@ + int min_regno; + bool speed = optimize_bb_for_speed_p (BLOCK_FOR_INSN (insn)); + int i; ++ bool changed = false; + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (reg_set_luid[i] > move2add_last_label_luid +@@ -1328,20 +1753,25 @@ + GET_MODE (reg)); + tem = gen_rtx_PLUS (GET_MODE (reg), tem, new_src); + } +- validate_change (insn, &SET_SRC (pat), tem, 0); ++ if (validate_change (insn, &SET_SRC (pat), tem, 0)) ++ changed = true; + } + reg_set_luid[regno] = move2add_luid; + reg_base_reg[regno] = -1; + reg_mode[regno] = GET_MODE (reg); + reg_symbol_ref[regno] = sym; + reg_offset[regno] = INTVAL (off); ++ return changed; + } + +-static void ++/* Convert move insns with constant inputs to additions if they are cheaper. ++ Return true if any changes were made. */ ++static bool + reload_cse_move2add (rtx first) + { + int i; + rtx insn; ++ bool changed = false; + + for (i = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--) + { +@@ -1402,7 +1832,7 @@ + && reg_base_reg[regno] < 0 + && reg_symbol_ref[regno] == NULL_RTX) + { +- move2add_use_add2_insn (reg, NULL_RTX, src, insn); ++ changed |= move2add_use_add2_insn (reg, NULL_RTX, src, insn); + continue; + } + +@@ -1463,6 +1893,7 @@ + } + if (success) + delete_insn (insn); ++ changed |= success; + insn = next; + reg_mode[regno] = GET_MODE (reg); + reg_offset[regno] = +@@ -1508,12 +1939,12 @@ + && reg_base_reg[regno] < 0 + && reg_symbol_ref[regno] != NULL_RTX + && rtx_equal_p (sym, reg_symbol_ref[regno])) +- move2add_use_add2_insn (reg, sym, off, insn); ++ changed |= move2add_use_add2_insn (reg, sym, off, insn); + + /* Otherwise, we have to find a register whose value is sum + of sym and some constant value. */ + else +- move2add_use_add3_insn (reg, sym, off, insn); ++ changed |= move2add_use_add3_insn (reg, sym, off, insn); + + continue; + } +@@ -1568,6 +1999,7 @@ + } + } + } ++ return changed; + } + + /* SET is a SET or CLOBBER that sets DST. DATA is the insn which + |