On Wed, Dec 13, 2017 at 10:30 PM, David Malcolm <dmalc...@redhat.com> wrote: > On Wed, 2017-12-13 at 10:47 -0700, Jeff Law wrote: >> On 12/13/2017 09:24 AM, Richard Biener wrote: >> > > >> > > Alternately we could to the dom_walker ctor that an initial state >> > > of >> > > EDGE_EXECUTABLE is already set. >> > >> > I'm quite sure that wouldn't help for VRP. >> >> Not sure why. But it's not worth digging deep into. >> >> I do think the current structure could still fail to pick up some >> secondary cases where blocks become unreachable as a result of both >> not >> needing to visit during the lattice propagation step and the >> substitution step. But I'd expect this to be rare. >> >> > I think David's approach is fine just we don't need any other API >> > to get at a known executable outgoing edge. We can improve the >> > existing one or just add the trivial folding required. >> >> I think Michael's suggestion to pass in NULL for the value and allow >> find_edge to try and determine the value makes the most sense here. >> >> Jeff > > Michael: thanks for the hint about find_taken_edge; I assumed that such > a "find the relevant out-edge" function would already exist; but I > didn't find it (I'm relatively unfamiliar with this part of the code). > > Here's an updated version of the patch, which eliminates the stuff I > added to gimple.h/gimple.c changes in favor of using > find_taken_edge (bb, NULL_TREE), > generalizing it to work with arbitrary bbs, so that the dom_walker > vfunc can simply use: > return find_taken_edge (bb, NULL_TREE); > without having to check e.g. for there being a last stmt (ENTRY > and EXIT), or having to check that it is indeed a control statement > (is there a requirement at this point of the IR that we don't just > fall off the last statment through an out-edge?) > > I handled var == NULL_TREE for GIMPLE_COND and GIMPLE_SWITCH, > but not for computed goto (find_taken_edge already handles that by > bailing out). > > I also made some things "const" whilst I was touching it. > > Successfully bootstrapped®rtested on x86_64-pc-linux-gnu. > > OK for trunk? > > gcc/ChangeLog: > PR tree-optimization/83312 > * domwalk.h (dom_walker::dom_walker): Fix typo in comment. > * tree-cfg.c (find_taken_edge): Update to handle NULL_TREE for > "val" param, and to cope with arbitrary basic blocks. > (find_taken_edge_cond_expr): Add "cond_stmt" param and use it to > handle NULL_TREE for "val". > (find_taken_edge_switch_expr): Make "switch_stmt" param const. > Handle NULL_TREE for "val". > (find_case_label_for_value): Make "switch_stmt" param const. > * tree-vrp.c (class check_array_bounds_dom_walker): New subclass > of dom_walker. > (vrp_prop::check_all_array_refs): Reimplement as... > (check_array_bounds_dom_walker::before_dom_children): ...this new > vfunc. Replace linear search through BB block list, excluding > those with non-executable in-edges via dominator walk. > > gcc/testsuite/ChangeLog: > PR tree-optimization/83312 > * gcc.dg/pr83312.c: New test case. > --- > gcc/domwalk.h | 2 +- > gcc/testsuite/gcc.dg/pr83312.c | 30 +++++++++++++++++ > gcc/tree-cfg.c | 59 +++++++++++++++++++++----------- > gcc/tree-vrp.c | 76 > ++++++++++++++++++++++++++---------------- > 4 files changed, 117 insertions(+), 50 deletions(-) > create mode 100644 gcc/testsuite/gcc.dg/pr83312.c > > diff --git a/gcc/domwalk.h b/gcc/domwalk.h > index 6ac93eb..c7e3450 100644 > --- a/gcc/domwalk.h > +++ b/gcc/domwalk.h > @@ -32,7 +32,7 @@ class dom_walker > public: > static const edge STOP; > > - /* Use SKIP_UNREACHBLE_BLOCKS = true when your client can discover > + /* Use SKIP_UNREACHABLE_BLOCKS = true when your client can discover > that some edges are not executable. > > If a client can discover that a COND, SWITCH or GOTO has a static > diff --git a/gcc/testsuite/gcc.dg/pr83312.c b/gcc/testsuite/gcc.dg/pr83312.c > new file mode 100644 > index 0000000..2eb241d > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/pr83312.c > @@ -0,0 +1,30 @@ > +/* { dg-options "-O2 -Warray-bounds" } */ > + > +struct ptlrpcd_ctl { > + char pc_name[20]; > +}; > +struct ptlrpcd { > + struct ptlrpcd_ctl pd_threads[6]; > +}; > +struct ptlrpcd *ptlrpcd_init_pd; > +static void ptlrpcd_ctl_init(struct ptlrpcd_ctl *pc, int index) { > + if (index < 0) > + __builtin_snprintf(pc->pc_name, sizeof(pc->pc_name), "ptlrpcd_rcv"); > + else > + __builtin_snprintf(pc->pc_name, sizeof(pc->pc_name), "ptlrpcd_%d", > index); > +} > +int ptlrpcd_init_ncpts; > +static int ptlrpcd_init(int nthreads) { > + int j; > + if (ptlrpcd_init_ncpts) { > + ptlrpcd_ctl_init(&ptlrpcd_init_pd->pd_threads[0], -1); > + for (j = 1; j < nthreads; j++) > + ptlrpcd_ctl_init(&ptlrpcd_init_pd->pd_threads[j], j); > + } > + return 0; > +} > +int ptlrpcd_init_groupsize; > +void ptlrpcd_addref(void) { > + ptlrpcd_init(ptlrpcd_init_groupsize); > +} > + > diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c > index 4d09b2c..7ecc5c8 100644 > --- a/gcc/tree-cfg.c > +++ b/gcc/tree-cfg.c > @@ -170,9 +170,9 @@ static void gimple_merge_blocks (basic_block, > basic_block); > static bool gimple_can_merge_blocks_p (basic_block, basic_block); > static void remove_bb (basic_block); > static edge find_taken_edge_computed_goto (basic_block, tree); > -static edge find_taken_edge_cond_expr (basic_block, tree); > -static edge find_taken_edge_switch_expr (gswitch *, basic_block, tree); > -static tree find_case_label_for_value (gswitch *, tree); > +static edge find_taken_edge_cond_expr (const gcond *, basic_block, tree); > +static edge find_taken_edge_switch_expr (const gswitch *, basic_block, tree); > +static tree find_case_label_for_value (const gswitch *, tree); > static void lower_phi_internal_fn (); > > void > @@ -2254,9 +2254,10 @@ remove_bb (basic_block bb) > } > > > -/* Given a basic block BB ending with COND_EXPR or SWITCH_EXPR, and a > - predicate VAL, return the edge that will be taken out of the block. > - If VAL does not match a unique edge, NULL is returned. */ > +/* Given a basic block BB and a predicate VAL for use in the final statement > + of the block, return the edge that will be taken out of the block. > + If VAL does not match a unique edge, NULL is returned. > + If VAL is NULL_TREE, then the current value of the predicate is used. */ > > edge > find_taken_edge (basic_block bb, tree val) > @@ -2265,10 +2266,12 @@ find_taken_edge (basic_block bb, tree val) > > stmt = last_stmt (bb); > > - gcc_assert (is_ctrl_stmt (stmt)); > + /* Handle ENTRY and EXIT. */ > + if (!stmt) > + return NULL; > > if (gimple_code (stmt) == GIMPLE_COND) > - return find_taken_edge_cond_expr (bb, val); > + return find_taken_edge_cond_expr (as_a <gcond *> (stmt), bb, val); > > if (gimple_code (stmt) == GIMPLE_SWITCH) > return find_taken_edge_switch_expr (as_a <gswitch *> (stmt), bb, val); > @@ -2285,10 +2288,10 @@ find_taken_edge (basic_block bb, tree val) > && (TREE_CODE (val) == ADDR_EXPR || TREE_CODE (val) == LABEL_EXPR) > && TREE_CODE (TREE_OPERAND (val, 0)) == LABEL_DECL) > return find_taken_edge_computed_goto (bb, TREE_OPERAND (val, 0)); > - return NULL; > } > > - gcc_unreachable (); > + /* Unable to find a unique edge. */ > + return NULL;
Not sure if important but if you want to handle arbitrary BBs then surely a single successor edge (a fallthru) would qualify? I think your updated comment doesn't reflect that passing a BB without a "predicate" is a valid input. So... return single_succ_p (bb) ? single_succ_edge (bb) : NULL; ? > } > > /* Given a constant value VAL and the entry block BB to a GOTO_EXPR > @@ -2313,15 +2316,25 @@ find_taken_edge_computed_goto (basic_block bb, tree > val) > > /* Given a constant value VAL and the entry block BB to a COND_EXPR > statement, determine which of the two edges will be taken out of the > - block. Return NULL if either edge may be taken. */ > + block. Return NULL if either edge may be taken. > + If VAL is NULL_TREE, then the current value of the predicate is used. */ > > static edge > -find_taken_edge_cond_expr (basic_block bb, tree val) > +find_taken_edge_cond_expr (const gcond *cond_stmt, basic_block bb, tree val) > { here and below the BB argument is somewhat redundant given the stmt has a reference to it via gimple_bb (). Let's remove it. Ok with those changes. Richard. > edge true_edge, false_edge; > > - if (val == NULL > - || TREE_CODE (val) != INTEGER_CST) > + if (val == NULL_TREE) > + { > + /* Use the current value of the predicate. */ > + if (gimple_cond_true_p (cond_stmt)) > + val = integer_one_node; > + else if (gimple_cond_false_p (cond_stmt)) > + val = integer_zero_node; > + else > + return NULL; > + } > + else if (TREE_CODE (val) != INTEGER_CST) > return NULL; > > extract_true_false_edges_from_block (bb, &true_edge, &false_edge); > @@ -2331,10 +2344,11 @@ find_taken_edge_cond_expr (basic_block bb, tree val) > > /* Given an INTEGER_CST VAL and the entry block BB to a SWITCH_EXPR > statement, determine which edge will be taken out of the block. Return > - NULL if any edge may be taken. */ > + NULL if any edge may be taken. > + If VAL is NULL_TREE, then the current value of the index is used. */ > > static edge > -find_taken_edge_switch_expr (gswitch *switch_stmt, basic_block bb, > +find_taken_edge_switch_expr (const gswitch *switch_stmt, basic_block bb, > tree val) > { > basic_block dest_bb; > @@ -2343,10 +2357,15 @@ find_taken_edge_switch_expr (gswitch *switch_stmt, > basic_block bb, > > if (gimple_switch_num_labels (switch_stmt) == 1) > taken_case = gimple_switch_default_label (switch_stmt); > - else if (! val || TREE_CODE (val) != INTEGER_CST) > - return NULL; > else > - taken_case = find_case_label_for_value (switch_stmt, val); > + { > + if (val == NULL_TREE) > + val = gimple_switch_index (switch_stmt); > + if (TREE_CODE (val) != INTEGER_CST) > + return NULL; > + else > + taken_case = find_case_label_for_value (switch_stmt, val); > + } > dest_bb = label_to_block (CASE_LABEL (taken_case)); > > e = find_edge (bb, dest_bb); > @@ -2360,7 +2379,7 @@ find_taken_edge_switch_expr (gswitch *switch_stmt, > basic_block bb, > sorted: We can do a binary search for a case matching VAL. */ > > static tree > -find_case_label_for_value (gswitch *switch_stmt, tree val) > +find_case_label_for_value (const gswitch *switch_stmt, tree val) > { > size_t low, high, n = gimple_switch_num_labels (switch_stmt); > tree default_case = gimple_switch_default_label (switch_stmt); > diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c > index a86b382..fe778b0 100644 > --- a/gcc/tree-vrp.c > +++ b/gcc/tree-vrp.c > @@ -5000,44 +5000,62 @@ check_array_bounds (tree *tp, int *walk_subtree, void > *data) > return NULL_TREE; > } > > -/* Walk over all statements of all reachable BBs and call check_array_bounds > - on them. */ > +/* A dom_walker subclass for use by vrp_prop::check_all_array_refs, > + to walk over all statements of all reachable BBs and call > + check_array_bounds on them. */ > > -void > -vrp_prop::check_all_array_refs () > +class check_array_bounds_dom_walker : public dom_walker > { > - basic_block bb; > - gimple_stmt_iterator si; > + public: > + check_array_bounds_dom_walker (vrp_prop *prop) > + : dom_walker (CDI_DOMINATORS, true), m_prop (prop) {} > + ~check_array_bounds_dom_walker () {} > > - FOR_EACH_BB_FN (bb, cfun) > - { > - edge_iterator ei; > - edge e; > - bool executable = false; > + edge before_dom_children (basic_block) FINAL OVERRIDE; > > - /* Skip blocks that were found to be unreachable. */ > - FOR_EACH_EDGE (e, ei, bb->preds) > - executable |= !!(e->flags & EDGE_EXECUTABLE); > - if (!executable) > - continue; > + private: > + vrp_prop *m_prop; > +}; > > - for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) > - { > - gimple *stmt = gsi_stmt (si); > - struct walk_stmt_info wi; > - if (!gimple_has_location (stmt) > - || is_gimple_debug (stmt)) > - continue; > +/* Implementation of dom_walker::before_dom_children. > > - memset (&wi, 0, sizeof (wi)); > + Walk over all statements of BB and call check_array_bounds on them, > + and determine if there's a unique successor edge. */ > > - wi.info = this; > +edge > +check_array_bounds_dom_walker::before_dom_children (basic_block bb) > +{ > + gimple_stmt_iterator si; > + for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) > + { > + gimple *stmt = gsi_stmt (si); > + struct walk_stmt_info wi; > + if (!gimple_has_location (stmt) > + || is_gimple_debug (stmt)) > + continue; > > - walk_gimple_op (gsi_stmt (si), > - check_array_bounds, > - &wi); > - } > + memset (&wi, 0, sizeof (wi)); > + > + wi.info = m_prop; > + > + walk_gimple_op (stmt, check_array_bounds, &wi); > } > + > + /* Determine if there's a unique successor edge, and if so, return > + that back to dom_walker, ensuring that we don't visit blocks that > + became unreachable during the VRP propagation > + (PR tree-optimization/83312). */ > + return find_taken_edge (bb, NULL_TREE); > +} > + > +/* Walk over all statements of all reachable BBs and call check_array_bounds > + on them. */ > + > +void > +vrp_prop::check_all_array_refs () > +{ > + check_array_bounds_dom_walker w (this); > + w.walk (ENTRY_BLOCK_PTR_FOR_FN (cfun)); > } > > /* Return true if all imm uses of VAR are either in STMT, or > -- > 1.8.5.3 >