On Wed, 10 Jul 2024, Tamar Christina wrote:

> > > > I might also point back to the idea I threw in somewhere, adding
> > > > OEP_VALUE (or a better name) to the set of flags accepted by
> > > > operand_equal_p.  You mentioned hashing IIRC but I don't see the patches
> > > > touching hashing?
> > > >
> > >
> > > Yes, That can indeed be done with this approach.  The hashing was that 
> > > before I
> > > was trying to prevent the "duplicate" IV expressions from being created 
> > > in the
> > > first place by modifying get_loop_invariant_expr.
> > >
> > > This function looks up if we have already seen a particular IV expression 
> > > and if
> > > we have it just returns that expression.  However after reading more of 
> > > the code
> > > I realized this wasn't the right approach, as without also dealing with 
> > > the
> > candidates
> > > we'd end up creating IV expression that can't be handled by any candidate.
> > >
> > > IVops would just give up then.   Reading the code it seems that
> > get_loop_invariant_expr
> > > is just there to prevent blatant duplicates.  i.e. it treats `(signed) a` 
> > > and `a` as the
> > same.
> > >
> > > This is also why I think that everywhere else *has* to continue stripping 
> > > the
> > expression.
> > >
> > > On a note from Richard S that he thought IVopts already had some code to 
> > > deal
> > with
> > > expressions that differ only in signs led me to take a different approach.
> > >
> > > The goal wasn't to remove the different sign/unsigned IV expressions, but
> > instead get
> > > Then to be servable by the same candidates. i.e. we want them in the same
> > candidate
> > > groups and then candidate optimization will just do its thing.
> > >
> > > That seemed a more natural fit to how it worked.
> > 
> > Yeah, I agree that sounds like the better strathegy.
> > 
> > > Do you want me to try the operand_equal_p approach? Though in this case 
> > > the
> > issue
> > > is we not only need to know they're equal, but also need to know the scale
> > factor.
> > 
> > For this case yes, but if you'd keep the code as-is, the equal with scale
> > factor one case would be fixed.  Not a case with different scale factors
> > though - but conversions "elsewhere" should be handled via the stripping.
> > So it would work to simply adjust the operand_equal_p check here?
> > 
> > > get_computation_aff_1 scales the common type IV by the scale we 
> > > determined,
> > > so I think operand_equal_p would not be very useful here.  But it does 
> > > look like
> > > constant_multiple_of can just be implemented with
> > aff_combination_constant_multiple_p.
> > >
> > > Should I try?
> > 
> > You've had the other places where you replace operand_equal_p with
> > affine-compute and compare.  As said that has some associated cost
> > as well as a limit on the number of elements after which it resorts
> > back to operand_equal_p.  So for strict equality tests implementing
> > a weaker operand_equal_p might be a better solution.
> > 
> 
> The structural comparison is implemented as a new mode for operand_equal_p 
> which
> compares two expressions ignoring NOP converts (unless their bitsizes differ)
> and ignoring constant values, but requiring both operands be a constant.
> 
> There is one downside compared to affine comparison, in that this approach 
> does
> not deal well with commutative operations. i.e. it does not see a + (b + c) as
> equivalent to c + (b + a).
> 
> This means we lose out on some of the more complicated addressing modes, but
> with so many operations the address will likely be split anyway and we'll deal
> with it then.
> 
> Bootstrapped Regtested on aarch64-none-linux-gnu,
> x86_64-pc-linux-gnu -m32, -m64 and no issues.
> 
> Ok for master?
> 
> Thanks,
> Tamar
> 
> gcc/ChangeLog:
> 
>       PR tree-optimization/114932
>       * fold-const.cc (operand_compare::operand_equal_p): Use it.
>       (operand_compare::verify_hash_value): Likewise.
>       * tree-core.h (enum operand_equal_flag): Add OEP_STRUCTURAL_EQ.
>       * tree-ssa-loop-ivopts.cc (record_group_use): Check for structural eq.
> 
> gcc/testsuite/ChangeLog:
> 
>       PR tree-optimization/114932
>       * gfortran.dg/addressing-modes_2.f90: New test.
> 
> -- inline copy of --
> 
> diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
> index 
> 710d697c0217c784b34f9f9f7b00b1945369076a..3d43020541c082c094164724da9d17fbb5793237
>  100644
> --- a/gcc/fold-const.cc
> +++ b/gcc/fold-const.cc
> @@ -3191,6 +3191,9 @@ operand_compare::operand_equal_p (const_tree arg0, 
> const_tree arg1,
>       precision differences.  */
>    if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST)
>      {
> +      if (flags & OEP_STRUCTURAL_EQ)
> +     return true;
> +

Hmm, so you ignore all constants?

>        /* Address of INTEGER_CST is not defined; check that we did not forget
>        to drop the OEP_ADDRESS_OF flags.  */
>        gcc_checking_assert (!(flags & OEP_ADDRESS_OF));
> @@ -3204,7 +3207,8 @@ operand_compare::operand_equal_p (const_tree arg0, 
> const_tree arg1,
>        because they may change the signedness of the arguments.  As pointers
>        strictly don't have a signedness, require either two pointers or
>        two non-pointers as well.  */
> -      if (TYPE_UNSIGNED (TREE_TYPE (arg0)) != TYPE_UNSIGNED (TREE_TYPE 
> (arg1))
> +      if ((TYPE_UNSIGNED (TREE_TYPE (arg0)) != TYPE_UNSIGNED (TREE_TYPE 
> (arg1))
> +        && !(flags & OEP_STRUCTURAL_EQ))
>         || POINTER_TYPE_P (TREE_TYPE (arg0))
>                            != POINTER_TYPE_P (TREE_TYPE (arg1)))

and all sign-conversions?

I was thinking to implement a "value equivalence" check, so
(int)((unsigned)i + (unsigned)j) would be equal to i + j for int i and j.

Doesn't your patch still treat (int)(unsigned)i and i as different?
And won't it treat (unsigned)i / 4 and (int)i / 4 as the same?  I'm not
sure this is the desired semantic for IVOPTs (or a useful one in general).

That said, when OEP_STRUCTURAL_EQ ignores a sign-change it can only
do that for operations where that maintains value-equivalence
(in terms of twos-complement bit representation).

OTOH if you ignore all differences in constants that's another thing.

>       return false;
> @@ -4204,7 +4208,7 @@ operand_compare::verify_hash_value (const_tree arg0, 
> const_tree arg1,
>      {
>        if (operand_equal_p (arg0, arg1, flags | OEP_NO_HASH_CHECK))
>       {
> -       if (arg0 != arg1 && !(flags & OEP_DECL_NAME))
> +       if (arg0 != arg1 && !(flags & (OEP_DECL_NAME | OEP_STRUCTURAL_EQ)))
>           {
>             inchash::hash hstate0 (0), hstate1 (0);
>             hash_operand (arg0, hstate0, flags | OEP_HASH_CHECK);
> diff --git a/gcc/testsuite/gfortran.dg/addressing-modes_2.f90 
> b/gcc/testsuite/gfortran.dg/addressing-modes_2.f90
> new file mode 100644
> index 
> 0000000000000000000000000000000000000000..8eee4be3dc4d69fecfacd4c2e24a4973c8539fae
> --- /dev/null
> +++ b/gcc/testsuite/gfortran.dg/addressing-modes_2.f90
> @@ -0,0 +1,20 @@
> +! { dg-do compile { target aarch64-*-* } }
> +! { dg-additional-options "-w -Ofast -fdump-tree-ivopts-all" }
> +
> +module a
> +integer, parameter :: b=3, c=b
> +contains
> +subroutine d(block)
> +integer f, col   , block(:, :, :), e
> +do f = 1, c
> +   do col = 1, c
> +             block(:f,                          :, e()) = do
> +     end do
> +  end do
> +  end
> +  end
> +
> +! { dg-final { scan-tree-dump-not {Selected IV set for loop .+ niters, 3 
> IVs:} ivopts } }
> +! { dg-final { scan-tree-dump-times {Selected IV set for loop .+ niters, 2 
> IVs:} 1 ivopts } }
> +! { dg-final { scan-tree-dump-times {Selected IV set for loop .+ niters, 1 
> IVs:} 1 ivopts } }
> +
> diff --git a/gcc/tree-core.h b/gcc/tree-core.h
> index 
> 27c569c77022227643151fa4a909a95f3d45bf55..fe398d99bcaa9e8fcec45e5e07d133bc393aa4a7
>  100644
> --- a/gcc/tree-core.h
> +++ b/gcc/tree-core.h
> @@ -962,7 +962,9 @@ enum operand_equal_flag {
>    /* In conjunction with OEP_LEXICOGRAPHIC considers names of declarations
>       of the same kind.  Used to compare VLA bounds involving parameters
>       across redeclarations of the same function.  */
> -  OEP_DECL_NAME = 512
> +  OEP_DECL_NAME = 512,
> +  /* Check for structural equivalence and ignore NOP_CONVERT casts.  */
> +  OEP_STRUCTURAL_EQ = 1024
>  };
>  
>  /* Enum and arrays used for tree allocation stats.
> diff --git a/gcc/tree-ssa-loop-ivopts.cc b/gcc/tree-ssa-loop-ivopts.cc
> index 
> c3218a3e8eedbb8d0a7f14c01eeb069cb6024c29..b05e4ac1e63b5c110707a8a3ed5e614b7ccc702f
>  100644
> --- a/gcc/tree-ssa-loop-ivopts.cc
> +++ b/gcc/tree-ssa-loop-ivopts.cc
> @@ -1623,8 +1623,8 @@ record_group_use (struct ivopts_data *data, tree *use_p,
>  
>         /* Check if it has the same stripped base and step.  */
>         if (operand_equal_p (iv->base_object, use->iv->base_object, 0)
> -           && operand_equal_p (iv->step, use->iv->step, 0)
> -           && operand_equal_p (addr_base, use->addr_base, 0))
> +           && operand_equal_p (iv->step, use->iv->step, OEP_STRUCTURAL_EQ)
> +           && operand_equal_p (addr_base, use->addr_base, OEP_STRUCTURAL_EQ))
>           break;
>       }
>        if (i == data->vgroups.length ())
> 

-- 
Richard Biener <rguent...@suse.de>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Ivo Totev, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)

Reply via email to