X-Git-Url: https://gcc.gnu.org/git/?a=blobdiff_plain;f=gcc%2Fifcvt.c;h=dbe2485987019e06022f59b365924ae62ed94d57;hb=1705cebd79f24bc85dab766a9a26390827f26fa0;hp=c830b3cb8c4280586b09e63c7f6340c0b6b22709;hpb=af121e828eb03233d731b25b590d1f7c5dd2693d;p=gcc.git diff --git a/gcc/ifcvt.c b/gcc/ifcvt.c index c830b3cb8c4..dbe24859870 100644 --- a/gcc/ifcvt.c +++ b/gcc/ifcvt.c @@ -1,5 +1,5 @@ /* If-conversion support. - Copyright (C) 2000-2015 Free Software Foundation, Inc. + Copyright (C) 2000-2016 Free Software Foundation, Inc. This file is part of GCC. @@ -26,6 +26,7 @@ #include "tree.h" #include "cfghooks.h" #include "df.h" +#include "memmodel.h" #include "tm_p.h" #include "expmed.h" #include "optabs.h" @@ -44,6 +45,7 @@ #include "shrink-wrap.h" #include "rtl-iter.h" #include "ifcvt.h" +#include "params.h" #ifndef MAX_CONDITIONAL_EXECUTE #define MAX_CONDITIONAL_EXECUTE \ @@ -434,7 +436,7 @@ cond_exec_get_condition (rtx_insn *jump) /* If this branches to JUMP_LABEL when the condition is false, reverse the condition. */ if (GET_CODE (XEXP (test_if, 2)) == LABEL_REF - && LABEL_REF_LABEL (XEXP (test_if, 2)) == JUMP_LABEL (jump)) + && label_ref_label (XEXP (test_if, 2)) == JUMP_LABEL (jump)) { enum rtx_code rev = reversed_comparison_code (cond, jump); if (rev == UNKNOWN) @@ -739,7 +741,7 @@ cond_exec_process_if_block (ce_if_block * ce_info, rtx_insn *from = then_first_tail; if (!INSN_P (from)) from = find_active_insn_after (then_bb, from); - delete_insn_chain (from, BB_END (then_bb), false); + delete_insn_chain (from, get_last_bb_insn (then_bb), false); } if (else_last_head) delete_insn_chain (first_active_insn (else_bb), else_last_head, false); @@ -792,6 +794,9 @@ struct noce_if_info /* The SET_DEST of INSN_A. */ rtx x; + /* The original set destination that the THEN and ELSE basic blocks finally + write their result to. */ + rtx orig_x; /* True if this if block is not canonical. In the canonical form of if blocks, the THEN_BB is the block reached via the fallthru edge from TEST_BB. For the noce transformations, we allow the symmetric @@ -803,16 +808,26 @@ struct noce_if_info bool then_simple; bool else_simple; - /* The total rtx cost of the instructions in then_bb and else_bb. */ - unsigned int then_cost; - unsigned int else_cost; + /* True if we're optimisizing the control block for speed, false if + we're optimizing for size. */ + bool speed_p; - /* Estimated cost of the particular branch instruction. */ - unsigned int branch_cost; + /* The combined cost of COND, JUMP and the costs for THEN_BB and + ELSE_BB. */ + unsigned int original_cost; + + /* Maximum permissible cost for the unconditional sequence we should + generate to replace this branch. */ + unsigned int max_seq_cost; + + /* The name of the noce transform that succeeded in if-converting + this structure. Used for debugging. */ + const char *transform_name; }; static rtx noce_emit_store_flag (struct noce_if_info *, rtx, int, int); static int noce_try_move (struct noce_if_info *); +static int noce_try_ifelse_collapse (struct noce_if_info *); static int noce_try_store_flag (struct noce_if_info *); static int noce_try_addcc (struct noce_if_info *); static int noce_try_store_flag_constants (struct noce_if_info *); @@ -826,6 +841,25 @@ static int noce_try_minmax (struct noce_if_info *); static int noce_try_abs (struct noce_if_info *); static int noce_try_sign_mask (struct noce_if_info *); +/* Return TRUE if SEQ is a good candidate as a replacement for the + if-convertible sequence described in IF_INFO. */ + +inline static bool +noce_conversion_profitable_p (rtx_insn *seq, struct noce_if_info *if_info) +{ + bool speed_p = if_info->speed_p; + + /* Cost up the new sequence. */ + unsigned int cost = seq_cost (seq, speed_p); + + /* When compiling for size, we can make a reasonably accurately guess + at the size growth. */ + if (!speed_p) + return cost <= if_info->original_cost; + else + return cost <= if_info->max_seq_cost; +} + /* Helper function for noce_try_store_flag*. */ static rtx @@ -847,7 +881,7 @@ noce_emit_store_flag (struct noce_if_info *if_info, rtx x, int reversep, rtx set = pc_set (if_info->jump); cond = XEXP (SET_SRC (set), 0); if (GET_CODE (XEXP (SET_SRC (set), 2)) == LABEL_REF - && LABEL_REF_LABEL (XEXP (SET_SRC (set), 2)) == JUMP_LABEL (if_info->jump)) + && label_ref_label (XEXP (SET_SRC (set), 2)) == JUMP_LABEL (if_info->jump)) reversep = !reversep; if (if_info->then_else_reversed) reversep = !reversep; @@ -1111,11 +1145,45 @@ noce_try_move (struct noce_if_info *if_info) emit_insn_before_setloc (seq, if_info->jump, INSN_LOCATION (if_info->insn_a)); } + if_info->transform_name = "noce_try_move"; return TRUE; } return FALSE; } +/* Try forming an IF_THEN_ELSE (cond, b, a) and collapsing that + through simplify_rtx. Sometimes that can eliminate the IF_THEN_ELSE. + If that is the case, emit the result into x. */ + +static int +noce_try_ifelse_collapse (struct noce_if_info * if_info) +{ + if (!noce_simple_bbs (if_info)) + return FALSE; + + machine_mode mode = GET_MODE (if_info->x); + rtx if_then_else = simplify_gen_ternary (IF_THEN_ELSE, mode, mode, + if_info->cond, if_info->b, + if_info->a); + + if (GET_CODE (if_then_else) == IF_THEN_ELSE) + return FALSE; + + rtx_insn *seq; + start_sequence (); + noce_emit_move_insn (if_info->x, if_then_else); + seq = end_ifcvt_sequence (if_info); + if (!seq) + return FALSE; + + emit_insn_before_setloc (seq, if_info->jump, + INSN_LOCATION (if_info->insn_a)); + + if_info->transform_name = "noce_try_ifelse_collapse"; + return TRUE; +} + + /* Convert "if (test) x = 1; else x = 0". Only try 0 and STORE_FLAG_VALUE here. Other combinations will be @@ -1159,6 +1227,7 @@ noce_try_store_flag (struct noce_if_info *if_info) emit_insn_before_setloc (seq, if_info->jump, INSN_LOCATION (if_info->insn_a)); + if_info->transform_name = "noce_try_store_flag"; return TRUE; } else @@ -1168,6 +1237,84 @@ noce_try_store_flag (struct noce_if_info *if_info) } } + +/* Convert "if (test) x = -A; else x = A" into + x = A; if (test) x = -x if the machine can do the + conditional negate form of this cheaply. + Try this before noce_try_cmove that will just load the + immediates into two registers and do a conditional select + between them. If the target has a conditional negate or + conditional invert operation we can save a potentially + expensive constant synthesis. */ + +static bool +noce_try_inverse_constants (struct noce_if_info *if_info) +{ + if (!noce_simple_bbs (if_info)) + return false; + + if (!CONST_INT_P (if_info->a) + || !CONST_INT_P (if_info->b) + || !REG_P (if_info->x)) + return false; + + machine_mode mode = GET_MODE (if_info->x); + + HOST_WIDE_INT val_a = INTVAL (if_info->a); + HOST_WIDE_INT val_b = INTVAL (if_info->b); + + rtx cond = if_info->cond; + + rtx x = if_info->x; + rtx target; + + start_sequence (); + + rtx_code code; + if (val_b != HOST_WIDE_INT_MIN && val_a == -val_b) + code = NEG; + else if (val_a == ~val_b) + code = NOT; + else + { + end_sequence (); + return false; + } + + rtx tmp = gen_reg_rtx (mode); + noce_emit_move_insn (tmp, if_info->a); + + target = emit_conditional_neg_or_complement (x, code, mode, cond, tmp, tmp); + + if (target) + { + rtx_insn *seq = get_insns (); + + if (!seq) + { + end_sequence (); + return false; + } + + if (target != if_info->x) + noce_emit_move_insn (if_info->x, target); + + seq = end_ifcvt_sequence (if_info); + + if (!seq) + return false; + + emit_insn_before_setloc (seq, if_info->jump, + INSN_LOCATION (if_info->insn_a)); + if_info->transform_name = "noce_try_inverse_constants"; + return true; + } + + end_sequence (); + return false; +} + + /* Convert "if (test) x = a; else x = b", for A and B constant. Also allow A = y + c1, B = y + c2, with a common y between A and B. */ @@ -1193,8 +1340,11 @@ noce_try_store_flag_constants (struct noce_if_info *if_info) && CONST_INT_P (XEXP (a, 1)) && CONST_INT_P (XEXP (b, 1)) && rtx_equal_p (XEXP (a, 0), XEXP (b, 0)) - && noce_operand_ok (XEXP (a, 0)) - && if_info->branch_cost >= 2) + /* Allow expressions that are not using the result or plain + registers where we handle overlap below. */ + && (REG_P (XEXP (a, 0)) + || (noce_operand_ok (XEXP (a, 0)) + && ! reg_overlap_mentioned_p (if_info->x, XEXP (a, 0))))) { common = XEXP (a, 0); a = XEXP (a, 1); @@ -1267,22 +1417,24 @@ noce_try_store_flag_constants (struct noce_if_info *if_info) else gcc_unreachable (); } - else if (ifalse == 0 && exact_log2 (itrue) >= 0 - && (STORE_FLAG_VALUE == 1 - || if_info->branch_cost >= 2)) + /* Is this (cond) ? 2^n : 0? */ + else if (ifalse == 0 && pow2p_hwi (itrue) + && STORE_FLAG_VALUE == 1) normalize = 1; - else if (itrue == 0 && exact_log2 (ifalse) >= 0 && can_reverse - && (STORE_FLAG_VALUE == 1 || if_info->branch_cost >= 2)) + /* Is this (cond) ? 0 : 2^n? */ + else if (itrue == 0 && pow2p_hwi (ifalse) && can_reverse + && STORE_FLAG_VALUE == 1) { normalize = 1; reversep = true; } + /* Is this (cond) ? -1 : x? */ else if (itrue == -1 - && (STORE_FLAG_VALUE == -1 - || if_info->branch_cost >= 2)) + && STORE_FLAG_VALUE == -1) normalize = -1; + /* Is this (cond) ? x : -1? */ else if (ifalse == -1 && can_reverse - && (STORE_FLAG_VALUE == -1 || if_info->branch_cost >= 2)) + && STORE_FLAG_VALUE == -1) { normalize = -1; reversep = true; @@ -1371,11 +1523,13 @@ noce_try_store_flag_constants (struct noce_if_info *if_info) noce_emit_move_insn (if_info->x, target); seq = end_ifcvt_sequence (if_info); - if (!seq) + if (!seq || !noce_conversion_profitable_p (seq, if_info)) return FALSE; emit_insn_before_setloc (seq, if_info->jump, INSN_LOCATION (if_info->insn_a)); + if_info->transform_name = "noce_try_store_flag_constants"; + return TRUE; } @@ -1423,21 +1577,23 @@ noce_try_addcc (struct noce_if_info *if_info) noce_emit_move_insn (if_info->x, target); seq = end_ifcvt_sequence (if_info); - if (!seq) + if (!seq || !noce_conversion_profitable_p (seq, if_info)) return FALSE; emit_insn_before_setloc (seq, if_info->jump, INSN_LOCATION (if_info->insn_a)); + if_info->transform_name = "noce_try_addcc"; + return TRUE; } end_sequence (); } /* If that fails, construct conditional increment or decrement using - setcc. */ - if (if_info->branch_cost >= 2 - && (XEXP (if_info->a, 1) == const1_rtx - || XEXP (if_info->a, 1) == constm1_rtx)) + setcc. We're changing a branch and an increment to a comparison and + an ADD/SUB. */ + if (XEXP (if_info->a, 1) == const1_rtx + || XEXP (if_info->a, 1) == constm1_rtx) { start_sequence (); if (STORE_FLAG_VALUE == INTVAL (XEXP (if_info->a, 1))) @@ -1463,11 +1619,12 @@ noce_try_addcc (struct noce_if_info *if_info) noce_emit_move_insn (if_info->x, target); seq = end_ifcvt_sequence (if_info); - if (!seq) + if (!seq || !noce_conversion_profitable_p (seq, if_info)) return FALSE; emit_insn_before_setloc (seq, if_info->jump, INSN_LOCATION (if_info->insn_a)); + if_info->transform_name = "noce_try_addcc"; return TRUE; } end_sequence (); @@ -1490,15 +1647,14 @@ noce_try_store_flag_mask (struct noce_if_info *if_info) return FALSE; reversep = 0; - if ((if_info->branch_cost >= 2 - || STORE_FLAG_VALUE == -1) - && ((if_info->a == const0_rtx - && rtx_equal_p (if_info->b, if_info->x)) - || ((reversep = (reversed_comparison_code (if_info->cond, - if_info->jump) - != UNKNOWN)) - && if_info->b == const0_rtx - && rtx_equal_p (if_info->a, if_info->x)))) + + if ((if_info->a == const0_rtx + && rtx_equal_p (if_info->b, if_info->x)) + || ((reversep = (reversed_comparison_code (if_info->cond, + if_info->jump) + != UNKNOWN)) + && if_info->b == const0_rtx + && rtx_equal_p (if_info->a, if_info->x))) { start_sequence (); target = noce_emit_store_flag (if_info, @@ -1512,26 +1668,17 @@ noce_try_store_flag_mask (struct noce_if_info *if_info) if (target) { - int old_cost, new_cost, insn_cost; - int speed_p; - if (target != if_info->x) noce_emit_move_insn (if_info->x, target); seq = end_ifcvt_sequence (if_info); - if (!seq) - return FALSE; - - speed_p = optimize_bb_for_speed_p (BLOCK_FOR_INSN (if_info->insn_a)); - insn_cost = insn_rtx_cost (PATTERN (if_info->insn_a), speed_p); - old_cost = COSTS_N_INSNS (if_info->branch_cost) + insn_cost; - new_cost = seq_cost (seq, speed_p); - - if (new_cost > old_cost) + if (!seq || !noce_conversion_profitable_p (seq, if_info)) return FALSE; emit_insn_before_setloc (seq, if_info->jump, INSN_LOCATION (if_info->insn_a)); + if_info->transform_name = "noce_try_store_flag_mask"; + return TRUE; } @@ -1682,6 +1829,8 @@ noce_try_cmove (struct noce_if_info *if_info) emit_insn_before_setloc (seq, if_info->jump, INSN_LOCATION (if_info->insn_a)); + if_info->transform_name = "noce_try_cmove"; + return TRUE; } /* If both a and b are constants try a last-ditch transformation: @@ -1692,9 +1841,7 @@ noce_try_cmove (struct noce_if_info *if_info) we don't know about, so give them a chance before trying this approach. */ else if (!targetm.have_conditional_execution () - && CONST_INT_P (if_info->a) && CONST_INT_P (if_info->b) - && ((if_info->branch_cost >= 2 && STORE_FLAG_VALUE == -1) - || if_info->branch_cost >= 3)) + && CONST_INT_P (if_info->a) && CONST_INT_P (if_info->b)) { machine_mode mode = GET_MODE (if_info->x); HOST_WIDE_INT ifalse = INTVAL (if_info->a); @@ -1730,11 +1877,12 @@ noce_try_cmove (struct noce_if_info *if_info) noce_emit_move_insn (if_info->x, target); seq = end_ifcvt_sequence (if_info); - if (!seq) + if (!seq || !noce_conversion_profitable_p (seq, if_info)) return FALSE; emit_insn_before_setloc (seq, if_info->jump, INSN_LOCATION (if_info->insn_a)); + if_info->transform_name = "noce_try_cmove"; return TRUE; } else @@ -1789,11 +1937,13 @@ insn_valid_noce_process_p (rtx_insn *insn, rtx cc) } -/* Return true iff the registers that the insns in BB_A set do not - get used in BB_B. */ +/* Return true iff the registers that the insns in BB_A set do not get + used in BB_B. If TO_RENAME is non-NULL then it is a location that will be + renamed later by the caller and so conflicts on it should be ignored + in this function. */ static bool -bbs_ok_for_cmove_arith (basic_block bb_a, basic_block bb_b) +bbs_ok_for_cmove_arith (basic_block bb_a, basic_block bb_b, rtx to_rename) { rtx_insn *a_insn; bitmap bba_sets = BITMAP_ALLOC (®_obstack); @@ -1813,10 +1963,10 @@ bbs_ok_for_cmove_arith (basic_block bb_a, basic_block bb_b) BITMAP_FREE (bba_sets); return false; } - /* Record all registers that BB_A sets. */ FOR_EACH_INSN_DEF (def, a_insn) - bitmap_set_bit (bba_sets, DF_REF_REGNO (def)); + if (!(to_rename && DF_REF_REG (def) == to_rename)) + bitmap_set_bit (bba_sets, DF_REF_REGNO (def)); } rtx_insn *b_insn; @@ -1835,8 +1985,15 @@ bbs_ok_for_cmove_arith (basic_block bb_a, basic_block bb_b) } /* Make sure this is a REG and not some instance - of ZERO_EXTRACT or SUBREG or other dangerous stuff. */ - if (!REG_P (SET_DEST (sset_b))) + of ZERO_EXTRACT or SUBREG or other dangerous stuff. + If we have a memory destination then we have a pair of simple + basic blocks performing an operation of the form [addr] = c ? a : b. + bb_valid_for_noce_process_p will have ensured that these are + the only stores present. In that case [addr] should be the location + to be renamed. Assert that the callers set this up properly. */ + if (MEM_P (SET_DEST (sset_b))) + gcc_assert (rtx_equal_p (SET_DEST (sset_b), to_rename)); + else if (!REG_P (SET_DEST (sset_b))) { BITMAP_FREE (bba_sets); return false; @@ -1933,11 +2090,9 @@ noce_try_cmove_arith (struct noce_if_info *if_info) conditional on their addresses followed by a load. Don't do this early because it'll screw alias analysis. Note that we've already checked for no side effects. */ - /* ??? FIXME: Magic number 5. */ if (cse_not_expected && MEM_P (a) && MEM_P (b) - && MEM_ADDR_SPACE (a) == MEM_ADDR_SPACE (b) - && if_info->branch_cost >= 5) + && MEM_ADDR_SPACE (a) == MEM_ADDR_SPACE (b)) { machine_mode address_mode = get_address_mode (a); @@ -1969,23 +2124,6 @@ noce_try_cmove_arith (struct noce_if_info *if_info) if (!can_conditionally_move_p (x_mode)) return FALSE; - unsigned int then_cost; - unsigned int else_cost; - if (insn_a) - then_cost = if_info->then_cost; - else - then_cost = 0; - - if (insn_b) - else_cost = if_info->else_cost; - else - else_cost = 0; - - /* We're going to execute one of the basic blocks anyway, so - bail out if the most expensive of the two blocks is unacceptable. */ - if (MAX (then_cost, else_cost) > COSTS_N_INSNS (if_info->branch_cost)) - return FALSE; - /* Possibly rearrange operands to make things come out more natural. */ if (reversed_comparison_code (if_info->cond, if_info->jump) != UNKNOWN) { @@ -2005,9 +2143,9 @@ noce_try_cmove_arith (struct noce_if_info *if_info) } } - if (then_bb && else_bb && !a_simple && !b_simple - && (!bbs_ok_for_cmove_arith (then_bb, else_bb) - || !bbs_ok_for_cmove_arith (else_bb, then_bb))) + if (then_bb && else_bb + && (!bbs_ok_for_cmove_arith (then_bb, else_bb, if_info->orig_x) + || !bbs_ok_for_cmove_arith (else_bb, then_bb, if_info->orig_x))) return FALSE; start_sequence (); @@ -2095,52 +2233,56 @@ noce_try_cmove_arith (struct noce_if_info *if_info) } } - /* If insn to set up A clobbers any registers B depends on, try to - swap insn that sets up A with the one that sets up B. If even - that doesn't help, punt. */ - modified_in_a = emit_a != NULL_RTX && modified_in_p (orig_b, emit_a); if (tmp_b && then_bb) { FOR_BB_INSNS (then_bb, tmp_insn) - if (modified_in_p (orig_b, tmp_insn)) + /* Don't check inside insn_a. We will have changed it to emit_a + with a destination that doesn't conflict. */ + if (!(insn_a && tmp_insn == insn_a) + && modified_in_p (orig_b, tmp_insn)) { modified_in_a = true; break; } } - if (emit_a && modified_in_a) - { - modified_in_b = emit_b != NULL_RTX && modified_in_p (orig_a, emit_b); - if (tmp_b && else_bb) - { - FOR_BB_INSNS (else_bb, tmp_insn) - if (modified_in_p (orig_a, tmp_insn)) - { - modified_in_b = true; - break; - } - - } - if (modified_in_b) - goto end_seq_and_fail; - if (!noce_emit_bb (emit_b, else_bb, b_simple)) - goto end_seq_and_fail; + modified_in_b = emit_b != NULL_RTX && modified_in_p (orig_a, emit_b); + if (tmp_a && else_bb) + { + FOR_BB_INSNS (else_bb, tmp_insn) + /* Don't check inside insn_b. We will have changed it to emit_b + with a destination that doesn't conflict. */ + if (!(insn_b && tmp_insn == insn_b) + && modified_in_p (orig_a, tmp_insn)) + { + modified_in_b = true; + break; + } + } - if (!noce_emit_bb (emit_a, then_bb, a_simple)) - goto end_seq_and_fail; - } - else - { - if (!noce_emit_bb (emit_a, then_bb, a_simple)) - goto end_seq_and_fail; + /* If insn to set up A clobbers any registers B depends on, try to + swap insn that sets up A with the one that sets up B. If even + that doesn't help, punt. */ + if (modified_in_a && !modified_in_b) + { + if (!noce_emit_bb (emit_b, else_bb, b_simple)) + goto end_seq_and_fail; - if (!noce_emit_bb (emit_b, else_bb, b_simple)) - goto end_seq_and_fail; + if (!noce_emit_bb (emit_a, then_bb, a_simple)) + goto end_seq_and_fail; + } + else if (!modified_in_a) + { + if (!noce_emit_bb (emit_a, then_bb, a_simple)) + goto end_seq_and_fail; - } + if (!noce_emit_bb (emit_b, else_bb, b_simple)) + goto end_seq_and_fail; + } + else + goto end_seq_and_fail; target = noce_emit_cmove (if_info, x, code, XEXP (if_info->cond, 0), XEXP (if_info->cond, 1), a, b); @@ -2170,11 +2312,12 @@ noce_try_cmove_arith (struct noce_if_info *if_info) noce_emit_move_insn (x, target); ifcvt_seq = end_ifcvt_sequence (if_info); - if (!ifcvt_seq) + if (!ifcvt_seq || !noce_conversion_profitable_p (ifcvt_seq, if_info)) return FALSE; emit_insn_before_setloc (ifcvt_seq, if_info->jump, INSN_LOCATION (if_info->insn_a)); + if_info->transform_name = "noce_try_cmove_arith"; return TRUE; end_seq_and_fail: @@ -2205,7 +2348,7 @@ noce_get_alt_condition (struct noce_if_info *if_info, rtx target, cond = XEXP (SET_SRC (set), 0); reverse = GET_CODE (XEXP (SET_SRC (set), 2)) == LABEL_REF - && LABEL_REF_LABEL (XEXP (SET_SRC (set), 2)) == JUMP_LABEL (if_info->jump); + && label_ref_label (XEXP (SET_SRC (set), 2)) == JUMP_LABEL (if_info->jump); if (if_info->then_else_reversed) reverse = !reverse; @@ -2266,28 +2409,32 @@ noce_get_alt_condition (struct noce_if_info *if_info, rtx target, switch (code) { case LT: - if (actual_val == desired_val + 1) + if (desired_val != HOST_WIDE_INT_MAX + && actual_val == desired_val + 1) { code = LE; op_b = GEN_INT (desired_val); } break; case LE: - if (actual_val == desired_val - 1) + if (desired_val != HOST_WIDE_INT_MIN + && actual_val == desired_val - 1) { code = LT; op_b = GEN_INT (desired_val); } break; case GT: - if (actual_val == desired_val - 1) + if (desired_val != HOST_WIDE_INT_MIN + && actual_val == desired_val - 1) { code = GE; op_b = GEN_INT (desired_val); } break; case GE: - if (actual_val == desired_val + 1) + if (desired_val != HOST_WIDE_INT_MAX + && actual_val == desired_val + 1) { code = GT; op_b = GEN_INT (desired_val); @@ -2427,6 +2574,7 @@ noce_try_minmax (struct noce_if_info *if_info) emit_insn_before_setloc (seq, if_info->jump, INSN_LOCATION (if_info->insn_a)); if_info->cond = cond; if_info->cond_earliest = earliest; + if_info->transform_name = "noce_try_minmax"; return TRUE; } @@ -2518,12 +2666,26 @@ noce_try_abs (struct noce_if_info *if_info) /* Work around funny ideas get_condition has wrt canonicalization. Note that these rtx constants are known to be CONST_INT, and - therefore imply integer comparisons. */ + therefore imply integer comparisons. + The one_cmpl case is more complicated, as we want to handle + only x < 0 ? ~x : x or x >= 0 ? x : ~x to one_cmpl_abs (x) + and x < 0 ? x : ~x or x >= 0 ? ~x : x to ~one_cmpl_abs (x), + but not other cases (x > -1 is equivalent of x >= 0). */ if (c == constm1_rtx && GET_CODE (cond) == GT) ; else if (c == const1_rtx && GET_CODE (cond) == LT) - ; - else if (c != CONST0_RTX (GET_MODE (b))) + { + if (one_cmpl) + return FALSE; + } + else if (c == CONST0_RTX (GET_MODE (b))) + { + if (one_cmpl + && GET_CODE (cond) != GE + && GET_CODE (cond) != LT) + return FALSE; + } + else return FALSE; /* Determine what sort of operation this is. */ @@ -2579,6 +2741,7 @@ noce_try_abs (struct noce_if_info *if_info) emit_insn_before_setloc (seq, if_info->jump, INSN_LOCATION (if_info->insn_a)); if_info->cond = cond; if_info->cond_earliest = earliest; + if_info->transform_name = "noce_try_abs"; return TRUE; } @@ -2635,7 +2798,7 @@ noce_try_sign_mask (struct noce_if_info *if_info) && (if_info->insn_b == NULL_RTX || BLOCK_FOR_INSN (if_info->insn_b) == if_info->test_bb)); if (!(t_unconditional - || (set_src_cost (t, mode, optimize_bb_for_speed_p (if_info->test_bb)) + || (set_src_cost (t, mode, if_info->speed_p) < COSTS_N_INSNS (2)))) return FALSE; @@ -2660,6 +2823,8 @@ noce_try_sign_mask (struct noce_if_info *if_info) return FALSE; emit_insn_before_setloc (seq, if_info->jump, INSN_LOCATION (if_info->insn_a)); + if_info->transform_name = "noce_try_sign_mask"; + return TRUE; } @@ -2718,7 +2883,7 @@ noce_try_bitop (struct noce_if_info *if_info) if (! rtx_equal_p (x, XEXP (a, 0)) || !CONST_INT_P (XEXP (a, 1)) || (INTVAL (XEXP (a, 1)) & GET_MODE_MASK (mode)) - != (unsigned HOST_WIDE_INT) 1 << bitnum) + != HOST_WIDE_INT_1U << bitnum) return FALSE; /* if ((x & C) == 0) x |= C; is transformed to x |= C. */ @@ -2728,13 +2893,13 @@ noce_try_bitop (struct noce_if_info *if_info) else if (code == NE) { /* if ((x & C) == 0) x ^= C; is transformed to x |= C. */ - result = gen_int_mode ((HOST_WIDE_INT) 1 << bitnum, mode); + result = gen_int_mode (HOST_WIDE_INT_1 << bitnum, mode); result = simplify_gen_binary (IOR, mode, x, result); } else { /* if ((x & C) != 0) x ^= C; is transformed to x &= ~C. */ - result = gen_int_mode (~((HOST_WIDE_INT) 1 << bitnum), mode); + result = gen_int_mode (~(HOST_WIDE_INT_1 << bitnum), mode); result = simplify_gen_binary (AND, mode, x, result); } } @@ -2744,7 +2909,7 @@ noce_try_bitop (struct noce_if_info *if_info) if (! rtx_equal_p (x, XEXP (a, 0)) || !CONST_INT_P (XEXP (a, 1)) || (INTVAL (XEXP (a, 1)) & GET_MODE_MASK (mode)) - != (~((HOST_WIDE_INT) 1 << bitnum) & GET_MODE_MASK (mode))) + != (~(HOST_WIDE_INT_1 << bitnum) & GET_MODE_MASK (mode))) return FALSE; /* if ((x & C) == 0) x &= ~C; is transformed to nothing. */ @@ -2765,6 +2930,7 @@ noce_try_bitop (struct noce_if_info *if_info) emit_insn_before_setloc (seq, if_info->jump, INSN_LOCATION (if_info->insn_a)); } + if_info->transform_name = "noce_try_bitop"; return TRUE; } @@ -2789,7 +2955,7 @@ noce_get_condition (rtx_insn *jump, rtx_insn **earliest, bool then_else_reversed /* If this branches to JUMP_LABEL when the condition is false, reverse the condition. */ reverse = (GET_CODE (XEXP (SET_SRC (set), 2)) == LABEL_REF - && LABEL_REF_LABEL (XEXP (SET_SRC (set), 2)) == JUMP_LABEL (jump)); + && label_ref_label (XEXP (SET_SRC (set), 2)) == JUMP_LABEL (jump)); /* We may have to reverse because the caller's if block is not canonical, i.e. the THEN block isn't the fallthrough block for the TEST block @@ -2842,97 +3008,6 @@ noce_operand_ok (const_rtx op) return ! may_trap_p (op); } -/* Return true if a write into MEM may trap or fault. */ - -static bool -noce_mem_write_may_trap_or_fault_p (const_rtx mem) -{ - rtx addr; - - if (MEM_READONLY_P (mem)) - return true; - - if (may_trap_or_fault_p (mem)) - return true; - - addr = XEXP (mem, 0); - - /* Call target hook to avoid the effects of -fpic etc.... */ - addr = targetm.delegitimize_address (addr); - - while (addr) - switch (GET_CODE (addr)) - { - case CONST: - case PRE_DEC: - case PRE_INC: - case POST_DEC: - case POST_INC: - case POST_MODIFY: - addr = XEXP (addr, 0); - break; - case LO_SUM: - case PRE_MODIFY: - addr = XEXP (addr, 1); - break; - case PLUS: - if (CONST_INT_P (XEXP (addr, 1))) - addr = XEXP (addr, 0); - else - return false; - break; - case LABEL_REF: - return true; - case SYMBOL_REF: - if (SYMBOL_REF_DECL (addr) - && decl_readonly_section (SYMBOL_REF_DECL (addr), 0)) - return true; - return false; - default: - return false; - } - - return false; -} - -/* Return whether we can use store speculation for MEM. TOP_BB is the - basic block above the conditional block where we are considering - doing the speculative store. We look for whether MEM is set - unconditionally later in the function. */ - -static bool -noce_can_store_speculate_p (basic_block top_bb, const_rtx mem) -{ - basic_block dominator; - - for (dominator = get_immediate_dominator (CDI_POST_DOMINATORS, top_bb); - dominator != NULL; - dominator = get_immediate_dominator (CDI_POST_DOMINATORS, dominator)) - { - rtx_insn *insn; - - FOR_BB_INSNS (dominator, insn) - { - /* If we see something that might be a memory barrier, we - have to stop looking. Even if the MEM is set later in - the function, we still don't want to set it - unconditionally before the barrier. */ - if (INSN_P (insn) - && (volatile_insn_p (PATTERN (insn)) - || (CALL_P (insn) && (!RTL_CONST_CALL_P (insn))))) - return false; - - if (memory_must_be_modified_in_insn_p (mem, insn)) - return true; - if (modified_in_p (XEXP (mem, 0), insn)) - return false; - - } - } - - return false; -} - /* Return true if X contains a MEM subrtx. */ static bool @@ -2952,8 +3027,8 @@ contains_mem_rtx_p (rtx x) x := a and all previous computations in TEST_BB don't produce any values that are live after TEST_BB. In other words, all the insns in TEST_BB are there only - to compute a value for x. Put the rtx cost of the insns - in TEST_BB into COST. Record whether TEST_BB is a single simple + to compute a value for x. Add the rtx cost of the insns + in TEST_BB to COST. Record whether TEST_BB is a single simple set instruction in SIMPLE_P. */ static bool @@ -2985,7 +3060,7 @@ bb_valid_for_noce_process_p (basic_block test_bb, rtx cond, if (first_insn == last_insn) { *simple_p = noce_operand_ok (SET_DEST (first_set)); - *cost = insn_rtx_cost (first_set, speed_p); + *cost += insn_rtx_cost (first_set, speed_p); return *simple_p; } @@ -3032,7 +3107,7 @@ bb_valid_for_noce_process_p (basic_block test_bb, rtx cond, goto free_bitmap_and_fail; BITMAP_FREE (test_bb_temps); - *cost = potential_cost; + *cost += potential_cost; *simple_p = false; return true; @@ -3146,6 +3221,41 @@ noce_convert_multiple_sets (struct noce_if_info *if_info) if (if_info->then_else_reversed) std::swap (old_val, new_val); + + /* We allow simple lowpart register subreg SET sources in + bb_ok_for_noce_convert_multiple_sets. Be careful when processing + sequences like: + (set (reg:SI r1) (reg:SI r2)) + (set (reg:HI r3) (subreg:HI (r1))) + For the second insn new_val or old_val (r1 in this example) will be + taken from the temporaries and have the wider mode which will not + match with the mode of the other source of the conditional move, so + we'll end up trying to emit r4:HI = cond ? (r1:SI) : (r3:HI). + Wrap the two cmove operands into subregs if appropriate to prevent + that. */ + if (GET_MODE (new_val) != GET_MODE (temp)) + { + machine_mode src_mode = GET_MODE (new_val); + machine_mode dst_mode = GET_MODE (temp); + if (GET_MODE_SIZE (src_mode) <= GET_MODE_SIZE (dst_mode)) + { + end_sequence (); + return FALSE; + } + new_val = lowpart_subreg (dst_mode, new_val, src_mode); + } + if (GET_MODE (old_val) != GET_MODE (temp)) + { + machine_mode src_mode = GET_MODE (old_val); + machine_mode dst_mode = GET_MODE (temp); + if (GET_MODE_SIZE (src_mode) <= GET_MODE_SIZE (dst_mode)) + { + end_sequence (); + return FALSE; + } + old_val = lowpart_subreg (dst_mode, old_val, src_mode); + } + /* Actually emit the conditional move. */ rtx temp_dest = noce_emit_cmove (if_info, temp, cond_code, x, y, new_val, old_val); @@ -3173,9 +3283,15 @@ noce_convert_multiple_sets (struct noce_if_info *if_info) for (int i = 0; i < count; i++) noce_emit_move_insn (targets[i], temporaries[i]); - /* Actually emit the sequence. */ + /* Actually emit the sequence if it isn't too expensive. */ rtx_insn *seq = get_insns (); + if (!noce_conversion_profitable_p (seq, if_info)) + { + end_sequence (); + return FALSE; + } + for (insn = seq; insn; insn = NEXT_INSN (insn)) set_used_flags (insn); @@ -3219,25 +3335,22 @@ noce_convert_multiple_sets (struct noce_if_info *if_info) } num_updated_if_blocks++; + if_info->transform_name = "noce_convert_multiple_sets"; return TRUE; } /* Return true iff basic block TEST_BB is comprised of only (SET (REG) (REG)) insns suitable for conversion to a series - of conditional moves. FORNOW: Use II to find the expected cost of - the branch into/over TEST_BB. - - TODO: This creates an implicit "magic number" for branch_cost. - II->branch_cost now guides the maximum number of set instructions in - a basic block which is considered profitable to completely - if-convert. */ + of conditional moves. Also check that we have more than one set + (other routines can handle a single set better than we would), and + fewer than PARAM_MAX_RTL_IF_CONVERSION_INSNS sets. */ static bool -bb_ok_for_noce_convert_multiple_sets (basic_block test_bb, - struct noce_if_info *ii) +bb_ok_for_noce_convert_multiple_sets (basic_block test_bb) { rtx_insn *insn; unsigned count = 0; + unsigned param = PARAM_VALUE (PARAM_MAX_RTL_IF_CONVERSION_INSNS); FOR_BB_INSNS (test_bb, insn) { @@ -3254,9 +3367,15 @@ bb_ok_for_noce_convert_multiple_sets (basic_block test_bb, rtx src = SET_SRC (set); /* We can possibly relax this, but for now only handle REG to REG - moves. This avoids any issues that might come from introducing - loads/stores that might violate data-race-freedom guarantees. */ - if (!(REG_P (src) && REG_P (dest))) + (including subreg) moves. This avoids any issues that might come + from introducing loads/stores that might violate data-race-freedom + guarantees. */ + if (!REG_P (dest)) + return false; + + if (!(REG_P (src) + || (GET_CODE (src) == SUBREG && REG_P (SUBREG_REG (src)) + && subreg_lowpart_p (src)))) return false; /* Destination must be appropriate for a conditional write. */ @@ -3267,16 +3386,15 @@ bb_ok_for_noce_convert_multiple_sets (basic_block test_bb, if (!can_conditionally_move_p (GET_MODE (dest))) return false; - ++count; + count++; } - /* FORNOW: Our cost model is a count of the number of instructions we - would if-convert. This is suboptimal, and should be improved as part - of a wider rework of branch_cost. */ - if (count > ii->branch_cost) - return FALSE; - - return count > 0; + /* If we would only put out one conditional move, the other strategies + this pass tries are better optimized and will be more appropriate. + Some targets want to strictly limit the number of conditional moves + that are emitted, they set this through PARAM, we need to respect + that. */ + return count > 1 && count <= param; } /* Given a simple IF-THEN-JOIN or IF-THEN-ELSE-JOIN block, attempt to convert @@ -3312,18 +3430,23 @@ noce_process_if_block (struct noce_if_info *if_info) if (!else_bb && HAVE_conditional_move && !HAVE_cc0 - && bb_ok_for_noce_convert_multiple_sets (then_bb, if_info)) + && bb_ok_for_noce_convert_multiple_sets (then_bb)) { if (noce_convert_multiple_sets (if_info)) - return TRUE; + { + if (dump_file && if_info->transform_name) + fprintf (dump_file, "if-conversion succeeded through %s\n", + if_info->transform_name); + return TRUE; + } } - if (! bb_valid_for_noce_process_p (then_bb, cond, &if_info->then_cost, + if (! bb_valid_for_noce_process_p (then_bb, cond, &if_info->original_cost, &if_info->then_simple)) return false; if (else_bb - && ! bb_valid_for_noce_process_p (else_bb, cond, &if_info->else_cost, + && ! bb_valid_for_noce_process_p (else_bb, cond, &if_info->original_cost, &if_info->else_simple)) return false; @@ -3396,6 +3519,7 @@ noce_process_if_block (struct noce_if_info *if_info) /* Only operate on register destinations, and even then avoid extending the lifetime of hard registers on small register class machines. */ orig_x = x; + if_info->orig_x = orig_x; if (!REG_P (x) || (HARD_REGISTER_P (x) && targetm.small_register_classes_for_mode_p (GET_MODE (x)))) @@ -3462,33 +3586,17 @@ noce_process_if_block (struct noce_if_info *if_info) } if (!set_b && MEM_P (orig_x)) - { - /* Disallow the "if (...) x = a;" form (implicit "else x = x;") - for optimizations if writing to x may trap or fault, - i.e. it's a memory other than a static var or a stack slot, - is misaligned on strict aligned machines or is read-only. If - x is a read-only memory, then the program is valid only if we - avoid the store into it. If there are stores on both the - THEN and ELSE arms, then we can go ahead with the conversion; - either the program is broken, or the condition is always - false such that the other memory is selected. */ - if (noce_mem_write_may_trap_or_fault_p (orig_x)) - return FALSE; - - /* Avoid store speculation: given "if (...) x = a" where x is a - MEM, we only want to do the store if x is always set - somewhere in the function. This avoids cases like - if (pthread_mutex_trylock(mutex)) - ++global_variable; - where we only want global_variable to be changed if the mutex - is held. FIXME: This should ideally be expressed directly in - RTL somehow. */ - if (!noce_can_store_speculate_p (test_bb, orig_x)) - return FALSE; - } + /* We want to avoid store speculation to avoid cases like + if (pthread_mutex_trylock(mutex)) + ++global_variable; + Rather than go to much effort here, we rely on the SSA optimizers, + which do a good enough job these days. */ + return FALSE; if (noce_try_move (if_info)) goto success; + if (noce_try_ifelse_collapse (if_info)) + goto success; if (noce_try_store_flag (if_info)) goto success; if (noce_try_bitop (if_info)) @@ -3497,6 +3605,8 @@ noce_process_if_block (struct noce_if_info *if_info) goto success; if (noce_try_abs (if_info)) goto success; + if (noce_try_inverse_constants (if_info)) + goto success; if (!targetm.have_conditional_execution () && noce_try_store_flag_constants (if_info)) goto success; @@ -3527,6 +3637,9 @@ noce_process_if_block (struct noce_if_info *if_info) return FALSE; success: + if (dump_file && if_info->transform_name) + fprintf (dump_file, "if-conversion succeeded through %s\n", + if_info->transform_name); /* If we used a temporary, fix it up now. */ if (orig_x != x) @@ -3739,6 +3852,7 @@ cond_move_process_if_block (struct noce_if_info *if_info) vec else_regs = vNULL; unsigned int i; int success_p = FALSE; + int limit = PARAM_VALUE (PARAM_MAX_RTL_IF_CONVERSION_INSNS); /* Build a mapping for each block to the value used for each register. */ @@ -3788,7 +3902,8 @@ cond_move_process_if_block (struct noce_if_info *if_info) is the number of assignments currently made in only one of the branches, since if we convert we are going to always execute them. */ - if (c > MAX_CONDITIONAL_EXECUTE) + if (c > MAX_CONDITIONAL_EXECUTE + || c > limit) goto done; /* Try to emit the conditional moves. First do the then block, @@ -3835,7 +3950,6 @@ cond_move_process_if_block (struct noce_if_info *if_info) } num_updated_if_blocks++; - success_p = TRUE; done: @@ -3863,6 +3977,7 @@ noce_find_if_block (basic_block test_bb, edge then_edge, edge else_edge, rtx cond; rtx_insn *cond_earliest; struct noce_if_info if_info; + bool speed_p = optimize_bb_for_speed_p (test_bb); /* We only ever should get here before reload. */ gcc_assert (!reload_completed); @@ -3954,8 +4069,16 @@ noce_find_if_block (basic_block test_bb, edge then_edge, edge else_edge, if_info.cond_earliest = cond_earliest; if_info.jump = jump; if_info.then_else_reversed = then_else_reversed; - if_info.branch_cost = BRANCH_COST (optimize_bb_for_speed_p (test_bb), - predictable_edge_p (then_edge)); + if_info.speed_p = speed_p; + if_info.max_seq_cost + = targetm.max_noce_ifcvt_seq_cost (then_edge); + /* We'll add in the cost of THEN_BB and ELSE_BB later, when we check + that they are valid to transform. We can't easily get back to the insn + for COND (and it may not exist if we had to canonicalize to get COND), + and jump_insns are always given a cost of 1 by seq_cost, so treat + both instructions as having cost COSTS_N_INSNS (1). */ + if_info.original_cost = COSTS_N_INSNS (2); + /* Do the real work. */ @@ -4538,8 +4661,11 @@ find_cond_trap (basic_block test_bb, edge then_edge, edge else_edge) return FALSE; /* If the conditional jump is more than just a conditional jump, then - we can not do if-conversion on this block. */ - if (! onlyjump_p (jump)) + we can not do if-conversion on this block. Give up for returnjump_p, + changing a conditional return followed by unconditional trap for + conditional trap followed by unconditional return is likely not + beneficial and harder to handle. */ + if (! onlyjump_p (jump) || returnjump_p (jump)) return FALSE; /* We must be comparing objects whose modes imply the size. */ @@ -4819,7 +4945,6 @@ find_if_case_1 (basic_block test_bb, edge then_edge, edge else_edge) num_true_changes++; num_updated_if_blocks++; - return TRUE; }