On Fri, Jun 12, 2020 at 11:28 PM Martin Jambor <mjam...@suse.cz> wrote:
>
> Hi,
>
> when Fortran functions pass array descriptors they receive as a
> parameter to another function, they actually rebuild it.  Thanks to
> work done mainly by Feng, IPA-CP can already handle the cases when
> they pass directly the values loaded from the original descriptor.
> Unfortunately, perhaps the most important one, stride, is first
> checked against zero and is replaced with one in that case:
>
>   _12 = *a_11(D).dim[0].stride;
>   if (_12 != 0)
>     goto <bb 4>; [50.00%]
>   else
>     goto <bb 3>; [50.00%]
>
>   <bb 3>
>     // empty BB
>   <bb 4>
>   # iftmp.22_9 = PHI <_12(2), 1(3)>
>    ...
>    parm.6.dim[0].stride = iftmp.22_9;
>    ...
>    __x_MOD_foo (&parm.6, b_31(D));
>
> in the most important and hopefully common cases, the incoming value
> is already 1 and we fail to propagate it.
>
> I would therefore like to propose the following way of encoding this
> situation in pass-through jump functions using using ASSERTT_EXPR
> operation code meaning that if the incoming value is the same as the
> "operand" in the jump function, it is passed on, otherwise the result
> is unknown.  This of course captures only the single (but most
> important) case but is an improvement and does not need enlarging the
> jump function structure and is simple to pattern match.  Encoding that
> zero needs to be changed to one would need another field and matching
> it would be slightly more complicated too.
>
> Bootstrapped and tested on x86_64-linux, LTO bootstrap is underway.  OK
> if it passes?

Looks reasonable - I wonder if we somehow track enough info to
infer the _12 != 0 check in IPA propagation?  So whether there's the
possibility to not use "conditional 1" as I understand you do but
"conditional *a_11(D).dim[0].stride"?  Because AFAICS you
compare _12 against 1 in IPA propagation to enable the propagation,
why not compare it against 0?  Or even allow all cases to be resolved
if _12 is a constant?  That is, propagate a "_12 != 0 ? 12 : 1" jump
function which you should be able to resolve in the exact same
way via values_equal_for_ipa_cp_p?

Thanks,
Richard.

