The following introduces a new param, max-combine-insns, to be able to limit the work done at -Og to linear complexity in the number of log-links (thus, two-insn combines). It also records statistics of performed combines where for fold-const.ii on x86_64 we see at -Og (unpatched):
208 combine "three-insn combine" 25 208 combine "two-insn combine" 14393 and patched: 208 combine "two-insn combine" 14392 Bootstrap / regtest scheduled on x86_64-unknown-linux-gnu. Ok? (I can rip out the statistics stuff if you mind) (the combine_insns diff is so large because of re-indenting) Thanks, Richard. 2014-07-15 Richard Biener <rguent...@suse.de> * params.def (PARAM_MAX_COMBINE_INSNS): New. * combine.c: Include statistics.h and params.h. (combine_instructions): Guard three and four insn combines with max-combine-insns value. Record statistics for combines performed. * doc/invoke.texi (max-combine-insns): Document new param. Index: gcc/params.def =================================================================== *** gcc/params.def (revision 212548) --- gcc/params.def (working copy) *************** DEFPARAM(PARAM_MAX_LAST_VALUE_RTL, *** 673,678 **** --- 673,683 ---- "The maximum number of RTL nodes that can be recorded as combiner's last value", 10000, 0, 0) + DEFPARAM(PARAM_MAX_COMBINE_INSNS, + "max-combine-insns", + "The maximum number of insns combine tries to combine", + 4, 2, 4) + /* INTEGER_CST nodes are shared for values [{-1,0} .. N) for {signed,unsigned} integral types. This determines N. Experimentation shows 251 to be a good value that generates the Index: gcc/combine.c =================================================================== *** gcc/combine.c (revision 212548) --- gcc/combine.c (working copy) *************** along with GCC; see the file COPYING3. *** 104,109 **** --- 104,111 ---- #include "valtrack.h" #include "cgraph.h" #include "obstack.h" + #include "statistics.h" + #include "params.h" /* Number of attempts to combine instructions in this function. */ *************** combine_instructions (rtx f, unsigned in *** 1209,1214 **** --- 1211,1217 ---- init_reg_last (); setup_incoming_promotions (first); last_bb = ENTRY_BLOCK_PTR_FOR_FN (cfun); + int max_combine = PARAM_VALUE (PARAM_MAX_COMBINE_INSNS); FOR_EACH_BB_FN (this_basic_block, cfun) { *************** combine_instructions (rtx f, unsigned in *** 1229,1446 **** insn = next ? next : NEXT_INSN (insn)) { next = 0; ! if (NONDEBUG_INSN_P (insn)) ! { ! while (last_combined_insn ! && INSN_DELETED_P (last_combined_insn)) ! last_combined_insn = PREV_INSN (last_combined_insn); ! if (last_combined_insn == NULL_RTX ! || BARRIER_P (last_combined_insn) ! || BLOCK_FOR_INSN (last_combined_insn) != this_basic_block ! || DF_INSN_LUID (last_combined_insn) <= DF_INSN_LUID (insn)) ! last_combined_insn = insn; ! ! /* See if we know about function return values before this ! insn based upon SUBREG flags. */ ! check_promoted_subreg (insn, PATTERN (insn)); ! ! /* See if we can find hardregs and subreg of pseudos in ! narrower modes. This could help turning TRUNCATEs ! into SUBREGs. */ ! note_uses (&PATTERN (insn), record_truncated_values, NULL); ! ! /* Try this insn with each insn it links back to. */ ! ! FOR_EACH_LOG_LINK (links, insn) ! if ((next = try_combine (insn, links->insn, NULL_RTX, ! NULL_RTX, &new_direct_jump_p, ! last_combined_insn)) != 0) ! goto retry; ! ! /* Try each sequence of three linked insns ending with this one. */ ! FOR_EACH_LOG_LINK (links, insn) ! { ! rtx link = links->insn; ! ! /* If the linked insn has been replaced by a note, then there ! is no point in pursuing this chain any further. */ ! if (NOTE_P (link)) ! continue; ! ! FOR_EACH_LOG_LINK (nextlinks, link) ! if ((next = try_combine (insn, link, nextlinks->insn, ! NULL_RTX, &new_direct_jump_p, ! last_combined_insn)) != 0) goto retry; ! } #ifdef HAVE_cc0 ! /* Try to combine a jump insn that uses CC0 ! with a preceding insn that sets CC0, and maybe with its ! logical predecessor as well. ! This is how we make decrement-and-branch insns. ! We need this special code because data flow connections ! via CC0 do not get entered in LOG_LINKS. */ ! ! if (JUMP_P (insn) ! && (prev = prev_nonnote_insn (insn)) != 0 ! && NONJUMP_INSN_P (prev) ! && sets_cc0_p (PATTERN (prev))) ! { ! if ((next = try_combine (insn, prev, NULL_RTX, NULL_RTX, ! &new_direct_jump_p, last_combined_insn)) != 0) goto retry; ! FOR_EACH_LOG_LINK (nextlinks, prev) ! if ((next = try_combine (insn, prev, nextlinks->insn, ! NULL_RTX, &new_direct_jump_p, ! last_combined_insn)) != 0) ! goto retry; ! } ! ! /* Do the same for an insn that explicitly references CC0. */ ! if (NONJUMP_INSN_P (insn) ! && (prev = prev_nonnote_insn (insn)) != 0 ! && NONJUMP_INSN_P (prev) ! && sets_cc0_p (PATTERN (prev)) ! && GET_CODE (PATTERN (insn)) == SET ! && reg_mentioned_p (cc0_rtx, SET_SRC (PATTERN (insn)))) ! { ! if ((next = try_combine (insn, prev, NULL_RTX, NULL_RTX, ! &new_direct_jump_p, last_combined_insn)) != 0) goto retry; ! FOR_EACH_LOG_LINK (nextlinks, prev) ! if ((next = try_combine (insn, prev, nextlinks->insn, ! NULL_RTX, &new_direct_jump_p, ! last_combined_insn)) != 0) ! goto retry; ! } ! ! /* Finally, see if any of the insns that this insn links to ! explicitly references CC0. If so, try this insn, that insn, ! and its predecessor if it sets CC0. */ ! FOR_EACH_LOG_LINK (links, insn) ! if (NONJUMP_INSN_P (links->insn) ! && GET_CODE (PATTERN (links->insn)) == SET ! && reg_mentioned_p (cc0_rtx, SET_SRC (PATTERN (links->insn))) ! && (prev = prev_nonnote_insn (links->insn)) != 0 ! && NONJUMP_INSN_P (prev) ! && sets_cc0_p (PATTERN (prev)) ! && (next = try_combine (insn, links->insn, ! prev, NULL_RTX, &new_direct_jump_p, ! last_combined_insn)) != 0) ! goto retry; #endif ! /* Try combining an insn with two different insns whose results it ! uses. */ ! FOR_EACH_LOG_LINK (links, insn) ! for (nextlinks = links->next; nextlinks; ! nextlinks = nextlinks->next) ! if ((next = try_combine (insn, links->insn, ! nextlinks->insn, NULL_RTX, ! &new_direct_jump_p, ! last_combined_insn)) != 0) ! goto retry; ! ! /* Try four-instruction combinations. */ ! FOR_EACH_LOG_LINK (links, insn) ! { ! struct insn_link *next1; ! rtx link = links->insn; ! /* If the linked insn has been replaced by a note, then there ! is no point in pursuing this chain any further. */ ! if (NOTE_P (link)) ! continue; ! FOR_EACH_LOG_LINK (next1, link) ! { ! rtx link1 = next1->insn; ! if (NOTE_P (link1)) ! continue; ! /* I0 -> I1 -> I2 -> I3. */ ! FOR_EACH_LOG_LINK (nextlinks, link1) ! if ((next = try_combine (insn, link, link1, ! nextlinks->insn, ! &new_direct_jump_p, ! last_combined_insn)) != 0) goto retry; ! /* I0, I1 -> I2, I2 -> I3. */ ! for (nextlinks = next1->next; nextlinks; ! nextlinks = nextlinks->next) ! if ((next = try_combine (insn, link, link1, ! nextlinks->insn, ! &new_direct_jump_p, ! last_combined_insn)) != 0) goto retry; ! } ! for (next1 = links->next; next1; next1 = next1->next) ! { ! rtx link1 = next1->insn; ! if (NOTE_P (link1)) ! continue; ! /* I0 -> I2; I1, I2 -> I3. */ ! FOR_EACH_LOG_LINK (nextlinks, link) ! if ((next = try_combine (insn, link, link1, ! nextlinks->insn, ! &new_direct_jump_p, ! last_combined_insn)) != 0) goto retry; ! /* I0 -> I1; I1, I2 -> I3. */ ! FOR_EACH_LOG_LINK (nextlinks, link1) ! if ((next = try_combine (insn, link, link1, ! nextlinks->insn, ! &new_direct_jump_p, ! last_combined_insn)) != 0) goto retry; ! } ! } ! /* Try this insn with each REG_EQUAL note it links back to. */ ! FOR_EACH_LOG_LINK (links, insn) { ! rtx set, note; ! rtx temp = links->insn; ! if ((set = single_set (temp)) != 0 ! && (note = find_reg_equal_equiv_note (temp)) != 0 ! && (note = XEXP (note, 0), GET_CODE (note)) != EXPR_LIST ! /* Avoid using a register that may already been marked ! dead by an earlier instruction. */ ! && ! unmentioned_reg_p (note, SET_SRC (set)) ! && (GET_MODE (note) == VOIDmode ! ? SCALAR_INT_MODE_P (GET_MODE (SET_DEST (set))) ! : GET_MODE (SET_DEST (set)) == GET_MODE (note))) { ! /* Temporarily replace the set's source with the ! contents of the REG_EQUAL note. The insn will ! be deleted or recognized by try_combine. */ ! rtx orig = SET_SRC (set); ! SET_SRC (set) = note; ! i2mod = temp; ! i2mod_old_rhs = copy_rtx (orig); ! i2mod_new_rhs = copy_rtx (note); ! next = try_combine (insn, i2mod, NULL_RTX, NULL_RTX, ! &new_direct_jump_p, ! last_combined_insn); ! i2mod = NULL_RTX; ! if (next) ! goto retry; ! SET_SRC (set) = orig; } } ! if (!NOTE_P (insn)) ! record_dead_and_set_regs (insn); ! retry: ! ; ! } } } --- 1232,1477 ---- insn = next ? next : NEXT_INSN (insn)) { next = 0; ! if (!NONDEBUG_INSN_P (insn)) ! continue; ! while (last_combined_insn ! && INSN_DELETED_P (last_combined_insn)) ! last_combined_insn = PREV_INSN (last_combined_insn); ! if (last_combined_insn == NULL_RTX ! || BARRIER_P (last_combined_insn) ! || BLOCK_FOR_INSN (last_combined_insn) != this_basic_block ! || DF_INSN_LUID (last_combined_insn) <= DF_INSN_LUID (insn)) ! last_combined_insn = insn; ! ! /* See if we know about function return values before this ! insn based upon SUBREG flags. */ ! check_promoted_subreg (insn, PATTERN (insn)); ! ! /* See if we can find hardregs and subreg of pseudos in ! narrower modes. This could help turning TRUNCATEs ! into SUBREGs. */ ! note_uses (&PATTERN (insn), record_truncated_values, NULL); ! ! /* Try this insn with each insn it links back to. */ ! ! FOR_EACH_LOG_LINK (links, insn) ! if ((next = try_combine (insn, links->insn, NULL_RTX, ! NULL_RTX, &new_direct_jump_p, ! last_combined_insn)) != 0) ! { ! statistics_counter_event (cfun, "two-insn combine", 1); ! goto retry; ! } ! ! /* Try each sequence of three linked insns ending with this one. */ ! ! if (max_combine >= 3) ! FOR_EACH_LOG_LINK (links, insn) ! { ! rtx link = links->insn; ! ! /* If the linked insn has been replaced by a note, then there ! is no point in pursuing this chain any further. */ ! if (NOTE_P (link)) ! continue; ! ! FOR_EACH_LOG_LINK (nextlinks, link) ! if ((next = try_combine (insn, link, nextlinks->insn, ! NULL_RTX, &new_direct_jump_p, ! last_combined_insn)) != 0) ! { ! statistics_counter_event (cfun, "three-insn combine", 1); goto retry; ! } ! } #ifdef HAVE_cc0 ! /* Try to combine a jump insn that uses CC0 ! with a preceding insn that sets CC0, and maybe with its ! logical predecessor as well. ! This is how we make decrement-and-branch insns. ! We need this special code because data flow connections ! via CC0 do not get entered in LOG_LINKS. */ ! ! if (JUMP_P (insn) ! && (prev = prev_nonnote_insn (insn)) != 0 ! && NONJUMP_INSN_P (prev) ! && sets_cc0_p (PATTERN (prev))) ! { ! if ((next = try_combine (insn, prev, NULL_RTX, NULL_RTX, ! &new_direct_jump_p, ! last_combined_insn)) != 0) ! goto retry; ! ! FOR_EACH_LOG_LINK (nextlinks, prev) ! if ((next = try_combine (insn, prev, nextlinks->insn, ! NULL_RTX, &new_direct_jump_p, last_combined_insn)) != 0) goto retry; + } ! /* Do the same for an insn that explicitly references CC0. */ ! if (NONJUMP_INSN_P (insn) ! && (prev = prev_nonnote_insn (insn)) != 0 ! && NONJUMP_INSN_P (prev) ! && sets_cc0_p (PATTERN (prev)) ! && GET_CODE (PATTERN (insn)) == SET ! && reg_mentioned_p (cc0_rtx, SET_SRC (PATTERN (insn)))) ! { ! if ((next = try_combine (insn, prev, NULL_RTX, NULL_RTX, ! &new_direct_jump_p, ! last_combined_insn)) != 0) ! goto retry; ! ! FOR_EACH_LOG_LINK (nextlinks, prev) ! if ((next = try_combine (insn, prev, nextlinks->insn, ! NULL_RTX, &new_direct_jump_p, last_combined_insn)) != 0) goto retry; + } ! /* Finally, see if any of the insns that this insn links to ! explicitly references CC0. If so, try this insn, that insn, ! and its predecessor if it sets CC0. */ ! FOR_EACH_LOG_LINK (links, insn) ! if (NONJUMP_INSN_P (links->insn) ! && GET_CODE (PATTERN (links->insn)) == SET ! && reg_mentioned_p (cc0_rtx, SET_SRC (PATTERN (links->insn))) ! && (prev = prev_nonnote_insn (links->insn)) != 0 ! && NONJUMP_INSN_P (prev) ! && sets_cc0_p (PATTERN (prev)) ! && (next = try_combine (insn, links->insn, ! prev, NULL_RTX, &new_direct_jump_p, ! last_combined_insn)) != 0) ! goto retry; #endif ! /* Try combining an insn with two different insns whose results it ! uses. */ ! if (max_combine >= 3) ! FOR_EACH_LOG_LINK (links, insn) ! for (nextlinks = links->next; nextlinks; ! nextlinks = nextlinks->next) ! if ((next = try_combine (insn, links->insn, ! nextlinks->insn, NULL_RTX, ! &new_direct_jump_p, ! last_combined_insn)) != 0) ! { ! statistics_counter_event (cfun, "three-insn combine", 1); ! goto retry; ! } ! /* Try four-instruction combinations. */ ! if (max_combine >= 4) ! FOR_EACH_LOG_LINK (links, insn) ! { ! struct insn_link *next1; ! rtx link = links->insn; ! ! /* If the linked insn has been replaced by a note, then there ! is no point in pursuing this chain any further. */ ! if (NOTE_P (link)) ! continue; ! ! FOR_EACH_LOG_LINK (next1, link) ! { ! rtx link1 = next1->insn; ! if (NOTE_P (link1)) ! continue; ! /* I0 -> I1 -> I2 -> I3. */ ! FOR_EACH_LOG_LINK (nextlinks, link1) ! if ((next = try_combine (insn, link, link1, ! nextlinks->insn, ! &new_direct_jump_p, ! last_combined_insn)) != 0) ! { ! statistics_counter_event (cfun, "four-insn combine", 1); goto retry; ! } ! /* I0, I1 -> I2, I2 -> I3. */ ! for (nextlinks = next1->next; nextlinks; ! nextlinks = nextlinks->next) ! if ((next = try_combine (insn, link, link1, ! nextlinks->insn, ! &new_direct_jump_p, ! last_combined_insn)) != 0) ! { ! statistics_counter_event (cfun, "four-insn combine", 1); goto retry; ! } ! } ! for (next1 = links->next; next1; next1 = next1->next) ! { ! rtx link1 = next1->insn; ! if (NOTE_P (link1)) ! continue; ! /* I0 -> I2; I1, I2 -> I3. */ ! FOR_EACH_LOG_LINK (nextlinks, link) ! if ((next = try_combine (insn, link, link1, ! nextlinks->insn, ! &new_direct_jump_p, ! last_combined_insn)) != 0) ! { ! statistics_counter_event (cfun, "four-insn combine", 1); goto retry; ! } ! /* I0 -> I1; I1, I2 -> I3. */ ! FOR_EACH_LOG_LINK (nextlinks, link1) ! if ((next = try_combine (insn, link, link1, ! nextlinks->insn, ! &new_direct_jump_p, ! last_combined_insn)) != 0) ! { ! statistics_counter_event (cfun, "four-insn combine", 1); goto retry; ! } ! } ! } ! /* Try this insn with each REG_EQUAL note it links back to. */ ! FOR_EACH_LOG_LINK (links, insn) ! { ! rtx set, note; ! rtx temp = links->insn; ! if ((set = single_set (temp)) != 0 ! && (note = find_reg_equal_equiv_note (temp)) != 0 ! && (note = XEXP (note, 0), GET_CODE (note)) != EXPR_LIST ! /* Avoid using a register that may already been marked ! dead by an earlier instruction. */ ! && ! unmentioned_reg_p (note, SET_SRC (set)) ! && (GET_MODE (note) == VOIDmode ! ? SCALAR_INT_MODE_P (GET_MODE (SET_DEST (set))) ! : GET_MODE (SET_DEST (set)) == GET_MODE (note))) { ! /* Temporarily replace the set's source with the ! contents of the REG_EQUAL note. The insn will ! be deleted or recognized by try_combine. */ ! rtx orig = SET_SRC (set); ! SET_SRC (set) = note; ! i2mod = temp; ! i2mod_old_rhs = copy_rtx (orig); ! i2mod_new_rhs = copy_rtx (note); ! next = try_combine (insn, i2mod, NULL_RTX, NULL_RTX, ! &new_direct_jump_p, ! last_combined_insn); ! i2mod = NULL_RTX; ! if (next) { ! statistics_counter_event (cfun, "insn-with-note combine", 1); ! goto retry; } + SET_SRC (set) = orig; } + } ! if (!NOTE_P (insn)) ! record_dead_and_set_regs (insn); ! retry: ! ; } } Index: gcc/doc/invoke.texi =================================================================== *** gcc/doc/invoke.texi (revision 212548) --- gcc/doc/invoke.texi (working copy) *************** The maximum size measured as number of R *** 10006,10011 **** --- 10006,10015 ---- in combiner for a pseudo register as last known value of that register. The default is 10000. + @item max-combine-insns + The maximum number of instructions the RTL combiner tries to combine. + The default value is 2 at @option{-Og} and 4 otherwise. + @item integer-share-limit Small integer constants can use a shared data structure, reducing the compiler's memory usage and increasing its speed. This sets the maximum