On Tue, Oct 23, 2012 at 2:52 AM, Richard Biener
<richard.guent...@gmail.com> wrote:
> On Mon, Oct 22, 2012 at 8:31 PM, Easwaran Raman <era...@google.com> wrote:
>> On Mon, Oct 22, 2012 at 12:59 AM, Richard Biener
>> <richard.guent...@gmail.com> wrote:
>>> On Fri, Oct 19, 2012 at 12:36 AM, Easwaran Raman <era...@google.com> wrote:
>>>> Hi,
>>>>
>>>> During expression reassociation, statements are conservatively moved
>>>> downwards to ensure that dependences are correctly satisfied after
>>>> reassocation. This could lead to lengthening of live ranges. This
>>>> patch moves statements only to the extent necessary. Bootstraps and no
>>>> test regression on x86_64/linux. OK for trunk?
>>>>
>>>> Thanks,
>>>> Easwaran
>>>>
>>>> 2012-10-18   Easwaran Raman  <era...@google.com>
>>>> * tree-ssa-reassoc.c(assign_uids): New function.
>>>> (assign_uids_in_relevant_bbs): Likewise.
>>>> (ensure_ops_are_available): Likewise.
>>>> (rewrite_expr_tree): Do not move statements beyond what is
>>>> necessary. Remove call to swap_ops_for_binary_stmt...
>>>> (reassociate_bb): ... and move it here.
>>>>
>>>>
>>>>
>>>> Index: gcc/tree-ssa-reassoc.c
>>>> ===================================================================
>>>> --- gcc/tree-ssa-reassoc.c (revision 192487)
>>>> +++ gcc/tree-ssa-reassoc.c (working copy)
>>>> @@ -2250,6 +2250,128 @@ swap_ops_for_binary_stmt (VEC(operand_entry_t, hea
>>>>      }
>>>>  }
>>>>
>>>> +/* Assign UIDs to statements in basic block BB.  */
>>>> +
>>>> +static void
>>>> +assign_uids (basic_block bb)
>>>> +{
>>>> +  unsigned uid = 0;
>>>> +  gimple_stmt_iterator gsi;
>>>> +  /* First assign uids to phis.  */
>>>> +  for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
>>>> +    {
>>>> +      gimple stmt = gsi_stmt (gsi);
>>>> +      gimple_set_uid (stmt, uid++);
>>>> +    }
>>>> +
>>>> +  /* Then assign uids to stmts.  */
>>>> +  for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
>>>> +    {
>>>> +      gimple stmt = gsi_stmt (gsi);
>>>> +      gimple_set_uid (stmt, uid++);
>>>> +    }
>>>> +}
>>>> +
>>>> +/* For each operand in OPS, find the basic block that contains the 
>>>> statement
>>>> +   which defines the operand. For all such basic blocks, assign UIDs.  */
>>>> +
>>>> +static void
>>>> +assign_uids_in_relevant_bbs (VEC(operand_entry_t, heap) * ops)
>>>> +{
>>>> +  operand_entry_t oe;
>>>> +  int i;
>>>> +  struct pointer_set_t *seen_bbs = pointer_set_create ();
>>>> +
>>>> +  for (i = 0; VEC_iterate (operand_entry_t, ops, i, oe); i++)
>>>> +    {
>>>> +      gimple def_stmt;
>>>> +      basic_block bb;
>>>> +      if (TREE_CODE (oe->op) != SSA_NAME)
>>>> +        continue;
>>>> +      def_stmt = SSA_NAME_DEF_STMT (oe->op);
>>>> +      bb = gimple_bb (def_stmt);
>>>> +      if (!pointer_set_contains (seen_bbs, bb))
>>>> +        {
>>>> +          assign_uids (bb);
>>>> +          pointer_set_insert (seen_bbs, bb);
>>>> +        }
>>>> +    }
>>>> +  pointer_set_destroy (seen_bbs);
>>>> +}
>>>
>>> Please assign UIDs once using the existing renumber_gimple_stmt_uids ().
>>> You seem to call the above multiple times and thus do work bigger than
>>> O(number of basic blocks).
>>
>> The reason I call the above multiple times is that gsi_move_before
>> might get called between two calls to the above. For instance, after
>> rewrite_expr_tree is called once, the following sequence of calls
>> could happen:
>> reassociate_bb -> linearize_expr_tree -> linearize_expr ->
>> gsi_move_before.  So it is not sufficient to call
>> renumber_gimple_stmt_uids once per do_reassoc. Or do you want me to
>> use renumber_gimple_stmt_uids_in_blocks instead of
>> assign_uids_in_relevant_bbs?
>
> It's sufficient to call it once if you conservatively update UIDs at stmt
> move / insert time (assign the same UID as the stmt before/after).

I was worried that that would make the code brittle, but looking at
the places where a new statement is added or moved, I think it is
fine.  I have made the change in the new patch.

