> 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.... > +/* 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 > @@ -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 > + 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. > @@ -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... Also with the flags, I think you want to modify ipa-cp so NULL pointers gets propagated acroess the ancestor jump functions. 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. Honza > default: > @@ -4758,6 +4764,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 +4890,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 42842d9466a..8f0039d1ef8 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.c > b/gcc/testsuite/gcc.dg/ipa/pr103083.c > new file mode 100644 > index 00000000000..e2fbb45d3cc > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/ipa/pr103083.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; > +} > -- > 2.33.1 >