Hello,

I would like to ping the patch below.

Martin

On Mon, Nov 29 2021, Martin Jambor wrote:
> Hi,
>
> On Sat, Nov 27 2021, Jan Hubicka wrote:
>>> Hi,
>>> 
>>> IPA_JF_ANCESTOR jump functions are constructed also when the formal
>>> parameter of the caller is first checked whether it is NULL and left
>>> as it is if it is NULL, to accommodate C++ casts to an ancestor class.
>>> 
>>> The jump function type was invented for devirtualization and IPA-CP
>>> propagation of tree constants is also careful to apply it only to
>>> existing DECLs(*) but as PR 103083 shows, the part propagating "known
>>> bits" was not careful about this, which can lead to miscompilations.
>>> 
>>> This patch introduces a flag to the ancestor jump functions which
>>> tells whether a NULL-check was elided when creating it and makes the
>>> bits propagation behave accordingly.
>>> 
>>> (*) There still may remain problems when a DECL resides on address
>>> zero (with -fno-delete-null-pointer-checks ...I hope it cannot happen
>>> otherwise).  I am looking into that now but I think it will be easier
>>> for everyone if I do so in a follow-up patch.
>>
>> I guess so.  Thinking about it bit more after our discussion yesterday
>> I think it really matters where the ADDR_EXPR was created (since in
>> funciton with -fno-delete-null-pointer-checks it might be equal to 0)
>> and we need to somehow keep track of it.
>>
>> One observation is that it is unsafe to constant propagate
>> ADDR_EXPR with -fno-delete-null-pointer-checks to function with
>> -fdelete-null-pointer-checks, so we may just simply stop propagating
>> known ADDR_EXPRs on when caller is -fno... and call is -f....
>
> Makes sense.
>
>>> +/* Return true if any of the known bits are non-zero.  */
>>> +bool
>>> +ipcp_bits_lattice::known_nonzero_p () const
>>> +{
>>> +  if (!constant_p ())
>>> +    return false;
>>> +  return !wi::eq_p (wi::bit_and (wi::bit_not (m_mask), m_value), 0);
>> There is also ne_p
>
> Changed.
>
>>> @@ -2374,6 +2384,7 @@ propagate_bits_across_jump_function (cgraph_edge *cs, 
>>> int idx,
>>>        tree operand = NULL_TREE;
>>>        enum tree_code code;
>>>        unsigned src_idx;
>>> +      bool only_for_nonzero = false;
>>>  
>>>        if (jfunc->type == IPA_JF_PASS_THROUGH)
>>>     {
>>> @@ -2386,7 +2397,9 @@ propagate_bits_across_jump_function (cgraph_edge *cs, 
>>> int idx,
>>>     {
>>>       code = POINTER_PLUS_EXPR;
>>>       src_idx = ipa_get_jf_ancestor_formal_id (jfunc);
>>> -     unsigned HOST_WIDE_INT offset = ipa_get_jf_ancestor_offset (jfunc) / 
>>> BITS_PER_UNIT;
>>> +     unsigned HOST_WIDE_INT offset
>>> +       = ipa_get_jf_ancestor_offset (jfunc) / BITS_PER_UNIT;
>> I still think the offset should be in bytes (and probably poly_int64).
>> There is get_addr_base_and_unit_offset
>
> I agree about bytes, not sure about poly_ints, but could be persuaded.
> But probably not as a part of this patch?
>
>>
>>> +     only_for_nonzero = (ipa_get_jf_ancestor_keep_null (jfunc) || !offset);
>>>       operand = build_int_cstu (size_type_node, offset);
>>>     }
>>>  
>>> @@ -2404,16 +2417,18 @@ propagate_bits_across_jump_function (cgraph_edge 
>>> *cs, int idx,
>>>      and we store it in jump function during analysis stage.  */
>>>  
>>>        if (src_lats->bits_lattice.bottom_p ()
>>> -     && jfunc->bits)
>>> -   return dest_lattice->meet_with (jfunc->bits->value, jfunc->bits->mask,
>>> -                                   precision);
>>> +     || (only_for_nonzero && !src_lats->bits_lattice.known_nonzero_p ()))
>>> +   {
>>> +     if (jfunc->bits)
>>> +       return dest_lattice->meet_with (jfunc->bits->value,
>>> +                                       jfunc->bits->mask, precision);
>>> +     else
>>> +       return dest_lattice->set_to_bottom ();
>>> +   }
>> I do not think you need to set to bottom here. For pointers, we
>> primarily track alignment and NULL is aligned - all you need to do is to
>> make sure that we do not believe that some bits are 1.
>
> I am not sure I understand, the testcase you wrote has all bits as zeros
> and still miscompiles?  It is primarily used for alignment but not only
> for that.
>
>>> @@ -3327,7 +3332,8 @@ update_jump_functions_after_inlining (struct 
>>> cgraph_edge *cs,
>>>                 ipa_set_ancestor_jf (dst,
>>>                                      ipa_get_jf_ancestor_offset (src),
>>>                                      ipa_get_jf_ancestor_formal_id (src),
>>> -                                    agg_p);
>>> +                                    agg_p,
>>> +                                    ipa_get_jf_ancestor_keep_null (src));
>>>                 break;
>> Somewhere here you need to consider the case where you have two accessor
>> jump functions to combine together and merge the keep_null flags...
>
> Oops, I missed that, fixed.
>
>>
>> Also with the flags, I think you want to modify ipa-cp so NULL pointers
>> gets propagated acroess the ancestor jump functions.
>
> OK, added, along with a new testcase.
>
>> Moreover ipa-modref is using ancestor jf to update its summary.  Here I
>> think with -fno-delete-null-pointer-checks it is safe to pdate also with
>> the flag set, since we know the memory access is invalid otherwise.
>
> I think it currently is, but only because for:
>
>   int array_at_addr_zero[2];
>
>   void __attribute__((noinline))
>   bar(int *p)
>   {
>     clobber(p ? &p[1] : p);
>   }
>   /* ... */
>     bar(array_at_addr_zero);
>
> we do not produce an ancestor jump function (or any jump function
> whatsoever).  In the IL it is not represented as an ADDR_EXPR of a
> MEM_REF but as a POINTER_PLUS.
>
> Anyway, I guess I'll need to amend it to avoid the set_to_bottom you
> claim is not necessary but currently I have the following.
>
> Thanks,
>
> Martin
>
>
> gcc/ChangeLog:
>
> 2021-11-29  Martin Jambor  <mjam...@suse.cz>
>
>       PR ipa/103083
>       * ipa-prop.h (ipa_ancestor_jf_data): New flag keep_null;
>       (ipa_get_jf_ancestor_keep_null): New function.
>       * ipa-prop.c (ipa_set_ancestor_jf): Initialize keep_null field of the
>       ancestor function.
>       (compute_complex_assign_jump_func): Pass false to keep_null
>       parameter of ipa_set_ancestor_jf.
>       (compute_complex_ancestor_jump_func): Pass true to keep_null
>       parameter of ipa_set_ancestor_jf.
>       (update_jump_functions_after_inlining): Carry over keep_null from the
>       original ancestor jump-function or merge them.
>       (ipa_write_jump_function): Stream keep_null flag.
>       (ipa_read_jump_function): Likewise.
>       (ipa_print_node_jump_functions_for_edge): Print the new flag.
>       * ipa-cp.c (class ipcp_bits_lattice): Make various getters const.  New
>       member function known_nonzero_p.
>       (ipcp_bits_lattice::known_nonzero_p): New.
>       (propagate_bits_across_jump_function): Only process ancestor functions
>       when safe.  Remove extraneous condition handling ancestor jump
>       functions.
>       (propagate_aggs_across_jump_function): Take care of keep_null
>       flag.
>       (ipa_get_jf_ancestor_result): Propagate NULL accross keep_null
>       jump functions.
>
> gcc/testsuite/ChangeLog:
>
> 2021-11-25  Martin Jambor  <mjam...@suse.cz>
>
>       * gcc.dg/ipa/pr103083-1.c: New test.
>       * gcc.dg/ipa/pr103083-2.c: Likewise.
> ---
>  gcc/ipa-cp.c                          | 42 +++++++++++++++++++--------
>  gcc/ipa-prop.c                        | 20 +++++++++----
>  gcc/ipa-prop.h                        | 13 +++++++++
>  gcc/testsuite/gcc.dg/ipa/pr103083-1.c | 28 ++++++++++++++++++
>  gcc/testsuite/gcc.dg/ipa/pr103083-2.c | 30 +++++++++++++++++++
>  5 files changed, 116 insertions(+), 17 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.dg/ipa/pr103083-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/ipa/pr103083-2.c
>
> diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
> index 5d9bb974d6b..c19fe9cf2ea 100644
> --- a/gcc/ipa-cp.c
> +++ b/gcc/ipa-cp.c
> @@ -306,14 +306,15 @@ public:
>  class ipcp_bits_lattice
>  {
>  public:
> -  bool bottom_p () { return m_lattice_val == IPA_BITS_VARYING; }
> -  bool top_p () { return m_lattice_val == IPA_BITS_UNDEFINED; }
> -  bool constant_p () { return m_lattice_val == IPA_BITS_CONSTANT; }
> +  bool bottom_p () const { return m_lattice_val == IPA_BITS_VARYING; }
> +  bool top_p () const { return m_lattice_val == IPA_BITS_UNDEFINED; }
> +  bool constant_p () const { return m_lattice_val == IPA_BITS_CONSTANT; }
>    bool set_to_bottom ();
>    bool set_to_constant (widest_int, widest_int);
> +  bool known_nonzero_p () const;
>  
> -  widest_int get_value () { return m_value; }
> -  widest_int get_mask () { return m_mask; }
> +  widest_int get_value () const { return m_value; }
> +  widest_int get_mask () const { return m_mask; }
>  
>    bool meet_with (ipcp_bits_lattice& other, unsigned, signop,
>                 enum tree_code, tree);
> @@ -1081,6 +1082,15 @@ ipcp_bits_lattice::set_to_constant (widest_int value, 
> widest_int mask)
>    return true;
>  }
>  
> +/* Return true if any of the known bits are non-zero.  */
> +bool
> +ipcp_bits_lattice::known_nonzero_p () const
> +{
> +  if (!constant_p ())
> +    return false;
> +  return wi::ne_p (wi::bit_and (wi::bit_not (m_mask), m_value), 0);
> +}
> +
>  /* Convert operand to value, mask form.  */
>  
>  void
> @@ -1477,6 +1487,9 @@ ipa_get_jf_ancestor_result (struct ipa_jump_func 
> *jfunc, tree input)
>                    fold_build2 (MEM_REF, TREE_TYPE (TREE_TYPE (input)), input,
>                                 build_int_cst (ptr_type_node, byte_offset)));
>      }
> +  else if (ipa_get_jf_ancestor_keep_null (jfunc)
> +        && zerop (input))
> +    return input;
>    else
>      return NULL_TREE;
>  }
> @@ -2373,6 +2386,7 @@ propagate_bits_across_jump_function (cgraph_edge *cs, 
> int idx,
>        tree operand = NULL_TREE;
>        enum tree_code code;
>        unsigned src_idx;
> +      bool only_for_nonzero = false;
>  
>        if (jfunc->type == IPA_JF_PASS_THROUGH)
>       {
> @@ -2385,7 +2399,9 @@ propagate_bits_across_jump_function (cgraph_edge *cs, 
> int idx,
>       {
>         code = POINTER_PLUS_EXPR;
>         src_idx = ipa_get_jf_ancestor_formal_id (jfunc);
> -       unsigned HOST_WIDE_INT offset = ipa_get_jf_ancestor_offset (jfunc) / 
> BITS_PER_UNIT;
> +       unsigned HOST_WIDE_INT offset
> +         = ipa_get_jf_ancestor_offset (jfunc) / BITS_PER_UNIT;
> +       only_for_nonzero = (ipa_get_jf_ancestor_keep_null (jfunc) || !offset);
>         operand = build_int_cstu (size_type_node, offset);
>       }
>  
> @@ -2403,16 +2419,18 @@ propagate_bits_across_jump_function (cgraph_edge *cs, 
> int idx,
>        and we store it in jump function during analysis stage.  */
>  
>        if (src_lats->bits_lattice.bottom_p ()
> -       && jfunc->bits)
> -     return dest_lattice->meet_with (jfunc->bits->value, jfunc->bits->mask,
> -                                     precision);
> +       || (only_for_nonzero && !src_lats->bits_lattice.known_nonzero_p ()))
> +     {
> +       if (jfunc->bits)
> +         return dest_lattice->meet_with (jfunc->bits->value,
> +                                         jfunc->bits->mask, precision);
> +       else
> +         return dest_lattice->set_to_bottom ();
> +     }
>        else
>       return dest_lattice->meet_with (src_lats->bits_lattice, precision, sgn,
>                                       code, operand);
>      }
> -
> -  else if (jfunc->type == IPA_JF_ANCESTOR)
> -    return dest_lattice->set_to_bottom ();
>    else if (jfunc->bits)
>      return dest_lattice->meet_with (jfunc->bits->value, jfunc->bits->mask,
>                                   precision);
> diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
> index bc5643206b9..ef9e353764c 100644
> --- a/gcc/ipa-prop.c
> +++ b/gcc/ipa-prop.c
> @@ -357,6 +357,8 @@ ipa_print_node_jump_functions_for_edge (FILE *f, struct 
> cgraph_edge *cs)
>                  jump_func->value.ancestor.offset);
>         if (jump_func->value.ancestor.agg_preserved)
>           fprintf (f, ", agg_preserved");
> +       if (jump_func->value.ancestor.keep_null)
> +         fprintf (f, ", keep_null");
>         fprintf (f, "\n");
>       }
>  
> @@ -601,12 +603,13 @@ ipa_set_jf_arith_pass_through (struct ipa_jump_func 
> *jfunc, int formal_id,
>  
>  static void
>  ipa_set_ancestor_jf (struct ipa_jump_func *jfunc, HOST_WIDE_INT offset,
> -                  int formal_id, bool agg_preserved)
> +                  int formal_id, bool agg_preserved, bool keep_null)
>  {
>    jfunc->type = IPA_JF_ANCESTOR;
>    jfunc->value.ancestor.formal_id = formal_id;
>    jfunc->value.ancestor.offset = offset;
>    jfunc->value.ancestor.agg_preserved = agg_preserved;
> +  jfunc->value.ancestor.keep_null = keep_null;
>  }
>  
>  /* Get IPA BB information about the given BB.  FBI is the context of analyzis
> @@ -1438,7 +1441,8 @@ compute_complex_assign_jump_func (struct 
> ipa_func_body_info *fbi,
>    index = ipa_get_param_decl_index (info, SSA_NAME_VAR (ssa));
>    if (index >= 0 && param_type && POINTER_TYPE_P (param_type))
>      ipa_set_ancestor_jf (jfunc, offset,  index,
> -                      parm_ref_data_pass_through_p (fbi, index, call, ssa));
> +                      parm_ref_data_pass_through_p (fbi, index, call, ssa),
> +                      false);
>  }
>  
>  /* Extract the base, offset and MEM_REF expression from a statement ASSIGN if
> @@ -1564,7 +1568,8 @@ compute_complex_ancestor_jump_func (struct 
> ipa_func_body_info *fbi,
>      }
>  
>    ipa_set_ancestor_jf (jfunc, offset, index,
> -                    parm_ref_data_pass_through_p (fbi, index, call, parm));
> +                    parm_ref_data_pass_through_p (fbi, index, call, parm),
> +                    true);
>  }
>  
>  /* Inspect the given TYPE and return true iff it has the same structure (the
> @@ -3250,6 +3255,7 @@ update_jump_functions_after_inlining (struct 
> cgraph_edge *cs,
>             dst->value.ancestor.offset += src->value.ancestor.offset;
>             dst->value.ancestor.agg_preserved &=
>               src->value.ancestor.agg_preserved;
> +           dst->value.ancestor.keep_null |= src->value.ancestor.keep_null;
>           }
>         else
>           ipa_set_jf_unknown (dst);
> @@ -3327,7 +3333,8 @@ update_jump_functions_after_inlining (struct 
> cgraph_edge *cs,
>                   ipa_set_ancestor_jf (dst,
>                                        ipa_get_jf_ancestor_offset (src),
>                                        ipa_get_jf_ancestor_formal_id (src),
> -                                      agg_p);
> +                                      agg_p,
> +                                      ipa_get_jf_ancestor_keep_null (src));
>                   break;
>                 }
>               default:
> @@ -4758,6 +4765,7 @@ ipa_write_jump_function (struct output_block *ob,
>        streamer_write_uhwi (ob, jump_func->value.ancestor.formal_id);
>        bp = bitpack_create (ob->main_stream);
>        bp_pack_value (&bp, jump_func->value.ancestor.agg_preserved, 1);
> +      bp_pack_value (&bp, jump_func->value.ancestor.keep_null, 1);
>        streamer_write_bitpack (&bp);
>        break;
>      default:
> @@ -4883,7 +4891,9 @@ ipa_read_jump_function (class lto_input_block *ib,
>       int formal_id = streamer_read_uhwi (ib);
>       struct bitpack_d bp = streamer_read_bitpack (ib);
>       bool agg_preserved = bp_unpack_value (&bp, 1);
> -     ipa_set_ancestor_jf (jump_func, offset, formal_id, agg_preserved);
> +     bool keep_null = bp_unpack_value (&bp, 1);
> +     ipa_set_ancestor_jf (jump_func, offset, formal_id, agg_preserved,
> +                          keep_null);
>       break;
>        }
>      default:
> diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h
> index ba49843a510..4e243bcfe19 100644
> --- a/gcc/ipa-prop.h
> +++ b/gcc/ipa-prop.h
> @@ -143,6 +143,8 @@ struct GTY(()) ipa_ancestor_jf_data
>    int formal_id;
>    /* Flag with the same meaning like agg_preserve in ipa_pass_through_data.  
> */
>    unsigned agg_preserved : 1;
> +  /* When set, the operation should not have any effect on NULL pointers.  */
> +  unsigned keep_null : 1;
>  };
>  
>  /* A jump function for an aggregate part at a given offset, which describes 
> how
> @@ -438,6 +440,17 @@ ipa_get_jf_ancestor_type_preserved (struct ipa_jump_func 
> *jfunc)
>    return jfunc->value.ancestor.agg_preserved;
>  }
>  
> +/* Return if jfunc represents an operation whether we first check the formal
> +   parameter for non-NULLness unless it does not matter because the offset is
> +   zero anyway.  */
> +
> +static inline bool
> +ipa_get_jf_ancestor_keep_null (struct ipa_jump_func *jfunc)
> +{
> +  gcc_checking_assert (jfunc->type == IPA_JF_ANCESTOR);
> +  return jfunc->value.ancestor.keep_null;
> +}
> +
>  /* Class for allocating a bundle of various potentially known properties 
> about
>     actual arguments of a particular call on stack for the usual case and on
>     heap only if there are unusually many arguments.  The data is deallocated
> diff --git a/gcc/testsuite/gcc.dg/ipa/pr103083-1.c 
> b/gcc/testsuite/gcc.dg/ipa/pr103083-1.c
> new file mode 100644
> index 00000000000..e2fbb45d3cc
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/ipa/pr103083-1.c
> @@ -0,0 +1,28 @@
> +/* { dg-do run } */
> +/* { dg-options "-O2 -Wno-pointer-to-int-cast" } */
> +
> +struct b {int b;};
> +struct a {int a; struct b b;};
> +
> +long i;
> +
> +__attribute__ ((noinline))
> +static void test2 (struct b *b)
> +{
> +  if (((int)b)&4)
> +    __builtin_abort ();
> +}
> +
> +__attribute__ ((noinline))
> +static void
> +test (struct a *a)
> +{
> +  test2(a? &a->b : 0);
> +}
> +
> +int
> +main()
> +{
> +  test(0);
> +  return 0;
> +}
> diff --git a/gcc/testsuite/gcc.dg/ipa/pr103083-2.c 
> b/gcc/testsuite/gcc.dg/ipa/pr103083-2.c
> new file mode 100644
> index 00000000000..ae1b905af81
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/ipa/pr103083-2.c
> @@ -0,0 +1,30 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fno-ipa-bit-cp -fdump-tree-optimized" } */
> +
> +struct b {int b;};
> +struct a {int a; struct b b;};
> +
> +void remove_any_mention (void);
> +
> +__attribute__ ((noinline))
> +static void test2 (struct b *b)
> +{
> +  if (b)
> +    remove_any_mention ();
> +}
> +
> +__attribute__ ((noinline))
> +static void
> +test (struct a *a)
> +{
> +  test2(a? &a->b : 0);
> +}
> +
> +int
> +foo()
> +{
> +  test(0);
> +  return 0;
> +}
> +
> +/* { dg-final { scan-tree-dump-not "remove_any_mention" "optimized" } } */
> -- 
> 2.33.1

Reply via email to