>>
>>
>>>> +/* Ensure that operands in the OPS vector starting from OPINDEXth
>>>> entry are live
>>>> +   at STMT.  This is accomplished by moving STMT if needed.  */
>>>> +
>>>> +static void
>>>> +ensure_ops_are_available (gimple stmt, VEC(operand_entry_t, heap) *
>>>> ops, int opindex)
>>>> +{
>>>> +  int i;
>>>> +  int len = VEC_length (operand_entry_t, ops);
>>>> +  gimple insert_stmt = stmt;
>>>> +  basic_block insert_bb = gimple_bb (stmt);
>>>> +  gimple_stmt_iterator gsi_insert, gsistmt;
>>>> +  for (i = opindex; i < len; i++)
>>>> +    {
>>>
>>> Likewise you call this for each call to rewrite_expr_tree, so it seems to me
>>> this is quadratic in the number of ops in the op vector.
>>
>> The call to ensure_ops_are_available inside rewrite_expr_tree is
>> guarded by if (!moved) and I am setting moved = true there to ensure
>> that ensure_ops_are_available inside is called once per reassociation
>> of a expression tree.
>
> It would be a lot easier to understand this function if you call it once
> after rewrite_expr_tree finished.
I think you meant  "before rewrite_expr_tree". One thing to note here
is this function is called only when we first see a statement whose
operand changes after reassociation. That's the reason the moving of
statements happen inside rewrite_expr_tree.
>
>>>
>>> Why make this all so complicated?  It seems to me that we should
>>> fixup stmt order only after the whole ops vector has been materialized.
>>
>>
>>>
>>>> +      operand_entry_t oe = VEC_index (operand_entry_t, ops, i);
>>>> +      gimple def_stmt;
>>>> +      basic_block def_bb;
>>>> +      /* Ignore constants and operands with default definitons.  */
>>>> +      if (TREE_CODE (oe->op) != SSA_NAME
>>>> +          || SSA_NAME_IS_DEFAULT_DEF (oe->op))
>>>> +        continue;
>>>> +      def_stmt = SSA_NAME_DEF_STMT (oe->op);
>>>> +      def_bb = gimple_bb (def_stmt);
>>>> +      if (def_bb != insert_bb
>>>> +          && !dominated_by_p (CDI_DOMINATORS, insert_bb, def_bb))
>>>> +        {
>>>> +          insert_bb = def_bb;
>>>> +          insert_stmt = def_stmt;
>>>> +        }
>>>> +      else if (def_bb == insert_bb
>>>> +               && gimple_uid (insert_stmt) < gimple_uid (def_stmt))
>>>> +        insert_stmt = def_stmt;
>>>> +    }
>>>> +  if (insert_stmt == stmt)
>>>> +    return;
>>>> +  gsistmt = gsi_for_stmt (stmt);
>>>> +  /* If GSI_STMT is a phi node, then do not insert just after that 
>>>> statement.
>>>> +     Instead, find the first non-label gimple statement in BB and insert 
>>>> before
>>>> +     that.  */
>>>> +  if (gimple_code (insert_stmt) == GIMPLE_PHI)
>>>> +    {
>>>> +      gsi_insert = gsi_after_labels (insert_bb);
>>>> +      gsi_move_before (&gsistmt, &gsi_insert);
>>>> +    }
>>>> +  /* Statements marked for throw can not be in the middle of a basic 
>>>> block. So
>>>> +     we can not insert a statement (not marked for throw) immediately
>>>> after.  */
>>>> +  else if (lookup_stmt_eh_lp (insert_stmt) > 0
>>>
>>> that's already performed by stmt_can_throw_internal
>> Ok. I was hit by the "statement marked for throw in middle of block"
>> error in verify_gimple_in_cfg  and simply copied the sequence of
>> checks that led to that.
>>
>>>
>>>> +           && stmt_can_throw_internal (insert_stmt))
>>>
>>> But all this should be a non-issue as re-assoc should never assign an ops
>>> vector entry for such stmts (but it could have leafs defined by such stmts).
>>> If you only ever move definitions down, like the present code does caring
>>> about leafs should not be necessary.
>>
>> I added this as I got an ICE in verify_gimple_in_cfg. So it seems like
>> such statements gets assigned into the ops vector.
>
> That would be a bug.  See how linearize_expr_tree checks whether
> the defining stmt of an op may throw.  That is, it _does_ add the
> SSA def to the vector but does not recurse on its defining stmt operands.

But as long as the SSA def of a throwing statement is added to the ops
vector, I need to add the check, even if it doesn't recurse on its
operands. Consider the following

<bb1>:
t1 = a + b
c = ... // c might throw

<bb2>:
t2 = t1 + c

The expression could be rewritten as (a+c) + b.

Since c's definition follows t1's, t1 = a+b has to be moved after c =
... That's why I need the check.

Thanks,
Easwaran

> Richard.
>
>> Thanks,
>> Easwaran
>>>
>>> Richard.
>>>
>>>> +    {
>>>> +      edge e, succ_edge = NULL;
>>>> +      edge_iterator ei;
>>>> +
>>>> +      /* There should be exactly one normal edge out of the basic block.  
>>>> */
>>>> +      FOR_EACH_EDGE (e, ei, insert_bb->succs)
>>>> +        {
>>>> +          if (!(e->flags & EDGE_COMPLEX))
>>>> +            {
>>>> +              gcc_assert (succ_edge == NULL);
>>>> +              succ_edge = e;
>>>> +            }
>>>> +        }
>>>> +      /* Insert STMT at the beginning of the successor basic block.  */
>>>> +      insert_bb = succ_edge->dest;
>>>> +      gsi_insert = gsi_after_labels (insert_bb);
>>>> +      gsi_move_before (&gsistmt, &gsi_insert);
>>>> +    }
>>>> +  else
>>>> +    {
>>>> +      gsi_insert = gsi_for_stmt (insert_stmt);
>>>> +      gsi_move_after (&gsistmt, &gsi_insert);
>>>> +    }
>>>> +
>>>> +}
>>>> +
>>>>  /* Recursively rewrite our linearized statements so that the operators
>>>>     match those in OPS[OPINDEX], putting the computation in rank
>>>>     order.  */
>>>> @@ -2262,11 +2384,6 @@ rewrite_expr_tree (gimple stmt, unsigned int opind
>>>>    tree rhs2 = gimple_assign_rhs2 (stmt);
>>>>    operand_entry_t oe;
>>>>
>>>> -  /* If we have three operands left, then we want to make sure the ones
>>>> -     that get the double binary op are chosen wisely.  */
>>>> -  if (opindex + 3 == VEC_length (operand_entry_t, ops))
>>>> -    swap_ops_for_binary_stmt (ops, opindex, stmt);
>>>> -
>>>>    /* The final recursion case for this function is that you have
>>>>       exactly two operations left.
>>>>       If we had one exactly one op in the entire list to start with, we
>>>> @@ -2312,19 +2429,17 @@ rewrite_expr_tree (gimple stmt, unsigned int opind
>>>>      {
>>>>        if (!moved)
>>>>   {
>>>> -  gimple_stmt_iterator gsinow, gsirhs1;
>>>> -  gimple stmt1 = stmt, stmt2;
>>>> -  unsigned int count;
>>>> +  gimple stmt1 = stmt;
>>>> +  unsigned int count, i = 1;
>>>>
>>>> -  gsinow = gsi_for_stmt (stmt);
>>>>    count = VEC_length (operand_entry_t, ops) - opindex - 2;
>>>> -  while (count-- != 0)
>>>> +  while (i <= count)
>>>>      {
>>>> -      stmt2 = SSA_NAME_DEF_STMT (gimple_assign_rhs1 (stmt1));
>>>> -      gsirhs1 = gsi_for_stmt (stmt2);
>>>> -      gsi_move_before (&gsirhs1, &gsinow);
>>>> -      gsi_prev (&gsinow);
>>>> -      stmt1 = stmt2;
>>>> +      stmt1 = SSA_NAME_DEF_STMT (gimple_assign_rhs1 (stmt1));
>>>> +              /* Ensure that STMT1 is moved to a place where all operands 
>>>> in
>>>> +                 OPS[opindex + i...] are available.  */
>>>> +              ensure_ops_are_available (stmt1, ops, opindex + i);
>>>> +              i++;
>>>>      }
>>>>    moved = true;
>>>>   }
>>>> @@ -3542,8 +3657,18 @@ reassociate_bb (basic_block bb)
>>>>        && VEC_length (operand_entry_t, ops) > 3)
>>>>      rewrite_expr_tree_parallel (stmt, width, ops);
>>>>    else
>>>> -    rewrite_expr_tree (stmt, 0, ops, false);
>>>> +                    {
>>>> +                      /* When there are three operands left, we want
>>>> +                         to make sure the ones that get the double
>>>> +                         binary op are chosen wisely.  */
>>>> +                      int len = VEC_length (operand_entry_t, ops);
>>>> +                      if (len >= 3)
>>>> +                        swap_ops_for_binary_stmt (ops, len - 3, stmt);
>>>>
>>>> +                      assign_uids_in_relevant_bbs (ops);
>>>> +      rewrite_expr_tree (stmt, 0, ops, false);
>>>> +                    }
>>>> +
>>>>    /* If we combined some repeated factors into a
>>>>       __builtin_powi call, multiply that result by the
>>>>       reassociated operands.  */

Attachment: reassoc_oct23.patch
Description: Binary data

Reply via email to