On 2/12/25 2:22 PM, Jakub Jelinek wrote:

I agree that the most common cases should be all the arguments the same
type.  I was working under the assumption that the args would be compatible
types already, forgetting that IFNs are different in that regard than other
gimple ops.  I wouldn't want to go any further than all three operands the
same with the easy to reason about relation checks.


For gcc-16 I think we can extend that block fairly easily to handle certain
mismatched size cases and we look to see if there's cases where the
combination of a relationship between the arguments and some range
information would allow us to capture further cases.

For the GCC 16 version, I think best would be (given Andrew's mail that
the relations aren't likely very useful for incompatible types) to
   relation_kind rel = VREL_VARYING;
   if (code == MINUS_EXPR
       && types_compatible_p (TREE_TYPE (op0), TREE_TYPE (op1))
     {
       rel = query->relation().query (s, op0, op1);
       /* The result of the infinite precision subtraction of
         the same values will be always 0.  That will fit into any result
         type.  */
       if (rel == VREL_EQ)
        return true;
     }

then do the current
   int_range_max vr0, vr1;
   if (!query->range_of_expr (vr0, op0, s) || vr0.undefined_p ())
     vr0.set_varying (TREE_TYPE (op0));
   if (!query->range_of_expr (vr1, op1, s) || vr1.undefined_p ())
     vr1.set_varying (TREE_TYPE (op1));

   tree vr0min = wide_int_to_tree (TREE_TYPE (op0), vr0.lower_bound ());
   tree vr0max = wide_int_to_tree (TREE_TYPE (op0), vr0.upper_bound ());
   tree vr1min = wide_int_to_tree (TREE_TYPE (op1), vr1.lower_bound ());
   tree vr1max = wide_int_to_tree (TREE_TYPE (op1), vr1.upper_bound ());

and then we can e.g. special case > and >=:
   /* If op1 is not negative, op0 - op1 for op0 >= op1 will be always
      in [0, op0] and so if vr0max - vr1min fits into type, there won't
      be any overflow.  */
   if ((rel == VREL_GT || rel == VREL_GE)
       && tree_int_cst_sgn (vr1min) >= 0
       && !arith_overflowed_p (MINUS_EXPR, type, vr0max, vr1min))
     return true;

Would need to think about if anything could be simplified for
VREL_G{T,E} if tree_int_cst_sgn (vr1min) < 0.

As for VREL_LT, one would need to think it through as well for both
tree_int_cst_sgn (vr1min) >= 0 and tree_int_cst_sgn (vr1min) < 0.
For the former, the infinite precision of subtraction is known given
the relation to be < 0.  Now obviously if TYPE_UNSIGNED (type) that
would imply always overflow.  But for !TYPE_UNSIGNED (type) that isn't
necessarily the case and the question is if the relation helps with the
reasoning.  Generally the code otherwise tries to check 2 boundaries
(for MULT_EXPR 4 but we don't care about that), if they both don't overflow,
it is ok, if only one overflows, we don't know, if both boundaries don't
overflow, we need to look further and check some corner cases in between.

Or just go with that even for GCC 15 (completely untested and dunno if
something needs to be done about s = NULL passed to query or not) for
now, with the advantage that it can do something even for the cases where
type is not compatible with types of arguments, and perhaps add additional
cases later?
This is further than I wanted to go for gcc-15. But I can support something like this as it's not a major extension to what I was suggesting. And of course it addresses the correctness issues around different types. I'll play with it a bit.

And WRT an earlier message about gcc-16. Yea, I think opening a bug for additional cases would be a good idea.

Jeff

Reply via email to