> Thanks,
>
> Martin
>
>
> 2020-06-12  Martin Jambor  <mjam...@suse.cz>
>
>         * ipa-prop.h (ipa_pass_through_data): Expand comment describing
>         operation.
>         * ipa-prop.c (analyze_agg_content_value): Detect new special case and
>         encode it as ASSERT_EXPR.
>         * ipa-cp.c (values_equal_for_ipcp_p): Move before
>         ipa_get_jf_arith_result.
>         (ipa_get_jf_arith_result): Special case ASSERT_EXPR.
>
>         testsuite/
>         * gfortran.dg/ipcp-array-2.f90: New test.
> ---
>  gcc/ipa-cp.c                               |  48 ++++---
>  gcc/ipa-prop.c                             | 148 ++++++++++++++-------
>  gcc/ipa-prop.h                             |  11 +-
>  gcc/testsuite/gfortran.dg/ipcp-array-2.f90 |  45 +++++++
>  4 files changed, 179 insertions(+), 73 deletions(-)
>  create mode 100644 gcc/testsuite/gfortran.dg/ipcp-array-2.f90
>
> diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
> index b0c8f405260..4a2714c634f 100644
> --- a/gcc/ipa-cp.c
> +++ b/gcc/ipa-cp.c
> @@ -1290,6 +1290,26 @@ initialize_node_lattices (struct cgraph_node *node)
>        }
>  }
>
> +/* Return true iff X and Y should be considered equal values by IPA-CP.  */
> +
> +static bool
> +values_equal_for_ipcp_p (tree x, tree y)
> +{
> +  gcc_checking_assert (x != NULL_TREE && y != NULL_TREE);
> +
> +  if (x == y)
> +    return true;
> +
> +  if (TREE_CODE (x) == ADDR_EXPR
> +      && TREE_CODE (y) == ADDR_EXPR
> +      && TREE_CODE (TREE_OPERAND (x, 0)) == CONST_DECL
> +      && TREE_CODE (TREE_OPERAND (y, 0)) == CONST_DECL)
> +    return operand_equal_p (DECL_INITIAL (TREE_OPERAND (x, 0)),
> +                           DECL_INITIAL (TREE_OPERAND (y, 0)), 0);
> +  else
> +    return operand_equal_p (x, y, 0);
> +}
> +
>  /* Return the result of a (possibly arithmetic) operation on the constant
>     value INPUT.  OPERAND is 2nd operand for binary operation.  RES_TYPE is
>     the type of the parameter to which the result is passed.  Return
> @@ -1307,6 +1327,14 @@ ipa_get_jf_arith_result (enum tree_code opcode, tree 
> input, tree operand,
>    if (!is_gimple_ip_invariant (input))
>      return NULL_TREE;
>
> +  if (opcode == ASSERT_EXPR)
> +    {
> +      if (values_equal_for_ipcp_p (input, operand))
> +       return input;
> +      else
> +       return NULL_TREE;
> +    }
> +
>    if (!res_type)
>      {
>        if (TREE_CODE_CLASS (opcode) == tcc_comparison)
> @@ -1739,26 +1767,6 @@ ipcp_verify_propagated_values (void)
>      }
>  }
>
> -/* Return true iff X and Y should be considered equal values by IPA-CP.  */
> -
> -static bool
> -values_equal_for_ipcp_p (tree x, tree y)
> -{
> -  gcc_checking_assert (x != NULL_TREE && y != NULL_TREE);
> -
> -  if (x == y)
> -    return true;
> -
> -  if (TREE_CODE (x) ==  ADDR_EXPR
> -      && TREE_CODE (y) ==  ADDR_EXPR
> -      && TREE_CODE (TREE_OPERAND (x, 0)) == CONST_DECL
> -      && TREE_CODE (TREE_OPERAND (y, 0)) == CONST_DECL)
> -    return operand_equal_p (DECL_INITIAL (TREE_OPERAND (x, 0)),
> -                           DECL_INITIAL (TREE_OPERAND (y, 0)), 0);
> -  else
> -    return operand_equal_p (x, y, 0);
> -}
> -
>  /* Return true iff X and Y should be considered equal contexts by IPA-CP.  */
>
>  static bool
> diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
> index 71ac0e104d2..16483fb413a 100644
> --- a/gcc/ipa-prop.c
> +++ b/gcc/ipa-prop.c
> @@ -1704,75 +1704,123 @@ analyze_agg_content_value (struct ipa_func_body_info 
> *fbi,
>
>        stmt = SSA_NAME_DEF_STMT (rhs1);
>        if (!is_gimple_assign (stmt))
> -       return;
> +       break;
>
>        rhs1 = gimple_assign_rhs1 (stmt);
>      }
>
> -  code = gimple_assign_rhs_code (stmt);
> -  switch (gimple_assign_rhs_class (stmt))
> +  if (gphi *phi = dyn_cast<gphi *> (stmt))
>      {
> -    case GIMPLE_SINGLE_RHS:
> -      if (is_gimple_ip_invariant (rhs1))
> +      /* Also special case like the following (a is a formal parameter):
> +
> +          _12 = *a_11(D).dim[0].stride;
> +          ...
> +          # iftmp.22_9 = PHI <_12(2), 1(3)>
> +          ...
> +          parm.6.dim[0].stride = iftmp.22_9;
> +          ...
> +          __x_MOD_foo (&parm.6, b_31(D));
> +
> +        The aggregate function describing parm.6.dim[0].stride is encoded as 
> a
> +        PASS-THROUGH jump function with ASSERT_EXPR operation whith operand 1
> +        (the constant from the PHI node).  */
> +
> +      if (gimple_phi_num_args (phi) != 2)
> +       return;
> +      tree arg0 = gimple_phi_arg_def (phi, 0);
> +      tree arg1 = gimple_phi_arg_def (phi, 1);
> +      tree operand;
> +
> +      if (is_gimple_ip_invariant (arg1))
>         {
> -         agg_value->pass_through.operand = rhs1;
> -         return;
> +         operand = arg1;
> +         rhs1 = arg0;
>         }
> -      code = NOP_EXPR;
> -      break;
> -
> -    case GIMPLE_UNARY_RHS:
> -      /* NOTE: A GIMPLE_UNARY_RHS operation might not be tcc_unary
> -        (truth_not_expr is example), GIMPLE_BINARY_RHS does not imply
> -        tcc_binary, this subtleness is somewhat misleading.
> -
> -        Since tcc_unary is widely used in IPA-CP code to check an operation
> -        with one operand, here we only allow tc_unary operation to avoid
> -        possible problem.  Then we can use (opclass == tc_unary) or not to
> -        distinguish unary and binary.  */
> -      if (TREE_CODE_CLASS (code) != tcc_unary || CONVERT_EXPR_CODE_P (code))
> +      else if (is_gimple_ip_invariant (arg0))
> +       {
> +         operand = arg0;
> +         rhs1 = arg1;
> +       }
> +      else
>         return;
>
>        rhs1 = get_ssa_def_if_simple_copy (rhs1, &stmt);
> -      break;
> +      if (!is_gimple_assign (stmt))
> +       return;
>
> -    case GIMPLE_BINARY_RHS:
> -      {
> -       gimple *rhs1_stmt = stmt;
> -       gimple *rhs2_stmt = stmt;
> -       tree rhs2 = gimple_assign_rhs2 (stmt);
> +      code = ASSERT_EXPR;
> +      agg_value->pass_through.operand = operand;
> +    }
> +  else if (is_gimple_assign (stmt))
> +    {
> +      code = gimple_assign_rhs_code (stmt);
> +      switch (gimple_assign_rhs_class (stmt))
> +       {
> +       case GIMPLE_SINGLE_RHS:
> +         if (is_gimple_ip_invariant (rhs1))
> +           {
> +             agg_value->pass_through.operand = rhs1;
> +             return;
> +           }
> +         code = NOP_EXPR;
> +         break;
>
> -       rhs1 = get_ssa_def_if_simple_copy (rhs1, &rhs1_stmt);
> -       rhs2 = get_ssa_def_if_simple_copy (rhs2, &rhs2_stmt);
> +       case GIMPLE_UNARY_RHS:
> +         /* NOTE: A GIMPLE_UNARY_RHS operation might not be tcc_unary
> +            (truth_not_expr is example), GIMPLE_BINARY_RHS does not imply
> +            tcc_binary, this subtleness is somewhat misleading.
>
> -       if (is_gimple_ip_invariant (rhs2))
> +            Since tcc_unary is widely used in IPA-CP code to check an 
> operation
> +            with one operand, here we only allow tc_unary operation to avoid
> +            possible problem.  Then we can use (opclass == tc_unary) or not 
> to
> +            distinguish unary and binary.  */
> +         if (TREE_CODE_CLASS (code) != tcc_unary || CONVERT_EXPR_CODE_P 
> (code))
> +           return;
> +
> +         rhs1 = get_ssa_def_if_simple_copy (rhs1, &stmt);
> +         break;
> +
> +       case GIMPLE_BINARY_RHS:
>           {
> -           agg_value->pass_through.operand = rhs2;
> -           stmt = rhs1_stmt;
> -         }
> -       else if (is_gimple_ip_invariant (rhs1))
> -         {
> -           if (TREE_CODE_CLASS (code) == tcc_comparison)
> -             code = swap_tree_comparison (code);
> -           else if (!commutative_tree_code (code))
> +           gimple *rhs1_stmt = stmt;
> +           gimple *rhs2_stmt = stmt;
> +           tree rhs2 = gimple_assign_rhs2 (stmt);
> +
> +           rhs1 = get_ssa_def_if_simple_copy (rhs1, &rhs1_stmt);
> +           rhs2 = get_ssa_def_if_simple_copy (rhs2, &rhs2_stmt);
> +
> +           if (is_gimple_ip_invariant (rhs2))
> +             {
> +               agg_value->pass_through.operand = rhs2;
> +               stmt = rhs1_stmt;
> +             }
> +           else if (is_gimple_ip_invariant (rhs1))
> +             {
> +               if (TREE_CODE_CLASS (code) == tcc_comparison)
> +                 code = swap_tree_comparison (code);
> +               else if (!commutative_tree_code (code))
> +                 return;
> +
> +               agg_value->pass_through.operand = rhs1;
> +               stmt = rhs2_stmt;
> +               rhs1 = rhs2;
> +             }
> +           else
>               return;
>
> -           agg_value->pass_through.operand = rhs1;
> -           stmt = rhs2_stmt;
> -           rhs1 = rhs2;
> +           if (TREE_CODE_CLASS (code) != tcc_comparison
> +               && !useless_type_conversion_p (TREE_TYPE (lhs),
> +                                              TREE_TYPE (rhs1)))
> +             return;
>           }
> -       else
> -         return;
> +         break;
>
> -       if (TREE_CODE_CLASS (code) != tcc_comparison
> -           && !useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (rhs1)))
> +       default:
>           return;
> -      }
> -      break;
> -
> -    default:
> -      return;
> -  }
> +       }
> +    }
> +  else
> +    return;
>
>    if (TREE_CODE (rhs1) != SSA_NAME)
>      index = load_from_unmodified_param_or_agg (fbi, fbi->info, stmt,
> diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h
> index 168c4c26443..a96dac85962 100644
> --- a/gcc/ipa-prop.h
> +++ b/gcc/ipa-prop.h
> @@ -94,9 +94,14 @@ struct GTY(()) ipa_pass_through_data
>    /* Number of the caller's formal parameter being passed.  */
>    int formal_id;
>    /* Operation that is performed on the argument before it is passed on.
> -     NOP_EXPR means no operation.  Otherwise oper must be a simple binary
> -     arithmetic operation where the caller's parameter is the first operand 
> and
> -     operand field from this structure is the second one.  */
> +     Special values which have other meaning than in normal contexts:
> +       - NOP_EXPR means no operation, not even type conversion.
> +       - ASSERT_EXPR means that only the value in operand is allowed to pass
> +         through (without any change), for all other values the result is
> +         unknown.
> +     Otherwise operation must be a simple binary or unary arithmetic 
> operation
> +     where the caller's parameter is the first operand and (for binary
> +     operations) the operand field from this structure is the second one.  */
>    enum tree_code operation;
>    /* When the passed value is a pointer, it is set to true only when we are
>       certain that no write to the object it points to has occurred since the
> diff --git a/gcc/testsuite/gfortran.dg/ipcp-array-2.f90 
> b/gcc/testsuite/gfortran.dg/ipcp-array-2.f90
> new file mode 100644
> index 00000000000..9af8fffa7ea
> --- /dev/null
> +++ b/gcc/testsuite/gfortran.dg/ipcp-array-2.f90
> @@ -0,0 +1,45 @@
> +! { dg-do compile }
> +! { dg-options "-O3 -fno-inline -fwhole-program -fdump-ipa-cp-details 
> -fdump-tree-lversion-details" }
> +
> +module x
> +  implicit none
> +contains
> +  subroutine foo(a, b)
> +    real :: a(:,:)
> +    real :: b
> +    integer :: i,j
> +    b = 0.
> +    do j=1,size(a,2)
> +       do i=1,size(a,1)
> +          b = b + a(i,j) * i * j
> +       end do
> +    end do
> +  end subroutine foo
> +
> +  subroutine bar(a, b)
> +    real :: a(:,:)
> +    real :: b
> +    call foo (a,b)
> +  end subroutine bar
> +
> +end module x
> +
> +program main
> +  use x
> +  implicit none
> +  integer :: n, m
> +  real, dimension(4,3) :: a
> +  real, dimension(3,4) :: c
> +  real :: b
> +  call random_number(a)
> +  call bar(a,b)
> +  print *,b
> +
> +  call random_number(c)
> +  call bar(c,b)
> +  print *,b
> +
> +end program main
> +
> +! { dg-final { scan-ipa-dump "op assert_expr 1" "cp" } }
> +! { dg-final { scan-tree-dump-not "versioned this loop for when certain 
> strides are 1" "lversion" } }
> --
> 2.26.2
>

Reply via email to