This stops combine messing with parameter and return value copies from/to hard registers. Bootstrapped and regression tested powerpc64le-linux, powerpc64-linux and x86_64-linux. In looking at a number of different powerpc64le gcc/*.o files, I noticed a few code generation improvements. There were cases where a register copy was no longer needed, cmp used in place of mr., and rlwinm instead of rldicl. x86_64 gcc/*.o showed no changes (apart from combine.o of course), but should see a small improvement in compile time with this change.
The "clear next_use when !can_combine_p" change is to fix a non-bug. Given 1) insn defining rn ... 2) insn defining rn but !can_combine_p ... 3) insn using rn then create_log_links might create a link from (3) to (1), I thought. However, can_combine_p doesn't currently allow this to happen. Obviously, any can_combine_p result depending on regno shouldn't give a different result at (1) and (2), but there is also at test of DF_REF_PRE_POST_MODIFY that can. The saving grace is that pre/post modify insns also use the register, which means next_use[rn] will point at (2), not (3), when (1) is processed. I came across this because at one stage I considered modifying can_combine_p. Someone who does so in the future might trigger the above problem, so I thought it worth posting the change. OK for mainline? * combine.c (set_return_regs): New function. (twiddle_first_block, twiddle_last_block): New functions. (create_log_links): Exclude instructions copying parameter values from hard regs to pseudos, and instructions copying return value pseudos to hard regs. Clear next_use when !can_combine_p. Index: gcc/combine.c =================================================================== --- gcc/combine.c (revision 223463) +++ gcc/combine.c (working copy) @@ -1048,9 +1048,79 @@ can_combine_use_p (df_ref use) return true; } -/* Fill in log links field for all insns. */ +/* Used to build set of return value regs. Add X to the set. */ static void +set_return_regs (rtx x, void *arg) +{ + HARD_REG_SET *regs = (HARD_REG_SET *) arg; + + add_to_hard_reg_set (regs, GET_MODE (x), REGNO (x)); +} + +/* Twiddle BLOCK_FOR_INSN to TO for instructions in the first block BB + that we don't want to combine with other instructions. */ + +static void +twiddle_first_block (basic_block bb, basic_block to) +{ + rtx_insn *insn; + + FOR_BB_INSNS (bb, insn) + { + if (NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_FUNCTION_BEG) + break; + if (!NONDEBUG_INSN_P (insn)) + continue; + + /* reg,reg copies from parameter hard regs. */ + rtx set = single_set (insn); + if (set + && REG_P (SET_DEST (set)) + && REG_P (SET_SRC (set)) + && HARD_REGISTER_P (SET_SRC (set))) + set_block_for_insn (insn, to); + } +} + +/* Twiddle BLOCK_FOR_INSN to TO for instructions in the last block BB + that we don't want to combine with other instructions. */ + +static void +twiddle_last_block (basic_block bb, basic_block to, HARD_REG_SET return_regs) +{ + rtx_insn *insn; + + FOR_BB_INSNS_REVERSE (bb, insn) + { + if (CALL_P (insn)) + break; + if (!NONDEBUG_INSN_P (insn)) + continue; + + rtx reg = NULL_RTX; + /* use_return_regster added USEs. */ + if (GET_CODE (PATTERN (insn)) == USE) + reg = XEXP (PATTERN (insn), 0); + else + { + /* reg,reg copies that set return value hard regs. */ + rtx set = single_set (insn); + if (set && REG_P (SET_SRC (set))) + reg = SET_DEST (set); + } + if (reg + && REG_P (reg) + && HARD_REGISTER_P (reg) + && overlaps_hard_reg_set_p (return_regs, + GET_MODE (reg), REGNO (reg))) + set_block_for_insn (insn, to); + } +} + +/* Fill in log links field for all insns that we wish to combine. */ + +static void create_log_links (void) { basic_block bb; @@ -1057,9 +1127,28 @@ create_log_links (void) rtx_insn **next_use; rtx_insn *insn; df_ref def, use; + HARD_REG_SET return_regs; next_use = XCNEWVEC (rtx_insn *, max_reg_num ()); + /* Don't combine instructions copying parameter values from hard + regs to pseudos. Exclude such instructions from LOG_LINKS by + temporarily zapping BLOCK_FOR_INSN. */ + + bb = ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb; + twiddle_first_block (bb, 0); + + /* Similarly, don't combine instructions copying return values + from pseudos to hard regs. */ + + CLEAR_HARD_REG_SET (return_regs); + diddle_return_value (set_return_regs, &return_regs); + if (!hard_reg_set_empty_p (return_regs)) + { + bb = EXIT_BLOCK_PTR_FOR_FN (cfun)->prev_bb; + twiddle_last_block (bb, 0, return_regs); + } + /* Pass through each block from the end, recording the uses of each register and establishing log links when def is encountered. Note that we do not clear next_use array in order to save time, @@ -1087,12 +1176,12 @@ create_log_links (void) if (!next_use[regno]) continue; + use_insn = next_use[regno]; + next_use[regno] = NULL; + if (!can_combine_def_p (def)) continue; - use_insn = next_use[regno]; - next_use[regno] = NULL; - if (BLOCK_FOR_INSN (use_insn) != bb) continue; @@ -1103,7 +1192,7 @@ create_log_links (void) we might wind up changing the semantics of the insn, even if reload can make what appear to be valid assignments later. */ - if (regno < FIRST_PSEUDO_REGISTER + if (HARD_REGISTER_NUM_P (regno) && asm_noperands (PATTERN (use_insn)) >= 0) continue; @@ -1124,6 +1213,16 @@ create_log_links (void) } } + /* Repair BLOCK_FOR_INSN. */ + + bb = ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb; + twiddle_first_block (bb, bb); + if (!hard_reg_set_empty_p (return_regs)) + { + bb = EXIT_BLOCK_PTR_FOR_FN (cfun)->prev_bb; + twiddle_last_block (bb, bb, return_regs); + } + free (next_use); } -- Alan Modra Australia Development Lab, IBM