This hooks the machinery into fold_stmt (but not allowed to traverse
SSA use-def chains by default) and provides a new overload to
provide a valueization hook.  With the patch tree-ssa-forwprop.c
makes use of that new overload, first folding all statements
while keeping a simple const-and-copy lattice.

Once all "toplevel" patterns are in place (patterns with
a single operation) we can remove the dispatch to GENERIC
folding from fold_stmt.

A question is whether to really do as the patch does and simplify
all statements or whether I should restrict this to stmts that
were previously covered by its manual patterns (which further
patches will transition to match-and-simplify patterns).

As for the GENERIC part, here is what the simplification routine
looks like for

/* fold_negate_exprs convert - (~A) to A + 1.  */
(simplify
 (negate (bit_not @0))
 (if (INTEGRAL_TYPE_P (type))
  (plus @0 { build_int_cst (TREE_TYPE (@0), 1); } )))

static bool
gimple_simplify (code_helper *res_code, tree *res_ops,
                 gimple_seq *seq, tree (*valueize)(tree),
                 code_helper code, tree type, tree op0)
{
  switch (code.get_rep())
    {
...
    case NEGATE_EXPR:
      {
        switch (TREE_CODE (op0))
          {
          case SSA_NAME:
            {
              gimple def_stmt = SSA_NAME_DEF_STMT (op0);
              if (is_gimple_assign (def_stmt))
                switch (gimple_assign_rhs_code (def_stmt))
                  {
                  case BIT_NOT_EXPR:
                    {
                      tree o20 = gimple_assign_rhs1 (def_stmt);
                      if ((o20 = do_valueize (valueize, o20)) != 0)
                        {
                            {
                              /* #line 136 
"/space/rguenther/src/svn/match-and-simplify/gcc/match.pd" */
                              tree captures[2] ATTRIBUTE_UNUSED = {};
                              captures[0] = o20;
                              /* #line 135 
"/space/rguenther/src/svn/match-and-simplify/gcc/match.pd" */
                              if (INTEGRAL_TYPE_P (type))
                                {
                                  if (dump_file && (dump_flags & TDF_DETAILS)) 
fprintf (dump_file, "Applying pattern match.pd:136, %s:%d\n", __FILE__, 
__LINE__);
                                  *res_code = PLUS_EXPR;
                                  res_ops[0] = captures[0];
                                  res_ops[1] =  build_int_cst (TREE_TYPE 
(captures[0]), 1);
                                  gimple_resimplify2 (seq, res_code, type, 
res_ops, valueize);
                                  return true;
                                }
                            }
                        }
                      break;
                    }
...

As you can see we use gimple_resimplify2 for re-simplification and
code-generation and the toplevel expression of the result is
kept in pieces.  gimple_resimplify2 in turn ends up calling
gimple_simplify again and fold to get the "real" constant folding cases.

Boostrap and regtest is pending on x86_64-unknown-linux-gnu.

Ok for trunk?

Thanks,
Richard.

2014-10-15  Richard Biener  <rguent...@suse.de>

        * gimple-fold.h (fold_stmt): New overload with valueization hook.
        (no_follow_ssa_edges): Declare.
        * gimple-fold.c: Include tree-eh.h and gimple-match.h.
        (fold_stmt_1): Add valueization hook parameter.  Dispatch to
        gimple_simplify after canonicalization.
        (no_follow_ssa_edges): New function.
        (fold_stmt): New overload and adjust.
        (fold_stmt_inplace): Adjust.
        * tree-ssa-forwprop.c: Include tree-cfgcleanup.h and tree-into-ssa.h.
        (lattice): New global.
        (fwprop_ssa_val): New function.
        (pass_forwprop::execute): Do a first pass over all stmts in
        dominator order simplifying all statements while keeping
        a simple const-and-copy lattice up-to-date.

Index: gcc/gimple-fold.h
===================================================================
*** gcc/gimple-fold.h.orig      2014-10-15 13:01:41.548100887 +0200
--- gcc/gimple-fold.h   2014-10-15 13:57:05.627872029 +0200
*************** extern tree canonicalize_constructor_val
*** 26,36 ****
--- 26,38 ----
  extern tree get_symbol_constant_value (tree);
  extern void gimplify_and_update_call_from_tree (gimple_stmt_iterator *, tree);
  extern bool fold_stmt (gimple_stmt_iterator *);
+ extern bool fold_stmt (gimple_stmt_iterator *, tree (*) (tree));
  extern bool fold_stmt_inplace (gimple_stmt_iterator *);
  extern tree maybe_fold_and_comparisons (enum tree_code, tree, tree, 
                                        enum tree_code, tree, tree);
  extern tree maybe_fold_or_comparisons (enum tree_code, tree, tree,
                                       enum tree_code, tree, tree);
+ extern tree no_follow_ssa_edges (tree);
  extern tree gimple_fold_stmt_to_constant_1 (gimple, tree (*) (tree));
  extern tree gimple_fold_stmt_to_constant (gimple, tree (*) (tree));
  extern tree fold_const_aggregate_ref_1 (tree, tree (*) (tree));
Index: gcc/gimple-fold.c
===================================================================
*** gcc/gimple-fold.c.orig      2014-10-15 13:02:08.158099055 +0200
--- gcc/gimple-fold.c   2014-10-15 13:56:20.710875121 +0200
*************** along with GCC; see the file COPYING3.
*** 55,60 ****
--- 55,63 ----
  #include "dbgcnt.h"
  #include "builtins.h"
  #include "output.h"
+ #include "tree-eh.h"
+ #include "gimple-match.h"
+ 
  
  
  /* Return true when DECL can be referenced from current unit.
*************** maybe_canonicalize_mem_ref_addr (tree *t
*** 2811,2817 ****
     distinguishes both cases.  */
  
  static bool
! fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace)
  {
    bool changed = false;
    gimple stmt = gsi_stmt (*gsi);
--- 2814,2820 ----
     distinguishes both cases.  */
  
  static bool
! fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace, tree (*valueize) (tree))
  {
    bool changed = false;
    gimple stmt = gsi_stmt (*gsi);
*************** fold_stmt_1 (gimple_stmt_iterator *gsi,
*** 2889,2894 ****
--- 2892,3017 ----
      default:;
      }
  
+   /* Dispatch to pattern-based folding.  */
+   /* ???  Change "inplace" semantics to allow replacing a stmt if
+      no further stmts need to be inserted (basically disallow
+      creating of new SSA names).  */
+   if (!inplace
+       || is_gimple_assign (stmt)
+       || gimple_code (stmt) == GIMPLE_COND)
+     {
+       gimple_seq seq = NULL;
+       code_helper rcode;
+       tree ops[3] = {};
+       if (gimple_simplify (stmt, &rcode, ops, inplace ? NULL : &seq, 
valueize))
+       {
+         if (gimple_code (stmt) == GIMPLE_COND)
+           {
+             gcc_assert (rcode.is_tree_code ());
+             if (TREE_CODE_CLASS ((enum tree_code)rcode) == tcc_comparison
+                 /* GIMPLE_CONDs condition may not throw.  */
+                 /* ???  Not sure how we want to deal with combining
+                    from possibly throwing statements.  Trivial
+                    simplifications may lead to DCEing an internal
+                    throw.  But we probably still want to simplify
+                    things to a constant for example?  Similar to
+                    abnormals we could discard the simplification
+                    result if we ever push a could-throw stmt to
+                    the sequence.  */
+                 && (!flag_exceptions
+                     || !cfun->can_throw_non_call_exceptions
+                     || !operation_could_trap_p (rcode, FLOAT_TYPE_P 
(TREE_TYPE (ops[0])), false, NULL_TREE)))
+               gimple_cond_set_condition (stmt, rcode, ops[0], ops[1]);
+             else if (rcode == SSA_NAME)
+               gimple_cond_set_condition (stmt, NE_EXPR, ops[0],
+                                          build_zero_cst (TREE_TYPE (ops[0])));
+             else if (rcode == INTEGER_CST)
+               {
+                 if (integer_zerop (ops[0]))
+                   gimple_cond_make_false (stmt);
+                 else
+                   gimple_cond_make_true (stmt);
+               }
+             else if (!inplace)
+               {
+                 tree res = maybe_push_res_to_seq (rcode, boolean_type_node,
+                                                   ops, &seq);
+                 if (!res)
+                   goto fail;
+                 gimple_cond_set_condition (stmt, NE_EXPR, res,
+                                            build_zero_cst (TREE_TYPE (res)));
+               }
+             else
+               goto fail;
+             if (dump_file && (dump_flags & TDF_DETAILS))
+               {
+                 fprintf (dump_file, "gimple_simplified to ");
+                 if (!gimple_seq_empty_p (seq))
+                   print_gimple_seq (dump_file, seq, 0, TDF_SLIM);
+                 print_gimple_stmt (dump_file, gsi_stmt (*gsi),
+                                    0, TDF_SLIM);
+               }
+             gsi_insert_seq_before (gsi, seq, GSI_SAME_STMT);
+             changed = true;
+ fail:
+             ;
+           }
+         else if (is_gimple_assign (stmt)
+                  && rcode.is_tree_code ())
+           {
+             if ((!inplace
+                  || gimple_num_ops (stmt) <= get_gimple_rhs_num_ops (rcode))
+                 /* Play safe and do not allow abnormals to be mentioned in
+                    newly created statements.  */
+                 && !((TREE_CODE (ops[0]) == SSA_NAME
+                       && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ops[0]))
+                      || (ops[1]
+                          && TREE_CODE (ops[1]) == SSA_NAME
+                          && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ops[1]))
+                      || (ops[2]
+                          && TREE_CODE (ops[2]) == SSA_NAME
+                          && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ops[2]))))
+               {
+                 maybe_build_generic_op (rcode,
+                                         TREE_TYPE (gimple_assign_lhs (stmt)),
+                                         &ops[0], ops[1], ops[2]);
+                 gimple_assign_set_rhs_with_ops_1 (gsi, rcode,
+                                                   ops[0], ops[1], ops[2]);
+                 if (dump_file && (dump_flags & TDF_DETAILS))
+                   {
+                     fprintf (dump_file, "gimple_simplified to ");
+                     if (!gimple_seq_empty_p (seq))
+                       print_gimple_seq (dump_file, seq, 0, TDF_SLIM);
+                     print_gimple_stmt (dump_file, gsi_stmt (*gsi),
+                                        0, TDF_SLIM);
+                   }
+                 gsi_insert_seq_before (gsi, seq, GSI_SAME_STMT);
+                 changed = true;
+               }
+           }
+         else if (!inplace)
+           {
+             if (gimple_has_lhs (stmt))
+               {
+                 tree lhs = gimple_get_lhs (stmt);
+                 maybe_push_res_to_seq (rcode, TREE_TYPE (lhs),
+                                        ops, &seq, lhs);
+                 if (dump_file && (dump_flags & TDF_DETAILS))
+                   {
+                     fprintf (dump_file, "gimple_simplified to ");
+                     print_gimple_seq (dump_file, seq, 0, TDF_SLIM);
+                   }
+                 gsi_replace_with_seq_vops (gsi, seq);
+                 changed = true;
+               }
+             else
+               gcc_unreachable ();
+           }
+       }
+     }
+ 
+   stmt = gsi_stmt (*gsi);
+ 
    /* Fold the main computation performed by the statement.  */
    switch (gimple_code (stmt))
      {
*************** fold_stmt_1 (gimple_stmt_iterator *gsi,
*** 3028,3033 ****
--- 3151,3164 ----
    return changed;
  }
  
+ /* Valueziation callback that ends up not following SSA edges.  */
+ 
+ tree
+ no_follow_ssa_edges (tree)
+ {
+   return NULL_TREE;
+ }
+ 
  /* Fold the statement pointed to by GSI.  In some cases, this function may
     replace the whole statement with a new one.  Returns true iff folding
     makes any changes.
*************** fold_stmt_1 (gimple_stmt_iterator *gsi,
*** 3038,3044 ****
  bool
  fold_stmt (gimple_stmt_iterator *gsi)
  {
!   return fold_stmt_1 (gsi, false);
  }
  
  /* Perform the minimal folding on statement *GSI.  Only operations like
--- 3169,3181 ----
  bool
  fold_stmt (gimple_stmt_iterator *gsi)
  {
!   return fold_stmt_1 (gsi, false, no_follow_ssa_edges);
! }
! 
! bool
! fold_stmt (gimple_stmt_iterator *gsi, tree (*valueize) (tree))
! {
!   return fold_stmt_1 (gsi, false, valueize);
  }
  
  /* Perform the minimal folding on statement *GSI.  Only operations like
*************** bool
*** 3053,3059 ****
  fold_stmt_inplace (gimple_stmt_iterator *gsi)
  {
    gimple stmt = gsi_stmt (*gsi);
!   bool changed = fold_stmt_1 (gsi, true);
    gcc_assert (gsi_stmt (*gsi) == stmt);
    return changed;
  }
--- 3190,3196 ----
  fold_stmt_inplace (gimple_stmt_iterator *gsi)
  {
    gimple stmt = gsi_stmt (*gsi);
!   bool changed = fold_stmt_1 (gsi, true, no_follow_ssa_edges);
    gcc_assert (gsi_stmt (*gsi) == stmt);
    return changed;
  }
Index: gcc/tree-ssa-forwprop.c
===================================================================
*** gcc/tree-ssa-forwprop.c.orig        2014-10-14 15:49:39.836355545 +0200
--- gcc/tree-ssa-forwprop.c     2014-10-15 13:56:20.725875120 +0200
*************** along with GCC; see the file COPYING3.
*** 54,59 ****
--- 54,61 ----
  #include "tree-ssa-propagate.h"
  #include "tree-ssa-dom.h"
  #include "builtins.h"
+ #include "tree-cfgcleanup.h"
+ #include "tree-into-ssa.h"
  
  /* This pass propagates the RHS of assignment statements into use
     sites of the LHS of the assignment.  It's basically a specialized
*************** simplify_mult (gimple_stmt_iterator *gsi
*** 3586,3591 ****
--- 3588,3613 ----
  
    return false;
  }
+ 
+ 
+ static vec<tree> lattice;
+ 
+ /* Primitive "lattice" function for gimple_simplify to discard
+    matches on names whose definition contains abnormal SSA names.  */
+ 
+ static tree
+ fwprop_ssa_val (tree name)
+ {
+   if (TREE_CODE (name) == SSA_NAME
+       && SSA_NAME_VERSION (name) < lattice.length ())
+     {
+       tree val = lattice[SSA_NAME_VERSION (name)];
+       if (val)
+       return val;
+     }
+   return name;
+ }
+ 
  /* Main entry point for the forward propagation and statement combine
     optimizer.  */
  
*************** pass_forwprop::execute (function *fun)
*** 3626,3631 ****
--- 3648,3708 ----
  
    cfg_changed = false;
  
+   /* Combine stmts with the stmts defining their operands.  Do that
+      in an order that guarantees visiting SSA defs before SSA uses.  */
+   lattice.create (num_ssa_names);
+   lattice.quick_grow_cleared (num_ssa_names);
+   int *postorder = XNEWVEC (int, n_basic_blocks_for_fn (fun));
+   int postorder_num = inverted_post_order_compute (postorder);
+   for (int i = 0; i < postorder_num; ++i)
+     {
+       bb = BASIC_BLOCK_FOR_FN (fun, postorder[i]);
+       /* ???  Maybe want to handle degenerate PHIs to populate
+        the lattice more optimistically.  */
+       for (gimple_stmt_iterator gsi = gsi_start_bb (bb);
+          !gsi_end_p (gsi); gsi_next (&gsi))
+       {
+         gimple stmt = gsi_stmt (gsi);
+         gimple orig_stmt = stmt;
+ 
+         if (fold_stmt (&gsi, fwprop_ssa_val))
+           {
+             stmt = gsi_stmt (gsi);
+             if (maybe_clean_or_replace_eh_stmt (orig_stmt, stmt)
+                 && gimple_purge_dead_eh_edges (bb))
+               cfg_changed = true;
+             update_stmt (stmt);
+           }
+ 
+         /* Fill up the lattice.  */
+         if (gimple_assign_single_p (stmt))
+           {
+             tree lhs = gimple_assign_lhs (stmt);
+             tree rhs = gimple_assign_rhs1 (stmt);
+             if (TREE_CODE (lhs) == SSA_NAME)
+               {
+                 if (TREE_CODE (rhs) == SSA_NAME)
+                   lattice[SSA_NAME_VERSION (lhs)] = fwprop_ssa_val (rhs);
+                 else if (is_gimple_min_invariant (rhs))
+                   lattice[SSA_NAME_VERSION (lhs)] = rhs;
+                 else
+                   lattice[SSA_NAME_VERSION (lhs)] = lhs;
+               }
+           }
+       }
+     }
+   free (postorder);
+   lattice.release ();
+ 
+   /* ???  Code below doesn't expect non-renamed VOPs and the above
+      doesn't keep virtual operand form up-to-date.  */
+   if (cfg_changed)
+     {
+       cleanup_tree_cfg ();
+       cfg_changed = false;
+     }
+   update_ssa (TODO_update_ssa_only_virtuals);
+ 
    FOR_EACH_BB_FN (bb, fun)
      {
        gimple_stmt_iterator gsi;

Reply via email to