diff --git a/gcc/fwprop.c b/gcc/fwprop.c index 377b33c..b2a5918 100644 --- a/gcc/fwprop.c +++ b/gcc/fwprop.c @@ -1399,6 +1399,133 @@ forward_propagate_into (df_ref use) return false; } +/* Loop invariant variable hoisting for critical code has + important impact on the performance. + + The RTL GCSE PRE pass could detect more hoisting opportunities + if we re-shuffle the instructions to associate fixed registers + with constant. + + This function try to transform + + RA <- RB_fixed + RC + RD <- MEM (RA + const_offset) + + into: + + RA <- RB_fixed + const_offset + RD <- MEM (RA + RC) + + If RA is DEAD after the second instruction. + + After this change, the first instruction is loop invariant. */ + +static void +prepare_for_gcse_pre () +{ + struct loop *loop; + + if (! current_loops) + return; + + FOR_EACH_LOOP (loop, LI_INCLUDE_ROOT) + { + if (loop && loop->header && loop->latch + && loop->header->index == loop->latch->index) + { + rtx_insn *insn, *next_insn; + rtx single_set1, single_set2, old_dest; + rtx op0, op0_; + rtx op1, op1_; + rtx inner; + rtx *mem_plus_loc; + + basic_block bb = BASIC_BLOCK_FOR_FN (cfun, loop->header->index); + + FOR_BB_INSNS (bb, insn) + { + if (! NONDEBUG_INSN_P (insn)) + continue; + + single_set1 = single_set (insn); + + if (! single_set1 + || GET_CODE (SET_SRC (single_set1)) != PLUS) + continue; + + old_dest = SET_DEST (single_set1); + op0 = XEXP (SET_SRC (single_set1), 0); + op1 = XEXP (SET_SRC (single_set1), 1); + + if (op1 == frame_pointer_rtx + || op1 == stack_pointer_rtx + || op1 == virtual_stack_vars_rtx) + std::swap (op0, op1); + + if (! (REG_P (old_dest) && REG_P (op0) && REG_P (op1) + && (op0 == frame_pointer_rtx + || op0 == stack_pointer_rtx + || op0 == virtual_stack_vars_rtx))) + continue; + + if (! (next_insn = next_real_insn (insn))) + break; + + do + { + if (DEBUG_INSN_P (next_insn)) + continue; + + single_set2 = single_set (next_insn); + + if (!single_set2 || ! REG_P (SET_DEST (single_set2))) + continue; + + inner = SET_SRC (single_set2); + + if (GET_CODE (inner) == ZERO_EXTEND + || GET_CODE (inner) == SIGN_EXTEND + || GET_CODE (inner) == TRUNCATE) + inner = XEXP (inner, 0); + + if (! MEM_P (inner) + || GET_CODE (XEXP (inner, 0)) != PLUS) + continue; + + mem_plus_loc = &XEXP (inner, 0); + op0_ = XEXP (XEXP (inner, 0), 0); + op1_ = XEXP (XEXP (inner, 0), 1); + + if (REG_P (op0_) && CONST_INT_P (op1_) + && rtx_equal_p (op0_, old_dest) + && GET_MODE (op0_) == GET_MODE (op1)) + { + rtx new_src; + + if (find_regno_note (next_insn, REG_DEAD, + REGNO (old_dest))) + { + new_src = plus_constant (GET_MODE (op0), op0, + INTVAL (op1_)); + validate_change (insn, &SET_SRC (single_set1), + new_src, 1); + new_src = gen_rtx_PLUS (GET_MODE (op0_), op0_, op1); + validate_change (next_insn, mem_plus_loc, new_src, 1); + if (apply_change_group () && dump_file) + fprintf (dump_file, + "\nRe-associate insn %d and %d for later" + " RTL loop invariant hoisting.\n", + INSN_UID (insn), INSN_UID (next_insn)); + } + break; + } + } while ((next_insn = next_real_insn (next_insn)) + && bb == BLOCK_FOR_INSN (next_insn)); + } + } + } +} + static void fwprop_init (void) @@ -1424,6 +1551,7 @@ fwprop_init (void) static void fwprop_done (void) { + prepare_for_gcse_pre (); loop_optimizer_finalize (); use_def_ref.release ();